aboutsummaryrefslogtreecommitdiff
path: root/internal/fusefrontend
diff options
context:
space:
mode:
authorAnkush Patel2026-02-14 03:41:23 +1300
committerAnkush Patel2026-02-14 03:41:23 +1300
commit8d8fb15f0b3680add1f3b28c062b573a92221ab0 (patch)
treefbd54302af652c38eb292167919125a8b57f5d2a /internal/fusefrontend
parent903fc9d077a81d9224de4207d1672c0b1127cf42 (diff)
parent5f5c34ac78cb9d1765ce9cabe87420c32f9d867e (diff)
Merge branch 'master' into freebsd-support
Diffstat (limited to 'internal/fusefrontend')
-rw-r--r--internal/fusefrontend/node_open_create.go29
-rw-r--r--internal/fusefrontend/node_xattr_darwin.go14
-rw-r--r--internal/fusefrontend/root_node.go29
3 files changed, 37 insertions, 35 deletions
diff --git a/internal/fusefrontend/node_open_create.go b/internal/fusefrontend/node_open_create.go
index 9598559..622d5dc 100644
--- a/internal/fusefrontend/node_open_create.go
+++ b/internal/fusefrontend/node_open_create.go
@@ -2,6 +2,7 @@ package fusefrontend
import (
"context"
+ "os"
"syscall"
"github.com/hanwen/go-fuse/v2/fs"
@@ -12,6 +13,30 @@ import (
"github.com/rfjakob/gocryptfs/v2/internal/tlog"
)
+// mangleOpenCreateFlags is used by Create() and Open() to convert the open flags the user
+// wants to the flags we internally use to open the backing file using Openat().
+// The returned flags always contain O_NOFOLLOW/O_SYMLINK.
+func mangleOpenCreateFlags(flags uint32) (newFlags int) {
+ newFlags = int(flags)
+ // Convert WRONLY to RDWR. We always need read access to do read-modify-write cycles.
+ if (newFlags & syscall.O_ACCMODE) == syscall.O_WRONLY {
+ newFlags = newFlags ^ os.O_WRONLY | os.O_RDWR
+ }
+ // We also cannot open the file in append mode, we need to seek back for RMW
+ newFlags = newFlags &^ os.O_APPEND
+ // O_DIRECT accesses must be aligned in both offset and length. Due to our
+ // crypto header, alignment will be off, even if userspace makes aligned
+ // accesses. Running xfstests generic/013 on ext4 used to trigger lots of
+ // EINVAL errors due to missing alignment. Just fall back to buffered IO.
+ newFlags = newFlags &^ syscallcompat.O_DIRECT
+ // Create and Open are two separate FUSE operations, so O_CREAT should usually not
+ // be part of the Open() flags. Create() will add O_CREAT back itself.
+ newFlags = newFlags &^ syscall.O_CREAT
+ // We always want O_NOFOLLOW/O_SYMLINK to be safe against symlink races
+ newFlags |= syscallcompat.OpenatFlagNofollowSymlink
+ return newFlags
+}
+
// Open - FUSE call. Open already-existing file.
//
// Symlink-safe through Openat().
@@ -23,7 +48,7 @@ func (n *Node) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFl
defer syscall.Close(dirfd)
rn := n.rootNode()
- newFlags := rn.mangleOpenFlags(flags)
+ newFlags := mangleOpenCreateFlags(flags)
// Taking this lock makes sure we don't race openWriteOnlyFile()
rn.openWriteOnlyLock.RLock()
defer rn.openWriteOnlyLock.RUnlock()
@@ -71,7 +96,7 @@ func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint3
if !rn.args.PreserveOwner {
ctx = nil
}
- newFlags := rn.mangleOpenFlags(flags)
+ newFlags := mangleOpenCreateFlags(flags)
// Handle long file name
ctx2 := toFuseCtx(ctx)
if !rn.args.PlaintextNames && nametransform.IsLongContent(cName) {
diff --git a/internal/fusefrontend/node_xattr_darwin.go b/internal/fusefrontend/node_xattr_darwin.go
index f8f224f..1d25f3d 100644
--- a/internal/fusefrontend/node_xattr_darwin.go
+++ b/internal/fusefrontend/node_xattr_darwin.go
@@ -29,8 +29,8 @@ func (n *Node) getXAttr(cAttr string) (out []byte, errno syscall.Errno) {
}
defer syscall.Close(dirfd)
- // O_NONBLOCK to not block on FIFOs.
- fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_NONBLOCK|syscall.O_NOFOLLOW, 0)
+ // O_NONBLOCK to not block on FIFOs, O_SYMLINK to open the symlink itself (if it is one).
+ fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_NONBLOCK|syscall.O_SYMLINK, 0)
if err != nil {
return nil, fs.ToErrno(err)
}
@@ -52,10 +52,10 @@ func (n *Node) setXAttr(context *fuse.Context, cAttr string, cData []byte, flags
defer syscall.Close(dirfd)
// O_NONBLOCK to not block on FIFOs.
- fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_WRONLY|syscall.O_NONBLOCK|syscall.O_NOFOLLOW, 0)
+ fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_WRONLY|syscall.O_NONBLOCK|syscall.O_SYMLINK, 0)
// Directories cannot be opened read-write. Retry.
if err == syscall.EISDIR {
- fd, err = syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NONBLOCK|syscall.O_NOFOLLOW, 0)
+ fd, err = syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NONBLOCK|syscall.O_SYMLINK, 0)
}
if err != nil {
fs.ToErrno(err)
@@ -74,10 +74,10 @@ func (n *Node) removeXAttr(cAttr string) (errno syscall.Errno) {
defer syscall.Close(dirfd)
// O_NONBLOCK to not block on FIFOs.
- fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_WRONLY|syscall.O_NONBLOCK|syscall.O_NOFOLLOW, 0)
+ fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_WRONLY|syscall.O_NONBLOCK|syscall.O_SYMLINK, 0)
// Directories cannot be opened read-write. Retry.
if err == syscall.EISDIR {
- fd, err = syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NONBLOCK|syscall.O_NOFOLLOW, 0)
+ fd, err = syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NONBLOCK|syscall.O_SYMLINK, 0)
}
if err != nil {
return fs.ToErrno(err)
@@ -96,7 +96,7 @@ func (n *Node) listXAttr() (out []string, errno syscall.Errno) {
defer syscall.Close(dirfd)
// O_NONBLOCK to not block on FIFOs.
- fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_NONBLOCK|syscall.O_NOFOLLOW, 0)
+ fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_NONBLOCK|syscall.O_SYMLINK, 0)
if err != nil {
return nil, fs.ToErrno(err)
}
diff --git a/internal/fusefrontend/root_node.go b/internal/fusefrontend/root_node.go
index 489e3c6..38d070d 100644
--- a/internal/fusefrontend/root_node.go
+++ b/internal/fusefrontend/root_node.go
@@ -1,7 +1,6 @@
package fusefrontend
import (
- "os"
"strings"
"sync"
"sync/atomic"
@@ -93,7 +92,9 @@ func NewRootNode(args Args, c *contentenc.ContentEnc, n *nametransform.NameTrans
}
// Suppress the message if the user has already specified -noprealloc
if rn.quirks&syscallcompat.QuirkBtrfsBrokenFalloc != 0 && !args.NoPrealloc {
- syscallcompat.LogQuirk("Btrfs detected, forcing -noprealloc. See https://github.com/rfjakob/gocryptfs/issues/395 for why.")
+ syscallcompat.LogQuirk("Btrfs detected, forcing -noprealloc. " +
+ "Use \"chattr +C\" on the backing directory to enable NOCOW and allow preallocation. " +
+ "See https://github.com/rfjakob/gocryptfs/issues/395 for details.")
}
if statErr == nil {
rn.inoMap.TranslateStat(&st)
@@ -108,30 +109,6 @@ func (rn *RootNode) AfterUnmount() {
rn.dirCache.stats()
}
-// mangleOpenFlags is used by Create() and Open() to convert the open flags the user
-// wants to the flags we internally use to open the backing file.
-// The returned flags always contain O_NOFOLLOW.
-func (rn *RootNode) mangleOpenFlags(flags uint32) (newFlags int) {
- newFlags = int(flags)
- // Convert WRONLY to RDWR. We always need read access to do read-modify-write cycles.
- if (newFlags & syscall.O_ACCMODE) == syscall.O_WRONLY {
- newFlags = newFlags ^ os.O_WRONLY | os.O_RDWR
- }
- // We also cannot open the file in append mode, we need to seek back for RMW
- newFlags = newFlags &^ os.O_APPEND
- // O_DIRECT accesses must be aligned in both offset and length. Due to our
- // crypto header, alignment will be off, even if userspace makes aligned
- // accesses. Running xfstests generic/013 on ext4 used to trigger lots of
- // EINVAL errors due to missing alignment. Just fall back to buffered IO.
- newFlags = newFlags &^ syscallcompat.O_DIRECT
- // Create and Open are two separate FUSE operations, so O_CREAT should not
- // be part of the open flags.
- newFlags = newFlags &^ syscall.O_CREAT
- // We always want O_NOFOLLOW to be safe against symlink races
- newFlags |= syscall.O_NOFOLLOW
- return newFlags
-}
-
// reportMitigatedCorruption is used to report a corruption that was transparently
// mitigated and did not return an error to the user. Pass the name of the corrupt
// item (filename for OpenDir(), xattr name for ListXAttr() etc).