270 lines · 7.5 KB · Go
RawDownload
// +build linux freebsd package mount import (	"context"	"io"	"os"	"time" 	"bazil.org/fuse"	fusefs "bazil.org/fuse/fs"	"github.com/pkg/errors"	"github.com/rclone/rclone/cmd/mountlib"	"github.com/rclone/rclone/fs"	"github.com/rclone/rclone/fs/log"	"github.com/rclone/rclone/vfs") // Dir represents a directory entrytype Dir struct {	*vfs.Dir	fsys *FS} // Check interface satisfiedvar _ fusefs.Node = (*Dir)(nil) // Attr updates the attributes of a directoryfunc (d *Dir) Attr(ctx context.Context, a *fuse.Attr) (err error) {	defer log.Trace(d, "")("attr=%+v, err=%v", a, &err)	a.Valid = d.fsys.opt.AttrTimeout	a.Gid = d.VFS().Opt.GID	a.Uid = d.VFS().Opt.UID	a.Mode = os.ModeDir | d.VFS().Opt.DirPerms	modTime := d.ModTime()	a.Atime = modTime	a.Mtime = modTime	a.Ctime = modTime	a.Crtime = modTime	// FIXME include Valid so get some caching?	// FIXME fs.Debugf(d.path, "Dir.Attr %+v", a)	return nil} // Check interface satisfiedvar _ fusefs.NodeSetattrer = (*Dir)(nil) // Setattr handles attribute changes from FUSE. Currently supports ModTime only.func (d *Dir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) (err error) {	defer log.Trace(d, "stat=%+v", req)("err=%v", &err)	if d.VFS().Opt.NoModTime {		return nil	} 	if req.Valid.MtimeNow() {		err = d.SetModTime(time.Now())	} else if req.Valid.Mtime() {		err = d.SetModTime(req.Mtime)	} 	return translateError(err)} // Check interface satisfiedvar _ fusefs.NodeRequestLookuper = (*Dir)(nil) // Lookup looks up a specific entry in the receiver.//// Lookup should return a Node corresponding to the entry.  If the// name does not exist in the directory, Lookup should return ENOENT.//// Lookup need not to handle the names "." and "..".func (d *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (node fusefs.Node, err error) {	defer log.Trace(d, "name=%q", req.Name)("node=%+v, err=%v", &node, &err)	mnode, err := d.Dir.Stat(req.Name)	if err != nil {		return nil, translateError(err)	}	resp.EntryValid = d.fsys.opt.AttrTimeout	// Check the mnode to see if it has a fuse Node cached	// We must return the same fuse nodes for vfs Nodes	node, ok := mnode.Sys().(fusefs.Node)	if ok {		return node, nil	}	switch x := mnode.(type) {	case *vfs.File:		node = &File{x, d.fsys}	case *vfs.Dir:		node = &Dir{x, d.fsys}	default:		panic("bad type")	}	// Cache the node for later	mnode.SetSys(node)	return node, nil} // Check interface satisfiedvar _ fusefs.HandleReadDirAller = (*Dir)(nil) // ReadDirAll reads the contents of the directoryfunc (d *Dir) ReadDirAll(ctx context.Context) (dirents []fuse.Dirent, err error) {	itemsRead := -1	defer log.Trace(d, "")("item=%d, err=%v", &itemsRead, &err)	items, err := d.Dir.ReadDirAll()	if err != nil {		return nil, translateError(err)	}	dirents = append(dirents, fuse.Dirent{		Type: fuse.DT_Dir,		Name: ".",	}, fuse.Dirent{		Type: fuse.DT_Dir,		Name: "..",	})	for _, node := range items {		name := node.Name()		if len(name) > mountlib.MaxLeafSize {			fs.Errorf(d, "Name too long (%d bytes) for FUSE, skipping: %s", len(name), name)			continue		}		var dirent = fuse.Dirent{			// Inode FIXME ???			Type: fuse.DT_File,			Name: name,		}		if node.IsDir() {			dirent.Type = fuse.DT_Dir		}		dirents = append(dirents, dirent)	}	itemsRead = len(dirents)	return dirents, nil} var _ fusefs.NodeCreater = (*Dir)(nil) // Create makes a new filefunc (d *Dir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (node fusefs.Node, handle fusefs.Handle, err error) {	defer log.Trace(d, "name=%q", req.Name)("node=%v, handle=%v, err=%v", &node, &handle, &err)	file, err := d.Dir.Create(req.Name, int(req.Flags))	if err != nil {		return nil, nil, translateError(err)	}	fh, err := file.Open(int(req.Flags) | os.O_CREATE)	if err != nil {		return nil, nil, translateError(err)	}	node = &File{file, d.fsys}	file.SetSys(node) // cache the FUSE node for later	return node, &FileHandle{fh}, err} var _ fusefs.NodeMkdirer = (*Dir)(nil) // Mkdir creates a new directoryfunc (d *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (node fusefs.Node, err error) {	defer log.Trace(d, "name=%q", req.Name)("node=%+v, err=%v", &node, &err)	dir, err := d.Dir.Mkdir(req.Name)	if err != nil {		return nil, translateError(err)	}	node = &Dir{dir, d.fsys}	dir.SetSys(node) // cache the FUSE node for later	return node, nil} var _ fusefs.NodeRemover = (*Dir)(nil) // Remove removes the entry with the given name from// the receiver, which must be a directory.  The entry to be removed// may correspond to a file (unlink) or to a directory (rmdir).func (d *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) (err error) {	defer log.Trace(d, "name=%q", req.Name)("err=%v", &err)	err = d.Dir.RemoveName(req.Name)	if err != nil {		return translateError(err)	}	return nil} // Invalidate a leaf in a directoryfunc (d *Dir) invalidateEntry(dirNode fusefs.Node, leaf string) {	fs.Debugf(dirNode, "Invalidating %q", leaf)	err := d.fsys.server.InvalidateEntry(dirNode, leaf)	if err != nil {		fs.Debugf(dirNode, "Failed to invalidate %q: %v", leaf, err)	}} // Check interface satisfiedvar _ fusefs.NodeRenamer = (*Dir)(nil) // Rename the filefunc (d *Dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fusefs.Node) (err error) {	defer log.Trace(d, "oldName=%q, newName=%q, newDir=%+v", req.OldName, req.NewName, newDir)("err=%v", &err)	destDir, ok := newDir.(*Dir)	if !ok {		return errors.Errorf("Unknown Dir type %T", newDir)	} 	err = d.Dir.Rename(req.OldName, req.NewName, destDir.Dir)	if err != nil {		return translateError(err)	} 	// Invalidate the new directory entry so it gets re-read (in	// the background otherwise we cause a deadlock)	//	// See https://github.com/rclone/rclone/issues/4977 for why	go d.invalidateEntry(newDir, req.NewName)	//go d.invalidateEntry(d, req.OldName) 	return nil} // Check interface satisfiedvar _ fusefs.NodeFsyncer = (*Dir)(nil) // Fsync the directoryfunc (d *Dir) Fsync(ctx context.Context, req *fuse.FsyncRequest) (err error) {	defer log.Trace(d, "")("err=%v", &err)	err = d.Dir.Sync()	if err != nil {		return translateError(err)	}	return nil} // Check interface satisfiedvar _ fusefs.NodeLinker = (*Dir)(nil) // Link creates a new directory entry in the receiver based on an// existing Node. Receiver must be a directory.func (d *Dir) Link(ctx context.Context, req *fuse.LinkRequest, old fusefs.Node) (newNode fusefs.Node, err error) {	defer log.Trace(d, "req=%v, old=%v", req, old)("new=%v, err=%v", &newNode, &err)	return nil, fuse.ENOSYS} // Check interface satisfiedvar _ fusefs.NodeMknoder = (*Dir)(nil) // Mknod is called to create a file. Since we define create this will// be called in preference, however NFS likes to call it for some// reason. We don't actually create a file here just the Node.func (d *Dir) Mknod(ctx context.Context, req *fuse.MknodRequest) (node fusefs.Node, err error) {	defer log.Trace(d, "name=%v, mode=%d, rdev=%d", req.Name, req.Mode, req.Rdev)("node=%v, err=%v", &node, &err)	if req.Rdev != 0 {		fs.Errorf(d, "Can't create device node %q", req.Name)		return nil, fuse.EIO	}	var cReq = fuse.CreateRequest{		Name:  req.Name,		Flags: fuse.OpenFlags(os.O_CREATE | os.O_WRONLY),		Mode:  req.Mode,		Umask: req.Umask,	}	var cResp fuse.CreateResponse	node, handle, err := d.Create(ctx, &cReq, &cResp)	if err != nil {		return nil, err	}	err = handle.(io.Closer).Close()	if err != nil {		return nil, err	}	return node, nil}