github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/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 20 if file.isdir() { 21 // I don't know any better way to do that for directory 22 return Stat(file.dirinfo.path) 23 } 24 if isWindowsNulName(file.name) { 25 return &devNullStat, nil 26 } 27 28 ft, err := file.pfd.GetFileType() 29 if err != nil { 30 return nil, &PathError{Op: "GetFileType", Path: file.name, Err: err} 31 } 32 switch ft { 33 case syscall.FILE_TYPE_PIPE, syscall.FILE_TYPE_CHAR: 34 return &fileStat{name: basename(file.name), filetype: ft}, nil 35 } 36 37 fs, err := newFileStatFromGetFileInformationByHandle(file.name, file.pfd.Sysfd) 38 if err != nil { 39 return nil, err 40 } 41 fs.filetype = ft 42 return fs, err 43 } 44 45 // stat implements both Stat and Lstat of a file. 46 func stat(funcname, name string, createFileAttrs uint32) (FileInfo, error) { 47 if len(name) == 0 { 48 return nil, &PathError{Op: funcname, Path: name, Err: syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)} 49 } 50 if isWindowsNulName(name) { 51 return &devNullStat, nil 52 } 53 namep, err := syscall.UTF16PtrFromString(fixLongPath(name)) 54 if err != nil { 55 return nil, &PathError{Op: funcname, Path: name, Err: err} 56 } 57 58 // Try GetFileAttributesEx first, because it is faster than CreateFile. 59 // See https://golang.org/issues/19922#issuecomment-300031421 for details. 60 var fa syscall.Win32FileAttributeData 61 err = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa))) 62 if err == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { 63 // Not a symlink. 64 fs := &fileStat{ 65 FileAttributes: fa.FileAttributes, 66 CreationTime: fa.CreationTime, 67 LastAccessTime: fa.LastAccessTime, 68 LastWriteTime: fa.LastWriteTime, 69 FileSizeHigh: fa.FileSizeHigh, 70 FileSizeLow: fa.FileSizeLow, 71 } 72 if err := fs.saveInfoFromPath(name); err != nil { 73 return nil, err 74 } 75 return fs, nil 76 } 77 // GetFileAttributesEx fails with ERROR_SHARING_VIOLATION error for 78 // files, like c:\pagefile.sys. Use FindFirstFile for such files. 79 if err == windows.ERROR_SHARING_VIOLATION { 80 var fd syscall.Win32finddata 81 sh, err := syscall.FindFirstFile(namep, &fd) 82 if err != nil { 83 return nil, &PathError{Op: "FindFirstFile", Path: name, Err: err} 84 } 85 syscall.FindClose(sh) 86 fs := newFileStatFromWin32finddata(&fd) 87 if err := fs.saveInfoFromPath(name); err != nil { 88 return nil, err 89 } 90 return fs, nil 91 } 92 93 // Finally use CreateFile. 94 h, err := syscall.CreateFile(namep, 0, 0, nil, 95 syscall.OPEN_EXISTING, createFileAttrs, 0) 96 if err != nil { 97 return nil, &PathError{Op: "CreateFile", Path: name, Err: err} 98 } 99 defer syscall.CloseHandle(h) 100 101 return newFileStatFromGetFileInformationByHandle(name, h) 102 } 103 104 // statNolog implements Stat for Windows. 105 func statNolog(name string) (FileInfo, error) { 106 return stat("Stat", name, syscall.FILE_FLAG_BACKUP_SEMANTICS) 107 } 108 109 // lstatNolog implements Lstat for Windows. 110 func lstatNolog(name string) (FileInfo, error) { 111 attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS) 112 // Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink. 113 // See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted 114 attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT 115 return stat("Lstat", name, attrs) 116 }