aboutsummaryrefslogtreecommitdiff
path: root/internal/fusefrontend/node_dir_ops.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/fusefrontend/node_dir_ops.go')
-rw-r--r--internal/fusefrontend/node_dir_ops.go121
1 files changed, 121 insertions, 0 deletions
diff --git a/internal/fusefrontend/node_dir_ops.go b/internal/fusefrontend/node_dir_ops.go
index 38d0940..0789c92 100644
--- a/internal/fusefrontend/node_dir_ops.go
+++ b/internal/fusefrontend/node_dir_ops.go
@@ -5,6 +5,8 @@ import (
"path/filepath"
"syscall"
+ "golang.org/x/sys/unix"
+
"github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/fuse"
@@ -14,6 +16,125 @@ import (
"github.com/rfjakob/gocryptfs/internal/tlog"
)
+// mkdirWithIv - create a new directory and corresponding diriv file. dirfd
+// should be a handle to the parent directory, cName is the name of the new
+// directory and mode specifies the access permissions to use.
+func (n *Node) mkdirWithIv(dirfd int, cName string, mode uint32, caller *fuse.Caller) error {
+ rn := n.rootNode()
+ // Between the creation of the directory and the creation of gocryptfs.diriv
+ // the directory is inconsistent. Take the lock to prevent other readers
+ // from seeing it.
+ rn.dirIVLock.Lock()
+ defer rn.dirIVLock.Unlock()
+ err := syscallcompat.MkdiratUser(dirfd, cName, mode, caller)
+ if err != nil {
+ return err
+ }
+ dirfd2, err := syscallcompat.Openat(dirfd, cName, syscall.O_DIRECTORY|syscall.O_NOFOLLOW|syscallcompat.O_PATH, 0)
+ if err == nil {
+ // Create gocryptfs.diriv
+ err = nametransform.WriteDirIVAt(dirfd2)
+ syscall.Close(dirfd2)
+ }
+ if err != nil {
+ // Delete inconsistent directory (missing gocryptfs.diriv!)
+ err2 := syscallcompat.Unlinkat(dirfd, cName, unix.AT_REMOVEDIR)
+ if err2 != nil {
+ tlog.Warn.Printf("mkdirWithIv: rollback failed: %v", err2)
+ }
+ }
+ return err
+}
+
+// Mkdir - FUSE call. Create a directory at "newPath" with permissions "mode".
+//
+// Symlink-safe through use of Mkdirat().
+func (n *Node) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) {
+ rn := n.rootNode()
+ newPath := filepath.Join(n.path(), name)
+ if rn.isFiltered(newPath) {
+ return nil, syscall.EPERM
+ }
+ dirfd, cName, err := rn.openBackingDir(newPath)
+ if err != nil {
+ return nil, fs.ToErrno(err)
+ }
+ defer syscall.Close(dirfd)
+ var caller *fuse.Caller
+ if rn.args.PreserveOwner {
+ caller, _ = fuse.FromContext(ctx)
+ }
+ if rn.args.PlaintextNames {
+ err = syscallcompat.MkdiratUser(dirfd, cName, mode, caller)
+ return nil, fs.ToErrno(err)
+ }
+
+ // We need write and execute permissions to create gocryptfs.diriv.
+ // Also, we need read permissions to open the directory (to avoid
+ // race-conditions between getting and setting the mode).
+ origMode := mode
+ mode = mode | 0700
+
+ // Handle long file name
+ if nametransform.IsLongContent(cName) {
+ // Create ".name"
+ err = rn.nameTransform.WriteLongNameAt(dirfd, cName, newPath)
+ if err != nil {
+ return nil, fs.ToErrno(err)
+ }
+
+ // Create directory
+ err = rn.mkdirWithIv(dirfd, cName, mode, caller)
+ if err != nil {
+ nametransform.DeleteLongNameAt(dirfd, cName)
+ return nil, fs.ToErrno(err)
+ }
+ } else {
+ err = rn.mkdirWithIv(dirfd, cName, mode, caller)
+ if err != nil {
+ return nil, fs.ToErrno(err)
+ }
+ }
+
+ fd, err := syscallcompat.Openat(dirfd, cName,
+ syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
+ if err != nil {
+ tlog.Warn.Printf("Mkdir %q: Openat failed: %v", cName, err)
+ return nil, fs.ToErrno(err)
+ }
+ defer syscall.Close(fd)
+
+ // Get unique inode number
+ var st syscall.Stat_t
+ err = syscall.Fstat(fd, &st)
+ if err != nil {
+ tlog.Warn.Printf("Mkdir %q: Fstat failed: %v", cName, err)
+ return nil, fs.ToErrno(err)
+ }
+ rn.inoMap.TranslateStat(&st)
+ out.Attr.FromStat(&st)
+ // Create child node
+ id := fs.StableAttr{
+ Mode: uint32(st.Mode),
+ Gen: 1,
+ Ino: st.Ino,
+ }
+ node := &Node{}
+ ch := n.NewInode(ctx, node, id)
+
+ // Set mode
+ if origMode != mode {
+ // Preserve SGID bit if it was set due to inheritance.
+ origMode = uint32(st.Mode&^0777) | origMode
+ err = syscall.Fchmod(fd, origMode)
+ if err != nil {
+ tlog.Warn.Printf("Mkdir %q: Fchmod %#o -> %#o failed: %v", cName, mode, origMode, err)
+ }
+ }
+
+ return ch, 0
+}
+
func (n *Node) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
rn := n.rootNode()
p := n.path()