github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/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  // 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  			path:           name,
    84  			FileAttributes: fa.FileAttributes,
    85  			CreationTime:   fa.CreationTime,
    86  			LastAccessTime: fa.LastAccessTime,
    87  			LastWriteTime:  fa.LastWriteTime,
    88  			FileSizeHigh:   fa.FileSizeHigh,
    89  			FileSizeLow:    fa.FileSizeLow,
    90  		}
    91  		// Gather full path to be used by os.SameFile later.
    92  		if !isAbs(fs.path) {
    93  			fs.path, err = syscall.FullPath(fs.path)
    94  			if err != nil {
    95  				return nil, &PathError{"FullPath", name, err}
    96  			}
    97  		}
    98  		fs.name = basename(name)
    99  		return fs, nil
   100  	}
   101  	// GetFileAttributesEx fails with ERROR_SHARING_VIOLATION error for
   102  	// files, like c:\pagefile.sys. Use FindFirstFile for such files.
   103  	if err == windows.ERROR_SHARING_VIOLATION {
   104  		var fd syscall.Win32finddata
   105  		sh, err := syscall.FindFirstFile(namep, &fd)
   106  		if err != nil {
   107  			return nil, &PathError{"FindFirstFile", name, err}
   108  		}
   109  		syscall.FindClose(sh)
   110  		return newFileStatFromWin32finddata(&fd), nil
   111  	}
   112  
   113  	// Finally use CreateFile.
   114  	h, err := syscall.CreateFile(namep, 0, 0, nil,
   115  		syscall.OPEN_EXISTING, createFileAttrs, 0)
   116  	if err != nil {
   117  		return nil, &PathError{"CreateFile", name, err}
   118  	}
   119  	defer syscall.CloseHandle(h)
   120  
   121  	return newFileStatFromGetFileInformationByHandle(name, h)
   122  }
   123  
   124  // statNolog implements Stat for Windows.
   125  func statNolog(name string) (FileInfo, error) {
   126  	return stat("Stat", name, syscall.FILE_FLAG_BACKUP_SEMANTICS)
   127  }
   128  
   129  // lstatNolog implements Lstat for Windows.
   130  func lstatNolog(name string) (FileInfo, error) {
   131  	attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
   132  	// Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink.
   133  	// See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted
   134  	attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT
   135  	return stat("Lstat", name, attrs)
   136  }