1// Package vfs provides a virtual filing system layer over rclone's
2// native objects.
3//
4// It attempts to behave in a similar way to Go's filing system
5// manipulation code in the os package.  The same named function
6// should behave in an identical fashion.  The objects also obey Go's
7// standard interfaces.
8//
9// Note that paths don't start or end with /, so the root directory
10// may be referred to as "".  However Stat strips slashes so you can
11// use paths with slashes in.
12//
13// It also includes directory caching
14//
15// The vfs package returns Error values to signal precisely which
16// error conditions have ocurred.  It may also return general errors
17// it receives.  It tries to use os Error values (e.g. os.ErrExist)
18// where possible.
19
20//go:generate sh -c "go run make_open_tests.go | gofmt > open_test.go"
21
22package vfs
23
24import (
25	"context"
26	"fmt"
27	"io/ioutil"
28	"os"
29	"path"
30	"sort"
31	"strings"
32	"sync"
33	"sync/atomic"
34	"time"
35
36	"github.com/rclone/rclone/fs"
37	"github.com/rclone/rclone/fs/cache"
38	"github.com/rclone/rclone/fs/log"
39	"github.com/rclone/rclone/fs/walk"
40	"github.com/rclone/rclone/vfs/vfscache"
41	"github.com/rclone/rclone/vfs/vfscommon"
42)
43
44// Node represents either a directory (*Dir) or a file (*File)
45type Node interface {
46	os.FileInfo
47	IsFile() bool
48	Inode() uint64
49	SetModTime(modTime time.Time) error
50	Sync() error
51	Remove() error
52	RemoveAll() error
53	DirEntry() fs.DirEntry
54	VFS() *VFS
55	Open(flags int) (Handle, error)
56	Truncate(size int64) error
57	Path() string
58	SetSys(interface{})
59}
60
61// Check interfaces
62var (
63	_ Node = (*File)(nil)
64	_ Node = (*Dir)(nil)
65)
66
67// Nodes is a slice of Node
68type Nodes []Node
69
70// Sort functions
71func (ns Nodes) Len() int           { return len(ns) }
72func (ns Nodes) Swap(i, j int)      { ns[i], ns[j] = ns[j], ns[i] }
73func (ns Nodes) Less(i, j int) bool { return ns[i].Path() < ns[j].Path() }
74
75// Noder represents something which can return a node
76type Noder interface {
77	fmt.Stringer
78	Node() Node
79}
80
81// Check interfaces
82var (
83	_ Noder = (*File)(nil)
84	_ Noder = (*Dir)(nil)
85	_ Noder = (*ReadFileHandle)(nil)
86	_ Noder = (*WriteFileHandle)(nil)
87	_ Noder = (*RWFileHandle)(nil)
88	_ Noder = (*DirHandle)(nil)
89)
90
91// OsFiler is the methods on *os.File
92type OsFiler interface {
93	Chdir() error
94	Chmod(mode os.FileMode) error
95	Chown(uid, gid int) error
96	Close() error
97	Fd() uintptr
98	Name() string
99	Read(b []byte) (n int, err error)
100	ReadAt(b []byte, off int64) (n int, err error)
101	Readdir(n int) ([]os.FileInfo, error)
102	Readdirnames(n int) (names []string, err error)
103	Seek(offset int64, whence int) (ret int64, err error)
104	Stat() (os.FileInfo, error)
105	Sync() error
106	Truncate(size int64) error
107	Write(b []byte) (n int, err error)
108	WriteAt(b []byte, off int64) (n int, err error)
109	WriteString(s string) (n int, err error)
110}
111
112// Handle is the interface satisfied by open files or directories.
113// It is the methods on *os.File, plus a few more useful for FUSE
114// filingsystems.  Not all of them are supported.
115type Handle interface {
116	OsFiler
117	// Additional methods useful for FUSE filesystems
118	Flush() error
119	Release() error
120	Node() Node
121	//	Size() int64
122}
123
124// baseHandle implements all the missing methods
125type baseHandle struct{}
126
127func (h baseHandle) Chdir() error                                         { return ENOSYS }
128func (h baseHandle) Chmod(mode os.FileMode) error                         { return ENOSYS }
129func (h baseHandle) Chown(uid, gid int) error                             { return ENOSYS }
130func (h baseHandle) Close() error                                         { return ENOSYS }
131func (h baseHandle) Fd() uintptr                                          { return 0 }
132func (h baseHandle) Name() string                                         { return "" }
133func (h baseHandle) Read(b []byte) (n int, err error)                     { return 0, ENOSYS }
134func (h baseHandle) ReadAt(b []byte, off int64) (n int, err error)        { return 0, ENOSYS }
135func (h baseHandle) Readdir(n int) ([]os.FileInfo, error)                 { return nil, ENOSYS }
136func (h baseHandle) Readdirnames(n int) (names []string, err error)       { return nil, ENOSYS }
137func (h baseHandle) Seek(offset int64, whence int) (ret int64, err error) { return 0, ENOSYS }
138func (h baseHandle) Stat() (os.FileInfo, error)                           { return nil, ENOSYS }
139func (h baseHandle) Sync() error                                          { return nil }
140func (h baseHandle) Truncate(size int64) error                            { return ENOSYS }
141func (h baseHandle) Write(b []byte) (n int, err error)                    { return 0, ENOSYS }
142func (h baseHandle) WriteAt(b []byte, off int64) (n int, err error)       { return 0, ENOSYS }
143func (h baseHandle) WriteString(s string) (n int, err error)              { return 0, ENOSYS }
144func (h baseHandle) Flush() (err error)                                   { return ENOSYS }
145func (h baseHandle) Release() (err error)                                 { return ENOSYS }
146func (h baseHandle) Node() Node                                           { return nil }
147
148//func (h baseHandle) Size() int64                                          { return 0 }
149
150// Check interfaces
151var (
152	_ OsFiler = (*os.File)(nil)
153	_ Handle  = (*baseHandle)(nil)
154	_ Handle  = (*ReadFileHandle)(nil)
155	_ Handle  = (*WriteFileHandle)(nil)
156	_ Handle  = (*DirHandle)(nil)
157)
158
159// VFS represents the top level filing system
160type VFS struct {
161	f           fs.Fs
162	root        *Dir
163	Opt         vfscommon.Options
164	cache       *vfscache.Cache
165	cancelCache context.CancelFunc
166	usageMu     sync.Mutex
167	usageTime   time.Time
168	usage       *fs.Usage
169	pollChan    chan time.Duration
170	inUse       int32 // count of number of opens accessed with atomic
171}
172
173// Keep track of active VFS keyed on fs.ConfigString(f)
174var (
175	activeMu sync.Mutex
176	active   = map[string][]*VFS{}
177)
178
179// New creates a new VFS and root directory.  If opt is nil, then
180// DefaultOpt will be used
181func New(f fs.Fs, opt *vfscommon.Options) *VFS {
182	fsDir := fs.NewDir("", time.Now())
183	vfs := &VFS{
184		f:     f,
185		inUse: int32(1),
186	}
187
188	// Make a copy of the options
189	if opt != nil {
190		vfs.Opt = *opt
191	} else {
192		vfs.Opt = vfscommon.DefaultOpt
193	}
194
195	// Mask the permissions with the umask
196	vfs.Opt.DirPerms &= ^os.FileMode(vfs.Opt.Umask)
197	vfs.Opt.FilePerms &= ^os.FileMode(vfs.Opt.Umask)
198
199	// Make sure directories are returned as directories
200	vfs.Opt.DirPerms |= os.ModeDir
201
202	// Find a VFS with the same name and options and return it if possible
203	activeMu.Lock()
204	defer activeMu.Unlock()
205	configName := fs.ConfigString(f)
206	for _, activeVFS := range active[configName] {
207		if vfs.Opt == activeVFS.Opt {
208			fs.Debugf(f, "Re-using VFS from active cache")
209			atomic.AddInt32(&activeVFS.inUse, 1)
210			return activeVFS
211		}
212	}
213	// Put the VFS into the active cache
214	active[configName] = append(active[configName], vfs)
215
216	// Create root directory
217	vfs.root = newDir(vfs, f, nil, fsDir)
218
219	// Start polling function
220	features := vfs.f.Features()
221	if do := features.ChangeNotify; do != nil {
222		vfs.pollChan = make(chan time.Duration)
223		do(context.TODO(), vfs.root.changeNotify, vfs.pollChan)
224		vfs.pollChan <- vfs.Opt.PollInterval
225	} else if vfs.Opt.PollInterval > 0 {
226		fs.Infof(f, "poll-interval is not supported by this remote")
227	}
228
229	// Warn if can't stream
230	if !vfs.Opt.ReadOnly && vfs.Opt.CacheMode < vfscommon.CacheModeWrites && features.PutStream == nil {
231		fs.Logf(f, "--vfs-cache-mode writes or full is recommended for this remote as it can't stream")
232	}
233
234	vfs.SetCacheMode(vfs.Opt.CacheMode)
235
236	// Pin the Fs into the cache so that when we use cache.NewFs
237	// with the same remote string we get this one. The Pin is
238	// removed when the vfs is finalized
239	cache.PinUntilFinalized(f, vfs)
240
241	return vfs
242}
243
244// Return the number of active cache entries and a VFS if any are in
245// the cache.
246func activeCacheEntries() (vfs *VFS, count int) {
247	activeMu.Lock()
248	for _, vfses := range active {
249		count += len(vfses)
250		if len(vfses) > 0 {
251			vfs = vfses[0]
252		}
253	}
254	activeMu.Unlock()
255	return vfs, count
256}
257
258// Fs returns the Fs passed into the New call
259func (vfs *VFS) Fs() fs.Fs {
260	return vfs.f
261}
262
263// SetCacheMode change the cache mode
264func (vfs *VFS) SetCacheMode(cacheMode vfscommon.CacheMode) {
265	vfs.shutdownCache()
266	vfs.cache = nil
267	if cacheMode > vfscommon.CacheModeOff {
268		ctx, cancel := context.WithCancel(context.Background())
269		cache, err := vfscache.New(ctx, vfs.f, &vfs.Opt, vfs.AddVirtual) // FIXME pass on context or get from Opt?
270		if err != nil {
271			fs.Errorf(nil, "Failed to create vfs cache - disabling: %v", err)
272			vfs.Opt.CacheMode = vfscommon.CacheModeOff
273			cancel()
274			return
275		}
276		vfs.Opt.CacheMode = cacheMode
277		vfs.cancelCache = cancel
278		vfs.cache = cache
279	}
280}
281
282// shutdown the cache if it was running
283func (vfs *VFS) shutdownCache() {
284	if vfs.cancelCache != nil {
285		vfs.cancelCache()
286		vfs.cancelCache = nil
287	}
288}
289
290// Shutdown stops any background go-routines and removes the VFS from
291// the active ache.
292func (vfs *VFS) Shutdown() {
293	if atomic.AddInt32(&vfs.inUse, -1) > 0 {
294		return
295	}
296
297	// Remove from active cache
298	activeMu.Lock()
299	configName := fs.ConfigString(vfs.f)
300	activeVFSes := active[configName]
301	for i, activeVFS := range activeVFSes {
302		if activeVFS == vfs {
303			activeVFSes[i] = nil
304			active[configName] = append(activeVFSes[:i], activeVFSes[i+1:]...)
305			break
306		}
307	}
308	activeMu.Unlock()
309
310	vfs.shutdownCache()
311}
312
313// CleanUp deletes the contents of the on disk cache
314func (vfs *VFS) CleanUp() error {
315	if vfs.Opt.CacheMode == vfscommon.CacheModeOff {
316		return nil
317	}
318	return vfs.cache.CleanUp()
319}
320
321// FlushDirCache empties the directory cache
322func (vfs *VFS) FlushDirCache() {
323	vfs.root.ForgetAll()
324}
325
326// WaitForWriters sleeps until all writers have finished or
327// time.Duration has elapsed
328func (vfs *VFS) WaitForWriters(timeout time.Duration) {
329	defer log.Trace(nil, "timeout=%v", timeout)("")
330	tickTime := 10 * time.Millisecond
331	deadline := time.NewTimer(timeout)
332	defer deadline.Stop()
333	tick := time.NewTimer(tickTime)
334	defer tick.Stop()
335	tick.Stop()
336	for {
337		writers := vfs.root.countActiveWriters()
338		cacheInUse := 0
339		if vfs.cache != nil {
340			cacheInUse = vfs.cache.TotalInUse()
341		}
342		if writers == 0 && cacheInUse == 0 {
343			return
344		}
345		fs.Debugf(nil, "Still %d writers active and %d cache items in use, waiting %v", writers, cacheInUse, tickTime)
346		tick.Reset(tickTime)
347		select {
348		case <-tick.C:
349			break
350		case <-deadline.C:
351			fs.Errorf(nil, "Exiting even though %d writers active and %d cache items in use after %v\n%s", writers, cacheInUse, timeout, vfs.cache.Dump())
352			return
353		}
354		tickTime *= 2
355		if tickTime > time.Second {
356			tickTime = time.Second
357		}
358	}
359}
360
361// Root returns the root node
362func (vfs *VFS) Root() (*Dir, error) {
363	// fs.Debugf(vfs.f, "Root()")
364	return vfs.root, nil
365}
366
367var inodeCount uint64
368
369// newInode creates a new unique inode number
370func newInode() (inode uint64) {
371	return atomic.AddUint64(&inodeCount, 1)
372}
373
374// Stat finds the Node by path starting from the root
375//
376// It is the equivalent of os.Stat - Node contains the os.FileInfo
377// interface.
378func (vfs *VFS) Stat(path string) (node Node, err error) {
379	path = strings.Trim(path, "/")
380	node = vfs.root
381	for path != "" {
382		i := strings.IndexRune(path, '/')
383		var name string
384		if i < 0 {
385			name, path = path, ""
386		} else {
387			name, path = path[:i], path[i+1:]
388		}
389		if name == "" {
390			continue
391		}
392		dir, ok := node.(*Dir)
393		if !ok {
394			// We need to look in a directory, but found a file
395			return nil, ENOENT
396		}
397		node, err = dir.Stat(name)
398		if err != nil {
399			return nil, err
400		}
401	}
402	return
403}
404
405// StatParent finds the parent directory and the leaf name of a path
406func (vfs *VFS) StatParent(name string) (dir *Dir, leaf string, err error) {
407	name = strings.Trim(name, "/")
408	parent, leaf := path.Split(name)
409	node, err := vfs.Stat(parent)
410	if err != nil {
411		return nil, "", err
412	}
413	if node.IsFile() {
414		return nil, "", os.ErrExist
415	}
416	dir = node.(*Dir)
417	return dir, leaf, nil
418}
419
420// decodeOpenFlags returns a string representing the open flags
421func decodeOpenFlags(flags int) string {
422	var out []string
423	rdwrMode := flags & accessModeMask
424	switch rdwrMode {
425	case os.O_RDONLY:
426		out = append(out, "O_RDONLY")
427	case os.O_WRONLY:
428		out = append(out, "O_WRONLY")
429	case os.O_RDWR:
430		out = append(out, "O_RDWR")
431	default:
432		out = append(out, fmt.Sprintf("0x%X", rdwrMode))
433	}
434	if flags&os.O_APPEND != 0 {
435		out = append(out, "O_APPEND")
436	}
437	if flags&os.O_CREATE != 0 {
438		out = append(out, "O_CREATE")
439	}
440	if flags&os.O_EXCL != 0 {
441		out = append(out, "O_EXCL")
442	}
443	if flags&os.O_SYNC != 0 {
444		out = append(out, "O_SYNC")
445	}
446	if flags&os.O_TRUNC != 0 {
447		out = append(out, "O_TRUNC")
448	}
449	flags &^= accessModeMask | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_SYNC | os.O_TRUNC
450	if flags != 0 {
451		out = append(out, fmt.Sprintf("0x%X", flags))
452	}
453	return strings.Join(out, "|")
454}
455
456// OpenFile a file according to the flags and perm provided
457func (vfs *VFS) OpenFile(name string, flags int, perm os.FileMode) (fd Handle, err error) {
458	defer log.Trace(name, "flags=%s, perm=%v", decodeOpenFlags(flags), perm)("fd=%v, err=%v", &fd, &err)
459
460	// http://pubs.opengroup.org/onlinepubs/7908799/xsh/open.html
461	// The result of using O_TRUNC with O_RDONLY is undefined.
462	// Linux seems to truncate the file, but we prefer to return EINVAL
463	if flags&accessModeMask == os.O_RDONLY && flags&os.O_TRUNC != 0 {
464		return nil, EINVAL
465	}
466
467	node, err := vfs.Stat(name)
468	if err != nil {
469		if err != ENOENT || flags&os.O_CREATE == 0 {
470			return nil, err
471		}
472		// If not found and O_CREATE then create the file
473		dir, leaf, err := vfs.StatParent(name)
474		if err != nil {
475			return nil, err
476		}
477		node, err = dir.Create(leaf, flags)
478		if err != nil {
479			return nil, err
480		}
481	}
482	return node.Open(flags)
483}
484
485// Open opens the named file for reading. If successful, methods on
486// the returned file can be used for reading; the associated file
487// descriptor has mode O_RDONLY.
488func (vfs *VFS) Open(name string) (Handle, error) {
489	return vfs.OpenFile(name, os.O_RDONLY, 0)
490}
491
492// Create creates the named file with mode 0666 (before umask), truncating
493// it if it already exists. If successful, methods on the returned
494// File can be used for I/O; the associated file descriptor has mode
495// O_RDWR.
496func (vfs *VFS) Create(name string) (Handle, error) {
497	return vfs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
498}
499
500// Rename oldName to newName
501func (vfs *VFS) Rename(oldName, newName string) error {
502	// find the parent directories
503	oldDir, oldLeaf, err := vfs.StatParent(oldName)
504	if err != nil {
505		return err
506	}
507	newDir, newLeaf, err := vfs.StatParent(newName)
508	if err != nil {
509		return err
510	}
511	err = oldDir.Rename(oldLeaf, newLeaf, newDir)
512	if err != nil {
513		return err
514	}
515	return nil
516}
517
518// This works out the missing values from (total, used, free) using
519// unknownFree as the intended free space
520func fillInMissingSizes(total, used, free, unknownFree int64) (newTotal, newUsed, newFree int64) {
521	if total < 0 {
522		if free >= 0 {
523			total = free
524		} else {
525			total = unknownFree
526		}
527		if used >= 0 {
528			total += used
529		}
530	}
531	// total is now defined
532	if used < 0 {
533		if free >= 0 {
534			used = total - free
535		} else {
536			used = 0
537		}
538	}
539	// used is now defined
540	if free < 0 {
541		free = total - used
542	}
543	return total, used, free
544}
545
546// If the total size isn't known then we will aim for this many bytes free (1 PiB)
547const unknownFreeBytes = 1 << 50
548
549// Statfs returns into about the filing system if known
550//
551// The values will be -1 if they aren't known
552//
553// This information is cached for the DirCacheTime interval
554func (vfs *VFS) Statfs() (total, used, free int64) {
555	// defer log.Trace("/", "")("total=%d, used=%d, free=%d", &total, &used, &free)
556	vfs.usageMu.Lock()
557	defer vfs.usageMu.Unlock()
558	total, used, free = -1, -1, -1
559	doAbout := vfs.f.Features().About
560	if (doAbout != nil || vfs.Opt.UsedIsSize) && (vfs.usageTime.IsZero() || time.Since(vfs.usageTime) >= vfs.Opt.DirCacheTime) {
561		var err error
562		ctx := context.TODO()
563		if doAbout == nil {
564			vfs.usage = &fs.Usage{}
565		} else {
566			vfs.usage, err = doAbout(ctx)
567		}
568		if vfs.Opt.UsedIsSize {
569			var usedBySizeAlgorithm int64
570			// Algorithm from `rclone size`
571			err = walk.ListR(ctx, vfs.f, "", true, -1, walk.ListObjects, func(entries fs.DirEntries) error {
572				entries.ForObject(func(o fs.Object) {
573					usedBySizeAlgorithm += o.Size()
574				})
575				return nil
576			})
577			vfs.usage.Used = &usedBySizeAlgorithm
578		}
579		vfs.usageTime = time.Now()
580		if err != nil {
581			fs.Errorf(vfs.f, "Statfs failed: %v", err)
582			return
583		}
584	}
585	if u := vfs.usage; u != nil {
586		if u.Total != nil {
587			total = *u.Total
588		}
589		if u.Free != nil {
590			free = *u.Free
591		}
592		if u.Used != nil {
593			used = *u.Used
594		}
595	}
596	total, used, free = fillInMissingSizes(total, used, free, unknownFreeBytes)
597	return
598}
599
600// Remove removes the named file or (empty) directory.
601func (vfs *VFS) Remove(name string) error {
602	node, err := vfs.Stat(name)
603	if err != nil {
604		return err
605	}
606	err = node.Remove()
607	if err != nil {
608		return err
609	}
610	return nil
611}
612
613// Chtimes changes the access and modification times of the named file, similar
614// to the Unix utime() or utimes() functions.
615//
616// The underlying filesystem may truncate or round the values to a less precise
617// time unit.
618func (vfs *VFS) Chtimes(name string, atime time.Time, mtime time.Time) error {
619	node, err := vfs.Stat(name)
620	if err != nil {
621		return err
622	}
623	err = node.SetModTime(mtime)
624	if err != nil {
625		return err
626	}
627	return nil
628}
629
630// Mkdir creates a new directory with the specified name and permission bits
631// (before umask).
632func (vfs *VFS) Mkdir(name string, perm os.FileMode) error {
633	dir, leaf, err := vfs.StatParent(name)
634	if err != nil {
635		return err
636	}
637	_, err = dir.Mkdir(leaf)
638	if err != nil {
639		return err
640	}
641	return nil
642}
643
644// ReadDir reads the directory named by dirname and returns
645// a list of directory entries sorted by filename.
646func (vfs *VFS) ReadDir(dirname string) ([]os.FileInfo, error) {
647	f, err := vfs.Open(dirname)
648	if err != nil {
649		return nil, err
650	}
651	list, err := f.Readdir(-1)
652	closeErr := f.Close()
653	if err != nil {
654		return nil, err
655	}
656	if closeErr != nil {
657		return nil, closeErr
658	}
659	sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })
660	return list, nil
661}
662
663// ReadFile reads the file named by filename and returns the contents.
664// A successful call returns err == nil, not err == EOF. Because ReadFile
665// reads the whole file, it does not treat an EOF from Read as an error
666// to be reported.
667func (vfs *VFS) ReadFile(filename string) (b []byte, err error) {
668	f, err := vfs.Open(filename)
669	if err != nil {
670		return nil, err
671	}
672	defer fs.CheckClose(f, &err)
673	return ioutil.ReadAll(f)
674}
675
676// AddVirtual adds the object (file or dir) to the directory cache
677func (vfs *VFS) AddVirtual(remote string, size int64, isDir bool) error {
678	dir, leaf, err := vfs.StatParent(remote)
679	if err != nil {
680		return err
681	}
682	dir.AddVirtual(leaf, size, false)
683	return nil
684}
685