github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/cmd/mount2/node.go (about)

     1  // +build linux darwin,amd64
     2  
     3  package mount2
     4  
     5  import (
     6  	"context"
     7  	"os"
     8  	"path"
     9  	"syscall"
    10  
    11  	fusefs "github.com/hanwen/go-fuse/v2/fs"
    12  	"github.com/hanwen/go-fuse/v2/fuse"
    13  	"github.com/rclone/rclone/cmd/mountlib"
    14  	"github.com/rclone/rclone/fs"
    15  	"github.com/rclone/rclone/fs/log"
    16  	"github.com/rclone/rclone/vfs"
    17  )
    18  
    19  // Node represents a directory or file
    20  type Node struct {
    21  	fusefs.Inode
    22  	node vfs.Node
    23  	fsys *FS
    24  }
    25  
    26  // Node types must be InodeEmbedders
    27  var _ fusefs.InodeEmbedder = (*Node)(nil)
    28  
    29  // newNode creates a new fusefs.Node from a vfs Node
    30  func newNode(fsys *FS, vfsNode vfs.Node) (node *Node) {
    31  	// Check the vfsNode to see if it has a fuse Node cached
    32  	// We must return the same fuse nodes for vfs Nodes
    33  	node, ok := vfsNode.Sys().(*Node)
    34  	if ok {
    35  		return node
    36  	}
    37  	node = &Node{
    38  		node: vfsNode,
    39  		fsys: fsys,
    40  	}
    41  	// Cache the node for later
    42  	vfsNode.SetSys(node)
    43  	return node
    44  }
    45  
    46  // String used for pretty printing.
    47  func (n *Node) String() string {
    48  	return n.node.Path()
    49  }
    50  
    51  // lookup a Node in a directory
    52  func (n *Node) lookupVfsNodeInDir(leaf string) (vfsNode vfs.Node, errno syscall.Errno) {
    53  	dir, ok := n.node.(*vfs.Dir)
    54  	if !ok {
    55  		return nil, syscall.ENOTDIR
    56  	}
    57  	vfsNode, err := dir.Stat(leaf)
    58  	return vfsNode, translateError(err)
    59  }
    60  
    61  // // lookup a Dir given a path
    62  // func (n *Node) lookupDir(path string) (dir *vfs.Dir, code fuse.Status) {
    63  // 	node, code := fsys.lookupVfsNodeInDir(path)
    64  // 	if !code.Ok() {
    65  // 		return nil, code
    66  // 	}
    67  // 	dir, ok := n.(*vfs.Dir)
    68  // 	if !ok {
    69  // 		return nil, fuse.ENOTDIR
    70  // 	}
    71  // 	return dir, fuse.OK
    72  // }
    73  
    74  // // lookup a parent Dir given a path returning the dir and the leaf
    75  // func (n *Node) lookupParentDir(filePath string) (leaf string, dir *vfs.Dir, code fuse.Status) {
    76  // 	parentDir, leaf := path.Split(filePath)
    77  // 	dir, code = fsys.lookupDir(parentDir)
    78  // 	return leaf, dir, code
    79  // }
    80  
    81  // Statfs implements statistics for the filesystem that holds this
    82  // Inode. If not defined, the `out` argument will zeroed with an OK
    83  // result.  This is because OSX filesystems must Statfs, or the mount
    84  // will not work.
    85  func (n *Node) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno {
    86  	defer log.Trace(n, "")("out=%+v", &out)
    87  	out = new(fuse.StatfsOut)
    88  	const blockSize = 4096
    89  	const fsBlocks = (1 << 50) / blockSize
    90  	out.Blocks = fsBlocks  // Total data blocks in file system.
    91  	out.Bfree = fsBlocks   // Free blocks in file system.
    92  	out.Bavail = fsBlocks  // Free blocks in file system if you're not root.
    93  	out.Files = 1e9        // Total files in file system.
    94  	out.Ffree = 1e9        // Free files in file system.
    95  	out.Bsize = blockSize  // Block size
    96  	out.NameLen = 255      // Maximum file name length?
    97  	out.Frsize = blockSize // Fragment size, smallest addressable data size in the file system.
    98  	mountlib.ClipBlocks(&out.Blocks)
    99  	mountlib.ClipBlocks(&out.Bfree)
   100  	mountlib.ClipBlocks(&out.Bavail)
   101  	return 0
   102  }
   103  
   104  var _ = (fusefs.NodeStatfser)((*Node)(nil))
   105  
   106  // Getattr reads attributes for an Inode. The library will ensure that
   107  // Mode and Ino are set correctly. For files that are not opened with
   108  // FOPEN_DIRECTIO, Size should be set so it can be read correctly.  If
   109  // returning zeroed permissions, the default behavior is to change the
   110  // mode of 0755 (directory) or 0644 (files). This can be switched off
   111  // with the Options.NullPermissions setting. If blksize is unset, 4096
   112  // is assumed, and the 'blocks' field is set accordingly.
   113  func (n *Node) Getattr(ctx context.Context, f fusefs.FileHandle, out *fuse.AttrOut) syscall.Errno {
   114  	setAttrOut(n.node, out)
   115  	return 0
   116  }
   117  
   118  var _ = (fusefs.NodeGetattrer)((*Node)(nil))
   119  
   120  // Setattr sets attributes for an Inode.
   121  func (n *Node) Setattr(ctx context.Context, f fusefs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) (errno syscall.Errno) {
   122  	defer log.Trace(n, "in=%v", in)("out=%#v, errno=%v", &out, &errno)
   123  	var err error
   124  	setAttrOut(n.node, out)
   125  	size, ok := in.GetSize()
   126  	if ok {
   127  		err = n.node.Truncate(int64(size))
   128  		if err != nil {
   129  			return translateError(err)
   130  		}
   131  		out.Attr.Size = size
   132  	}
   133  	mtime, ok := in.GetMTime()
   134  	if ok {
   135  		err = n.node.SetModTime(mtime)
   136  		if err != nil {
   137  			return translateError(err)
   138  		}
   139  		out.Attr.Mtime = uint64(mtime.Unix())
   140  		out.Attr.Mtimensec = uint32(mtime.Nanosecond())
   141  	}
   142  	return 0
   143  }
   144  
   145  var _ = (fusefs.NodeSetattrer)((*Node)(nil))
   146  
   147  // Open opens an Inode (of regular file type) for reading. It
   148  // is optional but recommended to return a FileHandle.
   149  func (n *Node) Open(ctx context.Context, flags uint32) (fh fusefs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
   150  	defer log.Trace(n, "flags=%#o", flags)("errno=%v", &errno)
   151  	// fuse flags are based off syscall flags as are os flags, so
   152  	// should be compatible
   153  	handle, err := n.node.Open(int(flags))
   154  	if err != nil {
   155  		return nil, 0, translateError(err)
   156  	}
   157  	// If size unknown then use direct io to read
   158  	if entry := n.node.DirEntry(); entry != nil && entry.Size() < 0 {
   159  		fuseFlags |= fuse.FOPEN_DIRECT_IO
   160  	}
   161  	return newFileHandle(handle), fuseFlags, 0
   162  }
   163  
   164  var _ = (fusefs.NodeOpener)((*Node)(nil))
   165  
   166  // Lookup should find a direct child of a directory by the child's name.  If
   167  // the entry does not exist, it should return ENOENT and optionally
   168  // set a NegativeTimeout in `out`. If it does exist, it should return
   169  // attribute data in `out` and return the Inode for the child. A new
   170  // inode can be created using `Inode.NewInode`. The new Inode will be
   171  // added to the FS tree automatically if the return status is OK.
   172  //
   173  // If a directory does not implement NodeLookuper, the library looks
   174  // for an existing child with the given name.
   175  //
   176  // The input to a Lookup is {parent directory, name string}.
   177  //
   178  // Lookup, if successful, must return an *Inode. Once the Inode is
   179  // returned to the kernel, the kernel can issue further operations,
   180  // such as Open or Getxattr on that node.
   181  //
   182  // A successful Lookup also returns an EntryOut. Among others, this
   183  // contains file attributes (mode, size, mtime, etc.).
   184  //
   185  // FUSE supports other operations that modify the namespace. For
   186  // example, the Symlink, Create, Mknod, Link methods all create new
   187  // children in directories. Hence, they also return *Inode and must
   188  // populate their fuse.EntryOut arguments.
   189  func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (inode *fusefs.Inode, errno syscall.Errno) {
   190  	defer log.Trace(n, "name=%q", name)("inode=%v, attr=%v, errno=%v", &inode, &out, &errno)
   191  	vfsNode, errno := n.lookupVfsNodeInDir(name)
   192  	if errno != 0 {
   193  		return nil, errno
   194  	}
   195  	newNode := newNode(n.fsys, vfsNode)
   196  
   197  	// FIXME
   198  	// out.SetEntryTimeout(dt time.Duration)
   199  	// out.SetAttrTimeout(dt time.Duration)
   200  	setEntryOut(vfsNode, out)
   201  
   202  	return n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode}), 0
   203  }
   204  
   205  var _ = (fusefs.NodeLookuper)((*Node)(nil))
   206  
   207  // Opendir opens a directory Inode for reading its
   208  // contents. The actual reading is driven from Readdir, so
   209  // this method is just for performing sanity/permission
   210  // checks. The default is to return success.
   211  func (n *Node) Opendir(ctx context.Context) syscall.Errno {
   212  	if !n.node.IsDir() {
   213  		return syscall.ENOTDIR
   214  	}
   215  	return 0
   216  }
   217  
   218  var _ = (fusefs.NodeOpendirer)((*Node)(nil))
   219  
   220  type dirStream struct {
   221  	nodes []os.FileInfo
   222  	i     int
   223  }
   224  
   225  // HasNext indicates if there are further entries. HasNext
   226  // might be called on already closed streams.
   227  func (ds *dirStream) HasNext() bool {
   228  	return ds.i < len(ds.nodes)
   229  }
   230  
   231  // Next retrieves the next entry. It is only called if HasNext
   232  // has previously returned true.  The Errno return may be used to
   233  // indicate I/O errors
   234  func (ds *dirStream) Next() (de fuse.DirEntry, errno syscall.Errno) {
   235  	// defer log.Trace(nil, "")("de=%+v, errno=%v", &de, &errno)
   236  	fi := ds.nodes[ds.i]
   237  	de = fuse.DirEntry{
   238  		// Mode is the file's mode. Only the high bits (eg. S_IFDIR)
   239  		// are considered.
   240  		Mode: getMode(fi),
   241  
   242  		// Name is the basename of the file in the directory.
   243  		Name: path.Base(fi.Name()),
   244  
   245  		// Ino is the inode number.
   246  		Ino: 0, // FIXME
   247  	}
   248  	ds.i++
   249  	return de, 0
   250  }
   251  
   252  // Close releases resources related to this directory
   253  // stream.
   254  func (ds *dirStream) Close() {
   255  }
   256  
   257  var _ fusefs.DirStream = (*dirStream)(nil)
   258  
   259  // Readdir opens a stream of directory entries.
   260  //
   261  // Readdir essentiallly returns a list of strings, and it is allowed
   262  // for Readdir to return different results from Lookup. For example,
   263  // you can return nothing for Readdir ("ls my-fuse-mount" is empty),
   264  // while still implementing Lookup ("ls my-fuse-mount/a-specific-file"
   265  // shows a single file).
   266  //
   267  // If a directory does not implement NodeReaddirer, a list of
   268  // currently known children from the tree is returned. This means that
   269  // static in-memory file systems need not implement NodeReaddirer.
   270  func (n *Node) Readdir(ctx context.Context) (ds fusefs.DirStream, errno syscall.Errno) {
   271  	defer log.Trace(n, "")("ds=%v, errno=%v", &ds, &errno)
   272  	if !n.node.IsDir() {
   273  		return nil, syscall.ENOTDIR
   274  	}
   275  	fh, err := n.node.Open(os.O_RDONLY)
   276  	if err != nil {
   277  		return nil, translateError(err)
   278  	}
   279  	defer func() {
   280  		closeErr := fh.Close()
   281  		if errno == 0 && closeErr != nil {
   282  			errno = translateError(closeErr)
   283  		}
   284  	}()
   285  	items, err := fh.Readdir(-1)
   286  	if err != nil {
   287  		return nil, translateError(err)
   288  	}
   289  	return &dirStream{
   290  		nodes: items,
   291  	}, 0
   292  }
   293  
   294  var _ = (fusefs.NodeReaddirer)((*Node)(nil))
   295  
   296  // Mkdir is similar to Lookup, but must create a directory entry and Inode.
   297  // Default is to return EROFS.
   298  func (n *Node) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (inode *fusefs.Inode, errno syscall.Errno) {
   299  	defer log.Trace(name, "mode=0%o", mode)("inode=%v, errno=%v", &inode, &errno)
   300  	dir, ok := n.node.(*vfs.Dir)
   301  	if !ok {
   302  		return nil, syscall.ENOTDIR
   303  	}
   304  	newDir, err := dir.Mkdir(name)
   305  	if err != nil {
   306  		return nil, translateError(err)
   307  	}
   308  	newNode := newNode(n.fsys, newDir)
   309  	setEntryOut(newNode.node, out)
   310  	newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode})
   311  	return newInode, 0
   312  }
   313  
   314  var _ = (fusefs.NodeMkdirer)((*Node)(nil))
   315  
   316  // Create is similar to Lookup, but should create a new
   317  // child. It typically also returns a FileHandle as a
   318  // reference for future reads/writes.
   319  // Default is to return EROFS.
   320  func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (node *fusefs.Inode, fh fusefs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
   321  	defer log.Trace(n, "name=%q, flags=%#o, mode=%#o", name, flags, mode)("node=%v, fh=%v, flags=%#o, errno=%v", &node, &fh, &fuseFlags, &errno)
   322  	dir, ok := n.node.(*vfs.Dir)
   323  	if !ok {
   324  		return nil, nil, 0, syscall.ENOTDIR
   325  	}
   326  	// translate the fuse flags to os flags
   327  	osFlags := int(flags) | os.O_CREATE
   328  	file, err := dir.Create(name, osFlags)
   329  	if err != nil {
   330  		return nil, nil, 0, translateError(err)
   331  	}
   332  	handle, err := file.Open(osFlags)
   333  	if err != nil {
   334  		return nil, nil, 0, translateError(err)
   335  	}
   336  	fh = newFileHandle(handle)
   337  	// FIXME
   338  	// fh = &fusefs.WithFlags{
   339  	// 	File: fh,
   340  	// 	//FuseFlags: fuse.FOPEN_NONSEEKABLE,
   341  	// 	OpenFlags: flags,
   342  	// }
   343  
   344  	// Find the created node
   345  	vfsNode, errno := n.lookupVfsNodeInDir(name)
   346  	if errno != 0 {
   347  		return nil, nil, 0, errno
   348  	}
   349  	setEntryOut(vfsNode, out)
   350  	newNode := newNode(n.fsys, vfsNode)
   351  	fs.Debugf(nil, "attr=%#v", out.Attr)
   352  	newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode})
   353  	return newInode, fh, 0, 0
   354  }
   355  
   356  var _ = (fusefs.NodeCreater)((*Node)(nil))
   357  
   358  // Unlink should remove a child from this directory.  If the
   359  // return status is OK, the Inode is removed as child in the
   360  // FS tree automatically. Default is to return EROFS.
   361  func (n *Node) Unlink(ctx context.Context, name string) (errno syscall.Errno) {
   362  	defer log.Trace(n, "name=%q", name)("errno=%v", &errno)
   363  	vfsNode, errno := n.lookupVfsNodeInDir(name)
   364  	if errno != 0 {
   365  		return errno
   366  	}
   367  	return translateError(vfsNode.Remove())
   368  }
   369  
   370  var _ = (fusefs.NodeUnlinker)((*Node)(nil))
   371  
   372  // Rmdir is like Unlink but for directories.
   373  // Default is to return EROFS.
   374  func (n *Node) Rmdir(ctx context.Context, name string) (errno syscall.Errno) {
   375  	defer log.Trace(n, "name=%q", name)("errno=%v", &errno)
   376  	vfsNode, errno := n.lookupVfsNodeInDir(name)
   377  	if errno != 0 {
   378  		return errno
   379  	}
   380  	return translateError(vfsNode.Remove())
   381  }
   382  
   383  var _ = (fusefs.NodeRmdirer)((*Node)(nil))
   384  
   385  // Rename should move a child from one directory to a different
   386  // one. The change is effected in the FS tree if the return status is
   387  // OK. Default is to return EROFS.
   388  func (n *Node) Rename(ctx context.Context, oldName string, newParent fusefs.InodeEmbedder, newName string, flags uint32) (errno syscall.Errno) {
   389  	defer log.Trace(n, "oldName=%q, newParent=%v, newName=%q", oldName, newParent, newName)("errno=%v", &errno)
   390  	oldDir, ok := n.node.(*vfs.Dir)
   391  	if !ok {
   392  		return syscall.ENOTDIR
   393  	}
   394  	newParentNode, ok := newParent.(*Node)
   395  	if !ok {
   396  		fs.Errorf(n, "newParent was not a *Node")
   397  		return syscall.EIO
   398  	}
   399  	newDir, ok := newParentNode.node.(*vfs.Dir)
   400  	if !ok {
   401  		return syscall.ENOTDIR
   402  	}
   403  	return translateError(oldDir.Rename(oldName, newName, newDir))
   404  }
   405  
   406  var _ = (fusefs.NodeRenamer)((*Node)(nil))