...

Source file src/pkg/os/stat_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		"syscall"
    10		"unsafe"
    11	)
    12	
    13	// isNulName reports whether name is NUL file name.
    14	// For example, it returns true for both "NUL" and "nul".
    15	func isNulName(name string) bool {
    16		if len(name) != 3 {
    17			return false
    18		}
    19		if name[0] != 'n' && name[0] != 'N' {
    20			return false
    21		}
    22		if name[1] != 'u' && name[1] != 'U' {
    23			return false
    24		}
    25		if name[2] != 'l' && name[2] != 'L' {
    26			return false
    27		}
    28		return true
    29	}
    30	
    31	// Stat returns the FileInfo structure describing file.
    32	// If there is an error, it will be of type *PathError.
    33	func (file *File) Stat() (FileInfo, error) {
    34		if file == nil {
    35			return nil, ErrInvalid
    36		}
    37	
    38		if file.isdir() {
    39			// I don't know any better way to do that for directory
    40			return Stat(file.dirinfo.path)
    41		}
    42		if isNulName(file.name) {
    43			return &devNullStat, nil
    44		}
    45	
    46		ft, err := file.pfd.GetFileType()
    47		if err != nil {
    48			return nil, &PathError{"GetFileType", file.name, err}
    49		}
    50		switch ft {
    51		case syscall.FILE_TYPE_PIPE, syscall.FILE_TYPE_CHAR:
    52			return &fileStat{name: basename(file.name), filetype: ft}, nil
    53		}
    54	
    55		fs, err := newFileStatFromGetFileInformationByHandle(file.name, file.pfd.Sysfd)
    56		if err != nil {
    57			return nil, err
    58		}
    59		fs.filetype = ft
    60		return fs, err
    61	}
    62	
    63	// stat implements both Stat and Lstat of a file.
    64	func stat(funcname, name string, createFileAttrs uint32) (FileInfo, error) {
    65		if len(name) == 0 {
    66			return nil, &PathError{funcname, name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)}
    67		}
    68		if isNulName(name) {
    69			return &devNullStat, nil
    70		}
    71		namep, err := syscall.UTF16PtrFromString(fixLongPath(name))
    72		if err != nil {
    73			return nil, &PathError{funcname, name, err}
    74		}
    75	
    76		// Try GetFileAttributesEx first, because it is faster than CreateFile.
    77		// See https://golang.org/issues/19922#issuecomment-300031421 for details.
    78		var fa syscall.Win32FileAttributeData
    79		err = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa)))
    80		if err == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
    81			// Not a symlink.
    82			fs := &fileStat{
    83				FileAttributes: fa.FileAttributes,
    84				CreationTime:   fa.CreationTime,
    85				LastAccessTime: fa.LastAccessTime,
    86				LastWriteTime:  fa.LastWriteTime,
    87				FileSizeHigh:   fa.FileSizeHigh,
    88				FileSizeLow:    fa.FileSizeLow,
    89			}
    90			if err := fs.saveInfoFromPath(name); err != nil {
    91				return nil, err
    92			}
    93			return fs, nil
    94		}
    95		// GetFileAttributesEx fails with ERROR_SHARING_VIOLATION error for
    96		// files, like c:\pagefile.sys. Use FindFirstFile for such files.
    97		if err == windows.ERROR_SHARING_VIOLATION {
    98			var fd syscall.Win32finddata
    99			sh, err := syscall.FindFirstFile(namep, &fd)
   100			if err != nil {
   101				return nil, &PathError{"FindFirstFile", name, err}
   102			}
   103			syscall.FindClose(sh)
   104			fs := newFileStatFromWin32finddata(&fd)
   105			if err := fs.saveInfoFromPath(name); err != nil {
   106				return nil, err
   107			}
   108			return fs, nil
   109		}
   110	
   111		// Finally use CreateFile.
   112		h, err := syscall.CreateFile(namep, 0, 0, nil,
   113			syscall.OPEN_EXISTING, createFileAttrs, 0)
   114		if err != nil {
   115			return nil, &PathError{"CreateFile", name, err}
   116		}
   117		defer syscall.CloseHandle(h)
   118	
   119		return newFileStatFromGetFileInformationByHandle(name, h)
   120	}
   121	
   122	// statNolog implements Stat for Windows.
   123	func statNolog(name string) (FileInfo, error) {
   124		return stat("Stat", name, syscall.FILE_FLAG_BACKUP_SEMANTICS)
   125	}
   126	
   127	// lstatNolog implements Lstat for Windows.
   128	func lstatNolog(name string) (FileInfo, error) {
   129		attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
   130		// Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink.
   131		// See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted
   132		attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT
   133		return stat("Lstat", name, attrs)
   134	}
   135	

View as plain text