| // Copyright 2011 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package os |
| |
| import ( |
| "syscall" |
| "time" |
| ) |
| |
| const bitSize16 = 2 |
| |
| func fileInfoFromStat(d *syscall.Dir) *fileStat { |
| fs := &fileStat{ |
| name: d.Name, |
| size: d.Length, |
| modTime: time.Unix(int64(d.Mtime), 0), |
| sys: d, |
| } |
| fs.mode = FileMode(d.Mode & 0777) |
| if d.Mode&syscall.DMDIR != 0 { |
| fs.mode |= ModeDir |
| } |
| if d.Mode&syscall.DMAPPEND != 0 { |
| fs.mode |= ModeAppend |
| } |
| if d.Mode&syscall.DMEXCL != 0 { |
| fs.mode |= ModeExclusive |
| } |
| if d.Mode&syscall.DMTMP != 0 { |
| fs.mode |= ModeTemporary |
| } |
| // Consider all files not served by #M as device files. |
| if d.Type != 'M' { |
| fs.mode |= ModeDevice |
| } |
| // Consider all files served by #c as character device files. |
| if d.Type == 'c' { |
| fs.mode |= ModeCharDevice |
| } |
| return fs |
| } |
| |
| // arg is an open *File or a path string. |
| func dirstat(arg interface{}) (*syscall.Dir, error) { |
| var name string |
| var err error |
| |
| size := syscall.STATFIXLEN + 16*4 |
| |
| for i := 0; i < 2; i++ { |
| buf := make([]byte, bitSize16+size) |
| |
| var n int |
| switch a := arg.(type) { |
| case *File: |
| name = a.name |
| n, err = syscall.Fstat(a.fd, buf) |
| case string: |
| name = a |
| n, err = syscall.Stat(a, buf) |
| default: |
| panic("phase error in dirstat") |
| } |
| |
| if n < bitSize16 { |
| return nil, &PathError{Op: "stat", Path: name, Err: err} |
| } |
| |
| // Pull the real size out of the stat message. |
| size = int(uint16(buf[0]) | uint16(buf[1])<<8) |
| |
| // If the stat message is larger than our buffer we will |
| // go around the loop and allocate one that is big enough. |
| if size <= n { |
| d, err := syscall.UnmarshalDir(buf[:n]) |
| if err != nil { |
| return nil, &PathError{Op: "stat", Path: name, Err: err} |
| } |
| return d, nil |
| } |
| |
| } |
| |
| if err == nil { |
| err = syscall.ErrBadStat |
| } |
| |
| return nil, &PathError{Op: "stat", Path: name, Err: err} |
| } |
| |
| // statNolog implements Stat for Plan 9. |
| func statNolog(name string) (FileInfo, error) { |
| d, err := dirstat(name) |
| if err != nil { |
| return nil, err |
| } |
| return fileInfoFromStat(d), nil |
| } |
| |
| // lstatNolog implements Lstat for Plan 9. |
| func lstatNolog(name string) (FileInfo, error) { |
| return statNolog(name) |
| } |
| |
| // For testing. |
| func atime(fi FileInfo) time.Time { |
| return time.Unix(int64(fi.Sys().(*syscall.Dir).Atime), 0) |
| } |