fs: stops pre-fetching the inode of dot-dot ("..") (#1544)

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2023-06-29 21:00:09 +08:00
committed by GitHub
parent 9762d5b28d
commit 221ed0373a
3 changed files with 35 additions and 16 deletions

View File

@@ -745,6 +745,33 @@ yet these are returned for `wasi_snapshot_preview1.fd_readdir`. This was a
change specifically made to pass wasi-testsuite, and has problems known since change specifically made to pass wasi-testsuite, and has problems known since
late 2019. late 2019.
### Why don't we pre-populate an inode for the dot-dot ("..") entry?
We only populate an inode for dot (".") because wasi-testsuite requires it, and
we likely already have it (because we cache it). We could attempt to populate
one for dot-dot (".."), but chose not to.
Firstly, wasi-testsuite does not require the inode of dot-dot, possibly because
the wasip2 adapter doesn't populate it (but we don't really know why).
The only other reason to populate it would be to avoid wasi-libc's stat fanout
when it is missing. However, the inode for dot-dot is not cached, and is also
likely not possible to get on a pseudo file. Even if we could get it, we would
have to use stat. In other words, pre-populating this would have the same cost
as waiting for something to call stat instead.
Fetching dot-dot's inode despite the above not only doesn't help wasi-libc, but
it also hurts languages that don't use it, such as Go. These languages would
pay a stat syscall penalty even if they don't need the inode. In fact, Go
discards both dot entries!
In summary, there are no significant upsides in attempting to pre-fetch
dot-dot's inode, and there are downsides to doing it anyway.
See https://github.com/WebAssembly/wasi-libc/pull/345
See https://github.com/WebAssembly/wasi-testsuite/blob/main/tests/rust/src/bin/fd_readdir.rs#L108
See https://github.com/bytecodealliance/preview2-prototyping/blob/e4c04bcfbd11c42c27c28984948d501a3e168121/crates/wasi-preview1-component-adapter/src/lib.rs#L1037
## Why does "wasi_snapshot_preview1" require dot entries when POSIX does not? ## Why does "wasi_snapshot_preview1" require dot entries when POSIX does not?
In October 2019, WASI project knew requiring dot entries ("." and "..") was not In October 2019, WASI project knew requiring dot entries ("." and "..") was not

View File

@@ -4919,8 +4919,8 @@ func requireOpenFile(t *testing.T, tmpDir string, pathName string, data []byte,
return mod, fd, log, r return mod, fd, log, r
} }
// Test_fdReaddir_dotEntriesHaveRealInodes because wasi-testsuite requires it. // Test_fdReaddir_dotEntryHasARealInode because wasi-testsuite requires it.
func Test_fdReaddir_dotEntriesHaveRealInodes(t *testing.T) { func Test_fdReaddir_dotEntryHasARealInode(t *testing.T) {
if runtime.GOOS == "windows" && !platform.IsGo120 { if runtime.GOOS == "windows" && !platform.IsGo120 {
t.Skip("windows before go 1.20 has trouble reading the inode information on directories.") t.Skip("windows before go 1.20 has trouble reading the inode information on directories.")
} }
@@ -4954,11 +4954,10 @@ func Test_fdReaddir_dotEntriesHaveRealInodes(t *testing.T) {
dirents = append(dirents, 3, 0, 0, 0) // d_type = directory dirents = append(dirents, 3, 0, 0, 0) // d_type = directory
dirents = append(dirents, '.') // name dirents = append(dirents, '.') // name
// get the real inode of the parent directory
st, errno = preopen.Stat(".")
require.EqualErrno(t, 0, errno) require.EqualErrno(t, 0, errno)
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 // See /RATIONALE.md for why we don't attempt to get an inode for ".."
dirents = append(dirents, 0, 0, 0, 0, 0, 0, 0, 0) // d_ino
dirents = append(dirents, 2, 0, 0, 0) // d_namlen = 2 characters dirents = append(dirents, 2, 0, 0, 0) // d_namlen = 2 characters
dirents = append(dirents, 3, 0, 0, 0) // d_type = directory dirents = append(dirents, 3, 0, 0, 0) // d_type = directory
dirents = append(dirents, '.', '.') // name dirents = append(dirents, '.', '.') // name
@@ -5021,7 +5020,8 @@ func Test_fdReaddir_opened_file_written(t *testing.T) {
st, errno = preopen.Stat(".") st, errno = preopen.Stat(".")
require.EqualErrno(t, 0, errno) require.EqualErrno(t, 0, errno)
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 // See /RATIONALE.md for why we don't attempt to get an inode for ".."
dirents = append(dirents, 0, 0, 0, 0, 0, 0, 0, 0) // d_ino
dirents = append(dirents, 2, 0, 0, 0) // d_namlen = 2 characters dirents = append(dirents, 2, 0, 0, 0) // d_namlen = 2 characters
dirents = append(dirents, 3, 0, 0, 0) // d_type = directory dirents = append(dirents, 3, 0, 0, 0) // d_type = directory
dirents = append(dirents, '.', '.') // name dirents = append(dirents, '.', '.') // name

View File

@@ -4,7 +4,6 @@ import (
"io" "io"
"io/fs" "io/fs"
"net" "net"
"path"
"syscall" "syscall"
"github.com/tetratelabs/wazero/internal/descriptor" "github.com/tetratelabs/wazero/internal/descriptor"
@@ -160,15 +159,8 @@ func synthesizeDotEntries(f *FileEntry) (result []fsapi.Dirent, errno syscall.Er
return nil, errno return nil, errno
} }
result = append(result, fsapi.Dirent{Name: ".", Ino: dotIno, Type: fs.ModeDir}) result = append(result, fsapi.Dirent{Name: ".", Ino: dotIno, Type: fs.ModeDir})
dotDotIno := uint64(0) // See /RATIONALE.md for why we don't attempt to get an inode for ".."
if !f.IsPreopen && f.Name != "." { result = append(result, fsapi.Dirent{Name: "..", Ino: 0, Type: fs.ModeDir})
if st, errno := f.FS.Stat(path.Dir(f.Name)); errno != 0 {
return nil, errno
} else {
dotDotIno = st.Ino
}
}
result = append(result, fsapi.Dirent{Name: "..", Ino: dotDotIno, Type: fs.ModeDir})
return result, 0 return result, 0
} }