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 	"sync"
      9 	"syscall"
     10 	"time"
     11 )
     12 
     13 // A fileStat is the implementation of FileInfo returned by Stat and Lstat.
     14 type fileStat struct {
     15 	name     string
     16 	sys      syscall.Win32FileAttributeData
     17 	filetype uint32 // what syscall.GetFileType returns
     18 
     19 	// used to implement SameFile
     20 	sync.Mutex
     21 	path             string
     22 	vol              uint32
     23 	idxhi            uint32
     24 	idxlo            uint32
     25 	appendNameToPath bool
     26 }
     27 
     28 func (fs *fileStat) Size() int64 {
     29 	return int64(fs.sys.FileSizeHigh)<<32 + int64(fs.sys.FileSizeLow)
     30 }
     31 
     32 func (fs *fileStat) Mode() (m FileMode) {
     33 	if fs == &devNullStat {
     34 		return ModeDevice | ModeCharDevice | 0666
     35 	}
     36 	if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
     37 		m |= 0444
     38 	} else {
     39 		m |= 0666
     40 	}
     41 	if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
     42 		return m | ModeSymlink
     43 	}
     44 	if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
     45 		m |= ModeDir | 0111
     46 	}
     47 	switch fs.filetype {
     48 	case syscall.FILE_TYPE_PIPE:
     49 		m |= ModeNamedPipe
     50 	case syscall.FILE_TYPE_CHAR:
     51 		m |= ModeCharDevice
     52 	}
     53 	return m
     54 }
     55 
     56 func (fs *fileStat) ModTime() time.Time {
     57 	return time.Unix(0, fs.sys.LastWriteTime.Nanoseconds())
     58 }
     59 
     60 // Sys returns syscall.Win32FileAttributeData for file fs.
     61 func (fs *fileStat) Sys() interface{} { return &fs.sys }
     62 
     63 func (fs *fileStat) loadFileId() error {
     64 	fs.Lock()
     65 	defer fs.Unlock()
     66 	if fs.path == "" {
     67 		// already done
     68 		return nil
     69 	}
     70 	var path string
     71 	if fs.appendNameToPath {
     72 		path = fs.path + `\` + fs.name
     73 	} else {
     74 		path = fs.path
     75 	}
     76 	pathp, err := syscall.UTF16PtrFromString(path)
     77 	if err != nil {
     78 		return err
     79 	}
     80 	h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
     81 	if err != nil {
     82 		return err
     83 	}
     84 	defer syscall.CloseHandle(h)
     85 	var i syscall.ByHandleFileInformation
     86 	err = syscall.GetFileInformationByHandle(h, &i)
     87 	if err != nil {
     88 		return err
     89 	}
     90 	fs.path = ""
     91 	fs.vol = i.VolumeSerialNumber
     92 	fs.idxhi = i.FileIndexHigh
     93 	fs.idxlo = i.FileIndexLow
     94 	return nil
     95 }
     96 
     97 // devNullStat is fileStat structure describing DevNull file ("NUL").
     98 var devNullStat = fileStat{
     99 	name: DevNull,
    100 	// hopefully this will work for SameFile
    101 	vol:   0,
    102 	idxhi: 0,
    103 	idxlo: 0,
    104 }
    105 
    106 func sameFile(fs1, fs2 *fileStat) bool {
    107 	e := fs1.loadFileId()
    108 	if e != nil {
    109 		return false
    110 	}
    111 	e = fs2.loadFileId()
    112 	if e != nil {
    113 		return false
    114 	}
    115 	return fs1.vol == fs2.vol && fs1.idxhi == fs2.idxhi && fs1.idxlo == fs2.idxlo
    116 }
    117 
    118 // For testing.
    119 func atime(fi FileInfo) time.Time {
    120 	return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds())
    121 }
    122