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

Allow filesystems to set atomic_o_trunc flag #167

Merged
merged 4 commits into from
Oct 25, 2024
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
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))
stapelberg marked this conversation as resolved.
Show resolved Hide resolved
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{}) }
Loading