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