diff --git a/src/os/file.go b/src/os/file.go index 21892778..3589c377 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -193,11 +193,6 @@ func (f *File) Seek(offset int64, whence int) (ret int64, err error) { return f.handle.Seek(offset, whence) } -// Stat is a stub, not yet implemented -func (f *File) Stat() (FileInfo, error) { - return nil, &PathError{"stat", f.name, ErrNotImplemented} -} - func (f *File) SyscallConn() (syscall.RawConn, error) { return nil, ErrNotImplemented } diff --git a/src/os/os_anyos_test.go b/src/os/os_anyos_test.go index b1d748a7..20328bc8 100644 --- a/src/os/os_anyos_test.go +++ b/src/os/os_anyos_test.go @@ -8,6 +8,7 @@ import ( "path/filepath" "runtime" "strconv" + "strings" "testing" "time" ) @@ -48,6 +49,40 @@ func TestStatBadDir(t *testing.T) { } } +func equal(name1, name2 string) (r bool) { + switch runtime.GOOS { + case "windows": + r = strings.ToLower(name1) == strings.ToLower(name2) + default: + r = name1 == name2 + } + return +} + +func TestFstat(t *testing.T) { + sfname := "TestFstat" + path := TempDir() + "/" + sfname + payload := writeFile(t, path, O_CREATE|O_TRUNC|O_RDWR, "Hello") + defer Remove(path) + + file, err1 := Open(path) + if err1 != nil { + t.Fatal("open failed:", err1) + } + defer file.Close() + dir, err2 := file.Stat() + if err2 != nil { + t.Fatal("fstat failed:", err2) + } + if !equal(sfname, dir.Name()) { + t.Error("name should be ", sfname, "; is", dir.Name()) + } + filesize := len(payload) + if dir.Size() != int64(filesize) { + t.Error("size should be", filesize, "; is", dir.Size()) + } +} + func writeFile(t *testing.T, fname string, flag int, text string) string { f, err := OpenFile(fname, flag, 0666) if err != nil { diff --git a/src/os/stat_other.go b/src/os/stat_other.go index 87615ec2..53555cef 100644 --- a/src/os/stat_other.go +++ b/src/os/stat_other.go @@ -11,6 +11,11 @@ func (f *File) Sync() error { return ErrNotImplemented } +// Stat is a stub, not yet implemented +func (f *File) Stat() (FileInfo, error) { + return nil, ErrNotImplemented +} + // statNolog stats a file with no test logging. func statNolog(name string) (FileInfo, error) { return nil, &PathError{Op: "stat", Path: name, Err: ErrNotImplemented} diff --git a/src/os/stat_unix.go b/src/os/stat_unix.go index 9187b54e..02e3e1aa 100644 --- a/src/os/stat_unix.go +++ b/src/os/stat_unix.go @@ -15,6 +15,20 @@ func (f *File) Sync() error { return ErrNotImplemented } +// Stat returns the FileInfo structure describing file. +// If there is an error, it will be of type *PathError. +func (f *File) Stat() (FileInfo, error) { + var fs fileStat + err := ignoringEINTR(func() error { + return syscall.Fstat(int(f.handle.(unixFileHandle)), &fs.sys) + }) + if err != nil { + return nil, &PathError{Op: "fstat", Path: f.name, Err: err} + } + fillFileStatFromSys(&fs, f.name) + return &fs, nil +} + // statNolog stats a file with no test logging. func statNolog(name string) (FileInfo, error) { var fs fileStat diff --git a/src/os/stat_windows.go b/src/os/stat_windows.go index f32ffc3a..8515c7ab 100644 --- a/src/os/stat_windows.go +++ b/src/os/stat_windows.go @@ -15,6 +15,34 @@ func (f *File) Sync() error { return ErrNotImplemented } +// Stat returns the FileInfo structure describing file. +// If there is an error, it will be of type *PathError. +func (file *File) Stat() (FileInfo, error) { + if file == nil { + return nil, ErrInvalid + } + + if isWindowsNulName(file.name) { + return &devNullStat, nil + } + + ft, err := syscall.GetFileType(syscallFd(file.handle.(unixFileHandle))) + if err != nil { + return nil, &PathError{Op: "GetFileType", Path: file.name, Err: err} + } + switch ft { + case syscall.FILE_TYPE_PIPE, syscall.FILE_TYPE_CHAR: + return &fileStat{name: basename(file.name), filetype: ft}, nil + } + + fs, err := newFileStatFromGetFileInformationByHandle(file.name, syscallFd(file.handle.(unixFileHandle))) + if err != nil { + return nil, err + } + fs.filetype = ft + return fs, err +} + // stat implements both Stat and Lstat of a file. func stat(funcname, name string, createFileAttrs uint32) (FileInfo, error) { if len(name) == 0 { diff --git a/src/syscall/syscall_libc_darwin.go b/src/syscall/syscall_libc_darwin.go index 9bc2c581..c19ddba4 100644 --- a/src/syscall/syscall_libc_darwin.go +++ b/src/syscall/syscall_libc_darwin.go @@ -172,6 +172,15 @@ func Stat(path string, p *Stat_t) (err error) { return } +func Fstat(fd int, p *Stat_t) (err error) { + n := libc_fstat(int32(fd), unsafe.Pointer(p)) + + if n < 0 { + err = getErrno() + } + return +} + func Lstat(path string, p *Stat_t) (err error) { data := cstring(path) n := libc_lstat(&data[0], unsafe.Pointer(p)) @@ -188,6 +197,10 @@ func Lstat(path string, p *Stat_t) (err error) { //export stat$INODE64 func libc_stat(pathname *byte, ptr unsafe.Pointer) int32 +// int fstat(int fd, struct stat * buf); +//export fstat$INODE64 +func libc_fstat(fd int32, ptr unsafe.Pointer) int32 + // int lstat(const char *path, struct stat * buf); //export lstat$INODE64 func libc_lstat(pathname *byte, ptr unsafe.Pointer) int32 diff --git a/src/syscall/syscall_libc_wasi.go b/src/syscall/syscall_libc_wasi.go index 9686a998..92b4601a 100644 --- a/src/syscall/syscall_libc_wasi.go +++ b/src/syscall/syscall_libc_wasi.go @@ -212,6 +212,15 @@ func Stat(path string, p *Stat_t) (err error) { return } +func Fstat(fd int, p *Stat_t) (err error) { + n := libc_fstat(int32(fd), unsafe.Pointer(p)) + + if n < 0 { + err = getErrno() + } + return +} + func Lstat(path string, p *Stat_t) (err error) { data := cstring(path) n := libc_lstat(&data[0], unsafe.Pointer(p)) @@ -225,6 +234,10 @@ func Lstat(path string, p *Stat_t) (err error) { //export stat func libc_stat(pathname *byte, ptr unsafe.Pointer) int32 +// int fstat(fd int, struct stat * buf); +//export fstat +func libc_fstat(fd int32, ptr unsafe.Pointer) int32 + // int lstat(const char *path, struct stat * buf); //export lstat func libc_lstat(pathname *byte, ptr unsafe.Pointer) int32