github.com/karrick/go@v0.0.0-20170817181416-d5b0ec858b37/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 file.name == DevNull {
    25  		return &devNullStat, nil
    26  	}
    27  
    28  	ft, err := file.pfd.GetFileType()
    29  	if err != nil {
    30  		return nil, &PathError{"GetFileType", file.name, 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  	var d syscall.ByHandleFileInformation
    38  	err = file.pfd.GetFileInformationByHandle(&d)
    39  	if err != nil {
    40  		return nil, &PathError{"GetFileInformationByHandle", file.name, err}
    41  	}
    42  	return &fileStat{
    43  		name: basename(file.name),
    44  		sys: syscall.Win32FileAttributeData{
    45  			FileAttributes: d.FileAttributes,
    46  			CreationTime:   d.CreationTime,
    47  			LastAccessTime: d.LastAccessTime,
    48  			LastWriteTime:  d.LastWriteTime,
    49  			FileSizeHigh:   d.FileSizeHigh,
    50  			FileSizeLow:    d.FileSizeLow,
    51  		},
    52  		filetype: ft,
    53  		vol:      d.VolumeSerialNumber,
    54  		idxhi:    d.FileIndexHigh,
    55  		idxlo:    d.FileIndexLow,
    56  	}, nil
    57  }
    58  
    59  // Stat returns a FileInfo structure describing the named file.
    60  // If there is an error, it will be of type *PathError.
    61  func Stat(name string) (FileInfo, error) {
    62  	if len(name) == 0 {
    63  		return nil, &PathError{"Stat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)}
    64  	}
    65  	if name == DevNull {
    66  		return &devNullStat, nil
    67  	}
    68  	namep, err := syscall.UTF16PtrFromString(fixLongPath(name))
    69  	if err != nil {
    70  		return nil, &PathError{"Stat", name, err}
    71  	}
    72  	// Apparently (see https://github.com/golang/go/issues/19922#issuecomment-300031421)
    73  	// GetFileAttributesEx is fastest approach to get file info.
    74  	// It does not work for symlinks. But symlinks are rare,
    75  	// so try GetFileAttributesEx first.
    76  	var fs fileStat
    77  	err = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fs.sys)))
    78  	if err == nil && fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
    79  		fs.path = name
    80  		if !isAbs(fs.path) {
    81  			fs.path, err = syscall.FullPath(fs.path)
    82  			if err != nil {
    83  				return nil, &PathError{"FullPath", name, err}
    84  			}
    85  		}
    86  		fs.name = basename(name)
    87  		return &fs, nil
    88  	}
    89  	// Use Windows I/O manager to dereference the symbolic link, as per
    90  	// https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/
    91  	h, err := syscall.CreateFile(namep, 0, 0, nil,
    92  		syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
    93  	if err != nil {
    94  		if err == windows.ERROR_SHARING_VIOLATION {
    95  			// try FindFirstFile now that CreateFile failed
    96  			return statWithFindFirstFile(name, namep)
    97  		}
    98  		return nil, &PathError{"CreateFile", name, err}
    99  	}
   100  	defer syscall.CloseHandle(h)
   101  
   102  	var d syscall.ByHandleFileInformation
   103  	err = syscall.GetFileInformationByHandle(h, &d)
   104  	if err != nil {
   105  		return nil, &PathError{"GetFileInformationByHandle", name, err}
   106  	}
   107  	return &fileStat{
   108  		name: basename(name),
   109  		sys: syscall.Win32FileAttributeData{
   110  			FileAttributes: d.FileAttributes,
   111  			CreationTime:   d.CreationTime,
   112  			LastAccessTime: d.LastAccessTime,
   113  			LastWriteTime:  d.LastWriteTime,
   114  			FileSizeHigh:   d.FileSizeHigh,
   115  			FileSizeLow:    d.FileSizeLow,
   116  		},
   117  		vol:   d.VolumeSerialNumber,
   118  		idxhi: d.FileIndexHigh,
   119  		idxlo: d.FileIndexLow,
   120  		// fileStat.path is used by os.SameFile to decide if it needs
   121  		// to fetch vol, idxhi and idxlo. But these are already set,
   122  		// so set fileStat.path to "" to prevent os.SameFile doing it again.
   123  		// Also do not set fileStat.filetype, because it is only used for
   124  		// console and stdin/stdout. But you cannot call os.Stat for these.
   125  	}, nil
   126  }
   127  
   128  // statWithFindFirstFile is used by Stat to handle special case of statting
   129  // c:\pagefile.sys. We might discover that other files need similar treatment.
   130  func statWithFindFirstFile(name string, namep *uint16) (FileInfo, error) {
   131  	var fd syscall.Win32finddata
   132  	h, err := syscall.FindFirstFile(namep, &fd)
   133  	if err != nil {
   134  		return nil, &PathError{"FindFirstFile", name, err}
   135  	}
   136  	syscall.FindClose(h)
   137  
   138  	fullpath := name
   139  	if !isAbs(fullpath) {
   140  		fullpath, err = syscall.FullPath(fullpath)
   141  		if err != nil {
   142  			return nil, &PathError{"FullPath", name, err}
   143  		}
   144  	}
   145  	return &fileStat{
   146  		name: basename(name),
   147  		path: fullpath,
   148  		sys: syscall.Win32FileAttributeData{
   149  			FileAttributes: fd.FileAttributes,
   150  			CreationTime:   fd.CreationTime,
   151  			LastAccessTime: fd.LastAccessTime,
   152  			LastWriteTime:  fd.LastWriteTime,
   153  			FileSizeHigh:   fd.FileSizeHigh,
   154  			FileSizeLow:    fd.FileSizeLow,
   155  		},
   156  	}, nil
   157  }
   158  
   159  // Lstat returns the FileInfo structure describing the named file.
   160  // If the file is a symbolic link, the returned FileInfo
   161  // describes the symbolic link. Lstat makes no attempt to follow the link.
   162  // If there is an error, it will be of type *PathError.
   163  func Lstat(name string) (FileInfo, error) {
   164  	if len(name) == 0 {
   165  		return nil, &PathError{"Lstat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)}
   166  	}
   167  	if name == DevNull {
   168  		return &devNullStat, nil
   169  	}
   170  	fs := &fileStat{name: basename(name)}
   171  	namep, e := syscall.UTF16PtrFromString(fixLongPath(name))
   172  	if e != nil {
   173  		return nil, &PathError{"Lstat", name, e}
   174  	}
   175  	e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fs.sys)))
   176  	if e != nil {
   177  		if e != windows.ERROR_SHARING_VIOLATION {
   178  			return nil, &PathError{"GetFileAttributesEx", name, e}
   179  		}
   180  		// try FindFirstFile now that GetFileAttributesEx failed
   181  		return statWithFindFirstFile(name, namep)
   182  	}
   183  	fs.path = name
   184  	if !isAbs(fs.path) {
   185  		fs.path, e = syscall.FullPath(fs.path)
   186  		if e != nil {
   187  			return nil, &PathError{"FullPath", name, e}
   188  		}
   189  	}
   190  	return fs, nil
   191  }