2020-04-25 07:03:07 +02:00
|
|
|
package mountlib
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"log"
|
2020-05-14 17:05:23 +02:00
|
|
|
"sort"
|
2020-06-04 19:13:10 +02:00
|
|
|
"sync"
|
|
|
|
"time"
|
2020-04-25 07:03:07 +02:00
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/rclone/rclone/fs"
|
|
|
|
"github.com/rclone/rclone/fs/rc"
|
2020-07-22 18:58:49 +02:00
|
|
|
"github.com/rclone/rclone/vfs"
|
2020-07-23 18:34:54 +02:00
|
|
|
"github.com/rclone/rclone/vfs/vfscommon"
|
2020-07-22 18:58:49 +02:00
|
|
|
"github.com/rclone/rclone/vfs/vfsflags"
|
2020-04-25 07:03:07 +02:00
|
|
|
)
|
|
|
|
|
2020-06-04 19:13:10 +02:00
|
|
|
// MountInfo defines the configuration for a mount
|
|
|
|
type MountInfo struct {
|
|
|
|
unmountFn UnmountFn
|
|
|
|
MountPoint string `json:"MountPoint"`
|
|
|
|
MountedOn time.Time `json:"MountedOn"`
|
|
|
|
Fs string `json:"Fs"`
|
2020-07-23 18:34:54 +02:00
|
|
|
MountOpt *Options
|
|
|
|
VFSOpt *vfscommon.Options
|
2020-06-04 19:13:10 +02:00
|
|
|
}
|
|
|
|
|
2020-04-25 07:03:07 +02:00
|
|
|
var (
|
2020-06-05 20:32:56 +02:00
|
|
|
// mutex to protect all the variables in this block
|
|
|
|
mountMu sync.Mutex
|
2020-04-25 07:03:07 +02:00
|
|
|
// Mount functions available
|
2020-06-04 19:13:10 +02:00
|
|
|
mountFns = map[string]MountFn{}
|
|
|
|
// Map of mounted path => MountInfo
|
|
|
|
liveMounts = map[string]MountInfo{}
|
2020-04-25 07:03:07 +02:00
|
|
|
)
|
|
|
|
|
2020-05-14 17:05:23 +02:00
|
|
|
// AddRc adds mount and unmount functionality to rc
|
|
|
|
func AddRc(mountUtilName string, mountFunction MountFn) {
|
2020-06-05 20:32:56 +02:00
|
|
|
mountMu.Lock()
|
|
|
|
defer mountMu.Unlock()
|
2020-05-14 17:05:23 +02:00
|
|
|
// rcMount allows the mount command to be run from rc
|
|
|
|
mountFns[mountUtilName] = mountFunction
|
|
|
|
}
|
|
|
|
|
2020-04-25 07:03:07 +02:00
|
|
|
func init() {
|
|
|
|
rc.Add(rc.Call{
|
|
|
|
Path: "mount/mount",
|
|
|
|
AuthRequired: true,
|
|
|
|
Fn: mountRc,
|
|
|
|
Title: "Create a new mount point",
|
|
|
|
Help: `rclone allows Linux, FreeBSD, macOS and Windows to mount any of
|
|
|
|
Rclone's cloud storage systems as a file system with FUSE.
|
|
|
|
|
2020-05-14 17:05:23 +02:00
|
|
|
If no mountType is provided, the priority is given as follows: 1. mount 2.cmount 3.mount2
|
2020-04-25 07:03:07 +02:00
|
|
|
|
|
|
|
This takes the following parameters
|
|
|
|
|
|
|
|
- fs - a remote path to be mounted (required)
|
|
|
|
- mountPoint: valid path on the local machine (required)
|
2020-05-14 17:05:23 +02:00
|
|
|
- mountType: One of the values (mount, cmount, mount2) specifies the mount implementation to use
|
2020-07-23 18:20:55 +02:00
|
|
|
- mountOpt: a JSON object with Mount options in.
|
2020-07-22 19:31:54 +02:00
|
|
|
- vfsOpt: a JSON object with VFS options in.
|
2020-04-25 07:03:07 +02:00
|
|
|
|
|
|
|
Eg
|
|
|
|
|
|
|
|
rclone rc mount/mount fs=mydrive: mountPoint=/home/<user>/mountPoint
|
2020-05-14 17:05:23 +02:00
|
|
|
rclone rc mount/mount fs=mydrive: mountPoint=/home/<user>/mountPoint mountType=mount
|
2020-07-23 18:20:55 +02:00
|
|
|
rclone rc mount/mount fs=TestDrive: mountPoint=/mnt/tmp vfsOpt='{"CacheMode": 2}' mountOpt='{"AllowOther": true}'
|
2020-07-22 19:31:54 +02:00
|
|
|
|
|
|
|
The vfsOpt are as described in options/get and can be seen in the the
|
2020-07-23 18:20:55 +02:00
|
|
|
"vfs" section when running and the mountOpt can be seen in the "mount" section.
|
2020-07-22 19:31:54 +02:00
|
|
|
|
|
|
|
rclone rc options/get
|
2020-04-25 07:03:07 +02:00
|
|
|
`,
|
|
|
|
})
|
2020-05-14 17:05:23 +02:00
|
|
|
}
|
|
|
|
|
2020-06-04 19:13:10 +02:00
|
|
|
// mountRc allows the mount command to be run from rc
|
2020-11-05 16:18:51 +01:00
|
|
|
func mountRc(ctx context.Context, in rc.Params) (out rc.Params, err error) {
|
2020-05-14 17:05:23 +02:00
|
|
|
mountPoint, err := in.GetString("mountPoint")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-07-22 19:31:54 +02:00
|
|
|
vfsOpt := vfsflags.Opt
|
|
|
|
err = in.GetStructMissingOK("vfsOpt", &vfsOpt)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-07-23 18:34:54 +02:00
|
|
|
mountOpt := Opt
|
|
|
|
err = in.GetStructMissingOK("mountOpt", &mountOpt)
|
2020-07-23 18:20:55 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-05-14 17:05:23 +02:00
|
|
|
mountType, err := in.GetString("mountType")
|
|
|
|
|
2020-06-05 20:32:56 +02:00
|
|
|
mountMu.Lock()
|
|
|
|
defer mountMu.Unlock()
|
|
|
|
|
2020-05-14 17:05:23 +02:00
|
|
|
if err != nil || mountType == "" {
|
|
|
|
if mountFns["mount"] != nil {
|
|
|
|
mountType = "mount"
|
|
|
|
} else if mountFns["cmount"] != nil {
|
|
|
|
mountType = "cmount"
|
|
|
|
} else if mountFns["mount2"] != nil {
|
|
|
|
mountType = "mount2"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get Fs.fs to be mounted from fs parameter in the params
|
2020-11-05 16:18:51 +01:00
|
|
|
fdst, err := rc.GetFs(ctx, in)
|
2020-05-14 17:05:23 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-04-25 07:03:07 +02:00
|
|
|
|
2020-05-14 17:05:23 +02:00
|
|
|
if mountFns[mountType] != nil {
|
2020-07-22 19:31:54 +02:00
|
|
|
VFS := vfs.New(fdst, &vfsOpt)
|
2020-07-23 18:34:54 +02:00
|
|
|
_, unmountFn, err := mountFns[mountType](VFS, mountPoint, &mountOpt)
|
2020-06-04 19:13:10 +02:00
|
|
|
|
2020-06-12 12:41:29 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("mount FAILED: %v", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Add mount to list if mount point was successfully created
|
2020-06-04 19:13:10 +02:00
|
|
|
liveMounts[mountPoint] = MountInfo{
|
|
|
|
unmountFn: unmountFn,
|
|
|
|
MountedOn: time.Now(),
|
|
|
|
Fs: fdst.Name(),
|
|
|
|
MountPoint: mountPoint,
|
2020-07-23 18:34:54 +02:00
|
|
|
VFSOpt: &vfsOpt,
|
|
|
|
MountOpt: &mountOpt,
|
2020-06-04 19:13:10 +02:00
|
|
|
}
|
2020-06-12 12:41:29 +02:00
|
|
|
|
2020-05-14 17:05:23 +02:00
|
|
|
fs.Debugf(nil, "Mount for %s created at %s using %s", fdst.String(), mountPoint, mountType)
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return nil, errors.New("Mount Option specified is not registered, or is invalid")
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
2020-04-25 07:03:07 +02:00
|
|
|
rc.Add(rc.Call{
|
|
|
|
Path: "mount/unmount",
|
|
|
|
AuthRequired: true,
|
|
|
|
Fn: unMountRc,
|
2020-06-04 19:13:10 +02:00
|
|
|
Title: "Unmount selected active mount",
|
2020-04-25 07:03:07 +02:00
|
|
|
Help: `
|
|
|
|
rclone allows Linux, FreeBSD, macOS and Windows to
|
|
|
|
mount any of Rclone's cloud storage systems as a file system with
|
|
|
|
FUSE.
|
|
|
|
|
|
|
|
This takes the following parameters
|
|
|
|
|
|
|
|
- mountPoint: valid path on the local machine where the mount was created (required)
|
|
|
|
|
|
|
|
Eg
|
|
|
|
|
|
|
|
rclone rc mount/unmount mountPoint=/home/<user>/mountPoint
|
|
|
|
`,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-06-04 19:13:10 +02:00
|
|
|
// unMountRc allows the umount command to be run from rc
|
2020-04-25 07:03:07 +02:00
|
|
|
func unMountRc(_ context.Context, in rc.Params) (out rc.Params, err error) {
|
|
|
|
mountPoint, err := in.GetString("mountPoint")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-06-05 20:32:56 +02:00
|
|
|
mountMu.Lock()
|
|
|
|
defer mountMu.Unlock()
|
2020-06-04 19:13:10 +02:00
|
|
|
err = performUnMount(mountPoint)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
2020-04-25 07:03:07 +02:00
|
|
|
|
2020-05-14 17:05:23 +02:00
|
|
|
func init() {
|
|
|
|
rc.Add(rc.Call{
|
|
|
|
Path: "mount/types",
|
|
|
|
AuthRequired: true,
|
|
|
|
Fn: mountTypesRc,
|
|
|
|
Title: "Show all possible mount types",
|
|
|
|
Help: `This shows all possible mount types and returns them as a list.
|
2020-04-25 07:03:07 +02:00
|
|
|
|
2020-05-14 17:05:23 +02:00
|
|
|
This takes no parameters and returns
|
2020-04-25 07:03:07 +02:00
|
|
|
|
2020-05-14 17:05:23 +02:00
|
|
|
- mountTypes: list of mount types
|
2020-04-25 07:03:07 +02:00
|
|
|
|
2020-05-14 17:05:23 +02:00
|
|
|
The mount types are strings like "mount", "mount2", "cmount" and can
|
|
|
|
be passed to mount/mount as the mountType parameter.
|
2020-04-25 07:03:07 +02:00
|
|
|
|
2020-05-14 17:05:23 +02:00
|
|
|
Eg
|
|
|
|
|
|
|
|
rclone rc mount/types
|
|
|
|
`,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// mountTypesRc returns a list of available mount types.
|
|
|
|
func mountTypesRc(_ context.Context, in rc.Params) (out rc.Params, err error) {
|
|
|
|
var mountTypes = []string{}
|
2020-06-05 20:32:56 +02:00
|
|
|
mountMu.Lock()
|
|
|
|
defer mountMu.Unlock()
|
2020-05-14 17:05:23 +02:00
|
|
|
for mountType := range mountFns {
|
|
|
|
mountTypes = append(mountTypes, mountType)
|
2020-04-25 07:03:07 +02:00
|
|
|
}
|
2020-05-14 17:05:23 +02:00
|
|
|
sort.Strings(mountTypes)
|
|
|
|
return rc.Params{
|
|
|
|
"mountTypes": mountTypes,
|
|
|
|
}, nil
|
2020-04-25 07:03:07 +02:00
|
|
|
}
|
2020-06-04 19:13:10 +02:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
rc.Add(rc.Call{
|
|
|
|
Path: "mount/listmounts",
|
|
|
|
AuthRequired: true,
|
|
|
|
Fn: listMountsRc,
|
|
|
|
Title: "Show current mount points",
|
|
|
|
Help: `This shows currently mounted points, which can be used for performing an unmount
|
|
|
|
|
|
|
|
This takes no parameters and returns
|
|
|
|
|
|
|
|
- mountPoints: list of current mount points
|
|
|
|
|
|
|
|
Eg
|
|
|
|
|
|
|
|
rclone rc mount/listmounts
|
|
|
|
`,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// listMountsRc returns a list of current mounts
|
|
|
|
func listMountsRc(_ context.Context, in rc.Params) (out rc.Params, err error) {
|
|
|
|
var mountTypes = []MountInfo{}
|
2020-06-05 20:32:56 +02:00
|
|
|
mountMu.Lock()
|
|
|
|
defer mountMu.Unlock()
|
2020-06-04 19:13:10 +02:00
|
|
|
for _, a := range liveMounts {
|
|
|
|
mountTypes = append(mountTypes, a)
|
|
|
|
}
|
|
|
|
return rc.Params{
|
|
|
|
"mountPoints": mountTypes,
|
|
|
|
}, nil
|
|
|
|
}
|
2020-06-05 20:32:56 +02:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
rc.Add(rc.Call{
|
|
|
|
Path: "mount/unmountall",
|
|
|
|
AuthRequired: true,
|
|
|
|
Fn: unmountAll,
|
|
|
|
Title: "Show current mount points",
|
|
|
|
Help: `This shows currently mounted points, which can be used for performing an unmount
|
|
|
|
|
|
|
|
This takes no parameters and returns error if unmount does not succeed.
|
|
|
|
|
|
|
|
Eg
|
|
|
|
|
|
|
|
rclone rc mount/unmountall
|
|
|
|
`,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// unmountAll unmounts all the created mounts
|
|
|
|
func unmountAll(_ context.Context, in rc.Params) (out rc.Params, err error) {
|
|
|
|
mountMu.Lock()
|
|
|
|
defer mountMu.Unlock()
|
|
|
|
for key, mountInfo := range liveMounts {
|
|
|
|
err = performUnMount(mountInfo.MountPoint)
|
|
|
|
if err != nil {
|
|
|
|
fs.Debugf(nil, "Couldn't unmount : %s", key)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// performUnMount unmounts the specified mountPoint
|
|
|
|
func performUnMount(mountPoint string) (err error) {
|
|
|
|
mountInfo, ok := liveMounts[mountPoint]
|
|
|
|
if ok {
|
|
|
|
err := mountInfo.unmountFn()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
delete(liveMounts, mountPoint)
|
|
|
|
} else {
|
|
|
|
return errors.New("mount not found")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|