...
Run Format

Source file src/os/types_windows.go

Documentation: os

     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  	"sync"
    10  	"syscall"
    11  	"time"
    12  	"unsafe"
    13  )
    14  
    15  // A fileStat is the implementation of FileInfo returned by Stat and Lstat.
    16  type fileStat struct {
    17  	name string
    18  
    19  	// from ByHandleFileInformation, Win32FileAttributeData and Win32finddata
    20  	FileAttributes uint32
    21  	CreationTime   syscall.Filetime
    22  	LastAccessTime syscall.Filetime
    23  	LastWriteTime  syscall.Filetime
    24  	FileSizeHigh   uint32
    25  	FileSizeLow    uint32
    26  
    27  	// from Win32finddata
    28  	Reserved0 uint32
    29  
    30  	// what syscall.GetFileType returns
    31  	filetype uint32
    32  
    33  	// used to implement SameFile
    34  	sync.Mutex
    35  	path             string
    36  	vol              uint32
    37  	idxhi            uint32
    38  	idxlo            uint32
    39  	appendNameToPath bool
    40  }
    41  
    42  // newFileStatFromGetFileInformationByHandle calls GetFileInformationByHandle
    43  // to gather all required information about the file handle h.
    44  func newFileStatFromGetFileInformationByHandle(path string, h syscall.Handle) (fs *fileStat, err error) {
    45  	var d syscall.ByHandleFileInformation
    46  	err = syscall.GetFileInformationByHandle(h, &d)
    47  	if err != nil {
    48  		return nil, &PathError{"GetFileInformationByHandle", path, err}
    49  	}
    50  	return &fileStat{
    51  		name:           basename(path),
    52  		FileAttributes: d.FileAttributes,
    53  		CreationTime:   d.CreationTime,
    54  		LastAccessTime: d.LastAccessTime,
    55  		LastWriteTime:  d.LastWriteTime,
    56  		FileSizeHigh:   d.FileSizeHigh,
    57  		FileSizeLow:    d.FileSizeLow,
    58  		vol:            d.VolumeSerialNumber,
    59  		idxhi:          d.FileIndexHigh,
    60  		idxlo:          d.FileIndexLow,
    61  		// fileStat.path is used by os.SameFile to decide if it needs
    62  		// to fetch vol, idxhi and idxlo. But these are already set,
    63  		// so set fileStat.path to "" to prevent os.SameFile doing it again.
    64  	}, nil
    65  }
    66  
    67  // newFileStatFromWin32finddata copies all required information
    68  // from syscall.Win32finddata d into the newly created fileStat.
    69  func newFileStatFromWin32finddata(d *syscall.Win32finddata) *fileStat {
    70  	return &fileStat{
    71  		FileAttributes: d.FileAttributes,
    72  		CreationTime:   d.CreationTime,
    73  		LastAccessTime: d.LastAccessTime,
    74  		LastWriteTime:  d.LastWriteTime,
    75  		FileSizeHigh:   d.FileSizeHigh,
    76  		FileSizeLow:    d.FileSizeLow,
    77  		Reserved0:      d.Reserved0,
    78  	}
    79  }
    80  
    81  // newFileStatFromGetFileAttributesExOrFindFirstFile calls GetFileAttributesEx
    82  // and FindFirstFile to gather all required information about the provided file path pathp.
    83  func newFileStatFromGetFileAttributesExOrFindFirstFile(path string, pathp *uint16) (*fileStat, error) {
    84  	// As suggested by Microsoft, use GetFileAttributes() to acquire the file information,
    85  	// and if it's a reparse point use FindFirstFile() to get the tag:
    86  	// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363940(v=vs.85).aspx
    87  	// Notice that always calling FindFirstFile can create performance problems
    88  	// (https://golang.org/issues/19922#issuecomment-300031421)
    89  	var fa syscall.Win32FileAttributeData
    90  	err := syscall.GetFileAttributesEx(pathp, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa)))
    91  	if err == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
    92  		// Not a symlink.
    93  		return &fileStat{
    94  			FileAttributes: fa.FileAttributes,
    95  			CreationTime:   fa.CreationTime,
    96  			LastAccessTime: fa.LastAccessTime,
    97  			LastWriteTime:  fa.LastWriteTime,
    98  			FileSizeHigh:   fa.FileSizeHigh,
    99  			FileSizeLow:    fa.FileSizeLow,
   100  		}, nil
   101  	}
   102  	// GetFileAttributesEx returns ERROR_INVALID_NAME if called
   103  	// for invalid file name like "*.txt". Do not attempt to call
   104  	// FindFirstFile with "*.txt", because FindFirstFile will
   105  	// succeed. So just return ERROR_INVALID_NAME instead.
   106  	// see https://golang.org/issue/24999 for details.
   107  	if errno, _ := err.(syscall.Errno); errno == windows.ERROR_INVALID_NAME {
   108  		return nil, &PathError{"GetFileAttributesEx", path, err}
   109  	}
   110  	// We might have symlink here. But some directories also have
   111  	// FileAttributes FILE_ATTRIBUTE_REPARSE_POINT bit set.
   112  	// For example, OneDrive directory is like that
   113  	// (see golang.org/issue/22579 for details).
   114  	// So use FindFirstFile instead to distinguish directories like
   115  	// OneDrive from real symlinks (see instructions described at
   116  	// https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/
   117  	// and in particular bits about using both FileAttributes and
   118  	// Reserved0 fields).
   119  	var fd syscall.Win32finddata
   120  	sh, err := syscall.FindFirstFile(pathp, &fd)
   121  	if err != nil {
   122  		return nil, &PathError{"FindFirstFile", path, err}
   123  	}
   124  	syscall.FindClose(sh)
   125  
   126  	return newFileStatFromWin32finddata(&fd), nil
   127  }
   128  
   129  func (fs *fileStat) updatePathAndName(name string) error {
   130  	fs.path = name
   131  	if !isAbs(fs.path) {
   132  		var err error
   133  		fs.path, err = syscall.FullPath(fs.path)
   134  		if err != nil {
   135  			return &PathError{"FullPath", name, err}
   136  		}
   137  	}
   138  	fs.name = basename(name)
   139  	return nil
   140  }
   141  
   142  func (fs *fileStat) isSymlink() bool {
   143  	// Use instructions described at
   144  	// https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/
   145  	// to recognize whether it's a symlink.
   146  	if fs.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
   147  		return false
   148  	}
   149  	return fs.Reserved0 == syscall.IO_REPARSE_TAG_SYMLINK ||
   150  		fs.Reserved0 == windows.IO_REPARSE_TAG_MOUNT_POINT
   151  }
   152  
   153  func (fs *fileStat) Size() int64 {
   154  	return int64(fs.FileSizeHigh)<<32 + int64(fs.FileSizeLow)
   155  }
   156  
   157  func (fs *fileStat) Mode() (m FileMode) {
   158  	if fs == &devNullStat {
   159  		return ModeDevice | ModeCharDevice | 0666
   160  	}
   161  	if fs.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
   162  		m |= 0444
   163  	} else {
   164  		m |= 0666
   165  	}
   166  	if fs.isSymlink() {
   167  		return m | ModeSymlink
   168  	}
   169  	if fs.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
   170  		m |= ModeDir | 0111
   171  	}
   172  	switch fs.filetype {
   173  	case syscall.FILE_TYPE_PIPE:
   174  		m |= ModeNamedPipe
   175  	case syscall.FILE_TYPE_CHAR:
   176  		m |= ModeDevice | ModeCharDevice
   177  	}
   178  	return m
   179  }
   180  
   181  func (fs *fileStat) ModTime() time.Time {
   182  	return time.Unix(0, fs.LastWriteTime.Nanoseconds())
   183  }
   184  
   185  // Sys returns syscall.Win32FileAttributeData for file fs.
   186  func (fs *fileStat) Sys() interface{} {
   187  	return &syscall.Win32FileAttributeData{
   188  		FileAttributes: fs.FileAttributes,
   189  		CreationTime:   fs.CreationTime,
   190  		LastAccessTime: fs.LastAccessTime,
   191  		LastWriteTime:  fs.LastWriteTime,
   192  		FileSizeHigh:   fs.FileSizeHigh,
   193  		FileSizeLow:    fs.FileSizeLow,
   194  	}
   195  }
   196  
   197  func (fs *fileStat) loadFileId() error {
   198  	fs.Lock()
   199  	defer fs.Unlock()
   200  	if fs.path == "" {
   201  		// already done
   202  		return nil
   203  	}
   204  	var path string
   205  	if fs.appendNameToPath {
   206  		path = fs.path + `\` + fs.name
   207  	} else {
   208  		path = fs.path
   209  	}
   210  	pathp, err := syscall.UTF16PtrFromString(path)
   211  	if err != nil {
   212  		return err
   213  	}
   214  	h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
   215  	if err != nil {
   216  		return err
   217  	}
   218  	defer syscall.CloseHandle(h)
   219  	var i syscall.ByHandleFileInformation
   220  	err = syscall.GetFileInformationByHandle(h, &i)
   221  	if err != nil {
   222  		return err
   223  	}
   224  	fs.path = ""
   225  	fs.vol = i.VolumeSerialNumber
   226  	fs.idxhi = i.FileIndexHigh
   227  	fs.idxlo = i.FileIndexLow
   228  	return nil
   229  }
   230  
   231  // devNullStat is fileStat structure describing DevNull file ("NUL").
   232  var devNullStat = fileStat{
   233  	name: DevNull,
   234  	// hopefully this will work for SameFile
   235  	vol:   0,
   236  	idxhi: 0,
   237  	idxlo: 0,
   238  }
   239  
   240  func sameFile(fs1, fs2 *fileStat) bool {
   241  	e := fs1.loadFileId()
   242  	if e != nil {
   243  		return false
   244  	}
   245  	e = fs2.loadFileId()
   246  	if e != nil {
   247  		return false
   248  	}
   249  	return fs1.vol == fs2.vol && fs1.idxhi == fs2.idxhi && fs1.idxlo == fs2.idxlo
   250  }
   251  
   252  // For testing.
   253  func atime(fi FileInfo) time.Time {
   254  	return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds())
   255  }
   256  

View as plain text