v2api: implement Mkdir
This commit is contained in:
parent
f6ded09e36
commit
192a29075a
@ -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)
|
||||
}
|
||||
|
||||
|
11
internal/fusefrontend/node_api_check.go
Normal file
11
internal/fusefrontend/node_api_check.go
Normal file
@ -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))
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -233,24 +233,24 @@ func SymlinkatUser(oldpath string, newdirfd int, newpath string, context *fuse.C
|
||||
}
|
||||
|
||||
// MkdiratUser runs the Mkdirat syscall in the context of a different user.
|
||||
func MkdiratUser(dirfd int, path string, mode uint32, context *fuse.Context) (err error) {
|
||||
if context != nil {
|
||||
func MkdiratUser(dirfd int, path string, mode uint32, caller *fuse.Caller) (err error) {
|
||||
if caller != nil {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
err = syscall.Setgroups(getSupplementaryGroups(context.Pid))
|
||||
err = syscall.Setgroups(getSupplementaryGroups(caller.Pid))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.Setgroups(nil)
|
||||
|
||||
err = syscall.Setregid(-1, int(context.Owner.Gid))
|
||||
err = syscall.Setregid(-1, int(caller.Gid))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.Setregid(-1, 0)
|
||||
|
||||
err = syscall.Setreuid(-1, int(context.Owner.Uid))
|
||||
err = syscall.Setreuid(-1, int(caller.Uid))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user