summaryrefslogtreecommitdiffstats
path: root/src/os/stat_windows.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
commitccd992355df7192993c666236047820244914598 (patch)
treef00fea65147227b7743083c6148396f74cd66935 /src/os/stat_windows.go
parentInitial commit. (diff)
downloadgolang-1.21-ccd992355df7192993c666236047820244914598.tar.xz
golang-1.21-ccd992355df7192993c666236047820244914598.zip
Adding upstream version 1.21.8.upstream/1.21.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/os/stat_windows.go')
-rw-r--r--src/os/stat_windows.go136
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..033c3b9
--- /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, followSymlinks 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 symlink or mount point. 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 {
+ // The file is definitely not a symlink, 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 symlink 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
+ // symlink, 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 && followSymlinks && fi.(*fileStat).isSymlink() {
+ // 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) {
+ followSymlinks := 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.
+ followSymlinks = true
+ }
+ return stat("Lstat", name, followSymlinks)
+}