forked from TrueCloudLab/rclone
mount: avoid incorrect or premature overlap check on windows
See: #6234
This commit is contained in:
parent
5b82576dbf
commit
e2afd00118
7 changed files with 60 additions and 41 deletions
|
@ -149,14 +149,14 @@ func waitFor(fn func() bool) (ok bool) {
|
||||||
// report an error when fusermount is called.
|
// report an error when fusermount is called.
|
||||||
func mount(VFS *vfs.VFS, mountPath string, opt *mountlib.Options) (<-chan error, func() error, error) {
|
func mount(VFS *vfs.VFS, mountPath string, opt *mountlib.Options) (<-chan error, func() error, error) {
|
||||||
// Get mountpoint using OS specific logic
|
// Get mountpoint using OS specific logic
|
||||||
mountpoint, err := getMountpoint(mountPath, opt)
|
f := VFS.Fs()
|
||||||
|
mountpoint, err := getMountpoint(f, mountPath, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
fs.Debugf(nil, "Mounting on %q (%q)", mountpoint, opt.VolumeName)
|
fs.Debugf(nil, "Mounting on %q (%q)", mountpoint, opt.VolumeName)
|
||||||
|
|
||||||
// Create underlying FS
|
// Create underlying FS
|
||||||
f := VFS.Fs()
|
|
||||||
fsys := NewFS(VFS)
|
fsys := NewFS(VFS)
|
||||||
host := fuse.NewFileSystemHost(fsys)
|
host := fuse.NewFileSystemHost(fsys)
|
||||||
host.SetCapReaddirPlus(true) // only works on Windows
|
host.SetCapReaddirPlus(true) // only works on Windows
|
||||||
|
|
|
@ -9,9 +9,10 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/rclone/rclone/cmd/mountlib"
|
"github.com/rclone/rclone/cmd/mountlib"
|
||||||
|
"github.com/rclone/rclone/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getMountpoint(mountPath string, opt *mountlib.Options) (string, error) {
|
func getMountpoint(f fs.Fs, mountPath string, opt *mountlib.Options) (string, error) {
|
||||||
fi, err := os.Stat(mountPath)
|
fi, err := os.Stat(mountPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to retrieve mount path information: %w", err)
|
return "", fmt.Errorf("failed to retrieve mount path information: %w", err)
|
||||||
|
@ -19,5 +20,11 @@ func getMountpoint(mountPath string, opt *mountlib.Options) (string, error) {
|
||||||
if !fi.IsDir() {
|
if !fi.IsDir() {
|
||||||
return "", errors.New("mount path is not a directory")
|
return "", errors.New("mount path is not a directory")
|
||||||
}
|
}
|
||||||
|
if err = mountlib.CheckOverlap(f, mountPath); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if err = mountlib.CheckAllowNonEmpty(mountPath, opt); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
return mountPath, nil
|
return mountPath, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ func handleNetworkShareMountpath(mountpath string, opt *mountlib.Options) (strin
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleLocalMountpath handles the case where mount path is a local file system path.
|
// handleLocalMountpath handles the case where mount path is a local file system path.
|
||||||
func handleLocalMountpath(mountpath string, opt *mountlib.Options) (string, error) {
|
func handleLocalMountpath(f fs.Fs, mountpath string, opt *mountlib.Options) (string, error) {
|
||||||
// Assuming path is drive letter or directory path, not network share (UNC) path.
|
// Assuming path is drive letter or directory path, not network share (UNC) path.
|
||||||
// If drive letter: Must be given as a single character followed by ":" and nothing else.
|
// If drive letter: Must be given as a single character followed by ":" and nothing else.
|
||||||
// Else, assume directory path: Directory must not exist, but its parent must.
|
// Else, assume directory path: Directory must not exist, but its parent must.
|
||||||
|
@ -125,6 +125,9 @@ func handleLocalMountpath(mountpath string, opt *mountlib.Options) (string, erro
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("failed to retrieve mountpoint directory parent information: %w", err)
|
return "", fmt.Errorf("failed to retrieve mountpoint directory parent information: %w", err)
|
||||||
}
|
}
|
||||||
|
if err = mountlib.CheckOverlap(f, mountpath); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return mountpath, nil
|
return mountpath, nil
|
||||||
}
|
}
|
||||||
|
@ -158,9 +161,19 @@ func handleVolumeName(opt *mountlib.Options, volumeName string) {
|
||||||
|
|
||||||
// getMountpoint handles mounting details on Windows,
|
// getMountpoint handles mounting details on Windows,
|
||||||
// where disk and network based file systems are treated different.
|
// where disk and network based file systems are treated different.
|
||||||
func getMountpoint(mountpath string, opt *mountlib.Options) (mountpoint string, err error) {
|
func getMountpoint(f fs.Fs, mountpath string, opt *mountlib.Options) (mountpoint string, err error) {
|
||||||
|
// Inform about some options not relevant in this mode
|
||||||
|
if opt.AllowNonEmpty {
|
||||||
|
fs.Logf(nil, "--allow-non-empty flag does nothing on Windows")
|
||||||
|
}
|
||||||
|
if opt.AllowRoot {
|
||||||
|
fs.Logf(nil, "--allow-root flag does nothing on Windows")
|
||||||
|
}
|
||||||
|
if opt.AllowOther {
|
||||||
|
fs.Logf(nil, "--allow-other flag does nothing on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
// First handle mountpath
|
// Handle mountpath
|
||||||
var volumeName string
|
var volumeName string
|
||||||
if isDefaultPath(mountpath) {
|
if isDefaultPath(mountpath) {
|
||||||
// Mount path indicates defaults, which will automatically pick an unused drive letter.
|
// Mount path indicates defaults, which will automatically pick an unused drive letter.
|
||||||
|
@ -172,10 +185,10 @@ func getMountpoint(mountpath string, opt *mountlib.Options) (mountpoint string,
|
||||||
volumeName = mountpath[1:] // WinFsp requires volume prefix as UNC-like path but with only a single backslash
|
volumeName = mountpath[1:] // WinFsp requires volume prefix as UNC-like path but with only a single backslash
|
||||||
} else {
|
} else {
|
||||||
// Mount path is drive letter or directory path.
|
// Mount path is drive letter or directory path.
|
||||||
mountpoint, err = handleLocalMountpath(mountpath, opt)
|
mountpoint, err = handleLocalMountpath(f, mountpath, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second handle volume name
|
// Handle volume name
|
||||||
handleVolumeName(opt, volumeName)
|
handleVolumeName(opt, volumeName)
|
||||||
|
|
||||||
// Done, return mountpoint to be used, together with updated mount options.
|
// Done, return mountpoint to be used, together with updated mount options.
|
||||||
|
|
|
@ -69,9 +69,17 @@ func mountOptions(VFS *vfs.VFS, device string, opt *mountlib.Options) (options [
|
||||||
// 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(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (<-chan error, func() error, error) {
|
func mount(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (<-chan error, func() error, error) {
|
||||||
|
f := VFS.Fs()
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
fs.Logf(nil, "macOS users: please try \"rclone cmount\" as it will be the default in v1.54")
|
fs.Logf(nil, "macOS users: please try \"rclone cmount\" as it will be the default in v1.54")
|
||||||
}
|
}
|
||||||
|
if err := mountlib.CheckOverlap(f, mountpoint); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := mountlib.CheckAllowNonEmpty(mountpoint, opt); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
fs.Debugf(f, "Mounting on %q", mountpoint)
|
||||||
|
|
||||||
if opt.DebugFUSE {
|
if opt.DebugFUSE {
|
||||||
fuse.Debug = func(msg interface{}) {
|
fuse.Debug = func(msg interface{}) {
|
||||||
|
@ -79,8 +87,6 @@ func mount(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (<-chan error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f := VFS.Fs()
|
|
||||||
fs.Debugf(f, "Mounting on %q", mountpoint)
|
|
||||||
c, err := fuse.Mount(mountpoint, mountOptions(VFS, opt.DeviceName, opt)...)
|
c, err := fuse.Mount(mountpoint, mountOptions(VFS, opt.DeviceName, opt)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -145,9 +145,16 @@ func mountOptions(fsys *FS, f fs.Fs, opt *mountlib.Options) (mountOpts *fuse.Mou
|
||||||
// report an error when fusermount is called.
|
// report an error when fusermount is called.
|
||||||
func mount(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (<-chan error, func() error, error) {
|
func mount(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (<-chan error, func() error, error) {
|
||||||
f := VFS.Fs()
|
f := VFS.Fs()
|
||||||
|
if err := mountlib.CheckOverlap(f, mountpoint); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := mountlib.CheckAllowNonEmpty(mountpoint, opt); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
fs.Debugf(f, "Mounting on %q", mountpoint)
|
fs.Debugf(f, "Mounting on %q", mountpoint)
|
||||||
|
|
||||||
fsys := NewFS(VFS, opt)
|
fsys := NewFS(VFS, opt)
|
||||||
|
|
||||||
// nodeFsOpts := &fusefs.PathNodeFsOptions{
|
// nodeFsOpts := &fusefs.PathNodeFsOptions{
|
||||||
// ClientInodes: false,
|
// ClientInodes: false,
|
||||||
// Debug: mountlib.DebugFUSE,
|
// Debug: mountlib.DebugFUSE,
|
||||||
|
|
|
@ -237,13 +237,8 @@ func NewMountCommand(commandName string, hidden bool, mount MountFn) *cobra.Comm
|
||||||
|
|
||||||
// Mount the remote at mountpoint
|
// Mount the remote at mountpoint
|
||||||
func (m *MountPoint) Mount() (daemon *os.Process, err error) {
|
func (m *MountPoint) Mount() (daemon *os.Process, err error) {
|
||||||
if err = m.CheckOverlap(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = m.CheckAllowed(); err != nil {
|
// Ensure sensible defaults
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m.SetVolumeName(m.MountOpt.VolumeName)
|
m.SetVolumeName(m.MountOpt.VolumeName)
|
||||||
m.SetDeviceName(m.MountOpt.DeviceName)
|
m.SetDeviceName(m.MountOpt.DeviceName)
|
||||||
|
|
||||||
|
|
|
@ -34,22 +34,22 @@ func ClipBlocks(b *uint64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckOverlap checks that root doesn't overlap with mountpoint
|
// CheckOverlap checks that root doesn't overlap with a mountpoint
|
||||||
func (m *MountPoint) CheckOverlap() error {
|
func CheckOverlap(f fs.Fs, mountpoint string) error {
|
||||||
name := m.Fs.Name()
|
name := f.Name()
|
||||||
if name != "" && name != "local" {
|
if name != "" && name != "local" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
rootAbs := absPath(m.Fs.Root())
|
rootAbs := absPath(f.Root())
|
||||||
mountpointAbs := absPath(m.MountPoint)
|
mountpointAbs := absPath(mountpoint)
|
||||||
if strings.HasPrefix(rootAbs, mountpointAbs) || strings.HasPrefix(mountpointAbs, rootAbs) {
|
if strings.HasPrefix(rootAbs, mountpointAbs) || strings.HasPrefix(mountpointAbs, rootAbs) {
|
||||||
const msg = "mount point %q and directory to be mounted %q mustn't overlap"
|
const msg = "mount point %q (%q) and directory to be mounted %q (%q) mustn't overlap"
|
||||||
return fmt.Errorf(msg, m.MountPoint, m.Fs.Root())
|
return fmt.Errorf(msg, mountpoint, mountpointAbs, f.Root(), rootAbs)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// absPath is a helper function for MountPoint.CheckOverlap
|
// absPath is a helper function for CheckOverlap
|
||||||
func absPath(path string) string {
|
func absPath(path string) string {
|
||||||
if abs, err := filepath.EvalSymlinks(path); err == nil {
|
if abs, err := filepath.EvalSymlinks(path); err == nil {
|
||||||
path = abs
|
path = abs
|
||||||
|
@ -58,30 +58,21 @@ func absPath(path string) string {
|
||||||
path = abs
|
path = abs
|
||||||
}
|
}
|
||||||
path = filepath.ToSlash(path)
|
path = filepath.ToSlash(path)
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// Removes any UNC long path prefix to make sure a simple HasPrefix test
|
||||||
|
// in CheckOverlap works when one is UNC (root) and one is not (mountpoint).
|
||||||
|
path = strings.TrimPrefix(path, `//?/`)
|
||||||
|
}
|
||||||
if !strings.HasSuffix(path, "/") {
|
if !strings.HasSuffix(path, "/") {
|
||||||
path += "/"
|
path += "/"
|
||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckAllowed informs about ignored flags on Windows. If not on Windows
|
// CheckAllowNonEmpty checks --allow-non-empty flag, and if not used verifies that mountpoint is empty.
|
||||||
// and not --allow-non-empty flag is used, verify that mountpoint is empty.
|
func CheckAllowNonEmpty(mountpoint string, opt *Options) error {
|
||||||
func (m *MountPoint) CheckAllowed() error {
|
|
||||||
opt := &m.MountOpt
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
if opt.AllowNonEmpty {
|
|
||||||
fs.Logf(nil, "--allow-non-empty flag does nothing on Windows")
|
|
||||||
}
|
|
||||||
if opt.AllowRoot {
|
|
||||||
fs.Logf(nil, "--allow-root flag does nothing on Windows")
|
|
||||||
}
|
|
||||||
if opt.AllowOther {
|
|
||||||
fs.Logf(nil, "--allow-other flag does nothing on Windows")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !opt.AllowNonEmpty {
|
if !opt.AllowNonEmpty {
|
||||||
return CheckMountEmpty(m.MountPoint)
|
return CheckMountEmpty(mountpoint)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue