diff options
| author | Jared Van Bortel | 2026-02-06 15:16:40 -0500 |
|---|---|---|
| committer | Jakob Unterwurzacher | 2026-02-10 21:26:49 +0100 |
| commit | b024e3696d215e6b7a0b8d17ca3eb28af20dd504 (patch) | |
| tree | afd60d9189530c63e3e7d15e1b59f035914b5293 /internal/syscallcompat | |
| parent | 61685370b1aaffde25e5427a08509d33d71746fe (diff) | |
enable falloc on btrfs if CoW is disabled
Diffstat (limited to 'internal/syscallcompat')
| -rw-r--r-- | internal/syscallcompat/quirks_linux.go | 40 |
1 files changed, 38 insertions, 2 deletions
diff --git a/internal/syscallcompat/quirks_linux.go b/internal/syscallcompat/quirks_linux.go index 87af03d..35f754d 100644 --- a/internal/syscallcompat/quirks_linux.go +++ b/internal/syscallcompat/quirks_linux.go @@ -1,11 +1,38 @@ package syscallcompat import ( + "syscall" + "golang.org/x/sys/unix" "github.com/rfjakob/gocryptfs/v2/internal/tlog" ) +// FS_NOCOW_FL is the flag set by "chattr +C" to disable copy-on-write on +// btrfs. Not exported by golang.org/x/sys/unix, value from linux/fs.h. +const FS_NOCOW_FL = 0x00800000 + +// dirHasNoCow checks whether the directory at the given path has the +// NOCOW (No Copy-on-Write) attribute set (i.e. "chattr +C"). +// When a directory has this attribute, files created within it inherit +// NOCOW, which makes fallocate work correctly on btrfs because writes +// go in-place rather than through COW. +func dirHasNoCow(path string) bool { + fd, err := syscall.Open(path, syscall.O_RDONLY|syscall.O_DIRECTORY, 0) + if err != nil { + tlog.Debug.Printf("dirHasNoCow: Open %q failed: %v", path, err) + return false + } + defer syscall.Close(fd) + + flags, err := unix.IoctlGetInt(fd, unix.FS_IOC_GETFLAGS) + if err != nil { + tlog.Debug.Printf("dirHasNoCow: FS_IOC_GETFLAGS on %q failed: %v", path, err) + return false + } + return flags&FS_NOCOW_FL != 0 +} + // DetectQuirks decides if there are known quirks on the backing filesystem // that need to be workarounded. // @@ -21,10 +48,19 @@ func DetectQuirks(cipherdir string) (q uint64) { // Preallocation on Btrfs is broken ( https://github.com/rfjakob/gocryptfs/issues/395 ) // and slow ( https://github.com/rfjakob/gocryptfs/issues/63 ). // + // The root cause is that btrfs COW allocates new blocks on write even for + // preallocated extents, defeating the purpose of fallocate. However, if the + // backing directory has the NOCOW attribute (chattr +C), writes go in-place + // and fallocate works correctly. + // // Cast to uint32 avoids compile error on arm: "constant 2435016766 overflows int32" if uint32(st.Type) == unix.BTRFS_SUPER_MAGIC { - // LogQuirk is called in fusefrontend/root_node.go - q |= QuirkBtrfsBrokenFalloc + if dirHasNoCow(cipherdir) { + tlog.Debug.Printf("DetectQuirks: Btrfs detected but cipherdir has NOCOW attribute (chattr +C), fallocate should work correctly") + } else { + // LogQuirk is called in fusefrontend/root_node.go + q |= QuirkBtrfsBrokenFalloc + } } return q |
