forked from TrueCloudLab/rclone
Factor new vfs module out of cmd/mountlib
This is an OS style file system abstraction with directory caching used in mount, cmount, serve webdav and serve http.
This commit is contained in:
parent
6da6b2556b
commit
c1aaff220d
20 changed files with 197 additions and 200 deletions
|
@ -12,8 +12,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/billziss-gh/cgofuse/fuse"
|
"github.com/billziss-gh/cgofuse/fuse"
|
||||||
"github.com/ncw/rclone/cmd/mountlib"
|
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/ncw/rclone/vfs"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ const fhUnset = ^uint64(0)
|
||||||
|
|
||||||
// FS represents the top level filing system
|
// FS represents the top level filing system
|
||||||
type FS struct {
|
type FS struct {
|
||||||
FS *mountlib.FS
|
VFS *vfs.VFS
|
||||||
f fs.Fs
|
f fs.Fs
|
||||||
openDirs *openFiles
|
openDirs *openFiles
|
||||||
openFilesWr *openFiles
|
openFilesWr *openFiles
|
||||||
|
@ -32,7 +32,7 @@ type FS struct {
|
||||||
// NewFS makes a new FS
|
// NewFS makes a new FS
|
||||||
func NewFS(f fs.Fs) *FS {
|
func NewFS(f fs.Fs) *FS {
|
||||||
fsys := &FS{
|
fsys := &FS{
|
||||||
FS: mountlib.NewFS(f),
|
VFS: vfs.New(f),
|
||||||
f: f,
|
f: f,
|
||||||
openDirs: newOpenFiles(0x01),
|
openDirs: newOpenFiles(0x01),
|
||||||
openFilesWr: newOpenFiles(0x02),
|
openFilesWr: newOpenFiles(0x02),
|
||||||
|
@ -45,7 +45,7 @@ func NewFS(f fs.Fs) *FS {
|
||||||
type openFiles struct {
|
type openFiles struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
mark uint8
|
mark uint8
|
||||||
nodes []mountlib.Noder
|
nodes []vfs.Noder
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOpenFiles(mark uint8) *openFiles {
|
func newOpenFiles(mark uint8) *openFiles {
|
||||||
|
@ -55,11 +55,11 @@ func newOpenFiles(mark uint8) *openFiles {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open a node returning a file handle
|
// Open a node returning a file handle
|
||||||
func (of *openFiles) Open(node mountlib.Noder) (fh uint64) {
|
func (of *openFiles) Open(node vfs.Noder) (fh uint64) {
|
||||||
of.mu.Lock()
|
of.mu.Lock()
|
||||||
defer of.mu.Unlock()
|
defer of.mu.Unlock()
|
||||||
var i int
|
var i int
|
||||||
var oldNode mountlib.Noder
|
var oldNode vfs.Noder
|
||||||
for i, oldNode = range of.nodes {
|
for i, oldNode = range of.nodes {
|
||||||
if oldNode == nil {
|
if oldNode == nil {
|
||||||
of.nodes[i] = node
|
of.nodes[i] = node
|
||||||
|
@ -78,7 +78,7 @@ func (of *openFiles) InRange(fh uint64) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the node for fh, call with the lock held
|
// get the node for fh, call with the lock held
|
||||||
func (of *openFiles) get(fh uint64) (i int, node mountlib.Noder, errc int) {
|
func (of *openFiles) get(fh uint64) (i int, node vfs.Noder, errc int) {
|
||||||
receivedMark := uint8(fh)
|
receivedMark := uint8(fh)
|
||||||
if receivedMark != of.mark {
|
if receivedMark != of.mark {
|
||||||
fs.Debugf(nil, "Bad file handle: bad mark 0x%X != 0x%X: 0x%X", receivedMark, of.mark, fh)
|
fs.Debugf(nil, "Bad file handle: bad mark 0x%X != 0x%X: 0x%X", receivedMark, of.mark, fh)
|
||||||
|
@ -99,7 +99,7 @@ func (of *openFiles) get(fh uint64) (i int, node mountlib.Noder, errc int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the node for the file handle
|
// Get the node for the file handle
|
||||||
func (of *openFiles) Get(fh uint64) (node mountlib.Noder, errc int) {
|
func (of *openFiles) Get(fh uint64) (node vfs.Noder, errc int) {
|
||||||
of.mu.Lock()
|
of.mu.Lock()
|
||||||
_, node, errc = of.get(fh)
|
_, node, errc = of.get(fh)
|
||||||
of.mu.Unlock()
|
of.mu.Unlock()
|
||||||
|
@ -118,18 +118,18 @@ func (of *openFiles) Close(fh uint64) (errc int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookup a Node given a path
|
// lookup a Node given a path
|
||||||
func (fsys *FS) lookupNode(path string) (node mountlib.Node, errc int) {
|
func (fsys *FS) lookupNode(path string) (node vfs.Node, errc int) {
|
||||||
node, err := fsys.FS.Lookup(path)
|
node, err := fsys.VFS.Lookup(path)
|
||||||
return node, translateError(err)
|
return node, translateError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookup a Dir given a path
|
// lookup a Dir given a path
|
||||||
func (fsys *FS) lookupDir(path string) (dir *mountlib.Dir, errc int) {
|
func (fsys *FS) lookupDir(path string) (dir *vfs.Dir, errc int) {
|
||||||
node, errc := fsys.lookupNode(path)
|
node, errc := fsys.lookupNode(path)
|
||||||
if errc != 0 {
|
if errc != 0 {
|
||||||
return nil, errc
|
return nil, errc
|
||||||
}
|
}
|
||||||
dir, ok := node.(*mountlib.Dir)
|
dir, ok := node.(*vfs.Dir)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, -fuse.ENOTDIR
|
return nil, -fuse.ENOTDIR
|
||||||
}
|
}
|
||||||
|
@ -137,19 +137,19 @@ func (fsys *FS) lookupDir(path string) (dir *mountlib.Dir, errc int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookup a parent Dir given a path returning the dir and the leaf
|
// lookup a parent Dir given a path returning the dir and the leaf
|
||||||
func (fsys *FS) lookupParentDir(filePath string) (leaf string, dir *mountlib.Dir, errc int) {
|
func (fsys *FS) lookupParentDir(filePath string) (leaf string, dir *vfs.Dir, errc int) {
|
||||||
parentDir, leaf := path.Split(filePath)
|
parentDir, leaf := path.Split(filePath)
|
||||||
dir, errc = fsys.lookupDir(parentDir)
|
dir, errc = fsys.lookupDir(parentDir)
|
||||||
return leaf, dir, errc
|
return leaf, dir, errc
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookup a File given a path
|
// lookup a File given a path
|
||||||
func (fsys *FS) lookupFile(path string) (file *mountlib.File, errc int) {
|
func (fsys *FS) lookupFile(path string) (file *vfs.File, errc int) {
|
||||||
node, errc := fsys.lookupNode(path)
|
node, errc := fsys.lookupNode(path)
|
||||||
if errc != 0 {
|
if errc != 0 {
|
||||||
return nil, errc
|
return nil, errc
|
||||||
}
|
}
|
||||||
file, ok := node.(*mountlib.File)
|
file, ok := node.(*vfs.File)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, -fuse.EISDIR
|
return nil, -fuse.EISDIR
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@ func (fsys *FS) getOpenFilesFromFh(fh uint64) (of *openFiles, errc int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the underlying handle from the file handle
|
// Get the underlying handle from the file handle
|
||||||
func (fsys *FS) getHandleFromFh(fh uint64) (handle mountlib.Noder, errc int) {
|
func (fsys *FS) getHandleFromFh(fh uint64) (handle vfs.Noder, errc int) {
|
||||||
of, errc := fsys.getOpenFilesFromFh(fh)
|
of, errc := fsys.getOpenFilesFromFh(fh)
|
||||||
if errc != 0 {
|
if errc != 0 {
|
||||||
return nil, errc
|
return nil, errc
|
||||||
|
@ -179,11 +179,11 @@ func (fsys *FS) getHandleFromFh(fh uint64) (handle mountlib.Noder, errc int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get a node from the path or from the fh if not fhUnset
|
// get a node from the path or from the fh if not fhUnset
|
||||||
func (fsys *FS) getNode(path string, fh uint64) (node mountlib.Node, errc int) {
|
func (fsys *FS) getNode(path string, fh uint64) (node vfs.Node, errc int) {
|
||||||
if fh == fhUnset {
|
if fh == fhUnset {
|
||||||
node, errc = fsys.lookupNode(path)
|
node, errc = fsys.lookupNode(path)
|
||||||
} else {
|
} else {
|
||||||
var n mountlib.Noder
|
var n vfs.Noder
|
||||||
n, errc = fsys.getHandleFromFh(fh)
|
n, errc = fsys.getHandleFromFh(fh)
|
||||||
if errc == 0 {
|
if errc == 0 {
|
||||||
node = n.Node()
|
node = n.Node()
|
||||||
|
@ -193,27 +193,27 @@ func (fsys *FS) getNode(path string, fh uint64) (node mountlib.Node, errc int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// stat fills up the stat block for Node
|
// stat fills up the stat block for Node
|
||||||
func (fsys *FS) stat(node mountlib.Node, stat *fuse.Stat_t) (errc int) {
|
func (fsys *FS) stat(node vfs.Node, stat *fuse.Stat_t) (errc int) {
|
||||||
var Size uint64
|
var Size uint64
|
||||||
var Blocks uint64
|
var Blocks uint64
|
||||||
var modTime time.Time
|
var modTime time.Time
|
||||||
var Mode os.FileMode
|
var Mode os.FileMode
|
||||||
switch x := node.(type) {
|
switch x := node.(type) {
|
||||||
case *mountlib.Dir:
|
case *vfs.Dir:
|
||||||
modTime = x.ModTime()
|
modTime = x.ModTime()
|
||||||
Mode = mountlib.DirPerms | fuse.S_IFDIR
|
Mode = vfs.DirPerms | fuse.S_IFDIR
|
||||||
case *mountlib.File:
|
case *vfs.File:
|
||||||
modTime = x.ModTime()
|
modTime = x.ModTime()
|
||||||
Size = uint64(x.Size())
|
Size = uint64(x.Size())
|
||||||
Blocks = (Size + 511) / 512
|
Blocks = (Size + 511) / 512
|
||||||
Mode = mountlib.FilePerms | fuse.S_IFREG
|
Mode = vfs.FilePerms | fuse.S_IFREG
|
||||||
}
|
}
|
||||||
//stat.Dev = 1
|
//stat.Dev = 1
|
||||||
stat.Ino = node.Inode() // FIXME do we need to set the inode number?
|
stat.Ino = node.Inode() // FIXME do we need to set the inode number?
|
||||||
stat.Mode = uint32(Mode)
|
stat.Mode = uint32(Mode)
|
||||||
stat.Nlink = 1
|
stat.Nlink = 1
|
||||||
stat.Uid = mountlib.UID
|
stat.Uid = vfs.UID
|
||||||
stat.Gid = mountlib.GID
|
stat.Gid = vfs.GID
|
||||||
//stat.Rdev
|
//stat.Rdev
|
||||||
stat.Size = int64(Size)
|
stat.Size = int64(Size)
|
||||||
t := fuse.NewTimespec(modTime)
|
t := fuse.NewTimespec(modTime)
|
||||||
|
@ -275,7 +275,7 @@ func (fsys *FS) Readdir(dirPath string,
|
||||||
return errc
|
return errc
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, ok := node.(*mountlib.Dir)
|
dir, ok := node.(*vfs.Dir)
|
||||||
if !ok {
|
if !ok {
|
||||||
return -fuse.ENOTDIR
|
return -fuse.ENOTDIR
|
||||||
}
|
}
|
||||||
|
@ -342,7 +342,7 @@ func (fsys *FS) Open(path string, flags int) (errc int, fh uint64) {
|
||||||
}
|
}
|
||||||
rdwrMode := flags & fuse.O_ACCMODE
|
rdwrMode := flags & fuse.O_ACCMODE
|
||||||
var err error
|
var err error
|
||||||
var handle mountlib.Noder
|
var handle vfs.Noder
|
||||||
switch {
|
switch {
|
||||||
case rdwrMode == fuse.O_RDONLY:
|
case rdwrMode == fuse.O_RDONLY:
|
||||||
handle, err = file.OpenRead()
|
handle, err = file.OpenRead()
|
||||||
|
@ -385,7 +385,7 @@ func (fsys *FS) Truncate(path string, size int64, fh uint64) (errc int) {
|
||||||
if errc != 0 {
|
if errc != 0 {
|
||||||
return errc
|
return errc
|
||||||
}
|
}
|
||||||
file, ok := node.(*mountlib.File)
|
file, ok := node.(*vfs.File)
|
||||||
if !ok {
|
if !ok {
|
||||||
return -fuse.EIO
|
return -fuse.EIO
|
||||||
}
|
}
|
||||||
|
@ -406,7 +406,7 @@ func (fsys *FS) Read(path string, buff []byte, ofst int64, fh uint64) (n int) {
|
||||||
if errc != 0 {
|
if errc != 0 {
|
||||||
return errc
|
return errc
|
||||||
}
|
}
|
||||||
rfh, ok := handle.(*mountlib.ReadFileHandle)
|
rfh, ok := handle.(*vfs.ReadFileHandle)
|
||||||
if !ok {
|
if !ok {
|
||||||
// Can only read from read file handle
|
// Can only read from read file handle
|
||||||
return -fuse.EIO
|
return -fuse.EIO
|
||||||
|
@ -425,7 +425,7 @@ func (fsys *FS) Write(path string, buff []byte, ofst int64, fh uint64) (n int) {
|
||||||
if errc != 0 {
|
if errc != 0 {
|
||||||
return errc
|
return errc
|
||||||
}
|
}
|
||||||
wfh, ok := handle.(*mountlib.WriteFileHandle)
|
wfh, ok := handle.(*vfs.WriteFileHandle)
|
||||||
if !ok {
|
if !ok {
|
||||||
// Can only write to write file handle
|
// Can only write to write file handle
|
||||||
return -fuse.EIO
|
return -fuse.EIO
|
||||||
|
@ -446,9 +446,9 @@ func (fsys *FS) Flush(path string, fh uint64) (errc int) {
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
switch x := handle.(type) {
|
switch x := handle.(type) {
|
||||||
case *mountlib.ReadFileHandle:
|
case *vfs.ReadFileHandle:
|
||||||
err = x.Flush()
|
err = x.Flush()
|
||||||
case *mountlib.WriteFileHandle:
|
case *vfs.WriteFileHandle:
|
||||||
err = x.Flush()
|
err = x.Flush()
|
||||||
default:
|
default:
|
||||||
return -fuse.EIO
|
return -fuse.EIO
|
||||||
|
@ -470,9 +470,9 @@ func (fsys *FS) Release(path string, fh uint64) (errc int) {
|
||||||
_ = of.Close(fh)
|
_ = of.Close(fh)
|
||||||
var err error
|
var err error
|
||||||
switch x := handle.(type) {
|
switch x := handle.(type) {
|
||||||
case *mountlib.ReadFileHandle:
|
case *vfs.ReadFileHandle:
|
||||||
err = x.Release()
|
err = x.Release()
|
||||||
case *mountlib.WriteFileHandle:
|
case *vfs.WriteFileHandle:
|
||||||
err = x.Release()
|
err = x.Release()
|
||||||
default:
|
default:
|
||||||
return -fuse.EIO
|
return -fuse.EIO
|
||||||
|
@ -540,9 +540,9 @@ func (fsys *FS) Utimens(path string, tmsp []fuse.Timespec) (errc int) {
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
switch x := node.(type) {
|
switch x := node.(type) {
|
||||||
case *mountlib.Dir:
|
case *vfs.Dir:
|
||||||
err = x.SetModTime(t)
|
err = x.SetModTime(t)
|
||||||
case *mountlib.File:
|
case *vfs.File:
|
||||||
err = x.SetModTime(t)
|
err = x.SetModTime(t)
|
||||||
}
|
}
|
||||||
return translateError(err)
|
return translateError(err)
|
||||||
|
@ -633,21 +633,21 @@ func translateError(err error) (errc int) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
if mErr, ok := cause.(mountlib.Error); ok {
|
if mErr, ok := cause.(vfs.Error); ok {
|
||||||
switch mErr {
|
switch mErr {
|
||||||
case mountlib.OK:
|
case vfs.OK:
|
||||||
return 0
|
return 0
|
||||||
case mountlib.ENOENT:
|
case vfs.ENOENT:
|
||||||
return -fuse.ENOENT
|
return -fuse.ENOENT
|
||||||
case mountlib.ENOTEMPTY:
|
case vfs.ENOTEMPTY:
|
||||||
return -fuse.ENOTEMPTY
|
return -fuse.ENOTEMPTY
|
||||||
case mountlib.EEXIST:
|
case vfs.EEXIST:
|
||||||
return -fuse.EEXIST
|
return -fuse.EEXIST
|
||||||
case mountlib.ESPIPE:
|
case vfs.ESPIPE:
|
||||||
return -fuse.ESPIPE
|
return -fuse.ESPIPE
|
||||||
case mountlib.EBADF:
|
case vfs.EBADF:
|
||||||
return -fuse.EBADF
|
return -fuse.EBADF
|
||||||
case mountlib.EROFS:
|
case vfs.EROFS:
|
||||||
return -fuse.EROFS
|
return -fuse.EROFS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/billziss-gh/cgofuse/fuse"
|
"github.com/billziss-gh/cgofuse/fuse"
|
||||||
"github.com/ncw/rclone/cmd/mountlib"
|
"github.com/ncw/rclone/cmd/mountlib"
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/ncw/rclone/vfs"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ func mountOptions(device string, mountpoint string) (options []string) {
|
||||||
if mountlib.DefaultPermissions {
|
if mountlib.DefaultPermissions {
|
||||||
options = append(options, "-o", "default_permissions")
|
options = append(options, "-o", "default_permissions")
|
||||||
}
|
}
|
||||||
if mountlib.ReadOnly {
|
if vfs.ReadOnly {
|
||||||
options = append(options, "-o", "ro")
|
options = append(options, "-o", "ro")
|
||||||
}
|
}
|
||||||
if mountlib.WritebackCache {
|
if mountlib.WritebackCache {
|
||||||
|
@ -90,7 +91,7 @@ func mountOptions(device string, mountpoint string) (options []string) {
|
||||||
//
|
//
|
||||||
// returns an error, and an error channel for the serve process to
|
// returns an error, and an error channel for the serve process to
|
||||||
// report an error when fusermount is called.
|
// report an error when fusermount is called.
|
||||||
func mount(f fs.Fs, mountpoint string) (*mountlib.FS, <-chan error, func() error, error) {
|
func mount(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, error) {
|
||||||
fs.Debugf(f, "Mounting on %q", mountpoint)
|
fs.Debugf(f, "Mounting on %q", mountpoint)
|
||||||
|
|
||||||
// Check the mountpoint - in Windows the mountpoint musn't exist before the mount
|
// Check the mountpoint - in Windows the mountpoint musn't exist before the mount
|
||||||
|
@ -160,7 +161,7 @@ func mount(f fs.Fs, mountpoint string) (*mountlib.FS, <-chan error, func() error
|
||||||
found:
|
found:
|
||||||
}
|
}
|
||||||
|
|
||||||
return fsys.FS, errChan, unmount, nil
|
return fsys.VFS, errChan, unmount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mount mounts the remote at mountpoint.
|
// Mount mounts the remote at mountpoint.
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
|
|
||||||
"bazil.org/fuse"
|
"bazil.org/fuse"
|
||||||
fusefs "bazil.org/fuse/fs"
|
fusefs "bazil.org/fuse/fs"
|
||||||
"github.com/ncw/rclone/cmd/mountlib"
|
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/ncw/rclone/vfs"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
@ -27,13 +27,7 @@ type DirEntry struct {
|
||||||
|
|
||||||
// Dir represents a directory entry
|
// Dir represents a directory entry
|
||||||
type Dir struct {
|
type Dir struct {
|
||||||
*mountlib.Dir
|
*vfs.Dir
|
||||||
// f fs.Fs
|
|
||||||
// path string
|
|
||||||
// modTime time.Time
|
|
||||||
// mu sync.RWMutex // protects the following
|
|
||||||
// read time.Time // time directory entry last read
|
|
||||||
// items map[string]*DirEntry
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check interface satsified
|
// Check interface satsified
|
||||||
|
@ -42,9 +36,9 @@ var _ fusefs.Node = (*Dir)(nil)
|
||||||
// Attr updates the attributes of a directory
|
// Attr updates the attributes of a directory
|
||||||
func (d *Dir) Attr(ctx context.Context, a *fuse.Attr) (err error) {
|
func (d *Dir) Attr(ctx context.Context, a *fuse.Attr) (err error) {
|
||||||
defer fs.Trace(d, "")("attr=%+v, err=%v", a, &err)
|
defer fs.Trace(d, "")("attr=%+v, err=%v", a, &err)
|
||||||
a.Gid = mountlib.GID
|
a.Gid = vfs.GID
|
||||||
a.Uid = mountlib.UID
|
a.Uid = vfs.UID
|
||||||
a.Mode = os.ModeDir | mountlib.DirPerms
|
a.Mode = os.ModeDir | vfs.DirPerms
|
||||||
modTime := d.ModTime()
|
modTime := d.ModTime()
|
||||||
a.Atime = modTime
|
a.Atime = modTime
|
||||||
a.Mtime = modTime
|
a.Mtime = modTime
|
||||||
|
@ -61,7 +55,7 @@ var _ fusefs.NodeSetattrer = (*Dir)(nil)
|
||||||
// Setattr handles attribute changes from FUSE. Currently supports ModTime only.
|
// Setattr handles attribute changes from FUSE. Currently supports ModTime only.
|
||||||
func (d *Dir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) (err error) {
|
func (d *Dir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) (err error) {
|
||||||
defer fs.Trace(d, "stat=%+v", req)("err=%v", &err)
|
defer fs.Trace(d, "stat=%+v", req)("err=%v", &err)
|
||||||
if mountlib.NoModTime {
|
if vfs.NoModTime {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,9 +84,9 @@ func (d *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.Lo
|
||||||
return nil, translateError(err)
|
return nil, translateError(err)
|
||||||
}
|
}
|
||||||
switch x := mnode.(type) {
|
switch x := mnode.(type) {
|
||||||
case *mountlib.File:
|
case *vfs.File:
|
||||||
return &File{x}, nil
|
return &File{x}, nil
|
||||||
case *mountlib.Dir:
|
case *vfs.Dir:
|
||||||
return &Dir{x}, nil
|
return &Dir{x}, nil
|
||||||
}
|
}
|
||||||
panic("bad type")
|
panic("bad type")
|
||||||
|
|
|
@ -7,21 +7,15 @@ import (
|
||||||
|
|
||||||
"bazil.org/fuse"
|
"bazil.org/fuse"
|
||||||
fusefs "bazil.org/fuse/fs"
|
fusefs "bazil.org/fuse/fs"
|
||||||
"github.com/ncw/rclone/cmd/mountlib"
|
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/ncw/rclone/vfs"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// File represents a file
|
// File represents a file
|
||||||
type File struct {
|
type File struct {
|
||||||
*mountlib.File
|
*vfs.File
|
||||||
// size int64 // size of file - read and written with atomic int64 - must be 64 bit aligned
|
|
||||||
// d *Dir // parent directory - read only
|
|
||||||
// mu sync.RWMutex // protects the following
|
|
||||||
// o fs.Object // NB o may be nil if file is being written
|
|
||||||
// writers int // number of writers for this file
|
|
||||||
// pendingModTime time.Time // will be applied once o becomes available, i.e. after file was written
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check interface satisfied
|
// Check interface satisfied
|
||||||
|
@ -33,9 +27,9 @@ func (f *File) Attr(ctx context.Context, a *fuse.Attr) (err error) {
|
||||||
modTime := f.File.ModTime()
|
modTime := f.File.ModTime()
|
||||||
Size := uint64(f.File.Size())
|
Size := uint64(f.File.Size())
|
||||||
Blocks := (Size + 511) / 512
|
Blocks := (Size + 511) / 512
|
||||||
a.Gid = mountlib.GID
|
a.Gid = vfs.GID
|
||||||
a.Uid = mountlib.UID
|
a.Uid = vfs.UID
|
||||||
a.Mode = mountlib.FilePerms
|
a.Mode = vfs.FilePerms
|
||||||
a.Size = Size
|
a.Size = Size
|
||||||
a.Atime = modTime
|
a.Atime = modTime
|
||||||
a.Mtime = modTime
|
a.Mtime = modTime
|
||||||
|
@ -51,7 +45,7 @@ var _ fusefs.NodeSetattrer = (*File)(nil)
|
||||||
// Setattr handles attribute changes from FUSE. Currently supports ModTime only.
|
// Setattr handles attribute changes from FUSE. Currently supports ModTime only.
|
||||||
func (f *File) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) (err error) {
|
func (f *File) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) (err error) {
|
||||||
defer fs.Trace(f, "a=%+v", req)("err=%v", &err)
|
defer fs.Trace(f, "a=%+v", req)("err=%v", &err)
|
||||||
if mountlib.NoModTime {
|
if vfs.NoModTime {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if req.Valid.MtimeNow() {
|
if req.Valid.MtimeNow() {
|
||||||
|
@ -70,15 +64,15 @@ func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenR
|
||||||
defer fs.Trace(f, "flags=%v", req.Flags)("fh=%v, err=%v", &fh, &err)
|
defer fs.Trace(f, "flags=%v", req.Flags)("fh=%v, err=%v", &fh, &err)
|
||||||
switch {
|
switch {
|
||||||
case req.Flags.IsReadOnly():
|
case req.Flags.IsReadOnly():
|
||||||
if mountlib.NoSeek {
|
if vfs.NoSeek {
|
||||||
resp.Flags |= fuse.OpenNonSeekable
|
resp.Flags |= fuse.OpenNonSeekable
|
||||||
}
|
}
|
||||||
var rfh *mountlib.ReadFileHandle
|
var rfh *vfs.ReadFileHandle
|
||||||
rfh, err = f.File.OpenRead()
|
rfh, err = f.File.OpenRead()
|
||||||
fh = &ReadFileHandle{rfh}
|
fh = &ReadFileHandle{rfh}
|
||||||
case req.Flags.IsWriteOnly() || (req.Flags.IsReadWrite() && (req.Flags&fuse.OpenTruncate) != 0):
|
case req.Flags.IsWriteOnly() || (req.Flags.IsReadWrite() && (req.Flags&fuse.OpenTruncate) != 0):
|
||||||
resp.Flags |= fuse.OpenNonSeekable
|
resp.Flags |= fuse.OpenNonSeekable
|
||||||
var wfh *mountlib.WriteFileHandle
|
var wfh *vfs.WriteFileHandle
|
||||||
wfh, err = f.File.OpenWrite()
|
wfh, err = f.File.OpenWrite()
|
||||||
fh = &WriteFileHandle{wfh}
|
fh = &WriteFileHandle{wfh}
|
||||||
case req.Flags.IsReadWrite():
|
case req.Flags.IsReadWrite():
|
||||||
|
|
|
@ -9,15 +9,15 @@ import (
|
||||||
|
|
||||||
"bazil.org/fuse"
|
"bazil.org/fuse"
|
||||||
fusefs "bazil.org/fuse/fs"
|
fusefs "bazil.org/fuse/fs"
|
||||||
"github.com/ncw/rclone/cmd/mountlib"
|
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/ncw/rclone/vfs"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FS represents the top level filing system
|
// FS represents the top level filing system
|
||||||
type FS struct {
|
type FS struct {
|
||||||
*mountlib.FS
|
*vfs.VFS
|
||||||
f fs.Fs
|
f fs.Fs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ var _ fusefs.FS = (*FS)(nil)
|
||||||
// NewFS makes a new FS
|
// NewFS makes a new FS
|
||||||
func NewFS(f fs.Fs) *FS {
|
func NewFS(f fs.Fs) *FS {
|
||||||
fsys := &FS{
|
fsys := &FS{
|
||||||
FS: mountlib.NewFS(f),
|
VFS: vfs.New(f),
|
||||||
f: f,
|
f: f,
|
||||||
}
|
}
|
||||||
return fsys
|
return fsys
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ func NewFS(f fs.Fs) *FS {
|
||||||
// Root returns the root node
|
// Root returns the root node
|
||||||
func (f *FS) Root() (node fusefs.Node, err error) {
|
func (f *FS) Root() (node fusefs.Node, err error) {
|
||||||
defer fs.Trace("", "")("node=%+v, err=%v", &node, &err)
|
defer fs.Trace("", "")("node=%+v, err=%v", &node, &err)
|
||||||
root, err := f.FS.Root()
|
root, err := f.VFS.Root()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, translateError(err)
|
return nil, translateError(err)
|
||||||
}
|
}
|
||||||
|
@ -69,21 +69,21 @@ func translateError(err error) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
if mErr, ok := cause.(mountlib.Error); ok {
|
if mErr, ok := cause.(vfs.Error); ok {
|
||||||
switch mErr {
|
switch mErr {
|
||||||
case mountlib.OK:
|
case vfs.OK:
|
||||||
return nil
|
return nil
|
||||||
case mountlib.ENOENT:
|
case vfs.ENOENT:
|
||||||
return fuse.ENOENT
|
return fuse.ENOENT
|
||||||
case mountlib.ENOTEMPTY:
|
case vfs.ENOTEMPTY:
|
||||||
return fuse.Errno(syscall.ENOTEMPTY)
|
return fuse.Errno(syscall.ENOTEMPTY)
|
||||||
case mountlib.EEXIST:
|
case vfs.EEXIST:
|
||||||
return fuse.EEXIST
|
return fuse.EEXIST
|
||||||
case mountlib.ESPIPE:
|
case vfs.ESPIPE:
|
||||||
return fuse.Errno(syscall.ESPIPE)
|
return fuse.Errno(syscall.ESPIPE)
|
||||||
case mountlib.EBADF:
|
case vfs.EBADF:
|
||||||
return fuse.Errno(syscall.EBADF)
|
return fuse.Errno(syscall.EBADF)
|
||||||
case mountlib.EROFS:
|
case vfs.EROFS:
|
||||||
return fuse.Errno(syscall.EROFS)
|
return fuse.Errno(syscall.EROFS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
fusefs "bazil.org/fuse/fs"
|
fusefs "bazil.org/fuse/fs"
|
||||||
"github.com/ncw/rclone/cmd/mountlib"
|
"github.com/ncw/rclone/cmd/mountlib"
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/ncw/rclone/vfs"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ func mountOptions(device string) (options []fuse.MountOption) {
|
||||||
if mountlib.DefaultPermissions {
|
if mountlib.DefaultPermissions {
|
||||||
options = append(options, fuse.DefaultPermissions())
|
options = append(options, fuse.DefaultPermissions())
|
||||||
}
|
}
|
||||||
if mountlib.ReadOnly {
|
if vfs.ReadOnly {
|
||||||
options = append(options, fuse.ReadOnly())
|
options = append(options, fuse.ReadOnly())
|
||||||
}
|
}
|
||||||
if mountlib.WritebackCache {
|
if mountlib.WritebackCache {
|
||||||
|
@ -69,7 +70,7 @@ func mountOptions(device string) (options []fuse.MountOption) {
|
||||||
//
|
//
|
||||||
// returns an error, and an error channel for the serve process to
|
// returns an error, and an error channel for the serve process to
|
||||||
// report an error when fusermount is called.
|
// report an error when fusermount is called.
|
||||||
func mount(f fs.Fs, mountpoint string) (*mountlib.FS, <-chan error, func() error, error) {
|
func mount(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, error) {
|
||||||
fs.Debugf(f, "Mounting on %q", mountpoint)
|
fs.Debugf(f, "Mounting on %q", mountpoint)
|
||||||
c, err := fuse.Mount(mountpoint, mountOptions(f.Name()+":"+f.Root())...)
|
c, err := fuse.Mount(mountpoint, mountOptions(f.Name()+":"+f.Root())...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -100,7 +101,7 @@ func mount(f fs.Fs, mountpoint string) (*mountlib.FS, <-chan error, func() error
|
||||||
return fuse.Unmount(mountpoint)
|
return fuse.Unmount(mountpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
return filesys.FS, errChan, unmount, nil
|
return filesys.VFS, errChan, unmount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mount mounts the remote at mountpoint.
|
// Mount mounts the remote at mountpoint.
|
||||||
|
|
|
@ -5,20 +5,14 @@ package mount
|
||||||
import (
|
import (
|
||||||
"bazil.org/fuse"
|
"bazil.org/fuse"
|
||||||
fusefs "bazil.org/fuse/fs"
|
fusefs "bazil.org/fuse/fs"
|
||||||
"github.com/ncw/rclone/cmd/mountlib"
|
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/ncw/rclone/vfs"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadFileHandle is an open for read file handle on a File
|
// ReadFileHandle is an open for read file handle on a File
|
||||||
type ReadFileHandle struct {
|
type ReadFileHandle struct {
|
||||||
*mountlib.ReadFileHandle
|
*vfs.ReadFileHandle
|
||||||
// mu sync.Mutex
|
|
||||||
// closed bool // set if handle has been closed
|
|
||||||
// r *fs.Account
|
|
||||||
// o fs.Object
|
|
||||||
// readCalled bool // set if read has been called
|
|
||||||
// offset int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check interface satisfied
|
// Check interface satisfied
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
|
|
||||||
"bazil.org/fuse"
|
"bazil.org/fuse"
|
||||||
fusefs "bazil.org/fuse/fs"
|
fusefs "bazil.org/fuse/fs"
|
||||||
"github.com/ncw/rclone/cmd/mountlib"
|
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/ncw/rclone/vfs"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ var errClosedFileHandle = errors.New("Attempt to use closed file handle")
|
||||||
|
|
||||||
// WriteFileHandle is an open for write handle on a File
|
// WriteFileHandle is an open for write handle on a File
|
||||||
type WriteFileHandle struct {
|
type WriteFileHandle struct {
|
||||||
*mountlib.WriteFileHandle
|
*vfs.WriteFileHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check interface satisfied
|
// Check interface satisfied
|
||||||
|
|
|
@ -1,42 +1,25 @@
|
||||||
package mountlib
|
package mountlib
|
||||||
|
|
||||||
// Globals
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ncw/rclone/cmd"
|
"github.com/ncw/rclone/cmd"
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/ncw/rclone/vfs"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options set by command line flags
|
// Options set by command line flags
|
||||||
var (
|
var (
|
||||||
NoModTime = false
|
DebugFUSE = false
|
||||||
NoChecksum = false
|
|
||||||
DebugFUSE = false
|
|
||||||
NoSeek = false
|
|
||||||
DirCacheTime = 5 * 60 * time.Second
|
|
||||||
PollInterval = time.Minute
|
|
||||||
// mount options
|
|
||||||
ReadOnly = false
|
|
||||||
AllowNonEmpty = false
|
AllowNonEmpty = false
|
||||||
AllowRoot = false
|
AllowRoot = false
|
||||||
AllowOther = false
|
AllowOther = false
|
||||||
DefaultPermissions = false
|
DefaultPermissions = false
|
||||||
WritebackCache = false
|
WritebackCache = false
|
||||||
MaxReadAhead fs.SizeSuffix = 128 * 1024
|
MaxReadAhead fs.SizeSuffix = 128 * 1024
|
||||||
Umask = 0
|
ExtraOptions *[]string
|
||||||
UID = ^uint32(0) // these values instruct WinFSP-FUSE to use the current user
|
ExtraFlags *[]string
|
||||||
GID = ^uint32(0) // overriden for non windows in mount_unix.go
|
|
||||||
// foreground = false
|
|
||||||
// default permissions for directories - modified by umask in Mount
|
|
||||||
DirPerms = os.FileMode(0777)
|
|
||||||
FilePerms = os.FileMode(0666)
|
|
||||||
ExtraOptions *[]string
|
|
||||||
ExtraFlags *[]string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewMountCommand makes a mount command with the given name and Mount function
|
// NewMountCommand makes a mount command with the given name and Mount function
|
||||||
|
@ -149,10 +132,6 @@ like this:
|
||||||
cmd.CheckArgs(2, 2, command, args)
|
cmd.CheckArgs(2, 2, command, args)
|
||||||
fdst := cmd.NewFsDst(args)
|
fdst := cmd.NewFsDst(args)
|
||||||
|
|
||||||
// Mask permissions
|
|
||||||
DirPerms = 0777 &^ os.FileMode(Umask)
|
|
||||||
FilePerms = 0666 &^ os.FileMode(Umask)
|
|
||||||
|
|
||||||
// Show stats if the user has specifically requested them
|
// Show stats if the user has specifically requested them
|
||||||
if cmd.ShowStats() {
|
if cmd.ShowStats() {
|
||||||
stopStats := cmd.StartStats()
|
stopStats := cmd.StartStats()
|
||||||
|
@ -184,18 +163,7 @@ like this:
|
||||||
//flags.BoolVarP(&foreground, "foreground", "", foreground, "Do not detach.")
|
//flags.BoolVarP(&foreground, "foreground", "", foreground, "Do not detach.")
|
||||||
|
|
||||||
// Add in the generic flags
|
// Add in the generic flags
|
||||||
AddFlags(flags)
|
vfs.AddFlags(flags)
|
||||||
|
|
||||||
return commandDefintion
|
return commandDefintion
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -176,7 +176,7 @@ func TestDirCacheFlush(t *testing.T) {
|
||||||
err := run.fremote.Mkdir("dir/subdir")
|
err := run.fremote.Mkdir("dir/subdir")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
root, err := run.filesys.Root()
|
root, err := run.vfs.Root()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// expect newly created "subdir" on remote to not show up
|
// expect newly created "subdir" on remote to not show up
|
||||||
|
|
|
@ -15,10 +15,10 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ncw/rclone/cmd/mountlib"
|
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
_ "github.com/ncw/rclone/fs/all" // import all the file systems
|
_ "github.com/ncw/rclone/fs/all" // import all the file systems
|
||||||
"github.com/ncw/rclone/fstest"
|
"github.com/ncw/rclone/fstest"
|
||||||
|
"github.com/ncw/rclone/vfs"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -27,7 +27,7 @@ type (
|
||||||
// UnmountFn is called to unmount the file system
|
// UnmountFn is called to unmount the file system
|
||||||
UnmountFn func() error
|
UnmountFn func() error
|
||||||
// MountFn is called to mount the file system
|
// MountFn is called to mount the file system
|
||||||
MountFn func(f fs.Fs, mountpoint string) (*mountlib.FS, <-chan error, func() error, error)
|
MountFn func(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, error)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -46,7 +46,7 @@ func TestMain(m *testing.M, fn MountFn) {
|
||||||
|
|
||||||
// Run holds the remotes for a test run
|
// Run holds the remotes for a test run
|
||||||
type Run struct {
|
type Run struct {
|
||||||
filesys *mountlib.FS
|
vfs *vfs.VFS
|
||||||
mountPath string
|
mountPath string
|
||||||
fremote fs.Fs
|
fremote fs.Fs
|
||||||
fremoteName string
|
fremoteName string
|
||||||
|
@ -112,7 +112,7 @@ func newRun() *Run {
|
||||||
func (r *Run) mount() {
|
func (r *Run) mount() {
|
||||||
log.Printf("mount %q %q", r.fremote, r.mountPath)
|
log.Printf("mount %q %q", r.fremote, r.mountPath)
|
||||||
var err error
|
var err error
|
||||||
r.filesys, r.umountResult, r.umountFn, err = mountFn(r.fremote, r.mountPath)
|
r.vfs, r.umountResult, r.umountFn, err = mountFn(r.fremote, r.mountPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("mount failed: %v", err)
|
log.Printf("mount failed: %v", err)
|
||||||
r.skip = true
|
r.skip = true
|
||||||
|
@ -207,10 +207,10 @@ func (r *Run) readLocal(t *testing.T, dir dirMap, filepath string) {
|
||||||
if fi.IsDir() {
|
if fi.IsDir() {
|
||||||
dir[name+"/"] = struct{}{}
|
dir[name+"/"] = struct{}{}
|
||||||
r.readLocal(t, dir, name)
|
r.readLocal(t, dir, name)
|
||||||
assert.Equal(t, mountlib.DirPerms, fi.Mode().Perm())
|
assert.Equal(t, vfs.DirPerms, fi.Mode().Perm())
|
||||||
} else {
|
} else {
|
||||||
dir[fmt.Sprintf("%s %d", name, fi.Size())] = struct{}{}
|
dir[fmt.Sprintf("%s %d", name, fi.Size())] = struct{}{}
|
||||||
assert.Equal(t, mountlib.FilePerms, fi.Mode().Perm())
|
assert.Equal(t, vfs.FilePerms, fi.Mode().Perm())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,5 +292,5 @@ func TestRoot(t *testing.T) {
|
||||||
fi, err := os.Lstat(run.mountPath)
|
fi, err := os.Lstat(run.mountPath)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.True(t, fi.IsDir())
|
assert.True(t, fi.IsDir())
|
||||||
assert.Equal(t, fi.Mode().Perm(), mountlib.DirPerms)
|
assert.Equal(t, fi.Mode().Perm(), vfs.DirPerms)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package mountlib
|
package vfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
|
@ -1,4 +1,4 @@
|
||||||
package mountlib
|
package vfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
@ -14,7 +14,7 @@ import (
|
||||||
|
|
||||||
// Dir represents a directory entry
|
// Dir represents a directory entry
|
||||||
type Dir struct {
|
type Dir struct {
|
||||||
fsys *FS
|
vfs *VFS
|
||||||
inode uint64 // inode number
|
inode uint64 // inode number
|
||||||
f fs.Fs
|
f fs.Fs
|
||||||
parent *Dir // parent, nil for root
|
parent *Dir // parent, nil for root
|
||||||
|
@ -26,9 +26,9 @@ type Dir struct {
|
||||||
items map[string]Node // NB can be nil when directory not read yet
|
items map[string]Node // NB can be nil when directory not read yet
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDir(fsys *FS, f fs.Fs, parent *Dir, fsDir fs.Directory) *Dir {
|
func newDir(vfs *VFS, f fs.Fs, parent *Dir, fsDir fs.Directory) *Dir {
|
||||||
return &Dir{
|
return &Dir{
|
||||||
fsys: fsys,
|
vfs: vfs,
|
||||||
f: f,
|
f: f,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
entry: fsDir,
|
entry: fsDir,
|
||||||
|
@ -169,7 +169,7 @@ func (d *Dir) _readDir() error {
|
||||||
// fs.Debugf(d.path, "Reading directory")
|
// fs.Debugf(d.path, "Reading directory")
|
||||||
} else {
|
} else {
|
||||||
age := when.Sub(d.read)
|
age := when.Sub(d.read)
|
||||||
if age < d.fsys.dirCacheTime {
|
if age < d.vfs.dirCacheTime {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fs.Debugf(d.path, "Re-reading directory (%v old)", age)
|
fs.Debugf(d.path, "Re-reading directory (%v old)", age)
|
||||||
|
@ -208,7 +208,7 @@ func (d *Dir) _readDir() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d.items[name] = newDir(d.fsys, d.f, d, dir)
|
d.items[name] = newDir(d.vfs, d.f, d, dir)
|
||||||
default:
|
default:
|
||||||
err = errors.Errorf("unknown type %T", item)
|
err = errors.Errorf("unknown type %T", item)
|
||||||
fs.Errorf(d.path, "readDir error: %v", err)
|
fs.Errorf(d.path, "readDir error: %v", err)
|
||||||
|
@ -260,7 +260,7 @@ func (d *Dir) Size() int64 {
|
||||||
|
|
||||||
// SetModTime sets the modTime for this dir
|
// SetModTime sets the modTime for this dir
|
||||||
func (d *Dir) SetModTime(modTime time.Time) error {
|
func (d *Dir) SetModTime(modTime time.Time) error {
|
||||||
if d.fsys.readOnly {
|
if d.vfs.readOnly {
|
||||||
return EROFS
|
return EROFS
|
||||||
}
|
}
|
||||||
d.mu.Lock()
|
d.mu.Lock()
|
||||||
|
@ -309,7 +309,7 @@ func (d *Dir) ReadDirAll() (items Nodes, err error) {
|
||||||
|
|
||||||
// Create makes a new file
|
// Create makes a new file
|
||||||
func (d *Dir) Create(name string) (*File, *WriteFileHandle, error) {
|
func (d *Dir) Create(name string) (*File, *WriteFileHandle, error) {
|
||||||
if d.fsys.readOnly {
|
if d.vfs.readOnly {
|
||||||
return nil, nil, EROFS
|
return nil, nil, EROFS
|
||||||
}
|
}
|
||||||
path := path.Join(d.path, name)
|
path := path.Join(d.path, name)
|
||||||
|
@ -328,7 +328,7 @@ func (d *Dir) Create(name string) (*File, *WriteFileHandle, error) {
|
||||||
|
|
||||||
// Mkdir creates a new directory
|
// Mkdir creates a new directory
|
||||||
func (d *Dir) Mkdir(name string) (*Dir, error) {
|
func (d *Dir) Mkdir(name string) (*Dir, error) {
|
||||||
if d.fsys.readOnly {
|
if d.vfs.readOnly {
|
||||||
return nil, EROFS
|
return nil, EROFS
|
||||||
}
|
}
|
||||||
path := path.Join(d.path, name)
|
path := path.Join(d.path, name)
|
||||||
|
@ -339,7 +339,7 @@ func (d *Dir) Mkdir(name string) (*Dir, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fsDir := fs.NewDir(path, time.Now())
|
fsDir := fs.NewDir(path, time.Now())
|
||||||
dir := newDir(d.fsys, d.f, d, fsDir)
|
dir := newDir(d.vfs, d.f, d, fsDir)
|
||||||
d.addObject(dir)
|
d.addObject(dir)
|
||||||
// fs.Debugf(path, "Dir.Mkdir OK")
|
// fs.Debugf(path, "Dir.Mkdir OK")
|
||||||
return dir, nil
|
return dir, nil
|
||||||
|
@ -347,7 +347,7 @@ func (d *Dir) Mkdir(name string) (*Dir, error) {
|
||||||
|
|
||||||
// Remove the directory
|
// Remove the directory
|
||||||
func (d *Dir) Remove() error {
|
func (d *Dir) Remove() error {
|
||||||
if d.fsys.readOnly {
|
if d.vfs.readOnly {
|
||||||
return EROFS
|
return EROFS
|
||||||
}
|
}
|
||||||
// Check directory is empty first
|
// Check directory is empty first
|
||||||
|
@ -375,7 +375,7 @@ func (d *Dir) Remove() error {
|
||||||
|
|
||||||
// RemoveAll removes the directory and any contents recursively
|
// RemoveAll removes the directory and any contents recursively
|
||||||
func (d *Dir) RemoveAll() error {
|
func (d *Dir) RemoveAll() error {
|
||||||
if d.fsys.readOnly {
|
if d.vfs.readOnly {
|
||||||
return EROFS
|
return EROFS
|
||||||
}
|
}
|
||||||
// Remove contents of the directory
|
// Remove contents of the directory
|
||||||
|
@ -403,7 +403,7 @@ func (d *Dir) DirEntry() (entry fs.DirEntry) {
|
||||||
// which must be a directory. The entry to be removed may correspond
|
// which must be a directory. The entry to be removed may correspond
|
||||||
// to a file (unlink) or to a directory (rmdir).
|
// to a file (unlink) or to a directory (rmdir).
|
||||||
func (d *Dir) RemoveName(name string) error {
|
func (d *Dir) RemoveName(name string) error {
|
||||||
if d.fsys.readOnly {
|
if d.vfs.readOnly {
|
||||||
return EROFS
|
return EROFS
|
||||||
}
|
}
|
||||||
path := path.Join(d.path, name)
|
path := path.Join(d.path, name)
|
||||||
|
@ -418,7 +418,7 @@ func (d *Dir) RemoveName(name string) error {
|
||||||
|
|
||||||
// Rename the file
|
// Rename the file
|
||||||
func (d *Dir) Rename(oldName, newName string, destDir *Dir) error {
|
func (d *Dir) Rename(oldName, newName string, destDir *Dir) error {
|
||||||
if d.fsys.readOnly {
|
if d.vfs.readOnly {
|
||||||
return EROFS
|
return EROFS
|
||||||
}
|
}
|
||||||
oldPath := path.Join(d.path, oldName)
|
oldPath := path.Join(d.path, oldName)
|
|
@ -1,6 +1,6 @@
|
||||||
// Cross platform errors
|
// Cross platform errors
|
||||||
|
|
||||||
package mountlib
|
package vfs
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package mountlib
|
package vfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
@ -98,7 +98,7 @@ func (f *File) ModTime() (modTime time.Time) {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
|
|
||||||
if !f.d.fsys.noModTime {
|
if !f.d.vfs.noModTime {
|
||||||
// if o is nil it isn't valid yet or there are writers, so return the size so far
|
// if o is nil it isn't valid yet or there are writers, so return the size so far
|
||||||
if f.o == nil || f.writers != 0 {
|
if f.o == nil || f.writers != 0 {
|
||||||
if !f.pendingModTime.IsZero() {
|
if !f.pendingModTime.IsZero() {
|
||||||
|
@ -126,7 +126,7 @@ func (f *File) Size() int64 {
|
||||||
|
|
||||||
// SetModTime sets the modtime for the file
|
// SetModTime sets the modtime for the file
|
||||||
func (f *File) SetModTime(modTime time.Time) error {
|
func (f *File) SetModTime(modTime time.Time) error {
|
||||||
if f.d.fsys.readOnly {
|
if f.d.vfs.readOnly {
|
||||||
return EROFS
|
return EROFS
|
||||||
}
|
}
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
|
@ -224,7 +224,7 @@ func (f *File) OpenRead() (fh *ReadFileHandle, err error) {
|
||||||
|
|
||||||
// OpenWrite open the file for write
|
// OpenWrite open the file for write
|
||||||
func (f *File) OpenWrite() (fh *WriteFileHandle, err error) {
|
func (f *File) OpenWrite() (fh *WriteFileHandle, err error) {
|
||||||
if f.d.fsys.readOnly {
|
if f.d.vfs.readOnly {
|
||||||
return nil, EROFS
|
return nil, EROFS
|
||||||
}
|
}
|
||||||
// if o is nil it isn't valid yet
|
// if o is nil it isn't valid yet
|
||||||
|
@ -254,7 +254,7 @@ func (f *File) Fsync() error {
|
||||||
|
|
||||||
// Remove the file
|
// Remove the file
|
||||||
func (f *File) Remove() error {
|
func (f *File) Remove() error {
|
||||||
if f.d.fsys.readOnly {
|
if f.d.vfs.readOnly {
|
||||||
return EROFS
|
return EROFS
|
||||||
}
|
}
|
||||||
err := f.o.Remove()
|
err := f.o.Remove()
|
|
@ -1,4 +1,4 @@
|
||||||
package mountlib
|
package vfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
@ -34,7 +34,7 @@ var (
|
||||||
func newReadFileHandle(f *File, o fs.Object) (*ReadFileHandle, error) {
|
func newReadFileHandle(f *File, o fs.Object) (*ReadFileHandle, error) {
|
||||||
var hash *fs.MultiHasher
|
var hash *fs.MultiHasher
|
||||||
var err error
|
var err error
|
||||||
if !f.d.fsys.noChecksum {
|
if !f.d.vfs.noChecksum {
|
||||||
hash, err = fs.NewMultiHasherTypes(o.Fs().Hashes())
|
hash, err = fs.NewMultiHasherTypes(o.Fs().Hashes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fs.Errorf(o.Fs(), "newReadFileHandle hash error: %v", err)
|
fs.Errorf(o.Fs(), "newReadFileHandle hash error: %v", err)
|
||||||
|
@ -43,7 +43,7 @@ func newReadFileHandle(f *File, o fs.Object) (*ReadFileHandle, error) {
|
||||||
|
|
||||||
fh := &ReadFileHandle{
|
fh := &ReadFileHandle{
|
||||||
o: o,
|
o: o,
|
||||||
noSeek: f.d.fsys.noSeek,
|
noSeek: f.d.vfs.noSeek,
|
||||||
file: f,
|
file: f,
|
||||||
hash: hash,
|
hash: hash,
|
||||||
}
|
}
|
|
@ -1,4 +1,13 @@
|
||||||
package mountlib
|
// 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
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -8,9 +17,28 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Node represents either a *Dir or a *File
|
// 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)
|
||||||
type Node interface {
|
type Node interface {
|
||||||
os.FileInfo
|
os.FileInfo
|
||||||
IsFile() bool
|
IsFile() bool
|
||||||
|
@ -22,6 +50,7 @@ type Node interface {
|
||||||
DirEntry() fs.DirEntry
|
DirEntry() fs.DirEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check interfaces
|
||||||
var (
|
var (
|
||||||
_ Node = (*File)(nil)
|
_ Node = (*File)(nil)
|
||||||
_ Node = (*Dir)(nil)
|
_ Node = (*Dir)(nil)
|
||||||
|
@ -41,6 +70,7 @@ type Noder interface {
|
||||||
Node() Node
|
Node() Node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check interfaces
|
||||||
var (
|
var (
|
||||||
_ Noder = (*File)(nil)
|
_ Noder = (*File)(nil)
|
||||||
_ Noder = (*Dir)(nil)
|
_ Noder = (*Dir)(nil)
|
||||||
|
@ -48,61 +78,65 @@ var (
|
||||||
_ Noder = (*WriteFileHandle)(nil)
|
_ Noder = (*WriteFileHandle)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
// FS represents the top level filing system
|
// VFS represents the top level filing system
|
||||||
type FS struct {
|
type VFS struct {
|
||||||
f fs.Fs
|
f fs.Fs
|
||||||
root *Dir
|
root *Dir
|
||||||
noSeek bool // don't allow seeking if set
|
noSeek bool // don't allow seeking if set
|
||||||
noChecksum bool // don't check checksums if set
|
noChecksum bool // don't check checksums if set
|
||||||
readOnly bool // if set FS is read only
|
readOnly bool // if set VFS is read only
|
||||||
noModTime bool // don't read mod times for files
|
noModTime bool // don't read mod times for files
|
||||||
dirCacheTime time.Duration // how long to consider directory listing cache valid
|
dirCacheTime time.Duration // how long to consider directory listing cache valid
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFS creates a new filing system and root directory
|
// New creates a new VFS and root directory
|
||||||
func NewFS(f fs.Fs) *FS {
|
func New(f fs.Fs) *VFS {
|
||||||
fsDir := fs.NewDir("", time.Now())
|
fsDir := fs.NewDir("", time.Now())
|
||||||
fsys := &FS{
|
vfs := &VFS{
|
||||||
f: f,
|
f: f,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mask permissions
|
||||||
|
DirPerms = 0777 &^ os.FileMode(Umask)
|
||||||
|
FilePerms = 0666 &^ os.FileMode(Umask)
|
||||||
|
|
||||||
if NoSeek {
|
if NoSeek {
|
||||||
fsys.noSeek = true
|
vfs.noSeek = true
|
||||||
}
|
}
|
||||||
if NoChecksum {
|
if NoChecksum {
|
||||||
fsys.noChecksum = true
|
vfs.noChecksum = true
|
||||||
}
|
}
|
||||||
if ReadOnly {
|
if ReadOnly {
|
||||||
fsys.readOnly = true
|
vfs.readOnly = true
|
||||||
}
|
}
|
||||||
if NoModTime {
|
if NoModTime {
|
||||||
fsys.noModTime = true
|
vfs.noModTime = true
|
||||||
}
|
}
|
||||||
fsys.dirCacheTime = DirCacheTime
|
vfs.dirCacheTime = DirCacheTime
|
||||||
|
|
||||||
fsys.root = newDir(fsys, f, nil, fsDir)
|
vfs.root = newDir(vfs, f, nil, fsDir)
|
||||||
|
|
||||||
if PollInterval > 0 {
|
if PollInterval > 0 {
|
||||||
fsys.PollChanges(PollInterval)
|
vfs.PollChanges(PollInterval)
|
||||||
}
|
}
|
||||||
return fsys
|
return vfs
|
||||||
}
|
}
|
||||||
|
|
||||||
// PollChanges will poll the remote every pollInterval for changes if the remote
|
// 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
|
// supports it. If a non-polling option is used, the given time interval can be
|
||||||
// ignored
|
// ignored
|
||||||
func (fsys *FS) PollChanges(pollInterval time.Duration) *FS {
|
func (vfs *VFS) PollChanges(pollInterval time.Duration) *VFS {
|
||||||
doDirChangeNotify := fsys.f.Features().DirChangeNotify
|
doDirChangeNotify := vfs.f.Features().DirChangeNotify
|
||||||
if doDirChangeNotify != nil {
|
if doDirChangeNotify != nil {
|
||||||
doDirChangeNotify(fsys.root.ForgetPath, pollInterval)
|
doDirChangeNotify(vfs.root.ForgetPath, pollInterval)
|
||||||
}
|
}
|
||||||
return fsys
|
return vfs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Root returns the root node
|
// Root returns the root node
|
||||||
func (fsys *FS) Root() (*Dir, error) {
|
func (vfs *VFS) Root() (*Dir, error) {
|
||||||
// fs.Debugf(fsys.f, "Root()")
|
// fs.Debugf(vfs.f, "Root()")
|
||||||
return fsys.root, nil
|
return vfs.root, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var inodeCount uint64
|
var inodeCount uint64
|
||||||
|
@ -113,8 +147,8 @@ func NewInode() (inode uint64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lookup finds the Node by path starting from the root
|
// Lookup finds the Node by path starting from the root
|
||||||
func (fsys *FS) Lookup(path string) (node Node, err error) {
|
func (vfs *VFS) Lookup(path string) (node Node, err error) {
|
||||||
node = fsys.root
|
node = vfs.root
|
||||||
for path != "" {
|
for path != "" {
|
||||||
i := strings.IndexRune(path, '/')
|
i := strings.IndexRune(path, '/')
|
||||||
var name string
|
var name string
|
||||||
|
@ -141,7 +175,7 @@ func (fsys *FS) Lookup(path string) (node Node, err error) {
|
||||||
|
|
||||||
// Statfs is called to obtain file system metadata.
|
// Statfs is called to obtain file system metadata.
|
||||||
// It should write that data to resp.
|
// It should write that data to resp.
|
||||||
func (fsys *FS) Statfs() error {
|
func (vfs *VFS) Statfs() error {
|
||||||
/* FIXME
|
/* FIXME
|
||||||
const blockSize = 4096
|
const blockSize = 4096
|
||||||
const fsBlocks = (1 << 50) / blockSize
|
const fsBlocks = (1 << 50) / blockSize
|
||||||
|
@ -156,3 +190,14 @@ func (fsys *FS) Statfs() error {
|
||||||
*/
|
*/
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
// +build !linux,!darwin,!freebsd
|
// +build !linux,!darwin,!freebsd
|
||||||
|
|
||||||
package mountlib
|
package vfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
|
@ -1,6 +1,6 @@
|
||||||
// +build linux darwin freebsd
|
// +build linux darwin freebsd
|
||||||
|
|
||||||
package mountlib
|
package vfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
|
@ -1,4 +1,4 @@
|
||||||
package mountlib
|
package vfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
Loading…
Reference in a new issue