1//go:build cmount && cgo && (linux || darwin || freebsd || windows)
2// +build cmount
3// +build cgo
4// +build linux darwin freebsd windows
5
6package cmount
7
8import (
9	"io"
10	"os"
11	"path"
12	"sync"
13	"sync/atomic"
14	"time"
15
16	"github.com/billziss-gh/cgofuse/fuse"
17	"github.com/pkg/errors"
18	"github.com/rclone/rclone/cmd/mountlib"
19	"github.com/rclone/rclone/fs"
20	"github.com/rclone/rclone/fs/log"
21	"github.com/rclone/rclone/vfs"
22)
23
24const fhUnset = ^uint64(0)
25
26// FS represents the top level filing system
27type FS struct {
28	VFS       *vfs.VFS
29	f         fs.Fs
30	ready     chan (struct{})
31	mu        sync.Mutex // to protect the below
32	handles   []vfs.Handle
33	destroyed int32 // read/write with sync/atomic
34}
35
36// NewFS makes a new FS
37func NewFS(VFS *vfs.VFS) *FS {
38	fsys := &FS{
39		VFS:   VFS,
40		f:     VFS.Fs(),
41		ready: make(chan (struct{})),
42	}
43	return fsys
44}
45
46// Open a handle returning an integer file handle
47func (fsys *FS) openHandle(handle vfs.Handle) (fh uint64) {
48	fsys.mu.Lock()
49	defer fsys.mu.Unlock()
50	var i int
51	var oldHandle vfs.Handle
52	for i, oldHandle = range fsys.handles {
53		if oldHandle == nil {
54			fsys.handles[i] = handle
55			goto found
56		}
57	}
58	fsys.handles = append(fsys.handles, handle)
59	i = len(fsys.handles) - 1
60found:
61	return uint64(i)
62}
63
64// get the handle for fh, call with the lock held
65func (fsys *FS) _getHandle(fh uint64) (i int, handle vfs.Handle, errc int) {
66	if fh > uint64(len(fsys.handles)) {
67		fs.Debugf(nil, "Bad file handle: too big: 0x%X", fh)
68		return i, nil, -fuse.EBADF
69	}
70	i = int(fh)
71	handle = fsys.handles[i]
72	if handle == nil {
73		fs.Debugf(nil, "Bad file handle: nil handle: 0x%X", fh)
74		return i, nil, -fuse.EBADF
75	}
76	return i, handle, 0
77}
78
79// Get the handle for the file handle
80func (fsys *FS) getHandle(fh uint64) (handle vfs.Handle, errc int) {
81	fsys.mu.Lock()
82	_, handle, errc = fsys._getHandle(fh)
83	fsys.mu.Unlock()
84	return
85}
86
87// Close the handle
88func (fsys *FS) closeHandle(fh uint64) (errc int) {
89	fsys.mu.Lock()
90	i, _, errc := fsys._getHandle(fh)
91	if errc == 0 {
92		fsys.handles[i] = nil
93	}
94	fsys.mu.Unlock()
95	return
96}
97
98// lookup a Node given a path
99func (fsys *FS) lookupNode(path string) (node vfs.Node, errc int) {
100	node, err := fsys.VFS.Stat(path)
101	return node, translateError(err)
102}
103
104// lookup a Dir given a path
105func (fsys *FS) lookupDir(path string) (dir *vfs.Dir, errc int) {
106	node, errc := fsys.lookupNode(path)
107	if errc != 0 {
108		return nil, errc
109	}
110	dir, ok := node.(*vfs.Dir)
111	if !ok {
112		return nil, -fuse.ENOTDIR
113	}
114	return dir, 0
115}
116
117// lookup a parent Dir given a path returning the dir and the leaf
118func (fsys *FS) lookupParentDir(filePath string) (leaf string, dir *vfs.Dir, errc int) {
119	parentDir, leaf := path.Split(filePath)
120	dir, errc = fsys.lookupDir(parentDir)
121	return leaf, dir, errc
122}
123
124// lookup a File given a path
125func (fsys *FS) lookupFile(path string) (file *vfs.File, errc int) {
126	node, errc := fsys.lookupNode(path)
127	if errc != 0 {
128		return nil, errc
129	}
130	file, ok := node.(*vfs.File)
131	if !ok {
132		return nil, -fuse.EISDIR
133	}
134	return file, 0
135}
136
137// get a node and handle from the path or from the fh if not fhUnset
138//
139// handle may be nil
140func (fsys *FS) getNode(path string, fh uint64) (node vfs.Node, handle vfs.Handle, errc int) {
141	if fh == fhUnset {
142		node, errc = fsys.lookupNode(path)
143	} else {
144		handle, errc = fsys.getHandle(fh)
145		if errc == 0 {
146			node = handle.Node()
147		}
148	}
149	return
150}
151
152// stat fills up the stat block for Node
153func (fsys *FS) stat(node vfs.Node, stat *fuse.Stat_t) (errc int) {
154	Size := uint64(node.Size())
155	Blocks := (Size + 511) / 512
156	modTime := node.ModTime()
157	Mode := node.Mode().Perm()
158	if node.IsDir() {
159		Mode |= fuse.S_IFDIR
160	} else {
161		Mode |= fuse.S_IFREG
162	}
163	//stat.Dev = 1
164	stat.Ino = node.Inode() // FIXME do we need to set the inode number?
165	stat.Mode = uint32(Mode)
166	stat.Nlink = 1
167	stat.Uid = fsys.VFS.Opt.UID
168	stat.Gid = fsys.VFS.Opt.GID
169	//stat.Rdev
170	stat.Size = int64(Size)
171	t := fuse.NewTimespec(modTime)
172	stat.Atim = t
173	stat.Mtim = t
174	stat.Ctim = t
175	stat.Blksize = 512
176	stat.Blocks = int64(Blocks)
177	stat.Birthtim = t
178	// fs.Debugf(nil, "stat = %+v", *stat)
179	return 0
180}
181
182// Init is called after the filesystem is ready
183func (fsys *FS) Init() {
184	defer log.Trace(fsys.f, "")("")
185	close(fsys.ready)
186}
187
188// Destroy is called when it is unmounted (note that depending on how
189// the file system is terminated the file system may not receive the
190// Destroy call).
191func (fsys *FS) Destroy() {
192	defer log.Trace(fsys.f, "")("")
193	atomic.StoreInt32(&fsys.destroyed, 1)
194}
195
196// Getattr reads the attributes for path
197func (fsys *FS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
198	defer log.Trace(path, "fh=0x%X", fh)("errc=%v", &errc)
199	node, _, errc := fsys.getNode(path, fh)
200	if errc == 0 {
201		errc = fsys.stat(node, stat)
202	}
203	return
204}
205
206// Opendir opens path as a directory
207func (fsys *FS) Opendir(path string) (errc int, fh uint64) {
208	defer log.Trace(path, "")("errc=%d, fh=0x%X", &errc, &fh)
209	handle, err := fsys.VFS.OpenFile(path, os.O_RDONLY, 0777)
210	if err != nil {
211		return translateError(err), fhUnset
212	}
213	return 0, fsys.openHandle(handle)
214}
215
216// Readdir reads the directory at dirPath
217func (fsys *FS) Readdir(dirPath string,
218	fill func(name string, stat *fuse.Stat_t, ofst int64) bool,
219	ofst int64,
220	fh uint64) (errc int) {
221	itemsRead := -1
222	defer log.Trace(dirPath, "ofst=%d, fh=0x%X", ofst, fh)("items=%d, errc=%d", &itemsRead, &errc)
223
224	dir, errc := fsys.lookupDir(dirPath)
225	if errc != 0 {
226		return errc
227	}
228
229	// We can't seek in directories and FUSE should know that so
230	// return an error if ofst is ever set.
231	if ofst > 0 {
232		return -fuse.ESPIPE
233	}
234
235	nodes, err := dir.ReadDirAll()
236	if err != nil {
237		return translateError(err)
238	}
239
240	// Optionally, create a struct stat that describes the file as
241	// for getattr (but FUSE only looks at st_ino and the
242	// file-type bits of st_mode).
243	//
244	// We have called host.SetCapReaddirPlus() so WinFsp will
245	// use the full stat information - a Useful optimization on
246	// Windows.
247	//
248	// NB we are using the first mode for readdir: The readdir
249	// implementation ignores the offset parameter, and passes
250	// zero to the filler function's offset. The filler function
251	// will not return '1' (unless an error happens), so the whole
252	// directory is read in a single readdir operation.
253	fill(".", nil, 0)
254	fill("..", nil, 0)
255	for _, node := range nodes {
256		name := node.Name()
257		if len(name) > mountlib.MaxLeafSize {
258			fs.Errorf(dirPath, "Name too long (%d bytes) for FUSE, skipping: %s", len(name), name)
259			continue
260		}
261		// We have called host.SetCapReaddirPlus() so supply the stat information
262		// It is very cheap at this point so supply it regardless of OS capabilities
263		var stat fuse.Stat_t
264		_ = fsys.stat(node, &stat) // not capable of returning an error
265		fill(name, &stat, 0)
266	}
267	itemsRead = len(nodes)
268	return 0
269}
270
271// Releasedir finished reading the directory
272func (fsys *FS) Releasedir(path string, fh uint64) (errc int) {
273	defer log.Trace(path, "fh=0x%X", fh)("errc=%d", &errc)
274	return fsys.closeHandle(fh)
275}
276
277// Statfs reads overall stats on the filesystem
278func (fsys *FS) Statfs(path string, stat *fuse.Statfs_t) (errc int) {
279	defer log.Trace(path, "")("stat=%+v, errc=%d", stat, &errc)
280	const blockSize = 4096
281	total, _, free := fsys.VFS.Statfs()
282	stat.Blocks = uint64(total) / blockSize // Total data blocks in file system.
283	stat.Bfree = uint64(free) / blockSize   // Free blocks in file system.
284	stat.Bavail = stat.Bfree                // Free blocks in file system if you're not root.
285	stat.Files = 1e9                        // Total files in file system.
286	stat.Ffree = 1e9                        // Free files in file system.
287	stat.Bsize = blockSize                  // Block size
288	stat.Namemax = 255                      // Maximum file name length?
289	stat.Frsize = blockSize                 // Fragment size, smallest addressable data size in the file system.
290	mountlib.ClipBlocks(&stat.Blocks)
291	mountlib.ClipBlocks(&stat.Bfree)
292	mountlib.ClipBlocks(&stat.Bavail)
293	return 0
294}
295
296// OpenEx opens a file
297func (fsys *FS) OpenEx(path string, fi *fuse.FileInfo_t) (errc int) {
298	defer log.Trace(path, "flags=0x%X", fi.Flags)("errc=%d, fh=0x%X", &errc, &fi.Fh)
299	fi.Fh = fhUnset
300
301	// translate the fuse flags to os flags
302	flags := translateOpenFlags(fi.Flags)
303	handle, err := fsys.VFS.OpenFile(path, flags, 0777)
304	if err != nil {
305		return translateError(err)
306	}
307
308	// If size unknown then use direct io to read
309	if entry := handle.Node().DirEntry(); entry != nil && entry.Size() < 0 {
310		fi.DirectIo = true
311	}
312
313	fi.Fh = fsys.openHandle(handle)
314	return 0
315}
316
317// Open opens a file
318func (fsys *FS) Open(path string, flags int) (errc int, fh uint64) {
319	var fi = fuse.FileInfo_t{
320		Flags: flags,
321	}
322	errc = fsys.OpenEx(path, &fi)
323	return errc, fi.Fh
324}
325
326// CreateEx creates and opens a file.
327func (fsys *FS) CreateEx(filePath string, mode uint32, fi *fuse.FileInfo_t) (errc int) {
328	defer log.Trace(filePath, "flags=0x%X, mode=0%o", fi.Flags, mode)("errc=%d, fh=0x%X", &errc, &fi.Fh)
329	fi.Fh = fhUnset
330	leaf, parentDir, errc := fsys.lookupParentDir(filePath)
331	if errc != 0 {
332		return errc
333	}
334	file, err := parentDir.Create(leaf, fi.Flags)
335	if err != nil {
336		return translateError(err)
337	}
338	// translate the fuse flags to os flags
339	flags := translateOpenFlags(fi.Flags) | os.O_CREATE
340	handle, err := file.Open(flags)
341	if err != nil {
342		return translateError(err)
343	}
344	fi.Fh = fsys.openHandle(handle)
345	return 0
346}
347
348// Create creates and opens a file.
349func (fsys *FS) Create(filePath string, flags int, mode uint32) (errc int, fh uint64) {
350	var fi = fuse.FileInfo_t{
351		Flags: flags,
352	}
353	errc = fsys.CreateEx(filePath, mode, &fi)
354	return errc, fi.Fh
355}
356
357// Truncate truncates a file to size
358func (fsys *FS) Truncate(path string, size int64, fh uint64) (errc int) {
359	defer log.Trace(path, "size=%d, fh=0x%X", size, fh)("errc=%d", &errc)
360	node, handle, errc := fsys.getNode(path, fh)
361	if errc != 0 {
362		return errc
363	}
364	var err error
365	if handle != nil {
366		err = handle.Truncate(size)
367	} else {
368		err = node.Truncate(size)
369	}
370	if err != nil {
371		return translateError(err)
372	}
373	return 0
374}
375
376// Read data from file handle
377func (fsys *FS) Read(path string, buff []byte, ofst int64, fh uint64) (n int) {
378	defer log.Trace(path, "ofst=%d, fh=0x%X", ofst, fh)("n=%d", &n)
379	handle, errc := fsys.getHandle(fh)
380	if errc != 0 {
381		return errc
382	}
383	n, err := handle.ReadAt(buff, ofst)
384	if err == io.EOF {
385	} else if err != nil {
386		return translateError(err)
387	}
388	return n
389}
390
391// Write data to file handle
392func (fsys *FS) Write(path string, buff []byte, ofst int64, fh uint64) (n int) {
393	defer log.Trace(path, "ofst=%d, fh=0x%X", ofst, fh)("n=%d", &n)
394	handle, errc := fsys.getHandle(fh)
395	if errc != 0 {
396		return errc
397	}
398	n, err := handle.WriteAt(buff, ofst)
399	if err != nil {
400		return translateError(err)
401	}
402	return n
403}
404
405// Flush flushes an open file descriptor or path
406func (fsys *FS) Flush(path string, fh uint64) (errc int) {
407	defer log.Trace(path, "fh=0x%X", fh)("errc=%d", &errc)
408	handle, errc := fsys.getHandle(fh)
409	if errc != 0 {
410		return errc
411	}
412	return translateError(handle.Flush())
413}
414
415// Release closes the file if still open
416func (fsys *FS) Release(path string, fh uint64) (errc int) {
417	defer log.Trace(path, "fh=0x%X", fh)("errc=%d", &errc)
418	handle, errc := fsys.getHandle(fh)
419	if errc != 0 {
420		return errc
421	}
422	_ = fsys.closeHandle(fh)
423	return translateError(handle.Release())
424}
425
426// Unlink removes a file.
427func (fsys *FS) Unlink(filePath string) (errc int) {
428	defer log.Trace(filePath, "")("errc=%d", &errc)
429	leaf, parentDir, errc := fsys.lookupParentDir(filePath)
430	if errc != 0 {
431		return errc
432	}
433	return translateError(parentDir.RemoveName(leaf))
434}
435
436// Mkdir creates a directory.
437func (fsys *FS) Mkdir(dirPath string, mode uint32) (errc int) {
438	defer log.Trace(dirPath, "mode=0%o", mode)("errc=%d", &errc)
439	leaf, parentDir, errc := fsys.lookupParentDir(dirPath)
440	if errc != 0 {
441		return errc
442	}
443	_, err := parentDir.Mkdir(leaf)
444	return translateError(err)
445}
446
447// Rmdir removes a directory
448func (fsys *FS) Rmdir(dirPath string) (errc int) {
449	defer log.Trace(dirPath, "")("errc=%d", &errc)
450	leaf, parentDir, errc := fsys.lookupParentDir(dirPath)
451	if errc != 0 {
452		return errc
453	}
454	return translateError(parentDir.RemoveName(leaf))
455}
456
457// Rename renames a file.
458func (fsys *FS) Rename(oldPath string, newPath string) (errc int) {
459	defer log.Trace(oldPath, "newPath=%q", newPath)("errc=%d", &errc)
460	return translateError(fsys.VFS.Rename(oldPath, newPath))
461}
462
463// Windows sometimes seems to send times that are the epoch which is
464// 1601-01-01 +/- timezone so filter out times that are earlier than
465// this.
466var invalidDateCutoff = time.Date(1601, 1, 2, 0, 0, 0, 0, time.UTC)
467
468// Utimens changes the access and modification times of a file.
469func (fsys *FS) Utimens(path string, tmsp []fuse.Timespec) (errc int) {
470	defer log.Trace(path, "tmsp=%+v", tmsp)("errc=%d", &errc)
471	node, errc := fsys.lookupNode(path)
472	if errc != 0 {
473		return errc
474	}
475	if tmsp == nil || len(tmsp) < 2 {
476		fs.Debugf(path, "Utimens: Not setting time as timespec isn't complete: %v", tmsp)
477		return 0
478	}
479	t := tmsp[1].Time()
480	if t.Before(invalidDateCutoff) {
481		fs.Debugf(path, "Utimens: Not setting out of range time: %v", t)
482		return 0
483	}
484	fs.Debugf(path, "Utimens: SetModTime: %v", t)
485	return translateError(node.SetModTime(t))
486}
487
488// Mknod creates a file node.
489func (fsys *FS) Mknod(path string, mode uint32, dev uint64) (errc int) {
490	defer log.Trace(path, "mode=0x%X, dev=0x%X", mode, dev)("errc=%d", &errc)
491	return -fuse.ENOSYS
492}
493
494// Fsync synchronizes file contents.
495func (fsys *FS) Fsync(path string, datasync bool, fh uint64) (errc int) {
496	defer log.Trace(path, "datasync=%v, fh=0x%X", datasync, fh)("errc=%d", &errc)
497	// This is a no-op for rclone
498	return 0
499}
500
501// Link creates a hard link to a file.
502func (fsys *FS) Link(oldpath string, newpath string) (errc int) {
503	defer log.Trace(oldpath, "newpath=%q", newpath)("errc=%d", &errc)
504	return -fuse.ENOSYS
505}
506
507// Symlink creates a symbolic link.
508func (fsys *FS) Symlink(target string, newpath string) (errc int) {
509	defer log.Trace(target, "newpath=%q", newpath)("errc=%d", &errc)
510	return -fuse.ENOSYS
511}
512
513// Readlink reads the target of a symbolic link.
514func (fsys *FS) Readlink(path string) (errc int, linkPath string) {
515	defer log.Trace(path, "")("linkPath=%q, errc=%d", &linkPath, &errc)
516	return -fuse.ENOSYS, ""
517}
518
519// Chmod changes the permission bits of a file.
520func (fsys *FS) Chmod(path string, mode uint32) (errc int) {
521	defer log.Trace(path, "mode=0%o", mode)("errc=%d", &errc)
522	// This is a no-op for rclone
523	return 0
524}
525
526// Chown changes the owner and group of a file.
527func (fsys *FS) Chown(path string, uid uint32, gid uint32) (errc int) {
528	defer log.Trace(path, "uid=%d, gid=%d", uid, gid)("errc=%d", &errc)
529	// This is a no-op for rclone
530	return 0
531}
532
533// Access checks file access permissions.
534func (fsys *FS) Access(path string, mask uint32) (errc int) {
535	defer log.Trace(path, "mask=0%o", mask)("errc=%d", &errc)
536	// This is a no-op for rclone
537	return 0
538}
539
540// Fsyncdir synchronizes directory contents.
541func (fsys *FS) Fsyncdir(path string, datasync bool, fh uint64) (errc int) {
542	defer log.Trace(path, "datasync=%v, fh=0x%X", datasync, fh)("errc=%d", &errc)
543	// This is a no-op for rclone
544	return 0
545}
546
547// Setxattr sets extended attributes.
548func (fsys *FS) Setxattr(path string, name string, value []byte, flags int) (errc int) {
549	return -fuse.ENOSYS
550}
551
552// Getxattr gets extended attributes.
553func (fsys *FS) Getxattr(path string, name string) (errc int, value []byte) {
554	return -fuse.ENOSYS, nil
555}
556
557// Removexattr removes extended attributes.
558func (fsys *FS) Removexattr(path string, name string) (errc int) {
559	return -fuse.ENOSYS
560}
561
562// Listxattr lists extended attributes.
563func (fsys *FS) Listxattr(path string, fill func(name string) bool) (errc int) {
564	return -fuse.ENOSYS
565}
566
567// Translate errors from mountlib
568func translateError(err error) (errc int) {
569	if err == nil {
570		return 0
571	}
572	switch errors.Cause(err) {
573	case vfs.OK:
574		return 0
575	case vfs.ENOENT, fs.ErrorDirNotFound, fs.ErrorObjectNotFound:
576		return -fuse.ENOENT
577	case vfs.EEXIST, fs.ErrorDirExists:
578		return -fuse.EEXIST
579	case vfs.EPERM, fs.ErrorPermissionDenied:
580		return -fuse.EPERM
581	case vfs.ECLOSED:
582		return -fuse.EBADF
583	case vfs.ENOTEMPTY:
584		return -fuse.ENOTEMPTY
585	case vfs.ESPIPE:
586		return -fuse.ESPIPE
587	case vfs.EBADF:
588		return -fuse.EBADF
589	case vfs.EROFS:
590		return -fuse.EROFS
591	case vfs.ENOSYS, fs.ErrorNotImplemented:
592		return -fuse.ENOSYS
593	case vfs.EINVAL:
594		return -fuse.EINVAL
595	}
596	fs.Errorf(nil, "IO error: %v", err)
597	return -fuse.EIO
598}
599
600// Translate Open Flags from FUSE to os (as used in the vfs layer)
601func translateOpenFlags(inFlags int) (outFlags int) {
602	switch inFlags & fuse.O_ACCMODE {
603	case fuse.O_RDONLY:
604		outFlags = os.O_RDONLY
605	case fuse.O_WRONLY:
606		outFlags = os.O_WRONLY
607	case fuse.O_RDWR:
608		outFlags = os.O_RDWR
609	}
610	if inFlags&fuse.O_APPEND != 0 {
611		outFlags |= os.O_APPEND
612	}
613	if inFlags&fuse.O_CREAT != 0 {
614		outFlags |= os.O_CREATE
615	}
616	if inFlags&fuse.O_EXCL != 0 {
617		outFlags |= os.O_EXCL
618	}
619	if inFlags&fuse.O_TRUNC != 0 {
620		outFlags |= os.O_TRUNC
621	}
622	// NB O_SYNC isn't defined by fuse
623	return outFlags
624}
625
626// Make sure interfaces are satisfied
627var (
628	_ fuse.FileSystemInterface = (*FS)(nil)
629	_ fuse.FileSystemOpenEx    = (*FS)(nil)
630	//_ fuse.FileSystemChflags    = (*FS)(nil)
631	//_ fuse.FileSystemSetcrtime  = (*FS)(nil)
632	//_ fuse.FileSystemSetchgtime = (*FS)(nil)
633)
634