1
0
mirror of https://github.com/golang/go synced 2024-11-21 17:04:42 -07:00

os: new FileInfo, FileMode types + update tree

R=golang-dev, r, r, gri, bradfitz, iant, iant, nigeltao, n13m3y3r
CC=golang-dev
https://golang.org/cl/5416060
This commit is contained in:
Russ Cox 2011-11-30 12:04:16 -05:00
parent 03823b881c
commit 8dce57e169
50 changed files with 620 additions and 525 deletions

View File

@ -410,12 +410,12 @@ func (b *Builder) envvWindows() []string {
func isDirectory(name string) bool { func isDirectory(name string) bool {
s, err := os.Stat(name) s, err := os.Stat(name)
return err == nil && s.IsDirectory() return err == nil && s.IsDir()
} }
func isFile(name string) bool { func isFile(name string) bool {
s, err := os.Stat(name) s, err := os.Stat(name)
return err == nil && (s.IsRegular() || s.IsSymlink()) return err == nil && !s.IsDir()
} }
// commitWatcher polls hg for new commits and tells the dashboard about them. // commitWatcher polls hg for new commits and tells the dashboard about them.

View File

@ -81,11 +81,11 @@ func (b *Builder) buildPackages(workpath string, hash string) error {
return nil return nil
} }
func isGoFile(fi *os.FileInfo) bool { func isGoFile(fi os.FileInfo) bool {
return fi.IsRegular() && // exclude directories return !fi.IsDir() && // exclude directories
!strings.HasPrefix(fi.Name, ".") && // ignore .files !strings.HasPrefix(fi.Name(), ".") && // ignore .files
!strings.HasSuffix(fi.Name, "_test.go") && // ignore tests !strings.HasSuffix(fi.Name(), "_test.go") && // ignore tests
filepath.Ext(fi.Name) == ".go" filepath.Ext(fi.Name()) == ".go"
} }
func packageComment(pkg, pkgpath string) (info string, err error) { func packageComment(pkg, pkgpath string) (info string, err error) {

View File

@ -41,7 +41,7 @@ func codewalk(w http.ResponseWriter, r *http.Request) {
// If directory exists, serve list of code walks. // If directory exists, serve list of code walks.
dir, err := fs.Lstat(abspath) dir, err := fs.Lstat(abspath)
if err == nil && dir.IsDirectory() { if err == nil && dir.IsDir() {
codewalkDir(w, r, relpath, abspath) codewalkDir(w, r, relpath, abspath)
return return
} }
@ -186,7 +186,7 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string
var v []interface{} var v []interface{}
for _, fi := range dir { for _, fi := range dir {
name := fi.Name() name := fi.Name()
if fi.IsDirectory() { if fi.IsDir() {
v = append(v, &elem{name + "/", ""}) v = append(v, &elem{name + "/", ""})
} else if strings.HasSuffix(name, ".xml") { } else if strings.HasSuffix(name, ".xml") {
cw, err := loadCodewalk(abspath + "/" + name) cw, err := loadCodewalk(abspath + "/" + name)

View File

@ -12,6 +12,7 @@ import (
"go/parser" "go/parser"
"go/token" "go/token"
"log" "log"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"unicode" "unicode"
@ -25,21 +26,21 @@ type Directory struct {
Dirs []*Directory // subdirectories Dirs []*Directory // subdirectories
} }
func isGoFile(fi FileInfo) bool { func isGoFile(fi os.FileInfo) bool {
name := fi.Name() name := fi.Name()
return fi.IsRegular() && return !fi.IsDir() &&
len(name) > 0 && name[0] != '.' && // ignore .files len(name) > 0 && name[0] != '.' && // ignore .files
filepath.Ext(name) == ".go" filepath.Ext(name) == ".go"
} }
func isPkgFile(fi FileInfo) bool { func isPkgFile(fi os.FileInfo) bool {
return isGoFile(fi) && return isGoFile(fi) &&
!strings.HasSuffix(fi.Name(), "_test.go") // ignore test files !strings.HasSuffix(fi.Name(), "_test.go") // ignore test files
} }
func isPkgDir(fi FileInfo) bool { func isPkgDir(fi os.FileInfo) bool {
name := fi.Name() name := fi.Name()
return fi.IsDirectory() && len(name) > 0 && return fi.IsDir() && len(name) > 0 &&
name[0] != '_' && name[0] != '.' // ignore _files and .files name[0] != '_' && name[0] != '.' // ignore _files and .files
} }

View File

@ -13,25 +13,15 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"time"
) )
// The FileInfo interface provides access to file information.
type FileInfo interface {
Name() string
Size() int64
ModTime() time.Time
IsRegular() bool
IsDirectory() bool
}
// The FileSystem interface specifies the methods godoc is using // The FileSystem interface specifies the methods godoc is using
// to access the file system for which it serves documentation. // to access the file system for which it serves documentation.
type FileSystem interface { type FileSystem interface {
Open(path string) (io.ReadCloser, error) Open(path string) (io.ReadCloser, error)
Lstat(path string) (FileInfo, error) Lstat(path string) (os.FileInfo, error)
Stat(path string) (FileInfo, error) Stat(path string) (os.FileInfo, error)
ReadDir(path string) ([]FileInfo, error) ReadDir(path string) ([]os.FileInfo, error)
} }
// ReadFile reads the file named by path from fs and returns the contents. // ReadFile reads the file named by path from fs and returns the contents.
@ -49,26 +39,6 @@ func ReadFile(fs FileSystem, path string) ([]byte, error) {
var OS FileSystem = osFS{} var OS FileSystem = osFS{}
// osFI is the OS-specific implementation of FileInfo.
type osFI struct {
*os.FileInfo
}
func (fi osFI) Name() string {
return fi.FileInfo.Name
}
func (fi osFI) Size() int64 {
if fi.IsDirectory() {
return 0
}
return fi.FileInfo.Size
}
func (fi osFI) ModTime() time.Time {
return fi.FileInfo.ModTime
}
// osFS is the OS-specific implementation of FileSystem // osFS is the OS-specific implementation of FileSystem
type osFS struct{} type osFS struct{}
@ -81,30 +51,20 @@ func (osFS) Open(path string) (io.ReadCloser, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if fi.IsDirectory() { if fi.IsDir() {
return nil, fmt.Errorf("Open: %s is a directory", path) return nil, fmt.Errorf("Open: %s is a directory", path)
} }
return f, nil return f, nil
} }
func (osFS) Lstat(path string) (FileInfo, error) { func (osFS) Lstat(path string) (os.FileInfo, error) {
fi, err := os.Lstat(path) return os.Lstat(path)
return osFI{fi}, err
} }
func (osFS) Stat(path string) (FileInfo, error) { func (osFS) Stat(path string) (os.FileInfo, error) {
fi, err := os.Stat(path) return os.Stat(path)
return osFI{fi}, err
} }
func (osFS) ReadDir(path string) ([]FileInfo, error) { func (osFS) ReadDir(path string) ([]os.FileInfo, error) {
l0, err := ioutil.ReadDir(path) // l0 is sorted return ioutil.ReadDir(path) // is sorted
if err != nil {
return nil, err
}
l1 := make([]FileInfo, len(l0))
for i, e := range l0 {
l1[i] = osFI{e}
}
return l1, nil
} }

View File

@ -381,15 +381,15 @@ func filenameFunc(path string) string {
return localname return localname
} }
func fileInfoNameFunc(fi FileInfo) string { func fileInfoNameFunc(fi os.FileInfo) string {
name := fi.Name() name := fi.Name()
if fi.IsDirectory() { if fi.IsDir() {
name += "/" name += "/"
} }
return name return name
} }
func fileInfoTimeFunc(fi FileInfo) string { func fileInfoTimeFunc(fi os.FileInfo) string {
if t := fi.ModTime(); t.Unix() != 0 { if t := fi.ModTime(); t.Unix() != 0 {
return t.Local().String() return t.Local().String()
} }
@ -789,7 +789,7 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
return return
} }
if dir != nil && dir.IsDirectory() { if dir != nil && dir.IsDir() {
if redirect(w, r) { if redirect(w, r) {
return return
} }
@ -894,22 +894,8 @@ type httpHandler struct {
} }
// fsReadDir implements ReadDir for the go/build package. // fsReadDir implements ReadDir for the go/build package.
func fsReadDir(dir string) ([]*os.FileInfo, error) { func fsReadDir(dir string) ([]os.FileInfo, error) {
fi, err := fs.ReadDir(dir) return fs.ReadDir(dir)
if err != nil {
return nil, err
}
// Convert []FileInfo to []*os.FileInfo.
osfi := make([]*os.FileInfo, len(fi))
for i, f := range fi {
mode := uint32(S_IFREG)
if f.IsDirectory() {
mode = S_IFDIR
}
osfi[i] = &os.FileInfo{Name: f.Name(), Size: f.Size(), ModTime: f.ModTime(), Mode: mode}
}
return osfi, nil
} }
// fsReadFile implements ReadFile for the go/build package. // fsReadFile implements ReadFile for the go/build package.
@ -969,7 +955,7 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf
} }
// filter function to select the desired .go files // filter function to select the desired .go files
filter := func(d FileInfo) bool { filter := func(d os.FileInfo) bool {
// Only Go files. // Only Go files.
if !isPkgFile(d) { if !isPkgFile(d) {
return false return false
@ -1048,7 +1034,7 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf
// get examples from *_test.go files // get examples from *_test.go files
var examples []*doc.Example var examples []*doc.Example
filter = func(d FileInfo) bool { filter = func(d os.FileInfo) bool {
return isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go") return isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go")
} }
if testpkgs, err := parseDir(fset, abspath, filter); err != nil { if testpkgs, err := parseDir(fset, abspath, filter); err != nil {

View File

@ -35,13 +35,18 @@ import (
"time" "time"
) )
// We cannot import syscall on app engine. type fileInfo struct {
// TODO(gri) Once we have a truly abstract FileInfo implementation name string
// this won't be needed anymore. mode os.FileMode
const ( size int64
S_IFDIR = 0x4000 // == syscall.S_IFDIR mtime time.Time
S_IFREG = 0x8000 // == syscall.S_IFREG }
)
func (fi *fileInfo) Name() string { return fi.name }
func (fi *fileInfo) Mode() os.FileMode { return fi.mode }
func (fi *fileInfo) Size() int64 { return fi.size }
func (fi *fileInfo) ModTime() time.Time { return fi.mtime }
func (fi *fileInfo) IsDir() bool { return fi.mode.IsDir() }
// httpZipFile is the zip-file based implementation of http.File // httpZipFile is the zip-file based implementation of http.File
type httpZipFile struct { type httpZipFile struct {
@ -52,15 +57,15 @@ type httpZipFile struct {
} }
func (f *httpZipFile) Close() error { func (f *httpZipFile) Close() error {
if f.info.IsRegular() { if !f.info.IsDir() {
return f.ReadCloser.Close() return f.ReadCloser.Close()
} }
f.list = nil f.list = nil
return nil return nil
} }
func (f *httpZipFile) Stat() (*os.FileInfo, error) { func (f *httpZipFile) Stat() (os.FileInfo, error) {
return &f.info, nil return f.info, nil
} }
func (f *httpZipFile) Readdir(count int) ([]os.FileInfo, error) { func (f *httpZipFile) Readdir(count int) ([]os.FileInfo, error) {
@ -77,17 +82,17 @@ func (f *httpZipFile) Readdir(count int) ([]os.FileInfo, error) {
break // not in the same directory anymore break // not in the same directory anymore
} }
name := e.Name[len(dirname):] // local name name := e.Name[len(dirname):] // local name
var mode uint32 var mode os.FileMode
var size int64 var size int64
var mtime time.Time var mtime time.Time
if i := strings.IndexRune(name, '/'); i >= 0 { if i := strings.IndexRune(name, '/'); i >= 0 {
// We infer directories from files in subdirectories. // We infer directories from files in subdirectories.
// If we have x/y, return a directory entry for x. // If we have x/y, return a directory entry for x.
name = name[0:i] // keep local directory name only name = name[0:i] // keep local directory name only
mode = S_IFDIR mode = os.ModeDir
// no size or mtime for directories // no size or mtime for directories
} else { } else {
mode = S_IFREG mode = 0
size = int64(e.UncompressedSize) size = int64(e.UncompressedSize)
mtime = e.ModTime() mtime = e.ModTime()
} }
@ -96,11 +101,11 @@ func (f *httpZipFile) Readdir(count int) ([]os.FileInfo, error) {
// by determining the (fs.list) range of local directory entries // by determining the (fs.list) range of local directory entries
// (via two binary searches). // (via two binary searches).
if name != prevname { if name != prevname {
list = append(list, os.FileInfo{ list = append(list, &fileInfo{
Name: name, name,
Mode: mode, mode,
Size: size, size,
ModTime: mtime, mtime,
}) })
prevname = name prevname = name
count-- count--
@ -115,7 +120,7 @@ func (f *httpZipFile) Readdir(count int) ([]os.FileInfo, error) {
} }
func (f *httpZipFile) Seek(offset int64, whence int) (int64, error) { func (f *httpZipFile) Seek(offset int64, whence int) (int64, error) {
return 0, fmt.Errorf("Seek not implemented for zip file entry: %s", f.info.Name) return 0, fmt.Errorf("Seek not implemented for zip file entry: %s", f.info.Name())
} }
// httpZipFS is the zip-file based implementation of http.FileSystem // httpZipFS is the zip-file based implementation of http.FileSystem
@ -143,11 +148,11 @@ func (fs *httpZipFS) Open(name string) (http.File, error) {
} }
return &httpZipFile{ return &httpZipFile{
path, path,
os.FileInfo{ &fileInfo{
Name: name, name,
Mode: S_IFREG, 0,
Size: int64(f.UncompressedSize), int64(f.UncompressedSize),
ModTime: f.ModTime(), f.ModTime(),
}, },
rc, rc,
nil, nil,
@ -157,10 +162,11 @@ func (fs *httpZipFS) Open(name string) (http.File, error) {
// not an exact match - must be a directory // not an exact match - must be a directory
return &httpZipFile{ return &httpZipFile{
path, path,
os.FileInfo{ &fileInfo{
Name: name, name,
Mode: S_IFDIR, os.ModeDir,
// no size or mtime for directories 0, // no size for directory
time.Time{}, // no mtime for directory
}, },
nil, nil,
fs.list[index:], fs.list[index:],

View File

@ -48,6 +48,7 @@ import (
"go/token" "go/token"
"index/suffixarray" "index/suffixarray"
"io" "io"
"os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"sort" "sort"
@ -701,8 +702,8 @@ func isWhitelisted(filename string) bool {
return whitelisted[key] return whitelisted[key]
} }
func (x *Indexer) visitFile(dirname string, f FileInfo, fulltextIndex bool) { func (x *Indexer) visitFile(dirname string, f os.FileInfo, fulltextIndex bool) {
if !f.IsRegular() { if f.IsDir() {
return return
} }
@ -781,7 +782,7 @@ func NewIndex(dirnames <-chan string, fulltextIndex bool, throttle float64) *Ind
continue // ignore this directory continue // ignore this directory
} }
for _, f := range list { for _, f := range list {
if !f.IsDirectory() { if !f.IsDir() {
x.visitFile(dirname, f, fulltextIndex) x.visitFile(dirname, f, fulltextIndex)
} }
th.Throttle() th.Throttle()

View File

@ -13,6 +13,7 @@ import (
"go/ast" "go/ast"
"go/parser" "go/parser"
"go/token" "go/token"
"os"
"path/filepath" "path/filepath"
) )
@ -47,7 +48,7 @@ func parseFiles(fset *token.FileSet, filenames []string) (pkgs map[string]*ast.P
return return
} }
func parseDir(fset *token.FileSet, path string, filter func(FileInfo) bool) (map[string]*ast.Package, error) { func parseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool) (map[string]*ast.Package, error) {
list, err := fs.ReadDir(path) list, err := fs.ReadDir(path)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -22,6 +22,7 @@ import (
"archive/zip" "archive/zip"
"fmt" "fmt"
"io" "io"
"os"
"path" "path"
"sort" "sort"
"strings" "strings"
@ -52,12 +53,16 @@ func (fi zipFI) ModTime() time.Time {
return time.Time{} // directory has no modified time entry return time.Time{} // directory has no modified time entry
} }
func (fi zipFI) IsDirectory() bool { func (fi zipFI) Mode() os.FileMode {
return fi.file == nil if fi.file == nil {
// Unix directories typically are executable, hence 555.
return os.ModeDir | 0555
}
return 0444
} }
func (fi zipFI) IsRegular() bool { func (fi zipFI) IsDir() bool {
return fi.file != nil return fi.file == nil
} }
// zipFS is the zip-file based implementation of FileSystem // zipFS is the zip-file based implementation of FileSystem
@ -98,33 +103,33 @@ func (fs *zipFS) Open(abspath string) (io.ReadCloser, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if fi.IsDirectory() { if fi.IsDir() {
return nil, fmt.Errorf("Open: %s is a directory", abspath) return nil, fmt.Errorf("Open: %s is a directory", abspath)
} }
return fi.file.Open() return fi.file.Open()
} }
func (fs *zipFS) Lstat(abspath string) (FileInfo, error) { func (fs *zipFS) Lstat(abspath string) (os.FileInfo, error) {
_, fi, err := fs.stat(zipPath(abspath)) _, fi, err := fs.stat(zipPath(abspath))
return fi, err return fi, err
} }
func (fs *zipFS) Stat(abspath string) (FileInfo, error) { func (fs *zipFS) Stat(abspath string) (os.FileInfo, error) {
_, fi, err := fs.stat(zipPath(abspath)) _, fi, err := fs.stat(zipPath(abspath))
return fi, err return fi, err
} }
func (fs *zipFS) ReadDir(abspath string) ([]FileInfo, error) { func (fs *zipFS) ReadDir(abspath string) ([]os.FileInfo, error) {
path := zipPath(abspath) path := zipPath(abspath)
i, fi, err := fs.stat(path) i, fi, err := fs.stat(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !fi.IsDirectory() { if !fi.IsDir() {
return nil, fmt.Errorf("ReadDir: %s is not a directory", abspath) return nil, fmt.Errorf("ReadDir: %s is not a directory", abspath)
} }
var list []FileInfo var list []os.FileInfo
dirname := path + "/" dirname := path + "/"
prevname := "" prevname := ""
for _, e := range fs.list[i:] { for _, e := range fs.list[i:] {

View File

@ -82,12 +82,12 @@ func main() {
switch dir, err := os.Stat(path); { switch dir, err := os.Stat(path); {
case err != nil: case err != nil:
report(err) report(err)
case dir.IsRegular(): case dir.IsDir():
walkDir(path)
default:
if err := processFile(path, false); err != nil { if err := processFile(path, false); err != nil {
report(err) report(err)
} }
case dir.IsDirectory():
walkDir(path)
} }
} }
@ -219,7 +219,7 @@ func walkDir(path string) {
filepath.Walk(path, visitFile) filepath.Walk(path, visitFile)
} }
func visitFile(path string, f *os.FileInfo, err error) error { func visitFile(path string, f os.FileInfo, err error) error {
if err == nil && isGoFile(f) { if err == nil && isGoFile(f) {
err = processFile(path, false) err = processFile(path, false)
} }
@ -229,9 +229,10 @@ func visitFile(path string, f *os.FileInfo, err error) error {
return nil return nil
} }
func isGoFile(f *os.FileInfo) bool { func isGoFile(f os.FileInfo) bool {
// ignore non-Go files // ignore non-Go files
return f.IsRegular() && !strings.HasPrefix(f.Name, ".") && strings.HasSuffix(f.Name, ".go") name := f.Name()
return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
} }
func diff(b1, b2 []byte) (data []byte, err error) { func diff(b1, b2 []byte) (data []byte, err error) {

View File

@ -80,9 +80,10 @@ func initPrinterMode() {
} }
} }
func isGoFile(f *os.FileInfo) bool { func isGoFile(f os.FileInfo) bool {
// ignore non-Go files // ignore non-Go files
return f.IsRegular() && !strings.HasPrefix(f.Name, ".") && strings.HasSuffix(f.Name, ".go") name := f.Name()
return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
} }
// If in == nil, the source is the contents of the file with the given filename. // If in == nil, the source is the contents of the file with the given filename.
@ -158,7 +159,7 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error
return err return err
} }
func visitFile(path string, f *os.FileInfo, err error) error { func visitFile(path string, f os.FileInfo, err error) error {
if err == nil && isGoFile(f) { if err == nil && isGoFile(f) {
err = processFile(path, nil, os.Stdout, false) err = processFile(path, nil, os.Stdout, false)
} }
@ -217,12 +218,12 @@ func gofmtMain() {
switch dir, err := os.Stat(path); { switch dir, err := os.Stat(path); {
case err != nil: case err != nil:
report(err) report(err)
case dir.IsRegular(): case dir.IsDir():
walkDir(path)
default:
if err := processFile(path, nil, os.Stdout, false); err != nil { if err := processFile(path, nil, os.Stdout, false); err != nil {
report(err) report(err)
} }
case dir.IsDirectory():
walkDir(path)
} }
} }
} }

View File

@ -525,5 +525,5 @@ func selectTag(goVersion string, tags []string) (match string) {
func isDir(dir string) bool { func isDir(dir string) bool {
fi, err := os.Stat(dir) fi, err := os.Stat(dir)
return err == nil && fi.IsDirectory() return err == nil && fi.IsDir()
} }

View File

@ -82,7 +82,7 @@ func main() {
} else { } else {
for _, name := range flag.Args() { for _, name := range flag.Args() {
// Is it a directory? // Is it a directory?
if fi, err := os.Stat(name); err == nil && fi.IsDirectory() { if fi, err := os.Stat(name); err == nil && fi.IsDir() {
walkDir(name) walkDir(name)
} else { } else {
doFile(name, nil) doFile(name, nil)
@ -105,12 +105,12 @@ func doFile(name string, reader io.Reader) {
file.checkFile(name, parsedFile) file.checkFile(name, parsedFile)
} }
func visit(path string, f *os.FileInfo, err error) error { func visit(path string, f os.FileInfo, err error) error {
if err != nil { if err != nil {
errorf("walk error: %s", err) errorf("walk error: %s", err)
return nil return nil
} }
if f.IsRegular() && strings.HasSuffix(path, ".go") { if !f.IsDir() && strings.HasSuffix(path, ".go") {
doFile(path, nil) doFile(path, nil)
} }
return nil return nil

View File

@ -192,7 +192,7 @@ func makeParent(name string) {
func mkdirAll(path string, perm uint32) error { func mkdirAll(path string, perm uint32) error {
dir, err := os.Lstat(path) dir, err := os.Lstat(path)
if err == nil { if err == nil {
if dir.IsDirectory() { if dir.IsDir() {
return nil return nil
} }
return &os.PathError{"mkdir", path, os.ENOTDIR} return &os.PathError{"mkdir", path, os.ENOTDIR}
@ -220,7 +220,7 @@ func mkdirAll(path string, perm uint32) error {
// Handle arguments like "foo/." by // Handle arguments like "foo/." by
// double-checking that directory doesn't exist. // double-checking that directory doesn't exist.
dir, err1 := os.Lstat(path) dir, err1 := os.Lstat(path)
if err1 == nil && dir.IsDirectory() { if err1 == nil && dir.IsDir() {
return nil return nil
} }
return err return err

View File

@ -56,7 +56,7 @@ func OpenReader(name string) (*ReadCloser, error) {
return nil, err return nil, err
} }
r := new(ReadCloser) r := new(ReadCloser)
if err := r.init(f, fi.Size); err != nil { if err := r.init(f, fi.Size()); err != nil {
f.Close() f.Close()
return nil, err return nil, err
} }

View File

@ -150,15 +150,15 @@ func processFiles(filenames []string, allFiles bool) {
switch info, err := os.Stat(filename); { switch info, err := os.Stat(filename); {
case err != nil: case err != nil:
report(err) report(err)
case info.IsRegular(): case info.IsDir():
if allFiles || isGoFilename(info.Name) {
filenames[i] = filename
i++
}
case info.IsDirectory():
if allFiles || *recursive { if allFiles || *recursive {
processDirectory(filename) processDirectory(filename)
} }
default:
if allFiles || isGoFilename(info.Name()) {
filenames[i] = filename
i++
}
} }
} }
fset := token.NewFileSet() fset := token.NewFileSet()

View File

@ -202,7 +202,7 @@ func TestCheck(t *testing.T) {
// For easy debugging w/o changing the testing code, // For easy debugging w/o changing the testing code,
// if there is a local test file, only test that file. // if there is a local test file, only test that file.
const testfile = "test.go" const testfile = "test.go"
if fi, err := os.Stat(testfile); err == nil && fi.IsRegular() { if fi, err := os.Stat(testfile); err == nil && !fi.IsDir() {
fmt.Printf("WARNING: Testing only %s (remove it to run all tests)\n", testfile) fmt.Printf("WARNING: Testing only %s (remove it to run all tests)\n", testfile)
check(t, testfile, []string{testfile}) check(t, testfile, []string{testfile})
return return

View File

@ -59,7 +59,7 @@ func findPkg(path string) (filename, id string) {
// try extensions // try extensions
for _, ext := range pkgExts { for _, ext := range pkgExts {
filename = noext + ext filename = noext + ext
if f, err := os.Stat(filename); err == nil && f.IsRegular() { if f, err := os.Stat(filename); err == nil && !f.IsDir() {
return return
} }
} }

View File

@ -72,18 +72,18 @@ func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
return return
} }
switch { switch {
case f.IsRegular(): case !f.IsDir():
// try extensions // try extensions
for _, ext := range pkgExts { for _, ext := range pkgExts {
if strings.HasSuffix(f.Name, ext) { if strings.HasSuffix(f.Name(), ext) {
name := f.Name[0 : len(f.Name)-len(ext)] // remove extension name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
if testPath(t, filepath.Join(dir, name)) { if testPath(t, filepath.Join(dir, name)) {
nimports++ nimports++
} }
} }
} }
case f.IsDirectory(): case f.IsDir():
nimports += testDir(t, filepath.Join(dir, f.Name), endTime) nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
} }
} }
return return

View File

@ -159,13 +159,13 @@ func (s *Script) Stale() bool {
// any error reading output files means stale // any error reading output files means stale
return true return true
} }
if fi.ModTime.After(latest) { if mtime := fi.ModTime(); mtime.After(latest) {
latest = fi.ModTime latest = mtime
} }
} }
for _, file := range s.Input { for _, file := range s.Input {
fi, err := os.Stat(file) fi, err := os.Stat(file)
if err != nil || fi.ModTime.After(latest) { if err != nil || fi.ModTime().After(latest) {
// any error reading input files means stale // any error reading input files means stale
// (attempt to rebuild to figure out why) // (attempt to rebuild to figure out why)
return true return true

View File

@ -38,16 +38,16 @@ type Context struct {
// format of the strings dir and file: they can be // format of the strings dir and file: they can be
// slash-separated, backslash-separated, even URLs. // slash-separated, backslash-separated, even URLs.
// ReadDir returns a slice of *os.FileInfo, sorted by Name, // ReadDir returns a slice of os.FileInfo, sorted by Name,
// describing the content of the named directory. // describing the content of the named directory.
// The dir argument is the argument to ScanDir. // The dir argument is the argument to ScanDir.
// If ReadDir is nil, ScanDir uses io.ReadDir. // If ReadDir is nil, ScanDir uses io.ReadDir.
ReadDir func(dir string) (fi []*os.FileInfo, err error) ReadDir func(dir string) (fi []os.FileInfo, err error)
// ReadFile returns the content of the file named file // ReadFile returns the content of the file named file
// in the directory named dir. The dir argument is the // in the directory named dir. The dir argument is the
// argument to ScanDir, and the file argument is the // argument to ScanDir, and the file argument is the
// Name field from an *os.FileInfo returned by ReadDir. // Name field from an os.FileInfo returned by ReadDir.
// The returned path is the full name of the file, to be // The returned path is the full name of the file, to be
// used in error messages. // used in error messages.
// //
@ -56,7 +56,7 @@ type Context struct {
ReadFile func(dir, file string) (path string, content []byte, err error) ReadFile func(dir, file string) (path string, content []byte, err error)
} }
func (ctxt *Context) readDir(dir string) ([]*os.FileInfo, error) { func (ctxt *Context) readDir(dir string) ([]os.FileInfo, error) {
if f := ctxt.ReadDir; f != nil { if f := ctxt.ReadDir; f != nil {
return f(dir) return f(dir)
} }
@ -140,18 +140,19 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
testImported := make(map[string]bool) testImported := make(map[string]bool)
fset := token.NewFileSet() fset := token.NewFileSet()
for _, d := range dirs { for _, d := range dirs {
if !d.IsRegular() { if d.IsDir() {
continue continue
} }
if strings.HasPrefix(d.Name, "_") || name := d.Name()
strings.HasPrefix(d.Name, ".") { if strings.HasPrefix(name, "_") ||
strings.HasPrefix(name, ".") {
continue continue
} }
if !ctxt.goodOSArchFile(d.Name) { if !ctxt.goodOSArchFile(name) {
continue continue
} }
ext := path.Ext(d.Name) ext := path.Ext(name)
switch ext { switch ext {
case ".go", ".c", ".s": case ".go", ".c", ".s":
// tentatively okay // tentatively okay
@ -161,7 +162,7 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
} }
// Look for +build comments to accept or reject the file. // Look for +build comments to accept or reject the file.
filename, data, err := ctxt.readFile(dir, d.Name) filename, data, err := ctxt.readFile(dir, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -172,10 +173,10 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
// Going to save the file. For non-Go files, can stop here. // Going to save the file. For non-Go files, can stop here.
switch ext { switch ext {
case ".c": case ".c":
di.CFiles = append(di.CFiles, d.Name) di.CFiles = append(di.CFiles, name)
continue continue
case ".s": case ".s":
di.SFiles = append(di.SFiles, d.Name) di.SFiles = append(di.SFiles, name)
continue continue
} }
@ -192,7 +193,7 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
continue continue
} }
isTest := strings.HasSuffix(d.Name, "_test.go") isTest := strings.HasSuffix(name, "_test.go")
if isTest && strings.HasSuffix(pkg, "_test") { if isTest && strings.HasSuffix(pkg, "_test") {
pkg = pkg[:len(pkg)-len("_test")] pkg = pkg[:len(pkg)-len("_test")]
} }
@ -255,15 +256,15 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
} }
} }
if isCgo { if isCgo {
di.CgoFiles = append(di.CgoFiles, d.Name) di.CgoFiles = append(di.CgoFiles, name)
} else if isTest { } else if isTest {
if pkg == string(pf.Name.Name) { if pkg == string(pf.Name.Name) {
di.TestGoFiles = append(di.TestGoFiles, d.Name) di.TestGoFiles = append(di.TestGoFiles, name)
} else { } else {
di.XTestGoFiles = append(di.XTestGoFiles, d.Name) di.XTestGoFiles = append(di.XTestGoFiles, name)
} }
} else { } else {
di.GoFiles = append(di.GoFiles, d.Name) di.GoFiles = append(di.GoFiles, name)
} }
} }
if di.Package == "" { if di.Package == "" {

View File

@ -70,7 +70,7 @@ func (t *Tree) HasSrc(pkg string) bool {
if err != nil { if err != nil {
return false return false
} }
return fi.IsDirectory() return fi.IsDir()
} }
// HasPkg returns whether the given package's // HasPkg returns whether the given package's
@ -80,7 +80,7 @@ func (t *Tree) HasPkg(pkg string) bool {
if err != nil { if err != nil {
return false return false
} }
return fi.IsRegular() return !fi.IsDir()
// TODO(adg): check object version is consistent // TODO(adg): check object version is consistent
} }

View File

@ -188,7 +188,7 @@ func ParseFiles(fset *token.FileSet, filenames []string, mode uint) (pkgs map[st
// returned. If a parse error occurred, a non-nil but incomplete map and the // returned. If a parse error occurred, a non-nil but incomplete map and the
// error are returned. // error are returned.
// //
func ParseDir(fset *token.FileSet, path string, filter func(*os.FileInfo) bool, mode uint) (map[string]*ast.Package, error) { func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, mode uint) (map[string]*ast.Package, error) {
fd, err := os.Open(path) fd, err := os.Open(path)
if err != nil { if err != nil {
return nil, err return nil, err
@ -202,10 +202,9 @@ func ParseDir(fset *token.FileSet, path string, filter func(*os.FileInfo) bool,
filenames := make([]string, len(list)) filenames := make([]string, len(list))
n := 0 n := 0
for i := 0; i < len(list); i++ { for _, d := range list {
d := &list[i]
if filter == nil || filter(d) { if filter == nil || filter(d) {
filenames[n] = filepath.Join(path, d.Name) filenames[n] = filepath.Join(path, d.Name())
n++ n++
} }
} }

View File

@ -113,7 +113,7 @@ func nameFilter(filename string) bool {
return true return true
} }
func dirFilter(f *os.FileInfo) bool { return nameFilter(f.Name) } func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) }
func TestParse4(t *testing.T) { func TestParse4(t *testing.T) {
path := "." path := "."

View File

@ -36,8 +36,8 @@ func ReadFile(filename string) ([]byte, error) {
// read, so let's try it but be prepared for the answer to be wrong. // read, so let's try it but be prepared for the answer to be wrong.
fi, err := f.Stat() fi, err := f.Stat()
var n int64 var n int64
if err == nil && fi.Size < 2e9 { // Don't preallocate a huge buffer, just in case. if size := fi.Size(); err == nil && size < 2e9 { // Don't preallocate a huge buffer, just in case.
n = fi.Size n = size
} }
// As initial capacity for readAll, use n + a little extra in case Size is zero, // As initial capacity for readAll, use n + a little extra in case Size is zero,
// and to avoid another allocation after Read has filled the buffer. The readAll // and to avoid another allocation after Read has filled the buffer. The readAll
@ -63,16 +63,16 @@ func WriteFile(filename string, data []byte, perm uint32) error {
return err return err
} }
// A fileInfoList implements sort.Interface. // byName implements sort.Interface.
type fileInfoList []*os.FileInfo type byName []os.FileInfo
func (f fileInfoList) Len() int { return len(f) } func (f byName) Len() int { return len(f) }
func (f fileInfoList) Less(i, j int) bool { return f[i].Name < f[j].Name } func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
func (f fileInfoList) Swap(i, j int) { f[i], f[j] = f[j], f[i] } func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
// ReadDir reads the directory named by dirname and returns // ReadDir reads the directory named by dirname and returns
// a list of sorted directory entries. // a list of sorted directory entries.
func ReadDir(dirname string) ([]*os.FileInfo, error) { func ReadDir(dirname string) ([]os.FileInfo, error) {
f, err := os.Open(dirname) f, err := os.Open(dirname)
if err != nil { if err != nil {
return nil, err return nil, err
@ -82,12 +82,8 @@ func ReadDir(dirname string) ([]*os.FileInfo, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
fi := make(fileInfoList, len(list)) sort.Sort(byName(list))
for i := range list { return list, nil
fi[i] = &list[i]
}
sort.Sort(fi)
return fi, nil
} }
type nopCloser struct { type nopCloser struct {

View File

@ -15,8 +15,8 @@ func checkSize(t *testing.T, path string, size int64) {
if err != nil { if err != nil {
t.Fatalf("Stat %q (looking for size %d): %s", path, size, err) t.Fatalf("Stat %q (looking for size %d): %s", path, size, err)
} }
if dir.Size != size { if dir.Size() != size {
t.Errorf("Stat %q: size %d want %d", path, dir.Size, size) t.Errorf("Stat %q: size %d want %d", path, dir.Size(), size)
} }
} }
@ -76,9 +76,9 @@ func TestReadDir(t *testing.T) {
foundTestDir := false foundTestDir := false
for _, dir := range list { for _, dir := range list {
switch { switch {
case dir.IsRegular() && dir.Name == "ioutil_test.go": case !dir.IsDir() && dir.Name() == "ioutil_test.go":
foundTest = true foundTest = true
case dir.IsDirectory() && dir.Name == "_test": case dir.IsDir() && dir.Name() == "_test":
foundTestDir = true foundTestDir = true
} }
} }

View File

@ -52,7 +52,7 @@ type FileSystem interface {
// served by the FileServer implementation. // served by the FileServer implementation.
type File interface { type File interface {
Close() error Close() error
Stat() (*os.FileInfo, error) Stat() (os.FileInfo, error)
Readdir(count int) ([]os.FileInfo, error) Readdir(count int) ([]os.FileInfo, error)
Read([]byte) (int, error) Read([]byte) (int, error)
Seek(offset int64, whence int) (int64, error) Seek(offset int64, whence int) (int64, error)
@ -93,8 +93,8 @@ func dirList(w ResponseWriter, f File) {
break break
} }
for _, d := range dirs { for _, d := range dirs {
name := d.Name name := d.Name()
if d.IsDirectory() { if d.IsDir() {
name += "/" name += "/"
} }
// TODO htmlescape // TODO htmlescape
@ -135,7 +135,7 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
// redirect to canonical path: / at end of directory url // redirect to canonical path: / at end of directory url
// r.URL.Path always begins with / // r.URL.Path always begins with /
url := r.URL.Path url := r.URL.Path
if d.IsDirectory() { if d.IsDir() {
if url[len(url)-1] != '/' { if url[len(url)-1] != '/' {
localRedirect(w, r, path.Base(url)+"/") localRedirect(w, r, path.Base(url)+"/")
return return
@ -148,14 +148,14 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
} }
} }
if t, err := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && !d.ModTime.After(t) { if t, err := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && !d.ModTime().After(t) {
w.WriteHeader(StatusNotModified) w.WriteHeader(StatusNotModified)
return return
} }
w.Header().Set("Last-Modified", d.ModTime.UTC().Format(TimeFormat)) w.Header().Set("Last-Modified", d.ModTime().UTC().Format(TimeFormat))
// use contents of index.html for directory, if present // use contents of index.html for directory, if present
if d.IsDirectory() { if d.IsDir() {
index := name + indexPage index := name + indexPage
ff, err := fs.Open(index) ff, err := fs.Open(index)
if err == nil { if err == nil {
@ -169,13 +169,13 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
} }
} }
if d.IsDirectory() { if d.IsDir() {
dirList(w, f) dirList(w, f)
return return
} }
// serve file // serve file
size := d.Size size := d.Size()
code := StatusOK code := StatusOK
// If Content-Type isn't set, use the file's extension to find it. // If Content-Type isn't set, use the file's extension to find it.
@ -215,7 +215,7 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
} }
size = ra.length size = ra.length
code = StatusPartialContent code = StatusPartialContent
w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, d.Size)) w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, d.Size()))
} }
w.Header().Set("Accept-Ranges", "bytes") w.Header().Set("Accept-Ranges", "bytes")

View File

@ -190,8 +190,8 @@ func TestDirJoin(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("stat of %s: %v", name, err) t.Fatalf("stat of %s: %v", name, err)
} }
if gfi.Ino != wfi.Ino { if !gfi.(*os.FileStat).SameFile(wfi.(*os.FileStat)) {
t.Errorf("%s got different inode", name) t.Errorf("%s got different file", name)
} }
} }
test(Dir("/etc/"), "/hosts") test(Dir("/etc/"), "/hosts")

View File

@ -8,7 +8,7 @@ func (file *File) Readdirnames(n int) (names []string, err error) {
fis, err := file.Readdir(n) fis, err := file.Readdir(n)
names = make([]string, len(fis)) names = make([]string, len(fis))
for i, fi := range fis { for i, fi := range fis {
names[i] = fi.Name names[i] = fi.Name()
} }
return names, err return names, err
} }

View File

@ -20,7 +20,7 @@ func findExecutable(file string) error {
if err != nil { if err != nil {
return err return err
} }
if d.IsRegular() && d.Permission()&0111 != 0 { if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
return nil return nil
} }
return os.EPERM return os.EPERM

View File

@ -18,10 +18,10 @@ func chkStat(file string) error {
if err != nil { if err != nil {
return err return err
} }
if d.IsRegular() { if d.IsDir() {
return nil return os.EPERM
} }
return os.EPERM return nil
} }
func findExecutable(file string, exts []string) (string, error) { func findExecutable(file string, exts []string) (string, error) {

10
src/pkg/os/export_test.go Normal file
View File

@ -0,0 +1,10 @@
// 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
// Export for testing.
var TimespecToTime = timespecToTime
var Atime = atime

View File

@ -99,50 +99,43 @@ func (file *file) close() error {
// Stat returns the FileInfo structure describing file. // Stat returns the FileInfo structure describing file.
// It returns the FileInfo and an error, if any. // It returns the FileInfo and an error, if any.
func (file *File) Stat() (fi *FileInfo, err error) { func (file *File) Stat() (fi FileInfo, err error) {
var stat syscall.Stat_t var stat syscall.Stat_t
e := syscall.Fstat(file.fd, &stat) err = syscall.Fstat(file.fd, &stat)
if e != nil { if err != nil {
return nil, &PathError{"stat", file.name, e} return nil, &PathError{"stat", file.name, err}
} }
return fileInfoFromStat(file.name, new(FileInfo), &stat, &stat), nil return fileInfoFromStat(&stat, file.name), nil
} }
// Stat returns a FileInfo structure describing the named file and an error, if any. // Stat returns a FileInfo describing the named file and an error, if any.
// If name names a valid symbolic link, the returned FileInfo describes // If name names a valid symbolic link, the returned FileInfo describes
// the file pointed at by the link and has fi.FollowedSymlink set to true. // the file pointed at by the link and has fi.FollowedSymlink set to true.
// If name names an invalid symbolic link, the returned FileInfo describes // If name names an invalid symbolic link, the returned FileInfo describes
// the link itself and has fi.FollowedSymlink set to false. // the link itself and has fi.FollowedSymlink set to false.
func Stat(name string) (fi *FileInfo, err error) { func Stat(name string) (fi FileInfo, err error) {
var lstat, stat syscall.Stat_t var stat syscall.Stat_t
e := syscall.Lstat(name, &lstat) err = syscall.Stat(name, &stat)
if e != nil { if err != nil {
return nil, &PathError{"stat", name, e} return nil, &PathError{"stat", name, err}
} }
statp := &lstat return fileInfoFromStat(&stat, name), nil
if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
e := syscall.Stat(name, &stat)
if e == nil {
statp = &stat
}
}
return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil
} }
// Lstat returns the FileInfo structure describing the named file and an // Lstat returns a FileInfo describing the named file and an
// error, if any. If the file is a symbolic link, the returned FileInfo // error, if any. If the file is a symbolic link, the returned FileInfo
// describes the symbolic link. Lstat makes no attempt to follow the link. // describes the symbolic link. Lstat makes no attempt to follow the link.
func Lstat(name string) (fi *FileInfo, err error) { func Lstat(name string) (fi FileInfo, err error) {
var stat syscall.Stat_t var stat syscall.Stat_t
e := syscall.Lstat(name, &stat) err = syscall.Lstat(name, &stat)
if e != nil { if err != nil {
return nil, &PathError{"lstat", name, e} return nil, &PathError{"lstat", name, err}
} }
return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil return fileInfoFromStat(&stat, name), nil
} }
// Readdir reads the contents of the directory associated with file and // Readdir reads the contents of the directory associated with file and
// returns an array of up to n FileInfo structures, as would be returned // returns an array of up to n FileInfo values, as would be returned
// by Lstat, in directory order. Subsequent calls on the same file will yield // by Lstat, in directory order. Subsequent calls on the same file will yield
// further FileInfos. // further FileInfos.
// //
@ -166,13 +159,13 @@ func (file *File) Readdir(n int) (fi []FileInfo, err error) {
fi = make([]FileInfo, len(names)) fi = make([]FileInfo, len(names))
for i, filename := range names { for i, filename := range names {
fip, err := Lstat(dirname + filename) fip, err := Lstat(dirname + filename)
if fip == nil || err != nil { if err == nil {
fi[i].Name = filename // rest is already zeroed out fi[i] = fip
} else { } else {
fi[i] = *fip fi[i] = &FileStat{name: filename}
} }
} }
return return fi, err
} }
// read reads up to len(b) bytes from the File. // read reads up to len(b) bytes from the File.

View File

@ -180,12 +180,12 @@ func (file *File) Readdir(n int) (fi []FileInfo, err error) {
} }
} }
} }
var f FileInfo
setFileInfo(&f, string(syscall.UTF16ToString(d.FileName[0:])), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime)
file.dirinfo.needdata = true file.dirinfo.needdata = true
if f.Name == "." || f.Name == ".." { // Useless names name := string(syscall.UTF16ToString(d.FileName[0:]))
if name == "." || name == ".." { // Useless names
continue continue
} }
f := toFileInfo(name, d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime)
n-- n--
fi = append(fi, f) fi = append(fi, f)
} }

View File

@ -12,7 +12,7 @@ import (
// current directory. If the current directory can be // current directory. If the current directory can be
// reached via multiple paths (due to symbolic links), // reached via multiple paths (due to symbolic links),
// Getwd may return any one of them. // Getwd may return any one of them.
func Getwd() (string, error) { func Getwd() (pwd string, err error) {
// If the operating system provides a Getwd call, use it. // If the operating system provides a Getwd call, use it.
if syscall.ImplementsGetwd { if syscall.ImplementsGetwd {
s, e := syscall.Getwd() s, e := syscall.Getwd()
@ -27,10 +27,10 @@ func Getwd() (string, error) {
// Clumsy but widespread kludge: // Clumsy but widespread kludge:
// if $PWD is set and matches ".", use it. // if $PWD is set and matches ".", use it.
pwd := Getenv("PWD") pwd = Getenv("PWD")
if len(pwd) > 0 && pwd[0] == '/' { if len(pwd) > 0 && pwd[0] == '/' {
d, err := Stat(pwd) d, err := Stat(pwd)
if err == nil && d.Dev == dot.Dev && d.Ino == dot.Ino { if err == nil && dot.(*FileStat).SameFile(d.(*FileStat)) {
return pwd, nil return pwd, nil
} }
} }
@ -42,7 +42,7 @@ func Getwd() (string, error) {
// Can't stat root - no hope. // Can't stat root - no hope.
return "", err return "", err
} }
if root.Dev == dot.Dev && root.Ino == dot.Ino { if root.(*FileStat).SameFile(dot.(*FileStat)) {
return "/", nil return "/", nil
} }
@ -67,7 +67,7 @@ func Getwd() (string, error) {
} }
for _, name := range names { for _, name := range names {
d, _ := Lstat(parent + "/" + name) d, _ := Lstat(parent + "/" + name)
if d.Dev == dot.Dev && d.Ino == dot.Ino { if d.(*FileStat).SameFile(dot.(*FileStat)) {
pwd = "/" + name + pwd pwd = "/" + name + pwd
goto Found goto Found
} }
@ -82,7 +82,7 @@ func Getwd() (string, error) {
return "", err return "", err
} }
fd.Close() fd.Close()
if pd.Dev == root.Dev && pd.Ino == root.Ino { if pd.(*FileStat).SameFile(root.(*FileStat)) {
break break
} }
// Set up for next round. // Set up for next round.

View File

@ -122,12 +122,12 @@ func TestStat(t *testing.T) {
if err != nil { if err != nil {
t.Fatal("stat failed:", err) t.Fatal("stat failed:", err)
} }
if !equal(sfname, dir.Name) { if !equal(sfname, dir.Name()) {
t.Error("name should be ", sfname, "; is", dir.Name) t.Error("name should be ", sfname, "; is", dir.Name())
} }
filesize := size(path, t) filesize := size(path, t)
if dir.Size != filesize { if dir.Size() != filesize {
t.Error("size should be", filesize, "; is", dir.Size) t.Error("size should be", filesize, "; is", dir.Size())
} }
} }
@ -142,12 +142,12 @@ func TestFstat(t *testing.T) {
if err2 != nil { if err2 != nil {
t.Fatal("fstat failed:", err2) t.Fatal("fstat failed:", err2)
} }
if !equal(sfname, dir.Name) { if !equal(sfname, dir.Name()) {
t.Error("name should be ", sfname, "; is", dir.Name) t.Error("name should be ", sfname, "; is", dir.Name())
} }
filesize := size(path, t) filesize := size(path, t)
if dir.Size != filesize { if dir.Size() != filesize {
t.Error("size should be", filesize, "; is", dir.Size) t.Error("size should be", filesize, "; is", dir.Size())
} }
} }
@ -157,12 +157,12 @@ func TestLstat(t *testing.T) {
if err != nil { if err != nil {
t.Fatal("lstat failed:", err) t.Fatal("lstat failed:", err)
} }
if !equal(sfname, dir.Name) { if !equal(sfname, dir.Name()) {
t.Error("name should be ", sfname, "; is", dir.Name) t.Error("name should be ", sfname, "; is", dir.Name())
} }
filesize := size(path, t) filesize := size(path, t)
if dir.Size != filesize { if dir.Size() != filesize {
t.Error("size should be", filesize, "; is", dir.Size) t.Error("size should be", filesize, "; is", dir.Size())
} }
} }
@ -229,7 +229,7 @@ func testReaddir(dir string, contents []string, t *testing.T) {
for _, m := range contents { for _, m := range contents {
found := false found := false
for _, n := range s { for _, n := range s {
if equal(m, n.Name) { if equal(m, n.Name()) {
if found { if found {
t.Error("present twice:", m) t.Error("present twice:", m)
} }
@ -408,7 +408,7 @@ func TestHardLink(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("stat %q failed: %v", from, err) t.Fatalf("stat %q failed: %v", from, err)
} }
if tostat.Dev != fromstat.Dev || tostat.Ino != fromstat.Ino { if !tostat.(*FileStat).SameFile(fromstat.(*FileStat)) {
t.Errorf("link %q, %q did not create hard link", to, from) t.Errorf("link %q, %q did not create hard link", to, from)
} }
} }
@ -433,32 +433,32 @@ func TestSymLink(t *testing.T) {
t.Fatalf("symlink %q, %q failed: %v", to, from, err) t.Fatalf("symlink %q, %q failed: %v", to, from, err)
} }
defer Remove(from) defer Remove(from)
tostat, err := Stat(to) tostat, err := Lstat(to)
if err != nil { if err != nil {
t.Fatalf("stat %q failed: %v", to, err) t.Fatalf("stat %q failed: %v", to, err)
} }
if tostat.FollowedSymlink { if tostat.Mode()&ModeSymlink != 0 {
t.Fatalf("stat %q claims to have followed a symlink", to) t.Fatalf("stat %q claims to have found a symlink", to)
} }
fromstat, err := Stat(from) fromstat, err := Stat(from)
if err != nil { if err != nil {
t.Fatalf("stat %q failed: %v", from, err) t.Fatalf("stat %q failed: %v", from, err)
} }
if tostat.Dev != fromstat.Dev || tostat.Ino != fromstat.Ino { if !tostat.(*FileStat).SameFile(fromstat.(*FileStat)) {
t.Errorf("symlink %q, %q did not create symlink", to, from) t.Errorf("symlink %q, %q did not create symlink", to, from)
} }
fromstat, err = Lstat(from) fromstat, err = Lstat(from)
if err != nil { if err != nil {
t.Fatalf("lstat %q failed: %v", from, err) t.Fatalf("lstat %q failed: %v", from, err)
} }
if !fromstat.IsSymlink() { if fromstat.Mode()&ModeSymlink == 0 {
t.Fatalf("symlink %q, %q did not create symlink", to, from) t.Fatalf("symlink %q, %q did not create symlink", to, from)
} }
fromstat, err = Stat(from) fromstat, err = Stat(from)
if err != nil { if err != nil {
t.Fatalf("stat %q failed: %v", from, err) t.Fatalf("stat %q failed: %v", from, err)
} }
if !fromstat.FollowedSymlink { if fromstat.Mode()&ModeSymlink != 0 {
t.Fatalf("stat %q did not follow symlink", from) t.Fatalf("stat %q did not follow symlink", from)
} }
s, err := Readlink(from) s, err := Readlink(from)
@ -566,13 +566,13 @@ func TestStartProcess(t *testing.T) {
exec(t, cmddir, cmdbase, args, filepath.Clean(cmddir)+le) exec(t, cmddir, cmdbase, args, filepath.Clean(cmddir)+le)
} }
func checkMode(t *testing.T, path string, mode uint32) { func checkMode(t *testing.T, path string, mode FileMode) {
dir, err := Stat(path) dir, err := Stat(path)
if err != nil { if err != nil {
t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err) t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
} }
if dir.Mode&0777 != mode { if dir.Mode()&0777 != mode {
t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode, mode) t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode)
} }
} }
@ -596,73 +596,13 @@ func TestChmod(t *testing.T) {
checkMode(t, f.Name(), 0123) checkMode(t, f.Name(), 0123)
} }
func checkUidGid(t *testing.T, path string, uid, gid int) {
dir, err := Stat(path)
if err != nil {
t.Fatalf("Stat %q (looking for uid/gid %d/%d): %s", path, uid, gid, err)
}
if dir.Uid != uid {
t.Errorf("Stat %q: uid %d want %d", path, dir.Uid, uid)
}
if dir.Gid != gid {
t.Errorf("Stat %q: gid %d want %d", path, dir.Gid, gid)
}
}
func TestChown(t *testing.T) {
// Chown is not supported under windows or Plan 9.
// Plan9 provides a native ChownPlan9 version instead.
if syscall.OS == "windows" || syscall.OS == "plan9" {
return
}
// Use TempDir() to make sure we're on a local file system,
// so that the group ids returned by Getgroups will be allowed
// on the file. On NFS, the Getgroups groups are
// basically useless.
f := newFile("TestChown", t)
defer Remove(f.Name())
defer f.Close()
dir, err := f.Stat()
if err != nil {
t.Fatalf("stat %s: %s", f.Name(), err)
}
// Can't change uid unless root, but can try
// changing the group id. First try our current group.
gid := Getgid()
t.Log("gid:", gid)
if err = Chown(f.Name(), -1, gid); err != nil {
t.Fatalf("chown %s -1 %d: %s", f.Name(), gid, err)
}
checkUidGid(t, f.Name(), dir.Uid, gid)
// Then try all the auxiliary groups.
groups, err := Getgroups()
if err != nil {
t.Fatalf("getgroups: %s", err)
}
t.Log("groups: ", groups)
for _, g := range groups {
if err = Chown(f.Name(), -1, g); err != nil {
t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err)
}
checkUidGid(t, f.Name(), dir.Uid, g)
// change back to gid to test fd.Chown
if err = f.Chown(-1, gid); err != nil {
t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
}
checkUidGid(t, f.Name(), dir.Uid, gid)
}
}
func checkSize(t *testing.T, f *File, size int64) { func checkSize(t *testing.T, f *File, size int64) {
dir, err := f.Stat() dir, err := f.Stat()
if err != nil { if err != nil {
t.Fatalf("Stat %q (looking for size %d): %s", f.Name(), size, err) t.Fatalf("Stat %q (looking for size %d): %s", f.Name(), size, err)
} }
if dir.Size != size { if dir.Size() != size {
t.Errorf("Stat %q: size %d want %d", f.Name(), dir.Size, size) t.Errorf("Stat %q: size %d want %d", f.Name(), dir.Size(), size)
} }
} }
@ -714,36 +654,38 @@ func TestChtimes(t *testing.T) {
f.Write([]byte("hello, world\n")) f.Write([]byte("hello, world\n"))
f.Close() f.Close()
preStat, err := Stat(f.Name()) st, err := Stat(f.Name())
if err != nil { if err != nil {
t.Fatalf("Stat %s: %s", f.Name(), err) t.Fatalf("Stat %s: %s", f.Name(), err)
} }
preStat := st.(*FileStat)
// Move access and modification time back a second // Move access and modification time back a second
err = Chtimes(f.Name(), preStat.AccessTime.Add(-time.Second), preStat.ModTime.Add(-time.Second)) at := Atime(preStat)
mt := preStat.ModTime()
err = Chtimes(f.Name(), at.Add(-time.Second), mt.Add(-time.Second))
if err != nil { if err != nil {
t.Fatalf("Chtimes %s: %s", f.Name(), err) t.Fatalf("Chtimes %s: %s", f.Name(), err)
} }
postStat, err := Stat(f.Name()) st, err = Stat(f.Name())
if err != nil { if err != nil {
t.Fatalf("second Stat %s: %s", f.Name(), err) t.Fatalf("second Stat %s: %s", f.Name(), err)
} }
postStat := st.(*FileStat)
/* Plan 9: /* Plan 9:
Mtime is the time of the last change of content. Similarly, atime is set whenever the Mtime is the time of the last change of content. Similarly, atime is set whenever the
contents are accessed; also, it is set whenever mtime is set. contents are accessed; also, it is set whenever mtime is set.
*/ */
if !postStat.AccessTime.Before(preStat.AccessTime) && syscall.OS != "plan9" { pat := Atime(postStat)
t.Errorf("AccessTime didn't go backwards; was=%d, after=%d", pmt := postStat.ModTime()
preStat.AccessTime, if !pat.Before(at) && syscall.OS != "plan9" {
postStat.AccessTime) t.Errorf("AccessTime didn't go backwards; was=%d, after=%d", at, pat)
} }
if !postStat.ModTime.Before(preStat.ModTime) { if !pmt.Before(mt) {
t.Errorf("ModTime didn't go backwards; was=%d, after=%d", t.Errorf("ModTime didn't go backwards; was=%d, after=%d", mt, pmt)
preStat.ModTime,
postStat.ModTime)
} }
} }
@ -885,7 +827,7 @@ func TestOpenError(t *testing.T) {
} }
perr, ok := err.(*PathError) perr, ok := err.(*PathError)
if !ok { if !ok {
t.Errorf("Open(%q, %d) returns error of %T type; want *os.PathError", tt.path, tt.mode, err) t.Errorf("Open(%q, %d) returns error of %T type; want *PathError", tt.path, tt.mode, err)
} }
if perr.Err != tt.error { if perr.Err != tt.error {
if syscall.OS == "plan9" { if syscall.OS == "plan9" {

View File

@ -0,0 +1,75 @@
// Copyright 2009 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.
// +build darwin freebsd linux openbsd
package os_test
import (
. "os"
"syscall"
"testing"
)
func checkUidGid(t *testing.T, path string, uid, gid int) {
dir, err := Stat(path)
if err != nil {
t.Fatalf("Stat %q (looking for uid/gid %d/%d): %s", path, uid, gid, err)
}
sys := dir.(*FileStat).Sys.(*syscall.Stat_t)
if int(sys.Uid) != uid {
t.Errorf("Stat %q: uid %d want %d", path, sys.Uid, uid)
}
if int(sys.Gid) != gid {
t.Errorf("Stat %q: gid %d want %d", path, sys.Gid, gid)
}
}
func TestChown(t *testing.T) {
// Chown is not supported under windows or Plan 9.
// Plan9 provides a native ChownPlan9 version instead.
if syscall.OS == "windows" || syscall.OS == "plan9" {
return
}
// Use TempDir() to make sure we're on a local file system,
// so that the group ids returned by Getgroups will be allowed
// on the file. On NFS, the Getgroups groups are
// basically useless.
f := newFile("TestChown", t)
defer Remove(f.Name())
defer f.Close()
dir, err := f.Stat()
if err != nil {
t.Fatalf("stat %s: %s", f.Name(), err)
}
// Can't change uid unless root, but can try
// changing the group id. First try our current group.
gid := Getgid()
t.Log("gid:", gid)
if err = Chown(f.Name(), -1, gid); err != nil {
t.Fatalf("chown %s -1 %d: %s", f.Name(), gid, err)
}
sys := dir.(*FileStat).Sys.(*syscall.Stat_t)
checkUidGid(t, f.Name(), int(sys.Uid), gid)
// Then try all the auxiliary groups.
groups, err := Getgroups()
if err != nil {
t.Fatalf("getgroups: %s", err)
}
t.Log("groups: ", groups)
for _, g := range groups {
if err = Chown(f.Name(), -1, g); err != nil {
t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err)
}
checkUidGid(t, f.Name(), int(sys.Uid), g)
// change back to gid to test fd.Chown
if err = f.Chown(-1, gid); err != nil {
t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
}
checkUidGid(t, f.Name(), int(sys.Uid), gid)
}
}

View File

@ -17,7 +17,7 @@ func MkdirAll(path string, perm uint32) error {
// If path exists, stop with success or error. // If path exists, stop with success or error.
dir, err := Stat(path) dir, err := Stat(path)
if err == nil { if err == nil {
if dir.IsDirectory() { if dir.IsDir() {
return nil return nil
} }
return &PathError{"mkdir", path, ENOTDIR} return &PathError{"mkdir", path, ENOTDIR}
@ -48,7 +48,7 @@ func MkdirAll(path string, perm uint32) error {
// Handle arguments like "foo/." by // Handle arguments like "foo/." by
// double-checking that directory doesn't exist. // double-checking that directory doesn't exist.
dir, err1 := Lstat(path) dir, err1 := Lstat(path)
if err1 == nil && dir.IsDirectory() { if err1 == nil && dir.IsDir() {
return nil return nil
} }
return err return err
@ -75,7 +75,7 @@ func RemoveAll(path string) error {
} }
return serr return serr
} }
if !dir.IsDirectory() { if !dir.IsDir() {
// Not a directory; return the error from Remove. // Not a directory; return the error from Remove.
return err return err
} }

View File

@ -9,31 +9,48 @@ import (
"time" "time"
) )
func isSymlink(stat *syscall.Stat_t) bool { func sameFile(fs1, fs2 *FileStat) bool {
return stat.Mode&syscall.S_IFMT == syscall.S_IFLNK sys1 := fs1.Sys.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino
} }
func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *FileInfo { func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fi.Dev = uint64(stat.Dev) fs := &FileStat{
fi.Ino = stat.Ino name: basename(name),
fi.Nlink = uint64(stat.Nlink) size: int64(st.Size),
fi.Mode = uint32(stat.Mode) modTime: timespecToTime(st.Mtimespec),
fi.Uid = int(stat.Uid) Sys: st,
fi.Gid = int(stat.Gid)
fi.Rdev = uint64(stat.Rdev)
fi.Size = stat.Size
fi.Blksize = int64(stat.Blksize)
fi.Blocks = stat.Blocks
fi.AccessTime = timespecToTime(stat.Atimespec)
fi.ModTime = timespecToTime(stat.Mtimespec)
fi.ChangeTime = timespecToTime(stat.Ctimespec)
fi.Name = basename(name)
if isSymlink(lstat) && !isSymlink(stat) {
fi.FollowedSymlink = true
} }
return fi fs.mode = FileMode(st.Mode & 0777)
switch st.Mode & syscall.S_IFMT {
case syscall.S_IFBLK, syscall.S_IFCHR, syscall.S_IFWHT:
fs.mode |= ModeDevice
case syscall.S_IFDIR:
fs.mode |= ModeDir
case syscall.S_IFIFO:
fs.mode |= ModeNamedPipe
case syscall.S_IFLNK:
fs.mode |= ModeSymlink
case syscall.S_IFREG:
// nothing to do
case syscall.S_IFSOCK:
fs.mode |= ModeSocket
}
if st.Mode&syscall.S_ISGID != 0 {
fs.mode |= ModeSetgid
}
if st.Mode&syscall.S_ISUID != 0 {
fs.mode |= ModeSetuid
}
return fs
} }
func timespecToTime(ts syscall.Timespec) time.Time { func timespecToTime(ts syscall.Timespec) time.Time {
return time.Unix(int64(ts.Sec), int64(ts.Nsec)) return time.Unix(int64(ts.Sec), int64(ts.Nsec))
} }
// For testing.
func atime(fi FileInfo) time.Time {
return timespecToTime(fi.(*FileStat).Sys.(*syscall.Stat_t).Atimespec)
}

View File

@ -9,31 +9,48 @@ import (
"time" "time"
) )
func isSymlink(stat *syscall.Stat_t) bool { func sameFile(fs1, fs2 *FileStat) bool {
return stat.Mode&syscall.S_IFMT == syscall.S_IFLNK sys1 := fs1.Sys.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino
} }
func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *FileInfo { func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fi.Dev = uint64(stat.Dev) fs := &FileStat{
fi.Ino = uint64(stat.Ino) name: basename(name),
fi.Nlink = uint64(stat.Nlink) size: int64(st.Size),
fi.Mode = uint32(stat.Mode) modTime: timespecToTime(st.Mtimespec),
fi.Uid = int(stat.Uid) Sys: st,
fi.Gid = int(stat.Gid)
fi.Rdev = uint64(stat.Rdev)
fi.Size = int64(stat.Size)
fi.Blksize = int64(stat.Blksize)
fi.Blocks = stat.Blocks
fi.AccessTime = timespecToTime(stat.Atimespec)
fi.ModTime = timespecToTime(stat.Mtimespec)
fi.ChangeTime = timespecToTime(stat.Ctimespec)
fi.Name = basename(name)
if isSymlink(lstat) && !isSymlink(stat) {
fi.FollowedSymlink = true
} }
return fi fs.mode = FileMode(st.Mode & 0777)
switch st.Mode & syscall.S_IFMT {
case syscall.S_IFBLK, syscall.S_IFCHR:
fs.mode |= ModeDevice
case syscall.S_IFDIR:
fs.mode |= ModeDir
case syscall.S_IFIFO:
fs.mode |= ModeNamedPipe
case syscall.S_IFLNK:
fs.mode |= ModeSymlink
case syscall.S_IFREG:
// nothing to do
case syscall.S_IFSOCK:
fs.mode |= ModeSocket
}
if st.Mode&syscall.S_ISGID != 0 {
fs.mode |= ModeSetgid
}
if st.Mode&syscall.S_ISUID != 0 {
fs.mode |= ModeSetuid
}
return fs
} }
func timespecToTime(ts syscall.Timespec) time.Time { func timespecToTime(ts syscall.Timespec) time.Time {
return time.Unix(int64(ts.Sec), int64(ts.Nsec)) return time.Unix(int64(ts.Sec), int64(ts.Nsec))
} }
// For testing.
func atime(fi FileInfo) time.Time {
return timespecToTime(fi.(*FileStat).Sys.(*syscall.Stat_t).Atimespec)
}

View File

@ -9,31 +9,48 @@ import (
"time" "time"
) )
func isSymlink(stat *syscall.Stat_t) bool { func sameFile(fs1, fs2 *FileStat) bool {
return stat.Mode&syscall.S_IFMT == syscall.S_IFLNK sys1 := fs1.Sys.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino
} }
func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *FileInfo { func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fi.Dev = stat.Dev fs := &FileStat{
fi.Ino = stat.Ino name: basename(name),
fi.Nlink = uint64(stat.Nlink) size: int64(st.Size),
fi.Mode = stat.Mode modTime: timespecToTime(st.Mtim),
fi.Uid = int(stat.Uid) Sys: st,
fi.Gid = int(stat.Gid)
fi.Rdev = stat.Rdev
fi.Size = stat.Size
fi.Blksize = int64(stat.Blksize)
fi.Blocks = stat.Blocks
fi.AccessTime = timespecToTime(stat.Atim)
fi.ModTime = timespecToTime(stat.Mtim)
fi.ChangeTime = timespecToTime(stat.Ctim)
fi.Name = basename(name)
if isSymlink(lstat) && !isSymlink(stat) {
fi.FollowedSymlink = true
} }
return fi fs.mode = FileMode(st.Mode & 0777)
switch st.Mode & syscall.S_IFMT {
case syscall.S_IFBLK, syscall.S_IFCHR:
fs.mode |= ModeDevice
case syscall.S_IFDIR:
fs.mode |= ModeDir
case syscall.S_IFIFO:
fs.mode |= ModeNamedPipe
case syscall.S_IFLNK:
fs.mode |= ModeSymlink
case syscall.S_IFREG:
// nothing to do
case syscall.S_IFSOCK:
fs.mode |= ModeSocket
}
if st.Mode&syscall.S_ISGID != 0 {
fs.mode |= ModeSetgid
}
if st.Mode&syscall.S_ISUID != 0 {
fs.mode |= ModeSetuid
}
return fs
} }
func timespecToTime(ts syscall.Timespec) time.Time { func timespecToTime(ts syscall.Timespec) time.Time {
return time.Unix(int64(ts.Sec), int64(ts.Nsec)) return time.Unix(int64(ts.Sec), int64(ts.Nsec))
} }
// For testing.
func atime(fi FileInfo) time.Time {
return timespecToTime(fi.(*FileStat).Sys.(*syscall.Stat_t).Atim)
}

View File

@ -9,31 +9,48 @@ import (
"time" "time"
) )
func isSymlink(stat *syscall.Stat_t) bool { func sameFile(fs1, fs2 *FileStat) bool {
return stat.Mode&syscall.S_IFMT == syscall.S_IFLNK sys1 := fs1.Sys.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino
} }
func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *FileInfo { func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fi.Dev = uint64(stat.Dev) fs := &FileStat{
fi.Ino = uint64(stat.Ino) name: basename(name),
fi.Nlink = uint64(stat.Nlink) size: int64(st.Size),
fi.Mode = uint32(stat.Mode) modTime: timespecToTime(st.Mtim),
fi.Uid = int(stat.Uid) Sys: st,
fi.Gid = int(stat.Gid)
fi.Rdev = uint64(stat.Rdev)
fi.Size = int64(stat.Size)
fi.Blksize = int64(stat.Blksize)
fi.Blocks = stat.Blocks
fi.AccessTime = timespecToTime(stat.Atim)
fi.ModTime = timespecToTime(stat.Mtim)
fi.ChangeTime = timespecToTime(stat.Ctim)
fi.Name = basename(name)
if isSymlink(lstat) && !isSymlink(stat) {
fi.FollowedSymlink = true
} }
return fi fs.mode = FileMode(st.Mode & 0777)
switch st.Mode & syscall.S_IFMT {
case syscall.S_IFBLK, syscall.S_IFCHR:
fs.mode |= ModeDevice
case syscall.S_IFDIR:
fs.mode |= ModeDir
case syscall.S_IFIFO:
fs.mode |= ModeNamedPipe
case syscall.S_IFLNK:
fs.mode |= ModeSymlink
case syscall.S_IFREG:
// nothing to do
case syscall.S_IFSOCK:
fs.mode |= ModeSocket
}
if st.Mode&syscall.S_ISGID != 0 {
fs.mode |= ModeSetgid
}
if st.Mode&syscall.S_ISUID != 0 {
fs.mode |= ModeSetuid
}
return fs
} }
func timespecToTime(ts syscall.Timespec) time.Time { func timespecToTime(ts syscall.Timespec) time.Time {
return time.Unix(int64(ts.Sec), int64(ts.Nsec)) return time.Unix(int64(ts.Sec), int64(ts.Nsec))
} }
// For testing.
func atime(fi FileInfo) time.Time {
return timespecToTime(fi.(*FileStat).Sys.(*syscall.Stat_t).Atim)
}

View File

@ -12,7 +12,7 @@ import (
// Stat returns the FileInfo structure describing file. // Stat returns the FileInfo structure describing file.
// It returns the FileInfo and an error, if any. // It returns the FileInfo and an error, if any.
func (file *File) Stat() (fi *FileInfo, err error) { func (file *File) Stat() (fi FileInfo, err error) {
if file == nil || file.fd < 0 { if file == nil || file.fd < 0 {
return nil, EINVAL return nil, EINVAL
} }
@ -25,7 +25,7 @@ func (file *File) Stat() (fi *FileInfo, err error) {
if e != nil { if e != nil {
return nil, &PathError{"GetFileInformationByHandle", file.name, e} return nil, &PathError{"GetFileInformationByHandle", file.name, e}
} }
return setFileInfo(new(FileInfo), basename(file.name), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime), nil return toFileInfo(basename(file.name), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime), nil
} }
// Stat returns a FileInfo structure describing the named file and an error, if any. // Stat returns a FileInfo structure describing the named file and an error, if any.
@ -33,7 +33,7 @@ func (file *File) Stat() (fi *FileInfo, err error) {
// the file pointed at by the link and has fi.FollowedSymlink set to true. // the file pointed at by the link and has fi.FollowedSymlink set to true.
// If name names an invalid symbolic link, the returned FileInfo describes // If name names an invalid symbolic link, the returned FileInfo describes
// the link itself and has fi.FollowedSymlink set to false. // the link itself and has fi.FollowedSymlink set to false.
func Stat(name string) (fi *FileInfo, err error) { func Stat(name string) (fi FileInfo, err error) {
if len(name) == 0 { if len(name) == 0 {
return nil, &PathError{"Stat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)} return nil, &PathError{"Stat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)}
} }
@ -42,13 +42,13 @@ func Stat(name string) (fi *FileInfo, err error) {
if e != nil { if e != nil {
return nil, &PathError{"GetFileAttributesEx", name, e} return nil, &PathError{"GetFileAttributesEx", name, e}
} }
return setFileInfo(new(FileInfo), basename(name), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime), nil return toFileInfo(basename(name), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime), nil
} }
// Lstat returns the FileInfo structure describing the named file and an // Lstat returns the FileInfo structure describing the named file and an
// error, if any. If the file is a symbolic link, the returned FileInfo // error, if any. If the file is a symbolic link, the returned FileInfo
// describes the symbolic link. Lstat makes no attempt to follow the link. // describes the symbolic link. Lstat makes no attempt to follow the link.
func Lstat(name string) (fi *FileInfo, err error) { func Lstat(name string) (fi FileInfo, err error) {
// No links on Windows // No links on Windows
return Stat(name) return Stat(name)
} }
@ -77,23 +77,23 @@ func basename(name string) string {
return name return name
} }
func setFileInfo(fi *FileInfo, name string, fa, sizehi, sizelo uint32, ctime, atime, wtime syscall.Filetime) *FileInfo { func toFileInfo(name string, fa, sizehi, sizelo uint32, ctime, atime, wtime syscall.Filetime) FileInfo {
fi.Mode = 0 fs := new(FileStat)
fs.mode = 0
if fa&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { if fa&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
fi.Mode = fi.Mode | syscall.S_IFDIR fs.mode |= ModeDir
} else {
fi.Mode = fi.Mode | syscall.S_IFREG
} }
if fa&syscall.FILE_ATTRIBUTE_READONLY != 0 { if fa&syscall.FILE_ATTRIBUTE_READONLY != 0 {
fi.Mode = fi.Mode | 0444 fs.mode |= 0444
} else { } else {
fi.Mode = fi.Mode | 0666 fs.mode |= 0666
} }
fi.Size = int64(sizehi)<<32 + int64(sizelo) fs.size = int64(sizehi)<<32 + int64(sizelo)
fi.Name = name fs.name = name
fi.FollowedSymlink = false fs.modTime = time.Unix(0, wtime.Nanoseconds())
fi.AccessTime = time.Unix(0, atime.Nanoseconds()) return fs
fi.ModTime = time.Unix(0, wtime.Nanoseconds()) }
fi.ChangeTime = time.Unix(0, ctime.Nanoseconds())
return fi func sameFile(fs1, fs2 *FileStat) bool {
return false
} }

View File

@ -9,51 +9,103 @@ import (
"time" "time"
) )
// An operating-system independent representation of Unix data structures.
// OS-specific routines in this directory convert the OS-local versions to these.
// Getpagesize returns the underlying system's memory page size. // Getpagesize returns the underlying system's memory page size.
func Getpagesize() int { return syscall.Getpagesize() } func Getpagesize() int { return syscall.Getpagesize() }
// A FileInfo describes a file and is returned by Stat, Fstat, and Lstat // A FileInfo describes a file and is returned by Stat and Lstat
type FileInfo struct { type FileInfo interface {
Dev uint64 // device number of file system holding file. Name() string // base name of the file
Ino uint64 // inode number. Size() int64 // length in bytes
Nlink uint64 // number of hard links. Mode() FileMode // file mode bits
Mode uint32 // permission and mode bits. ModTime() time.Time // modification time
Uid int // user id of owner. IsDir() bool // abbreviation for Mode().IsDir()
Gid int // group id of owner.
Rdev uint64 // device type for special file.
Size int64 // length in bytes.
Blksize int64 // size of blocks, in bytes.
Blocks int64 // number of blocks allocated for file.
AccessTime time.Time // access time
ModTime time.Time // modification time
ChangeTime time.Time // status change time
Name string // base name of the file name provided in Open, Stat, etc.
FollowedSymlink bool // followed a symlink to get this information
} }
// IsFifo reports whether the FileInfo describes a FIFO file. // A FileMode represents a file's mode and permission bits.
func (f *FileInfo) IsFifo() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFIFO } // The bits have the same definition on all systems, so that
// information about files can be moved from one system
// to another portably. Not all bits apply to all systems.
// The only required bit is ModeDir for directories.
type FileMode uint32
// IsChar reports whether the FileInfo describes a character special file. // The defined file mode bits are the most significant bits of the FileMode.
func (f *FileInfo) IsChar() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFCHR } // The nine least-significant bits are the standard Unix rwxrwxrwx permissions.
const (
// The single letters are the abbreviations
// used by the String method's formatting.
ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory
ModeAppend // a: append-only
ModeExclusive // l: exclusive use
ModeTemporary // t: temporary file (not backed up)
ModeSymlink // L: symbolic link
ModeDevice // D: device file
ModeNamedPipe // p: named pipe (FIFO)
ModeSocket // S: Unix domain socket
ModeSetuid // u: setuid
ModeSetgid // g: setgid
// IsDirectory reports whether the FileInfo describes a directory. ModePerm FileMode = 0777 // permission bits
func (f *FileInfo) IsDirectory() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFDIR } )
// IsBlock reports whether the FileInfo describes a block special file. func (m FileMode) String() string {
func (f *FileInfo) IsBlock() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFBLK } const str = "daltLDpSug"
var buf [20]byte
w := 0
for i, c := range str {
if m&(1<<uint(32-1-i)) != 0 {
buf[w] = byte(c)
w++
}
}
if w == 0 {
buf[w] = '-'
w++
}
const rwx = "rwxrwxrwx"
for i, c := range rwx {
if m&(1<<uint(9-1-i)) != 0 {
buf[w] = byte(c)
} else {
buf[w] = '-'
}
w++
}
return string(buf[:w])
}
// IsRegular reports whether the FileInfo describes a regular file. // IsDir reports whether m describes a directory.
func (f *FileInfo) IsRegular() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFREG } // That is, it tests for the ModeDir bit being set in m.
func (m FileMode) IsDir() bool {
return m&ModeDir != 0
}
// IsSymlink reports whether the FileInfo describes a symbolic link. // Perm returns the Unix permission bits in m.
func (f *FileInfo) IsSymlink() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFLNK } func (m FileMode) Perm() FileMode {
return m & ModePerm
}
// IsSocket reports whether the FileInfo describes a socket. // A FileStat is the implementation of FileInfo returned by Stat and Lstat.
func (f *FileInfo) IsSocket() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFSOCK } // Clients that need access to the underlying system-specific stat information
// can test for *os.FileStat and then consult the Sys field.
type FileStat struct {
name string
size int64
mode FileMode
modTime time.Time
// Permission returns the file permission bits. Sys interface{}
func (f *FileInfo) Permission() uint32 { return f.Mode & 0777 } }
func (fs *FileStat) Name() string { return fs.name }
func (fs *FileStat) Size() int64 { return fs.size }
func (fs *FileStat) Mode() FileMode { return fs.mode }
func (fs *FileStat) ModTime() time.Time { return fs.modTime }
func (fs *FileStat) IsDir() bool { return fs.mode.IsDir() }
// SameFile reports whether fs and other describe the same file.
// For example, on Unix this means that the device and inode fields
// of the two underlying structures are identical; on other systems
// the decision may be based on the path names.
func (fs *FileStat) SameFile(other *FileStat) bool {
return sameFile(fs, other)
}

View File

@ -41,8 +41,8 @@ func TestLookup(t *testing.T) {
t.Errorf("expected Uid of %d; got %d", e, g) t.Errorf("expected Uid of %d; got %d", e, g)
} }
fi, err := os.Stat(u.HomeDir) fi, err := os.Stat(u.HomeDir)
if err != nil || !fi.IsDirectory() { if err != nil || !fi.IsDir() {
t.Errorf("expected a valid HomeDir; stat(%q): err=%v, IsDirectory=%v", u.HomeDir, err, fi.IsDirectory()) t.Errorf("expected a valid HomeDir; stat(%q): err=%v, IsDir=%v", u.HomeDir, err, fi.IsDir())
} }
if u.Username == "" { if u.Username == "" {
t.Fatalf("didn't get a username") t.Fatalf("didn't get a username")

View File

@ -260,7 +260,7 @@ func glob(dir, pattern string, matches []string) (m []string, e error) {
if err != nil { if err != nil {
return return
} }
if !fi.IsDirectory() { if !fi.IsDir() {
return return
} }
d, err := os.Open(dir) d, err := os.Open(dir)

View File

@ -223,7 +223,7 @@ func EvalSymlinks(path string) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
if !fi.IsSymlink() { if fi.Mode()&os.ModeSymlink == 0 {
b.WriteString(p) b.WriteString(p)
if path != "" { if path != "" {
b.WriteRune(Separator) b.WriteRune(Separator)
@ -345,19 +345,19 @@ var SkipDir = errors.New("skip this directory")
// sole exception is that if path is a directory and the function returns the // sole exception is that if path is a directory and the function returns the
// special value SkipDir, the contents of the directory are skipped // special value SkipDir, the contents of the directory are skipped
// and processing continues as usual on the next file. // and processing continues as usual on the next file.
type WalkFunc func(path string, info *os.FileInfo, err error) error type WalkFunc func(path string, info os.FileInfo, err error) error
// walk recursively descends path, calling w. // walk recursively descends path, calling w.
func walk(path string, info *os.FileInfo, walkFn WalkFunc) error { func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
err := walkFn(path, info, nil) err := walkFn(path, info, nil)
if err != nil { if err != nil {
if info.IsDirectory() && err == SkipDir { if info.IsDir() && err == SkipDir {
return nil return nil
} }
return err return err
} }
if !info.IsDirectory() { if !info.IsDir() {
return nil return nil
} }
@ -367,7 +367,7 @@ func walk(path string, info *os.FileInfo, walkFn WalkFunc) error {
} }
for _, fileInfo := range list { for _, fileInfo := range list {
if err = walk(Join(path, fileInfo.Name), fileInfo, walkFn); err != nil { if err = walk(Join(path, fileInfo.Name()), fileInfo, walkFn); err != nil {
return err return err
} }
} }
@ -390,7 +390,7 @@ func Walk(root string, walkFn WalkFunc) error {
// readDir reads the directory named by dirname and returns // readDir reads the directory named by dirname and returns
// a sorted list of directory entries. // a sorted list of directory entries.
// Copied from io/ioutil to avoid the circular import. // Copied from io/ioutil to avoid the circular import.
func readDir(dirname string) ([]*os.FileInfo, error) { func readDir(dirname string) ([]os.FileInfo, error) {
f, err := os.Open(dirname) f, err := os.Open(dirname)
if err != nil { if err != nil {
return nil, err return nil, err
@ -400,20 +400,16 @@ func readDir(dirname string) ([]*os.FileInfo, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
fi := make(fileInfoList, len(list)) sort.Sort(byName(list))
for i := range list { return list, nil
fi[i] = &list[i]
}
sort.Sort(fi)
return fi, nil
} }
// A dirList implements sort.Interface. // byName implements sort.Interface.
type fileInfoList []*os.FileInfo type byName []os.FileInfo
func (f fileInfoList) Len() int { return len(f) } func (f byName) Len() int { return len(f) }
func (f fileInfoList) Less(i, j int) bool { return f[i].Name < f[j].Name } func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
func (f fileInfoList) Swap(i, j int) { f[i], f[j] = f[j], f[i] } func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
// Base returns the last element of path. // Base returns the last element of path.
// Trailing path separators are removed before extracting the last element. // Trailing path separators are removed before extracting the last element.

View File

@ -318,7 +318,7 @@ func checkMarks(t *testing.T, report bool) {
// Assumes that each node name is unique. Good enough for a test. // Assumes that each node name is unique. Good enough for a test.
// If clear is true, any incoming error is cleared before return. The errors // If clear is true, any incoming error is cleared before return. The errors
// are always accumulated, though. // are always accumulated, though.
func mark(path string, info *os.FileInfo, err error, errors *[]error, clear bool) error { func mark(path string, info os.FileInfo, err error, errors *[]error, clear bool) error {
if err != nil { if err != nil {
*errors = append(*errors, err) *errors = append(*errors, err)
if clear { if clear {
@ -326,8 +326,9 @@ func mark(path string, info *os.FileInfo, err error, errors *[]error, clear bool
} }
return err return err
} }
name := info.Name()
walkTree(tree, tree.name, func(path string, n *Node) { walkTree(tree, tree.name, func(path string, n *Node) {
if n.name == info.Name { if n.name == name {
n.mark++ n.mark++
} }
}) })
@ -338,7 +339,7 @@ func TestWalk(t *testing.T) {
makeTree(t) makeTree(t)
errors := make([]error, 0, 10) errors := make([]error, 0, 10)
clear := true clear := true
markFn := func(path string, info *os.FileInfo, err error) error { markFn := func(path string, info os.FileInfo, err error) error {
return mark(path, info, err, &errors, clear) return mark(path, info, err, &errors, clear)
} }
// Expect no errors. // Expect no errors.
@ -600,7 +601,7 @@ func TestAbs(t *testing.T) {
t.Errorf("Abs(%q) error: %v", path, err) t.Errorf("Abs(%q) error: %v", path, err)
} }
absinfo, err := os.Stat(abspath) absinfo, err := os.Stat(abspath)
if err != nil || absinfo.Ino != info.Ino { if err != nil || !absinfo.(*os.FileStat).SameFile(info.(*os.FileStat)) {
t.Errorf("Abs(%q)=%q, not the same file", path, abspath) t.Errorf("Abs(%q)=%q, not the same file", path, abspath)
} }
if !filepath.IsAbs(abspath) { if !filepath.IsAbs(abspath) {

View File

@ -6,10 +6,10 @@
package time package time
import ( //import (
"strconv" // "strconv"
"strings" // "strings"
) //)
func parseZones(s string) (zt []zonetime) { func parseZones(s string) (zt []zonetime) {
f := strings.Fields(s) f := strings.Fields(s)