Home | History | Annotate | Download | only in os
      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 }
    172