2023-10-29 22:15:34 +08:00
|
|
|
//go:build linux
|
2016-07-18 06:03:23 +08:00
|
|
|
|
|
|
|
package mount
|
|
|
|
|
|
|
|
import (
|
2019-05-07 20:53:16 +08:00
|
|
|
"context"
|
2021-11-04 18:12:57 +08:00
|
|
|
"fmt"
|
2020-10-29 23:12:36 +08:00
|
|
|
"io"
|
2016-07-18 06:03:23 +08:00
|
|
|
"os"
|
2022-12-15 05:14:20 +08:00
|
|
|
"path"
|
2022-06-12 18:37:00 +08:00
|
|
|
"syscall"
|
2016-07-18 06:03:23 +08:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"bazil.org/fuse"
|
|
|
|
fusefs "bazil.org/fuse/fs"
|
2019-07-29 01:47:38 +08:00
|
|
|
"github.com/rclone/rclone/cmd/mountlib"
|
2019-10-18 17:53:07 +08:00
|
|
|
"github.com/rclone/rclone/fs"
|
2019-07-29 01:47:38 +08:00
|
|
|
"github.com/rclone/rclone/fs/log"
|
|
|
|
"github.com/rclone/rclone/vfs"
|
2016-07-18 06:03:23 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// Dir represents a directory entry
|
|
|
|
type Dir struct {
|
2017-10-29 03:01:34 +08:00
|
|
|
*vfs.Dir
|
2020-07-24 00:17:01 +08:00
|
|
|
fsys *FS
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
|
|
|
|
2019-04-30 20:06:24 +08:00
|
|
|
// Check interface satisfied
|
2016-07-18 06:03:23 +08:00
|
|
|
var _ fusefs.Node = (*Dir)(nil)
|
|
|
|
|
2017-03-24 03:41:21 +08:00
|
|
|
// Attr updates the attributes of a directory
|
2017-05-09 18:39:33 +08:00
|
|
|
func (d *Dir) Attr(ctx context.Context, a *fuse.Attr) (err error) {
|
2018-01-13 00:30:54 +08:00
|
|
|
defer log.Trace(d, "")("attr=%+v, err=%v", a, &err)
|
2024-07-02 17:26:15 +08:00
|
|
|
a.Valid = time.Duration(d.fsys.opt.AttrTimeout)
|
2017-10-29 19:00:56 +08:00
|
|
|
a.Gid = d.VFS().Opt.GID
|
|
|
|
a.Uid = d.VFS().Opt.UID
|
2022-12-15 05:14:20 +08:00
|
|
|
a.Mode = d.Mode()
|
2017-05-03 05:35:07 +08:00
|
|
|
modTime := d.ModTime()
|
|
|
|
a.Atime = modTime
|
|
|
|
a.Mtime = modTime
|
|
|
|
a.Ctime = modTime
|
2017-02-07 05:17:45 +08:00
|
|
|
// FIXME include Valid so get some caching?
|
2017-05-03 05:35:07 +08:00
|
|
|
// FIXME fs.Debugf(d.path, "Dir.Attr %+v", a)
|
2016-07-18 06:03:23 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-03-24 03:41:21 +08:00
|
|
|
// Check interface satisfied
|
|
|
|
var _ fusefs.NodeSetattrer = (*Dir)(nil)
|
|
|
|
|
|
|
|
// Setattr handles attribute changes from FUSE. Currently supports ModTime only.
|
2017-05-03 05:35:07 +08:00
|
|
|
func (d *Dir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) (err error) {
|
2018-01-13 00:30:54 +08:00
|
|
|
defer log.Trace(d, "stat=%+v", req)("err=%v", &err)
|
2017-10-29 19:00:56 +08:00
|
|
|
if d.VFS().Opt.NoModTime {
|
2017-03-24 03:41:21 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if req.Valid.MtimeNow() {
|
2017-05-03 05:35:07 +08:00
|
|
|
err = d.SetModTime(time.Now())
|
2017-03-24 03:41:21 +08:00
|
|
|
} else if req.Valid.Mtime() {
|
2017-05-03 05:35:07 +08:00
|
|
|
err = d.SetModTime(req.Mtime)
|
2017-03-24 03:41:21 +08:00
|
|
|
}
|
|
|
|
|
2017-05-03 05:35:07 +08:00
|
|
|
return translateError(err)
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check interface satisfied
|
|
|
|
var _ 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) {
|
2018-01-13 00:30:54 +08:00
|
|
|
defer log.Trace(d, "name=%q", req.Name)("node=%+v, err=%v", &node, &err)
|
2017-10-29 19:36:38 +08:00
|
|
|
mnode, err := d.Dir.Stat(req.Name)
|
2016-07-18 06:03:23 +08:00
|
|
|
if err != nil {
|
2017-05-03 05:35:07 +08:00
|
|
|
return nil, translateError(err)
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
2024-07-02 17:26:15 +08:00
|
|
|
resp.EntryValid = time.Duration(d.fsys.opt.AttrTimeout)
|
2020-05-02 01:31:19 +08:00
|
|
|
// 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
|
|
|
|
}
|
2017-05-03 05:35:07 +08:00
|
|
|
switch x := mnode.(type) {
|
2017-10-29 03:01:34 +08:00
|
|
|
case *vfs.File:
|
2020-07-24 00:17:01 +08:00
|
|
|
node = &File{x, d.fsys}
|
2017-10-29 03:01:34 +08:00
|
|
|
case *vfs.Dir:
|
2020-07-24 00:17:01 +08:00
|
|
|
node = &Dir{x, d.fsys}
|
2020-05-02 01:31:19 +08:00
|
|
|
default:
|
|
|
|
panic("bad type")
|
2017-05-03 05:35:07 +08:00
|
|
|
}
|
2020-05-02 01:31:19 +08:00
|
|
|
// Cache the node for later
|
|
|
|
mnode.SetSys(node)
|
|
|
|
return node, nil
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check interface satisfied
|
|
|
|
var _ fusefs.HandleReadDirAller = (*Dir)(nil)
|
|
|
|
|
|
|
|
// ReadDirAll reads the contents of the directory
|
|
|
|
func (d *Dir) ReadDirAll(ctx context.Context) (dirents []fuse.Dirent, err error) {
|
2017-05-09 18:39:33 +08:00
|
|
|
itemsRead := -1
|
2018-01-13 00:30:54 +08:00
|
|
|
defer log.Trace(d, "")("item=%d, err=%v", &itemsRead, &err)
|
2017-05-03 05:35:07 +08:00
|
|
|
items, err := d.Dir.ReadDirAll()
|
2016-07-18 06:03:23 +08:00
|
|
|
if err != nil {
|
2017-05-03 05:35:07 +08:00
|
|
|
return nil, translateError(err)
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
2020-12-16 18:57:02 +08:00
|
|
|
dirents = append(dirents, fuse.Dirent{
|
|
|
|
Type: fuse.DT_Dir,
|
|
|
|
Name: ".",
|
|
|
|
}, fuse.Dirent{
|
|
|
|
Type: fuse.DT_Dir,
|
|
|
|
Name: "..",
|
|
|
|
})
|
2017-11-18 19:47:21 +08:00
|
|
|
for _, node := range items {
|
2019-10-18 17:53:07 +08:00
|
|
|
name := node.Name()
|
2020-01-19 22:54:55 +08:00
|
|
|
if len(name) > mountlib.MaxLeafSize {
|
2019-10-18 17:53:07 +08:00
|
|
|
fs.Errorf(d, "Name too long (%d bytes) for FUSE, skipping: %s", len(name), name)
|
|
|
|
continue
|
|
|
|
}
|
2017-11-18 19:47:21 +08:00
|
|
|
var dirent = fuse.Dirent{
|
|
|
|
// Inode FIXME ???
|
|
|
|
Type: fuse.DT_File,
|
2019-10-18 17:53:07 +08:00
|
|
|
Name: name,
|
2017-11-18 19:47:21 +08:00
|
|
|
}
|
|
|
|
if node.IsDir() {
|
|
|
|
dirent.Type = fuse.DT_Dir
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
|
|
|
dirents = append(dirents, dirent)
|
|
|
|
}
|
2017-05-09 18:39:33 +08:00
|
|
|
itemsRead = len(dirents)
|
2016-07-18 06:03:23 +08:00
|
|
|
return dirents, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ fusefs.NodeCreater = (*Dir)(nil)
|
|
|
|
|
|
|
|
// Create makes a new file
|
2017-05-09 18:39:33 +08:00
|
|
|
func (d *Dir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (node fusefs.Node, handle fusefs.Handle, err error) {
|
2018-01-13 00:30:54 +08:00
|
|
|
defer log.Trace(d, "name=%q", req.Name)("node=%v, handle=%v, err=%v", &node, &handle, &err)
|
2022-12-15 05:14:20 +08:00
|
|
|
// translate the fuse flags to os flags
|
|
|
|
osFlags := int(req.Flags) | os.O_CREATE
|
|
|
|
file, err := d.Dir.Create(req.Name, osFlags)
|
2017-11-06 20:22:45 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, translateError(err)
|
|
|
|
}
|
2022-12-15 05:14:20 +08:00
|
|
|
fh, err := file.Open(osFlags)
|
2016-07-18 06:03:23 +08:00
|
|
|
if err != nil {
|
2017-05-03 05:35:07 +08:00
|
|
|
return nil, nil, translateError(err)
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
2020-07-24 00:17:01 +08:00
|
|
|
node = &File{file, d.fsys}
|
2020-05-02 01:31:19 +08:00
|
|
|
file.SetSys(node) // cache the FUSE node for later
|
|
|
|
return node, &FileHandle{fh}, err
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
var _ fusefs.NodeMkdirer = (*Dir)(nil)
|
|
|
|
|
|
|
|
// Mkdir creates a new directory
|
2017-05-09 18:39:33 +08:00
|
|
|
func (d *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (node fusefs.Node, err error) {
|
2018-01-13 00:30:54 +08:00
|
|
|
defer log.Trace(d, "name=%q", req.Name)("node=%+v, err=%v", &node, &err)
|
2017-05-03 05:35:07 +08:00
|
|
|
dir, err := d.Dir.Mkdir(req.Name)
|
2017-01-06 19:24:22 +08:00
|
|
|
if err != nil {
|
2017-05-03 05:35:07 +08:00
|
|
|
return nil, translateError(err)
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
2020-07-24 00:17:01 +08:00
|
|
|
node = &Dir{dir, d.fsys}
|
2020-05-02 01:31:19 +08:00
|
|
|
dir.SetSys(node) // cache the FUSE node for later
|
|
|
|
return node, nil
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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).
|
2017-05-09 18:39:33 +08:00
|
|
|
func (d *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) (err error) {
|
2018-01-13 00:30:54 +08:00
|
|
|
defer log.Trace(d, "name=%q", req.Name)("err=%v", &err)
|
2017-10-26 22:37:45 +08:00
|
|
|
err = d.Dir.RemoveName(req.Name)
|
2016-07-18 06:03:23 +08:00
|
|
|
if err != nil {
|
2017-05-03 05:35:07 +08:00
|
|
|
return translateError(err)
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-03-16 21:23:36 +08:00
|
|
|
// Invalidate a leaf in a directory
|
|
|
|
func (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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-18 06:03:23 +08:00
|
|
|
// Check interface satisfied
|
|
|
|
var _ fusefs.NodeRenamer = (*Dir)(nil)
|
|
|
|
|
|
|
|
// Rename the file
|
2017-05-09 18:39:33 +08:00
|
|
|
func (d *Dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fusefs.Node) (err error) {
|
2018-01-13 00:30:54 +08:00
|
|
|
defer log.Trace(d, "oldName=%q, newName=%q, newDir=%+v", req.OldName, req.NewName, newDir)("err=%v", &err)
|
2016-07-18 06:03:23 +08:00
|
|
|
destDir, ok := newDir.(*Dir)
|
|
|
|
if !ok {
|
2022-06-09 04:54:39 +08:00
|
|
|
return fmt.Errorf("unknown Dir type %T", newDir)
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
2017-05-09 18:39:33 +08:00
|
|
|
err = d.Dir.Rename(req.OldName, req.NewName, destDir.Dir)
|
2016-07-18 06:03:23 +08:00
|
|
|
if err != nil {
|
2017-05-03 05:35:07 +08:00
|
|
|
return translateError(err)
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
|
|
|
|
2021-03-16 21:23:36 +08:00
|
|
|
// 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)
|
|
|
|
|
2016-07-18 06:03:23 +08:00
|
|
|
return nil
|
|
|
|
}
|
2017-02-03 05:30:32 +08:00
|
|
|
|
|
|
|
// Check interface satisfied
|
|
|
|
var _ fusefs.NodeFsyncer = (*Dir)(nil)
|
|
|
|
|
|
|
|
// Fsync the directory
|
2017-05-09 18:39:33 +08:00
|
|
|
func (d *Dir) Fsync(ctx context.Context, req *fuse.FsyncRequest) (err error) {
|
2018-01-13 00:30:54 +08:00
|
|
|
defer log.Trace(d, "")("err=%v", &err)
|
2017-11-18 23:48:49 +08:00
|
|
|
err = d.Dir.Sync()
|
2017-05-03 05:35:07 +08:00
|
|
|
if err != nil {
|
|
|
|
return translateError(err)
|
|
|
|
}
|
2017-02-03 05:30:32 +08:00
|
|
|
return nil
|
|
|
|
}
|
2018-03-13 20:31:46 +08:00
|
|
|
|
|
|
|
// Check interface satisfied
|
|
|
|
var _ fusefs.NodeLinker = (*Dir)(nil)
|
|
|
|
|
|
|
|
// Link creates a new directory entry in the receiver based on an
|
|
|
|
// existing Node. Receiver must be a directory.
|
2018-08-04 18:16:43 +08:00
|
|
|
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)
|
2022-06-12 18:37:00 +08:00
|
|
|
return nil, syscall.ENOSYS
|
2018-03-13 20:31:46 +08:00
|
|
|
}
|
2020-10-29 23:12:36 +08:00
|
|
|
|
2022-12-15 05:14:20 +08:00
|
|
|
var _ fusefs.NodeSymlinker = (*Dir)(nil)
|
|
|
|
|
|
|
|
// Symlink create a symbolic link.
|
|
|
|
func (d *Dir) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (node fusefs.Node, err error) {
|
|
|
|
defer log.Trace(d, "newname=%v, target=%v", req.NewName, req.Target)("node=%v, err=%v", &node, &err)
|
|
|
|
|
|
|
|
newName := path.Join(d.Path(), req.NewName)
|
|
|
|
target := req.Target
|
|
|
|
|
|
|
|
n, err := d.VFS().CreateSymlink(target, newName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
node = &File{n.(*vfs.File), d.fsys}
|
|
|
|
return node, nil
|
|
|
|
}
|
|
|
|
|
2020-10-29 23:12:36 +08:00
|
|
|
// Check interface satisfied
|
|
|
|
var _ 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)
|
2023-03-26 06:27:07 +08:00
|
|
|
return nil, fuse.Errno(syscall.EIO)
|
2020-10-29 23:12:36 +08:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|