Skip to content

Commit 8bad848

Browse files
dgryskidkegel-fastly
authored andcommitted
src/{syscall, os}: add File.Stat, with smoke test
This is dgryski's File.Stat from #2371, plus the windows bits, plus a smoke test more or less from upstream, rebased on FileHandle.SysHandle from #2469.
1 parent e2217df commit 8bad848

7 files changed

+116
-8
lines changed

src/os/file.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,6 @@ func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
161161
return 0, &PathError{"seek", f.name, ErrNotImplemented}
162162
}
163163

164-
// Stat is a stub, not yet implemented
165-
func (f *File) Stat() (FileInfo, error) {
166-
return nil, &PathError{"stat", f.name, ErrNotImplemented}
167-
}
168-
169164
func (f *File) SyscallConn() (syscall.RawConn, error) {
170165
return nil, ErrNotImplemented
171166
}

src/os/os_anyos_test.go

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//go:build windows || darwin || (linux && !baremetal)
12
// +build windows darwin linux,!baremetal
23

34
package os_test
@@ -7,6 +8,7 @@ import (
78
"path/filepath"
89
"runtime"
910
"strconv"
11+
"strings"
1012
"testing"
1113
"time"
1214
)
@@ -21,7 +23,7 @@ func randomName() string {
2123
func TestMkdir(t *testing.T) {
2224
dir := "TestMkdir" + randomName()
2325
Remove(dir)
24-
err := Mkdir(dir, 0755)
26+
err := Mkdir(dir, 0o755)
2527
defer Remove(dir)
2628
if err != nil {
2729
t.Errorf("Mkdir(%s, 0755) returned %v", dir, err)
@@ -47,8 +49,42 @@ func TestStatBadDir(t *testing.T) {
4749
}
4850
}
4951

52+
func equal(name1, name2 string) (r bool) {
53+
switch runtime.GOOS {
54+
case "windows":
55+
r = strings.ToLower(name1) == strings.ToLower(name2)
56+
default:
57+
r = name1 == name2
58+
}
59+
return
60+
}
61+
62+
func TestFstat(t *testing.T) {
63+
sfname := "TestFstat"
64+
path := TempDir() + "/" + sfname
65+
payload := writeFile(t, path, O_CREATE|O_TRUNC|O_RDWR, "Hello")
66+
defer Remove(path)
67+
68+
file, err1 := Open(path)
69+
if err1 != nil {
70+
t.Fatal("open failed:", err1)
71+
}
72+
defer file.Close()
73+
dir, err2 := file.Stat()
74+
if err2 != nil {
75+
t.Fatal("fstat failed:", err2)
76+
}
77+
if !equal(sfname, dir.Name()) {
78+
t.Error("name should be ", sfname, "; is", dir.Name())
79+
}
80+
filesize := len(payload)
81+
if dir.Size() != int64(filesize) {
82+
t.Error("size should be", filesize, "; is", dir.Size())
83+
}
84+
}
85+
5086
func writeFile(t *testing.T, fname string, flag int, text string) string {
51-
f, err := OpenFile(fname, flag, 0666)
87+
f, err := OpenFile(fname, flag, 0o666)
5288
if err != nil {
5389
t.Fatalf("Open: %v", err)
5490
}

src/os/stat_other.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// +build baremetal wasm,!wasi
1+
//go:build baremetal || (wasm && !wasi)
2+
// +build baremetal wasm,!wasi
23

34
// Copyright 2016 The Go Authors. All rights reserved.
45
// Use of this source code is governed by a BSD-style
@@ -11,6 +12,11 @@ func (f *File) Sync() error {
1112
return ErrNotImplemented
1213
}
1314

15+
// Stat is a stub, not yet implemented
16+
func (f *File) Stat() (FileInfo, error) {
17+
return nil, ErrNotImplemented
18+
}
19+
1420
// statNolog stats a file with no test logging.
1521
func statNolog(name string) (FileInfo, error) {
1622
return nil, &PathError{Op: "stat", Path: name, Err: ErrNotImplemented}

src/os/stat_unix.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//go:build darwin || (linux && !baremetal)
12
// +build darwin linux,!baremetal
23

34
// Copyright 2016 The Go Authors. All rights reserved.
@@ -15,6 +16,20 @@ func (f *File) Sync() error {
1516
return ErrNotImplemented
1617
}
1718

19+
// Stat returns the FileInfo structure describing file.
20+
// If there is an error, it will be of type *PathError.
21+
func (f *File) Stat() (FileInfo, error) {
22+
var fs fileStat
23+
err := ignoringEINTR(func() error {
24+
return syscall.Fstat(int(f.handle.(unixFileHandle)), &fs.sys)
25+
})
26+
if err != nil {
27+
return nil, &PathError{Op: "fstat", Path: f.name, Err: err}
28+
}
29+
fillFileStatFromSys(&fs, f.name)
30+
return &fs, nil
31+
}
32+
1833
// statNolog stats a file with no test logging.
1934
func statNolog(name string) (FileInfo, error) {
2035
var fs fileStat

src/os/stat_windows.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,34 @@ func (f *File) Sync() error {
1515
return ErrNotImplemented
1616
}
1717

18+
// Stat returns the FileInfo structure describing file.
19+
// If there is an error, it will be of type *PathError.
20+
func (file *File) Stat() (FileInfo, error) {
21+
if file == nil {
22+
return nil, ErrInvalid
23+
}
24+
25+
if isWindowsNulName(file.name) {
26+
return &devNullStat, nil
27+
}
28+
29+
ft, err := syscall.GetFileType(syscallFd(file.SysHandle()))
30+
if err != nil {
31+
return nil, &PathError{Op: "GetFileType", Path: file.name, Err: err}
32+
}
33+
switch ft {
34+
case syscall.FILE_TYPE_PIPE, syscall.FILE_TYPE_CHAR:
35+
return &fileStat{name: basename(file.name), filetype: ft}, nil
36+
}
37+
38+
fs, err := newFileStatFromGetFileInformationByHandle(file.name, syscallFd(file.SysHandle()))
39+
if err != nil {
40+
return nil, err
41+
}
42+
fs.filetype = ft
43+
return fs, err
44+
}
45+
1846
// stat implements both Stat and Lstat of a file.
1947
func stat(funcname, name string, createFileAttrs uint32) (FileInfo, error) {
2048
if len(name) == 0 {

src/syscall/syscall_libc_darwin.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//go:build darwin
12
// +build darwin
23

34
package syscall
@@ -172,6 +173,15 @@ func Stat(path string, p *Stat_t) (err error) {
172173
return
173174
}
174175

176+
func Fstat(fd int, p *Stat_t) (err error) {
177+
n := libc_fstat(int32(fd), unsafe.Pointer(p))
178+
179+
if n < 0 {
180+
err = getErrno()
181+
}
182+
return
183+
}
184+
175185
func Lstat(path string, p *Stat_t) (err error) {
176186
data := cstring(path)
177187
n := libc_lstat(&data[0], unsafe.Pointer(p))
@@ -188,6 +198,10 @@ func Lstat(path string, p *Stat_t) (err error) {
188198
//export stat$INODE64
189199
func libc_stat(pathname *byte, ptr unsafe.Pointer) int32
190200

201+
// int fstat(int fd, struct stat * buf);
202+
//export fstat$INODE64
203+
func libc_fstat(fd int32, ptr unsafe.Pointer) int32
204+
191205
// int lstat(const char *path, struct stat * buf);
192206
//export lstat$INODE64
193207
func libc_lstat(pathname *byte, ptr unsafe.Pointer) int32

src/syscall/syscall_libc_wasi.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//go:build wasi
12
// +build wasi
23

34
package syscall
@@ -212,6 +213,15 @@ func Stat(path string, p *Stat_t) (err error) {
212213
return
213214
}
214215

216+
func Fstat(fd int, p *Stat_t) (err error) {
217+
n := libc_fstat(int32(fd), unsafe.Pointer(p))
218+
219+
if n < 0 {
220+
err = getErrno()
221+
}
222+
return
223+
}
224+
215225
func Lstat(path string, p *Stat_t) (err error) {
216226
data := cstring(path)
217227
n := libc_lstat(&data[0], unsafe.Pointer(p))
@@ -225,6 +235,10 @@ func Lstat(path string, p *Stat_t) (err error) {
225235
//export stat
226236
func libc_stat(pathname *byte, ptr unsafe.Pointer) int32
227237

238+
// int fstat(fd int, struct stat * buf);
239+
//export fstat
240+
func libc_fstat(fd int32, ptr unsafe.Pointer) int32
241+
228242
// int lstat(const char *path, struct stat * buf);
229243
//export lstat
230244
func libc_lstat(pathname *byte, ptr unsafe.Pointer) int32

0 commit comments

Comments
 (0)