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