sysfs: return st instead of accepting it (#1261)
This returns stat as a value instead of a pointer param. This is both more efficient and faster. It is also more efficient than returning a pointer to a stat. Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -96,8 +96,8 @@ func fdAllocateFn(_ context.Context, mod api.Module, params []uint64) Errno {
|
|||||||
return ErrnoInval
|
return ErrnoInval
|
||||||
}
|
}
|
||||||
|
|
||||||
var st platform.Stat_t
|
st, err := f.Stat()
|
||||||
if err := f.Stat(&st); err != nil {
|
if err != nil {
|
||||||
return ToErrno(err)
|
return ToErrno(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,12 +350,12 @@ func fdFilestatGetFunc(mod api.Module, fd, resultBuf uint32) Errno {
|
|||||||
return ErrnoBadf
|
return ErrnoBadf
|
||||||
}
|
}
|
||||||
|
|
||||||
var st platform.Stat_t
|
st, err := f.Stat()
|
||||||
if err := f.Stat(&st); err != nil {
|
if err != nil {
|
||||||
return ToErrno(err)
|
return ToErrno(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := writeFilestat(buf, &st); err != nil {
|
if err = writeFilestat(buf, &st); err != nil {
|
||||||
return ToErrno(err)
|
return ToErrno(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -896,11 +896,11 @@ func dotDirents(f *sys.FileEntry) ([]*platform.Dirent, error) {
|
|||||||
}
|
}
|
||||||
dotDotIno := uint64(0)
|
dotDotIno := uint64(0)
|
||||||
if !f.IsPreopen && f.Name != "." {
|
if !f.IsPreopen && f.Name != "." {
|
||||||
var st platform.Stat_t
|
if st, err := f.FS.Stat(path.Dir(f.Name)); err != nil {
|
||||||
if err = f.FS.Stat(path.Dir(f.Name), &st); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
|
} else {
|
||||||
|
dotDotIno = st.Ino
|
||||||
}
|
}
|
||||||
dotDotIno = st.Ino
|
|
||||||
}
|
}
|
||||||
return []*platform.Dirent{
|
return []*platform.Dirent{
|
||||||
{Name: ".", Ino: dotIno, Type: fs.ModeDir},
|
{Name: ".", Ino: dotIno, Type: fs.ModeDir},
|
||||||
@@ -1436,9 +1436,9 @@ func pathFilestatGetFn(_ context.Context, mod api.Module, params []uint64) Errno
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
if (flags & LOOKUP_SYMLINK_FOLLOW) == 0 {
|
if (flags & LOOKUP_SYMLINK_FOLLOW) == 0 {
|
||||||
err = preopen.Lstat(pathName, &st)
|
st, err = preopen.Lstat(pathName)
|
||||||
} else {
|
} else {
|
||||||
err = preopen.Stat(pathName, &st)
|
st, err = preopen.Stat(pathName)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ToErrno(err)
|
return ToErrno(err)
|
||||||
@@ -1451,7 +1451,7 @@ func pathFilestatGetFn(_ context.Context, mod api.Module, params []uint64) Errno
|
|||||||
return ErrnoFault
|
return ErrnoFault
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := writeFilestat(buf, &st); err != nil {
|
if err = writeFilestat(buf, &st); err != nil {
|
||||||
return ToErrno(err)
|
return ToErrno(err)
|
||||||
}
|
}
|
||||||
return ErrnoSuccess
|
return ErrnoSuccess
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ func Test_fdAllocate(t *testing.T) {
|
|||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
requireSizeEqual := func(exp int64) {
|
requireSizeEqual := func(exp int64) {
|
||||||
var st platform.Stat_t
|
st, err := f.Stat()
|
||||||
require.NoError(t, f.Stat(&st))
|
require.NoError(t, err)
|
||||||
require.Equal(t, exp, st.Size)
|
require.Equal(t, exp, st.Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -849,8 +849,8 @@ func Test_fdFilestatSetTimes(t *testing.T) {
|
|||||||
f, ok := fsc.LookupFile(fd)
|
f, ok := fsc.LookupFile(fd)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
var st platform.Stat_t
|
st, err := f.Stat()
|
||||||
require.NoError(t, f.Stat(&st))
|
require.NoError(t, err)
|
||||||
prevAtime, prevMtime := st.Atim, st.Mtim
|
prevAtime, prevMtime := st.Atim, st.Mtim
|
||||||
|
|
||||||
requireErrnoResult(t, tc.expectedErrno, mod, FdFilestatSetTimesName,
|
requireErrnoResult(t, tc.expectedErrno, mod, FdFilestatSetTimesName,
|
||||||
@@ -862,8 +862,8 @@ func Test_fdFilestatSetTimes(t *testing.T) {
|
|||||||
f, ok := fsc.LookupFile(fd)
|
f, ok := fsc.LookupFile(fd)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
var st platform.Stat_t
|
st, err = f.Stat()
|
||||||
require.NoError(t, f.Stat(&st))
|
require.NoError(t, err)
|
||||||
if tc.flags&FstflagsAtim != 0 {
|
if tc.flags&FstflagsAtim != 0 {
|
||||||
require.Equal(t, tc.atime, st.Atim)
|
require.Equal(t, tc.atime, st.Atim)
|
||||||
} else if tc.flags&FstflagsAtimNow != 0 {
|
} else if tc.flags&FstflagsAtimNow != 0 {
|
||||||
@@ -3551,8 +3551,10 @@ func Test_pathFilestatSetTimes(t *testing.T) {
|
|||||||
fsc := sys.FS()
|
fsc := sys.FS()
|
||||||
|
|
||||||
var oldSt platform.Stat_t
|
var oldSt platform.Stat_t
|
||||||
|
var err error
|
||||||
if tc.expectedErrno == ErrnoSuccess {
|
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),
|
requireErrnoResult(t, tc.expectedErrno, mod, PathFilestatSetTimesName, uint64(fd), uint64(tc.flags),
|
||||||
@@ -3563,8 +3565,8 @@ func Test_pathFilestatSetTimes(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var newSt platform.Stat_t
|
newSt, err := fsc.RootFS().Stat(pathName)
|
||||||
require.NoError(t, fsc.RootFS().Stat(pathName, &newSt))
|
require.NoError(t, err)
|
||||||
|
|
||||||
if platform.CompilerSupported() {
|
if platform.CompilerSupported() {
|
||||||
if tc.fstFlags&FstflagsAtim != 0 {
|
if tc.fstFlags&FstflagsAtim != 0 {
|
||||||
@@ -3639,8 +3641,7 @@ func Test_pathLink(t *testing.T) {
|
|||||||
require.NoError(t, f.Close())
|
require.NoError(t, f.Close())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var st platform.Stat_t
|
st, err := platform.StatFile(f)
|
||||||
require.NoError(t, platform.StatFile(f, &st))
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.False(t, st.Mode&os.ModeSymlink == os.ModeSymlink)
|
require.False(t, st.Mode&os.ModeSymlink == os.ModeSymlink)
|
||||||
require.Equal(t, uint64(2), st.Nlink)
|
require.Equal(t, uint64(2), st.Nlink)
|
||||||
@@ -4870,8 +4871,8 @@ func Test_fdReaddir_dotEntriesHaveRealInodes(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// get the real inode of the current directory
|
// get the real inode of the current directory
|
||||||
var st platform.Stat_t
|
st, err := preopen.Stat(readDirTarget)
|
||||||
require.NoError(t, preopen.Stat(readDirTarget, &st))
|
require.NoError(t, err)
|
||||||
dirents := []byte{1, 0, 0, 0, 0, 0, 0, 0} // d_next = 1
|
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, u64.LeBytes(st.Ino)...) // d_ino
|
||||||
dirents = append(dirents, 1, 0, 0, 0) // d_namlen = 1 character
|
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
|
dirents = append(dirents, '.') // name
|
||||||
|
|
||||||
// get the real inode of the parent directory
|
// 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, 2, 0, 0, 0, 0, 0, 0, 0) // d_next = 2
|
||||||
dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino
|
dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino
|
||||||
dirents = append(dirents, 2, 0, 0, 0) // d_namlen = 2 characters
|
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()
|
defer f.Close()
|
||||||
|
|
||||||
// get the real inode of the current directory
|
// get the real inode of the current directory
|
||||||
var st platform.Stat_t
|
st, err := preopen.Stat(dirName)
|
||||||
require.NoError(t, preopen.Stat(dirName, &st))
|
require.NoError(t, err)
|
||||||
dirents := []byte{1, 0, 0, 0, 0, 0, 0, 0} // d_next = 1
|
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, u64.LeBytes(st.Ino)...) // d_ino
|
||||||
dirents = append(dirents, 1, 0, 0, 0) // d_namlen = 1 character
|
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
|
dirents = append(dirents, '.') // name
|
||||||
|
|
||||||
// get the real inode of the parent directory
|
// 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, 2, 0, 0, 0, 0, 0, 0, 0) // d_next = 2
|
||||||
dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino
|
dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino
|
||||||
dirents = append(dirents, 2, 0, 0, 0) // d_namlen = 2 characters
|
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
|
dirents = append(dirents, '.', '.') // name
|
||||||
|
|
||||||
// get the real inode of the file
|
// 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, 3, 0, 0, 0, 0, 0, 0, 0) // d_next = 3
|
||||||
dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino
|
dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino
|
||||||
dirents = append(dirents, 4, 0, 0, 0) // d_namlen = 4 characters
|
dirents = append(dirents, 4, 0, 0, 0) // d_namlen = 4 characters
|
||||||
|
|||||||
@@ -100,16 +100,16 @@ func WriteTestFiles(tmpDir string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// os.Stat uses GetFileInformationByHandle internally.
|
// os.Stat uses GetFileInformationByHandle internally.
|
||||||
var stat platform.Stat_t
|
var st platform.Stat_t
|
||||||
if err = platform.Stat(path, &stat); err != nil {
|
if st, err = platform.Stat(path); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if stat.Mtim == info.ModTime().UnixNano() {
|
if st.Mtim == info.ModTime().UnixNano() {
|
||||||
return nil // synced!
|
return nil // synced!
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we need to sync the timestamps.
|
// 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
|
return
|
||||||
|
|||||||
@@ -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) {
|
func syscallStat(mod api.Module, path string) (*jsSt, error) {
|
||||||
fsc := mod.(*wasm.CallContext).Sys.FS()
|
fsc := mod.(*wasm.CallContext).Sys.FS()
|
||||||
|
|
||||||
var stat platform.Stat_t
|
if st, err := fsc.RootFS().Stat(path); err != nil {
|
||||||
if err := fsc.RootFS().Stat(path, &stat); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return newJsSt(st), nil
|
||||||
}
|
}
|
||||||
return newJsSt(&stat), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// jsfsLstat implements jsFn for syscall.Lstat
|
// 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) {
|
func syscallLstat(mod api.Module, path string) (*jsSt, error) {
|
||||||
fsc := mod.(*wasm.CallContext).Sys.FS()
|
fsc := mod.(*wasm.CallContext).Sys.FS()
|
||||||
|
|
||||||
var stat platform.Stat_t
|
if st, err := fsc.RootFS().Lstat(path); err != nil {
|
||||||
if err := fsc.RootFS().Lstat(path, &stat); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return newJsSt(st), nil
|
||||||
}
|
}
|
||||||
return newJsSt(&stat), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// jsfsFstat implements jsFn for syscall.Open
|
// jsfsFstat implements jsFn for syscall.Open
|
||||||
@@ -190,14 +190,14 @@ func syscallFstat(fsc *internalsys.FSContext, fd uint32) (*jsSt, error) {
|
|||||||
return nil, syscall.EBADF
|
return nil, syscall.EBADF
|
||||||
}
|
}
|
||||||
|
|
||||||
var st platform.Stat_t
|
if st, err := f.Stat(); err != nil {
|
||||||
if err := f.Stat(&st); err != nil {
|
|
||||||
return nil, err
|
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 := &jsSt{}
|
||||||
ret.isDir = st.Mode.IsDir()
|
ret.isDir = st.Mode.IsDir()
|
||||||
ret.dev = st.Dev
|
ret.dev = st.Dev
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ func normalizeTimespec(path string, times *[2]syscall.Timespec, i int) (ts sysca
|
|||||||
// - https://github.com/golang/go/issues/32558.
|
// - https://github.com/golang/go/issues/32558.
|
||||||
// - https://go-review.googlesource.com/c/go/+/219638 (unmerged)
|
// - https://go-review.googlesource.com/c/go/+/219638 (unmerged)
|
||||||
var st Stat_t
|
var st Stat_t
|
||||||
if err = stat(path, &st); err != nil {
|
if st, err = stat(path); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch i {
|
switch i {
|
||||||
|
|||||||
@@ -146,8 +146,8 @@ func testFutimens(t *testing.T, usePath bool) {
|
|||||||
panic(tc)
|
panic(tc)
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldSt Stat_t
|
oldSt, err := Lstat(statPath)
|
||||||
require.NoError(t, Lstat(statPath, &oldSt))
|
require.NoError(t, err)
|
||||||
|
|
||||||
if usePath {
|
if usePath {
|
||||||
err = Utimens(path, tc.times, !symlinkNoFollow)
|
err = Utimens(path, tc.times, !symlinkNoFollow)
|
||||||
@@ -172,8 +172,8 @@ func testFutimens(t *testing.T, usePath bool) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var newSt Stat_t
|
newSt, err := Lstat(statPath)
|
||||||
require.NoError(t, Lstat(statPath, &newSt))
|
require.NoError(t, err)
|
||||||
|
|
||||||
if CompilerSupported() {
|
if CompilerSupported() {
|
||||||
if tc.times != nil && tc.times[0].Nsec == UTIME_OMIT {
|
if tc.times != nil && tc.times[0].Nsec == UTIME_OMIT {
|
||||||
|
|||||||
@@ -58,38 +58,39 @@ type Stat_t struct {
|
|||||||
// The primary difference between this and Stat is, when the path is a
|
// 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
|
// symbolic link, the stat is about the link, not its target, such as directory
|
||||||
// listings.
|
// listings.
|
||||||
func Lstat(path string, st *Stat_t) error {
|
func Lstat(path string) (Stat_t, error) {
|
||||||
err := lstat(path, st) // extracted to override more expensively in windows
|
st, err := lstat(path) // extracted to override more expensively in windows
|
||||||
return UnwrapOSError(err)
|
return st, UnwrapOSError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stat is like syscall.Stat. This returns syscall.ENOENT if the path doesn't
|
// Stat is like syscall.Stat. This returns syscall.ENOENT if the path doesn't
|
||||||
// exist.
|
// exist.
|
||||||
func Stat(path string, st *Stat_t) error {
|
func Stat(path string) (Stat_t, error) {
|
||||||
err := stat(path, st) // extracted to override more expensively in windows
|
st, err := stat(path) // extracted to override more expensively in windows
|
||||||
return UnwrapOSError(err)
|
return st, UnwrapOSError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatFile is like syscall.Fstat, but for fs.File instead of a file
|
// 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.
|
// descriptor. This returns syscall.EBADF if the file or directory was closed.
|
||||||
// Note: windows allows you to stat a closed directory.
|
// Note: windows allows you to stat a closed directory.
|
||||||
func StatFile(f fs.File, st *Stat_t) (err error) {
|
func StatFile(f fs.File) (Stat_t, error) {
|
||||||
err = statFile(f, st)
|
st, err := statFile(f)
|
||||||
if err = UnwrapOSError(err); err == syscall.EIO {
|
if err = UnwrapOSError(err); err == syscall.EIO {
|
||||||
err = syscall.EBADF
|
err = syscall.EBADF
|
||||||
}
|
}
|
||||||
return
|
return st, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultStatFile(f fs.File, st *Stat_t) (err error) {
|
func defaultStatFile(f fs.File) (Stat_t, error) {
|
||||||
var t fs.FileInfo
|
if t, err := f.Stat(); err != nil {
|
||||||
if t, err = f.Stat(); err == nil {
|
return Stat_t{}, err
|
||||||
fillStatFromFileInfo(st, t)
|
} 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.Ino = 0
|
||||||
st.Dev = 0
|
st.Dev = 0
|
||||||
st.Mode = t.Mode()
|
st.Mode = t.Mode()
|
||||||
@@ -99,4 +100,5 @@ func fillStatFromDefaultFileInfo(st *Stat_t, t fs.FileInfo) {
|
|||||||
st.Atim = mtim
|
st.Atim = mtim
|
||||||
st.Mtim = mtim
|
st.Mtim = mtim
|
||||||
st.Ctim = mtim
|
st.Ctim = mtim
|
||||||
|
return st
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,24 +8,24 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func lstat(path string, st *Stat_t) (err error) {
|
func lstat(path string) (Stat_t, error) {
|
||||||
var t fs.FileInfo
|
if t, err := os.Lstat(path); err != nil {
|
||||||
if t, err = os.Lstat(path); err == nil {
|
return Stat_t{}, err
|
||||||
fillStatFromFileInfo(st, t)
|
} else {
|
||||||
|
return statFromFileInfo(t), nil
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func stat(path string, st *Stat_t) (err error) {
|
func stat(path string) (Stat_t, error) {
|
||||||
var t fs.FileInfo
|
if t, err := os.Stat(path); err != nil {
|
||||||
if t, err = os.Stat(path); err == nil {
|
return Stat_t{}, err
|
||||||
fillStatFromFileInfo(st, t)
|
} else {
|
||||||
|
return statFromFileInfo(t), nil
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func statFile(f fs.File, st *Stat_t) error {
|
func statFile(f fs.File) (Stat_t, error) {
|
||||||
return defaultStatFile(f, st)
|
return defaultStatFile(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func inoFromFileInfo(_ readdirFile, t fs.FileInfo) (ino uint64, err error) {
|
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
|
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 {
|
if d, ok := t.Sys().(*syscall.Stat_t); ok {
|
||||||
|
st := Stat_t{}
|
||||||
st.Dev = uint64(d.Dev)
|
st.Dev = uint64(d.Dev)
|
||||||
st.Ino = d.Ino
|
st.Ino = d.Ino
|
||||||
st.Uid = d.Uid
|
st.Uid = d.Uid
|
||||||
@@ -50,7 +51,7 @@ func fillStatFromFileInfo(st *Stat_t, t fs.FileInfo) {
|
|||||||
st.Mtim = mtime.Sec*1e9 + mtime.Nsec
|
st.Mtim = mtime.Sec*1e9 + mtime.Nsec
|
||||||
ctime := d.Ctimespec
|
ctime := d.Ctimespec
|
||||||
st.Ctim = ctime.Sec*1e9 + ctime.Nsec
|
st.Ctim = ctime.Sec*1e9 + ctime.Nsec
|
||||||
} else {
|
return st
|
||||||
fillStatFromDefaultFileInfo(st, t)
|
|
||||||
}
|
}
|
||||||
|
return statFromDefaultFileInfo(t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,35 +11,36 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func lstat(path string, st *Stat_t) (err error) {
|
func lstat(path string) (Stat_t, error) {
|
||||||
var t fs.FileInfo
|
if t, err := os.Lstat(path); err != nil {
|
||||||
if t, err = os.Lstat(path); err == nil {
|
return Stat_t{}, err
|
||||||
fillStatFromFileInfo(st, t)
|
} else {
|
||||||
|
return statFromFileInfo(t), nil
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func stat(path string, st *Stat_t) (err error) {
|
func stat(path string) (Stat_t, error) {
|
||||||
var t fs.FileInfo
|
if t, err := os.Stat(path); err != nil {
|
||||||
if t, err = os.Stat(path); err == nil {
|
return Stat_t{}, err
|
||||||
fillStatFromFileInfo(st, t)
|
} else {
|
||||||
|
return statFromFileInfo(t), nil
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func statFile(f fs.File, st *Stat_t) error {
|
func statFile(f fs.File) (Stat_t, error) {
|
||||||
return defaultStatFile(f, st)
|
return defaultStatFile(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func inoFromFileInfo(_ readdirFile, t fs.FileInfo) (ino uint64, err error) {
|
func inoFromFileInfo(_ readdirFile, t fs.FileInfo) (ino uint64, err error) {
|
||||||
if d, ok := t.Sys().(*syscall.Stat_t); ok {
|
if d, ok := t.Sys().(*syscall.Stat_t); ok {
|
||||||
ino = (d.Ino)
|
ino = d.Ino
|
||||||
}
|
}
|
||||||
return
|
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 {
|
if d, ok := t.Sys().(*syscall.Stat_t); ok {
|
||||||
|
st := Stat_t{}
|
||||||
st.Dev = uint64(d.Dev)
|
st.Dev = uint64(d.Dev)
|
||||||
st.Ino = uint64(d.Ino)
|
st.Ino = uint64(d.Ino)
|
||||||
st.Uid = d.Uid
|
st.Uid = d.Uid
|
||||||
@@ -53,7 +54,7 @@ func fillStatFromFileInfo(st *Stat_t, t fs.FileInfo) {
|
|||||||
st.Mtim = mtime.Sec*1e9 + mtime.Nsec
|
st.Mtim = mtime.Sec*1e9 + mtime.Nsec
|
||||||
ctime := d.Ctim
|
ctime := d.Ctim
|
||||||
st.Ctim = ctime.Sec*1e9 + ctime.Nsec
|
st.Ctim = ctime.Sec*1e9 + ctime.Nsec
|
||||||
} else {
|
return st
|
||||||
fillStatFromDefaultFileInfo(st, t)
|
|
||||||
}
|
}
|
||||||
|
return statFromDefaultFileInfo(t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,139 +15,152 @@ import (
|
|||||||
func TestLstat(t *testing.T) {
|
func TestLstat(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
var stat Stat_t
|
_, err := Lstat(path.Join(tmpDir, "cat"))
|
||||||
require.EqualErrno(t, syscall.ENOENT, Lstat(path.Join(tmpDir, "cat"), &stat))
|
require.EqualErrno(t, syscall.ENOENT, err)
|
||||||
require.EqualErrno(t, syscall.ENOENT, Lstat(path.Join(tmpDir, "sub/cat"), &stat))
|
_, err = Lstat(path.Join(tmpDir, "sub/cat"))
|
||||||
|
require.EqualErrno(t, syscall.ENOENT, err)
|
||||||
|
|
||||||
|
var st Stat_t
|
||||||
t.Run("dir", func(t *testing.T) {
|
t.Run("dir", func(t *testing.T) {
|
||||||
err := Lstat(tmpDir, &stat)
|
st, err = Lstat(tmpDir)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, stat.Mode.IsDir())
|
require.True(t, st.Mode.IsDir())
|
||||||
require.NotEqual(t, uint64(0), stat.Ino)
|
require.NotEqual(t, uint64(0), st.Ino)
|
||||||
})
|
})
|
||||||
|
|
||||||
file := path.Join(tmpDir, "file")
|
file := path.Join(tmpDir, "file")
|
||||||
var statFile Stat_t
|
var stFile Stat_t
|
||||||
|
|
||||||
t.Run("file", func(t *testing.T) {
|
t.Run("file", func(t *testing.T) {
|
||||||
require.NoError(t, os.WriteFile(file, []byte{1, 2}, 0o400))
|
require.NoError(t, os.WriteFile(file, []byte{1, 2}, 0o400))
|
||||||
require.NoError(t, Lstat(file, &statFile))
|
stFile, err = Lstat(file)
|
||||||
require.Zero(t, statFile.Mode.Type())
|
require.NoError(t, err)
|
||||||
require.Equal(t, int64(2), statFile.Size)
|
require.Zero(t, stFile.Mode.Type())
|
||||||
require.NotEqual(t, uint64(0), statFile.Ino)
|
require.Equal(t, int64(2), stFile.Size)
|
||||||
|
require.NotEqual(t, uint64(0), stFile.Ino)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("link to file", func(t *testing.T) {
|
t.Run("link to file", func(t *testing.T) {
|
||||||
requireLinkStat(t, file, &statFile)
|
requireLinkStat(t, file, stFile)
|
||||||
})
|
})
|
||||||
|
|
||||||
subdir := path.Join(tmpDir, "sub")
|
subdir := path.Join(tmpDir, "sub")
|
||||||
var statSubdir Stat_t
|
var stSubdir Stat_t
|
||||||
t.Run("subdir", func(t *testing.T) {
|
t.Run("subdir", func(t *testing.T) {
|
||||||
require.NoError(t, os.Mkdir(subdir, 0o500))
|
require.NoError(t, os.Mkdir(subdir, 0o500))
|
||||||
|
|
||||||
require.NoError(t, Lstat(subdir, &statSubdir))
|
stSubdir, err = Lstat(subdir)
|
||||||
require.True(t, statSubdir.Mode.IsDir())
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, uint64(0), statSubdir.Ino)
|
require.True(t, stSubdir.Mode.IsDir())
|
||||||
|
require.NotEqual(t, uint64(0), stSubdir.Ino)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("link to dir", func(t *testing.T) {
|
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) {
|
t.Run("link to dir link", func(t *testing.T) {
|
||||||
pathLink := subdir + "-link"
|
pathLink := subdir + "-link"
|
||||||
var statLink Stat_t
|
stLink, err := Lstat(pathLink)
|
||||||
require.NoError(t, Lstat(pathLink, &statLink))
|
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"
|
link := path + "-link"
|
||||||
var linkStat Stat_t
|
|
||||||
require.NoError(t, os.Symlink(path, link))
|
require.NoError(t, os.Symlink(path, link))
|
||||||
|
|
||||||
require.NoError(t, Lstat(link, &linkStat))
|
stLink, err := Lstat(link)
|
||||||
require.NotEqual(t, uint64(0), linkStat.Ino)
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, stat.Ino, linkStat.Ino) // inodes are not equal
|
|
||||||
require.Equal(t, fs.ModeSymlink, linkStat.Mode.Type())
|
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:
|
// From https://linux.die.net/man/2/lstat:
|
||||||
// The size of a symbolic link is the length of the pathname it
|
// The size of a symbolic link is the length of the pathname it
|
||||||
// contains, without a terminating null byte.
|
// contains, without a terminating null byte.
|
||||||
if runtime.GOOS == "windows" { // size is zero, not the path length
|
if runtime.GOOS == "windows" { // size is zero, not the path length
|
||||||
require.Zero(t, linkStat.Size)
|
require.Zero(t, stLink.Size)
|
||||||
} else {
|
} else {
|
||||||
require.Equal(t, int64(len(path)), linkStat.Size)
|
require.Equal(t, int64(len(path)), stLink.Size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStat(t *testing.T) {
|
func TestStat(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
var stat Stat_t
|
_, err := Stat(path.Join(tmpDir, "cat"))
|
||||||
require.EqualErrno(t, syscall.ENOENT, Stat(path.Join(tmpDir, "cat"), &stat))
|
require.EqualErrno(t, syscall.ENOENT, err)
|
||||||
require.EqualErrno(t, syscall.ENOENT, Stat(path.Join(tmpDir, "sub/cat"), &stat))
|
_, err = Stat(path.Join(tmpDir, "sub/cat"))
|
||||||
|
require.EqualErrno(t, syscall.ENOENT, err)
|
||||||
|
|
||||||
|
var st Stat_t
|
||||||
|
|
||||||
t.Run("dir", func(t *testing.T) {
|
t.Run("dir", func(t *testing.T) {
|
||||||
err := Stat(tmpDir, &stat)
|
st, err = Stat(tmpDir)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, stat.Mode.IsDir())
|
require.True(t, st.Mode.IsDir())
|
||||||
require.NotEqual(t, uint64(0), stat.Ino)
|
require.NotEqual(t, uint64(0), st.Ino)
|
||||||
})
|
})
|
||||||
|
|
||||||
file := path.Join(tmpDir, "file")
|
file := path.Join(tmpDir, "file")
|
||||||
var statFile Stat_t
|
var stFile Stat_t
|
||||||
|
|
||||||
t.Run("file", func(t *testing.T) {
|
t.Run("file", func(t *testing.T) {
|
||||||
require.NoError(t, os.WriteFile(file, nil, 0o400))
|
require.NoError(t, os.WriteFile(file, nil, 0o400))
|
||||||
require.NoError(t, Stat(file, &statFile))
|
|
||||||
require.False(t, statFile.Mode.IsDir())
|
stFile, err = Stat(file)
|
||||||
require.NotEqual(t, uint64(0), stat.Ino)
|
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) {
|
t.Run("link to file", func(t *testing.T) {
|
||||||
link := path.Join(tmpDir, "file-link")
|
link := path.Join(tmpDir, "file-link")
|
||||||
require.NoError(t, os.Symlink(file, link))
|
require.NoError(t, os.Symlink(file, link))
|
||||||
|
|
||||||
require.NoError(t, Stat(link, &stat))
|
stLink, err := Stat(link)
|
||||||
require.Equal(t, statFile, stat) // resolves to the file
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, stFile, stLink) // resolves to the file
|
||||||
})
|
})
|
||||||
|
|
||||||
subdir := path.Join(tmpDir, "sub")
|
subdir := path.Join(tmpDir, "sub")
|
||||||
var statSubdir Stat_t
|
var stSubdir Stat_t
|
||||||
t.Run("subdir", func(t *testing.T) {
|
t.Run("subdir", func(t *testing.T) {
|
||||||
require.NoError(t, os.Mkdir(subdir, 0o500))
|
require.NoError(t, os.Mkdir(subdir, 0o500))
|
||||||
|
|
||||||
require.NoError(t, Stat(subdir, &statSubdir))
|
stSubdir, err = Stat(subdir)
|
||||||
require.True(t, statSubdir.Mode.IsDir())
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, uint64(0), stat.Ino)
|
require.True(t, stSubdir.Mode.IsDir())
|
||||||
|
require.NotEqual(t, uint64(0), st.Ino)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("link to dir", func(t *testing.T) {
|
t.Run("link to dir", func(t *testing.T) {
|
||||||
link := path.Join(tmpDir, "dir-link")
|
link := path.Join(tmpDir, "dir-link")
|
||||||
require.NoError(t, os.Symlink(subdir, link))
|
require.NoError(t, os.Symlink(subdir, link))
|
||||||
|
|
||||||
require.NoError(t, Stat(link, &stat))
|
stLink, err := Stat(link)
|
||||||
require.Equal(t, statSubdir, stat) // resolves to the dir
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, stSubdir, stLink) // resolves to the dir
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStatFile(t *testing.T) {
|
func TestStatFile(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
var stat Stat_t
|
var st Stat_t
|
||||||
|
|
||||||
tmpDirF, err := OpenFile(tmpDir, syscall.O_RDONLY, 0)
|
tmpDirF, err := OpenFile(tmpDir, syscall.O_RDONLY, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer tmpDirF.Close()
|
defer tmpDirF.Close()
|
||||||
|
|
||||||
t.Run("dir", func(t *testing.T) {
|
t.Run("dir", func(t *testing.T) {
|
||||||
err = StatFile(tmpDirF, &stat)
|
st, err = StatFile(tmpDirF)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, stat.Mode.IsDir())
|
require.True(t, st.Mode.IsDir())
|
||||||
requireDirectoryDevIno(t, stat)
|
requireDirectoryDevIno(t, st)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Windows allows you to stat a closed dir because it is accessed by path,
|
// 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" {
|
if runtime.GOOS != "windows" {
|
||||||
t.Run("closed dir", func(t *testing.T) {
|
t.Run("closed dir", func(t *testing.T) {
|
||||||
require.NoError(t, tmpDirF.Close())
|
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()
|
defer fileF.Close()
|
||||||
|
|
||||||
t.Run("file", func(t *testing.T) {
|
t.Run("file", func(t *testing.T) {
|
||||||
err = StatFile(fileF, &stat)
|
st, err = StatFile(fileF)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.False(t, stat.Mode.IsDir())
|
require.False(t, st.Mode.IsDir())
|
||||||
require.NotEqual(t, uint64(0), stat.Ino)
|
require.NotEqual(t, uint64(0), st.Ino)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("closed file", func(t *testing.T) {
|
t.Run("closed file", func(t *testing.T) {
|
||||||
require.NoError(t, fileF.Close())
|
require.NoError(t, fileF.Close())
|
||||||
require.EqualErrno(t, syscall.EBADF, StatFile(fileF, &stat))
|
_, err = StatFile(fileF)
|
||||||
require.NotEqual(t, uint64(0), stat.Ino)
|
require.EqualErrno(t, syscall.EBADF, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
subdir := path.Join(tmpDir, "sub")
|
subdir := path.Join(tmpDir, "sub")
|
||||||
@@ -185,16 +199,17 @@ func TestStatFile(t *testing.T) {
|
|||||||
defer subdirF.Close()
|
defer subdirF.Close()
|
||||||
|
|
||||||
t.Run("subdir", func(t *testing.T) {
|
t.Run("subdir", func(t *testing.T) {
|
||||||
err = StatFile(subdirF, &stat)
|
st, err = StatFile(subdirF)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, stat.Mode.IsDir())
|
require.True(t, st.Mode.IsDir())
|
||||||
requireDirectoryDevIno(t, stat)
|
requireDirectoryDevIno(t, st)
|
||||||
})
|
})
|
||||||
|
|
||||||
if runtime.GOOS != "windows" { // windows allows you to stat a closed dir
|
if runtime.GOOS != "windows" { // windows allows you to stat a closed dir
|
||||||
t.Run("closed subdir", func(t *testing.T) {
|
t.Run("closed subdir", func(t *testing.T) {
|
||||||
require.NoError(t, subdirF.Close())
|
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))
|
err := os.Chtimes(file, time.UnixMicro(tc.atimeNsec/1e3), time.UnixMicro(tc.mtimeNsec/1e3))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
file, err := os.Open(file)
|
f, err := os.Open(file)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer file.Close()
|
defer f.Close()
|
||||||
|
|
||||||
var stat Stat_t
|
st, err := StatFile(f)
|
||||||
require.NoError(t, StatFile(file, &stat))
|
require.NoError(t, err)
|
||||||
require.Equal(t, stat.Atim, tc.atimeNsec)
|
require.Equal(t, st.Atim, tc.atimeNsec)
|
||||||
require.Equal(t, stat.Mtim, tc.mtimeNsec)
|
require.Equal(t, st.Mtim, tc.mtimeNsec)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,28 +289,29 @@ func TestStatFile_dev_inode(t *testing.T) {
|
|||||||
defer l2.Close()
|
defer l2.Close()
|
||||||
|
|
||||||
// First, stat the directory
|
// First, stat the directory
|
||||||
var stat1 Stat_t
|
st1, err := StatFile(d)
|
||||||
require.NoError(t, StatFile(d, &stat1))
|
require.NoError(t, err)
|
||||||
requireDirectoryDevIno(t, stat1)
|
requireDirectoryDevIno(t, st1)
|
||||||
|
|
||||||
// Now, stat the files in it
|
// Now, stat the files in it
|
||||||
require.NoError(t, StatFile(f1, &stat1))
|
st1, err = StatFile(f1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
var stat2 Stat_t
|
st2, err := StatFile(f2)
|
||||||
require.NoError(t, StatFile(f2, &stat2))
|
require.NoError(t, err)
|
||||||
|
|
||||||
var stat3 Stat_t
|
st3, err := StatFile(l2)
|
||||||
require.NoError(t, StatFile(l2, &stat3))
|
require.NoError(t, err)
|
||||||
|
|
||||||
// The files should be on the same device, but different inodes
|
// The files should be on the same device, but different inodes
|
||||||
require.Equal(t, stat1.Dev, stat2.Dev)
|
require.Equal(t, st1.Dev, st2.Dev)
|
||||||
require.NotEqual(t, stat1.Ino, stat2.Ino)
|
require.NotEqual(t, st1.Ino, st2.Ino)
|
||||||
require.Equal(t, stat2, stat3) // stat on a link is for its target
|
require.Equal(t, st2, st3) // stat on a link is for its target
|
||||||
|
|
||||||
// Redoing stat should result in the same inodes
|
// Redoing stat should result in the same inodes
|
||||||
var stat1Again Stat_t
|
st1Again, err := StatFile(f1)
|
||||||
require.NoError(t, StatFile(f1, &stat1Again))
|
require.NoError(t, err)
|
||||||
require.Equal(t, stat1.Dev, stat1Again.Dev)
|
require.Equal(t, st1.Dev, st1Again.Dev)
|
||||||
|
|
||||||
// On Windows, we cannot rename while opening.
|
// On Windows, we cannot rename while opening.
|
||||||
// So we manually close here before renaming.
|
// So we manually close here before renaming.
|
||||||
@@ -309,9 +325,10 @@ func TestStatFile_dev_inode(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer f1.Close()
|
defer f1.Close()
|
||||||
|
|
||||||
require.NoError(t, StatFile(f1, &stat1Again))
|
st1Again, err = StatFile(f1)
|
||||||
require.Equal(t, stat1.Dev, stat1Again.Dev)
|
require.NoError(t, err)
|
||||||
require.Equal(t, stat1.Ino, stat1Again.Ino)
|
require.Equal(t, st1.Dev, st1Again.Dev)
|
||||||
|
require.Equal(t, st1.Ino, st1Again.Ino)
|
||||||
}
|
}
|
||||||
|
|
||||||
func requireDirectoryDevIno(t *testing.T, st Stat_t) {
|
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, os.Mkdir(dir, 0o0700))
|
||||||
require.NoError(t, chgid(dir, gid))
|
require.NoError(t, chgid(dir, gid))
|
||||||
|
|
||||||
var st Stat_t
|
st, err := Stat(dir)
|
||||||
require.NoError(t, Stat(dir, &st))
|
require.NoError(t, err)
|
||||||
require.Equal(t, uid, st.Uid)
|
require.Equal(t, uid, st.Uid)
|
||||||
require.Equal(t, gid, st.Gid)
|
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, os.Symlink(tmpDir, link))
|
||||||
require.NoError(t, chgid(link, gid))
|
require.NoError(t, chgid(link, gid))
|
||||||
|
|
||||||
var st Stat_t
|
st, err := Lstat(link)
|
||||||
require.NoError(t, Lstat(link, &st))
|
require.NoError(t, err)
|
||||||
require.Equal(t, uid, st.Uid)
|
require.Equal(t, uid, st.Uid)
|
||||||
require.Equal(t, gid, st.Gid)
|
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, os.WriteFile(file, nil, 0o0600))
|
||||||
require.NoError(t, chgid(file, gid))
|
require.NoError(t, chgid(file, gid))
|
||||||
|
|
||||||
var st Stat_t
|
st, err := Lstat(file)
|
||||||
require.NoError(t, Lstat(file, &st))
|
require.NoError(t, err)
|
||||||
require.Equal(t, uid, st.Uid)
|
require.Equal(t, uid, st.Uid)
|
||||||
require.Equal(t, gid, st.Gid)
|
require.Equal(t, gid, st.Gid)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,35 +7,30 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func lstat(path string, st *Stat_t) (err error) {
|
func lstat(path string) (Stat_t, error) {
|
||||||
t, err := os.Lstat(path)
|
t, err := os.Lstat(path)
|
||||||
if err = UnwrapOSError(err); err == nil {
|
if err = UnwrapOSError(err); err != nil {
|
||||||
fillStatFromFileInfo(st, t)
|
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)
|
t, err := os.Stat(path)
|
||||||
if err = UnwrapOSError(err); err == nil {
|
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 {
|
func statFile(f fs.File) (Stat_t, error) {
|
||||||
return defaultStatFile(f, st)
|
return defaultStatFile(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func inoFromFileInfo(readdirFile, fs.FileInfo) (ino uint64, err error) {
|
func inoFromFileInfo(readdirFile, fs.FileInfo) (ino uint64, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func fillStatFromFileInfo(st *Stat_t, t fs.FileInfo) {
|
func statFromFileInfo(t fs.FileInfo) Stat_t {
|
||||||
fillStatFromDefaultFileInfo(st, t)
|
return statFromDefaultFileInfo(t)
|
||||||
}
|
|
||||||
|
|
||||||
func fillStatFromOpenFile(st *Stat_t, fd uintptr, t os.FileInfo) (err error) {
|
|
||||||
fillStatFromFileInfo(st, t)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,26 +8,26 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func lstat(path string, st *Stat_t) error {
|
func lstat(path string) (Stat_t, error) {
|
||||||
attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
|
attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
|
||||||
// Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink.
|
// 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
|
// 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
|
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)
|
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 {
|
if len(path) == 0 {
|
||||||
return syscall.ENOENT
|
return Stat_t{}, syscall.ENOENT
|
||||||
}
|
}
|
||||||
pathp, err := syscall.UTF16PtrFromString(path)
|
pathp, err := syscall.UTF16PtrFromString(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return syscall.EINVAL
|
return Stat_t{}, syscall.EINVAL
|
||||||
}
|
}
|
||||||
|
|
||||||
// open the file handle
|
// open the file handle
|
||||||
@@ -39,41 +39,41 @@ func statPath(createFileAttrs uint32, path string, st *Stat_t) (err error) {
|
|||||||
if err == syscall.ENOTDIR {
|
if err == syscall.ENOTDIR {
|
||||||
err = syscall.ENOENT
|
err = syscall.ENOENT
|
||||||
}
|
}
|
||||||
return err
|
return Stat_t{}, err
|
||||||
}
|
}
|
||||||
defer syscall.CloseHandle(h)
|
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 {
|
if of, ok := f.(fdFile); ok {
|
||||||
// Attempt to get the stat by handle, which works for normal files
|
// 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
|
// 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.
|
// use that approach to fill in inode data, which is not critical.
|
||||||
if err != ERROR_INVALID_HANDLE {
|
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.
|
// inoFromFileInfo uses stat to get the inode information of the file.
|
||||||
func inoFromFileInfo(f readdirFile, t fs.FileInfo) (ino uint64, err error) {
|
func inoFromFileInfo(f readdirFile, t fs.FileInfo) (ino uint64, err error) {
|
||||||
if pf, ok := f.(PathFile); ok {
|
if pf, ok := f.(PathFile); ok {
|
||||||
var st Stat_t
|
|
||||||
inoPath := path.Clean(path.Join(pf.Path(), t.Name()))
|
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
|
ino = st.Ino
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return // not in Win32FileAttributeData
|
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 {
|
if d, ok := t.Sys().(*syscall.Win32FileAttributeData); ok {
|
||||||
|
st := Stat_t{}
|
||||||
st.Ino = 0 // not in Win32FileAttributeData
|
st.Ino = 0 // not in Win32FileAttributeData
|
||||||
st.Dev = 0 // not in Win32FileAttributeData
|
st.Dev = 0 // not in Win32FileAttributeData
|
||||||
st.Mode = t.Mode()
|
st.Mode = t.Mode()
|
||||||
@@ -82,20 +82,21 @@ func fillStatFromFileInfo(st *Stat_t, t fs.FileInfo) {
|
|||||||
st.Atim = d.LastAccessTime.Nanoseconds()
|
st.Atim = d.LastAccessTime.Nanoseconds()
|
||||||
st.Mtim = d.LastWriteTime.Nanoseconds()
|
st.Mtim = d.LastWriteTime.Nanoseconds()
|
||||||
st.Ctim = d.CreationTime.Nanoseconds()
|
st.Ctim = d.CreationTime.Nanoseconds()
|
||||||
|
return st
|
||||||
} else {
|
} 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)
|
winFt, err := syscall.GetFileType(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return Stat_t{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var fi syscall.ByHandleFileInformation
|
var fi syscall.ByHandleFileInformation
|
||||||
if err = syscall.GetFileInformationByHandle(h, &fi); err != nil {
|
if err = syscall.GetFileInformationByHandle(h, &fi); err != nil {
|
||||||
return err
|
return Stat_t{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var m fs.FileMode
|
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
|
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.
|
// 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
|
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-by_handle_file_information
|
||||||
st.Dev = uint64(fi.VolumeSerialNumber)
|
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.Atim = fi.LastAccessTime.Nanoseconds()
|
||||||
st.Mtim = fi.LastWriteTime.Nanoseconds()
|
st.Mtim = fi.LastWriteTime.Nanoseconds()
|
||||||
st.Ctim = fi.CreationTime.Nanoseconds()
|
st.Ctim = fi.CreationTime.Nanoseconds()
|
||||||
return
|
return st, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
// getFileType caches the file type as this cannot change on an open file.
|
||||||
func (w *windowsWrappedFile) getFileType() (fs.FileMode, error) {
|
func (w *windowsWrappedFile) getFileType() (fs.FileMode, error) {
|
||||||
if w.fileType == nil {
|
if w.fileType == nil {
|
||||||
var st Stat_t
|
st, err := StatFile(w.File)
|
||||||
if err := StatFile(w.File, &st); err != nil {
|
if err != nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
ft := st.Mode & fs.ModeType
|
ft := st.Mode & fs.ModeType
|
||||||
|
|||||||
@@ -190,8 +190,7 @@ type cachedStat struct {
|
|||||||
// they couldn't be retrieved.
|
// they couldn't be retrieved.
|
||||||
func (f *FileEntry) CachedStat() (ino uint64, fileType fs.FileMode, err error) {
|
func (f *FileEntry) CachedStat() (ino uint64, fileType fs.FileMode, err error) {
|
||||||
if f.cachedStat == nil {
|
if f.cachedStat == nil {
|
||||||
var st platform.Stat_t
|
if _, err = f.Stat(); err != nil {
|
||||||
if err = f.Stat(&st); err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,14 +198,14 @@ func (f *FileEntry) CachedStat() (ino uint64, fileType fs.FileMode, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stat returns the underlying stat of this file.
|
// 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 {
|
if ld, ok := f.File.(*lazyDir); ok {
|
||||||
var sf fs.File
|
var sf fs.File
|
||||||
if sf, err = ld.file(); err == nil {
|
if sf, err = ld.file(); err == nil {
|
||||||
err = platform.StatFile(sf, st)
|
st, err = platform.StatFile(sf)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = platform.StatFile(f.File, st)
|
st, err = platform.StatFile(f.File)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ func TestFileEntry_cachedStat(t *testing.T) {
|
|||||||
dirFS := sysfs.NewDirFS(tmpDir)
|
dirFS := sysfs.NewDirFS(tmpDir)
|
||||||
|
|
||||||
// get the expected inode
|
// get the expected inode
|
||||||
var st platform.Stat_t
|
st, err := platform.Stat(tmpDir)
|
||||||
require.NoError(t, platform.Stat(tmpDir, &st))
|
require.NoError(t, err)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|||||||
@@ -51,18 +51,18 @@ func (a *adapter) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stat implements FS.Stat
|
// 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)
|
name := cleanPath(path)
|
||||||
f, err := a.fs.Open(name)
|
f, err := a.fs.Open(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return platform.UnwrapOSError(err)
|
return platform.Stat_t{}, platform.UnwrapOSError(err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
return platform.StatFile(f, stat)
|
return platform.StatFile(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lstat implements FS.Lstat
|
// 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
|
// 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
|
// 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
|
// 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:
|
// For further discussions on the topic, see:
|
||||||
// https://github.com/golang/go/issues/49580
|
// https://github.com/golang/go/issues/49580
|
||||||
return a.Stat(path, stat)
|
return a.Stat(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanPath(name string) string {
|
func cleanPath(name string) string {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/internal/fstest"
|
"github.com/tetratelabs/wazero/internal/fstest"
|
||||||
"github.com/tetratelabs/wazero/internal/platform"
|
|
||||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -126,8 +125,8 @@ func TestAdapt_Lstat(t *testing.T) {
|
|||||||
fullPath := joinPath(tmpDir, path)
|
fullPath := joinPath(tmpDir, path)
|
||||||
linkPath := joinPath(tmpDir, path+"-link")
|
linkPath := joinPath(tmpDir, path+"-link")
|
||||||
require.NoError(t, os.Symlink(fullPath, linkPath))
|
require.NoError(t, os.Symlink(fullPath, linkPath))
|
||||||
var stat platform.Stat_t
|
_, err := testFS.Lstat(filepath.Base(linkPath))
|
||||||
require.NoError(t, testFS.Lstat(filepath.Base(linkPath), &stat))
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,13 +46,13 @@ func (d *dirFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lstat implements FS.Lstat
|
// Lstat implements FS.Lstat
|
||||||
func (d *dirFS) Lstat(path string, stat *platform.Stat_t) error {
|
func (d *dirFS) Lstat(path string) (platform.Stat_t, error) {
|
||||||
return platform.Lstat(d.join(path), stat)
|
return platform.Lstat(d.join(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stat implements FS.Stat
|
// Stat implements FS.Stat
|
||||||
func (d *dirFS) Stat(path string, stat *platform.Stat_t) error {
|
func (d *dirFS) Stat(path string) (platform.Stat_t, error) {
|
||||||
return platform.Stat(d.join(path), stat)
|
return platform.Stat(d.join(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mkdir implements FS.Mkdir
|
// Mkdir implements FS.Mkdir
|
||||||
|
|||||||
@@ -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) {
|
func requireMode(t *testing.T, testFS FS, path string, mode fs.FileMode) {
|
||||||
var stat platform.Stat_t
|
st, err := testFS.Stat(path)
|
||||||
require.NoError(t, testFS.Stat(path, &stat))
|
require.NoError(t, err)
|
||||||
require.Equal(t, mode, stat.Mode.Perm())
|
require.Equal(t, mode, st.Mode.Perm())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDirFS_Rename(t *testing.T) {
|
func TestDirFS_Rename(t *testing.T) {
|
||||||
@@ -638,8 +638,8 @@ func TestDirFS_Utimesns(t *testing.T) {
|
|||||||
panic(tc)
|
panic(tc)
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldSt platform.Stat_t
|
oldSt, err := testFS.Lstat(statPath)
|
||||||
require.NoError(t, testFS.Lstat(statPath, &oldSt))
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = testFS.Utimens(path, tc.times, !symlinkNoFollow)
|
err = testFS.Utimens(path, tc.times, !symlinkNoFollow)
|
||||||
if symlinkNoFollow && !platform.SupportsSymlinkNoFollow {
|
if symlinkNoFollow && !platform.SupportsSymlinkNoFollow {
|
||||||
@@ -648,8 +648,8 @@ func TestDirFS_Utimesns(t *testing.T) {
|
|||||||
}
|
}
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var newSt platform.Stat_t
|
newSt, err := testFS.Lstat(statPath)
|
||||||
require.NoError(t, testFS.Lstat(statPath, &newSt))
|
require.NoError(t, err)
|
||||||
|
|
||||||
if platform.CompilerSupported() {
|
if platform.CompilerSupported() {
|
||||||
if tc.times != nil && tc.times[0].Nsec == platform.UTIME_OMIT {
|
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`
|
name := `e:xperi\ment.txt`
|
||||||
require.NoError(t, os.WriteFile(path.Join(tmpDir, name), nil, 0o600))
|
require.NoError(t, os.WriteFile(path.Join(tmpDir, name), nil, 0o600))
|
||||||
|
|
||||||
var st platform.Stat_t
|
_, err := testFS.Stat(name)
|
||||||
require.NoError(t, testFS.Stat(name, &st))
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,13 +141,13 @@ func maskForReads(f fs.File) fs.File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lstat implements FS.Lstat
|
// Lstat implements FS.Lstat
|
||||||
func (r *readFS) Lstat(path string, lstat *platform.Stat_t) error {
|
func (r *readFS) Lstat(path string) (platform.Stat_t, error) {
|
||||||
return r.fs.Lstat(path, lstat)
|
return r.fs.Lstat(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stat implements FS.Stat
|
// Stat implements FS.Stat
|
||||||
func (r *readFS) Stat(path string, stat *platform.Stat_t) error {
|
func (r *readFS) Stat(path string) (platform.Stat_t, error) {
|
||||||
return r.fs.Stat(path, stat)
|
return r.fs.Stat(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Readlink implements FS.Readlink
|
// Readlink implements FS.Readlink
|
||||||
|
|||||||
@@ -192,11 +192,10 @@ func (d *openRootDir) readDir() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *openRootDir) rootEntry(name string, fsI int) (fs.DirEntry, error) {
|
func (d *openRootDir) rootEntry(name string, fsI int) (fs.DirEntry, error) {
|
||||||
var stat platform.Stat_t
|
if st, err := d.c.fs[fsI].Stat("."); err != nil {
|
||||||
if err := d.c.fs[fsI].Stat(".", &stat); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} 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
|
// directory is masked. For example, we don't want to leak the underlying
|
||||||
// host directory name.
|
// host directory name.
|
||||||
name string
|
name string
|
||||||
stat *platform.Stat_t
|
stat platform.Stat_t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *dirInfo) Name() string { return i.name }
|
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
|
// 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)
|
matchIndex, relativePath := c.chooseFS(path)
|
||||||
return c.fs[matchIndex].Lstat(relativePath, stat)
|
return c.fs[matchIndex].Lstat(relativePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stat implements FS.Stat
|
// 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)
|
matchIndex, relativePath := c.chooseFS(path)
|
||||||
return c.fs[matchIndex].Stat(relativePath, stat)
|
return c.fs[matchIndex].Stat(relativePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mkdir implements FS.Mkdir
|
// Mkdir implements FS.Mkdir
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ type FS interface {
|
|||||||
// same value.
|
// same value.
|
||||||
// - When the path is a symbolic link, the stat returned is for the link,
|
// - When the path is a symbolic link, the stat returned is for the link,
|
||||||
// not the file it refers to.
|
// 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
|
// Stat is similar to syscall.Stat, except the path is relative to this
|
||||||
// file system.
|
// file system.
|
||||||
@@ -91,7 +91,7 @@ type FS interface {
|
|||||||
// same value.
|
// same value.
|
||||||
// - When the path is a symbolic link, the stat returned is for the file
|
// - When the path is a symbolic link, the stat returned is for the file
|
||||||
// it refers to.
|
// 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
|
// 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.
|
// system, and syscall.Errno are returned instead of a os.PathError.
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ func testOpen_O_RDWR(t *testing.T, tmpDir string, testFS FS) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
var st platform.Stat_t
|
_, err = platform.StatFile(f)
|
||||||
require.NoError(t, platform.StatFile(f, &st))
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,84 +263,90 @@ human
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testLstat(t *testing.T, testFS FS) {
|
func testLstat(t *testing.T, testFS FS) {
|
||||||
var stat platform.Stat_t
|
_, err := testFS.Lstat("cat")
|
||||||
require.EqualErrno(t, syscall.ENOENT, testFS.Lstat("cat", &stat))
|
require.EqualErrno(t, syscall.ENOENT, err)
|
||||||
require.EqualErrno(t, syscall.ENOENT, testFS.Lstat("sub/cat", &stat))
|
_, err = testFS.Lstat("sub/cat")
|
||||||
|
require.EqualErrno(t, syscall.ENOENT, err)
|
||||||
|
|
||||||
|
var st platform.Stat_t
|
||||||
|
|
||||||
t.Run("dir", func(t *testing.T) {
|
t.Run("dir", func(t *testing.T) {
|
||||||
err := testFS.Lstat(".", &stat)
|
st, err = testFS.Lstat(".")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, stat.Mode.IsDir())
|
require.True(t, st.Mode.IsDir())
|
||||||
require.NotEqual(t, uint64(0), stat.Ino)
|
require.NotEqual(t, uint64(0), st.Ino)
|
||||||
})
|
})
|
||||||
|
|
||||||
var statFile platform.Stat_t
|
var stFile platform.Stat_t
|
||||||
|
|
||||||
t.Run("file", func(t *testing.T) {
|
t.Run("file", func(t *testing.T) {
|
||||||
require.NoError(t, testFS.Lstat("animals.txt", &statFile))
|
stFile, err = testFS.Lstat("animals.txt")
|
||||||
require.Zero(t, statFile.Mode.Type())
|
require.NoError(t, err)
|
||||||
require.Equal(t, int64(30), statFile.Size)
|
require.Zero(t, stFile.Mode.Type())
|
||||||
require.NotEqual(t, uint64(0), stat.Ino)
|
require.Equal(t, int64(30), stFile.Size)
|
||||||
|
require.NotEqual(t, uint64(0), st.Ino)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("link to file", func(t *testing.T) {
|
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) {
|
t.Run("subdir", func(t *testing.T) {
|
||||||
require.NoError(t, testFS.Lstat("sub", &statSubdir))
|
stSubdir, err = testFS.Lstat("sub")
|
||||||
require.True(t, statSubdir.Mode.IsDir())
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, uint64(0), stat.Ino)
|
require.True(t, stSubdir.Mode.IsDir())
|
||||||
|
require.NotEqual(t, uint64(0), st.Ino)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("link to dir", func(t *testing.T) {
|
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) {
|
t.Run("link to dir link", func(t *testing.T) {
|
||||||
pathLink := "sub-link"
|
pathLink := "sub-link"
|
||||||
var statLink platform.Stat_t
|
stLink, err := testFS.Lstat(pathLink)
|
||||||
require.NoError(t, testFS.Lstat(pathLink, &statLink))
|
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"
|
link := path + "-link"
|
||||||
var linkStat platform.Stat_t
|
stLink, err := testFS.Lstat(link)
|
||||||
require.NoError(t, testFS.Lstat(link, &linkStat))
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, stat.Ino, linkStat.Ino) // inodes are not equal
|
require.NotEqual(t, stat.Ino, stLink.Ino) // inodes are not equal
|
||||||
require.Equal(t, fs.ModeSymlink, linkStat.Mode.Type())
|
require.Equal(t, fs.ModeSymlink, stLink.Mode.Type())
|
||||||
// From https://linux.die.net/man/2/lstat:
|
// From https://linux.die.net/man/2/lstat:
|
||||||
// The size of a symbolic link is the length of the pathname it
|
// The size of a symbolic link is the length of the pathname it
|
||||||
// contains, without a terminating null byte.
|
// contains, without a terminating null byte.
|
||||||
if runtime.GOOS == "windows" { // size is zero, not the path length
|
if runtime.GOOS == "windows" { // size is zero, not the path length
|
||||||
require.Zero(t, linkStat.Size)
|
require.Zero(t, stLink.Size)
|
||||||
} else {
|
} else {
|
||||||
require.Equal(t, int64(len(path)), linkStat.Size)
|
require.Equal(t, int64(len(path)), stLink.Size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testStat(t *testing.T, testFS FS) {
|
func testStat(t *testing.T, testFS FS) {
|
||||||
var stat platform.Stat_t
|
_, err := testFS.Stat("cat")
|
||||||
require.EqualErrno(t, syscall.ENOENT, testFS.Stat("cat", &stat))
|
require.EqualErrno(t, syscall.ENOENT, err)
|
||||||
require.EqualErrno(t, syscall.ENOENT, testFS.Stat("sub/cat", &stat))
|
_, 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.NoError(t, err)
|
||||||
require.False(t, stat.Mode.IsDir())
|
require.False(t, st.Mode.IsDir())
|
||||||
require.NotEqual(t, uint64(0), stat.Dev)
|
require.NotEqual(t, uint64(0), st.Dev)
|
||||||
require.NotEqual(t, uint64(0), stat.Ino)
|
require.NotEqual(t, uint64(0), st.Ino)
|
||||||
|
|
||||||
err = testFS.Stat("sub", &stat)
|
st, err = testFS.Stat("sub")
|
||||||
require.NoError(t, err)
|
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.
|
// windows before go 1.20 has trouble reading the inode information on directories.
|
||||||
if runtime.GOOS != "windows" || platform.IsGo120 {
|
if runtime.GOOS != "windows" || platform.IsGo120 {
|
||||||
require.NotEqual(t, uint64(0), stat.Dev)
|
require.NotEqual(t, uint64(0), st.Dev)
|
||||||
require.NotEqual(t, uint64(0), stat.Ino)
|
require.NotEqual(t, uint64(0), st.Ino)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ func (UnimplementedFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.Fil
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lstat implements FS.Lstat
|
// Lstat implements FS.Lstat
|
||||||
func (UnimplementedFS) Lstat(path string, stat *platform.Stat_t) error {
|
func (UnimplementedFS) Lstat(path string) (platform.Stat_t, error) {
|
||||||
return syscall.ENOSYS
|
return platform.Stat_t{}, syscall.ENOSYS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stat implements FS.Stat
|
// Stat implements FS.Stat
|
||||||
func (UnimplementedFS) Stat(path string, stat *platform.Stat_t) error {
|
func (UnimplementedFS) Stat(path string) (platform.Stat_t, error) {
|
||||||
return syscall.ENOSYS
|
return platform.Stat_t{}, syscall.ENOSYS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Readlink implements FS.Readlink
|
// Readlink implements FS.Readlink
|
||||||
|
|||||||
Reference in New Issue
Block a user