2017-10-28 21:01:34 +02:00
// Package vfs provides a virtual filing system layer over rclone's
// native objects.
//
// It attempts to behave in a similar way to Go's filing system
// manipulation code in the os package. The same named function
// should behave in an identical fashion. The objects also obey Go's
// standard interfaces.
//
// It also includes directory caching
package vfs
2017-05-02 23:35:07 +02:00
import (
2017-05-09 12:29:02 +02:00
"fmt"
2017-10-25 11:00:26 +02:00
"os"
2017-05-02 23:35:07 +02:00
"strings"
"sync/atomic"
"time"
"github.com/ncw/rclone/fs"
2017-10-28 21:01:34 +02:00
"github.com/spf13/pflag"
2017-05-02 23:35:07 +02:00
)
2017-10-28 21:01:34 +02:00
// Options set by command line flags
var (
NoModTime = false
NoChecksum = false
NoSeek = false
DirCacheTime = 5 * 60 * time . Second
PollInterval = time . Minute
// mount options
ReadOnly = false
Umask = 0
UID = ^ uint32 ( 0 ) // these values instruct WinFSP-FUSE to use the current user
GID = ^ uint32 ( 0 ) // overriden for non windows in mount_unix.go
// foreground = false
// default permissions for directories - modified by umask in New
DirPerms = os . FileMode ( 0777 )
FilePerms = os . FileMode ( 0666 )
)
// Node represents either a directory (*Dir) or a file (*File)
2017-05-02 23:35:07 +02:00
type Node interface {
2017-10-25 11:00:26 +02:00
os . FileInfo
2017-05-02 23:35:07 +02:00
IsFile ( ) bool
Inode ( ) uint64
2017-10-25 11:00:26 +02:00
SetModTime ( modTime time . Time ) error
Fsync ( ) error
2017-10-26 17:55:40 +02:00
Remove ( ) error
RemoveAll ( ) error
2017-10-26 18:02:48 +02:00
DirEntry ( ) fs . DirEntry
2017-05-02 23:35:07 +02:00
}
2017-10-28 21:01:34 +02:00
// Check interfaces
2017-05-02 23:35:07 +02:00
var (
_ Node = ( * File ) ( nil )
_ Node = ( * Dir ) ( nil )
)
2017-10-27 23:07:59 +02:00
// Nodes is a slice of Node
type Nodes [ ] Node
// Sort functions
func ( ns Nodes ) Len ( ) int { return len ( ns ) }
func ( ns Nodes ) Swap ( i , j int ) { ns [ i ] , ns [ j ] = ns [ j ] , ns [ i ] }
func ( ns Nodes ) Less ( i , j int ) bool { return ns [ i ] . DirEntry ( ) . Remote ( ) < ns [ j ] . DirEntry ( ) . Remote ( ) }
2017-05-02 23:35:07 +02:00
// Noder represents something which can return a node
type Noder interface {
2017-05-09 12:29:02 +02:00
fmt . Stringer
2017-05-02 23:35:07 +02:00
Node ( ) Node
}
2017-10-28 21:01:34 +02:00
// Check interfaces
2017-05-02 23:35:07 +02:00
var (
_ Noder = ( * File ) ( nil )
_ Noder = ( * Dir ) ( nil )
_ Noder = ( * ReadFileHandle ) ( nil )
_ Noder = ( * WriteFileHandle ) ( nil )
)
2017-10-28 21:01:34 +02:00
// VFS represents the top level filing system
type VFS struct {
2017-05-31 16:55:46 +02:00
f fs . Fs
root * Dir
noSeek bool // don't allow seeking if set
noChecksum bool // don't check checksums if set
2017-10-28 21:01:34 +02:00
readOnly bool // if set VFS is read only
2017-10-25 11:00:26 +02:00
noModTime bool // don't read mod times for files
2017-05-31 16:55:46 +02:00
dirCacheTime time . Duration // how long to consider directory listing cache valid
2017-05-02 23:35:07 +02:00
}
2017-10-28 21:01:34 +02:00
// New creates a new VFS and root directory
func New ( f fs . Fs ) * VFS {
2017-06-30 14:37:29 +02:00
fsDir := fs . NewDir ( "" , time . Now ( ) )
2017-10-28 21:01:34 +02:00
vfs := & VFS {
2017-05-02 23:35:07 +02:00
f : f ,
}
2017-05-25 23:05:49 +02:00
2017-10-28 21:01:34 +02:00
// Mask permissions
DirPerms = 0777 &^ os . FileMode ( Umask )
FilePerms = 0666 &^ os . FileMode ( Umask )
2017-06-19 14:44:49 +02:00
if NoSeek {
2017-10-28 21:01:34 +02:00
vfs . noSeek = true
2017-06-19 14:44:49 +02:00
}
if NoChecksum {
2017-10-28 21:01:34 +02:00
vfs . noChecksum = true
2017-06-19 14:44:49 +02:00
}
if ReadOnly {
2017-10-28 21:01:34 +02:00
vfs . readOnly = true
2017-06-19 14:44:49 +02:00
}
2017-10-25 11:00:26 +02:00
if NoModTime {
2017-10-28 21:01:34 +02:00
vfs . noModTime = true
2017-10-25 11:00:26 +02:00
}
2017-10-28 21:01:34 +02:00
vfs . dirCacheTime = DirCacheTime
2017-05-25 23:05:49 +02:00
2017-10-28 21:01:34 +02:00
vfs . root = newDir ( vfs , f , nil , fsDir )
2017-05-25 23:05:49 +02:00
2017-06-19 14:44:49 +02:00
if PollInterval > 0 {
2017-10-28 21:01:34 +02:00
vfs . PollChanges ( PollInterval )
2017-06-19 14:44:49 +02:00
}
2017-10-28 21:01:34 +02:00
return vfs
2017-05-31 16:55:46 +02:00
}
2017-05-25 23:05:49 +02:00
// PollChanges will poll the remote every pollInterval for changes if the remote
// supports it. If a non-polling option is used, the given time interval can be
// ignored
2017-10-28 21:01:34 +02:00
func ( vfs * VFS ) PollChanges ( pollInterval time . Duration ) * VFS {
doDirChangeNotify := vfs . f . Features ( ) . DirChangeNotify
2017-05-25 23:05:49 +02:00
if doDirChangeNotify != nil {
2017-10-28 21:01:34 +02:00
doDirChangeNotify ( vfs . root . ForgetPath , pollInterval )
2017-05-25 23:05:49 +02:00
}
2017-10-28 21:01:34 +02:00
return vfs
2017-05-02 23:35:07 +02:00
}
// Root returns the root node
2017-10-28 21:01:34 +02:00
func ( vfs * VFS ) Root ( ) ( * Dir , error ) {
// fs.Debugf(vfs.f, "Root()")
return vfs . root , nil
2017-05-02 23:35:07 +02:00
}
var inodeCount uint64
// NewInode creates a new unique inode number
func NewInode ( ) ( inode uint64 ) {
return atomic . AddUint64 ( & inodeCount , 1 )
}
// Lookup finds the Node by path starting from the root
2017-10-28 21:01:34 +02:00
func ( vfs * VFS ) Lookup ( path string ) ( node Node , err error ) {
node = vfs . root
2017-05-02 23:35:07 +02:00
for path != "" {
i := strings . IndexRune ( path , '/' )
var name string
if i < 0 {
name , path = path , ""
} else {
name , path = path [ : i ] , path [ i + 1 : ]
}
if name == "" {
continue
}
dir , ok := node . ( * Dir )
if ! ok {
// We need to look in a directory, but found a file
return nil , ENOENT
}
node , err = dir . Lookup ( name )
if err != nil {
return nil , err
}
}
return
}
// Statfs is called to obtain file system metadata.
// It should write that data to resp.
2017-10-28 21:01:34 +02:00
func ( vfs * VFS ) Statfs ( ) error {
2017-05-02 23:35:07 +02:00
/ * FIXME
const blockSize = 4096
const fsBlocks = ( 1 << 50 ) / blockSize
resp . Blocks = fsBlocks // Total data blocks in file system.
resp . Bfree = fsBlocks // Free blocks in file system.
resp . Bavail = fsBlocks // Free blocks in file system if you're not root.
resp . Files = 1E9 // Total files in file system.
resp . Ffree = 1E9 // Free files in file system.
resp . Bsize = blockSize // Block size
resp . Namelen = 255 // Maximum file name length?
resp . Frsize = blockSize // Fragment size, smallest addressable data size in the file system.
* /
return nil
}
2017-10-28 21:01:34 +02:00
// AddFlags adds the non filing system specific flags to the command
func AddFlags ( flags * pflag . FlagSet ) {
flags . BoolVarP ( & NoModTime , "no-modtime" , "" , NoModTime , "Don't read/write the modification time (can speed things up)." )
flags . BoolVarP ( & NoChecksum , "no-checksum" , "" , NoChecksum , "Don't compare checksums on up/download." )
flags . BoolVarP ( & NoSeek , "no-seek" , "" , NoSeek , "Don't allow seeking in files." )
flags . DurationVarP ( & DirCacheTime , "dir-cache-time" , "" , DirCacheTime , "Time to cache directory entries for." )
flags . DurationVarP ( & PollInterval , "poll-interval" , "" , PollInterval , "Time to wait between polling for changes. Must be smaller than dir-cache-time. Only on supported remotes. Set to 0 to disable." )
flags . BoolVarP ( & ReadOnly , "read-only" , "" , ReadOnly , "Mount read-only." )
platformFlags ( flags )
}