Skip to content

Commit

Permalink
Adds Datasync to platform.File (#1427)
Browse files Browse the repository at this point in the history
Signed-off-by: Adrian Cole <[email protected]>
  • Loading branch information
codefromthecrypt authored May 2, 2023
1 parent b79c45b commit c5871c7
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 109 deletions.
2 changes: 1 addition & 1 deletion imports/wasi_snapshot_preview1/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func fdDatasyncFn(_ context.Context, mod api.Module, params []uint64) syscall.Er
if f, ok := fsc.LookupFile(fd); !ok {
return syscall.EBADF
} else {
return sysfs.FileDatasync(f.File.File())
return f.File.Datasync()
}
}

Expand Down
25 changes: 25 additions & 0 deletions internal/platform/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,21 @@ type File interface {
// unimplemented. This prevents fake filesystems from erring.
Sync() syscall.Errno

// Datasync synchronizes the data of a file.
//
// # Errors
//
// A zero syscall.Errno is success. The below are expected otherwise:
// - syscall.EBADF if the file or directory was closed.
//
// # Notes
//
// - This is like syscall.Fdatasync and `fdatasync` in POSIX. See
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html
// - This returns with no error instead of syscall.ENOSYS when
// unimplemented. This prevents fake filesystems from erring.
Datasync() syscall.Errno

// Close closes the underlying file.
//
// A zero syscall.Errno is success. The below are expected otherwise:
Expand Down Expand Up @@ -130,6 +145,11 @@ func (UnimplementedFile) Sync() syscall.Errno {
return 0 // not syscall.ENOSYS
}

// Datasync implements File.Datasync
func (UnimplementedFile) Datasync() syscall.Errno {
return 0 // not syscall.ENOSYS
}

type DefaultFile struct {
F fs.File
}
Expand Down Expand Up @@ -167,6 +187,11 @@ func (f *DefaultFile) Sync() syscall.Errno {
return 0 // don't error
}

// Datasync implements File.Datasync
func (f *DefaultFile) Datasync() syscall.Errno {
return fdatasync(f.F)
}

// Close implements File.Close
func (f *DefaultFile) Close() syscall.Errno {
return UnwrapOSError(f.F.Close())
Expand Down
57 changes: 54 additions & 3 deletions internal/platform/file_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package platform

import (
"bytes"
"embed"
"io"
"io/fs"
"os"
"path"
Expand Down Expand Up @@ -29,12 +31,20 @@ func (NoopFile) File() fs.File { panic("noop") }
//go:embed file_test.go
var embedFS embed.FS

func TestFileSync(t *testing.T) {
func TestFileSync_NoError(t *testing.T) {
testSync_NoError(t, File.Sync)
}

func TestFileDatasync_NoError(t *testing.T) {
testSync_NoError(t, File.Datasync)
}

func testSync_NoError(t *testing.T, sync func(File) syscall.Errno) {
ro, err := embedFS.Open("file_test.go")
require.NoError(t, err)
defer ro.Close()

rw, err := os.Create(path.Join(t.TempDir(), "sync"))
rw, err := os.Create(path.Join(t.TempDir(), "datasync"))
require.NoError(t, err)
defer rw.Close()

Expand All @@ -60,7 +70,48 @@ func TestFileSync(t *testing.T) {
tc := tt

t.Run(tc.name, func(b *testing.T) {
require.Zero(t, tc.f.Sync())
require.Zero(t, sync(tc.f))
})
}
}

func TestFileSync(t *testing.T) {
testSync(t, File.Sync)
}

func TestFileDatasync(t *testing.T) {
testSync(t, File.Datasync)
}

// testSync doesn't guarantee sync works because the operating system may
// sync anyway. There is no test in Go for syscall.Fdatasync, but closest is
// similar to below. Effectively, this only tests that things don't error.
func testSync(t *testing.T, sync func(File) syscall.Errno) {
f, errno := os.CreateTemp("", t.Name())
require.NoError(t, errno)
defer f.Close()

expected := "hello world!"

// Write the expected data
_, errno = f.Write([]byte(expected))
require.NoError(t, errno)

// Sync the data.
if errno = sync(&DefaultFile{F: f}); errno == syscall.ENOSYS {
return // don't continue if it isn't supported.
}
require.Zero(t, errno)

// Rewind while the file is still open.
_, err := f.Seek(0, io.SeekStart)
require.NoError(t, err)

// Read data from the file
var buf bytes.Buffer
_, errno = io.Copy(&buf, f)
require.NoError(t, errno)

// It may be the case that sync worked.
require.Equal(t, expected, buf.String())
}
14 changes: 0 additions & 14 deletions internal/platform/sync.go

This file was deleted.

44 changes: 0 additions & 44 deletions internal/platform/sync_test.go

This file was deleted.

9 changes: 9 additions & 0 deletions internal/sys/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,15 @@ func (r *lazyDir) Sync() syscall.Errno {
}
}

// Datasync implements the same method as documented on platform.File
func (r *lazyDir) Datasync() syscall.Errno {
if f, ok := r.file(); !ok {
return syscall.EBADF
} else {
return f.Datasync()
}
}

// File implements the same method as documented on platform.File
func (r *lazyDir) File() fs.File {
if f, ok := r.file(); !ok {
Expand Down
5 changes: 0 additions & 5 deletions internal/sysfs/sysfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,11 +389,6 @@ func ReaderAtOffset(f fs.File, offset int64) io.Reader {
}
}

// FileDatasync is like syscall.Fdatasync except that's only defined in linux.
func FileDatasync(f fs.File) (err syscall.Errno) {
return platform.Fdatasync(f)
}

type enosysReader struct{}

// enosysReader implements io.Reader
Expand Down
42 changes: 0 additions & 42 deletions internal/sysfs/sysfs_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package sysfs

import (
"bytes"
"embed"
_ "embed"
"io"
Expand Down Expand Up @@ -686,47 +685,6 @@ func TestWriterAtOffset_Unsupported(t *testing.T) {
require.Equal(t, syscall.ENOSYS, err)
}

// Test_FileSync doesn't guarantee sync works because the operating system may
// sync anyway. There is no test in Go for os.File Sync, but closest is similar
// to below. Effectively, this only tests that things don't error.
func Test_FileSync(t *testing.T) {
testSync(t, func(f fs.File) syscall.Errno {
return (&platform.DefaultFile{F: f}).Sync()
})
}

// Test_FileDatasync has same issues as Test_Sync.
func Test_FileDatasync(t *testing.T) {
testSync(t, FileDatasync)
}

func testSync(t *testing.T, sync func(fs.File) syscall.Errno) {
f, err := os.CreateTemp("", t.Name())
require.NoError(t, err)
defer f.Close()

expected := "hello world!"

// Write the expected data
_, err = f.Write([]byte(expected))
require.NoError(t, err)

// Sync the data.
require.Zero(t, sync(f))

// Rewind while the file is still open.
_, err = f.Seek(0, io.SeekStart)
require.NoError(t, err)

// Read data from the file
var buf bytes.Buffer
_, err = io.Copy(&buf, f)
require.NoError(t, err)

// It may be the case that sync worked.
require.Equal(t, expected, buf.String())
}

func requireIno(t *testing.T, dirents []*platform.Dirent, expectIno bool) {
for _, e := range dirents {
if expectIno {
Expand Down

0 comments on commit c5871c7

Please sign in to comment.