github.com/4ad/go@v0.0.0-20161219182952-69a12818b605/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  	"syscall"
     9  	"unsafe"
    10  )
    11  
    12  // Stat returns the FileInfo structure describing file.
    13  // If there is an error, it will be of type *PathError.
    14  func (file *File) Stat() (FileInfo, error) {
    15  	if file == nil {
    16  		return nil, ErrInvalid
    17  	}
    18  	if file == nil || file.fd < 0 {
    19  		return nil, syscall.EINVAL
    20  	}
    21  	if file.isdir() {
    22  		// I don't know any better way to do that for directory
    23  		return Stat(file.dirinfo.path)
    24  	}
    25  	if file.name == DevNull {
    26  		return &devNullStat, nil
    27  	}
    28  
    29  	ft, err := syscall.GetFileType(file.fd)
    30  	if err != nil {
    31  		return nil, &PathError{"GetFileType", file.name, err}
    32  	}
    33  	if ft == syscall.FILE_TYPE_PIPE {
    34  		return &fileStat{name: basename(file.name), pipe: true}, nil
    35  	}
    36  
    37  	var d syscall.ByHandleFileInformation
    38  	err = syscall.GetFileInformationByHandle(file.fd, &d)
    39  	if err != nil {
    40  		return nil, &PathError{"GetFileInformationByHandle", file.name, err}
    41  	}
    42  	return &fileStat{
    43  		name: basename(file.name),
    44  		sys: syscall.Win32FileAttributeData{
    45  			FileAttributes: d.FileAttributes,
    46  			CreationTime:   d.CreationTime,
    47  			LastAccessTime: d.LastAccessTime,
    48  			LastWriteTime:  d.LastWriteTime,
    49  			FileSizeHigh:   d.FileSizeHigh,
    50  			FileSizeLow:    d.FileSizeLow,
    51  		},
    52  		vol:   d.VolumeSerialNumber,
    53  		idxhi: d.FileIndexHigh,
    54  		idxlo: d.FileIndexLow,
    55  		pipe:  false,
    56  	}, nil
    57  }
    58  
    59  // Stat returns a FileInfo structure describing the named file.
    60  // If there is an error, it will be of type *PathError.
    61  func Stat(name string) (FileInfo, error) {
    62  	var fi FileInfo
    63  	var err error
    64  	for {
    65  		fi, err = Lstat(name)
    66  		if err != nil {
    67  			return fi, err
    68  		}
    69  		if fi.Mode()&ModeSymlink == 0 {
    70  			return fi, nil
    71  		}
    72  		name, err = Readlink(name)
    73  		if err != nil {
    74  			return fi, err
    75  		}
    76  	}
    77  }
    78  
    79  // Lstat returns the FileInfo structure describing the named file.
    80  // If the file is a symbolic link, the returned FileInfo
    81  // describes the symbolic link. Lstat makes no attempt to follow the link.
    82  // If there is an error, it will be of type *PathError.
    83  func Lstat(name string) (FileInfo, error) {
    84  	if len(name) == 0 {
    85  		return nil, &PathError{"Lstat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)}
    86  	}
    87  	if name == DevNull {
    88  		return &devNullStat, nil
    89  	}
    90  	fs := &fileStat{name: basename(name)}
    91  	namep, e := syscall.UTF16PtrFromString(name)
    92  	if e != nil {
    93  		return nil, &PathError{"Lstat", name, e}
    94  	}
    95  	e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fs.sys)))
    96  	if e != nil {
    97  		return nil, &PathError{"GetFileAttributesEx", name, e}
    98  	}
    99  	fs.path = name
   100  	if !isAbs(fs.path) {
   101  		fs.path, e = syscall.FullPath(fs.path)
   102  		if e != nil {
   103  			return nil, e
   104  		}
   105  	}
   106  	return fs, nil
   107  }
   108  
   109  // basename removes trailing slashes and the leading
   110  // directory name and drive letter from path name.
   111  func basename(name string) string {
   112  	// Remove drive letter
   113  	if len(name) == 2 && name[1] == ':' {
   114  		name = "."
   115  	} else if len(name) > 2 && name[1] == ':' {
   116  		name = name[2:]
   117  	}
   118  	i := len(name) - 1
   119  	// Remove trailing slashes
   120  	for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i-- {
   121  		name = name[:i]
   122  	}
   123  	// Remove leading directory name
   124  	for i--; i >= 0; i-- {
   125  		if name[i] == '/' || name[i] == '\\' {
   126  			name = name[i+1:]
   127  			break
   128  		}
   129  	}
   130  	return name
   131  }
   132  
   133  func isAbs(path string) (b bool) {
   134  	v := volumeName(path)
   135  	if v == "" {
   136  		return false
   137  	}
   138  	path = path[len(v):]
   139  	if path == "" {
   140  		return false
   141  	}
   142  	return IsPathSeparator(path[0])
   143  }
   144  
   145  func volumeName(path string) (v string) {
   146  	if len(path) < 2 {
   147  		return ""
   148  	}
   149  	// with drive letter
   150  	c := path[0]
   151  	if path[1] == ':' &&
   152  		('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
   153  			'A' <= c && c <= 'Z') {
   154  		return path[:2]
   155  	}
   156  	// is it UNC
   157  	if l := len(path); l >= 5 && IsPathSeparator(path[0]) && IsPathSeparator(path[1]) &&
   158  		!IsPathSeparator(path[2]) && path[2] != '.' {
   159  		// first, leading `\\` and next shouldn't be `\`. its server name.
   160  		for n := 3; n < l-1; n++ {
   161  			// second, next '\' shouldn't be repeated.
   162  			if IsPathSeparator(path[n]) {
   163  				n++
   164  				// third, following something characters. its share name.
   165  				if !IsPathSeparator(path[n]) {
   166  					if path[n] == '.' {
   167  						break
   168  					}
   169  					for ; n < l; n++ {
   170  						if IsPathSeparator(path[n]) {
   171  							break
   172  						}
   173  					}
   174  					return path[:n]
   175  				}
   176  				break
   177  			}
   178  		}
   179  	}
   180  	return ""
   181  }