diff options
| author | Jakob Unterwurzacher | 2020-06-21 13:46:08 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2020-06-21 13:46:08 +0200 | 
| commit | 192a29075a7a567931959c2b4c8e4a9513742eee (patch) | |
| tree | 4cc2eb036fe60a1e66c6f704a69eadf7fd664d87 /internal/fusefrontend | |
| parent | f6ded09e36a679695354f4b9bc74242ef399be09 (diff) | |
v2api: implement Mkdir
Diffstat (limited to 'internal/fusefrontend')
| -rw-r--r-- | internal/fusefrontend/fs_dir.go | 4 | ||||
| -rw-r--r-- | internal/fusefrontend/node_api_check.go | 11 | ||||
| -rw-r--r-- | internal/fusefrontend/node_dir_ops.go | 121 | ||||
| -rw-r--r-- | internal/fusefrontend/root_node.go | 5 | 
4 files changed, 139 insertions, 2 deletions
| diff --git a/internal/fusefrontend/fs_dir.go b/internal/fusefrontend/fs_dir.go index 8481d56..0d8adae 100644 --- a/internal/fusefrontend/fs_dir.go +++ b/internal/fusefrontend/fs_dir.go @@ -30,7 +30,7 @@ func (fs *FS) mkdirWithIv(dirfd int, cName string, mode uint32, context *fuse.Co  	// from seeing it.  	fs.dirIVLock.Lock()  	defer fs.dirIVLock.Unlock() -	err := syscallcompat.MkdiratUser(dirfd, cName, mode, context) +	err := syscallcompat.MkdiratUser(dirfd, cName, mode, &context.Caller)  	if err != nil {  		return err  	} @@ -67,7 +67,7 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu  		context = nil  	}  	if fs.args.PlaintextNames { -		err = syscallcompat.MkdiratUser(dirfd, cName, mode, context) +		err = syscallcompat.MkdiratUser(dirfd, cName, mode, &context.Caller)  		return fuse.ToStatus(err)  	} diff --git a/internal/fusefrontend/node_api_check.go b/internal/fusefrontend/node_api_check.go new file mode 100644 index 0000000..e6d1681 --- /dev/null +++ b/internal/fusefrontend/node_api_check.go @@ -0,0 +1,11 @@ +package fusefrontend + +import ( +	"github.com/hanwen/go-fuse/v2/fs" +) + +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)) 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() diff --git a/internal/fusefrontend/root_node.go b/internal/fusefrontend/root_node.go index c84ac93..be82851 100644 --- a/internal/fusefrontend/root_node.go +++ b/internal/fusefrontend/root_node.go @@ -2,6 +2,7 @@ package fusefrontend  import (  	"os" +	"sync"  	"sync/atomic"  	"syscall"  	"time" @@ -19,6 +20,10 @@ type RootNode struct {  	Node  	// args stores configuration arguments  	args Args +	// dirIVLock: Lock()ed if any "gocryptfs.diriv" file is modified +	// Readers must RLock() it to prevent them from seeing intermediate +	// states +	dirIVLock sync.RWMutex  	// Filename encryption helper  	nameTransform nametransform.NameTransformer  	// Content encryption helper | 
