...

Source file src/os/types_windows.go

     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	
    51		var ti windows.FILE_ATTRIBUTE_TAG_INFO
    52		err = windows.GetFileInformationByHandleEx(h, windows.FileAttributeTagInfo, (*byte)(unsafe.Pointer(&ti)), uint32(unsafe.Sizeof(ti)))
    53		if err != nil {
    54			if errno, ok := err.(syscall.Errno); ok && errno == windows.ERROR_INVALID_PARAMETER {
    55				// It appears calling GetFileInformationByHandleEx with
    56				// FILE_ATTRIBUTE_TAG_INFO fails on FAT file system with
    57				// ERROR_INVALID_PARAMETER. Clear ti.ReparseTag in that
    58				// instance to indicate no symlinks are possible.
    59				ti.ReparseTag = 0
    60			} else {
    61				return nil, &PathError{"GetFileInformationByHandleEx", path, err}
    62			}
    63		}
    64	
    65		return &fileStat{
    66			name:           basename(path),
    67			FileAttributes: d.FileAttributes,
    68			CreationTime:   d.CreationTime,
    69			LastAccessTime: d.LastAccessTime,
    70			LastWriteTime:  d.LastWriteTime,
    71			FileSizeHigh:   d.FileSizeHigh,
    72			FileSizeLow:    d.FileSizeLow,
    73			vol:            d.VolumeSerialNumber,
    74			idxhi:          d.FileIndexHigh,
    75			idxlo:          d.FileIndexLow,
    76			Reserved0:      ti.ReparseTag,
    77			// fileStat.path is used by os.SameFile to decide if it needs
    78			// to fetch vol, idxhi and idxlo. But these are already set,
    79			// so set fileStat.path to "" to prevent os.SameFile doing it again.
    80		}, nil
    81	}
    82	
    83	// newFileStatFromWin32finddata copies all required information
    84	// from syscall.Win32finddata d into the newly created fileStat.
    85	func newFileStatFromWin32finddata(d *syscall.Win32finddata) *fileStat {
    86		return &fileStat{
    87			FileAttributes: d.FileAttributes,
    88			CreationTime:   d.CreationTime,
    89			LastAccessTime: d.LastAccessTime,
    90			LastWriteTime:  d.LastWriteTime,
    91			FileSizeHigh:   d.FileSizeHigh,
    92			FileSizeLow:    d.FileSizeLow,
    93			Reserved0:      d.Reserved0,
    94		}
    95	}
    96	
    97	func (fs *fileStat) isSymlink() bool {
    98		// Use instructions described at
    99		// https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/
   100		// to recognize whether it's a symlink.
   101		if fs.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
   102			return false
   103		}
   104		return fs.Reserved0 == syscall.IO_REPARSE_TAG_SYMLINK ||
   105			fs.Reserved0 == windows.IO_REPARSE_TAG_MOUNT_POINT
   106	}
   107	
   108	func (fs *fileStat) Size() int64 {
   109		return int64(fs.FileSizeHigh)<<32 + int64(fs.FileSizeLow)
   110	}
   111	
   112	func (fs *fileStat) Mode() (m FileMode) {
   113		if fs == &devNullStat {
   114			return ModeDevice | ModeCharDevice | 0666
   115		}
   116		if fs.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
   117			m |= 0444
   118		} else {
   119			m |= 0666
   120		}
   121		if fs.isSymlink() {
   122			return m | ModeSymlink
   123		}
   124		if fs.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
   125			m |= ModeDir | 0111
   126		}
   127		switch fs.filetype {
   128		case syscall.FILE_TYPE_PIPE:
   129			m |= ModeNamedPipe
   130		case syscall.FILE_TYPE_CHAR:
   131			m |= ModeDevice | ModeCharDevice
   132		}
   133		return m
   134	}
   135	
   136	func (fs *fileStat) ModTime() time.Time {
   137		return time.Unix(0, fs.LastWriteTime.Nanoseconds())
   138	}
   139	
   140	// Sys returns syscall.Win32FileAttributeData for file fs.
   141	func (fs *fileStat) Sys() interface{} {
   142		return &syscall.Win32FileAttributeData{
   143			FileAttributes: fs.FileAttributes,
   144			CreationTime:   fs.CreationTime,
   145			LastAccessTime: fs.LastAccessTime,
   146			LastWriteTime:  fs.LastWriteTime,
   147			FileSizeHigh:   fs.FileSizeHigh,
   148			FileSizeLow:    fs.FileSizeLow,
   149		}
   150	}
   151	
   152	func (fs *fileStat) loadFileId() error {
   153		fs.Lock()
   154		defer fs.Unlock()
   155		if fs.path == "" {
   156			// already done
   157			return nil
   158		}
   159		var path string
   160		if fs.appendNameToPath {
   161			path = fs.path + `\` + fs.name
   162		} else {
   163			path = fs.path
   164		}
   165		pathp, err := syscall.UTF16PtrFromString(path)
   166		if err != nil {
   167			return err
   168		}
   169		attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
   170		if fs.isSymlink() {
   171			// Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink.
   172			// See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted
   173			attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT
   174		}
   175		h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, attrs, 0)
   176		if err != nil {
   177			return err
   178		}
   179		defer syscall.CloseHandle(h)
   180		var i syscall.ByHandleFileInformation
   181		err = syscall.GetFileInformationByHandle(h, &i)
   182		if err != nil {
   183			return err
   184		}
   185		fs.path = ""
   186		fs.vol = i.VolumeSerialNumber
   187		fs.idxhi = i.FileIndexHigh
   188		fs.idxlo = i.FileIndexLow
   189		return nil
   190	}
   191	
   192	// saveInfoFromPath saves full path of the file to be used by os.SameFile later,
   193	// and set name from path.
   194	func (fs *fileStat) saveInfoFromPath(path string) error {
   195		fs.path = path
   196		if !isAbs(fs.path) {
   197			var err error
   198			fs.path, err = syscall.FullPath(fs.path)
   199			if err != nil {
   200				return &PathError{"FullPath", path, err}
   201			}
   202		}
   203		fs.name = basename(path)
   204		return nil
   205	}
   206	
   207	// devNullStat is fileStat structure describing DevNull file ("NUL").
   208	var devNullStat = fileStat{
   209		name: DevNull,
   210		// hopefully this will work for SameFile
   211		vol:   0,
   212		idxhi: 0,
   213		idxlo: 0,
   214	}
   215	
   216	func sameFile(fs1, fs2 *fileStat) bool {
   217		e := fs1.loadFileId()
   218		if e != nil {
   219			return false
   220		}
   221		e = fs2.loadFileId()
   222		if e != nil {
   223			return false
   224		}
   225		return fs1.vol == fs2.vol && fs1.idxhi == fs2.idxhi && fs1.idxlo == fs2.idxlo
   226	}
   227	
   228	// For testing.
   229	func atime(fi FileInfo) time.Time {
   230		return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds())
   231	}
   232	

View as plain text