mirror of https://github.com/golang/go.git
[release-branch.go1.21] Revert "os: use handle based APIs to read directories on windows"
This reverts CL 452995. Reason for revert: caused os.File.ReadDir to fail on filesystems that do not support FILE_ID_BOTH_DIR_INFO. This is an alternative to a fix-forward change in CL 518196. Since the original change was mostly a performance improvement, reverting to the previous implementation seems less risky than backporting a larger fix. Fixes #61910 Fixes #61964 Change-Id: I60f1602b9eb6ea353e7eb23429f19f1ffa16b394 Reviewed-on: https://go-review.googlesource.com/c/go/+/520156 Run-TryBot: Bryan Mills <bcmills@google.com> Reviewed-by: Austin Clements <austin@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Quim Muntal <quimmuntal@gmail.com>
This commit is contained in:
parent
cb6ea94996
commit
7c97cc7d97
|
|
@ -375,25 +375,5 @@ func ErrorLoadingGetTempPath2() error {
|
|||
|
||||
//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036
|
||||
|
||||
type FILE_ID_BOTH_DIR_INFO struct {
|
||||
NextEntryOffset uint32
|
||||
FileIndex uint32
|
||||
CreationTime syscall.Filetime
|
||||
LastAccessTime syscall.Filetime
|
||||
LastWriteTime syscall.Filetime
|
||||
ChangeTime syscall.Filetime
|
||||
EndOfFile uint64
|
||||
AllocationSize uint64
|
||||
FileAttributes uint32
|
||||
FileNameLength uint32
|
||||
EaSize uint32
|
||||
ShortNameLength uint32
|
||||
ShortName [12]uint16
|
||||
FileID uint64
|
||||
FileName [1]uint16
|
||||
}
|
||||
|
||||
//sys GetVolumeInformationByHandle(file syscall.Handle, volumeNameBuffer *uint16, volumeNameSize uint32, volumeNameSerialNumber *uint32, maximumComponentLength *uint32, fileSystemFlags *uint32, fileSystemNameBuffer *uint16, fileSystemNameSize uint32) (err error) = GetVolumeInformationByHandleW
|
||||
|
||||
//sys RtlLookupFunctionEntry(pc uintptr, baseAddress *uintptr, table *byte) (ret uintptr) = kernel32.RtlLookupFunctionEntry
|
||||
//sys RtlVirtualUnwind(handlerType uint32, baseAddress uintptr, pc uintptr, entry uintptr, ctxt uintptr, data *uintptr, frame *uintptr, ctxptrs *byte) (ret uintptr) = kernel32.RtlVirtualUnwind
|
||||
|
|
|
|||
|
|
@ -45,43 +45,42 @@ var (
|
|||
moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll"))
|
||||
modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll"))
|
||||
|
||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||
procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx")
|
||||
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
||||
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
||||
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
||||
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
||||
procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation")
|
||||
procSystemFunction036 = modadvapi32.NewProc("SystemFunction036")
|
||||
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
|
||||
procCreateEventW = modkernel32.NewProc("CreateEventW")
|
||||
procGetACP = modkernel32.NewProc("GetACP")
|
||||
procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
|
||||
procGetConsoleCP = modkernel32.NewProc("GetConsoleCP")
|
||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
||||
procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW")
|
||||
procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW")
|
||||
procGetTempPath2W = modkernel32.NewProc("GetTempPath2W")
|
||||
procGetVolumeInformationByHandleW = modkernel32.NewProc("GetVolumeInformationByHandleW")
|
||||
procLockFileEx = modkernel32.NewProc("LockFileEx")
|
||||
procModule32FirstW = modkernel32.NewProc("Module32FirstW")
|
||||
procModule32NextW = modkernel32.NewProc("Module32NextW")
|
||||
procMoveFileExW = modkernel32.NewProc("MoveFileExW")
|
||||
procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar")
|
||||
procRtlLookupFunctionEntry = modkernel32.NewProc("RtlLookupFunctionEntry")
|
||||
procRtlVirtualUnwind = modkernel32.NewProc("RtlVirtualUnwind")
|
||||
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
||||
procUnlockFileEx = modkernel32.NewProc("UnlockFileEx")
|
||||
procVirtualQuery = modkernel32.NewProc("VirtualQuery")
|
||||
procNetShareAdd = modnetapi32.NewProc("NetShareAdd")
|
||||
procNetShareDel = modnetapi32.NewProc("NetShareDel")
|
||||
procNetUserGetLocalGroups = modnetapi32.NewProc("NetUserGetLocalGroups")
|
||||
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
|
||||
procCreateEnvironmentBlock = moduserenv.NewProc("CreateEnvironmentBlock")
|
||||
procDestroyEnvironmentBlock = moduserenv.NewProc("DestroyEnvironmentBlock")
|
||||
procGetProfilesDirectoryW = moduserenv.NewProc("GetProfilesDirectoryW")
|
||||
procWSASocketW = modws2_32.NewProc("WSASocketW")
|
||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||
procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx")
|
||||
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
||||
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
||||
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
||||
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
||||
procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation")
|
||||
procSystemFunction036 = modadvapi32.NewProc("SystemFunction036")
|
||||
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
|
||||
procCreateEventW = modkernel32.NewProc("CreateEventW")
|
||||
procGetACP = modkernel32.NewProc("GetACP")
|
||||
procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
|
||||
procGetConsoleCP = modkernel32.NewProc("GetConsoleCP")
|
||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
||||
procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW")
|
||||
procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW")
|
||||
procGetTempPath2W = modkernel32.NewProc("GetTempPath2W")
|
||||
procLockFileEx = modkernel32.NewProc("LockFileEx")
|
||||
procModule32FirstW = modkernel32.NewProc("Module32FirstW")
|
||||
procModule32NextW = modkernel32.NewProc("Module32NextW")
|
||||
procMoveFileExW = modkernel32.NewProc("MoveFileExW")
|
||||
procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar")
|
||||
procRtlLookupFunctionEntry = modkernel32.NewProc("RtlLookupFunctionEntry")
|
||||
procRtlVirtualUnwind = modkernel32.NewProc("RtlVirtualUnwind")
|
||||
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
||||
procUnlockFileEx = modkernel32.NewProc("UnlockFileEx")
|
||||
procVirtualQuery = modkernel32.NewProc("VirtualQuery")
|
||||
procNetShareAdd = modnetapi32.NewProc("NetShareAdd")
|
||||
procNetShareDel = modnetapi32.NewProc("NetShareDel")
|
||||
procNetUserGetLocalGroups = modnetapi32.NewProc("NetUserGetLocalGroups")
|
||||
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
|
||||
procCreateEnvironmentBlock = moduserenv.NewProc("CreateEnvironmentBlock")
|
||||
procDestroyEnvironmentBlock = moduserenv.NewProc("DestroyEnvironmentBlock")
|
||||
procGetProfilesDirectoryW = moduserenv.NewProc("GetProfilesDirectoryW")
|
||||
procWSASocketW = modws2_32.NewProc("WSASocketW")
|
||||
)
|
||||
|
||||
func adjustTokenPrivileges(token syscall.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) (ret uint32, err error) {
|
||||
|
|
@ -242,14 +241,6 @@ func GetTempPath2(buflen uint32, buf *uint16) (n uint32, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func GetVolumeInformationByHandle(file syscall.Handle, volumeNameBuffer *uint16, volumeNameSize uint32, volumeNameSerialNumber *uint32, maximumComponentLength *uint32, fileSystemFlags *uint32, fileSystemNameBuffer *uint16, fileSystemNameSize uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procGetVolumeInformationByHandleW.Addr(), 8, uintptr(file), uintptr(unsafe.Pointer(volumeNameBuffer)), uintptr(volumeNameSize), uintptr(unsafe.Pointer(volumeNameSerialNumber)), uintptr(unsafe.Pointer(maximumComponentLength)), uintptr(unsafe.Pointer(fileSystemFlags)), uintptr(unsafe.Pointer(fileSystemNameBuffer)), uintptr(fileSystemNameSize), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func LockFileEx(file syscall.Handle, flags uint32, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *syscall.Overlapped) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6, uintptr(file), uintptr(flags), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped)))
|
||||
if r1 == 0 {
|
||||
|
|
|
|||
|
|
@ -5,138 +5,60 @@
|
|||
package os
|
||||
|
||||
import (
|
||||
"internal/syscall/windows"
|
||||
"io"
|
||||
"io/fs"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Auxiliary information if the File describes a directory
|
||||
type dirInfo struct {
|
||||
// buf is a slice pointer so the slice header
|
||||
// does not escape to the heap when returning
|
||||
// buf to dirBufPool.
|
||||
buf *[]byte // buffer for directory I/O
|
||||
bufp int // location of next record in buf
|
||||
vol uint32
|
||||
}
|
||||
|
||||
const (
|
||||
// dirBufSize is the size of the dirInfo buffer.
|
||||
// The buffer must be big enough to hold at least a single entry.
|
||||
// The filename alone can be 512 bytes (MAX_PATH*2), and the fixed part of
|
||||
// the FILE_ID_BOTH_DIR_INFO structure is 105 bytes, so dirBufSize
|
||||
// should not be set below 1024 bytes (512+105+safety buffer).
|
||||
// Windows 8.1 and earlier only works with buffer sizes up to 64 kB.
|
||||
dirBufSize = 64 * 1024 // 64kB
|
||||
)
|
||||
|
||||
var dirBufPool = sync.Pool{
|
||||
New: func() any {
|
||||
// The buffer must be at least a block long.
|
||||
buf := make([]byte, dirBufSize)
|
||||
return &buf
|
||||
},
|
||||
}
|
||||
|
||||
func (d *dirInfo) close() {
|
||||
if d.buf != nil {
|
||||
dirBufPool.Put(d.buf)
|
||||
d.buf = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (file *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
|
||||
// If this file has no dirinfo, create one.
|
||||
var infoClass uint32 = windows.FileIdBothDirectoryInfo
|
||||
needdata := true
|
||||
if file.dirinfo == nil {
|
||||
// vol is used by os.SameFile.
|
||||
// It is safe to query it once and reuse the value.
|
||||
// Hard links are not allowed to reference files in other volumes.
|
||||
// Junctions and symbolic links can reference files and directories in other volumes,
|
||||
// but the reparse point should still live in the parent volume.
|
||||
var vol uint32
|
||||
err = windows.GetVolumeInformationByHandle(file.pfd.Sysfd, nil, 0, &vol, nil, nil, nil, 0)
|
||||
runtime.KeepAlive(file)
|
||||
needdata = false
|
||||
file.dirinfo, err = openDir(file.name)
|
||||
if err != nil {
|
||||
err = &PathError{Op: "readdir", Path: file.name, Err: err}
|
||||
return
|
||||
}
|
||||
infoClass = windows.FileIdBothDirectoryRestartInfo
|
||||
file.dirinfo = new(dirInfo)
|
||||
file.dirinfo.buf = dirBufPool.Get().(*[]byte)
|
||||
file.dirinfo.vol = vol
|
||||
}
|
||||
d := file.dirinfo
|
||||
wantAll := n <= 0
|
||||
if wantAll {
|
||||
n = -1
|
||||
}
|
||||
for n != 0 {
|
||||
// Refill the buffer if necessary
|
||||
if d.bufp == 0 {
|
||||
err = windows.GetFileInformationByHandleEx(file.pfd.Sysfd, infoClass, (*byte)(unsafe.Pointer(&(*d.buf)[0])), uint32(len(*d.buf)))
|
||||
d := &file.dirinfo.data
|
||||
for n != 0 && !file.dirinfo.isempty {
|
||||
if needdata {
|
||||
e := syscall.FindNextFile(file.dirinfo.h, d)
|
||||
runtime.KeepAlive(file)
|
||||
if err != nil {
|
||||
if err == syscall.ERROR_NO_MORE_FILES {
|
||||
if e != nil {
|
||||
if e == syscall.ERROR_NO_MORE_FILES {
|
||||
break
|
||||
}
|
||||
if infoClass == windows.FileIdBothDirectoryRestartInfo && err == syscall.ERROR_FILE_NOT_FOUND {
|
||||
// GetFileInformationByHandleEx doesn't document the return error codes when the info class is FileIdBothDirectoryRestartInfo,
|
||||
// but MS-FSA 2.1.5.6.3 [1] specifies that the underlying file system driver should return STATUS_NO_SUCH_FILE when
|
||||
// reading an empty root directory, which is mapped to ERROR_FILE_NOT_FOUND by Windows.
|
||||
// Note that some file system drivers may never return this error code, as the spec allows to return the "." and ".."
|
||||
// entries in such cases, making the directory appear non-empty.
|
||||
// The chances of false positive are very low, as we know that the directory exists, else GetVolumeInformationByHandle
|
||||
// would have failed, and that the handle is still valid, as we haven't closed it.
|
||||
// See go.dev/issue/61159.
|
||||
// [1] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fsa/fa8194e0-53ec-413b-8315-e8fa85396fd8
|
||||
break
|
||||
}
|
||||
if s, _ := file.Stat(); s != nil && !s.IsDir() {
|
||||
err = &PathError{Op: "readdir", Path: file.name, Err: syscall.ENOTDIR}
|
||||
} else {
|
||||
err = &PathError{Op: "GetFileInformationByHandleEx", Path: file.name, Err: err}
|
||||
err = &PathError{Op: "FindNextFile", Path: file.name, Err: e}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
infoClass = windows.FileIdBothDirectoryInfo
|
||||
}
|
||||
// Drain the buffer
|
||||
var islast bool
|
||||
for n != 0 && !islast {
|
||||
info := (*windows.FILE_ID_BOTH_DIR_INFO)(unsafe.Pointer(&(*d.buf)[d.bufp]))
|
||||
d.bufp += int(info.NextEntryOffset)
|
||||
islast = info.NextEntryOffset == 0
|
||||
if islast {
|
||||
d.bufp = 0
|
||||
}
|
||||
nameslice := unsafe.Slice(&info.FileName[0], info.FileNameLength/2)
|
||||
name := syscall.UTF16ToString(nameslice)
|
||||
if name == "." || name == ".." { // Useless names
|
||||
continue
|
||||
}
|
||||
if mode == readdirName {
|
||||
names = append(names, name)
|
||||
needdata = true
|
||||
name := syscall.UTF16ToString(d.FileName[0:])
|
||||
if name == "." || name == ".." { // Useless names
|
||||
continue
|
||||
}
|
||||
if mode == readdirName {
|
||||
names = append(names, name)
|
||||
} else {
|
||||
f := newFileStatFromWin32finddata(d)
|
||||
f.name = name
|
||||
f.path = file.dirinfo.path
|
||||
f.appendNameToPath = true
|
||||
if mode == readdirDirEntry {
|
||||
dirents = append(dirents, dirEntry{f})
|
||||
} else {
|
||||
f := newFileStatFromFileIDBothDirInfo(info)
|
||||
f.name = name
|
||||
f.vol = d.vol
|
||||
// f.path is used by os.SameFile to decide if it needs
|
||||
// to fetch vol, idxhi and idxlo. But these are already set,
|
||||
// so set f.path to "" to prevent os.SameFile doing it again.
|
||||
f.path = ""
|
||||
if mode == readdirDirEntry {
|
||||
dirents = append(dirents, dirEntry{f})
|
||||
} else {
|
||||
infos = append(infos, f)
|
||||
}
|
||||
infos = append(infos, f)
|
||||
}
|
||||
n--
|
||||
}
|
||||
n--
|
||||
}
|
||||
if !wantAll && len(names)+len(dirents)+len(infos) == 0 {
|
||||
return nil, nil, nil, io.EOF
|
||||
|
|
|
|||
|
|
@ -87,6 +87,18 @@ func NewFile(fd uintptr, name string) *File {
|
|||
return newFile(h, name, "file")
|
||||
}
|
||||
|
||||
// Auxiliary information if the File describes a directory
|
||||
type dirInfo struct {
|
||||
h syscall.Handle // search handle created with FindFirstFile
|
||||
data syscall.Win32finddata
|
||||
path string
|
||||
isempty bool // set if FindFirstFile returns ERROR_FILE_NOT_FOUND
|
||||
}
|
||||
|
||||
func (d *dirInfo) close() error {
|
||||
return syscall.FindClose(d.h)
|
||||
}
|
||||
|
||||
func epipecheck(file *File, e error) {
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +106,63 @@ func epipecheck(file *File, e error) {
|
|||
// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
|
||||
const DevNull = "NUL"
|
||||
|
||||
func openDir(name string) (d *dirInfo, e error) {
|
||||
var mask string
|
||||
|
||||
path := fixLongPath(name)
|
||||
|
||||
if len(path) == 2 && path[1] == ':' { // it is a drive letter, like C:
|
||||
mask = path + `*`
|
||||
} else if len(path) > 0 {
|
||||
lc := path[len(path)-1]
|
||||
if lc == '/' || lc == '\\' {
|
||||
mask = path + `*`
|
||||
} else {
|
||||
mask = path + `\*`
|
||||
}
|
||||
} else {
|
||||
mask = `\*`
|
||||
}
|
||||
maskp, e := syscall.UTF16PtrFromString(mask)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
d = new(dirInfo)
|
||||
d.h, e = syscall.FindFirstFile(maskp, &d.data)
|
||||
if e != nil {
|
||||
// FindFirstFile returns ERROR_FILE_NOT_FOUND when
|
||||
// no matching files can be found. Then, if directory
|
||||
// exists, we should proceed.
|
||||
// If FindFirstFile failed because name does not point
|
||||
// to a directory, we should return ENOTDIR.
|
||||
var fa syscall.Win32FileAttributeData
|
||||
pathp, e1 := syscall.UTF16PtrFromString(path)
|
||||
if e1 != nil {
|
||||
return nil, e
|
||||
}
|
||||
e1 = syscall.GetFileAttributesEx(pathp, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa)))
|
||||
if e1 != nil {
|
||||
return nil, e
|
||||
}
|
||||
if fa.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 {
|
||||
return nil, syscall.ENOTDIR
|
||||
}
|
||||
if e != syscall.ERROR_FILE_NOT_FOUND {
|
||||
return nil, e
|
||||
}
|
||||
d.isempty = true
|
||||
}
|
||||
d.path = path
|
||||
if !isAbs(d.path) {
|
||||
d.path, e = syscall.FullPath(d.path)
|
||||
if e != nil {
|
||||
d.close()
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// openFileNolog is the Windows implementation of OpenFile.
|
||||
func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
|
||||
if name == "" {
|
||||
|
|
|
|||
|
|
@ -1453,3 +1453,15 @@ func TestNewFileInvalid(t *testing.T) {
|
|||
t.Errorf("NewFile(InvalidHandle) got %v want nil", f)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadDirPipe(t *testing.T) {
|
||||
dir := `\\.\pipe\`
|
||||
fi, err := os.Stat(dir)
|
||||
if err != nil || !fi.IsDir() {
|
||||
t.Skipf("%s is not a directory", dir)
|
||||
}
|
||||
_, err = os.ReadDir(dir)
|
||||
if err != nil {
|
||||
t.Errorf("ReadDir(%q) = %v", dir, err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import (
|
|||
type fileStat struct {
|
||||
name string
|
||||
|
||||
// from ByHandleFileInformation, Win32FileAttributeData, Win32finddata, and GetFileInformationByHandleEx
|
||||
// from ByHandleFileInformation, Win32FileAttributeData and Win32finddata
|
||||
FileAttributes uint32
|
||||
CreationTime syscall.Filetime
|
||||
LastAccessTime syscall.Filetime
|
||||
|
|
@ -24,7 +24,7 @@ type fileStat struct {
|
|||
FileSizeHigh uint32
|
||||
FileSizeLow uint32
|
||||
|
||||
// from Win32finddata and GetFileInformationByHandleEx
|
||||
// from Win32finddata
|
||||
ReparseTag uint32
|
||||
|
||||
// what syscall.GetFileType returns
|
||||
|
|
@ -32,10 +32,11 @@ type fileStat struct {
|
|||
|
||||
// used to implement SameFile
|
||||
sync.Mutex
|
||||
path string
|
||||
vol uint32
|
||||
idxhi uint32
|
||||
idxlo uint32
|
||||
path string
|
||||
vol uint32
|
||||
idxhi uint32
|
||||
idxlo uint32
|
||||
appendNameToPath bool
|
||||
}
|
||||
|
||||
// newFileStatFromGetFileInformationByHandle calls GetFileInformationByHandle
|
||||
|
|
@ -79,26 +80,6 @@ func newFileStatFromGetFileInformationByHandle(path string, h syscall.Handle) (f
|
|||
}, nil
|
||||
}
|
||||
|
||||
// newFileStatFromFileIDBothDirInfo copies all required information
|
||||
// from windows.FILE_ID_BOTH_DIR_INFO d into the newly created fileStat.
|
||||
func newFileStatFromFileIDBothDirInfo(d *windows.FILE_ID_BOTH_DIR_INFO) *fileStat {
|
||||
// The FILE_ID_BOTH_DIR_INFO MSDN documentations isn't completely correct.
|
||||
// FileAttributes can contain any file attributes that is currently set on the file,
|
||||
// not just the ones documented.
|
||||
// EaSize contains the reparse tag if the file is a reparse point.
|
||||
return &fileStat{
|
||||
FileAttributes: d.FileAttributes,
|
||||
CreationTime: d.CreationTime,
|
||||
LastAccessTime: d.LastAccessTime,
|
||||
LastWriteTime: d.LastWriteTime,
|
||||
FileSizeHigh: uint32(d.EndOfFile >> 32),
|
||||
FileSizeLow: uint32(d.EndOfFile),
|
||||
ReparseTag: d.EaSize,
|
||||
idxhi: uint32(d.FileID >> 32),
|
||||
idxlo: uint32(d.FileID),
|
||||
}
|
||||
}
|
||||
|
||||
// newFileStatFromWin32finddata copies all required information
|
||||
// from syscall.Win32finddata d into the newly created fileStat.
|
||||
func newFileStatFromWin32finddata(d *syscall.Win32finddata) *fileStat {
|
||||
|
|
@ -188,7 +169,13 @@ func (fs *fileStat) loadFileId() error {
|
|||
// already done
|
||||
return nil
|
||||
}
|
||||
pathp, err := syscall.UTF16PtrFromString(fs.path)
|
||||
var path string
|
||||
if fs.appendNameToPath {
|
||||
path = fs.path + `\` + fs.name
|
||||
} else {
|
||||
path = fs.path
|
||||
}
|
||||
pathp, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue