github.com/artpar/rclone@v1.67.3/cmd/mount/dir.go (about)

     1  //go:build linux
     2  
     3  package mount
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"syscall"
    11  	"time"
    12  
    13  	"bazil.org/fuse"
    14  	fusefs "bazil.org/fuse/fs"
    15  	"github.com/artpar/rclone/cmd/mountlib"
    16  	"github.com/artpar/rclone/fs"
    17  	"github.com/artpar/rclone/fs/log"
    18  	"github.com/artpar/rclone/vfs"
    19  )
    20  
    21  // Dir represents a directory entry
    22  type Dir struct {
    23  	*vfs.Dir
    24  	fsys *FS
    25  }
    26  
    27  // Check interface satisfied
    28  var _ fusefs.Node = (*Dir)(nil)
    29  
    30  // Attr updates the attributes of a directory
    31  func (d *Dir) Attr(ctx context.Context, a *fuse.Attr) (err error) {
    32  	defer log.Trace(d, "")("attr=%+v, err=%v", a, &err)
    33  	a.Valid = d.fsys.opt.AttrTimeout
    34  	a.Gid = d.VFS().Opt.GID
    35  	a.Uid = d.VFS().Opt.UID
    36  	a.Mode = os.ModeDir | d.VFS().Opt.DirPerms
    37  	modTime := d.ModTime()
    38  	a.Atime = modTime
    39  	a.Mtime = modTime
    40  	a.Ctime = modTime
    41  	// FIXME include Valid so get some caching?
    42  	// FIXME fs.Debugf(d.path, "Dir.Attr %+v", a)
    43  	return nil
    44  }
    45  
    46  // Check interface satisfied
    47  var _ fusefs.NodeSetattrer = (*Dir)(nil)
    48  
    49  // Setattr handles attribute changes from FUSE. Currently supports ModTime only.
    50  func (d *Dir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) (err error) {
    51  	defer log.Trace(d, "stat=%+v", req)("err=%v", &err)
    52  	if d.VFS().Opt.NoModTime {
    53  		return nil
    54  	}
    55  
    56  	if req.Valid.MtimeNow() {
    57  		err = d.SetModTime(time.Now())
    58  	} else if req.Valid.Mtime() {
    59  		err = d.SetModTime(req.Mtime)
    60  	}
    61  
    62  	return translateError(err)
    63  }
    64  
    65  // Check interface satisfied
    66  var _ fusefs.NodeRequestLookuper = (*Dir)(nil)
    67  
    68  // Lookup looks up a specific entry in the receiver.
    69  //
    70  // Lookup should return a Node corresponding to the entry.  If the
    71  // name does not exist in the directory, Lookup should return ENOENT.
    72  //
    73  // Lookup need not to handle the names "." and "..".
    74  func (d *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (node fusefs.Node, err error) {
    75  	defer log.Trace(d, "name=%q", req.Name)("node=%+v, err=%v", &node, &err)
    76  	mnode, err := d.Dir.Stat(req.Name)
    77  	if err != nil {
    78  		return nil, translateError(err)
    79  	}
    80  	resp.EntryValid = d.fsys.opt.AttrTimeout
    81  	// Check the mnode to see if it has a fuse Node cached
    82  	// We must return the same fuse nodes for vfs Nodes
    83  	node, ok := mnode.Sys().(fusefs.Node)
    84  	if ok {
    85  		return node, nil
    86  	}
    87  	switch x := mnode.(type) {
    88  	case *vfs.File:
    89  		node = &File{x, d.fsys}
    90  	case *vfs.Dir:
    91  		node = &Dir{x, d.fsys}
    92  	default:
    93  		panic("bad type")
    94  	}
    95  	// Cache the node for later
    96  	mnode.SetSys(node)
    97  	return node, nil
    98  }
    99  
   100  // Check interface satisfied
   101  var _ fusefs.HandleReadDirAller = (*Dir)(nil)
   102  
   103  // ReadDirAll reads the contents of the directory
   104  func (d *Dir) ReadDirAll(ctx context.Context) (dirents []fuse.Dirent, err error) {
   105  	itemsRead := -1
   106  	defer log.Trace(d, "")("item=%d, err=%v", &itemsRead, &err)
   107  	items, err := d.Dir.ReadDirAll()
   108  	if err != nil {
   109  		return nil, translateError(err)
   110  	}
   111  	dirents = append(dirents, fuse.Dirent{
   112  		Type: fuse.DT_Dir,
   113  		Name: ".",
   114  	}, fuse.Dirent{
   115  		Type: fuse.DT_Dir,
   116  		Name: "..",
   117  	})
   118  	for _, node := range items {
   119  		name := node.Name()
   120  		if len(name) > mountlib.MaxLeafSize {
   121  			fs.Errorf(d, "Name too long (%d bytes) for FUSE, skipping: %s", len(name), name)
   122  			continue
   123  		}
   124  		var dirent = fuse.Dirent{
   125  			// Inode FIXME ???
   126  			Type: fuse.DT_File,
   127  			Name: name,
   128  		}
   129  		if node.IsDir() {
   130  			dirent.Type = fuse.DT_Dir
   131  		}
   132  		dirents = append(dirents, dirent)
   133  	}
   134  	itemsRead = len(dirents)
   135  	return dirents, nil
   136  }
   137  
   138  var _ fusefs.NodeCreater = (*Dir)(nil)
   139  
   140  // Create makes a new file
   141  func (d *Dir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (node fusefs.Node, handle fusefs.Handle, err error) {
   142  	defer log.Trace(d, "name=%q", req.Name)("node=%v, handle=%v, err=%v", &node, &handle, &err)
   143  	file, err := d.Dir.Create(req.Name, int(req.Flags))
   144  	if err != nil {
   145  		return nil, nil, translateError(err)
   146  	}
   147  	fh, err := file.Open(int(req.Flags) | os.O_CREATE)
   148  	if err != nil {
   149  		return nil, nil, translateError(err)
   150  	}
   151  	node = &File{file, d.fsys}
   152  	file.SetSys(node) // cache the FUSE node for later
   153  	return node, &FileHandle{fh}, err
   154  }
   155  
   156  var _ fusefs.NodeMkdirer = (*Dir)(nil)
   157  
   158  // Mkdir creates a new directory
   159  func (d *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (node fusefs.Node, err error) {
   160  	defer log.Trace(d, "name=%q", req.Name)("node=%+v, err=%v", &node, &err)
   161  	dir, err := d.Dir.Mkdir(req.Name)
   162  	if err != nil {
   163  		return nil, translateError(err)
   164  	}
   165  	node = &Dir{dir, d.fsys}
   166  	dir.SetSys(node) // cache the FUSE node for later
   167  	return node, nil
   168  }
   169  
   170  var _ fusefs.NodeRemover = (*Dir)(nil)
   171  
   172  // Remove removes the entry with the given name from
   173  // the receiver, which must be a directory.  The entry to be removed
   174  // may correspond to a file (unlink) or to a directory (rmdir).
   175  func (d *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) (err error) {
   176  	defer log.Trace(d, "name=%q", req.Name)("err=%v", &err)
   177  	err = d.Dir.RemoveName(req.Name)
   178  	if err != nil {
   179  		return translateError(err)
   180  	}
   181  	return nil
   182  }
   183  
   184  // Invalidate a leaf in a directory
   185  func (d *Dir) invalidateEntry(dirNode fusefs.Node, leaf string) {
   186  	fs.Debugf(dirNode, "Invalidating %q", leaf)
   187  	err := d.fsys.server.InvalidateEntry(dirNode, leaf)
   188  	if err != nil {
   189  		fs.Debugf(dirNode, "Failed to invalidate %q: %v", leaf, err)
   190  	}
   191  }
   192  
   193  // Check interface satisfied
   194  var _ fusefs.NodeRenamer = (*Dir)(nil)
   195  
   196  // Rename the file
   197  func (d *Dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fusefs.Node) (err error) {
   198  	defer log.Trace(d, "oldName=%q, newName=%q, newDir=%+v", req.OldName, req.NewName, newDir)("err=%v", &err)
   199  	destDir, ok := newDir.(*Dir)
   200  	if !ok {
   201  		return fmt.Errorf("unknown Dir type %T", newDir)
   202  	}
   203  
   204  	err = d.Dir.Rename(req.OldName, req.NewName, destDir.Dir)
   205  	if err != nil {
   206  		return translateError(err)
   207  	}
   208  
   209  	// Invalidate the new directory entry so it gets re-read (in
   210  	// the background otherwise we cause a deadlock)
   211  	//
   212  	// See https://github.com/artpar/rclone/issues/4977 for why
   213  	go d.invalidateEntry(newDir, req.NewName)
   214  	//go d.invalidateEntry(d, req.OldName)
   215  
   216  	return nil
   217  }
   218  
   219  // Check interface satisfied
   220  var _ fusefs.NodeFsyncer = (*Dir)(nil)
   221  
   222  // Fsync the directory
   223  func (d *Dir) Fsync(ctx context.Context, req *fuse.FsyncRequest) (err error) {
   224  	defer log.Trace(d, "")("err=%v", &err)
   225  	err = d.Dir.Sync()
   226  	if err != nil {
   227  		return translateError(err)
   228  	}
   229  	return nil
   230  }
   231  
   232  // Check interface satisfied
   233  var _ fusefs.NodeLinker = (*Dir)(nil)
   234  
   235  // Link creates a new directory entry in the receiver based on an
   236  // existing Node. Receiver must be a directory.
   237  func (d *Dir) Link(ctx context.Context, req *fuse.LinkRequest, old fusefs.Node) (newNode fusefs.Node, err error) {
   238  	defer log.Trace(d, "req=%v, old=%v", req, old)("new=%v, err=%v", &newNode, &err)
   239  	return nil, syscall.ENOSYS
   240  }
   241  
   242  // Check interface satisfied
   243  var _ fusefs.NodeMknoder = (*Dir)(nil)
   244  
   245  // Mknod is called to create a file. Since we define create this will
   246  // be called in preference, however NFS likes to call it for some
   247  // reason. We don't actually create a file here just the Node.
   248  func (d *Dir) Mknod(ctx context.Context, req *fuse.MknodRequest) (node fusefs.Node, err error) {
   249  	defer log.Trace(d, "name=%v, mode=%d, rdev=%d", req.Name, req.Mode, req.Rdev)("node=%v, err=%v", &node, &err)
   250  	if req.Rdev != 0 {
   251  		fs.Errorf(d, "Can't create device node %q", req.Name)
   252  		return nil, fuse.Errno(syscall.EIO)
   253  	}
   254  	var cReq = fuse.CreateRequest{
   255  		Name:  req.Name,
   256  		Flags: fuse.OpenFlags(os.O_CREATE | os.O_WRONLY),
   257  		Mode:  req.Mode,
   258  		Umask: req.Umask,
   259  	}
   260  	var cResp fuse.CreateResponse
   261  	node, handle, err := d.Create(ctx, &cReq, &cResp)
   262  	if err != nil {
   263  		return nil, err
   264  	}
   265  	err = handle.(io.Closer).Close()
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  	return node, nil
   270  }