diff options
Diffstat (limited to 'src/os/stat_windows.go')
-rw-r--r-- | src/os/stat_windows.go | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/src/os/stat_windows.go b/src/os/stat_windows.go new file mode 100644 index 0000000..668255f --- /dev/null +++ b/src/os/stat_windows.go @@ -0,0 +1,136 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os + +import ( + "internal/syscall/windows" + "syscall" + "unsafe" +) + +// Stat returns the FileInfo structure describing file. +// If there is an error, it will be of type *PathError. +func (file *File) Stat() (FileInfo, error) { + if file == nil { + return nil, ErrInvalid + } + return statHandle(file.name, file.pfd.Sysfd) +} + +// stat implements both Stat and Lstat of a file. +func stat(funcname, name string, followSurrogates bool) (FileInfo, error) { + if len(name) == 0 { + return nil, &PathError{Op: funcname, Path: name, Err: syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)} + } + namep, err := syscall.UTF16PtrFromString(fixLongPath(name)) + if err != nil { + return nil, &PathError{Op: funcname, Path: name, Err: err} + } + + // Try GetFileAttributesEx first, because it is faster than CreateFile. + // See https://golang.org/issues/19922#issuecomment-300031421 for details. + var fa syscall.Win32FileAttributeData + err = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa))) + + // GetFileAttributesEx fails with ERROR_SHARING_VIOLATION error for + // files like c:\pagefile.sys. Use FindFirstFile for such files. + if err == windows.ERROR_SHARING_VIOLATION { + var fd syscall.Win32finddata + sh, err := syscall.FindFirstFile(namep, &fd) + if err != nil { + return nil, &PathError{Op: "FindFirstFile", Path: name, Err: err} + } + syscall.FindClose(sh) + if fd.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { + // Not a surrogate for another named entity. FindFirstFile is good enough. + fs := newFileStatFromWin32finddata(&fd) + if err := fs.saveInfoFromPath(name); err != nil { + return nil, err + } + return fs, nil + } + } + + if err == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { + // Not a surrogate for another named entity, because it isn't any kind of reparse point. + // The information we got from GetFileAttributesEx is good enough for now. + fs := &fileStat{ + FileAttributes: fa.FileAttributes, + CreationTime: fa.CreationTime, + LastAccessTime: fa.LastAccessTime, + LastWriteTime: fa.LastWriteTime, + FileSizeHigh: fa.FileSizeHigh, + FileSizeLow: fa.FileSizeLow, + } + if err := fs.saveInfoFromPath(name); err != nil { + return nil, err + } + return fs, nil + } + + // Use CreateFile to determine whether the file is a name surrogate and, if so, + // save information about the link target. + // Set FILE_FLAG_BACKUP_SEMANTICS so that CreateFile will create the handle + // even if name refers to a directory. + h, err := syscall.CreateFile(namep, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0) + if err != nil { + // Since CreateFile failed, we can't determine whether name refers to a + // name surrogate, or some other kind of reparse point. Since we can't return a + // FileInfo with a known-accurate Mode, we must return an error. + return nil, &PathError{Op: "CreateFile", Path: name, Err: err} + } + + fi, err := statHandle(name, h) + syscall.CloseHandle(h) + if err == nil && followSurrogates && fi.(*fileStat).isReparseTagNameSurrogate() { + // To obtain information about the link target, we reopen the file without + // FILE_FLAG_OPEN_REPARSE_POINT and examine the resulting handle. + // (See https://devblogs.microsoft.com/oldnewthing/20100212-00/?p=14963.) + h, err = syscall.CreateFile(namep, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) + if err != nil { + // name refers to a symlink, but we couldn't resolve the symlink target. + return nil, &PathError{Op: "CreateFile", Path: name, Err: err} + } + defer syscall.CloseHandle(h) + return statHandle(name, h) + } + return fi, err +} + +func statHandle(name string, h syscall.Handle) (FileInfo, error) { + ft, err := syscall.GetFileType(h) + if err != nil { + return nil, &PathError{Op: "GetFileType", Path: name, Err: err} + } + switch ft { + case syscall.FILE_TYPE_PIPE, syscall.FILE_TYPE_CHAR: + return &fileStat{name: basename(name), filetype: ft}, nil + } + fs, err := newFileStatFromGetFileInformationByHandle(name, h) + if err != nil { + return nil, err + } + fs.filetype = ft + return fs, err +} + +// statNolog implements Stat for Windows. +func statNolog(name string) (FileInfo, error) { + return stat("Stat", name, true) +} + +// lstatNolog implements Lstat for Windows. +func lstatNolog(name string) (FileInfo, error) { + followSurrogates := false + if name != "" && IsPathSeparator(name[len(name)-1]) { + // We try to implement POSIX semantics for Lstat path resolution + // (per https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/basedefs/V1_chap04.html#tag_04_12): + // symlinks before the last separator in the path must be resolved. Since + // the last separator in this case follows the last path element, we should + // follow symlinks in the last path element. + followSurrogates = true + } + return stat("Lstat", name, followSurrogates) +} |