aboutsummaryrefslogtreecommitdiff
path: root/internal/fusefrontend
diff options
context:
space:
mode:
Diffstat (limited to 'internal/fusefrontend')
-rw-r--r--internal/fusefrontend/args.go2
-rw-r--r--internal/fusefrontend/file.go28
-rw-r--r--internal/fusefrontend/file_allocate_truncate.go24
-rw-r--r--internal/fusefrontend/file_dir_ops.go177
-rw-r--r--internal/fusefrontend/file_holes.go10
-rw-r--r--internal/fusefrontend/node.go32
-rw-r--r--internal/fusefrontend/node_api_check.go1
-rw-r--r--internal/fusefrontend/node_dir_ops.go97
-rw-r--r--internal/fusefrontend/node_helpers.go5
-rw-r--r--internal/fusefrontend/node_open_create.go29
-rw-r--r--internal/fusefrontend/node_prepare_syscall.go3
-rw-r--r--internal/fusefrontend/node_xattr.go39
-rw-r--r--internal/fusefrontend/node_xattr_darwin.go17
-rw-r--r--internal/fusefrontend/node_xattr_linux.go3
-rw-r--r--internal/fusefrontend/prepare_syscall_test.go13
-rw-r--r--internal/fusefrontend/root_node.go36
-rw-r--r--internal/fusefrontend/xattr_unit_test.go6
17 files changed, 329 insertions, 193 deletions
diff --git a/internal/fusefrontend/args.go b/internal/fusefrontend/args.go
index 64a5923..ec3d1c2 100644
--- a/internal/fusefrontend/args.go
+++ b/internal/fusefrontend/args.go
@@ -51,4 +51,6 @@ type Args struct {
OneFileSystem bool
// DeterministicNames disables gocryptfs.diriv files
DeterministicNames bool
+ // NoXattr disables extended attribute operations
+ NoXattr bool
}
diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go
index 0e25de3..64c6ca0 100644
--- a/internal/fusefrontend/file.go
+++ b/internal/fusefrontend/file.go
@@ -37,8 +37,6 @@ type File struct {
// Every FUSE entrypoint should RLock(). The only user of Lock() is
// Release(), which closes the fd and sets "released" to true.
fdLock sync.RWMutex
- // Content encryption helper
- contentEnc *contentenc.ContentEnc
// Device and inode number uniquely identify the backing file
qIno inomap.QIno
// Entry in the open file table
@@ -50,6 +48,8 @@ type File struct {
lastOpCount uint64
// Parent filesystem
rootNode *RootNode
+ // If this open file is a directory, dirHandle will be set, otherwise it's nil.
+ dirHandle *DirHandle
}
// NewFile returns a new go-fuse File instance based on an already-open file
@@ -71,7 +71,6 @@ func NewFile(fd int, cName string, rn *RootNode) (f *File, st *syscall.Stat_t, e
f = &File{
fd: osFile,
- contentEnc: rn.contentEnc,
qIno: qi,
fileTableEntry: e,
rootNode: rn,
@@ -116,7 +115,7 @@ func (f *File) createHeader() (fileID []byte, err error) {
h := contentenc.RandomHeader()
buf := h.Pack()
// Prevent partially written (=corrupt) header by preallocating the space beforehand
- if !f.rootNode.args.NoPrealloc && f.rootNode.quirks&syscallcompat.QuirkBrokenFalloc == 0 {
+ if !f.rootNode.args.NoPrealloc && f.rootNode.quirks&syscallcompat.QuirkBtrfsBrokenFalloc == 0 {
err = syscallcompat.EnospcPrealloc(f.intFd(), 0, contentenc.HeaderLen)
if err != nil {
if !syscallcompat.IsENOSPC(err) {
@@ -175,7 +174,7 @@ func (f *File) doRead(dst []byte, off uint64, length uint64) ([]byte, syscall.Er
log.Panicf("fileID=%v", fileID)
}
// Read the backing ciphertext in one go
- blocks := f.contentEnc.ExplodePlainRange(off, length)
+ blocks := f.rootNode.contentEnc.ExplodePlainRange(off, length)
alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks)
// f.fd.ReadAt takes an int64!
if alignedOffset > math.MaxInt64 {
@@ -204,10 +203,10 @@ func (f *File) doRead(dst []byte, off uint64, length uint64) ([]byte, syscall.Er
tlog.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d", alignedOffset, firstBlockNo, alignedLength, n)
// Decrypt it
- plaintext, err := f.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, fileID)
+ plaintext, err := f.rootNode.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, fileID)
f.rootNode.contentEnc.CReqPool.Put(ciphertext)
if err != nil {
- corruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext)))
+ corruptBlockNo := firstBlockNo + f.rootNode.contentEnc.PlainOffToBlockNo(uint64(len(plaintext)))
tlog.Warn.Printf("doRead %d: corrupt block #%d: %v", f.qIno.Ino, corruptBlockNo, err)
return nil, syscall.EIO
}
@@ -285,20 +284,20 @@ func (f *File) doWrite(data []byte, off int64) (uint32, syscall.Errno) {
}
// Handle payload data
dataBuf := bytes.NewBuffer(data)
- blocks := f.contentEnc.ExplodePlainRange(uint64(off), uint64(len(data)))
+ blocks := f.rootNode.contentEnc.ExplodePlainRange(uint64(off), uint64(len(data)))
toEncrypt := make([][]byte, len(blocks))
for i, b := range blocks {
blockData := dataBuf.Next(int(b.Length))
// Incomplete block -> Read-Modify-Write
if b.IsPartial() {
// Read
- oldData, errno := f.doRead(nil, b.BlockPlainOff(), f.contentEnc.PlainBS())
+ oldData, errno := f.doRead(nil, b.BlockPlainOff(), f.rootNode.contentEnc.PlainBS())
if errno != 0 {
tlog.Warn.Printf("ino%d fh%d: RMW read failed: errno=%d", f.qIno.Ino, f.intFd(), errno)
return 0, errno
}
// Modify
- blockData = f.contentEnc.MergeBlocks(oldData, blockData, int(b.Skip))
+ blockData = f.rootNode.contentEnc.MergeBlocks(oldData, blockData, int(b.Skip))
tlog.Debug.Printf("len(oldData)=%d len(blockData)=%d", len(oldData), len(blockData))
}
tlog.Debug.Printf("ino%d: Writing %d bytes to block #%d",
@@ -307,7 +306,7 @@ func (f *File) doWrite(data []byte, off int64) (uint32, syscall.Errno) {
toEncrypt[i] = blockData
}
// Encrypt all blocks
- ciphertext := f.contentEnc.EncryptBlocks(toEncrypt, blocks[0].BlockNo, f.fileTableEntry.ID)
+ ciphertext := f.rootNode.contentEnc.EncryptBlocks(toEncrypt, blocks[0].BlockNo, f.fileTableEntry.ID)
// Preallocate so we cannot run out of space in the middle of the write.
// This prevents partially written (=corrupt) blocks.
var err error
@@ -316,7 +315,7 @@ func (f *File) doWrite(data []byte, off int64) (uint32, syscall.Errno) {
if cOff > math.MaxInt64 {
return 0, syscall.EFBIG
}
- if !f.rootNode.args.NoPrealloc && f.rootNode.quirks&syscallcompat.QuirkBrokenFalloc == 0 {
+ if !f.rootNode.args.NoPrealloc && f.rootNode.quirks&syscallcompat.QuirkBtrfsBrokenFalloc == 0 {
err = syscallcompat.EnospcPrealloc(f.intFd(), int64(cOff), int64(len(ciphertext)))
if err != nil {
if !syscallcompat.IsENOSPC(err) {
@@ -437,7 +436,10 @@ func (f *File) Getattr(ctx context.Context, a *fuse.AttrOut) syscall.Errno {
}
f.rootNode.inoMap.TranslateStat(&st)
a.FromStat(&st)
- a.Size = f.contentEnc.CipherSizeToPlainSize(a.Size)
+ if a.IsRegular() {
+ a.Size = f.rootNode.contentEnc.CipherSizeToPlainSize(a.Size)
+ }
+ // TODO: Handle symlink size similar to node.translateSize()
if f.rootNode.args.ForceOwner != nil {
a.Owner = *f.rootNode.args.ForceOwner
}
diff --git a/internal/fusefrontend/file_allocate_truncate.go b/internal/fusefrontend/file_allocate_truncate.go
index cae796e..a3decf9 100644
--- a/internal/fusefrontend/file_allocate_truncate.go
+++ b/internal/fusefrontend/file_allocate_truncate.go
@@ -54,7 +54,7 @@ func (f *File) Allocate(ctx context.Context, off uint64, sz uint64, mode uint32)
f.fileTableEntry.ContentLock.Lock()
defer f.fileTableEntry.ContentLock.Unlock()
- blocks := f.contentEnc.ExplodePlainRange(off, sz)
+ blocks := f.rootNode.contentEnc.ExplodePlainRange(off, sz)
firstBlock := blocks[0]
lastBlock := blocks[len(blocks)-1]
@@ -63,7 +63,7 @@ func (f *File) Allocate(ctx context.Context, off uint64, sz uint64, mode uint32)
// the file.
cipherOff := firstBlock.BlockCipherOff()
cipherSz := lastBlock.BlockCipherOff() - cipherOff +
- f.contentEnc.BlockOverhead() + lastBlock.Skip + lastBlock.Length
+ f.rootNode.contentEnc.BlockOverhead() + lastBlock.Skip + lastBlock.Length
err := syscallcompat.Fallocate(f.intFd(), FALLOC_FL_KEEP_SIZE, int64(cipherOff), int64(cipherSz))
tlog.Debug.Printf("Allocate off=%d sz=%d mode=%x cipherOff=%d cipherSz=%d\n",
off, sz, mode, cipherOff, cipherSz)
@@ -113,8 +113,8 @@ func (f *File) truncate(newSize uint64) (errno syscall.Errno) {
return fs.ToErrno(err)
}
- oldB := float32(oldSize) / float32(f.contentEnc.PlainBS())
- newB := float32(newSize) / float32(f.contentEnc.PlainBS())
+ oldB := float32(oldSize) / float32(f.rootNode.contentEnc.PlainBS())
+ newB := float32(newSize) / float32(f.rootNode.contentEnc.PlainBS())
tlog.Debug.Printf("ino%d: FUSE Truncate from %.2f to %.2f blocks (%d to %d bytes)", f.qIno.Ino, oldB, newB, oldSize, newSize)
// File size stays the same - nothing to do
@@ -127,9 +127,9 @@ func (f *File) truncate(newSize uint64) (errno syscall.Errno) {
}
// File shrinks
- blockNo := f.contentEnc.PlainOffToBlockNo(newSize)
- cipherOff := f.contentEnc.BlockNoToCipherOff(blockNo)
- plainOff := f.contentEnc.BlockNoToPlainOff(blockNo)
+ blockNo := f.rootNode.contentEnc.PlainOffToBlockNo(newSize)
+ cipherOff := f.rootNode.contentEnc.BlockNoToCipherOff(blockNo)
+ plainOff := f.rootNode.contentEnc.BlockNoToPlainOff(blockNo)
lastBlockLen := newSize - plainOff
var data []byte
if lastBlockLen > 0 {
@@ -161,7 +161,7 @@ func (f *File) statPlainSize() (uint64, error) {
return 0, err
}
cipherSz := uint64(fi.Size())
- plainSz := uint64(f.contentEnc.CipherSizeToPlainSize(cipherSz))
+ plainSz := uint64(f.rootNode.contentEnc.CipherSizeToPlainSize(cipherSz))
return plainSz, nil
}
@@ -174,8 +174,8 @@ func (f *File) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) syscall.Er
}
newEOFOffset := newPlainSz - 1
if oldPlainSz > 0 {
- n1 := f.contentEnc.PlainOffToBlockNo(oldPlainSz - 1)
- n2 := f.contentEnc.PlainOffToBlockNo(newEOFOffset)
+ n1 := f.rootNode.contentEnc.PlainOffToBlockNo(oldPlainSz - 1)
+ n2 := f.rootNode.contentEnc.PlainOffToBlockNo(newEOFOffset)
// The file is grown within one block, no need to pad anything.
// Write a single zero to the last byte and let doWrite figure out the RMW.
if n1 == n2 {
@@ -194,7 +194,7 @@ func (f *File) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) syscall.Er
}
// The new size is block-aligned. In this case we can do everything ourselves
// and avoid the call to doWrite.
- if newPlainSz%f.contentEnc.PlainBS() == 0 {
+ if newPlainSz%f.rootNode.contentEnc.PlainBS() == 0 {
// The file was empty, so it did not have a header. Create one.
if oldPlainSz == 0 {
id, err := f.createHeader()
@@ -203,7 +203,7 @@ func (f *File) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) syscall.Er
}
f.fileTableEntry.ID = id
}
- cSz := int64(f.contentEnc.PlainSizeToCipherSize(newPlainSz))
+ cSz := int64(f.rootNode.contentEnc.PlainSizeToCipherSize(newPlainSz))
err := syscall.Ftruncate(f.intFd(), cSz)
if err != nil {
tlog.Warn.Printf("Truncate: grow Ftruncate returned error: %v", err)
diff --git a/internal/fusefrontend/file_dir_ops.go b/internal/fusefrontend/file_dir_ops.go
new file mode 100644
index 0000000..b69e7bc
--- /dev/null
+++ b/internal/fusefrontend/file_dir_ops.go
@@ -0,0 +1,177 @@
+package fusefrontend
+
+import (
+ "context"
+ "syscall"
+
+ "github.com/hanwen/go-fuse/v2/fs"
+ "github.com/hanwen/go-fuse/v2/fuse"
+ "github.com/rfjakob/gocryptfs/v2/internal/configfile"
+ "github.com/rfjakob/gocryptfs/v2/internal/nametransform"
+ "github.com/rfjakob/gocryptfs/v2/internal/syscallcompat"
+ "github.com/rfjakob/gocryptfs/v2/internal/tlog"
+)
+
+func (n *Node) OpendirHandle(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
+ var fd int = -1
+ var fdDup int = -1
+ var file *File
+ var dirIV []byte
+ var ds fs.DirStream
+ rn := n.rootNode()
+
+ dirfd, cName, errno := n.prepareAtSyscallMyself()
+ if errno != 0 {
+ return
+ }
+ defer syscall.Close(dirfd)
+
+ // Open backing directory
+ fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
+ if err != nil {
+ errno = fs.ToErrno(err)
+ return
+ }
+
+ // NewLoopbackDirStreamFd gets its own fd to untangle Release vs Releasedir
+ fdDup, err = syscall.Dup(fd)
+
+ if err != nil {
+ errno = fs.ToErrno(err)
+ goto err_out
+ }
+
+ ds, errno = fs.NewLoopbackDirStreamFd(fdDup)
+ if errno != 0 {
+ goto err_out
+ }
+
+ if !rn.args.PlaintextNames {
+ // Read the DirIV from disk
+ dirIV, err = rn.nameTransform.ReadDirIVAt(fd)
+ if err != nil {
+ tlog.Warn.Printf("OpendirHandle: could not read %s: %v", nametransform.DirIVFilename, err)
+ errno = syscall.EIO
+ goto err_out
+ }
+ }
+
+ file, _, errno = NewFile(fd, cName, rn)
+ if errno != 0 {
+ goto err_out
+ }
+
+ file.dirHandle = &DirHandle{
+ ds: ds,
+ dirIV: dirIV,
+ isRootDir: n.IsRoot(),
+ }
+
+ return file, fuseFlags, errno
+
+err_out:
+ if fd >= 0 {
+ syscall.Close(fd)
+ }
+ if fdDup >= 0 {
+ syscall.Close(fdDup)
+ }
+ if errno == 0 {
+ tlog.Warn.Printf("BUG: OpendirHandle: err_out called with errno == 0")
+ errno = syscall.EIO
+ }
+ return nil, 0, errno
+}
+
+type DirHandle struct {
+ // Content of gocryptfs.diriv. nil if plaintextnames is used.
+ dirIV []byte
+
+ isRootDir bool
+
+ // fs.loopbackDirStream with a private dup of the file descriptor
+ ds fs.FileHandle
+}
+
+var _ = (fs.FileReleasedirer)((*File)(nil))
+
+func (f *File) Releasedir(ctx context.Context, flags uint32) {
+ // Does its own locking
+ f.dirHandle.ds.(fs.FileReleasedirer).Releasedir(ctx, flags)
+ // Does its own locking
+ f.Release(ctx)
+}
+
+var _ = (fs.FileSeekdirer)((*File)(nil))
+
+func (f *File) Seekdir(ctx context.Context, off uint64) syscall.Errno {
+ return f.dirHandle.ds.(fs.FileSeekdirer).Seekdir(ctx, off)
+}
+
+var _ = (fs.FileFsyncdirer)((*File)(nil))
+
+func (f *File) Fsyncdir(ctx context.Context, flags uint32) syscall.Errno {
+ return f.dirHandle.ds.(fs.FileFsyncdirer).Fsyncdir(ctx, flags)
+}
+
+var _ = (fs.FileReaddirenter)((*File)(nil))
+
+// This function is symlink-safe through use of openBackingDir() and
+// ReadDirIVAt().
+func (f *File) Readdirent(ctx context.Context) (entry *fuse.DirEntry, errno syscall.Errno) {
+ f.fdLock.RLock()
+ defer f.fdLock.RUnlock()
+
+ for {
+ entry, errno = f.dirHandle.ds.(fs.FileReaddirenter).Readdirent(ctx)
+ if errno != 0 || entry == nil {
+ return
+ }
+
+ cName := entry.Name
+ if cName == "." || cName == ".." {
+ // We want these as-is
+ return
+ }
+ if f.dirHandle.isRootDir && cName == configfile.ConfDefaultName {
+ // silently ignore "gocryptfs.conf" in the top level dir
+ continue
+ }
+ if f.rootNode.args.PlaintextNames {
+ return
+ }
+ if !f.rootNode.args.DeterministicNames && cName == nametransform.DirIVFilename {
+ // silently ignore "gocryptfs.diriv" everywhere if dirIV is enabled
+ continue
+ }
+ // Handle long file name
+ isLong := nametransform.LongNameNone
+ if f.rootNode.args.LongNames {
+ isLong = nametransform.NameType(cName)
+ }
+ if isLong == nametransform.LongNameContent {
+ cNameLong, err := nametransform.ReadLongNameAt(f.intFd(), cName)
+ if err != nil {
+ tlog.Warn.Printf("Readdirent: incomplete entry %q: Could not read .name: %v",
+ cName, err)
+ f.rootNode.reportMitigatedCorruption(cName)
+ continue
+ }
+ cName = cNameLong
+ } else if isLong == nametransform.LongNameFilename {
+ // ignore "gocryptfs.longname.*.name"
+ continue
+ }
+ name, err := f.rootNode.nameTransform.DecryptName(cName, f.dirHandle.dirIV)
+ if err != nil {
+ tlog.Warn.Printf("Readdirent: could not decrypt entry %q: %v",
+ cName, err)
+ f.rootNode.reportMitigatedCorruption(cName)
+ continue
+ }
+ // Override the ciphertext name with the plaintext name but reuse the rest
+ // of the structure
+ entry.Name = name
+ return
+ }
+}
diff --git a/internal/fusefrontend/file_holes.go b/internal/fusefrontend/file_holes.go
index f35fa70..fc58898 100644
--- a/internal/fusefrontend/file_holes.go
+++ b/internal/fusefrontend/file_holes.go
@@ -21,12 +21,12 @@ func (f *File) writePadHole(targetOff int64) syscall.Errno {
tlog.Warn.Printf("checkAndPadHole: Fstat failed: %v", err)
return fs.ToErrno(err)
}
- plainSize := f.contentEnc.CipherSizeToPlainSize(uint64(fi.Size()))
+ plainSize := f.rootNode.contentEnc.CipherSizeToPlainSize(uint64(fi.Size()))
// Appending a single byte to the file (equivalent to writing to
// offset=plainSize) would write to "nextBlock".
- nextBlock := f.contentEnc.PlainOffToBlockNo(plainSize)
+ nextBlock := f.rootNode.contentEnc.PlainOffToBlockNo(plainSize)
// targetBlock is the block the user wants to write to.
- targetBlock := f.contentEnc.PlainOffToBlockNo(uint64(targetOff))
+ targetBlock := f.rootNode.contentEnc.PlainOffToBlockNo(uint64(targetOff))
// The write goes into an existing block or (if the last block was full)
// starts a new one directly after the last block. Nothing to do.
if targetBlock <= nextBlock {
@@ -45,12 +45,12 @@ func (f *File) writePadHole(targetOff int64) syscall.Errno {
// Zero-pad the file of size plainSize to the next block boundary. This is a no-op
// if the file is already block-aligned.
func (f *File) zeroPad(plainSize uint64) syscall.Errno {
- lastBlockLen := plainSize % f.contentEnc.PlainBS()
+ lastBlockLen := plainSize % f.rootNode.contentEnc.PlainBS()
if lastBlockLen == 0 {
// Already block-aligned
return 0
}
- missing := f.contentEnc.PlainBS() - lastBlockLen
+ missing := f.rootNode.contentEnc.PlainBS() - lastBlockLen
pad := make([]byte, missing)
tlog.Debug.Printf("zeroPad: Writing %d bytes\n", missing)
_, errno := f.doWrite(pad, int64(plainSize))
diff --git a/internal/fusefrontend/node.go b/internal/fusefrontend/node.go
index 687a386..95be48d 100644
--- a/internal/fusefrontend/node.go
+++ b/internal/fusefrontend/node.go
@@ -71,28 +71,54 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ch
func (n *Node) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) (errno syscall.Errno) {
// If the kernel gives us a file handle, use it.
if f != nil {
- return f.(fs.FileGetattrer).Getattr(ctx, out)
+ if fga, ok := f.(fs.FileGetattrer); ok {
+ return fga.Getattr(ctx, out)
+ }
}
+ rn := n.rootNode()
+ var st *syscall.Stat_t
+ var err error
dirfd, cName, errno := n.prepareAtSyscallMyself()
+ // Hack for deleted fifos. As OPEN on a fifo does not reach
+ // the filesystem, we have no fd to access it. To make "cat" and git's
+ // t9300-fast-import.sh happy, we fake it as best as we can.
+ // https://github.com/rfjakob/gocryptfs/issues/929
+ if errno == syscall.ENOENT && n.StableAttr().Mode == syscall.S_IFIFO {
+ out.Mode = syscall.S_IFIFO
+ out.Ino = n.StableAttr().Ino
+ // cat looks at this to determine the optimal io size. Seems to be always 4096 for fifos.
+ out.Blksize = 4096
+ // We don't know what the owner was. Set it to nobody which seems safer
+ // than leaving it at 0 (root).
+ out.Owner.Gid = 65534
+ out.Owner.Uid = 65534
+ // All the other fields stay 0. This is what cat sees (strace -v log):
+ //
+ // fstat(1, {st_dev=makedev(0, 0x4d), st_ino=3838227, st_mode=S_IFIFO|000, st_nlink=0,
+ // st_uid=65534, st_gid=65534, st_blksize=4096, st_blocks=0, st_size=0, st_atime=0,
+ // st_atime_nsec=0, st_mtime=0, st_mtime_nsec=0, st_ctime=0, st_ctime_nsec=0}) = 0
+ goto out
+ }
if errno != 0 {
return
}
+
defer syscall.Close(dirfd)
- st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW)
+ st, err = syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW)
if err != nil {
return fs.ToErrno(err)
}
// Fix inode number
- rn := n.rootNode()
rn.inoMap.TranslateStat(st)
out.Attr.FromStat(st)
// Translate ciphertext size in `out.Attr.Size` to plaintext size
n.translateSize(dirfd, cName, &out.Attr)
+out:
if rn.args.ForceOwner != nil {
out.Owner = *rn.args.ForceOwner
}
diff --git a/internal/fusefrontend/node_api_check.go b/internal/fusefrontend/node_api_check.go
index 0f60c74..37d4293 100644
--- a/internal/fusefrontend/node_api_check.go
+++ b/internal/fusefrontend/node_api_check.go
@@ -7,7 +7,6 @@ import (
// Check that we have implemented the fs.Node* interfaces
var _ = (fs.NodeGetattrer)((*Node)(nil))
var _ = (fs.NodeLookuper)((*Node)(nil))
-var _ = (fs.NodeReaddirer)((*Node)(nil))
var _ = (fs.NodeCreater)((*Node)(nil))
var _ = (fs.NodeMkdirer)((*Node)(nil))
var _ = (fs.NodeRmdirer)((*Node)(nil))
diff --git a/internal/fusefrontend/node_dir_ops.go b/internal/fusefrontend/node_dir_ops.go
index 97e4caa..97327ce 100644
--- a/internal/fusefrontend/node_dir_ops.go
+++ b/internal/fusefrontend/node_dir_ops.go
@@ -12,7 +12,6 @@ import (
"github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/fuse"
- "github.com/rfjakob/gocryptfs/v2/internal/configfile"
"github.com/rfjakob/gocryptfs/v2/internal/cryptocore"
"github.com/rfjakob/gocryptfs/v2/internal/nametransform"
"github.com/rfjakob/gocryptfs/v2/internal/syscallcompat"
@@ -137,12 +136,6 @@ func (n *Node) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.En
}
defer syscall.Close(fd)
- err = syscall.Fstat(fd, &st)
- if err != nil {
- tlog.Warn.Printf("Mkdir %q: Fstat failed: %v", cName, err)
- return nil, fs.ToErrno(err)
- }
-
// Fix permissions
if origMode != mode {
// Preserve SGID bit if it was set due to inheritance.
@@ -151,97 +144,17 @@ func (n *Node) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.En
if err != nil {
tlog.Warn.Printf("Mkdir %q: Fchmod %#o -> %#o failed: %v", cName, mode, origMode, err)
}
-
}
- // Create child node & return
- ch := n.newChild(ctx, &st, out)
- return ch, 0
-}
-
-// Readdir - FUSE call.
-//
-// This function is symlink-safe through use of openBackingDir() and
-// ReadDirIVAt().
-func (n *Node) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
- parentDirFd, cDirName, errno := n.prepareAtSyscallMyself()
- if errno != 0 {
- return nil, errno
- }
- defer syscall.Close(parentDirFd)
-
- // Read ciphertext directory
- fd, err := syscallcompat.Openat(parentDirFd, cDirName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
- if err != nil {
- return nil, fs.ToErrno(err)
- }
- defer syscall.Close(fd)
- cipherEntries, specialEntries, err := syscallcompat.GetdentsSpecial(fd)
+ err = syscall.Fstat(fd, &st)
if err != nil {
+ tlog.Warn.Printf("Mkdir %q: Fstat failed: %v", cName, err)
return nil, fs.ToErrno(err)
}
- // Get DirIV (stays nil if PlaintextNames is used)
- var cachedIV []byte
- rn := n.rootNode()
- if !rn.args.PlaintextNames {
- // Read the DirIV from disk
- cachedIV, err = rn.nameTransform.ReadDirIVAt(fd)
- if err != nil {
- tlog.Warn.Printf("OpenDir %q: could not read %s: %v", cDirName, nametransform.DirIVFilename, err)
- return nil, syscall.EIO
- }
- }
- // Decrypted directory entries
- var plain []fuse.DirEntry
- // Add "." and ".."
- plain = append(plain, specialEntries...)
- // Filter and decrypt filenames
- for i := range cipherEntries {
- cName := cipherEntries[i].Name
- if n.IsRoot() && cName == configfile.ConfDefaultName {
- // silently ignore "gocryptfs.conf" in the top level dir
- continue
- }
- if rn.args.PlaintextNames {
- plain = append(plain, cipherEntries[i])
- continue
- }
- if !rn.args.DeterministicNames && cName == nametransform.DirIVFilename {
- // silently ignore "gocryptfs.diriv" everywhere if dirIV is enabled
- continue
- }
- // Handle long file name
- isLong := nametransform.LongNameNone
- if rn.args.LongNames {
- isLong = nametransform.NameType(cName)
- }
- if isLong == nametransform.LongNameContent {
- cNameLong, err := nametransform.ReadLongNameAt(fd, cName)
- if err != nil {
- tlog.Warn.Printf("OpenDir %q: invalid entry %q: Could not read .name: %v",
- cDirName, cName, err)
- rn.reportMitigatedCorruption(cName)
- continue
- }
- cName = cNameLong
- } else if isLong == nametransform.LongNameFilename {
- // ignore "gocryptfs.longname.*.name"
- continue
- }
- name, err := rn.nameTransform.DecryptName(cName, cachedIV)
- if err != nil {
- tlog.Warn.Printf("OpenDir %q: invalid entry %q: %v",
- cDirName, cName, err)
- rn.reportMitigatedCorruption(cName)
- continue
- }
- // Override the ciphertext name with the plaintext name but reuse the rest
- // of the structure
- cipherEntries[i].Name = name
- plain = append(plain, cipherEntries[i])
- }
- return fs.NewListDirStream(plain), 0
+ // Create child node & return
+ ch := n.newChild(ctx, &st, out)
+ return ch, 0
}
// Rmdir - FUSE call.
diff --git a/internal/fusefrontend/node_helpers.go b/internal/fusefrontend/node_helpers.go
index f5dfeb6..e8fca80 100644
--- a/internal/fusefrontend/node_helpers.go
+++ b/internal/fusefrontend/node_helpers.go
@@ -2,7 +2,6 @@ package fusefrontend
import (
"context"
- "sync/atomic"
"syscall"
"github.com/hanwen/go-fuse/v2/fs"
@@ -91,12 +90,12 @@ func (n *Node) newChild(ctx context.Context, st *syscall.Stat_t, out *fuse.Entry
if rn.args.SharedStorage || rn.quirks&syscallcompat.QuirkDuplicateIno1 != 0 {
// Make each directory entry a unique node by using a unique generation
// value - see the comment at RootNode.gen for details.
- gen = atomic.AddUint64(&rn.gen, 1)
+ gen = rn.gen.Add(1)
}
// Create child node
id := fs.StableAttr{
- Mode: uint32(st.Mode),
+ Mode: uint32(st.Mode), // go-fuse masks this with syscall.S_IFMT
Gen: gen,
Ino: st.Ino,
}
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_prepare_syscall.go b/internal/fusefrontend/node_prepare_syscall.go
index 2a4d6ab..9021350 100644
--- a/internal/fusefrontend/node_prepare_syscall.go
+++ b/internal/fusefrontend/node_prepare_syscall.go
@@ -1,7 +1,6 @@
package fusefrontend
import (
- "sync/atomic"
"syscall"
"github.com/rfjakob/gocryptfs/v2/internal/tlog"
@@ -24,7 +23,7 @@ func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno sy
// All filesystem operations go through here, so this is a good place
// to reset the idle marker.
- atomic.StoreUint32(&rn.IsIdle, 0)
+ rn.IsIdle.Store(false)
if n.IsRoot() && rn.isFiltered(child) {
return -1, "", syscall.EPERM
diff --git a/internal/fusefrontend/node_xattr.go b/internal/fusefrontend/node_xattr.go
index 44bc502..1470a2a 100644
--- a/internal/fusefrontend/node_xattr.go
+++ b/internal/fusefrontend/node_xattr.go
@@ -12,9 +12,6 @@ import (
"github.com/rfjakob/gocryptfs/v2/internal/tlog"
)
-// -1 as uint32
-const minus1 = ^uint32(0)
-
// We store encrypted xattrs under this prefix plus the base64-encoded
// encrypted original name.
var xattrStorePrefix = "user.gocryptfs."
@@ -35,6 +32,10 @@ func isAcl(attr string) bool {
// This function is symlink-safe through Fgetxattr.
func (n *Node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) {
rn := n.rootNode()
+ // If -noxattr is enabled, return ENOATTR for all getxattr calls
+ if rn.args.NoXattr {
+ return 0, noSuchAttributeError
+ }
// If we are not mounted with -suid, reading the capability xattr does not
// make a lot of sense, so reject the request and gain a massive speedup.
// See https://github.com/rfjakob/gocryptfs/issues/515 .
@@ -50,13 +51,13 @@ func (n *Node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32,
var errno syscall.Errno
data, errno = n.getXAttr(attr)
if errno != 0 {
- return minus1, errno
+ return 0, errno
}
} else {
// encrypted user xattr
cAttr, err := rn.encryptXattrName(attr)
if err != nil {
- return minus1, syscall.EIO
+ return 0, syscall.EIO
}
cData, errno := n.getXAttr(cAttr)
if errno != 0 {
@@ -65,15 +66,11 @@ func (n *Node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32,
data, err = rn.decryptXattrValue(cData)
if err != nil {
tlog.Warn.Printf("GetXAttr: %v", err)
- return minus1, syscall.EIO
+ return 0, syscall.EIO
}
}
- // Caller passes size zero to find out how large their buffer should be
- if len(dest) == 0 {
- return uint32(len(data)), 0
- }
if len(dest) < len(data) {
- return minus1, syscall.ERANGE
+ return uint32(len(data)), syscall.ERANGE
}
l := copy(dest, data)
return uint32(l), 0
@@ -84,6 +81,10 @@ func (n *Node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32,
// This function is symlink-safe through Fsetxattr.
func (n *Node) Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno {
rn := n.rootNode()
+ // If -noxattr is enabled, fail all setxattr calls
+ if rn.args.NoXattr {
+ return syscall.EOPNOTSUPP
+ }
flags = uint32(filterXattrSetFlags(int(flags)))
// ACLs are passed through without encryption
@@ -109,6 +110,10 @@ func (n *Node) Setxattr(ctx context.Context, attr string, data []byte, flags uin
// This function is symlink-safe through Fremovexattr.
func (n *Node) Removexattr(ctx context.Context, attr string) syscall.Errno {
rn := n.rootNode()
+ // If -noxattr is enabled, fail all removexattr calls
+ if rn.args.NoXattr {
+ return syscall.EOPNOTSUPP
+ }
// ACLs are passed through without encryption
if isAcl(attr) {
@@ -126,11 +131,15 @@ func (n *Node) Removexattr(ctx context.Context, attr string) syscall.Errno {
//
// This function is symlink-safe through Flistxattr.
func (n *Node) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) {
+ rn := n.rootNode()
+ // If -noxattr is enabled, return zero results for listxattr
+ if rn.args.NoXattr {
+ return 0, 0
+ }
cNames, errno := n.listXAttr()
if errno != 0 {
return 0, errno
}
- rn := n.rootNode()
var buf bytes.Buffer
for _, curName := range cNames {
// ACLs are passed through without encryption
@@ -155,12 +164,8 @@ func (n *Node) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errn
}
buf.WriteString(name + "\000")
}
- // Caller passes size zero to find out how large their buffer should be
- if len(dest) == 0 {
- return uint32(buf.Len()), 0
- }
if buf.Len() > len(dest) {
- return minus1, syscall.ERANGE
+ return uint32(buf.Len()), syscall.ERANGE
}
return uint32(copy(dest, buf.Bytes())), 0
}
diff --git a/internal/fusefrontend/node_xattr_darwin.go b/internal/fusefrontend/node_xattr_darwin.go
index a539847..1d25f3d 100644
--- a/internal/fusefrontend/node_xattr_darwin.go
+++ b/internal/fusefrontend/node_xattr_darwin.go
@@ -11,6 +11,9 @@ import (
"github.com/rfjakob/gocryptfs/v2/internal/syscallcompat"
)
+// On Darwin, ENOATTR is returned when an attribute is not found.
+const noSuchAttributeError = syscall.ENOATTR
+
// On Darwin we have to unset XATTR_NOSECURITY 0x0008
func filterXattrSetFlags(flags int) int {
// See https://opensource.apple.com/source/xnu/xnu-1504.15.3/bsd/sys/xattr.h.auto.html
@@ -26,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)
}
@@ -49,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)
@@ -71,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)
@@ -93,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/node_xattr_linux.go b/internal/fusefrontend/node_xattr_linux.go
index 4a356a5..9964212 100644
--- a/internal/fusefrontend/node_xattr_linux.go
+++ b/internal/fusefrontend/node_xattr_linux.go
@@ -12,6 +12,9 @@ import (
"github.com/rfjakob/gocryptfs/v2/internal/syscallcompat"
)
+// On Linux, ENODATA is returned when an attribute is not found.
+const noSuchAttributeError = syscall.ENODATA
+
func filterXattrSetFlags(flags int) int {
return flags
}
diff --git a/internal/fusefrontend/prepare_syscall_test.go b/internal/fusefrontend/prepare_syscall_test.go
index acddaf3..e2c7d08 100644
--- a/internal/fusefrontend/prepare_syscall_test.go
+++ b/internal/fusefrontend/prepare_syscall_test.go
@@ -1,6 +1,7 @@
package fusefrontend
import (
+ "context"
"strings"
"syscall"
"testing"
@@ -22,13 +23,13 @@ func TestPrepareAtSyscall(t *testing.T) {
rn := newTestFS(args)
out := &fuse.EntryOut{}
- child, errno := rn.Mkdir(nil, "dir1", 0700, out)
+ child, errno := rn.Mkdir(context.TODO(), "dir1", 0700, out)
if errno != 0 {
t.Fatal(errno)
}
rn.AddChild("dir1", child, false)
dir1 := toNode(child.Operations())
- _, errno = dir1.Mkdir(nil, "dir2", 0700, out)
+ _, errno = dir1.Mkdir(context.TODO(), "dir2", 0700, out)
if errno != 0 {
t.Fatal(errno)
}
@@ -43,7 +44,7 @@ func TestPrepareAtSyscall(t *testing.T) {
syscall.Close(dirfd)
// Again, but populate the cache for "" by looking up a non-existing file
- rn.Lookup(nil, "xyz1234", &fuse.EntryOut{})
+ rn.Lookup(context.TODO(), "xyz1234", &fuse.EntryOut{})
dirfd, cName, errno = rn.prepareAtSyscallMyself()
if errno != 0 {
t.Fatal(errno)
@@ -89,7 +90,7 @@ func TestPrepareAtSyscall(t *testing.T) {
syscall.Close(dirfd)
n255 := strings.Repeat("n", 255)
- dir1.Mkdir(nil, n255, 0700, out)
+ dir1.Mkdir(context.TODO(), n255, 0700, out)
dirfd, cName, errno = dir1.prepareAtSyscall(n255)
if errno != 0 {
t.Fatal(errno)
@@ -116,13 +117,13 @@ func TestPrepareAtSyscallPlaintextnames(t *testing.T) {
rn := newTestFS(args)
out := &fuse.EntryOut{}
- child, errno := rn.Mkdir(nil, "dir1", 0700, out)
+ child, errno := rn.Mkdir(context.TODO(), "dir1", 0700, out)
if errno != 0 {
t.Fatal(errno)
}
rn.AddChild("dir1", child, false)
dir1 := toNode(child.Operations())
- _, errno = dir1.Mkdir(nil, "dir2", 0700, out)
+ _, errno = dir1.Mkdir(context.TODO(), "dir2", 0700, out)
if errno != 0 {
t.Fatal(errno)
}
diff --git a/internal/fusefrontend/root_node.go b/internal/fusefrontend/root_node.go
index ac814ad..38d070d 100644
--- a/internal/fusefrontend/root_node.go
+++ b/internal/fusefrontend/root_node.go
@@ -1,9 +1,9 @@
package fusefrontend
import (
- "os"
"strings"
"sync"
+ "sync/atomic"
"syscall"
"time"
@@ -44,7 +44,7 @@ type RootNode struct {
// (uint32 so that it can be reset with CompareAndSwapUint32).
// When -idle was used when mounting, idleMonitor() sets it to 1
// periodically.
- IsIdle uint32
+ IsIdle atomic.Bool
// dirCache caches directory fds
dirCache dirCache
// inoMap translates inode numbers from different devices to unique inode
@@ -55,7 +55,7 @@ type RootNode struct {
// This makes each directory entry unique (even hard links),
// makes go-fuse hand out separate FUSE Node IDs for each, and prevents
// bizarre problems when inode numbers are reused behind our back.
- gen uint64
+ gen atomic.Uint64
// quirks is a bitmap that enables workaround for quirks in the filesystem
// backing the cipherdir
quirks uint64
@@ -90,6 +90,12 @@ func NewRootNode(args Args, c *contentenc.ContentEnc, n *nametransform.NameTrans
dirCache: dirCache{ivLen: ivLen},
quirks: syscallcompat.DetectQuirks(args.Cipherdir),
}
+ // Suppress the message if the user has already specified -noprealloc
+ if rn.quirks&syscallcompat.QuirkBtrfsBrokenFalloc != 0 && !args.NoPrealloc {
+ 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)
rn.rootIno = st.Ino
@@ -103,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).
diff --git a/internal/fusefrontend/xattr_unit_test.go b/internal/fusefrontend/xattr_unit_test.go
index 86c87a7..7d8e32e 100644
--- a/internal/fusefrontend/xattr_unit_test.go
+++ b/internal/fusefrontend/xattr_unit_test.go
@@ -21,10 +21,10 @@ func newTestFS(args Args) *RootNode {
cEnc := contentenc.New(cCore, contentenc.DefaultBS)
n := nametransform.New(cCore.EMECipher, true, 0, true, nil, false)
rn := NewRootNode(args, cEnc, n)
- oneSec := time.Second
+ oneSecond := time.Second
options := &fs.Options{
- EntryTimeout: &oneSec,
- AttrTimeout: &oneSec,
+ EntryTimeout: &oneSecond,
+ AttrTimeout: &oneSecond,
}
fs.NewNodeFS(rn, options)
return rn