mirror of
https://github.com/rclone/rclone.git
synced 2025-12-15 15:53:41 +00:00
Compare commits
2 Commits
fix-macos-
...
fix-4883-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
544757a33b | ||
|
|
bfcf6baf93 |
@@ -207,7 +207,7 @@ func init() {
|
||||
}
|
||||
err = configTeamDrive(ctx, opt, m, name)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to configure Shared Drive: %v", err)
|
||||
log.Fatalf("Failed to configure team drive: %v", err)
|
||||
}
|
||||
},
|
||||
Options: append(driveOAuthOptions(), []fs.Option{{
|
||||
@@ -247,7 +247,7 @@ a non root folder as its starting point.
|
||||
Advanced: true,
|
||||
}, {
|
||||
Name: "team_drive",
|
||||
Help: "ID of the Shared Drive (Team Drive)",
|
||||
Help: "ID of the Team Drive",
|
||||
Hide: fs.OptionHideConfigurator,
|
||||
Advanced: true,
|
||||
}, {
|
||||
@@ -666,7 +666,7 @@ func (f *Fs) shouldRetry(err error) (bool, error) {
|
||||
fs.Errorf(f, "Received download limit error: %v", err)
|
||||
return false, fserrors.FatalError(err)
|
||||
} else if f.opt.StopOnUploadLimit && reason == "teamDriveFileLimitExceeded" {
|
||||
fs.Errorf(f, "Received Shared Drive file limit error: %v", err)
|
||||
fs.Errorf(f, "Received team drive file limit error: %v", err)
|
||||
return false, fserrors.FatalError(err)
|
||||
}
|
||||
}
|
||||
@@ -955,24 +955,24 @@ func configTeamDrive(ctx context.Context, opt *Options, m configmap.Mapper, name
|
||||
return nil
|
||||
}
|
||||
if opt.TeamDriveID == "" {
|
||||
fmt.Printf("Configure this as a Shared Drive (Team Drive)?\n")
|
||||
fmt.Printf("Configure this as a team drive?\n")
|
||||
} else {
|
||||
fmt.Printf("Change current Shared Drive (Team Drive) ID %q?\n", opt.TeamDriveID)
|
||||
fmt.Printf("Change current team drive ID %q?\n", opt.TeamDriveID)
|
||||
}
|
||||
if !config.Confirm(false) {
|
||||
return nil
|
||||
}
|
||||
f, err := newFs(ctx, name, "", m)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to make Fs to list Shared Drives")
|
||||
return errors.Wrap(err, "failed to make Fs to list teamdrives")
|
||||
}
|
||||
fmt.Printf("Fetching Shared Drive list...\n")
|
||||
fmt.Printf("Fetching team drive list...\n")
|
||||
teamDrives, err := f.listTeamDrives(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(teamDrives) == 0 {
|
||||
fmt.Printf("No Shared Drives found in your account")
|
||||
fmt.Printf("No team drives found in your account")
|
||||
return nil
|
||||
}
|
||||
var driveIDs, driveNames []string
|
||||
@@ -980,7 +980,7 @@ func configTeamDrive(ctx context.Context, opt *Options, m configmap.Mapper, name
|
||||
driveIDs = append(driveIDs, teamDrive.Id)
|
||||
driveNames = append(driveNames, teamDrive.Name)
|
||||
}
|
||||
driveID := config.Choose("Enter a Shared Drive ID", driveIDs, driveNames, true)
|
||||
driveID := config.Choose("Enter a Team Drive ID", driveIDs, driveNames, true)
|
||||
m.Set("team_drive", driveID)
|
||||
m.Set("root_folder_id", "")
|
||||
opt.TeamDriveID = driveID
|
||||
@@ -2475,9 +2475,9 @@ func (f *Fs) teamDriveOK(ctx context.Context) (err error) {
|
||||
return f.shouldRetry(err)
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get Shared Drive info")
|
||||
return errors.Wrap(err, "failed to get Team/Shared Drive info")
|
||||
}
|
||||
fs.Debugf(f, "read info from Shared Drive %q", td.Name)
|
||||
fs.Debugf(f, "read info from team drive %q", td.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2963,7 +2963,7 @@ func (f *Fs) listTeamDrives(ctx context.Context) (drives []*drive.TeamDrive, err
|
||||
return defaultFs.shouldRetry(err)
|
||||
})
|
||||
if err != nil {
|
||||
return drives, errors.Wrap(err, "listing Team Drives failed")
|
||||
return drives, errors.Wrap(err, "listing team drives failed")
|
||||
}
|
||||
drives = append(drives, teamDrives.TeamDrives...)
|
||||
if teamDrives.NextPageToken == "" {
|
||||
@@ -3131,8 +3131,8 @@ authenticated with "drive2:" can't read files from "drive:".
|
||||
},
|
||||
}, {
|
||||
Name: "drives",
|
||||
Short: "List the Shared Drives available to this account",
|
||||
Long: `This command lists the Shared Drives (Team Drives) available to this
|
||||
Short: "List the shared drives available to this account",
|
||||
Long: `This command lists the shared drives (teamdrives) available to this
|
||||
account.
|
||||
|
||||
Usage:
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
package cmount
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
@@ -19,11 +18,9 @@ import (
|
||||
"github.com/rclone/rclone/fs"
|
||||
"github.com/rclone/rclone/fs/log"
|
||||
"github.com/rclone/rclone/vfs"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
const fhUnset = ^uint64(0)
|
||||
const tpsBurst = 100
|
||||
|
||||
// FS represents the top level filing system
|
||||
type FS struct {
|
||||
@@ -32,31 +29,19 @@ type FS struct {
|
||||
ready chan (struct{})
|
||||
mu sync.Mutex // to protect the below
|
||||
handles []vfs.Handle
|
||||
destroyed int32 // read/write with sync/atomic
|
||||
tps *rate.Limiter // for limiting number of transactions per second
|
||||
destroyed int32 // read/write with sync/atomic
|
||||
}
|
||||
|
||||
// NewFS makes a new FS
|
||||
func NewFS(VFS *vfs.VFS, opt *mountlib.Options) *FS {
|
||||
func NewFS(VFS *vfs.VFS) *FS {
|
||||
fsys := &FS{
|
||||
VFS: VFS,
|
||||
f: VFS.Fs(),
|
||||
ready: make(chan (struct{})),
|
||||
}
|
||||
if opt.TPSLimit > 0 {
|
||||
fsys.tps = rate.NewLimiter(rate.Limit(opt.TPSLimit), tpsBurst)
|
||||
fs.Infof(nil, "Starting mount transaction limiter: max %g transactions/s with burst %d", opt.TPSLimit, tpsBurst)
|
||||
}
|
||||
return fsys
|
||||
}
|
||||
|
||||
// Limit the number of transactions per second
|
||||
func (fsys *FS) rateLimit() {
|
||||
if fsys.tps != nil {
|
||||
_ = fsys.tps.Wait(context.Background())
|
||||
}
|
||||
}
|
||||
|
||||
// Open a handle returning an integer file handle
|
||||
func (fsys *FS) openHandle(handle vfs.Handle) (fh uint64) {
|
||||
fsys.mu.Lock()
|
||||
@@ -210,7 +195,6 @@ func (fsys *FS) Destroy() {
|
||||
// Getattr reads the attributes for path
|
||||
func (fsys *FS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
|
||||
defer log.Trace(path, "fh=0x%X", fh)("errc=%v", &errc)
|
||||
fsys.rateLimit()
|
||||
node, _, errc := fsys.getNode(path, fh)
|
||||
if errc == 0 {
|
||||
errc = fsys.stat(node, stat)
|
||||
@@ -221,7 +205,6 @@ func (fsys *FS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
|
||||
// Opendir opens path as a directory
|
||||
func (fsys *FS) Opendir(path string) (errc int, fh uint64) {
|
||||
defer log.Trace(path, "")("errc=%d, fh=0x%X", &errc, &fh)
|
||||
fsys.rateLimit()
|
||||
handle, err := fsys.VFS.OpenFile(path, os.O_RDONLY, 0777)
|
||||
if err != nil {
|
||||
return translateError(err), fhUnset
|
||||
@@ -236,7 +219,6 @@ func (fsys *FS) Readdir(dirPath string,
|
||||
fh uint64) (errc int) {
|
||||
itemsRead := -1
|
||||
defer log.Trace(dirPath, "ofst=%d, fh=0x%X", ofst, fh)("items=%d, errc=%d", &itemsRead, &errc)
|
||||
fsys.rateLimit()
|
||||
|
||||
dir, errc := fsys.lookupDir(dirPath)
|
||||
if errc != 0 {
|
||||
@@ -288,14 +270,12 @@ func (fsys *FS) Readdir(dirPath string,
|
||||
// Releasedir finished reading the directory
|
||||
func (fsys *FS) Releasedir(path string, fh uint64) (errc int) {
|
||||
defer log.Trace(path, "fh=0x%X", fh)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
return fsys.closeHandle(fh)
|
||||
}
|
||||
|
||||
// Statfs reads overall stats on the filesystem
|
||||
func (fsys *FS) Statfs(path string, stat *fuse.Statfs_t) (errc int) {
|
||||
defer log.Trace(path, "")("stat=%+v, errc=%d", stat, &errc)
|
||||
fsys.rateLimit()
|
||||
const blockSize = 4096
|
||||
total, _, free := fsys.VFS.Statfs()
|
||||
stat.Blocks = uint64(total) / blockSize // Total data blocks in file system.
|
||||
@@ -315,7 +295,6 @@ func (fsys *FS) Statfs(path string, stat *fuse.Statfs_t) (errc int) {
|
||||
// OpenEx opens a file
|
||||
func (fsys *FS) OpenEx(path string, fi *fuse.FileInfo_t) (errc int) {
|
||||
defer log.Trace(path, "flags=0x%X", fi.Flags)("errc=%d, fh=0x%X", &errc, &fi.Fh)
|
||||
fsys.rateLimit()
|
||||
fi.Fh = fhUnset
|
||||
|
||||
// translate the fuse flags to os flags
|
||||
@@ -346,7 +325,6 @@ func (fsys *FS) Open(path string, flags int) (errc int, fh uint64) {
|
||||
// CreateEx creates and opens a file.
|
||||
func (fsys *FS) CreateEx(filePath string, mode uint32, fi *fuse.FileInfo_t) (errc int) {
|
||||
defer log.Trace(filePath, "flags=0x%X, mode=0%o", fi.Flags, mode)("errc=%d, fh=0x%X", &errc, &fi.Fh)
|
||||
fsys.rateLimit()
|
||||
fi.Fh = fhUnset
|
||||
leaf, parentDir, errc := fsys.lookupParentDir(filePath)
|
||||
if errc != 0 {
|
||||
@@ -378,7 +356,6 @@ func (fsys *FS) Create(filePath string, flags int, mode uint32) (errc int, fh ui
|
||||
// Truncate truncates a file to size
|
||||
func (fsys *FS) Truncate(path string, size int64, fh uint64) (errc int) {
|
||||
defer log.Trace(path, "size=%d, fh=0x%X", size, fh)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
node, handle, errc := fsys.getNode(path, fh)
|
||||
if errc != 0 {
|
||||
return errc
|
||||
@@ -398,7 +375,6 @@ func (fsys *FS) Truncate(path string, size int64, fh uint64) (errc int) {
|
||||
// Read data from file handle
|
||||
func (fsys *FS) Read(path string, buff []byte, ofst int64, fh uint64) (n int) {
|
||||
defer log.Trace(path, "ofst=%d, fh=0x%X", ofst, fh)("n=%d", &n)
|
||||
fsys.rateLimit()
|
||||
handle, errc := fsys.getHandle(fh)
|
||||
if errc != 0 {
|
||||
return errc
|
||||
@@ -414,7 +390,6 @@ func (fsys *FS) Read(path string, buff []byte, ofst int64, fh uint64) (n int) {
|
||||
// Write data to file handle
|
||||
func (fsys *FS) Write(path string, buff []byte, ofst int64, fh uint64) (n int) {
|
||||
defer log.Trace(path, "ofst=%d, fh=0x%X", ofst, fh)("n=%d", &n)
|
||||
fsys.rateLimit()
|
||||
handle, errc := fsys.getHandle(fh)
|
||||
if errc != 0 {
|
||||
return errc
|
||||
@@ -429,7 +404,6 @@ func (fsys *FS) Write(path string, buff []byte, ofst int64, fh uint64) (n int) {
|
||||
// Flush flushes an open file descriptor or path
|
||||
func (fsys *FS) Flush(path string, fh uint64) (errc int) {
|
||||
defer log.Trace(path, "fh=0x%X", fh)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
handle, errc := fsys.getHandle(fh)
|
||||
if errc != 0 {
|
||||
return errc
|
||||
@@ -440,7 +414,6 @@ func (fsys *FS) Flush(path string, fh uint64) (errc int) {
|
||||
// Release closes the file if still open
|
||||
func (fsys *FS) Release(path string, fh uint64) (errc int) {
|
||||
defer log.Trace(path, "fh=0x%X", fh)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
handle, errc := fsys.getHandle(fh)
|
||||
if errc != 0 {
|
||||
return errc
|
||||
@@ -452,7 +425,6 @@ func (fsys *FS) Release(path string, fh uint64) (errc int) {
|
||||
// Unlink removes a file.
|
||||
func (fsys *FS) Unlink(filePath string) (errc int) {
|
||||
defer log.Trace(filePath, "")("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
leaf, parentDir, errc := fsys.lookupParentDir(filePath)
|
||||
if errc != 0 {
|
||||
return errc
|
||||
@@ -463,7 +435,6 @@ func (fsys *FS) Unlink(filePath string) (errc int) {
|
||||
// Mkdir creates a directory.
|
||||
func (fsys *FS) Mkdir(dirPath string, mode uint32) (errc int) {
|
||||
defer log.Trace(dirPath, "mode=0%o", mode)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
leaf, parentDir, errc := fsys.lookupParentDir(dirPath)
|
||||
if errc != 0 {
|
||||
return errc
|
||||
@@ -475,7 +446,6 @@ func (fsys *FS) Mkdir(dirPath string, mode uint32) (errc int) {
|
||||
// Rmdir removes a directory
|
||||
func (fsys *FS) Rmdir(dirPath string) (errc int) {
|
||||
defer log.Trace(dirPath, "")("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
leaf, parentDir, errc := fsys.lookupParentDir(dirPath)
|
||||
if errc != 0 {
|
||||
return errc
|
||||
@@ -486,7 +456,6 @@ func (fsys *FS) Rmdir(dirPath string) (errc int) {
|
||||
// Rename renames a file.
|
||||
func (fsys *FS) Rename(oldPath string, newPath string) (errc int) {
|
||||
defer log.Trace(oldPath, "newPath=%q", newPath)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
return translateError(fsys.VFS.Rename(oldPath, newPath))
|
||||
}
|
||||
|
||||
@@ -498,7 +467,6 @@ var invalidDateCutoff = time.Date(1601, 1, 2, 0, 0, 0, 0, time.UTC)
|
||||
// Utimens changes the access and modification times of a file.
|
||||
func (fsys *FS) Utimens(path string, tmsp []fuse.Timespec) (errc int) {
|
||||
defer log.Trace(path, "tmsp=%+v", tmsp)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
node, errc := fsys.lookupNode(path)
|
||||
if errc != 0 {
|
||||
return errc
|
||||
@@ -519,14 +487,12 @@ func (fsys *FS) Utimens(path string, tmsp []fuse.Timespec) (errc int) {
|
||||
// Mknod creates a file node.
|
||||
func (fsys *FS) Mknod(path string, mode uint32, dev uint64) (errc int) {
|
||||
defer log.Trace(path, "mode=0x%X, dev=0x%X", mode, dev)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
return -fuse.ENOSYS
|
||||
}
|
||||
|
||||
// Fsync synchronizes file contents.
|
||||
func (fsys *FS) Fsync(path string, datasync bool, fh uint64) (errc int) {
|
||||
defer log.Trace(path, "datasync=%v, fh=0x%X", datasync, fh)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
// This is a no-op for rclone
|
||||
return 0
|
||||
}
|
||||
@@ -534,28 +500,24 @@ func (fsys *FS) Fsync(path string, datasync bool, fh uint64) (errc int) {
|
||||
// Link creates a hard link to a file.
|
||||
func (fsys *FS) Link(oldpath string, newpath string) (errc int) {
|
||||
defer log.Trace(oldpath, "newpath=%q", newpath)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
return -fuse.ENOSYS
|
||||
}
|
||||
|
||||
// Symlink creates a symbolic link.
|
||||
func (fsys *FS) Symlink(target string, newpath string) (errc int) {
|
||||
defer log.Trace(target, "newpath=%q", newpath)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
return -fuse.ENOSYS
|
||||
}
|
||||
|
||||
// Readlink reads the target of a symbolic link.
|
||||
func (fsys *FS) Readlink(path string) (errc int, linkPath string) {
|
||||
defer log.Trace(path, "")("linkPath=%q, errc=%d", &linkPath, &errc)
|
||||
fsys.rateLimit()
|
||||
return -fuse.ENOSYS, ""
|
||||
}
|
||||
|
||||
// Chmod changes the permission bits of a file.
|
||||
func (fsys *FS) Chmod(path string, mode uint32) (errc int) {
|
||||
defer log.Trace(path, "mode=0%o", mode)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
// This is a no-op for rclone
|
||||
return 0
|
||||
}
|
||||
@@ -563,7 +525,6 @@ func (fsys *FS) Chmod(path string, mode uint32) (errc int) {
|
||||
// Chown changes the owner and group of a file.
|
||||
func (fsys *FS) Chown(path string, uid uint32, gid uint32) (errc int) {
|
||||
defer log.Trace(path, "uid=%d, gid=%d", uid, gid)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
// This is a no-op for rclone
|
||||
return 0
|
||||
}
|
||||
@@ -571,7 +532,6 @@ func (fsys *FS) Chown(path string, uid uint32, gid uint32) (errc int) {
|
||||
// Access checks file access permissions.
|
||||
func (fsys *FS) Access(path string, mask uint32) (errc int) {
|
||||
defer log.Trace(path, "mask=0%o", mask)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
// This is a no-op for rclone
|
||||
return 0
|
||||
}
|
||||
@@ -579,32 +539,27 @@ func (fsys *FS) Access(path string, mask uint32) (errc int) {
|
||||
// Fsyncdir synchronizes directory contents.
|
||||
func (fsys *FS) Fsyncdir(path string, datasync bool, fh uint64) (errc int) {
|
||||
defer log.Trace(path, "datasync=%v, fh=0x%X", datasync, fh)("errc=%d", &errc)
|
||||
fsys.rateLimit()
|
||||
// This is a no-op for rclone
|
||||
return 0
|
||||
}
|
||||
|
||||
// Setxattr sets extended attributes.
|
||||
func (fsys *FS) Setxattr(path string, name string, value []byte, flags int) (errc int) {
|
||||
fsys.rateLimit()
|
||||
return -fuse.ENOSYS
|
||||
}
|
||||
|
||||
// Getxattr gets extended attributes.
|
||||
func (fsys *FS) Getxattr(path string, name string) (errc int, value []byte) {
|
||||
fsys.rateLimit()
|
||||
return -fuse.ENOSYS, nil
|
||||
}
|
||||
|
||||
// Removexattr removes extended attributes.
|
||||
func (fsys *FS) Removexattr(path string, name string) (errc int) {
|
||||
fsys.rateLimit()
|
||||
return -fuse.ENOSYS
|
||||
}
|
||||
|
||||
// Listxattr lists extended attributes.
|
||||
func (fsys *FS) Listxattr(path string, fill func(name string) bool) (errc int) {
|
||||
fsys.rateLimit()
|
||||
return -fuse.ENOSYS
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ func mount(VFS *vfs.VFS, mountPath string, opt *mountlib.Options) (<-chan error,
|
||||
|
||||
// Create underlying FS
|
||||
f := VFS.Fs()
|
||||
fsys := NewFS(VFS, opt)
|
||||
fsys := NewFS(VFS)
|
||||
host := fuse.NewFileSystemHost(fsys)
|
||||
host.SetCapReaddirPlus(true) // only works on Windows
|
||||
host.SetCapCaseInsensitive(f.Features().CaseInsensitive)
|
||||
|
||||
@@ -45,7 +45,6 @@ type Options struct {
|
||||
DaemonTimeout time.Duration // OSXFUSE only
|
||||
AsyncRead bool
|
||||
NetworkMode bool // Windows only
|
||||
TPSLimit float64
|
||||
}
|
||||
|
||||
// DefaultOpt is the default values for creating the mount
|
||||
@@ -98,7 +97,6 @@ func AddFlags(flagSet *pflag.FlagSet) {
|
||||
flags.BoolVarP(flagSet, &Opt.AsyncRead, "async-read", "", Opt.AsyncRead, "Use asynchronous reads. Not supported on Windows.")
|
||||
flags.FVarP(flagSet, &Opt.MaxReadAhead, "max-read-ahead", "", "The number of bytes that can be prefetched for sequential reads. Not supported on Windows.")
|
||||
flags.BoolVarP(flagSet, &Opt.WritebackCache, "write-back-cache", "", Opt.WritebackCache, "Makes kernel buffer writes before sending them to rclone. Without this, writethrough caching is used. Not supported on Windows.")
|
||||
flags.Float64VarP(flagSet, &Opt.TPSLimit, "mount-tpslimit", "", 0, "Limit mount transactions per second to this. (0 is off).")
|
||||
// Windows and OSX
|
||||
flags.StringVarP(flagSet, &Opt.VolumeName, "volname", "", Opt.VolumeName, "Set the volume name. Supported on Windows and OSX only.")
|
||||
// OSX only
|
||||
|
||||
@@ -456,4 +456,3 @@ put them back in again.` >}}
|
||||
* Nicolas Rueff <nicolas@rueff.fr>
|
||||
* Pau Rodriguez-Estivill <prodrigestivill@gmail.com>
|
||||
* Bob Pusateri <BobPusateri@users.noreply.github.com>
|
||||
* Alex JOST <25005220+dimejo@users.noreply.github.com>
|
||||
|
||||
@@ -72,7 +72,7 @@ If your browser doesn't open automatically go to the following link: http://127.
|
||||
Log in and authorize rclone for access
|
||||
Waiting for code...
|
||||
Got code
|
||||
Configure this as a Shared Drive (Team Drive)?
|
||||
Configure this as a team drive?
|
||||
y) Yes
|
||||
n) No
|
||||
y/n> n
|
||||
@@ -279,24 +279,23 @@ Note: in case you configured a specific root folder on gdrive and rclone is unab
|
||||
`rclone -v foo@example.com lsf gdrive:backup`
|
||||
|
||||
|
||||
### Shared drives (team drives) ###
|
||||
### Team drives ###
|
||||
|
||||
If you want to configure the remote to point to a Google Shared Drive
|
||||
(previously known as Team Drives) then answer `y` to the question
|
||||
`Configure this as a Shared Drive (Team Drive)?`.
|
||||
If you want to configure the remote to point to a Google Team Drive
|
||||
then answer `y` to the question `Configure this as a team drive?`.
|
||||
|
||||
This will fetch the list of Shared Drives from google and allow you to
|
||||
configure which one you want to use. You can also type in a Shared
|
||||
Drive ID if you prefer.
|
||||
This will fetch the list of Team Drives from google and allow you to
|
||||
configure which one you want to use. You can also type in a team
|
||||
drive ID if you prefer.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
Configure this as a Shared Drive (Team Drive)?
|
||||
Configure this as a team drive?
|
||||
y) Yes
|
||||
n) No
|
||||
y/n> y
|
||||
Fetching Shared Drive list...
|
||||
Fetching team drive list...
|
||||
Choose a number from below, or type in your own value
|
||||
1 / Rclone Test
|
||||
\ "xxxxxxxxxxxxxxxxxxxx"
|
||||
@@ -304,7 +303,7 @@ Choose a number from below, or type in your own value
|
||||
\ "yyyyyyyyyyyyyyyyyyyy"
|
||||
3 / Rclone Test 3
|
||||
\ "zzzzzzzzzzzzzzzzzzzz"
|
||||
Enter a Shared Drive ID> 1
|
||||
Enter a Team Drive ID> 1
|
||||
--------------------
|
||||
[remote]
|
||||
client_id =
|
||||
@@ -675,7 +674,7 @@ Needed only if you want use SA instead of interactive login.
|
||||
|
||||
#### --drive-team-drive
|
||||
|
||||
ID of the Shared Drive (Team Drive)
|
||||
ID of the Team Drive
|
||||
|
||||
- Config: team_drive
|
||||
- Env Var: RCLONE_DRIVE_TEAM_DRIVE
|
||||
@@ -1138,11 +1137,11 @@ Options:
|
||||
|
||||
#### drives
|
||||
|
||||
List the Shared Drives available to this account
|
||||
List the shared drives available to this account
|
||||
|
||||
rclone backend drives remote: [options] [<arguments>+]
|
||||
|
||||
This command lists the Shared Drives (Team Drives) available to this
|
||||
This command lists the shared drives (teamdrives) available to this
|
||||
account.
|
||||
|
||||
Usage:
|
||||
|
||||
37
lib/cache/cache.go
vendored
37
lib/cache/cache.go
vendored
@@ -22,15 +22,17 @@ func New() *Cache {
|
||||
return &Cache{
|
||||
cache: map[string]*cacheEntry{},
|
||||
expireRunning: false,
|
||||
expireDuration: 300 * time.Second,
|
||||
expireDuration: 24 * time.Hour,
|
||||
expireInterval: 60 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
// cacheEntry is stored in the cache
|
||||
type cacheEntry struct {
|
||||
createMu sync.Mutex // held while creating the item
|
||||
value interface{} // cached item
|
||||
err error // creation error
|
||||
ok bool // true if entry is valid
|
||||
key string // key
|
||||
lastUsed time.Time // time used for expiry
|
||||
pinCount int // non zero if the entry should not be removed
|
||||
@@ -55,23 +57,27 @@ func (c *Cache) used(entry *cacheEntry) {
|
||||
// afresh with the create function.
|
||||
func (c *Cache) Get(key string, create CreateFunc) (value interface{}, err error) {
|
||||
c.mu.Lock()
|
||||
entry, ok := c.cache[key]
|
||||
if !ok {
|
||||
c.mu.Unlock() // Unlock in case Get is called recursively
|
||||
value, ok, err = create(key)
|
||||
if err != nil && !ok {
|
||||
return value, err
|
||||
}
|
||||
entry, found := c.cache[key]
|
||||
if !found {
|
||||
entry = &cacheEntry{
|
||||
value: value,
|
||||
key: key,
|
||||
err: err,
|
||||
key: key,
|
||||
}
|
||||
c.mu.Lock()
|
||||
c.cache[key] = entry
|
||||
}
|
||||
defer c.mu.Unlock()
|
||||
c.used(entry)
|
||||
c.mu.Unlock()
|
||||
// Only one racing Get will have found=false here
|
||||
entry.createMu.Lock()
|
||||
if !found {
|
||||
entry.value, entry.ok, entry.err = create(key)
|
||||
}
|
||||
entry.createMu.Unlock()
|
||||
c.mu.Lock()
|
||||
if !found && !entry.ok {
|
||||
delete(c.cache, key)
|
||||
} else {
|
||||
c.used(entry)
|
||||
}
|
||||
c.mu.Unlock()
|
||||
return entry.value, entry.err
|
||||
}
|
||||
|
||||
@@ -102,6 +108,7 @@ func (c *Cache) Put(key string, value interface{}) {
|
||||
entry := &cacheEntry{
|
||||
value: value,
|
||||
key: key,
|
||||
ok: true,
|
||||
}
|
||||
c.used(entry)
|
||||
c.cache[key] = entry
|
||||
@@ -112,7 +119,7 @@ func (c *Cache) GetMaybe(key string) (value interface{}, found bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
entry, found := c.cache[key]
|
||||
if !found {
|
||||
if !found || !entry.ok {
|
||||
return nil, found
|
||||
}
|
||||
c.used(entry)
|
||||
|
||||
30
lib/cache/cache_test.go
vendored
30
lib/cache/cache_test.go
vendored
@@ -3,6 +3,8 @@ package cache
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -11,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
called = 0
|
||||
called = int32(0)
|
||||
errSentinel = errors.New("an error")
|
||||
errCached = errors.New("a cached error")
|
||||
)
|
||||
@@ -19,17 +21,19 @@ var (
|
||||
func setup(t *testing.T) (*Cache, CreateFunc) {
|
||||
called = 0
|
||||
create := func(path string) (interface{}, bool, error) {
|
||||
assert.Equal(t, 0, called)
|
||||
called++
|
||||
newCalled := atomic.AddInt32(&called, 1)
|
||||
assert.Equal(t, int32(1), newCalled)
|
||||
switch path {
|
||||
case "/":
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
return "/", true, nil
|
||||
case "/file.txt":
|
||||
return "/file.txt", true, errCached
|
||||
case "/error":
|
||||
return nil, false, errSentinel
|
||||
}
|
||||
panic(fmt.Sprintf("Unknown path %q", path))
|
||||
assert.Fail(t, fmt.Sprintf("Unknown path %q", path))
|
||||
return nil, false, nil
|
||||
}
|
||||
c := New()
|
||||
return c, create
|
||||
@@ -51,6 +55,24 @@ func TestGet(t *testing.T) {
|
||||
assert.Equal(t, f, f2)
|
||||
}
|
||||
|
||||
func TestGetConcurrent(t *testing.T) {
|
||||
c, create := setup(t)
|
||||
assert.Equal(t, 0, len(c.cache))
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_, err := c.Get("/", create)
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
assert.Equal(t, 1, len(c.cache))
|
||||
}
|
||||
|
||||
func TestGetFile(t *testing.T) {
|
||||
c, create := setup(t)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user