github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/os/stat_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 "syscall" 10 "unsafe" 11 ) 12 13 // Stat returns the FileInfo structure describing file. 14 // If there is an error, it will be of type *PathError. 15 func (file *File) Stat() (FileInfo, error) { 16 if file == nil { 17 return nil, ErrInvalid 18 } 19 return statHandle(file.name, file.pfd.Sysfd) 20 } 21 22 // stat implements both Stat and Lstat of a file. 23 func stat(funcname, name string, followSymlinks bool) (FileInfo, error) { 24 if len(name) == 0 { 25 return nil, &PathError{Op: funcname, Path: name, Err: syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)} 26 } 27 namep, err := syscall.UTF16PtrFromString(fixLongPath(name)) 28 if err != nil { 29 return nil, &PathError{Op: funcname, Path: name, Err: err} 30 } 31 32 // Try GetFileAttributesEx first, because it is faster than CreateFile. 33 // See https://golang.org/issues/19922#issuecomment-300031421 for details. 34 var fa syscall.Win32FileAttributeData 35 err = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa))) 36 37 // GetFileAttributesEx fails with ERROR_SHARING_VIOLATION error for 38 // files like c:\pagefile.sys. Use FindFirstFile for such files. 39 if err == windows.ERROR_SHARING_VIOLATION { 40 var fd syscall.Win32finddata 41 sh, err := syscall.FindFirstFile(namep, &fd) 42 if err != nil { 43 return nil, &PathError{Op: "FindFirstFile", Path: name, Err: err} 44 } 45 syscall.FindClose(sh) 46 if fd.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { 47 // Not a symlink or mount point. FindFirstFile is good enough. 48 fs := newFileStatFromWin32finddata(&fd) 49 if err := fs.saveInfoFromPath(name); err != nil { 50 return nil, err 51 } 52 return fs, nil 53 } 54 } 55 56 if err == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { 57 // The file is definitely not a symlink, because it isn't any kind of reparse point. 58 // The information we got from GetFileAttributesEx is good enough for now. 59 fs := &fileStat{ 60 FileAttributes: fa.FileAttributes, 61 CreationTime: fa.CreationTime, 62 LastAccessTime: fa.LastAccessTime, 63 LastWriteTime: fa.LastWriteTime, 64 FileSizeHigh: fa.FileSizeHigh, 65 FileSizeLow: fa.FileSizeLow, 66 } 67 if err := fs.saveInfoFromPath(name); err != nil { 68 return nil, err 69 } 70 return fs, nil 71 } 72 73 // Use CreateFile to determine whether the file is a symlink and, if so, 74 // save information about the link target. 75 // Set FILE_FLAG_BACKUP_SEMANTICS so that CreateFile will create the handle 76 // even if name refers to a directory. 77 h, err := syscall.CreateFile(namep, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0) 78 if err != nil { 79 // Since CreateFile failed, we can't determine whether name refers to a 80 // symlink, or some other kind of reparse point. Since we can't return a 81 // FileInfo with a known-accurate Mode, we must return an error. 82 return nil, &PathError{Op: "CreateFile", Path: name, Err: err} 83 } 84 85 fi, err := statHandle(name, h) 86 syscall.CloseHandle(h) 87 if err == nil && followSymlinks && fi.(*fileStat).isSymlink() { 88 // To obtain information about the link target, we reopen the file without 89 // FILE_FLAG_OPEN_REPARSE_POINT and examine the resulting handle. 90 // (See https://devblogs.microsoft.com/oldnewthing/20100212-00/?p=14963.) 91 h, err = syscall.CreateFile(namep, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) 92 if err != nil { 93 // name refers to a symlink, but we couldn't resolve the symlink target. 94 return nil, &PathError{Op: "CreateFile", Path: name, Err: err} 95 } 96 defer syscall.CloseHandle(h) 97 return statHandle(name, h) 98 } 99 return fi, err 100 } 101 102 func statHandle(name string, h syscall.Handle) (FileInfo, error) { 103 ft, err := syscall.GetFileType(h) 104 if err != nil { 105 return nil, &PathError{Op: "GetFileType", Path: name, Err: err} 106 } 107 switch ft { 108 case syscall.FILE_TYPE_PIPE, syscall.FILE_TYPE_CHAR: 109 return &fileStat{name: basename(name), filetype: ft}, nil 110 } 111 fs, err := newFileStatFromGetFileInformationByHandle(name, h) 112 if err != nil { 113 return nil, err 114 } 115 fs.filetype = ft 116 return fs, err 117 } 118 119 // statNolog implements Stat for Windows. 120 func statNolog(name string) (FileInfo, error) { 121 return stat("Stat", name, true) 122 } 123 124 // lstatNolog implements Lstat for Windows. 125 func lstatNolog(name string) (FileInfo, error) { 126 followSymlinks := false 127 if name != "" && IsPathSeparator(name[len(name)-1]) { 128 // We try to implement POSIX semantics for Lstat path resolution 129 // (per https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/basedefs/V1_chap04.html#tag_04_12): 130 // symlinks before the last separator in the path must be resolved. Since 131 // the last separator in this case follows the last path element, we should 132 // follow symlinks in the last path element. 133 followSymlinks = true 134 } 135 return stat("Lstat", name, followSymlinks) 136 }