| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package os | |
| import ( | |
| "syscall" | |
| ) | |
| // isNulName returns true if name is NUL file name. | |
| // For example, it returns true for both "NUL" and "nul". | |
| func isNulName(name string) bool { | |
| if len(name) != 3 { | |
| return false | |
| } | |
| if name[0] != 'n' && name[0] != 'N' { | |
| return false | |
| } | |
| if name[1] != 'u' && name[1] != 'U' { | |
| return false | |
| } | |
| if name[2] != 'l' && name[2] != 'L' { | |
| return false | |
| } | |
| return true | |
| } | |
| // Stat returns the FileInfo structure describing file. | |
| // If there is an error, it will be of type *PathError. | |
| func (file *File) Stat() (FileInfo, error) { | |
| if file == nil { | |
| return nil, ErrInvalid | |
| } | |
| if file.isdir() { | |
| // I don't know any better way to do that for directory | |
| return Stat(file.dirinfo.path) | |
| } | |
| if isNulName(file.name) { | |
| return &devNullStat, nil | |
| } | |
| ft, err := file.pfd.GetFileType() | |
| if err != nil { | |
| return nil, &PathError{"GetFileType", file.name, err} | |
| } | |
| switch ft { | |
| case syscall.FILE_TYPE_PIPE, syscall.FILE_TYPE_CHAR: | |
| return &fileStat{name: basename(file.name), filetype: ft}, nil | |
| } | |
| fs, err := newFileStatFromGetFileInformationByHandle(file.name, file.pfd.Sysfd) | |
| if err != nil { | |
| return nil, err | |
| } | |
| fs.filetype = ft | |
| return fs, err | |
| } | |
| // statNolog implements Stat for Windows. | |
| func statNolog(name string) (FileInfo, error) { | |
| if len(name) == 0 { | |
| return nil, &PathError{"Stat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)} | |
| } | |
| if isNulName(name) { | |
| return &devNullStat, nil | |
| } | |
| namep, err := syscall.UTF16PtrFromString(fixLongPath(name)) | |
| if err != nil { | |
| return nil, &PathError{"Stat", name, err} | |
| } | |
| fs, err := newFileStatFromGetFileAttributesExOrFindFirstFile(name, namep) | |
| if err != nil { | |
| return nil, err | |
| } | |
| if !fs.isSymlink() { | |
| err = fs.updatePathAndName(name) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return fs, nil | |
| } | |
| // Use Windows I/O manager to dereference the symbolic link, as per | |
| // https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/ | |
| h, err := syscall.CreateFile(namep, 0, 0, nil, | |
| syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) | |
| if err != nil { | |
| return nil, &PathError{"CreateFile", name, err} | |
| } | |
| defer syscall.CloseHandle(h) | |
| return newFileStatFromGetFileInformationByHandle(name, h) | |
| } | |
| // lstatNolog implements Lstat for Windows. | |
| func lstatNolog(name string) (FileInfo, error) { | |
| if len(name) == 0 { | |
| return nil, &PathError{"Lstat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)} | |
| } | |
| if isNulName(name) { | |
| return &devNullStat, nil | |
| } | |
| namep, err := syscall.UTF16PtrFromString(fixLongPath(name)) | |
| if err != nil { | |
| return nil, &PathError{"Lstat", name, err} | |
| } | |
| fs, err := newFileStatFromGetFileAttributesExOrFindFirstFile(name, namep) | |
| if err != nil { | |
| return nil, err | |
| } | |
| err = fs.updatePathAndName(name) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return fs, nil | |
| } |