Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wasi: implements fd_allocate #1102

Merged
merged 3 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions imports/wasi_snapshot_preview1/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,55 @@ func fdAdviseFn(_ context.Context, mod api.Module, params []uint64) Errno {
// allocation of space in a file.
//
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_allocatefd-fd-offset-filesize-len-filesize---errno
var fdAllocate = stubFunction(
FdAllocateName,
var fdAllocate = newHostFunc(
FdAllocateName, fdAllocateFn,
[]wasm.ValueType{i32, i64, i64},
"fd", "offset", "len",
)

func fdAllocateFn(_ context.Context, mod api.Module, params []uint64) Errno {
fd := uint32(params[0])
offset := params[1]
length := params[2]

fsc := mod.(*wasm.CallContext).Sys.FS()
f, ok := fsc.LookupFile(fd)
if !ok {
return ErrnoBadf
}

tail := int64(offset + length)
if tail < 0 {
return ErrnoInval
}

st, err := f.Stat()
if err != nil {
return ToErrno(err)
}

if st.Size() >= tail {
// We already have enough space.
return ErrnoSuccess
}

// This is implemented the implementation of fs.File by all platforms.
// TODO: this should be removed once we have fs.File.
type truncatable interface {
Truncate(size int64) error
}

osf, ok := f.File.(truncatable)
if !ok {
return ErrnoBadf
}

if err = osf.Truncate(tail); err != nil {
return ToErrno(err)
}
return ErrnoSuccess
}

// fdClose is the WASI function named FdCloseName which closes a file
// descriptor.
//
Expand Down
83 changes: 79 additions & 4 deletions imports/wasi_snapshot_preview1/fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,86 @@ func Test_fdAdvise(t *testing.T) {

// Test_fdAllocate only tests it is stubbed for GrainLang per #271
func Test_fdAllocate(t *testing.T) {
log := requireErrnoNosys(t, FdAllocateName, 0, 0, 0)
tmpDir := t.TempDir() // open before loop to ensure no locking problems.
const fileName = "file.txt"

// Create the target file.
realPath := path.Join(tmpDir, fileName)
require.NoError(t, os.WriteFile(realPath, []byte("0123456789"), 0o600))

mod, r, log := requireProxyModule(t, wazero.NewModuleConfig().WithFSConfig(
wazero.NewFSConfig().WithDirMount(tmpDir, "/"),
))
fsc := mod.(*wasm.CallContext).Sys.FS()
preopen := fsc.RootFS()
defer r.Close(testCtx)

fd, err := fsc.OpenFile(preopen, fileName, os.O_RDWR, 0)
require.NoError(t, err)

f, ok := fsc.LookupFile(fd)
require.True(t, ok)

requireSizeEqual := func(exp int64) {
st, err := f.Stat()
require.NoError(t, err)
require.Equal(t, exp, st.Size())
}

t.Run("errors", func(t *testing.T) {
requireErrno(t, ErrnoBadf, mod, FdAllocateName, uint64(12345), 0, 0)
minusOne := int64(-1)
requireErrno(t, ErrnoInval, mod, FdAllocateName, uint64(fd), uint64(minusOne), uint64(minusOne))
requireErrno(t, ErrnoInval, mod, FdAllocateName, uint64(fd), 0, uint64(minusOne))
requireErrno(t, ErrnoInval, mod, FdAllocateName, uint64(fd), uint64(minusOne), 0)
})

t.Run("do not change size", func(t *testing.T) {
for _, tc := range []struct{ offset, length uint64 }{
{offset: 0, length: 10},
{offset: 5, length: 5},
{offset: 4, length: 0},
{offset: 10, length: 0},
} {
// This shouldn't change the size.
requireErrno(t, ErrnoSuccess, mod, FdAllocateName,
uint64(fd), tc.offset, tc.length)
requireSizeEqual(10)
}
})

t.Run("increase", func(t *testing.T) {
// 10 + 10 > the current size -> increase the size.
requireErrno(t, ErrnoSuccess, mod, FdAllocateName,
uint64(fd), 10, 10)
requireSizeEqual(20)

// But the original content must be kept.
buf, err := os.ReadFile(realPath)
require.NoError(t, err)
require.Equal(t, "0123456789", string(buf[:10]))
})

require.Equal(t, `
--> wasi_snapshot_preview1.fd_allocate(fd=0,offset=0,len=0)
<-- errno=ENOSYS
`, log)
==> wasi_snapshot_preview1.fd_allocate(fd=12345,offset=0,len=0)
<== errno=EBADF
==> wasi_snapshot_preview1.fd_allocate(fd=4,offset=-1,len=-1)
<== errno=EINVAL
==> wasi_snapshot_preview1.fd_allocate(fd=4,offset=0,len=-1)
<== errno=EINVAL
==> wasi_snapshot_preview1.fd_allocate(fd=4,offset=-1,len=0)
<== errno=EINVAL
==> wasi_snapshot_preview1.fd_allocate(fd=4,offset=0,len=10)
<== errno=ESUCCESS
==> wasi_snapshot_preview1.fd_allocate(fd=4,offset=5,len=5)
<== errno=ESUCCESS
==> wasi_snapshot_preview1.fd_allocate(fd=4,offset=4,len=0)
<== errno=ESUCCESS
==> wasi_snapshot_preview1.fd_allocate(fd=4,offset=10,len=0)
<== errno=ESUCCESS
==> wasi_snapshot_preview1.fd_allocate(fd=4,offset=10,len=10)
<== errno=ESUCCESS
`, "\n"+log.String())
}

func Test_fdClose(t *testing.T) {
Expand Down