//go:build linux || (darwin && amd64) package fuse import ( "context" "os" "syscall" fusefs "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" "gitlink.org.cn/cloudream/common/pkgs/logger" ) type DirNode struct { NodeBase dir FsDir } func newDirNode(fs *Fuse, dir FsDir) *DirNode { return &DirNode{NodeBase: NodeBase{fs: fs}, dir: dir} } func (n *DirNode) Getattr(ctx context.Context, f fusefs.FileHandle, out *fuse.AttrOut) syscall.Errno { logger.Tracef("DirNode.Getattr: %v", n.dir.Name()) n.fs.fillAttrOut(n.dir, out) return 0 } // Setattr sets attributes for an Inode. func (n *DirNode) Setattr(ctx context.Context, f fusefs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) (errno syscall.Errno) { logger.Tracef("DirNode.Setattr: %v", n.dir.Name()) n.fs.fillAttrOut(n.dir, out) _, ok := in.GetSize() if ok { return syscall.ENOSYS } modTime, ok := in.GetMTime() if ok { err := n.dir.SetModTime(modTime) if err != nil { return translateError(err) } out.Mtime = uint64(modTime.Unix()) out.Mtimensec = uint32(modTime.Nanosecond()) } return 0 } var _ = (fusefs.NodeSetattrer)((*DirNode)(nil)) // accessModeMask masks off the read modes from the flags const accessModeMask = (os.O_RDONLY | os.O_WRONLY | os.O_RDWR) // Open opens an Inode (of regular file type) for reading. It // is optional but recommended to return a FileHandle. func (n *DirNode) Open(ctx context.Context, flags uint32) (fh fusefs.FileHandle, fuseFlags uint32, errno syscall.Errno) { log := logger.WithField("F", "DirNode.Open") log.Tracef("args: %v, %#o", n.dir.Name(), flags) rdwrMode := int(flags) & accessModeMask if rdwrMode != os.O_RDONLY { return nil, 0, syscall.EPERM } reader, err := n.dir.ReadChildren() if err != nil { log.Warnf("read children: %v", err) return nil, 0, translateError(err) } return newDirHandle(n.dir, reader), 0, 0 } var _ = (fusefs.NodeOpener)((*DirNode)(nil)) func (n *DirNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (inode *fusefs.Inode, errno syscall.Errno) { log := logger.WithField("F", "DirNode.Lookup") log.Tracef("args: %v, %v", n.dir.Name(), name) child, err := n.dir.Child(ctx, name) if err != nil { log.Warnf("child: %v", err) return nil, translateError(err) } switch child := child.(type) { case FsDir: node := newDirNode(n.fs, child) n.fs.fillEntryOut(child, out) return n.NewInode(ctx, node, fusefs.StableAttr{ Mode: out.Attr.Mode, }), 0 case FsFile: node := newFileNode(n.fs, child) n.fs.fillEntryOut(child, out) return n.NewInode(ctx, node, fusefs.StableAttr{ Mode: out.Attr.Mode, }), 0 default: return nil, syscall.EINVAL } } var _ = (fusefs.NodeLookuper)((*DirNode)(nil)) func (n *DirNode) Opendir(ctx context.Context) syscall.Errno { return 0 } var _ = (fusefs.NodeOpendirer)((*DirNode)(nil)) type dirStream struct { reader DirReader fs *Fuse } func (s *dirStream) HasNext() bool { return s.reader.HasNext() } func (s *dirStream) Next() (fuse.DirEntry, syscall.Errno) { log := logger.WithField("F", "dirStream.Next") entries, err := s.reader.Next(1) if err != nil { log.Warnf("next: %v", err) return fuse.DirEntry{}, translateError(err) } entry := entries[0] return fuse.DirEntry{ Name: entry.Name(), Mode: s.fs.getMode(entry), }, 0 } func (s *dirStream) Close() { s.reader.Close() } func (n *DirNode) Readdir(ctx context.Context) (ds fusefs.DirStream, errno syscall.Errno) { log := logger.WithField("F", "DirNode.Readdir") reader, err := n.dir.ReadChildren() if err != nil { log.Warnf("read children: %v", err) return nil, translateError(err) } return &dirStream{reader: reader, fs: n.fs}, 0 } var _ = (fusefs.NodeReaddirer)((*DirNode)(nil)) func (n *DirNode) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (inode *fusefs.Inode, errno syscall.Errno) { log := logger.WithField("F", "DirNode.Mkdir") log.Tracef("args: %v, %v, %#o", n.dir.Name(), name, mode) newDir, err := n.dir.NewDir(ctx, name) if err != nil { log.Warnf("new dir: %v", err) return nil, translateError(err) } node := newDirNode(n.fs, newDir) n.fs.fillEntryOut(newDir, out) return n.NewInode(ctx, node, fusefs.StableAttr{ Mode: out.Attr.Mode, }), 0 } var _ = (fusefs.NodeMkdirer)((*DirNode)(nil)) func (n *DirNode) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (node *fusefs.Inode, fh fusefs.FileHandle, fuseFlags uint32, errno syscall.Errno) { log := logger.WithField("F", "DirNode.Create") logger.Tracef("args: %v, %v, %#x, %#o", n.dir.Name(), name, flags, mode) hd, flags, err := n.dir.NewFile(ctx, name, flags) if err != nil { log.Warnf("new file: %v", err) return nil, nil, 0, translateError(err) } n.fs.fillEntryOut(hd.Entry(), out) fileNode := newFileNode(n.fs, hd.Entry()) return n.NewInode(ctx, fileNode, fusefs.StableAttr{ Mode: out.Attr.Mode, }), newFileHandle(hd), 0, 0 } var _ = (fusefs.NodeCreater)((*DirNode)(nil)) // Unlink should remove a child from this directory. If the // return status is OK, the Inode is removed as child in the // FS tree automatically. Default is to return EROFS. func (n *DirNode) Unlink(ctx context.Context, name string) (errno syscall.Errno) { log := logger.WithField("F", "DirNode.Unlink") log.Tracef("args: %v, %v", n.dir.Name(), name) err := n.dir.RemoveChild(ctx, name) if err != nil { log.Warnf("remove child: %v", err) return translateError(err) } return 0 } var _ = (fusefs.NodeUnlinker)((*DirNode)(nil)) // Rmdir is like Unlink but for directories. // Default is to return EROFS. func (n *DirNode) Rmdir(ctx context.Context, name string) (errno syscall.Errno) { log := logger.WithField("F", "DirNode.Rmdir") log.Tracef("args: %v, %v", n.dir.Name(), name) err := n.dir.RemoveChild(ctx, name) if err != nil { log.Warnf("remove child: %v", err) return translateError(err) } return 0 } var _ = (fusefs.NodeRmdirer)((*DirNode)(nil)) func (n *DirNode) Rename(ctx context.Context, oldName string, newParent fusefs.InodeEmbedder, newName string, flags uint32) (errno syscall.Errno) { log := logger.WithField("F", "DirNode.Rename") log.Tracef("args: %v/%v->%v, %#o", n.dir.Name(), oldName, newName, flags) newParentNode, ok := newParent.(*DirNode) if !ok { return syscall.ENOTDIR } err := n.dir.MoveChild(ctx, oldName, newName, newParentNode.dir) if err != nil { log.Warnf("move child: %v", err) return translateError(err) } return 0 } var _ = (fusefs.NodeRenamer)((*DirNode)(nil))