diff --git a/imports/wasi_snapshot_preview1/fs.go b/imports/wasi_snapshot_preview1/fs.go index 615fcc64..7b187709 100644 --- a/imports/wasi_snapshot_preview1/fs.go +++ b/imports/wasi_snapshot_preview1/fs.go @@ -96,8 +96,8 @@ func fdAllocateFn(_ context.Context, mod api.Module, params []uint64) Errno { return ErrnoInval } - var st platform.Stat_t - if err := f.Stat(&st); err != nil { + st, err := f.Stat() + if err != nil { return ToErrno(err) } @@ -350,12 +350,12 @@ func fdFilestatGetFunc(mod api.Module, fd, resultBuf uint32) Errno { return ErrnoBadf } - var st platform.Stat_t - if err := f.Stat(&st); err != nil { + st, err := f.Stat() + if err != nil { return ToErrno(err) } - if err := writeFilestat(buf, &st); err != nil { + if err = writeFilestat(buf, &st); err != nil { return ToErrno(err) } @@ -896,11 +896,11 @@ func dotDirents(f *sys.FileEntry) ([]*platform.Dirent, error) { } dotDotIno := uint64(0) if !f.IsPreopen && f.Name != "." { - var st platform.Stat_t - if err = f.FS.Stat(path.Dir(f.Name), &st); err != nil { + if st, err := f.FS.Stat(path.Dir(f.Name)); err != nil { return nil, err + } else { + dotDotIno = st.Ino } - dotDotIno = st.Ino } return []*platform.Dirent{ {Name: ".", Ino: dotIno, Type: fs.ModeDir}, @@ -1436,9 +1436,9 @@ func pathFilestatGetFn(_ context.Context, mod api.Module, params []uint64) Errno var err error if (flags & LOOKUP_SYMLINK_FOLLOW) == 0 { - err = preopen.Lstat(pathName, &st) + st, err = preopen.Lstat(pathName) } else { - err = preopen.Stat(pathName, &st) + st, err = preopen.Stat(pathName) } if err != nil { return ToErrno(err) @@ -1451,7 +1451,7 @@ func pathFilestatGetFn(_ context.Context, mod api.Module, params []uint64) Errno return ErrnoFault } - if err := writeFilestat(buf, &st); err != nil { + if err = writeFilestat(buf, &st); err != nil { return ToErrno(err) } return ErrnoSuccess diff --git a/imports/wasi_snapshot_preview1/fs_test.go b/imports/wasi_snapshot_preview1/fs_test.go index d8cfb2c2..e956db5c 100644 --- a/imports/wasi_snapshot_preview1/fs_test.go +++ b/imports/wasi_snapshot_preview1/fs_test.go @@ -63,8 +63,8 @@ func Test_fdAllocate(t *testing.T) { require.True(t, ok) requireSizeEqual := func(exp int64) { - var st platform.Stat_t - require.NoError(t, f.Stat(&st)) + st, err := f.Stat() + require.NoError(t, err) require.Equal(t, exp, st.Size) } @@ -849,8 +849,8 @@ func Test_fdFilestatSetTimes(t *testing.T) { f, ok := fsc.LookupFile(fd) require.True(t, ok) - var st platform.Stat_t - require.NoError(t, f.Stat(&st)) + st, err := f.Stat() + require.NoError(t, err) prevAtime, prevMtime := st.Atim, st.Mtim requireErrnoResult(t, tc.expectedErrno, mod, FdFilestatSetTimesName, @@ -862,8 +862,8 @@ func Test_fdFilestatSetTimes(t *testing.T) { f, ok := fsc.LookupFile(fd) require.True(t, ok) - var st platform.Stat_t - require.NoError(t, f.Stat(&st)) + st, err = f.Stat() + require.NoError(t, err) if tc.flags&FstflagsAtim != 0 { require.Equal(t, tc.atime, st.Atim) } else if tc.flags&FstflagsAtimNow != 0 { @@ -3551,8 +3551,10 @@ func Test_pathFilestatSetTimes(t *testing.T) { fsc := sys.FS() var oldSt platform.Stat_t + var err error if tc.expectedErrno == ErrnoSuccess { - require.NoError(t, fsc.RootFS().Stat(pathName, &oldSt)) + oldSt, err = fsc.RootFS().Stat(pathName) + require.NoError(t, err) } requireErrnoResult(t, tc.expectedErrno, mod, PathFilestatSetTimesName, uint64(fd), uint64(tc.flags), @@ -3563,8 +3565,8 @@ func Test_pathFilestatSetTimes(t *testing.T) { return } - var newSt platform.Stat_t - require.NoError(t, fsc.RootFS().Stat(pathName, &newSt)) + newSt, err := fsc.RootFS().Stat(pathName) + require.NoError(t, err) if platform.CompilerSupported() { if tc.fstFlags&FstflagsAtim != 0 { @@ -3639,8 +3641,7 @@ func Test_pathLink(t *testing.T) { require.NoError(t, f.Close()) }() - var st platform.Stat_t - require.NoError(t, platform.StatFile(f, &st)) + st, err := platform.StatFile(f) require.NoError(t, err) require.False(t, st.Mode&os.ModeSymlink == os.ModeSymlink) require.Equal(t, uint64(2), st.Nlink) @@ -4870,8 +4871,8 @@ func Test_fdReaddir_dotEntriesHaveRealInodes(t *testing.T) { require.NoError(t, err) // get the real inode of the current directory - var st platform.Stat_t - require.NoError(t, preopen.Stat(readDirTarget, &st)) + st, err := preopen.Stat(readDirTarget) + require.NoError(t, err) dirents := []byte{1, 0, 0, 0, 0, 0, 0, 0} // d_next = 1 dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino dirents = append(dirents, 1, 0, 0, 0) // d_namlen = 1 character @@ -4879,7 +4880,8 @@ func Test_fdReaddir_dotEntriesHaveRealInodes(t *testing.T) { dirents = append(dirents, '.') // name // get the real inode of the parent directory - require.NoError(t, preopen.Stat(".", &st)) + st, err = preopen.Stat(".") + require.NoError(t, err) dirents = append(dirents, 2, 0, 0, 0, 0, 0, 0, 0) // d_next = 2 dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino dirents = append(dirents, 2, 0, 0, 0) // d_namlen = 2 characters @@ -4933,8 +4935,8 @@ func Test_fdReaddir_opened_file_written(t *testing.T) { defer f.Close() // get the real inode of the current directory - var st platform.Stat_t - require.NoError(t, preopen.Stat(dirName, &st)) + st, err := preopen.Stat(dirName) + require.NoError(t, err) dirents := []byte{1, 0, 0, 0, 0, 0, 0, 0} // d_next = 1 dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino dirents = append(dirents, 1, 0, 0, 0) // d_namlen = 1 character @@ -4942,7 +4944,8 @@ func Test_fdReaddir_opened_file_written(t *testing.T) { dirents = append(dirents, '.') // name // get the real inode of the parent directory - require.NoError(t, preopen.Stat(".", &st)) + st, err = preopen.Stat(".") + require.NoError(t, err) dirents = append(dirents, 2, 0, 0, 0, 0, 0, 0, 0) // d_next = 2 dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino dirents = append(dirents, 2, 0, 0, 0) // d_namlen = 2 characters @@ -4950,7 +4953,8 @@ func Test_fdReaddir_opened_file_written(t *testing.T) { dirents = append(dirents, '.', '.') // name // get the real inode of the file - require.NoError(t, platform.StatFile(f, &st)) + st, err = platform.StatFile(f) + require.NoError(t, err) dirents = append(dirents, 3, 0, 0, 0, 0, 0, 0, 0) // d_next = 3 dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino dirents = append(dirents, 4, 0, 0, 0) // d_namlen = 4 characters diff --git a/internal/fstest/fstest.go b/internal/fstest/fstest.go index 8bd223a0..aac0c564 100644 --- a/internal/fstest/fstest.go +++ b/internal/fstest/fstest.go @@ -100,16 +100,16 @@ func WriteTestFiles(tmpDir string) (err error) { } // os.Stat uses GetFileInformationByHandle internally. - var stat platform.Stat_t - if err = platform.Stat(path, &stat); err != nil { + var st platform.Stat_t + if st, err = platform.Stat(path); err != nil { return err } - if stat.Mtim == info.ModTime().UnixNano() { + if st.Mtim == info.ModTime().UnixNano() { return nil // synced! } // Otherwise, we need to sync the timestamps. - return os.Chtimes(path, time.Unix(0, stat.Atim), time.Unix(0, stat.Mtim)) + return os.Chtimes(path, time.Unix(0, st.Atim), time.Unix(0, st.Mtim)) }) } return diff --git a/internal/gojs/fs.go b/internal/gojs/fs.go index 280ab9eb..dd93aa55 100644 --- a/internal/gojs/fs.go +++ b/internal/gojs/fs.go @@ -134,11 +134,11 @@ func (s *jsfsStat) invoke(ctx context.Context, mod api.Module, args ...interface func syscallStat(mod api.Module, path string) (*jsSt, error) { fsc := mod.(*wasm.CallContext).Sys.FS() - var stat platform.Stat_t - if err := fsc.RootFS().Stat(path, &stat); err != nil { + if st, err := fsc.RootFS().Stat(path); err != nil { return nil, err + } else { + return newJsSt(st), nil } - return newJsSt(&stat), nil } // jsfsLstat implements jsFn for syscall.Lstat @@ -161,11 +161,11 @@ func (l *jsfsLstat) invoke(ctx context.Context, mod api.Module, args ...interfac func syscallLstat(mod api.Module, path string) (*jsSt, error) { fsc := mod.(*wasm.CallContext).Sys.FS() - var stat platform.Stat_t - if err := fsc.RootFS().Lstat(path, &stat); err != nil { + if st, err := fsc.RootFS().Lstat(path); err != nil { return nil, err + } else { + return newJsSt(st), nil } - return newJsSt(&stat), nil } // jsfsFstat implements jsFn for syscall.Open @@ -190,14 +190,14 @@ func syscallFstat(fsc *internalsys.FSContext, fd uint32) (*jsSt, error) { return nil, syscall.EBADF } - var st platform.Stat_t - if err := f.Stat(&st); err != nil { + if st, err := f.Stat(); err != nil { return nil, err + } else { + return newJsSt(st), nil } - return newJsSt(&st), nil } -func newJsSt(st *platform.Stat_t) *jsSt { +func newJsSt(st platform.Stat_t) *jsSt { ret := &jsSt{} ret.isDir = st.Mode.IsDir() ret.dev = st.Dev diff --git a/internal/platform/futimens.go b/internal/platform/futimens.go index b908101e..16db9975 100644 --- a/internal/platform/futimens.go +++ b/internal/platform/futimens.go @@ -122,7 +122,7 @@ func normalizeTimespec(path string, times *[2]syscall.Timespec, i int) (ts sysca // - https://github.com/golang/go/issues/32558. // - https://go-review.googlesource.com/c/go/+/219638 (unmerged) var st Stat_t - if err = stat(path, &st); err != nil { + if st, err = stat(path); err != nil { return } switch i { diff --git a/internal/platform/futimens_test.go b/internal/platform/futimens_test.go index f082b0a2..94f1d1c2 100644 --- a/internal/platform/futimens_test.go +++ b/internal/platform/futimens_test.go @@ -146,8 +146,8 @@ func testFutimens(t *testing.T, usePath bool) { panic(tc) } - var oldSt Stat_t - require.NoError(t, Lstat(statPath, &oldSt)) + oldSt, err := Lstat(statPath) + require.NoError(t, err) if usePath { err = Utimens(path, tc.times, !symlinkNoFollow) @@ -172,8 +172,8 @@ func testFutimens(t *testing.T, usePath bool) { require.NoError(t, err) } - var newSt Stat_t - require.NoError(t, Lstat(statPath, &newSt)) + newSt, err := Lstat(statPath) + require.NoError(t, err) if CompilerSupported() { if tc.times != nil && tc.times[0].Nsec == UTIME_OMIT { diff --git a/internal/platform/stat.go b/internal/platform/stat.go index f41b3e5f..059ded7c 100644 --- a/internal/platform/stat.go +++ b/internal/platform/stat.go @@ -58,38 +58,39 @@ type Stat_t struct { // The primary difference between this and Stat is, when the path is a // symbolic link, the stat is about the link, not its target, such as directory // listings. -func Lstat(path string, st *Stat_t) error { - err := lstat(path, st) // extracted to override more expensively in windows - return UnwrapOSError(err) +func Lstat(path string) (Stat_t, error) { + st, err := lstat(path) // extracted to override more expensively in windows + return st, UnwrapOSError(err) } // Stat is like syscall.Stat. This returns syscall.ENOENT if the path doesn't // exist. -func Stat(path string, st *Stat_t) error { - err := stat(path, st) // extracted to override more expensively in windows - return UnwrapOSError(err) +func Stat(path string) (Stat_t, error) { + st, err := stat(path) // extracted to override more expensively in windows + return st, UnwrapOSError(err) } // StatFile is like syscall.Fstat, but for fs.File instead of a file // descriptor. This returns syscall.EBADF if the file or directory was closed. // Note: windows allows you to stat a closed directory. -func StatFile(f fs.File, st *Stat_t) (err error) { - err = statFile(f, st) +func StatFile(f fs.File) (Stat_t, error) { + st, err := statFile(f) if err = UnwrapOSError(err); err == syscall.EIO { err = syscall.EBADF } - return + return st, err } -func defaultStatFile(f fs.File, st *Stat_t) (err error) { - var t fs.FileInfo - if t, err = f.Stat(); err == nil { - fillStatFromFileInfo(st, t) +func defaultStatFile(f fs.File) (Stat_t, error) { + if t, err := f.Stat(); err != nil { + return Stat_t{}, err + } else { + return statFromFileInfo(t), nil } - return } -func fillStatFromDefaultFileInfo(st *Stat_t, t fs.FileInfo) { +func statFromDefaultFileInfo(t fs.FileInfo) Stat_t { + st := Stat_t{} st.Ino = 0 st.Dev = 0 st.Mode = t.Mode() @@ -99,4 +100,5 @@ func fillStatFromDefaultFileInfo(st *Stat_t, t fs.FileInfo) { st.Atim = mtim st.Mtim = mtim st.Ctim = mtim + return st } diff --git a/internal/platform/stat_bsd.go b/internal/platform/stat_bsd.go index 4e8c9a20..f3640b44 100644 --- a/internal/platform/stat_bsd.go +++ b/internal/platform/stat_bsd.go @@ -8,24 +8,24 @@ import ( "syscall" ) -func lstat(path string, st *Stat_t) (err error) { - var t fs.FileInfo - if t, err = os.Lstat(path); err == nil { - fillStatFromFileInfo(st, t) +func lstat(path string) (Stat_t, error) { + if t, err := os.Lstat(path); err != nil { + return Stat_t{}, err + } else { + return statFromFileInfo(t), nil } - return } -func stat(path string, st *Stat_t) (err error) { - var t fs.FileInfo - if t, err = os.Stat(path); err == nil { - fillStatFromFileInfo(st, t) +func stat(path string) (Stat_t, error) { + if t, err := os.Stat(path); err != nil { + return Stat_t{}, err + } else { + return statFromFileInfo(t), nil } - return } -func statFile(f fs.File, st *Stat_t) error { - return defaultStatFile(f, st) +func statFile(f fs.File) (Stat_t, error) { + return defaultStatFile(f) } func inoFromFileInfo(_ readdirFile, t fs.FileInfo) (ino uint64, err error) { @@ -35,8 +35,9 @@ func inoFromFileInfo(_ readdirFile, t fs.FileInfo) (ino uint64, err error) { return } -func fillStatFromFileInfo(st *Stat_t, t fs.FileInfo) { +func statFromFileInfo(t fs.FileInfo) Stat_t { if d, ok := t.Sys().(*syscall.Stat_t); ok { + st := Stat_t{} st.Dev = uint64(d.Dev) st.Ino = d.Ino st.Uid = d.Uid @@ -50,7 +51,7 @@ func fillStatFromFileInfo(st *Stat_t, t fs.FileInfo) { st.Mtim = mtime.Sec*1e9 + mtime.Nsec ctime := d.Ctimespec st.Ctim = ctime.Sec*1e9 + ctime.Nsec - } else { - fillStatFromDefaultFileInfo(st, t) + return st } + return statFromDefaultFileInfo(t) } diff --git a/internal/platform/stat_linux.go b/internal/platform/stat_linux.go index 03e94555..19a82438 100644 --- a/internal/platform/stat_linux.go +++ b/internal/platform/stat_linux.go @@ -11,35 +11,36 @@ import ( "syscall" ) -func lstat(path string, st *Stat_t) (err error) { - var t fs.FileInfo - if t, err = os.Lstat(path); err == nil { - fillStatFromFileInfo(st, t) +func lstat(path string) (Stat_t, error) { + if t, err := os.Lstat(path); err != nil { + return Stat_t{}, err + } else { + return statFromFileInfo(t), nil } - return } -func stat(path string, st *Stat_t) (err error) { - var t fs.FileInfo - if t, err = os.Stat(path); err == nil { - fillStatFromFileInfo(st, t) +func stat(path string) (Stat_t, error) { + if t, err := os.Stat(path); err != nil { + return Stat_t{}, err + } else { + return statFromFileInfo(t), nil } - return } -func statFile(f fs.File, st *Stat_t) error { - return defaultStatFile(f, st) +func statFile(f fs.File) (Stat_t, error) { + return defaultStatFile(f) } func inoFromFileInfo(_ readdirFile, t fs.FileInfo) (ino uint64, err error) { if d, ok := t.Sys().(*syscall.Stat_t); ok { - ino = (d.Ino) + ino = d.Ino } return } -func fillStatFromFileInfo(st *Stat_t, t fs.FileInfo) { +func statFromFileInfo(t fs.FileInfo) Stat_t { if d, ok := t.Sys().(*syscall.Stat_t); ok { + st := Stat_t{} st.Dev = uint64(d.Dev) st.Ino = uint64(d.Ino) st.Uid = d.Uid @@ -53,7 +54,7 @@ func fillStatFromFileInfo(st *Stat_t, t fs.FileInfo) { st.Mtim = mtime.Sec*1e9 + mtime.Nsec ctime := d.Ctim st.Ctim = ctime.Sec*1e9 + ctime.Nsec - } else { - fillStatFromDefaultFileInfo(st, t) + return st } + return statFromDefaultFileInfo(t) } diff --git a/internal/platform/stat_test.go b/internal/platform/stat_test.go index 57e016d0..ca455c26 100644 --- a/internal/platform/stat_test.go +++ b/internal/platform/stat_test.go @@ -15,139 +15,152 @@ import ( func TestLstat(t *testing.T) { tmpDir := t.TempDir() - var stat Stat_t - require.EqualErrno(t, syscall.ENOENT, Lstat(path.Join(tmpDir, "cat"), &stat)) - require.EqualErrno(t, syscall.ENOENT, Lstat(path.Join(tmpDir, "sub/cat"), &stat)) + _, err := Lstat(path.Join(tmpDir, "cat")) + require.EqualErrno(t, syscall.ENOENT, err) + _, err = Lstat(path.Join(tmpDir, "sub/cat")) + require.EqualErrno(t, syscall.ENOENT, err) + var st Stat_t t.Run("dir", func(t *testing.T) { - err := Lstat(tmpDir, &stat) + st, err = Lstat(tmpDir) require.NoError(t, err) - require.True(t, stat.Mode.IsDir()) - require.NotEqual(t, uint64(0), stat.Ino) + require.True(t, st.Mode.IsDir()) + require.NotEqual(t, uint64(0), st.Ino) }) file := path.Join(tmpDir, "file") - var statFile Stat_t + var stFile Stat_t t.Run("file", func(t *testing.T) { require.NoError(t, os.WriteFile(file, []byte{1, 2}, 0o400)) - require.NoError(t, Lstat(file, &statFile)) - require.Zero(t, statFile.Mode.Type()) - require.Equal(t, int64(2), statFile.Size) - require.NotEqual(t, uint64(0), statFile.Ino) + stFile, err = Lstat(file) + require.NoError(t, err) + require.Zero(t, stFile.Mode.Type()) + require.Equal(t, int64(2), stFile.Size) + require.NotEqual(t, uint64(0), stFile.Ino) }) t.Run("link to file", func(t *testing.T) { - requireLinkStat(t, file, &statFile) + requireLinkStat(t, file, stFile) }) subdir := path.Join(tmpDir, "sub") - var statSubdir Stat_t + var stSubdir Stat_t t.Run("subdir", func(t *testing.T) { require.NoError(t, os.Mkdir(subdir, 0o500)) - require.NoError(t, Lstat(subdir, &statSubdir)) - require.True(t, statSubdir.Mode.IsDir()) - require.NotEqual(t, uint64(0), statSubdir.Ino) + stSubdir, err = Lstat(subdir) + require.NoError(t, err) + require.True(t, stSubdir.Mode.IsDir()) + require.NotEqual(t, uint64(0), stSubdir.Ino) }) t.Run("link to dir", func(t *testing.T) { - requireLinkStat(t, subdir, &statSubdir) + requireLinkStat(t, subdir, stSubdir) }) t.Run("link to dir link", func(t *testing.T) { pathLink := subdir + "-link" - var statLink Stat_t - require.NoError(t, Lstat(pathLink, &statLink)) + stLink, err := Lstat(pathLink) + require.NoError(t, err) - requireLinkStat(t, pathLink, &statLink) + requireLinkStat(t, pathLink, stLink) }) } -func requireLinkStat(t *testing.T, path string, stat *Stat_t) { +func requireLinkStat(t *testing.T, path string, stat Stat_t) { link := path + "-link" - var linkStat Stat_t require.NoError(t, os.Symlink(path, link)) - require.NoError(t, Lstat(link, &linkStat)) - require.NotEqual(t, uint64(0), linkStat.Ino) - require.NotEqual(t, stat.Ino, linkStat.Ino) // inodes are not equal - require.Equal(t, fs.ModeSymlink, linkStat.Mode.Type()) + stLink, err := Lstat(link) + require.NoError(t, err) + + require.NotEqual(t, uint64(0), stLink.Ino) + require.NotEqual(t, stat.Ino, stLink.Ino) // inodes are not equal + require.Equal(t, fs.ModeSymlink, stLink.Mode.Type()) // From https://linux.die.net/man/2/lstat: // The size of a symbolic link is the length of the pathname it // contains, without a terminating null byte. if runtime.GOOS == "windows" { // size is zero, not the path length - require.Zero(t, linkStat.Size) + require.Zero(t, stLink.Size) } else { - require.Equal(t, int64(len(path)), linkStat.Size) + require.Equal(t, int64(len(path)), stLink.Size) } } func TestStat(t *testing.T) { tmpDir := t.TempDir() - var stat Stat_t - require.EqualErrno(t, syscall.ENOENT, Stat(path.Join(tmpDir, "cat"), &stat)) - require.EqualErrno(t, syscall.ENOENT, Stat(path.Join(tmpDir, "sub/cat"), &stat)) + _, err := Stat(path.Join(tmpDir, "cat")) + require.EqualErrno(t, syscall.ENOENT, err) + _, err = Stat(path.Join(tmpDir, "sub/cat")) + require.EqualErrno(t, syscall.ENOENT, err) + + var st Stat_t t.Run("dir", func(t *testing.T) { - err := Stat(tmpDir, &stat) + st, err = Stat(tmpDir) require.NoError(t, err) - require.True(t, stat.Mode.IsDir()) - require.NotEqual(t, uint64(0), stat.Ino) + require.True(t, st.Mode.IsDir()) + require.NotEqual(t, uint64(0), st.Ino) }) file := path.Join(tmpDir, "file") - var statFile Stat_t + var stFile Stat_t t.Run("file", func(t *testing.T) { require.NoError(t, os.WriteFile(file, nil, 0o400)) - require.NoError(t, Stat(file, &statFile)) - require.False(t, statFile.Mode.IsDir()) - require.NotEqual(t, uint64(0), stat.Ino) + + stFile, err = Stat(file) + require.NoError(t, err) + require.False(t, stFile.Mode.IsDir()) + require.NotEqual(t, uint64(0), st.Ino) }) t.Run("link to file", func(t *testing.T) { link := path.Join(tmpDir, "file-link") require.NoError(t, os.Symlink(file, link)) - require.NoError(t, Stat(link, &stat)) - require.Equal(t, statFile, stat) // resolves to the file + stLink, err := Stat(link) + require.NoError(t, err) + require.Equal(t, stFile, stLink) // resolves to the file }) subdir := path.Join(tmpDir, "sub") - var statSubdir Stat_t + var stSubdir Stat_t t.Run("subdir", func(t *testing.T) { require.NoError(t, os.Mkdir(subdir, 0o500)) - require.NoError(t, Stat(subdir, &statSubdir)) - require.True(t, statSubdir.Mode.IsDir()) - require.NotEqual(t, uint64(0), stat.Ino) + stSubdir, err = Stat(subdir) + require.NoError(t, err) + require.True(t, stSubdir.Mode.IsDir()) + require.NotEqual(t, uint64(0), st.Ino) }) t.Run("link to dir", func(t *testing.T) { link := path.Join(tmpDir, "dir-link") require.NoError(t, os.Symlink(subdir, link)) - require.NoError(t, Stat(link, &stat)) - require.Equal(t, statSubdir, stat) // resolves to the dir + stLink, err := Stat(link) + require.NoError(t, err) + require.Equal(t, stSubdir, stLink) // resolves to the dir }) } func TestStatFile(t *testing.T) { tmpDir := t.TempDir() - var stat Stat_t + var st Stat_t tmpDirF, err := OpenFile(tmpDir, syscall.O_RDONLY, 0) require.NoError(t, err) defer tmpDirF.Close() t.Run("dir", func(t *testing.T) { - err = StatFile(tmpDirF, &stat) + st, err = StatFile(tmpDirF) require.NoError(t, err) - require.True(t, stat.Mode.IsDir()) - requireDirectoryDevIno(t, stat) + require.True(t, st.Mode.IsDir()) + requireDirectoryDevIno(t, st) }) // Windows allows you to stat a closed dir because it is accessed by path, @@ -155,7 +168,8 @@ func TestStatFile(t *testing.T) { if runtime.GOOS != "windows" { t.Run("closed dir", func(t *testing.T) { require.NoError(t, tmpDirF.Close()) - require.EqualErrno(t, syscall.EBADF, StatFile(tmpDirF, &stat)) + st, err = StatFile(tmpDirF) + require.EqualErrno(t, syscall.EBADF, err) }) } @@ -166,16 +180,16 @@ func TestStatFile(t *testing.T) { defer fileF.Close() t.Run("file", func(t *testing.T) { - err = StatFile(fileF, &stat) + st, err = StatFile(fileF) require.NoError(t, err) - require.False(t, stat.Mode.IsDir()) - require.NotEqual(t, uint64(0), stat.Ino) + require.False(t, st.Mode.IsDir()) + require.NotEqual(t, uint64(0), st.Ino) }) t.Run("closed file", func(t *testing.T) { require.NoError(t, fileF.Close()) - require.EqualErrno(t, syscall.EBADF, StatFile(fileF, &stat)) - require.NotEqual(t, uint64(0), stat.Ino) + _, err = StatFile(fileF) + require.EqualErrno(t, syscall.EBADF, err) }) subdir := path.Join(tmpDir, "sub") @@ -185,16 +199,17 @@ func TestStatFile(t *testing.T) { defer subdirF.Close() t.Run("subdir", func(t *testing.T) { - err = StatFile(subdirF, &stat) + st, err = StatFile(subdirF) require.NoError(t, err) - require.True(t, stat.Mode.IsDir()) - requireDirectoryDevIno(t, stat) + require.True(t, st.Mode.IsDir()) + requireDirectoryDevIno(t, st) }) if runtime.GOOS != "windows" { // windows allows you to stat a closed dir t.Run("closed subdir", func(t *testing.T) { require.NoError(t, subdirF.Close()) - require.EqualErrno(t, syscall.EBADF, StatFile(subdirF, &stat)) + st, err = StatFile(subdirF) + require.EqualErrno(t, syscall.EBADF, err) }) } } @@ -238,14 +253,14 @@ func Test_StatFile_times(t *testing.T) { err := os.Chtimes(file, time.UnixMicro(tc.atimeNsec/1e3), time.UnixMicro(tc.mtimeNsec/1e3)) require.NoError(t, err) - file, err := os.Open(file) + f, err := os.Open(file) require.NoError(t, err) - defer file.Close() + defer f.Close() - var stat Stat_t - require.NoError(t, StatFile(file, &stat)) - require.Equal(t, stat.Atim, tc.atimeNsec) - require.Equal(t, stat.Mtim, tc.mtimeNsec) + st, err := StatFile(f) + require.NoError(t, err) + require.Equal(t, st.Atim, tc.atimeNsec) + require.Equal(t, st.Mtim, tc.mtimeNsec) }) } } @@ -274,28 +289,29 @@ func TestStatFile_dev_inode(t *testing.T) { defer l2.Close() // First, stat the directory - var stat1 Stat_t - require.NoError(t, StatFile(d, &stat1)) - requireDirectoryDevIno(t, stat1) + st1, err := StatFile(d) + require.NoError(t, err) + requireDirectoryDevIno(t, st1) // Now, stat the files in it - require.NoError(t, StatFile(f1, &stat1)) + st1, err = StatFile(f1) + require.NoError(t, err) - var stat2 Stat_t - require.NoError(t, StatFile(f2, &stat2)) + st2, err := StatFile(f2) + require.NoError(t, err) - var stat3 Stat_t - require.NoError(t, StatFile(l2, &stat3)) + st3, err := StatFile(l2) + require.NoError(t, err) // The files should be on the same device, but different inodes - require.Equal(t, stat1.Dev, stat2.Dev) - require.NotEqual(t, stat1.Ino, stat2.Ino) - require.Equal(t, stat2, stat3) // stat on a link is for its target + require.Equal(t, st1.Dev, st2.Dev) + require.NotEqual(t, st1.Ino, st2.Ino) + require.Equal(t, st2, st3) // stat on a link is for its target // Redoing stat should result in the same inodes - var stat1Again Stat_t - require.NoError(t, StatFile(f1, &stat1Again)) - require.Equal(t, stat1.Dev, stat1Again.Dev) + st1Again, err := StatFile(f1) + require.NoError(t, err) + require.Equal(t, st1.Dev, st1Again.Dev) // On Windows, we cannot rename while opening. // So we manually close here before renaming. @@ -309,9 +325,10 @@ func TestStatFile_dev_inode(t *testing.T) { require.NoError(t, err) defer f1.Close() - require.NoError(t, StatFile(f1, &stat1Again)) - require.Equal(t, stat1.Dev, stat1Again.Dev) - require.Equal(t, stat1.Ino, stat1Again.Ino) + st1Again, err = StatFile(f1) + require.NoError(t, err) + require.Equal(t, st1.Dev, st1Again.Dev) + require.Equal(t, st1.Ino, st1Again.Ino) } func requireDirectoryDevIno(t *testing.T, st Stat_t) { @@ -344,8 +361,8 @@ func TestStat_uid_gid(t *testing.T) { require.NoError(t, os.Mkdir(dir, 0o0700)) require.NoError(t, chgid(dir, gid)) - var st Stat_t - require.NoError(t, Stat(dir, &st)) + st, err := Stat(dir) + require.NoError(t, err) require.Equal(t, uid, st.Uid) require.Equal(t, gid, st.Gid) }) @@ -356,8 +373,8 @@ func TestStat_uid_gid(t *testing.T) { require.NoError(t, os.Symlink(tmpDir, link)) require.NoError(t, chgid(link, gid)) - var st Stat_t - require.NoError(t, Lstat(link, &st)) + st, err := Lstat(link) + require.NoError(t, err) require.Equal(t, uid, st.Uid) require.Equal(t, gid, st.Gid) }) @@ -368,8 +385,8 @@ func TestStat_uid_gid(t *testing.T) { require.NoError(t, os.WriteFile(file, nil, 0o0600)) require.NoError(t, chgid(file, gid)) - var st Stat_t - require.NoError(t, Lstat(file, &st)) + st, err := Lstat(file) + require.NoError(t, err) require.Equal(t, uid, st.Uid) require.Equal(t, gid, st.Gid) }) diff --git a/internal/platform/stat_unsupported.go b/internal/platform/stat_unsupported.go index 397c137b..d2c73b9c 100644 --- a/internal/platform/stat_unsupported.go +++ b/internal/platform/stat_unsupported.go @@ -7,35 +7,30 @@ import ( "os" ) -func lstat(path string, st *Stat_t) (err error) { +func lstat(path string) (Stat_t, error) { t, err := os.Lstat(path) - if err = UnwrapOSError(err); err == nil { - fillStatFromFileInfo(st, t) + if err = UnwrapOSError(err); err != nil { + return statFromFileInfo(t), nil } - return + return Stat_t{}, err } -func stat(path string, st *Stat_t) (err error) { +func stat(path string) (Stat_t, error) { t, err := os.Stat(path) if err = UnwrapOSError(err); err == nil { - fillStatFromFileInfo(st, t) + return statFromFileInfo(t), nil } - return + return Stat_t{}, err } -func statFile(f fs.File, st *Stat_t) error { - return defaultStatFile(f, st) +func statFile(f fs.File) (Stat_t, error) { + return defaultStatFile(f) } func inoFromFileInfo(readdirFile, fs.FileInfo) (ino uint64, err error) { return } -func fillStatFromFileInfo(st *Stat_t, t fs.FileInfo) { - fillStatFromDefaultFileInfo(st, t) -} - -func fillStatFromOpenFile(st *Stat_t, fd uintptr, t os.FileInfo) (err error) { - fillStatFromFileInfo(st, t) - return +func statFromFileInfo(t fs.FileInfo) Stat_t { + return statFromDefaultFileInfo(t) } diff --git a/internal/platform/stat_windows.go b/internal/platform/stat_windows.go index f44a4835..e0a517ba 100644 --- a/internal/platform/stat_windows.go +++ b/internal/platform/stat_windows.go @@ -8,26 +8,26 @@ import ( "syscall" ) -func lstat(path string, st *Stat_t) error { +func lstat(path string) (Stat_t, error) { attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS) // Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink. // See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT - return statPath(attrs, path, st) + return statPath(attrs, path) } -func stat(path string, st *Stat_t) error { +func stat(path string) (Stat_t, error) { attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS) - return statPath(attrs, path, st) + return statPath(attrs, path) } -func statPath(createFileAttrs uint32, path string, st *Stat_t) (err error) { +func statPath(createFileAttrs uint32, path string) (Stat_t, error) { if len(path) == 0 { - return syscall.ENOENT + return Stat_t{}, syscall.ENOENT } pathp, err := syscall.UTF16PtrFromString(path) if err != nil { - return syscall.EINVAL + return Stat_t{}, syscall.EINVAL } // open the file handle @@ -39,41 +39,41 @@ func statPath(createFileAttrs uint32, path string, st *Stat_t) (err error) { if err == syscall.ENOTDIR { err = syscall.ENOENT } - return err + return Stat_t{}, err } defer syscall.CloseHandle(h) - return statHandle(h, st) + return statHandle(h) } -func statFile(f fs.File, st *Stat_t) (err error) { +func statFile(f fs.File) (Stat_t, error) { if of, ok := f.(fdFile); ok { // Attempt to get the stat by handle, which works for normal files - err = statHandle(syscall.Handle(of.Fd()), st) + st, err := statHandle(syscall.Handle(of.Fd())) // ERROR_INVALID_HANDLE happens before Go 1.20. Don't fail as we only // use that approach to fill in inode data, which is not critical. if err != ERROR_INVALID_HANDLE { - return + return st, err } } - return defaultStatFile(f, st) + return defaultStatFile(f) } // inoFromFileInfo uses stat to get the inode information of the file. func inoFromFileInfo(f readdirFile, t fs.FileInfo) (ino uint64, err error) { if pf, ok := f.(PathFile); ok { - var st Stat_t inoPath := path.Clean(path.Join(pf.Path(), t.Name())) - if err = lstat(inoPath, &st); err == nil { + if st, err := lstat(inoPath); err == nil { ino = st.Ino } } return // not in Win32FileAttributeData } -func fillStatFromFileInfo(st *Stat_t, t fs.FileInfo) { +func statFromFileInfo(t fs.FileInfo) Stat_t { if d, ok := t.Sys().(*syscall.Win32FileAttributeData); ok { + st := Stat_t{} st.Ino = 0 // not in Win32FileAttributeData st.Dev = 0 // not in Win32FileAttributeData st.Mode = t.Mode() @@ -82,20 +82,21 @@ func fillStatFromFileInfo(st *Stat_t, t fs.FileInfo) { st.Atim = d.LastAccessTime.Nanoseconds() st.Mtim = d.LastWriteTime.Nanoseconds() st.Ctim = d.CreationTime.Nanoseconds() + return st } else { - fillStatFromDefaultFileInfo(st, t) + return statFromDefaultFileInfo(t) } } -func statHandle(h syscall.Handle, st *Stat_t) (err error) { +func statHandle(h syscall.Handle) (Stat_t, error) { winFt, err := syscall.GetFileType(h) if err != nil { - return err + return Stat_t{}, err } var fi syscall.ByHandleFileInformation if err = syscall.GetFileInformationByHandle(h, &fi); err != nil { - return err + return Stat_t{}, err } var m fs.FileMode @@ -116,6 +117,7 @@ func statHandle(h syscall.Handle, st *Stat_t) (err error) { m |= fs.ModeDir | 0o111 // e.g. 0o444 -> 0o555 } + st := Stat_t{} // FileIndex{High,Low} can be combined and used as a unique identifier like inode. // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-by_handle_file_information st.Dev = uint64(fi.VolumeSerialNumber) @@ -126,5 +128,5 @@ func statHandle(h syscall.Handle, st *Stat_t) (err error) { st.Atim = fi.LastAccessTime.Nanoseconds() st.Mtim = fi.LastWriteTime.Nanoseconds() st.Ctim = fi.CreationTime.Nanoseconds() - return + return st, nil } diff --git a/internal/platform/wrap_windows.go b/internal/platform/wrap_windows.go index 5b9399ff..ba7d4c8f 100644 --- a/internal/platform/wrap_windows.go +++ b/internal/platform/wrap_windows.go @@ -120,8 +120,8 @@ func (w *windowsWrappedFile) requireFile(op string, readOnly, isDir bool) error // getFileType caches the file type as this cannot change on an open file. func (w *windowsWrappedFile) getFileType() (fs.FileMode, error) { if w.fileType == nil { - var st Stat_t - if err := StatFile(w.File, &st); err != nil { + st, err := StatFile(w.File) + if err != nil { return 0, nil } ft := st.Mode & fs.ModeType diff --git a/internal/sys/fs.go b/internal/sys/fs.go index 21e8a642..5f87d783 100644 --- a/internal/sys/fs.go +++ b/internal/sys/fs.go @@ -190,8 +190,7 @@ type cachedStat struct { // they couldn't be retrieved. func (f *FileEntry) CachedStat() (ino uint64, fileType fs.FileMode, err error) { if f.cachedStat == nil { - var st platform.Stat_t - if err = f.Stat(&st); err != nil { + if _, err = f.Stat(); err != nil { return } } @@ -199,14 +198,14 @@ func (f *FileEntry) CachedStat() (ino uint64, fileType fs.FileMode, err error) { } // Stat returns the underlying stat of this file. -func (f *FileEntry) Stat(st *platform.Stat_t) (err error) { +func (f *FileEntry) Stat() (st platform.Stat_t, err error) { if ld, ok := f.File.(*lazyDir); ok { var sf fs.File if sf, err = ld.file(); err == nil { - err = platform.StatFile(sf, st) + st, err = platform.StatFile(sf) } } else { - err = platform.StatFile(f.File, st) + st, err = platform.StatFile(f.File) } if err == nil { diff --git a/internal/sys/sys_test.go b/internal/sys/sys_test.go index a000c422..b2dbab68 100644 --- a/internal/sys/sys_test.go +++ b/internal/sys/sys_test.go @@ -88,8 +88,8 @@ func TestFileEntry_cachedStat(t *testing.T) { dirFS := sysfs.NewDirFS(tmpDir) // get the expected inode - var st platform.Stat_t - require.NoError(t, platform.Stat(tmpDir, &st)) + st, err := platform.Stat(tmpDir) + require.NoError(t, err) tests := []struct { name string diff --git a/internal/sysfs/adapter.go b/internal/sysfs/adapter.go index cb398d6c..cc5b253c 100644 --- a/internal/sysfs/adapter.go +++ b/internal/sysfs/adapter.go @@ -51,18 +51,18 @@ func (a *adapter) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, er } // Stat implements FS.Stat -func (a *adapter) Stat(path string, stat *platform.Stat_t) error { +func (a *adapter) Stat(path string) (platform.Stat_t, error) { name := cleanPath(path) f, err := a.fs.Open(name) if err != nil { - return platform.UnwrapOSError(err) + return platform.Stat_t{}, platform.UnwrapOSError(err) } defer f.Close() - return platform.StatFile(f, stat) + return platform.StatFile(f) } // Lstat implements FS.Lstat -func (a *adapter) Lstat(path string, stat *platform.Stat_t) error { +func (a *adapter) Lstat(path string) (platform.Stat_t, error) { // At this time, we make the assumption that fs.FS instances do not support // symbolic links, therefore Lstat is the same as Stat. This is obviously // not true but until fs.FS has a solid story for how to handle symlinks we @@ -71,7 +71,7 @@ func (a *adapter) Lstat(path string, stat *platform.Stat_t) error { // // For further discussions on the topic, see: // https://github.com/golang/go/issues/49580 - return a.Stat(path, stat) + return a.Stat(path) } func cleanPath(name string) string { diff --git a/internal/sysfs/adapter_test.go b/internal/sysfs/adapter_test.go index 85f7c575..75aa7c7e 100644 --- a/internal/sysfs/adapter_test.go +++ b/internal/sysfs/adapter_test.go @@ -10,7 +10,6 @@ import ( "testing" "github.com/tetratelabs/wazero/internal/fstest" - "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/internal/testing/require" ) @@ -126,8 +125,8 @@ func TestAdapt_Lstat(t *testing.T) { fullPath := joinPath(tmpDir, path) linkPath := joinPath(tmpDir, path+"-link") require.NoError(t, os.Symlink(fullPath, linkPath)) - var stat platform.Stat_t - require.NoError(t, testFS.Lstat(filepath.Base(linkPath), &stat)) + _, err := testFS.Lstat(filepath.Base(linkPath)) + require.NoError(t, err) } } diff --git a/internal/sysfs/dirfs.go b/internal/sysfs/dirfs.go index 81281780..e209a8aa 100644 --- a/internal/sysfs/dirfs.go +++ b/internal/sysfs/dirfs.go @@ -46,13 +46,13 @@ func (d *dirFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, erro } // Lstat implements FS.Lstat -func (d *dirFS) Lstat(path string, stat *platform.Stat_t) error { - return platform.Lstat(d.join(path), stat) +func (d *dirFS) Lstat(path string) (platform.Stat_t, error) { + return platform.Lstat(d.join(path)) } // Stat implements FS.Stat -func (d *dirFS) Stat(path string, stat *platform.Stat_t) error { - return platform.Stat(d.join(path), stat) +func (d *dirFS) Stat(path string) (platform.Stat_t, error) { + return platform.Stat(d.join(path)) } // Mkdir implements FS.Mkdir diff --git a/internal/sysfs/dirfs_test.go b/internal/sysfs/dirfs_test.go index 07576113..8da4fcb5 100644 --- a/internal/sysfs/dirfs_test.go +++ b/internal/sysfs/dirfs_test.go @@ -140,9 +140,9 @@ func testChmod(t *testing.T, testFS FS, path string) { } func requireMode(t *testing.T, testFS FS, path string, mode fs.FileMode) { - var stat platform.Stat_t - require.NoError(t, testFS.Stat(path, &stat)) - require.Equal(t, mode, stat.Mode.Perm()) + st, err := testFS.Stat(path) + require.NoError(t, err) + require.Equal(t, mode, st.Mode.Perm()) } func TestDirFS_Rename(t *testing.T) { @@ -638,8 +638,8 @@ func TestDirFS_Utimesns(t *testing.T) { panic(tc) } - var oldSt platform.Stat_t - require.NoError(t, testFS.Lstat(statPath, &oldSt)) + oldSt, err := testFS.Lstat(statPath) + require.NoError(t, err) err = testFS.Utimens(path, tc.times, !symlinkNoFollow) if symlinkNoFollow && !platform.SupportsSymlinkNoFollow { @@ -648,8 +648,8 @@ func TestDirFS_Utimesns(t *testing.T) { } require.NoError(t, err) - var newSt platform.Stat_t - require.NoError(t, testFS.Lstat(statPath, &newSt)) + newSt, err := testFS.Lstat(statPath) + require.NoError(t, err) if platform.CompilerSupported() { if tc.times != nil && tc.times[0].Nsec == platform.UTIME_OMIT { @@ -711,8 +711,8 @@ func TestDirFS_Stat(t *testing.T) { name := `e:xperi\ment.txt` require.NoError(t, os.WriteFile(path.Join(tmpDir, name), nil, 0o600)) - var st platform.Stat_t - require.NoError(t, testFS.Stat(name, &st)) + _, err := testFS.Stat(name) + require.NoError(t, err) }) } } diff --git a/internal/sysfs/readfs.go b/internal/sysfs/readfs.go index ec1accd7..1d18a9d0 100644 --- a/internal/sysfs/readfs.go +++ b/internal/sysfs/readfs.go @@ -141,13 +141,13 @@ func maskForReads(f fs.File) fs.File { } // Lstat implements FS.Lstat -func (r *readFS) Lstat(path string, lstat *platform.Stat_t) error { - return r.fs.Lstat(path, lstat) +func (r *readFS) Lstat(path string) (platform.Stat_t, error) { + return r.fs.Lstat(path) } // Stat implements FS.Stat -func (r *readFS) Stat(path string, stat *platform.Stat_t) error { - return r.fs.Stat(path, stat) +func (r *readFS) Stat(path string) (platform.Stat_t, error) { + return r.fs.Stat(path) } // Readlink implements FS.Readlink diff --git a/internal/sysfs/rootfs.go b/internal/sysfs/rootfs.go index e6720a16..3a9ff181 100644 --- a/internal/sysfs/rootfs.go +++ b/internal/sysfs/rootfs.go @@ -192,11 +192,10 @@ func (d *openRootDir) readDir() (err error) { } func (d *openRootDir) rootEntry(name string, fsI int) (fs.DirEntry, error) { - var stat platform.Stat_t - if err := d.c.fs[fsI].Stat(".", &stat); err != nil { + if st, err := d.c.fs[fsI].Stat("."); err != nil { return nil, err } else { - return &dirInfo{name, &stat}, nil + return &dirInfo{name, st}, nil } } @@ -206,7 +205,7 @@ type dirInfo struct { // directory is masked. For example, we don't want to leak the underlying // host directory name. name string - stat *platform.Stat_t + stat platform.Stat_t } func (i *dirInfo) Name() string { return i.name } @@ -247,15 +246,15 @@ func (d *openRootDir) ReadDir(count int) ([]fs.DirEntry, error) { } // Lstat implements FS.Lstat -func (c *CompositeFS) Lstat(path string, stat *platform.Stat_t) error { +func (c *CompositeFS) Lstat(path string) (platform.Stat_t, error) { matchIndex, relativePath := c.chooseFS(path) - return c.fs[matchIndex].Lstat(relativePath, stat) + return c.fs[matchIndex].Lstat(relativePath) } // Stat implements FS.Stat -func (c *CompositeFS) Stat(path string, stat *platform.Stat_t) error { +func (c *CompositeFS) Stat(path string) (platform.Stat_t, error) { matchIndex, relativePath := c.chooseFS(path) - return c.fs[matchIndex].Stat(relativePath, stat) + return c.fs[matchIndex].Stat(relativePath) } // Mkdir implements FS.Mkdir diff --git a/internal/sysfs/sysfs.go b/internal/sysfs/sysfs.go index 71865e9c..e677c120 100644 --- a/internal/sysfs/sysfs.go +++ b/internal/sysfs/sysfs.go @@ -75,7 +75,7 @@ type FS interface { // same value. // - When the path is a symbolic link, the stat returned is for the link, // not the file it refers to. - Lstat(path string, stat *platform.Stat_t) error + Lstat(path string) (platform.Stat_t, error) // Stat is similar to syscall.Stat, except the path is relative to this // file system. @@ -91,7 +91,7 @@ type FS interface { // same value. // - When the path is a symbolic link, the stat returned is for the file // it refers to. - Stat(path string, stat *platform.Stat_t) error + Stat(path string) (platform.Stat_t, error) // Mkdir is similar to os.Mkdir, except the path is relative to this file // system, and syscall.Errno are returned instead of a os.PathError. diff --git a/internal/sysfs/sysfs_test.go b/internal/sysfs/sysfs_test.go index c1bc5e9b..aef35713 100644 --- a/internal/sysfs/sysfs_test.go +++ b/internal/sysfs/sysfs_test.go @@ -71,8 +71,8 @@ func testOpen_O_RDWR(t *testing.T, tmpDir string, testFS FS) { require.NoError(t, err) defer f.Close() - var st platform.Stat_t - require.NoError(t, platform.StatFile(f, &st)) + _, err = platform.StatFile(f) + require.NoError(t, err) }) } } @@ -263,84 +263,90 @@ human } func testLstat(t *testing.T, testFS FS) { - var stat platform.Stat_t - require.EqualErrno(t, syscall.ENOENT, testFS.Lstat("cat", &stat)) - require.EqualErrno(t, syscall.ENOENT, testFS.Lstat("sub/cat", &stat)) + _, err := testFS.Lstat("cat") + require.EqualErrno(t, syscall.ENOENT, err) + _, err = testFS.Lstat("sub/cat") + require.EqualErrno(t, syscall.ENOENT, err) + + var st platform.Stat_t t.Run("dir", func(t *testing.T) { - err := testFS.Lstat(".", &stat) + st, err = testFS.Lstat(".") require.NoError(t, err) - require.True(t, stat.Mode.IsDir()) - require.NotEqual(t, uint64(0), stat.Ino) + require.True(t, st.Mode.IsDir()) + require.NotEqual(t, uint64(0), st.Ino) }) - var statFile platform.Stat_t + var stFile platform.Stat_t t.Run("file", func(t *testing.T) { - require.NoError(t, testFS.Lstat("animals.txt", &statFile)) - require.Zero(t, statFile.Mode.Type()) - require.Equal(t, int64(30), statFile.Size) - require.NotEqual(t, uint64(0), stat.Ino) + stFile, err = testFS.Lstat("animals.txt") + require.NoError(t, err) + require.Zero(t, stFile.Mode.Type()) + require.Equal(t, int64(30), stFile.Size) + require.NotEqual(t, uint64(0), st.Ino) }) t.Run("link to file", func(t *testing.T) { - requireLinkStat(t, testFS, "animals.txt", &statFile) + requireLinkStat(t, testFS, "animals.txt", stFile) }) - var statSubdir platform.Stat_t + var stSubdir platform.Stat_t t.Run("subdir", func(t *testing.T) { - require.NoError(t, testFS.Lstat("sub", &statSubdir)) - require.True(t, statSubdir.Mode.IsDir()) - require.NotEqual(t, uint64(0), stat.Ino) + stSubdir, err = testFS.Lstat("sub") + require.NoError(t, err) + require.True(t, stSubdir.Mode.IsDir()) + require.NotEqual(t, uint64(0), st.Ino) }) t.Run("link to dir", func(t *testing.T) { - requireLinkStat(t, testFS, "sub", &statSubdir) + requireLinkStat(t, testFS, "sub", stSubdir) }) t.Run("link to dir link", func(t *testing.T) { pathLink := "sub-link" - var statLink platform.Stat_t - require.NoError(t, testFS.Lstat(pathLink, &statLink)) + stLink, err := testFS.Lstat(pathLink) + require.NoError(t, err) - requireLinkStat(t, testFS, pathLink, &statLink) + requireLinkStat(t, testFS, pathLink, stLink) }) } -func requireLinkStat(t *testing.T, testFS FS, path string, stat *platform.Stat_t) { +func requireLinkStat(t *testing.T, testFS FS, path string, stat platform.Stat_t) { link := path + "-link" - var linkStat platform.Stat_t - require.NoError(t, testFS.Lstat(link, &linkStat)) - require.NotEqual(t, stat.Ino, linkStat.Ino) // inodes are not equal - require.Equal(t, fs.ModeSymlink, linkStat.Mode.Type()) + stLink, err := testFS.Lstat(link) + require.NoError(t, err) + require.NotEqual(t, stat.Ino, stLink.Ino) // inodes are not equal + require.Equal(t, fs.ModeSymlink, stLink.Mode.Type()) // From https://linux.die.net/man/2/lstat: // The size of a symbolic link is the length of the pathname it // contains, without a terminating null byte. if runtime.GOOS == "windows" { // size is zero, not the path length - require.Zero(t, linkStat.Size) + require.Zero(t, stLink.Size) } else { - require.Equal(t, int64(len(path)), linkStat.Size) + require.Equal(t, int64(len(path)), stLink.Size) } } func testStat(t *testing.T, testFS FS) { - var stat platform.Stat_t - require.EqualErrno(t, syscall.ENOENT, testFS.Stat("cat", &stat)) - require.EqualErrno(t, syscall.ENOENT, testFS.Stat("sub/cat", &stat)) + _, err := testFS.Stat("cat") + require.EqualErrno(t, syscall.ENOENT, err) + _, err = testFS.Stat("sub/cat") + require.EqualErrno(t, syscall.ENOENT, err) - err := testFS.Stat("sub/test.txt", &stat) + st, err := testFS.Stat("sub/test.txt") require.NoError(t, err) - require.False(t, stat.Mode.IsDir()) - require.NotEqual(t, uint64(0), stat.Dev) - require.NotEqual(t, uint64(0), stat.Ino) + require.False(t, st.Mode.IsDir()) + require.NotEqual(t, uint64(0), st.Dev) + require.NotEqual(t, uint64(0), st.Ino) - err = testFS.Stat("sub", &stat) + st, err = testFS.Stat("sub") require.NoError(t, err) - require.True(t, stat.Mode.IsDir()) + require.True(t, st.Mode.IsDir()) // windows before go 1.20 has trouble reading the inode information on directories. if runtime.GOOS != "windows" || platform.IsGo120 { - require.NotEqual(t, uint64(0), stat.Dev) - require.NotEqual(t, uint64(0), stat.Ino) + require.NotEqual(t, uint64(0), st.Dev) + require.NotEqual(t, uint64(0), st.Ino) } } diff --git a/internal/sysfs/unsupported.go b/internal/sysfs/unsupported.go index 8aa21dae..7e5a4bc7 100644 --- a/internal/sysfs/unsupported.go +++ b/internal/sysfs/unsupported.go @@ -27,13 +27,13 @@ func (UnimplementedFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.Fil } // Lstat implements FS.Lstat -func (UnimplementedFS) Lstat(path string, stat *platform.Stat_t) error { - return syscall.ENOSYS +func (UnimplementedFS) Lstat(path string) (platform.Stat_t, error) { + return platform.Stat_t{}, syscall.ENOSYS } // Stat implements FS.Stat -func (UnimplementedFS) Stat(path string, stat *platform.Stat_t) error { - return syscall.ENOSYS +func (UnimplementedFS) Stat(path string) (platform.Stat_t, error) { + return platform.Stat_t{}, syscall.ENOSYS } // Readlink implements FS.Readlink