github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/os/dir_windows.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package os 6 7 import ( 8 "internal/syscall/windows" 9 "io" 10 "io/fs" 11 "runtime" 12 "sync" 13 "syscall" 14 "unsafe" 15 ) 16 17 // Auxiliary information if the File describes a directory 18 type dirInfo struct { 19 // buf is a slice pointer so the slice header 20 // does not escape to the heap when returning 21 // buf to dirBufPool. 22 buf *[]byte // buffer for directory I/O 23 bufp int // location of next record in buf 24 vol uint32 25 } 26 27 const ( 28 // dirBufSize is the size of the dirInfo buffer. 29 // The buffer must be big enough to hold at least a single entry. 30 // The filename alone can be 512 bytes (MAX_PATH*2), and the fixed part of 31 // the FILE_ID_BOTH_DIR_INFO structure is 105 bytes, so dirBufSize 32 // should not be set below 1024 bytes (512+105+safety buffer). 33 // Windows 8.1 and earlier only works with buffer sizes up to 64 kB. 34 dirBufSize = 64 * 1024 // 64kB 35 ) 36 37 var dirBufPool = sync.Pool{ 38 New: func() any { 39 // The buffer must be at least a block long. 40 buf := make([]byte, dirBufSize) 41 return &buf 42 }, 43 } 44 45 func (d *dirInfo) close() { 46 if d.buf != nil { 47 dirBufPool.Put(d.buf) 48 d.buf = nil 49 } 50 } 51 52 func (file *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) { 53 // If this file has no dirinfo, create one. 54 var infoClass uint32 = windows.FileIdBothDirectoryInfo 55 if file.dirinfo == nil { 56 // vol is used by os.SameFile. 57 // It is safe to query it once and reuse the value. 58 // Hard links are not allowed to reference files in other volumes. 59 // Junctions and symbolic links can reference files and directories in other volumes, 60 // but the reparse point should still live in the parent volume. 61 var vol uint32 62 err = windows.GetVolumeInformationByHandle(file.pfd.Sysfd, nil, 0, &vol, nil, nil, nil, 0) 63 runtime.KeepAlive(file) 64 if err != nil { 65 err = &PathError{Op: "readdir", Path: file.name, Err: err} 66 return 67 } 68 infoClass = windows.FileIdBothDirectoryRestartInfo 69 file.dirinfo = new(dirInfo) 70 file.dirinfo.buf = dirBufPool.Get().(*[]byte) 71 file.dirinfo.vol = vol 72 } 73 d := file.dirinfo 74 wantAll := n <= 0 75 if wantAll { 76 n = -1 77 } 78 for n != 0 { 79 // Refill the buffer if necessary 80 if d.bufp == 0 { 81 err = windows.GetFileInformationByHandleEx(file.pfd.Sysfd, infoClass, (*byte)(unsafe.Pointer(&(*d.buf)[0])), uint32(len(*d.buf))) 82 runtime.KeepAlive(file) 83 if err != nil { 84 if err == syscall.ERROR_NO_MORE_FILES { 85 break 86 } 87 if s, _ := file.Stat(); s != nil && !s.IsDir() { 88 err = &PathError{Op: "readdir", Path: file.name, Err: syscall.ENOTDIR} 89 } else { 90 err = &PathError{Op: "GetFileInformationByHandleEx", Path: file.name, Err: err} 91 } 92 return 93 } 94 infoClass = windows.FileIdBothDirectoryInfo 95 } 96 // Drain the buffer 97 var islast bool 98 for n != 0 && !islast { 99 info := (*windows.FILE_ID_BOTH_DIR_INFO)(unsafe.Pointer(&(*d.buf)[d.bufp])) 100 d.bufp += int(info.NextEntryOffset) 101 islast = info.NextEntryOffset == 0 102 if islast { 103 d.bufp = 0 104 } 105 nameslice := unsafe.Slice(&info.FileName[0], info.FileNameLength/2) 106 name := syscall.UTF16ToString(nameslice) 107 if name == "." || name == ".." { // Useless names 108 continue 109 } 110 if mode == readdirName { 111 names = append(names, name) 112 } else { 113 f := newFileStatFromFileIDBothDirInfo(info) 114 f.name = name 115 f.vol = d.vol 116 // f.path is used by os.SameFile to decide if it needs 117 // to fetch vol, idxhi and idxlo. But these are already set, 118 // so set f.path to "" to prevent os.SameFile doing it again. 119 f.path = "" 120 if mode == readdirDirEntry { 121 dirents = append(dirents, dirEntry{f}) 122 } else { 123 infos = append(infos, f) 124 } 125 } 126 n-- 127 } 128 } 129 if !wantAll && len(names)+len(dirents)+len(infos) == 0 { 130 return nil, nil, nil, io.EOF 131 } 132 return names, dirents, infos, nil 133 } 134 135 type dirEntry struct { 136 fs *fileStat 137 } 138 139 func (de dirEntry) Name() string { return de.fs.Name() } 140 func (de dirEntry) IsDir() bool { return de.fs.IsDir() } 141 func (de dirEntry) Type() FileMode { return de.fs.Mode().Type() } 142 func (de dirEntry) Info() (FileInfo, error) { return de.fs, nil } 143 144 func (de dirEntry) String() string { 145 return fs.FormatDirEntry(de) 146 }