// 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) }