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