mirror of
https://github.com/rclone/rclone.git
synced 2025-12-31 15:43:53 +00:00
Compare commits
2 Commits
pr-6811-dr
...
mount-wind
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0093e23e42 | ||
|
|
11443e4491 |
@@ -202,7 +202,7 @@ func init() {
|
||||
m.Set("root_folder_id", "appDataFolder")
|
||||
}
|
||||
|
||||
if opt.ServiceAccountFile == "" && opt.ServiceAccountCredentials == "" && !opt.EnvAuth {
|
||||
if opt.ServiceAccountFile == "" && opt.ServiceAccountCredentials == "" {
|
||||
return oauthutil.ConfigOut("teamdrive", &oauthutil.Options{
|
||||
OAuth2Config: driveConfig,
|
||||
})
|
||||
@@ -598,18 +598,6 @@ resource key is no needed.
|
||||
// Encode invalid UTF-8 bytes as json doesn't handle them properly.
|
||||
// Don't encode / as it's a valid name character in drive.
|
||||
Default: encoder.EncodeInvalidUtf8,
|
||||
}, {
|
||||
Name: "env_auth",
|
||||
Help: "Get IAM credentials from runtime (environment variables or instance meta data if no env vars).\n\nOnly applies if service_account_file and service_account_credentials is blank.",
|
||||
Default: false,
|
||||
Advanced: true,
|
||||
Examples: []fs.OptionExample{{
|
||||
Value: "false",
|
||||
Help: "Enter AWS credentials in the next step.",
|
||||
}, {
|
||||
Value: "true",
|
||||
Help: "Get GCP IAM credentials from the environment (env vars or IAM).",
|
||||
}},
|
||||
}}...),
|
||||
})
|
||||
|
||||
@@ -666,7 +654,6 @@ type Options struct {
|
||||
SkipDanglingShortcuts bool `config:"skip_dangling_shortcuts"`
|
||||
ResourceKey string `config:"resource_key"`
|
||||
Enc encoder.MultiEncoder `config:"encoding"`
|
||||
EnvAuth bool `config:"env_auth"`
|
||||
}
|
||||
|
||||
// Fs represents a remote drive server
|
||||
@@ -1135,12 +1122,6 @@ func createOAuthClient(ctx context.Context, opt *Options, name string, m configm
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create oauth client from service account: %w", err)
|
||||
}
|
||||
} else if opt.EnvAuth {
|
||||
scopes := driveScopes(opt.Scope)
|
||||
oAuthClient, err = google.DefaultClient(ctx, scopes...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create client from environment: %w", err)
|
||||
}
|
||||
} else {
|
||||
oAuthClient, _, err = oauthutil.NewClientWithBaseClient(ctx, name, m, driveConfig, getClient(ctx, opt))
|
||||
if err != nil {
|
||||
|
||||
@@ -26,5 +26,6 @@ func getMountpoint(f fs.Fs, mountPath string, opt *mountlib.Options) (string, er
|
||||
if err = mountlib.CheckAllowNonEmpty(mountPath, opt); err != nil {
|
||||
return "", err
|
||||
}
|
||||
opt.VolumeName = mountlib.MakeVolumeNameValidOnUnix(opt.VolumeName)
|
||||
return mountPath, nil
|
||||
}
|
||||
|
||||
@@ -9,9 +9,11 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/rclone/rclone/cmd/mountlib"
|
||||
"github.com/rclone/rclone/fs"
|
||||
"github.com/rclone/rclone/lib/encoder"
|
||||
"github.com/rclone/rclone/lib/file"
|
||||
)
|
||||
|
||||
@@ -19,10 +21,13 @@ var isDriveRegex = regexp.MustCompile(`^[a-zA-Z]\:$`)
|
||||
var isDriveRootPathRegex = regexp.MustCompile(`^[a-zA-Z]\:\\$`)
|
||||
var isDriveOrRootPathRegex = regexp.MustCompile(`^[a-zA-Z]\:\\?$`)
|
||||
var isNetworkSharePathRegex = regexp.MustCompile(`^\\\\[^\\\?]+\\[^\\]`)
|
||||
var isAnyPathSeparatorRegex = regexp.MustCompile(`[/\\]+`) // Matches any path separators, slash or backslash, or sequences of them
|
||||
|
||||
// isNetworkSharePath returns true if the given string is a valid network share path,
|
||||
// in the basic UNC format "\\Server\Share\Path", where the first two path components
|
||||
// are required ("\\Server\Share", which represents the volume).
|
||||
// isNetworkSharePath returns true if the given string is a network share path,
|
||||
// in the basic UNC format "\\Server\Share\Path". The first two path components
|
||||
// are required ("\\Server\Share"), and represents the volume. The rest of the
|
||||
// string can be anything, i.e. can be a nested path ("\\Server\Share\Path\Path\Path").
|
||||
// Actual validity of the path, e.g. if it contains invalid characters, is not considered.
|
||||
// Extended-length UNC format "\\?\UNC\Server\Share\Path" is not considered, as it is
|
||||
// not supported by cgofuse/winfsp, so returns false for any paths with prefix "\\?\".
|
||||
// Note: There is a UNCPath function in lib/file, but it refers to any extended-length
|
||||
@@ -111,7 +116,7 @@ func handleLocalMountpath(f fs.Fs, mountpath string, opt *mountlib.Options) (str
|
||||
// Drive letter string can be used as is, since we have already checked it does not exist,
|
||||
// but directory path needs more checks.
|
||||
if opt.NetworkMode {
|
||||
fs.Errorf(nil, "Ignoring --network-mode as it is not supported with directory mountpoint")
|
||||
fs.Debugf(nil, "Ignoring --network-mode as it is not supported with directory mountpoint")
|
||||
opt.NetworkMode = false
|
||||
}
|
||||
var err error
|
||||
@@ -132,30 +137,47 @@ func handleLocalMountpath(f fs.Fs, mountpath string, opt *mountlib.Options) (str
|
||||
return mountpath, nil
|
||||
}
|
||||
|
||||
// networkSharePathEncoder is an encoder used to make strings valid as (part of) Windows network share UNC paths
|
||||
const networkSharePathEncoder = (encoder.EncodeZero | // NUL(0x00)
|
||||
encoder.EncodeCtl | // CTRL(0x01-0x1F)
|
||||
encoder.EncodeDel | // DEL(0x7F)
|
||||
encoder.EncodeWin | // :?"*<>|
|
||||
encoder.EncodeInvalidUtf8) // Also encode invalid UTF-8 bytes as Go can't convert them to UTF-16.
|
||||
|
||||
// encodeNetworkSharePath makes a string valid to use as (part of) a Windows network share UNC path.
|
||||
// Using backslash as path separator here, but forward slashes would also be treated as
|
||||
// path separators by the library, and therefore does not encode either of them. For convenience,
|
||||
// normalizes to backslashes-only. UNC paths always start with two path separators, but WinFsp
|
||||
// requires volume prefix as UNC-like path but with only a single backslash prefix, and multiple
|
||||
// separators are not valid in any other parts of network share paths, so therefore (unlike what
|
||||
// filepath.FromSlash would do) replaces multiple separators with a single one (like filpath.Clean
|
||||
// would do, but it does also more). A trailing path separator would just be ignored, but we
|
||||
// remove it here as well for convenience.
|
||||
func encodeNetworkSharePath(volumeName string) string {
|
||||
return networkSharePathEncoder.Encode(strings.TrimRight(isAnyPathSeparatorRegex.ReplaceAllString(volumeName, `\`), `\`))
|
||||
}
|
||||
|
||||
// handleVolumeName handles the volume name option.
|
||||
func handleVolumeName(opt *mountlib.Options, volumeName string) {
|
||||
// If volumeName parameter is set, then just set that into options replacing any existing value.
|
||||
// Else, ensure the volume name option is a valid network share UNC path if network mode,
|
||||
func handleVolumeName(opt *mountlib.Options) {
|
||||
// Ensure the volume name option is a valid network share UNC path if network mode,
|
||||
// and ensure network mode if configured volume name is already UNC path.
|
||||
if volumeName != "" {
|
||||
opt.VolumeName = volumeName
|
||||
} else if opt.VolumeName != "" { // Should always be true due to code in mountlib caller
|
||||
if opt.VolumeName != "" { // Should always be true due to code in mountlib caller
|
||||
// Use value of given volume name option, but check if it is disk volume name or network volume prefix
|
||||
if isNetworkSharePath(opt.VolumeName) {
|
||||
// Specified volume name is network share UNC path, assume network mode and use it as volume prefix
|
||||
opt.VolumeName = opt.VolumeName[1:] // WinFsp requires volume prefix as UNC-like path but with only a single backslash
|
||||
opt.VolumeName = encodeNetworkSharePath(opt.VolumeName[1:]) // We know from isNetworkSharePath it has a duplicate path separator prefix, so removes that right away (but encodeNetworkSharePath would remove it also)
|
||||
if !opt.NetworkMode {
|
||||
// Specified volume name is network share UNC path, force network mode and use it as volume prefix
|
||||
fs.Debugf(nil, "Forcing network mode due to network share (UNC) volume name")
|
||||
opt.NetworkMode = true
|
||||
}
|
||||
} else if opt.NetworkMode {
|
||||
// Plain volume name treated as share name in network mode, append to hard coded "\\server" prefix to get full volume prefix.
|
||||
opt.VolumeName = "\\server\\" + opt.VolumeName
|
||||
// Specified volume name is not a valid network share UNC path, but network mode is enabled, so append to a hard coded server prefix and use it as volume prefix
|
||||
opt.VolumeName = `\server\` + strings.TrimLeft(encodeNetworkSharePath(opt.VolumeName), `\`)
|
||||
}
|
||||
} else if opt.NetworkMode {
|
||||
// Hard coded default
|
||||
opt.VolumeName = "\\server\\share"
|
||||
// Use hard coded default
|
||||
opt.VolumeName = `\server\share`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,22 +196,27 @@ func getMountpoint(f fs.Fs, mountpath string, opt *mountlib.Options) (mountpoint
|
||||
}
|
||||
|
||||
// Handle mountpath
|
||||
var volumeName string
|
||||
if isDefaultPath(mountpath) {
|
||||
// Mount path indicates defaults, which will automatically pick an unused drive letter.
|
||||
mountpoint, err = handleDefaultMountpath()
|
||||
if mountpoint, err = handleDefaultMountpath(); err != nil {
|
||||
return
|
||||
}
|
||||
} else if isNetworkSharePath(mountpath) {
|
||||
// Mount path is a valid network share path (UNC format, "\\Server\Share" prefix).
|
||||
mountpoint, err = handleNetworkShareMountpath(mountpath, opt)
|
||||
// In this case the volume name is taken from the mount path, will replace any existing volume name option.
|
||||
volumeName = mountpath[1:] // WinFsp requires volume prefix as UNC-like path but with only a single backslash
|
||||
if mountpoint, err = handleNetworkShareMountpath(mountpath, opt); err != nil {
|
||||
return
|
||||
}
|
||||
// In this case the volume name is taken from the mount path, it replaces any existing volume name option.
|
||||
opt.VolumeName = mountpath
|
||||
} else {
|
||||
// Mount path is drive letter or directory path.
|
||||
mountpoint, err = handleLocalMountpath(f, mountpath, opt)
|
||||
if mountpoint, err = handleLocalMountpath(f, mountpath, opt); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Handle volume name
|
||||
handleVolumeName(opt, volumeName)
|
||||
handleVolumeName(opt)
|
||||
|
||||
// Done, return mountpoint to be used, together with updated mount options.
|
||||
if opt.NetworkMode {
|
||||
|
||||
@@ -79,6 +79,7 @@ func mount(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (<-chan error
|
||||
if err := mountlib.CheckAllowNonEmpty(mountpoint, opt); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.VolumeName = mountlib.MakeVolumeNameValidOnUnix(opt.VolumeName)
|
||||
fs.Debugf(f, "Mounting on %q", mountpoint)
|
||||
|
||||
if opt.DebugFUSE {
|
||||
|
||||
@@ -151,6 +151,7 @@ func mount(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (<-chan error
|
||||
if err := mountlib.CheckAllowNonEmpty(mountpoint, opt); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.VolumeName = mountlib.MakeVolumeNameValidOnUnix(opt.VolumeName)
|
||||
fs.Debugf(f, "Mounting on %q", mountpoint)
|
||||
|
||||
fsys := NewFS(VFS, opt)
|
||||
|
||||
@@ -57,6 +57,7 @@ var DefaultOpt = Options{
|
||||
NoAppleDouble: true, // use noappledouble by default
|
||||
NoAppleXattr: false, // do not use noapplexattr by default
|
||||
AsyncRead: true, // do async reads by default
|
||||
NetworkMode: true, // use network mode by default (Windows only)
|
||||
}
|
||||
|
||||
type (
|
||||
@@ -239,8 +240,12 @@ func NewMountCommand(commandName string, hidden bool, mount MountFn) *cobra.Comm
|
||||
func (m *MountPoint) Mount() (daemon *os.Process, err error) {
|
||||
|
||||
// Ensure sensible defaults
|
||||
m.SetVolumeName(m.MountOpt.VolumeName)
|
||||
m.SetDeviceName(m.MountOpt.DeviceName)
|
||||
if m.MountOpt.VolumeName == "" {
|
||||
m.MountOpt.VolumeName = fs.ConfigString(m.Fs)
|
||||
}
|
||||
if m.MountOpt.DeviceName == "" {
|
||||
m.MountOpt.DeviceName = fs.ConfigString(m.Fs)
|
||||
}
|
||||
|
||||
// Start background task if --daemon is specified
|
||||
if m.MountOpt.Daemon {
|
||||
|
||||
@@ -97,29 +97,10 @@ func checkMountEmpty(mountpoint string) error {
|
||||
return fmt.Errorf(msg+": %w", mountpoint, err)
|
||||
}
|
||||
|
||||
// SetVolumeName with sensible default
|
||||
func (m *MountPoint) SetVolumeName(vol string) {
|
||||
if vol == "" {
|
||||
vol = fs.ConfigString(m.Fs)
|
||||
}
|
||||
m.MountOpt.SetVolumeName(vol)
|
||||
}
|
||||
|
||||
// SetVolumeName removes special characters from volume name if necessary
|
||||
func (o *Options) SetVolumeName(vol string) {
|
||||
vol = strings.ReplaceAll(vol, ":", " ")
|
||||
vol = strings.ReplaceAll(vol, "/", " ")
|
||||
vol = strings.TrimSpace(vol)
|
||||
if runtime.GOOS == "windows" && len(vol) > 32 {
|
||||
vol = vol[:32]
|
||||
}
|
||||
o.VolumeName = vol
|
||||
}
|
||||
|
||||
// SetDeviceName with sensible default
|
||||
func (m *MountPoint) SetDeviceName(dev string) {
|
||||
if dev == "" {
|
||||
dev = fs.ConfigString(m.Fs)
|
||||
}
|
||||
m.MountOpt.DeviceName = dev
|
||||
// MakeVolumeNameValidOnUnix takes a volume name and returns a variant that is valid on unix systems.
|
||||
func MakeVolumeNameValidOnUnix(volumeName string) string {
|
||||
volumeName = strings.ReplaceAll(volumeName, ":", " ")
|
||||
volumeName = strings.ReplaceAll(volumeName, "/", " ")
|
||||
volumeName = strings.TrimSpace(volumeName)
|
||||
return volumeName
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user