2010-04-02 02:11:17 -06:00
|
|
|
// 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
|
|
|
|
|
2011-09-05 17:59:08 -06:00
|
|
|
import (
|
2018-10-19 23:30:57 -06:00
|
|
|
"internal/syscall/windows"
|
2011-09-05 17:59:08 -06:00
|
|
|
"syscall"
|
2018-10-19 23:30:57 -06:00
|
|
|
"unsafe"
|
2011-09-05 17:59:08 -06:00
|
|
|
)
|
2010-04-02 02:11:17 -06:00
|
|
|
|
2011-09-05 17:59:08 -06:00
|
|
|
// Stat returns the FileInfo structure describing file.
|
2012-02-08 22:55:36 -07:00
|
|
|
// If there is an error, it will be of type *PathError.
|
2015-07-17 09:26:29 -06:00
|
|
|
func (file *File) Stat() (FileInfo, error) {
|
2013-08-19 22:33:03 -06:00
|
|
|
if file == nil {
|
|
|
|
return nil, ErrInvalid
|
|
|
|
}
|
2017-07-20 01:20:46 -06:00
|
|
|
|
2011-09-05 17:59:08 -06:00
|
|
|
if file.isdir() {
|
|
|
|
// I don't know any better way to do that for directory
|
2015-12-28 05:35:48 -07:00
|
|
|
return Stat(file.dirinfo.path)
|
2011-09-05 17:59:08 -06:00
|
|
|
}
|
2019-07-14 22:32:40 -06:00
|
|
|
if isWindowsNulName(file.name) {
|
2013-01-30 23:17:37 -07:00
|
|
|
return &devNullStat, nil
|
2012-03-14 23:33:45 -06:00
|
|
|
}
|
2016-03-17 22:00:26 -06:00
|
|
|
|
2017-02-10 16:17:38 -07:00
|
|
|
ft, err := file.pfd.GetFileType()
|
2016-03-17 22:00:26 -06:00
|
|
|
if err != nil {
|
2020-07-03 10:25:49 -06:00
|
|
|
return nil, &PathError{Op: "GetFileType", Path: file.name, Err: err}
|
2016-03-17 22:00:26 -06:00
|
|
|
}
|
2016-12-06 16:49:45 -07:00
|
|
|
switch ft {
|
|
|
|
case syscall.FILE_TYPE_PIPE, syscall.FILE_TYPE_CHAR:
|
|
|
|
return &fileStat{name: basename(file.name), filetype: ft}, nil
|
2016-03-17 22:00:26 -06:00
|
|
|
}
|
|
|
|
|
2018-01-06 18:12:25 -07:00
|
|
|
fs, err := newFileStatFromGetFileInformationByHandle(file.name, file.pfd.Sysfd)
|
2016-03-17 22:00:26 -06:00
|
|
|
if err != nil {
|
2018-01-06 18:12:25 -07:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
fs.filetype = ft
|
|
|
|
return fs, err
|
2010-04-27 00:17:14 -06:00
|
|
|
}
|
|
|
|
|
2018-10-19 23:30:57 -06:00
|
|
|
// stat implements both Stat and Lstat of a file.
|
|
|
|
func stat(funcname, name string, createFileAttrs uint32) (FileInfo, error) {
|
2017-04-27 01:43:33 -06:00
|
|
|
if len(name) == 0 {
|
2020-07-03 10:25:49 -06:00
|
|
|
return nil, &PathError{Op: funcname, Path: name, Err: syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)}
|
2017-04-27 01:43:33 -06:00
|
|
|
}
|
2019-07-14 22:32:40 -06:00
|
|
|
if isWindowsNulName(name) {
|
2017-04-27 01:43:33 -06:00
|
|
|
return &devNullStat, nil
|
|
|
|
}
|
|
|
|
namep, err := syscall.UTF16PtrFromString(fixLongPath(name))
|
|
|
|
if err != nil {
|
2020-07-03 10:25:49 -06:00
|
|
|
return nil, &PathError{Op: funcname, Path: name, Err: err}
|
2017-04-27 01:43:33 -06:00
|
|
|
}
|
2018-10-19 23:30:57 -06:00
|
|
|
|
|
|
|
// 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)))
|
|
|
|
if err == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
|
|
|
// Not a symlink.
|
|
|
|
fs := &fileStat{
|
|
|
|
FileAttributes: fa.FileAttributes,
|
|
|
|
CreationTime: fa.CreationTime,
|
|
|
|
LastAccessTime: fa.LastAccessTime,
|
|
|
|
LastWriteTime: fa.LastWriteTime,
|
|
|
|
FileSizeHigh: fa.FileSizeHigh,
|
|
|
|
FileSizeLow: fa.FileSizeLow,
|
|
|
|
}
|
2019-03-16 22:56:29 -06:00
|
|
|
if err := fs.saveInfoFromPath(name); err != nil {
|
|
|
|
return nil, err
|
2018-10-19 23:30:57 -06:00
|
|
|
}
|
|
|
|
return fs, nil
|
2018-01-06 18:12:25 -07:00
|
|
|
}
|
2018-10-19 23:30:57 -06:00
|
|
|
// 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)
|
2018-01-06 18:12:25 -07:00
|
|
|
if err != nil {
|
2020-07-03 10:25:49 -06:00
|
|
|
return nil, &PathError{Op: "FindFirstFile", Path: name, Err: err}
|
2017-05-09 00:50:41 -06:00
|
|
|
}
|
2018-10-19 23:30:57 -06:00
|
|
|
syscall.FindClose(sh)
|
2019-03-16 22:56:29 -06:00
|
|
|
fs := newFileStatFromWin32finddata(&fd)
|
|
|
|
if err := fs.saveInfoFromPath(name); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return fs, nil
|
2017-05-09 00:50:41 -06:00
|
|
|
}
|
2018-10-19 23:30:57 -06:00
|
|
|
|
|
|
|
// Finally use CreateFile.
|
2017-04-27 01:43:33 -06:00
|
|
|
h, err := syscall.CreateFile(namep, 0, 0, nil,
|
2018-10-19 23:30:57 -06:00
|
|
|
syscall.OPEN_EXISTING, createFileAttrs, 0)
|
2017-04-27 01:43:33 -06:00
|
|
|
if err != nil {
|
2020-07-03 10:25:49 -06:00
|
|
|
return nil, &PathError{Op: "CreateFile", Path: name, Err: err}
|
2017-04-27 01:43:33 -06:00
|
|
|
}
|
|
|
|
defer syscall.CloseHandle(h)
|
|
|
|
|
2018-01-06 18:12:25 -07:00
|
|
|
return newFileStatFromGetFileInformationByHandle(name, h)
|
2014-07-17 01:02:46 -06:00
|
|
|
}
|
|
|
|
|
2018-10-19 23:30:57 -06:00
|
|
|
// statNolog implements Stat for Windows.
|
|
|
|
func statNolog(name string) (FileInfo, error) {
|
|
|
|
return stat("Stat", name, syscall.FILE_FLAG_BACKUP_SEMANTICS)
|
|
|
|
}
|
|
|
|
|
2017-12-11 17:41:37 -07:00
|
|
|
// lstatNolog implements Lstat for Windows.
|
|
|
|
func lstatNolog(name string) (FileInfo, error) {
|
2018-10-19 23:30:57 -06:00
|
|
|
attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
|
|
|
|
// Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink.
|
|
|
|
// See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted
|
|
|
|
attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT
|
|
|
|
return stat("Lstat", name, attrs)
|
2010-04-27 00:17:14 -06:00
|
|
|
}
|