2016-07-17 22:03:23 +00:00
|
|
|
// FUSE main Fs
|
|
|
|
|
|
|
|
// +build linux darwin freebsd
|
|
|
|
|
|
|
|
package mount
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bazil.org/fuse"
|
|
|
|
fusefs "bazil.org/fuse/fs"
|
|
|
|
"github.com/ncw/rclone/fs"
|
2016-11-20 22:54:03 +00:00
|
|
|
"golang.org/x/net/context"
|
2016-07-17 22:03:23 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// FS represents the top level filing system
|
|
|
|
type FS struct {
|
|
|
|
f fs.Fs
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check interface satistfied
|
|
|
|
var _ fusefs.FS = (*FS)(nil)
|
|
|
|
|
|
|
|
// Root returns the root node
|
|
|
|
func (f *FS) Root() (fusefs.Node, error) {
|
|
|
|
fs.Debug(f.f, "Root()")
|
|
|
|
return newDir(f.f, ""), nil
|
|
|
|
}
|
|
|
|
|
2016-09-09 07:39:19 +00:00
|
|
|
// mountOptions configures the options from the command line flags
|
|
|
|
func mountOptions(device string) (options []fuse.MountOption) {
|
|
|
|
options = []fuse.MountOption{
|
|
|
|
fuse.MaxReadahead(uint32(maxReadAhead)),
|
|
|
|
fuse.Subtype("rclone"),
|
|
|
|
fuse.FSName(device), fuse.VolumeName(device),
|
|
|
|
fuse.NoAppleDouble(),
|
|
|
|
fuse.NoAppleXattr(),
|
2016-09-19 15:25:20 +00:00
|
|
|
|
|
|
|
// Options from benchmarking in the fuse module
|
|
|
|
//fuse.MaxReadahead(64 * 1024 * 1024),
|
|
|
|
//fuse.AsyncRead(), - FIXME this causes
|
|
|
|
// ReadFileHandle.Read error: read /home/files/ISOs/xubuntu-15.10-desktop-amd64.iso: bad file descriptor
|
|
|
|
// which is probably related to errors people are having
|
|
|
|
//fuse.WritebackCache(),
|
2016-09-09 07:39:19 +00:00
|
|
|
}
|
|
|
|
if allowNonEmpty {
|
|
|
|
options = append(options, fuse.AllowNonEmptyMount())
|
|
|
|
}
|
|
|
|
if allowOther {
|
|
|
|
options = append(options, fuse.AllowOther())
|
|
|
|
}
|
|
|
|
if allowRoot {
|
|
|
|
options = append(options, fuse.AllowRoot())
|
|
|
|
}
|
|
|
|
if defaultPermissions {
|
|
|
|
options = append(options, fuse.DefaultPermissions())
|
|
|
|
}
|
|
|
|
if readOnly {
|
|
|
|
options = append(options, fuse.ReadOnly())
|
|
|
|
}
|
|
|
|
if writebackCache {
|
|
|
|
options = append(options, fuse.WritebackCache())
|
|
|
|
}
|
|
|
|
return options
|
|
|
|
}
|
|
|
|
|
2016-07-17 22:03:23 +00:00
|
|
|
// mount the file system
|
|
|
|
//
|
|
|
|
// The mount point will be ready when this returns.
|
|
|
|
//
|
|
|
|
// returns an error, and an error channel for the serve process to
|
|
|
|
// report an error when fusermount is called.
|
|
|
|
func mount(f fs.Fs, mountpoint string) (<-chan error, error) {
|
2016-11-05 09:59:36 +00:00
|
|
|
fs.Debug(f, "Mounting on %q", mountpoint)
|
2016-09-09 07:39:19 +00:00
|
|
|
c, err := fuse.Mount(mountpoint, mountOptions(f.Name()+":"+f.Root())...)
|
2016-07-17 22:03:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
filesys := &FS{
|
|
|
|
f: f,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serve the mount point in the background returning error to errChan
|
|
|
|
errChan := make(chan error, 1)
|
|
|
|
go func() {
|
|
|
|
err := fusefs.Serve(c, filesys)
|
|
|
|
closeErr := c.Close()
|
|
|
|
if err == nil {
|
|
|
|
err = closeErr
|
|
|
|
}
|
|
|
|
errChan <- err
|
|
|
|
}()
|
|
|
|
|
|
|
|
// check if the mount process has an error to report
|
|
|
|
<-c.Ready
|
|
|
|
if err := c.MountError; err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return errChan, nil
|
|
|
|
}
|
2016-11-20 22:54:03 +00:00
|
|
|
|
|
|
|
// Check interface satsified
|
|
|
|
var _ fusefs.FSStatfser = (*FS)(nil)
|
|
|
|
|
|
|
|
// Statfs is called to obtain file system metadata.
|
|
|
|
// It should write that data to resp.
|
|
|
|
func (f *FS) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error {
|
|
|
|
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
|
|
|
|
}
|