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