Clears O_CREATE when re-opening a file to set flags (#1458)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -205,7 +205,7 @@ func fdFdstatGetFn(_ context.Context, mod api.Module, params []uint64) syscall.E
|
||||
if f.File.IsNonblock() {
|
||||
fdflags |= wasip1.FD_NONBLOCK
|
||||
}
|
||||
} else if f.File.AccessMode() != syscall.O_RDONLY {
|
||||
} else if f.IsAppend() {
|
||||
fdflags |= wasip1.FD_APPEND
|
||||
}
|
||||
|
||||
|
||||
@@ -250,7 +250,8 @@ func Test_fdFdstatGet(t *testing.T) {
|
||||
|
||||
stdin.File = stdinFile
|
||||
|
||||
fileFD, errno := fsc.OpenFile(preopen, file, os.O_RDONLY, 0)
|
||||
// Make this file writeable, to ensure flags read-back correctly.
|
||||
fileFD, errno := fsc.OpenFile(preopen, file, os.O_RDWR|os.O_APPEND, 0)
|
||||
require.EqualErrno(t, 0, errno)
|
||||
|
||||
dirFD, errno := fsc.OpenFile(preopen, dir, os.O_RDONLY, 0)
|
||||
@@ -325,13 +326,13 @@ func Test_fdFdstatGet(t *testing.T) {
|
||||
fd: fileFD,
|
||||
expectedMemory: []byte{
|
||||
4, 0, // fs_filetype
|
||||
0, 0, 0, 0, 0, 0, // fs_flags
|
||||
1, 0, 0, 0, 0, 0, // fs_flags
|
||||
0xff, 0x1, 0xe0, 0x8, 0x0, 0x0, 0x0, 0x0, // fs_rights_base
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
|
||||
},
|
||||
expectedLog: `
|
||||
==> wasi_snapshot_preview1.fd_fdstat_get(fd=4)
|
||||
<== (stat={filetype=REGULAR_FILE,fdflags=,fs_rights_base=FD_DATASYNC|FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_WRITE|FD_ADVISE|FD_ALLOCATE,fs_rights_inheriting=},errno=ESUCCESS)
|
||||
<== (stat={filetype=REGULAR_FILE,fdflags=APPEND,fs_rights_base=FD_DATASYNC|FD_READ|FD_SEEK|FDSTAT_SET_FLAGS|FD_SYNC|FD_TELL|FD_WRITE|FD_ADVISE|FD_ALLOCATE,fs_rights_inheriting=},errno=ESUCCESS)
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -481,11 +482,6 @@ func Test_fdFdstatGet_StdioNonblock(t *testing.T) {
|
||||
|
||||
func Test_fdFdstatSetFlags(t *testing.T) {
|
||||
tmpDir := t.TempDir() // open before loop to ensure no locking problems.
|
||||
const fileName = "file.txt"
|
||||
|
||||
// Create the target file.
|
||||
realPath := joinPath(tmpDir, fileName)
|
||||
require.NoError(t, os.WriteFile(realPath, []byte("0123456789"), 0o600))
|
||||
|
||||
stdinR, stdinW := openPipe(t)
|
||||
defer closePipe(stdinR, stdinW)
|
||||
@@ -505,8 +501,18 @@ func Test_fdFdstatSetFlags(t *testing.T) {
|
||||
preopen := fsc.RootFS()
|
||||
defer r.Close(testCtx)
|
||||
|
||||
// First, open it with O_APPEND.
|
||||
fd, errno := fsc.OpenFile(preopen, fileName, os.O_RDWR|os.O_APPEND, 0)
|
||||
// First, O_CREATE the file with O_APPEND. We use O_EXCL because that
|
||||
// triggers an EEXIST error if called a second time with O_CREATE. Our
|
||||
// logic should clear O_CREATE preventing this.
|
||||
const fileName = "file.txt"
|
||||
// Create the target file.
|
||||
fd, errno := fsc.OpenFile(preopen, fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE|syscall.O_EXCL, 0o600)
|
||||
require.EqualErrno(t, 0, errno)
|
||||
|
||||
// Write the initial text to the file.
|
||||
f, ok := fsc.LookupFile(fd)
|
||||
require.True(t, ok)
|
||||
_, errno = f.File.Write([]byte("0123456789"))
|
||||
require.EqualErrno(t, 0, errno)
|
||||
|
||||
writeWazero := func() {
|
||||
|
||||
@@ -255,6 +255,11 @@ type cachedStat struct {
|
||||
Ino uint64
|
||||
}
|
||||
|
||||
// IsAppend returns true if the file is open with syscall.O_APPEND.
|
||||
func (f *FileEntry) IsAppend() bool {
|
||||
return f.openFlag&syscall.O_APPEND != 0
|
||||
}
|
||||
|
||||
// Inode returns the cached inode from of platform.Stat_t or an error if it
|
||||
// couldn't be retrieved.
|
||||
func (f *FileEntry) Inode() (ino uint64, errno syscall.Errno) {
|
||||
@@ -462,6 +467,8 @@ func (c *FSContext) ChangeOpenFlag(fd int32, flag int) syscall.Errno {
|
||||
return errno
|
||||
} else if isDir {
|
||||
return syscall.EISDIR
|
||||
} else if flag&syscall.O_APPEND == f.openFlag&syscall.O_APPEND {
|
||||
return 0 // don't re-open
|
||||
}
|
||||
|
||||
if flag&syscall.O_APPEND != 0 {
|
||||
@@ -470,6 +477,9 @@ func (c *FSContext) ChangeOpenFlag(fd int32, flag int) syscall.Errno {
|
||||
f.openFlag &= ^syscall.O_APPEND
|
||||
}
|
||||
|
||||
// Clear any create flag, as we are re-opening, not re-creating.
|
||||
f.openFlag &= ^syscall.O_CREAT
|
||||
|
||||
// Changing the flag while opening is not really supported well in Go. Even when using
|
||||
// syscall package, the feasibility of doing so really depends on the platform. For examples:
|
||||
//
|
||||
|
||||
@@ -329,7 +329,7 @@ func TestFSContext_ChangeOpenFlag(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dirFs := sysfs.NewDirFS(tmpDir)
|
||||
|
||||
const fileName = "dir"
|
||||
const fileName = "file"
|
||||
require.NoError(t, os.WriteFile(path.Join(tmpDir, fileName), []byte("0123456789"), 0o600))
|
||||
|
||||
c := Context{}
|
||||
@@ -360,6 +360,22 @@ func TestFSContext_ChangeOpenFlag(t *testing.T) {
|
||||
f2, ok := fsc.openedFiles.Lookup(fd)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, f2.openFlag&syscall.O_APPEND, 0)
|
||||
|
||||
t.Run("create exclusive", func(t *testing.T) {
|
||||
// O_EXCL triggers an EEXIST error if called a second time with
|
||||
// O_CREATE. This test proves the internal logic clears O_CREATE on
|
||||
// re-open.
|
||||
const fileName = "exclusive"
|
||||
fd, errno := fsc.OpenFile(dirFs, fileName, os.O_RDWR|os.O_CREATE|syscall.O_EXCL, 0o600)
|
||||
require.EqualErrno(t, 0, errno)
|
||||
|
||||
errno = fsc.ChangeOpenFlag(fd, syscall.O_APPEND)
|
||||
require.EqualErrno(t, 0, errno)
|
||||
|
||||
f1, ok := fsc.openedFiles.Lookup(fd)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, f1.openFlag&syscall.O_APPEND, syscall.O_APPEND)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStdio(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user