Skip to content

Commit

Permalink
Allow filesystems to set atomic_o_trunc flag (#167)
Browse files Browse the repository at this point in the history
  • Loading branch information
sbauersfeld authored Oct 25, 2024
1 parent a1c7c82 commit 8ccd611
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 10 deletions.
4 changes: 4 additions & 0 deletions connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ func (c *Connection) Init() error {
initOp.Flags |= fusekernel.InitParallelDirOps
}

if c.cfg.EnableAtomicTrunc {
initOp.Flags |= fusekernel.InitAtomicTrunc
}

return c.Reply(ctx, nil)
}

Expand Down
7 changes: 7 additions & 0 deletions mount_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,13 @@ type MountConfig struct {
// kernel
// Ref: https://github.com/torvalds/linux/commit/5c672ab3f0ee0f78f7acad183f34db0f8781a200
EnableParallelDirOps bool

// Flag to enable atomic truncate during file open operations.
// When enabled, application calls to open with the O_TRUNC flag will cause a FUSE OpenFile
// op with the O_TRUNC flag set. In comparison, the default behavior is an OpenFile op
// without O_TRUNC, followed by a SetInodeAttributes op with the target size set to 0.
// Ref: https://github.com/torvalds/linux/commit/6ff958edbf39c014eb06b65ad25b736be08c4e63
EnableAtomicTrunc bool
}

type FUSEImpl uint8
Expand Down
2 changes: 1 addition & 1 deletion samples/memfs/memfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@ func (fs *memFS) OpenFile(
// Set attribute (name=fileOpenFlagsXattr, value=OpenFlags) to test whether
// we set OpenFlags correctly. The value is checked in test with getXattr.
value := make([]byte, 4)
binary.LittleEndian.PutUint32(value, uint32(op.OpenFlags)&syscall.O_ACCMODE)
binary.LittleEndian.PutUint32(value, uint32(op.OpenFlags))
err := fs.setXattrHelper(inode, &fuseops.SetXattrOp{
Name: FileOpenFlagsXattrName,
Value: value,
Expand Down
94 changes: 85 additions & 9 deletions samples/memfs/memfs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,29 @@ func applyUmask(m os.FileMode) os.FileMode {
return m &^ os.FileMode(umask)
}

func (t *MemFSTest) checkOpenFlagsXattr(
func (t *memFSTest) checkReadWriteOpenFlags(
fileName string, expectedOpenFlags fusekernel.OpenFlags) {
openFlags := t.getOpenFlagsXattr(fileName)
AssertEq(expectedOpenFlags, openFlags&fusekernel.OpenAccessModeMask)
}

func (t *memFSTest) checkOpenFlagsContainsFlag(
fileName string, flag fusekernel.OpenFlags) {
openFlags := t.getOpenFlagsXattr(fileName)
AssertNe(0, openFlags&flag)
}

func (t *memFSTest) checkOpenFlagsNotContainsFlag(
fileName string, flag fusekernel.OpenFlags) {
openFlags := t.getOpenFlagsXattr(fileName)
AssertEq(0, openFlags&flag)
}

func (t *memFSTest) getOpenFlagsXattr(fileName string) fusekernel.OpenFlags {
dest := make([]byte, 4)
_, err := unix.Getxattr(fileName, memfs.FileOpenFlagsXattrName, dest)
AssertEq(nil, err)
openFlags := binary.LittleEndian.Uint32(dest)
AssertEq(openFlags, uint32(expectedOpenFlags))
return fusekernel.OpenFlags(binary.LittleEndian.Uint32(dest))
}

////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -332,7 +348,7 @@ func (t *MemFSTest) CreateNewFile_InRoot() {
slice, err := ioutil.ReadFile(fileName)
AssertEq(nil, err)
ExpectEq(contents, string(slice))
t.checkOpenFlagsXattr(fileName, fusekernel.OpenReadOnly)
t.checkReadWriteOpenFlags(fileName, fusekernel.OpenReadOnly)
}

func (t *MemFSTest) CreateNewFile_InSubDir() {
Expand Down Expand Up @@ -394,7 +410,7 @@ func (t *MemFSTest) ModifyExistingFile_InRoot() {
f, err := os.OpenFile(fileName, os.O_WRONLY, 0400)
t.ToClose = append(t.ToClose, f)
AssertEq(nil, err)
t.checkOpenFlagsXattr(fileName, fusekernel.OpenWriteOnly)
t.checkReadWriteOpenFlags(fileName, fusekernel.OpenWriteOnly)

modifyTime := time.Now()
n, err = f.WriteAt([]byte("H"), 0)
Expand Down Expand Up @@ -423,7 +439,7 @@ func (t *MemFSTest) ModifyExistingFile_InRoot() {
slice, err := ioutil.ReadFile(fileName)
AssertEq(nil, err)
ExpectEq("Hello, world!", string(slice))
t.checkOpenFlagsXattr(fileName, fusekernel.OpenReadOnly)
t.checkReadWriteOpenFlags(fileName, fusekernel.OpenReadOnly)
}

func (t *MemFSTest) ModifyExistingFile_InSubDir() {
Expand Down Expand Up @@ -854,7 +870,7 @@ func (t *MemFSTest) AppendMode() {
f, err := os.OpenFile(fileName, os.O_RDWR|os.O_APPEND, 0600)
t.ToClose = append(t.ToClose, f)
AssertEq(nil, err)
t.checkOpenFlagsXattr(fileName, fusekernel.OpenReadWrite)
t.checkReadWriteOpenFlags(fileName, fusekernel.OpenReadWrite)

// Seek to somewhere silly and then write.
off, err = f.Seek(2, 0)
Expand Down Expand Up @@ -932,7 +948,7 @@ func (t *MemFSTest) Truncate_Smaller() {
f, err := os.OpenFile(fileName, os.O_RDWR, 0)
t.ToClose = append(t.ToClose, f)
AssertEq(nil, err)
t.checkOpenFlagsXattr(fileName, fusekernel.OpenReadWrite)
t.checkReadWriteOpenFlags(fileName, fusekernel.OpenReadWrite)

// Truncate it.
err = f.Truncate(2)
Expand All @@ -947,7 +963,7 @@ func (t *MemFSTest) Truncate_Smaller() {
contents, err := ioutil.ReadFile(fileName)
AssertEq(nil, err)
ExpectEq("ta", string(contents))
t.checkOpenFlagsXattr(fileName, fusekernel.OpenReadOnly)
t.checkReadWriteOpenFlags(fileName, fusekernel.OpenReadOnly)
}

func (t *MemFSTest) Truncate_SameSize() {
Expand Down Expand Up @@ -2018,3 +2034,63 @@ func (t *MknodTest) Fallocate_Larger() {
AssertEq(nil, err)
ExpectEq("taco\x00\x00", string(contents))
}

////////////////////////////////////////////////////////////////////////
// atomic_o_trunc
////////////////////////////////////////////////////////////////////////

type atomicOTruncTest struct {
memFSTest
enableAtomicOTrunc bool
}

func (t *atomicOTruncTest) SetUp(ti *TestInfo) {
// Disable writeback caching so that pid is always available in OpContext
t.MountConfig.DisableWritebackCaching = true
t.MountConfig.EnableAtomicTrunc = t.enableAtomicOTrunc

t.Server = memfs.NewMemFS(currentUid(), currentGid())
t.SampleTest.SetUp(ti)
}

func (t *atomicOTruncTest) OpenFileWithOTrunc() {
// Write a file.
fileName := path.Join(t.Dir, memfs.CheckFileOpenFlagsFileName)
err := ioutil.WriteFile(fileName, []byte("Hello, world!"), 0600)
AssertEq(nil, err)

// Open the file with O_TRUNC
f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_TRUNC, 0400)
AssertEq(nil, err)
defer f.Close()

t.checkReadWriteOpenFlags(fileName, fusekernel.OpenWriteOnly)

if t.enableAtomicOTrunc {
t.checkOpenFlagsContainsFlag(fileName, fusekernel.OpenTruncate)
} else {
t.checkOpenFlagsNotContainsFlag(fileName, fusekernel.OpenTruncate)
}
}

type AtmoicOTruncEnabledTest struct {
atomicOTruncTest
}

func (t *AtmoicOTruncEnabledTest) SetUp(ti *TestInfo) {
t.enableAtomicOTrunc = true
t.atomicOTruncTest.SetUp(ti)
}

func init() { RegisterTestSuite(&AtmoicOTruncEnabledTest{}) }

type AtmoicOTruncDisabledTest struct {
atomicOTruncTest
}

func (t *AtmoicOTruncDisabledTest) SetUp(ti *TestInfo) {
t.enableAtomicOTrunc = false
t.atomicOTruncTest.SetUp(ti)
}

func init() { RegisterTestSuite(&AtmoicOTruncDisabledTest{}) }

0 comments on commit 8ccd611

Please sign in to comment.