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 } ...@@ -47,6 +47,7 @@ func (fi *fileInfo) Mode() os.FileMode { return fi.mode }
func (fi *fileInfo) Size() int64 { return fi.size } func (fi *fileInfo) Size() int64 { return fi.size }
func (fi *fileInfo) ModTime() time.Time { return fi.mtime } func (fi *fileInfo) ModTime() time.Time { return fi.mtime }
func (fi *fileInfo) IsDir() bool { return fi.mode.IsDir() } 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 // httpZipFile is the zip-file based implementation of http.File
type httpZipFile struct { type httpZipFile struct {
......
...@@ -65,6 +65,10 @@ func (fi zipFI) IsDir() bool { ...@@ -65,6 +65,10 @@ func (fi zipFI) IsDir() bool {
return fi.file == nil return fi.file == nil
} }
func (fi zipFI) Sys() interface{} {
return nil
}
// zipFS is the zip-file based implementation of FileSystem // zipFS is the zip-file based implementation of FileSystem
type zipFS struct { type zipFS struct {
*zip.ReadCloser *zip.ReadCloser
......
...@@ -71,6 +71,7 @@ func (fi headerFileInfo) Size() int64 { return int64(fi.fh.UncompressedSi ...@@ -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) IsDir() bool { return fi.Mode().IsDir() }
func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() } func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() }
func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() } 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 // FileInfoHeader creates a partially-populated FileHeader from an
// os.FileInfo. // os.FileInfo.
......
...@@ -85,4 +85,7 @@ func TestFileHeaderRoundTrip(t *testing.T) { ...@@ -85,4 +85,7 @@ func TestFileHeaderRoundTrip(t *testing.T) {
if !reflect.DeepEqual(fh, fh2) { if !reflect.DeepEqual(fh, fh2) {
t.Errorf("mismatch\n input=%#v\noutput=%#v\nerr=%v", fh, fh2, err) 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) { ...@@ -190,7 +190,7 @@ 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.(*os.FileStat).SameFile(wfi.(*os.FileStat)) { if !os.SameFile(gfi, wfi) {
t.Errorf("%s got different file", name) t.Errorf("%s got different file", name)
} }
} }
......
...@@ -152,7 +152,7 @@ func (f *File) readdir(n int) (fi []FileInfo, err error) { ...@@ -152,7 +152,7 @@ func (f *File) readdir(n int) (fi []FileInfo, err error) {
if err == nil { if err == nil {
fi[i] = fip fi[i] = fip
} else { } else {
fi[i] = &FileStat{name: filename} fi[i] = &fileStat{name: filename}
} }
} }
return fi, err return fi, err
......
...@@ -30,7 +30,7 @@ func Getwd() (pwd string, err error) { ...@@ -30,7 +30,7 @@ func Getwd() (pwd string, err error) {
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 && dot.(*FileStat).SameFile(d.(*FileStat)) { if err == nil && SameFile(dot, d) {
return pwd, nil return pwd, nil
} }
} }
...@@ -42,7 +42,7 @@ func Getwd() (pwd string, err error) { ...@@ -42,7 +42,7 @@ func Getwd() (pwd string, err error) {
// Can't stat root - no hope. // Can't stat root - no hope.
return "", err return "", err
} }
if root.(*FileStat).SameFile(dot.(*FileStat)) { if SameFile(root, dot) {
return "/", nil return "/", nil
} }
...@@ -67,7 +67,7 @@ func Getwd() (pwd string, err error) { ...@@ -67,7 +67,7 @@ func Getwd() (pwd string, err error) {
} }
for _, name := range names { for _, name := range names {
d, _ := Lstat(parent + "/" + name) d, _ := Lstat(parent + "/" + name)
if d.(*FileStat).SameFile(dot.(*FileStat)) { if SameFile(d, dot) {
pwd = "/" + name + pwd pwd = "/" + name + pwd
goto Found goto Found
} }
...@@ -82,7 +82,7 @@ func Getwd() (pwd string, err error) { ...@@ -82,7 +82,7 @@ func Getwd() (pwd string, err error) {
return "", err return "", err
} }
fd.Close() fd.Close()
if pd.(*FileStat).SameFile(root.(*FileStat)) { if SameFile(pd, root) {
break break
} }
// Set up for next round. // Set up for next round.
......
...@@ -408,7 +408,7 @@ func TestHardLink(t *testing.T) { ...@@ -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.(*FileStat).SameFile(fromstat.(*FileStat)) { if !SameFile(tostat, fromstat) {
t.Errorf("link %q, %q did not create hard link", to, from) t.Errorf("link %q, %q did not create hard link", to, from)
} }
} }
...@@ -444,7 +444,7 @@ func TestSymLink(t *testing.T) { ...@@ -444,7 +444,7 @@ func TestSymLink(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.(*FileStat).SameFile(fromstat.(*FileStat)) { if !SameFile(tostat, fromstat) {
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)
...@@ -658,7 +658,7 @@ func TestChtimes(t *testing.T) { ...@@ -658,7 +658,7 @@ func TestChtimes(t *testing.T) {
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) preStat := st
// Move access and modification time back a second // Move access and modification time back a second
at := Atime(preStat) at := Atime(preStat)
...@@ -672,7 +672,7 @@ func TestChtimes(t *testing.T) { ...@@ -672,7 +672,7 @@ func TestChtimes(t *testing.T) {
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) postStat := st
/* 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
......
...@@ -18,7 +18,7 @@ func checkUidGid(t *testing.T, path string, uid, gid int) { ...@@ -18,7 +18,7 @@ func checkUidGid(t *testing.T, path string, uid, gid int) {
if err != nil { if err != nil {
t.Fatalf("Stat %q (looking for uid/gid %d/%d): %s", path, uid, gid, err) 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 { if int(sys.Uid) != uid {
t.Errorf("Stat %q: uid %d want %d", path, sys.Uid, uid) t.Errorf("Stat %q: uid %d want %d", path, sys.Uid, uid)
} }
...@@ -52,7 +52,7 @@ func TestChown(t *testing.T) { ...@@ -52,7 +52,7 @@ func TestChown(t *testing.T) {
if err = Chown(f.Name(), -1, gid); err != nil { if err = Chown(f.Name(), -1, gid); err != nil {
t.Fatalf("chown %s -1 %d: %s", f.Name(), gid, err) 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) checkUidGid(t, f.Name(), int(sys.Uid), gid)
// Then try all the auxiliary groups. // Then try all the auxiliary groups.
......
...@@ -9,18 +9,18 @@ import ( ...@@ -9,18 +9,18 @@ import (
"time" "time"
) )
func sameFile(fs1, fs2 *FileStat) bool { func sameFile(sys1, sys2 interface{}) bool {
sys1 := fs1.Sys.(*syscall.Stat_t) stat1 := sys1.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t) stat2 := sys2.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
} }
func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo { func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fs := &FileStat{ fs := &fileStat{
name: basename(name), name: basename(name),
size: int64(st.Size), size: int64(st.Size),
modTime: timespecToTime(st.Mtimespec), modTime: timespecToTime(st.Mtimespec),
Sys: st, sys: st,
} }
fs.mode = FileMode(st.Mode & 0777) fs.mode = FileMode(st.Mode & 0777)
switch st.Mode & syscall.S_IFMT { switch st.Mode & syscall.S_IFMT {
...@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time { ...@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time {
// For testing. // For testing.
func atime(fi FileInfo) time.Time { 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 ( ...@@ -9,18 +9,18 @@ import (
"time" "time"
) )
func sameFile(fs1, fs2 *FileStat) bool { func sameFile(sys1, sys2 interface{}) bool {
sys1 := fs1.Sys.(*syscall.Stat_t) stat1 := sys1.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t) stat2 := sys2.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
} }
func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo { func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fs := &FileStat{ fs := &fileStat{
name: basename(name), name: basename(name),
size: int64(st.Size), size: int64(st.Size),
modTime: timespecToTime(st.Mtimespec), modTime: timespecToTime(st.Mtimespec),
Sys: st, sys: st,
} }
fs.mode = FileMode(st.Mode & 0777) fs.mode = FileMode(st.Mode & 0777)
switch st.Mode & syscall.S_IFMT { switch st.Mode & syscall.S_IFMT {
...@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time { ...@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time {
// For testing. // For testing.
func atime(fi FileInfo) time.Time { 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 ( ...@@ -9,18 +9,18 @@ import (
"time" "time"
) )
func sameFile(fs1, fs2 *FileStat) bool { func sameFile(sys1, sys2 interface{}) bool {
sys1 := fs1.Sys.(*syscall.Stat_t) stat1 := sys1.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t) stat2 := sys2.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
} }
func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo { func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fs := &FileStat{ fs := &fileStat{
name: basename(name), name: basename(name),
size: int64(st.Size), size: int64(st.Size),
modTime: timespecToTime(st.Mtim), modTime: timespecToTime(st.Mtim),
Sys: st, sys: st,
} }
fs.mode = FileMode(st.Mode & 0777) fs.mode = FileMode(st.Mode & 0777)
switch st.Mode & syscall.S_IFMT { switch st.Mode & syscall.S_IFMT {
...@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time { ...@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time {
// For testing. // For testing.
func atime(fi FileInfo) time.Time { 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 ( ...@@ -9,14 +9,14 @@ import (
"time" "time"
) )
func sameFile(fs1, fs2 *FileStat) bool { func sameFile(sys1, sys2 interface{}) bool {
sys1 := fs1.Sys.(*syscall.Stat_t) stat1 := sys1.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t) stat2 := sys2.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
} }
func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo { func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fs := &FileStat{ fs := &fileStat{
name: basename(name), name: basename(name),
size: int64(st.Size), size: int64(st.Size),
modTime: timespecToTime(st.Mtim), modTime: timespecToTime(st.Mtim),
...@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time { ...@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time {
// For testing. // For testing.
func atime(fi FileInfo) time.Time { 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 ( ...@@ -9,18 +9,18 @@ import (
"time" "time"
) )
func sameFile(fs1, fs2 *FileStat) bool { func sameFile(sys1, sys2 interface{}) bool {
sys1 := fs1.Sys.(*syscall.Stat_t) stat1 := sys1.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t) stat2 := sys2.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
} }
func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo { func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fs := &FileStat{ fs := &fileStat{
name: basename(name), name: basename(name),
size: int64(st.Size), size: int64(st.Size),
modTime: timespecToTime(st.Mtim), modTime: timespecToTime(st.Mtim),
Sys: st, sys: st,
} }
fs.mode = FileMode(st.Mode & 0777) fs.mode = FileMode(st.Mode & 0777)
switch st.Mode & syscall.S_IFMT { switch st.Mode & syscall.S_IFMT {
...@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time { ...@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time {
// For testing. // For testing.
func atime(fi FileInfo) time.Time { 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 ( ...@@ -9,18 +9,18 @@ import (
"time" "time"
) )
func sameFile(fs1, fs2 *FileStat) bool { func sameFile(sys1, sys2 interface{}) bool {
a := fs1.Sys.(*Dir) a := sys1.(*Dir)
b := fs2.Sys.(*Dir) b := sys2.(*Dir)
return a.Qid.Path == b.Qid.Path && a.Type == b.Type && a.Dev == b.Dev return a.Qid.Path == b.Qid.Path && a.Type == b.Type && a.Dev == b.Dev
} }
func fileInfoFromStat(d *Dir) FileInfo { func fileInfoFromStat(d *Dir) FileInfo {
fs := &FileStat{ fs := &fileStat{
name: d.Name, name: d.Name,
size: int64(d.Length), size: int64(d.Length),
modTime: time.Unix(int64(d.Mtime), 0), modTime: time.Unix(int64(d.Mtime), 0),
Sys: d, sys: d,
} }
fs.mode = FileMode(d.Mode & 0777) fs.mode = FileMode(d.Mode & 0777)
if d.Mode&syscall.DMDIR != 0 { if d.Mode&syscall.DMDIR != 0 {
...@@ -100,5 +100,5 @@ func Lstat(name string) (FileInfo, error) { ...@@ -100,5 +100,5 @@ func Lstat(name string) (FileInfo, error) {
// For testing. // For testing.
func atime(fi FileInfo) time.Time { 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 { ...@@ -82,8 +82,12 @@ type winTimes struct {
} }
func toFileInfo(name string, fa, sizehi, sizelo uint32, ctime, atime, mtime syscall.Filetime) FileInfo { func toFileInfo(name string, fa, sizehi, sizelo uint32, ctime, atime, mtime syscall.Filetime) FileInfo {
fs := new(FileStat) fs := &fileStat{
fs.mode = 0 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 { if fa&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
fs.mode |= ModeDir fs.mode |= ModeDir
} }
...@@ -92,14 +96,10 @@ func toFileInfo(name string, fa, sizehi, sizelo uint32, ctime, atime, mtime sysc ...@@ -92,14 +96,10 @@ func toFileInfo(name string, fa, sizehi, sizelo uint32, ctime, atime, mtime sysc
} else { } else {
fs.mode |= 0666 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 return fs
} }
func sameFile(fs1, fs2 *FileStat) bool { func sameFile(sys1, sys2 interface{}) bool {
// TODO(rsc): Do better than this, but this matches what // TODO(rsc): Do better than this, but this matches what
// used to happen when code compared .Dev and .Ino, // used to happen when code compared .Dev and .Ino,
// which were both always zero. Obviously not all files // which were both always zero. Obviously not all files
...@@ -109,5 +109,5 @@ func sameFile(fs1, fs2 *FileStat) bool { ...@@ -109,5 +109,5 @@ func sameFile(fs1, fs2 *FileStat) bool {
// For testing. // For testing.
func atime(fi FileInfo) time.Time { 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 { ...@@ -19,6 +19,7 @@ type FileInfo interface {
Mode() FileMode // file mode bits Mode() FileMode // file mode bits
ModTime() time.Time // modification time ModTime() time.Time // modification time
IsDir() bool // abbreviation for Mode().IsDir() IsDir() bool // abbreviation for Mode().IsDir()
Sys() interface{} // underlying data source (can return nil)
} }
// A FileMode represents a file's mode and permission bits. // A FileMode represents a file's mode and permission bits.
...@@ -92,28 +93,33 @@ func (m FileMode) Perm() FileMode { ...@@ -92,28 +93,33 @@ func (m FileMode) Perm() FileMode {
return m & ModePerm return m & ModePerm
} }
// A FileStat is the implementation of FileInfo returned by Stat and Lstat. // A fileStat is the implementation of FileInfo returned by Stat and Lstat.
// Clients that need access to the underlying system-specific stat information type fileStat struct {
// can test for *os.FileStat and then consult the Sys field.
type FileStat struct {
name string name string
size int64 size int64
mode FileMode mode FileMode
modTime time.Time modTime time.Time
sys interface{}
Sys interface{}
} }
func (fs *FileStat) Name() string { return fs.name } func (fs *fileStat) Name() string { return fs.name }
func (fs *FileStat) Size() int64 { return fs.size } func (fs *fileStat) Size() int64 { return fs.size }
func (fs *FileStat) Mode() FileMode { return fs.mode } func (fs *fileStat) Mode() FileMode { return fs.mode }
func (fs *FileStat) ModTime() time.Time { return fs.modTime } func (fs *fileStat) ModTime() time.Time { return fs.modTime }
func (fs *FileStat) IsDir() bool { return fs.mode.IsDir() } 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 // For example, on Unix this means that the device and inode fields
// of the two underlying structures are identical; on other systems // of the two underlying structures are identical; on other systems
// the decision may be based on the path names. // the decision may be based on the path names.
func (fs *FileStat) SameFile(other *FileStat) bool { // SameFile only applies to results returned by this package's Stat.
return sameFile(fs, other) // 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) { ...@@ -667,7 +667,7 @@ func TestAbs(t *testing.T) {
continue continue
} }
absinfo, err := os.Stat(abspath) 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) t.Errorf("Abs(%q)=%q, not the same file", path, abspath)
} }
if !filepath.IsAbs(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