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