From b971c75e67c26b126a64ab8b00f416b3b573f194 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sat, 11 Jul 2020 19:23:04 +0200 Subject: [PATCH] v2api: implement Mknod --- internal/fusefrontend/node.go | 76 +++++++++++++++++++++---- internal/fusefrontend/node_api_check.go | 2 +- internal/fusefrontend/node_dir_ops.go | 10 +--- internal/syscallcompat/sys_linux.go | 14 +++++ 4 files changed, 81 insertions(+), 21 deletions(-) diff --git a/internal/fusefrontend/node.go b/internal/fusefrontend/node.go index bdc30b3..c4a80dd 100644 --- a/internal/fusefrontend/node.go +++ b/internal/fusefrontend/node.go @@ -102,6 +102,23 @@ func (n *Node) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) return 0 } +// newChild attaches a new child inode to n. +// The passed-in `st` will be modified to get a unique inode number. +func (n *Node) newChild(ctx context.Context, st *syscall.Stat_t, out *fuse.EntryOut) *fs.Inode { + // Get unique inode number + rn := n.rootNode() + 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{} + return n.NewInode(ctx, node, id) +} + // Create - FUSE call. Creates a new file. // // Symlink-safe through the use of Openat(). @@ -153,17 +170,7 @@ func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint3 errno = fs.ToErrno(err) return } - // Get unique inode number - 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) + ch := n.newChild(ctx, &st, out) f := os.NewFile(uintptr(fd), cName) return ch, NewFile2(f, rn, &st), 0, 0 @@ -296,3 +303,50 @@ func (n *Node) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno { out.FromStatfsT(&st) return 0 } + +// Mknod - FUSE call. Create a device file. +// +// Symlink-safe through use of Mknodat(). +func (n *Node) Mknod(ctx context.Context, name string, mode, rdev uint32, out *fuse.EntryOut) (inode *fs.Inode, errno syscall.Errno) { + dirfd, cName, errno := n.prepareAtSyscall("") + if errno != 0 { + return + } + defer syscall.Close(dirfd) + + // Make sure context is nil if we don't want to preserve the owner + rn := n.rootNode() + if !rn.args.PreserveOwner { + ctx = nil + } + + // Create ".name" file to store long file name (except in PlaintextNames mode) + var err error + if !rn.args.PlaintextNames && nametransform.IsLongContent(cName) { + err := rn.nameTransform.WriteLongNameAt(dirfd, cName, name) + if err != nil { + errno = fs.ToErrno(err) + return + } + // Create "gocryptfs.longfile." device node + err = syscallcompat.MknodatUserCtx(dirfd, cName, mode, int(rdev), ctx) + if err != nil { + nametransform.DeleteLongNameAt(dirfd, cName) + } + } else { + // Create regular device node + err = syscallcompat.MknodatUserCtx(dirfd, cName, mode, int(rdev), ctx) + } + if err != nil { + errno = fs.ToErrno(err) + return + } + + st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW) + if err != nil { + errno = fs.ToErrno(err) + return + } + inode = n.newChild(ctx, st, out) + return inode, 0 +} diff --git a/internal/fusefrontend/node_api_check.go b/internal/fusefrontend/node_api_check.go index 384badc..d99564f 100644 --- a/internal/fusefrontend/node_api_check.go +++ b/internal/fusefrontend/node_api_check.go @@ -17,6 +17,7 @@ var _ = (fs.NodeOpener)((*Node)(nil)) var _ = (fs.NodeOpendirer)((*Node)(nil)) var _ = (fs.NodeSetattrer)((*Node)(nil)) var _ = (fs.NodeStatfser)((*Node)(nil)) +var _ = (fs.NodeMknoder)((*Node)(nil)) /* TODO var _ = (fs.NodeGetxattrer)((*Node)(nil)) @@ -24,7 +25,6 @@ var _ = (fs.NodeSetxattrer)((*Node)(nil)) var _ = (fs.NodeRemovexattrer)((*Node)(nil)) var _ = (fs.NodeListxattrer)((*Node)(nil)) var _ = (fs.NodeCopyFileRanger)((*Node)(nil)) -var _ = (fs.NodeMknoder)((*Node)(nil)) var _ = (fs.NodeLinker)((*Node)(nil)) var _ = (fs.NodeSymlinker)((*Node)(nil)) var _ = (fs.NodeRenamer)((*Node)(nil)) diff --git a/internal/fusefrontend/node_dir_ops.go b/internal/fusefrontend/node_dir_ops.go index 32e5e86..df8f060 100644 --- a/internal/fusefrontend/node_dir_ops.go +++ b/internal/fusefrontend/node_dir_ops.go @@ -115,16 +115,8 @@ func (n *Node) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.En 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) + ch := n.newChild(ctx, &st, out) // Set mode if origMode != mode { diff --git a/internal/syscallcompat/sys_linux.go b/internal/syscallcompat/sys_linux.go index c82480e..5a23084 100644 --- a/internal/syscallcompat/sys_linux.go +++ b/internal/syscallcompat/sys_linux.go @@ -136,6 +136,20 @@ func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) { return syscall.Mknodat(dirfd, path, mode, dev) } +// MknodatUserCtx is a tries to extract a fuse.Context from the generic ctx and +// calls OpenatUser. +func MknodatUserCtx(dirfd int, path string, mode uint32, dev int, ctx context.Context) (err error) { + var ctx2 *fuse.Context + if ctx != nil { + if caller, ok := fuse.FromContext(ctx); ok { + ctx2 = &fuse.Context{ + Caller: *caller, + } + } + } + return MknodatUser(dirfd, path, mode, dev, ctx2) +} + // MknodatUser runs the Mknodat syscall in the context of a different user. func MknodatUser(dirfd int, path string, mode uint32, dev int, context *fuse.Context) (err error) { if context != nil {