Commit 20f4385a authored by Gustavo Niemeyer's avatar Gustavo Niemeyer

os: turn FileStat.Sys into a method on FileInfo

This reduces the overhead necessary to work with OS-specific
file details, hides the implementation of FileStat, and
preserves the implementation-specific nature of Sys.

Expressions such as:

  stat.(*os.FileInfo).Sys.(*syscall.Stat_t).Uid
  fi1.(*os.FileStat).SameFile(fi2.(*os.FileStat))

Are now spelled as::

  stat.Sys().(*syscall.Stat_t).Uid
  os.SameFile(fi1, fi2)

R=cw, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/5448079
parent b9474de2
......@@ -47,6 +47,7 @@ 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() }
func (fi *fileInfo) Sys() interface{} { return nil }
// httpZipFile is the zip-file based implementation of http.File
type httpZipFile struct {
......
......@@ -65,6 +65,10 @@ func (fi zipFI) IsDir() bool {
return fi.file == nil
}
func (fi zipFI) Sys() interface{} {
return nil
}
// zipFS is the zip-file based implementation of FileSystem
type zipFS struct {
*zip.ReadCloser
......
......@@ -71,6 +71,7 @@ func (fi headerFileInfo) Size() int64 { return int64(fi.fh.UncompressedSi
func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() }
func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() }
func (fi headerFileInfo) Sys() interface{} { return fi.fh }
// FileInfoHeader creates a partially-populated FileHeader from an
// os.FileInfo.
......
......@@ -85,4 +85,7 @@ func TestFileHeaderRoundTrip(t *testing.T) {
if !reflect.DeepEqual(fh, fh2) {
t.Errorf("mismatch\n input=%#v\noutput=%#v\nerr=%v", fh, fh2, err)
}
if sysfh, ok := fi.Sys().(*FileHeader); !ok && sysfh != fh {
t.Errorf("Sys didn't return original *FileHeader")
}
}
......@@ -190,7 +190,7 @@ func TestDirJoin(t *testing.T) {
if err != nil {
t.Fatalf("stat of %s: %v", name, err)
}
if !gfi.(*os.FileStat).SameFile(wfi.(*os.FileStat)) {
if !os.SameFile(gfi, wfi) {
t.Errorf("%s got different file", name)
}
}
......
......@@ -152,7 +152,7 @@ func (f *File) readdir(n int) (fi []FileInfo, err error) {
if err == nil {
fi[i] = fip
} else {
fi[i] = &FileStat{name: filename}
fi[i] = &fileStat{name: filename}
}
}
return fi, err
......
......@@ -30,7 +30,7 @@ func Getwd() (pwd string, err error) {
pwd = Getenv("PWD")
if len(pwd) > 0 && pwd[0] == '/' {
d, err := Stat(pwd)
if err == nil && dot.(*FileStat).SameFile(d.(*FileStat)) {
if err == nil && SameFile(dot, d) {
return pwd, nil
}
}
......@@ -42,7 +42,7 @@ func Getwd() (pwd string, err error) {
// Can't stat root - no hope.
return "", err
}
if root.(*FileStat).SameFile(dot.(*FileStat)) {
if SameFile(root, dot) {
return "/", nil
}
......@@ -67,7 +67,7 @@ func Getwd() (pwd string, err error) {
}
for _, name := range names {
d, _ := Lstat(parent + "/" + name)
if d.(*FileStat).SameFile(dot.(*FileStat)) {
if SameFile(d, dot) {
pwd = "/" + name + pwd
goto Found
}
......@@ -82,7 +82,7 @@ func Getwd() (pwd string, err error) {
return "", err
}
fd.Close()
if pd.(*FileStat).SameFile(root.(*FileStat)) {
if SameFile(pd, root) {
break
}
// Set up for next round.
......
......@@ -408,7 +408,7 @@ func TestHardLink(t *testing.T) {
if err != nil {
t.Fatalf("stat %q failed: %v", from, err)
}
if !tostat.(*FileStat).SameFile(fromstat.(*FileStat)) {
if !SameFile(tostat, fromstat) {
t.Errorf("link %q, %q did not create hard link", to, from)
}
}
......@@ -444,7 +444,7 @@ func TestSymLink(t *testing.T) {
if err != nil {
t.Fatalf("stat %q failed: %v", from, err)
}
if !tostat.(*FileStat).SameFile(fromstat.(*FileStat)) {
if !SameFile(tostat, fromstat) {
t.Errorf("symlink %q, %q did not create symlink", to, from)
}
fromstat, err = Lstat(from)
......@@ -658,7 +658,7 @@ func TestChtimes(t *testing.T) {
if err != nil {
t.Fatalf("Stat %s: %s", f.Name(), err)
}
preStat := st.(*FileStat)
preStat := st
// Move access and modification time back a second
at := Atime(preStat)
......@@ -672,7 +672,7 @@ func TestChtimes(t *testing.T) {
if err != nil {
t.Fatalf("second Stat %s: %s", f.Name(), err)
}
postStat := st.(*FileStat)
postStat := st
/* Plan 9:
Mtime is the time of the last change of content. Similarly, atime is set whenever the
......
......@@ -18,7 +18,7 @@ func checkUidGid(t *testing.T, path string, uid, gid int) {
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)
sys := dir.Sys().(*syscall.Stat_t)
if int(sys.Uid) != uid {
t.Errorf("Stat %q: uid %d want %d", path, sys.Uid, uid)
}
......@@ -52,7 +52,7 @@ func TestChown(t *testing.T) {
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)
sys := dir.Sys().(*syscall.Stat_t)
checkUidGid(t, f.Name(), int(sys.Uid), gid)
// Then try all the auxiliary groups.
......
......@@ -9,18 +9,18 @@ import (
"time"
)
func sameFile(fs1, fs2 *FileStat) bool {
sys1 := fs1.Sys.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino
func sameFile(sys1, sys2 interface{}) bool {
stat1 := sys1.(*syscall.Stat_t)
stat2 := sys2.(*syscall.Stat_t)
return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
}
func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fs := &FileStat{
fs := &fileStat{
name: basename(name),
size: int64(st.Size),
modTime: timespecToTime(st.Mtimespec),
Sys: st,
sys: st,
}
fs.mode = FileMode(st.Mode & 0777)
switch st.Mode & syscall.S_IFMT {
......@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time {
// For testing.
func atime(fi FileInfo) time.Time {
return timespecToTime(fi.(*FileStat).Sys.(*syscall.Stat_t).Atimespec)
return timespecToTime(fi.Sys().(*syscall.Stat_t).Atimespec)
}
......@@ -9,18 +9,18 @@ import (
"time"
)
func sameFile(fs1, fs2 *FileStat) bool {
sys1 := fs1.Sys.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino
func sameFile(sys1, sys2 interface{}) bool {
stat1 := sys1.(*syscall.Stat_t)
stat2 := sys2.(*syscall.Stat_t)
return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
}
func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fs := &FileStat{
fs := &fileStat{
name: basename(name),
size: int64(st.Size),
modTime: timespecToTime(st.Mtimespec),
Sys: st,
sys: st,
}
fs.mode = FileMode(st.Mode & 0777)
switch st.Mode & syscall.S_IFMT {
......@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time {
// For testing.
func atime(fi FileInfo) time.Time {
return timespecToTime(fi.(*FileStat).Sys.(*syscall.Stat_t).Atimespec)
return timespecToTime(fi.Sys().(*syscall.Stat_t).Atimespec)
}
......@@ -9,18 +9,18 @@ import (
"time"
)
func sameFile(fs1, fs2 *FileStat) bool {
sys1 := fs1.Sys.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino
func sameFile(sys1, sys2 interface{}) bool {
stat1 := sys1.(*syscall.Stat_t)
stat2 := sys2.(*syscall.Stat_t)
return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
}
func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fs := &FileStat{
fs := &fileStat{
name: basename(name),
size: int64(st.Size),
modTime: timespecToTime(st.Mtim),
Sys: st,
sys: st,
}
fs.mode = FileMode(st.Mode & 0777)
switch st.Mode & syscall.S_IFMT {
......@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time {
// For testing.
func atime(fi FileInfo) time.Time {
return timespecToTime(fi.(*FileStat).Sys.(*syscall.Stat_t).Atim)
return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim)
}
......@@ -9,14 +9,14 @@ import (
"time"
)
func sameFile(fs1, fs2 *FileStat) bool {
sys1 := fs1.Sys.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino
func sameFile(sys1, sys2 interface{}) bool {
stat1 := sys1.(*syscall.Stat_t)
stat2 := sys2.(*syscall.Stat_t)
return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
}
func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fs := &FileStat{
fs := &fileStat{
name: basename(name),
size: int64(st.Size),
modTime: timespecToTime(st.Mtim),
......@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time {
// For testing.
func atime(fi FileInfo) time.Time {
return timespecToTime(fi.(*FileStat).Sys.(*syscall.Stat_t).Atim)
return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim)
}
......@@ -9,18 +9,18 @@ import (
"time"
)
func sameFile(fs1, fs2 *FileStat) bool {
sys1 := fs1.Sys.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino
func sameFile(sys1, sys2 interface{}) bool {
stat1 := sys1.(*syscall.Stat_t)
stat2 := sys2.(*syscall.Stat_t)
return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
}
func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fs := &FileStat{
fs := &fileStat{
name: basename(name),
size: int64(st.Size),
modTime: timespecToTime(st.Mtim),
Sys: st,
sys: st,
}
fs.mode = FileMode(st.Mode & 0777)
switch st.Mode & syscall.S_IFMT {
......@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time {
// For testing.
func atime(fi FileInfo) time.Time {
return timespecToTime(fi.(*FileStat).Sys.(*syscall.Stat_t).Atim)
return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim)
}
......@@ -9,18 +9,18 @@ import (
"time"
)
func sameFile(fs1, fs2 *FileStat) bool {
a := fs1.Sys.(*Dir)
b := fs2.Sys.(*Dir)
func sameFile(sys1, sys2 interface{}) bool {
a := sys1.(*Dir)
b := sys2.(*Dir)
return a.Qid.Path == b.Qid.Path && a.Type == b.Type && a.Dev == b.Dev
}
func fileInfoFromStat(d *Dir) FileInfo {
fs := &FileStat{
fs := &fileStat{
name: d.Name,
size: int64(d.Length),
modTime: time.Unix(int64(d.Mtime), 0),
Sys: d,
sys: d,
}
fs.mode = FileMode(d.Mode & 0777)
if d.Mode&syscall.DMDIR != 0 {
......@@ -100,5 +100,5 @@ func Lstat(name string) (FileInfo, error) {
// For testing.
func atime(fi FileInfo) time.Time {
return time.Unix(int64(fi.(*FileStat).Sys.(*Dir).Atime), 0)
return time.Unix(int64(fi.Sys().(*Dir).Atime), 0)
}
......@@ -82,8 +82,12 @@ type winTimes struct {
}
func toFileInfo(name string, fa, sizehi, sizelo uint32, ctime, atime, mtime syscall.Filetime) FileInfo {
fs := new(FileStat)
fs.mode = 0
fs := &fileStat{
name: name,
size: int64(sizehi)<<32 + int64(sizelo),
modTime: time.Unix(0, mtime.Nanoseconds()),
sys: &winTimes{atime, ctime},
}
if fa&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
fs.mode |= ModeDir
}
......@@ -92,14 +96,10 @@ func toFileInfo(name string, fa, sizehi, sizelo uint32, ctime, atime, mtime sysc
} else {
fs.mode |= 0666
}
fs.size = int64(sizehi)<<32 + int64(sizelo)
fs.name = name
fs.modTime = time.Unix(0, mtime.Nanoseconds())
fs.Sys = &winTimes{atime, ctime}
return fs
}
func sameFile(fs1, fs2 *FileStat) bool {
func sameFile(sys1, sys2 interface{}) bool {
// TODO(rsc): Do better than this, but this matches what
// used to happen when code compared .Dev and .Ino,
// which were both always zero. Obviously not all files
......@@ -109,5 +109,5 @@ func sameFile(fs1, fs2 *FileStat) bool {
// For testing.
func atime(fi FileInfo) time.Time {
return time.Unix(0, fi.(*FileStat).Sys.(*winTimes).atime.Nanoseconds())
return time.Unix(0, fi.Sys().(*winTimes).atime.Nanoseconds())
}
......@@ -19,6 +19,7 @@ type FileInfo interface {
Mode() FileMode // file mode bits
ModTime() time.Time // modification time
IsDir() bool // abbreviation for Mode().IsDir()
Sys() interface{} // underlying data source (can return nil)
}
// A FileMode represents a file's mode and permission bits.
......@@ -92,28 +93,33 @@ func (m FileMode) Perm() FileMode {
return m & ModePerm
}
// A FileStat is the implementation of FileInfo returned by Stat and Lstat.
// 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 {
// A fileStat is the implementation of FileInfo returned by Stat and Lstat.
type fileStat struct {
name string
size int64
mode FileMode
modTime time.Time
Sys interface{}
sys interface{}
}
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() }
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() }
func (fs *fileStat) Sys() interface{} { return fs.sys }
// SameFile reports whether fs and other describe the same file.
// SameFile reports whether fi1 and fi2 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)
// SameFile only applies to results returned by this package's Stat.
// It returns false in other cases.
func SameFile(fi1, fi2 FileInfo) bool {
fs1, ok1 := fi1.(*fileStat)
fs2, ok2 := fi2.(*fileStat)
if !ok1 || !ok2 {
return false
}
return sameFile(fs1.sys, fs2.sys)
}
......@@ -667,7 +667,7 @@ func TestAbs(t *testing.T) {
continue
}
absinfo, err := os.Stat(abspath)
if err != nil || !absinfo.(*os.FileStat).SameFile(info.(*os.FileStat)) {
if err != nil || !os.SameFile(absinfo, info) {
t.Errorf("Abs(%q)=%q, not the same file", path, abspath)
}
if !filepath.IsAbs(abspath) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment