onedrive: fall back to normal copy if server-side copy unavailable (#4903)

Fixes #4342 by:
* Disabling server-side copy if either drive isn't OneDrive Personal
* Falling back to normal copy if server-side copy fails
This commit is contained in:
Alex Chen 2021-01-05 21:26:00 +08:00 committed by GitHub
parent add7a35e55
commit b594cb9430
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 19 additions and 7 deletions

View File

@ -281,6 +281,7 @@ type CreateShareLinkResponse struct {
type AsyncOperationStatus struct { type AsyncOperationStatus struct {
PercentageComplete float64 `json:"percentageComplete"` // A float value between 0 and 100 that indicates the percentage complete. PercentageComplete float64 `json:"percentageComplete"` // A float value between 0 and 100 that indicates the percentage complete.
Status string `json:"status"` // A string value that maps to an enumeration of possible values about the status of the job. "notStarted | inProgress | completed | updating | failed | deletePending | deleteFailed | waiting" Status string `json:"status"` // A string value that maps to an enumeration of possible values about the status of the job. "notStarted | inProgress | completed | updating | failed | deletePending | deleteFailed | waiting"
ErrorCode string `json:"errorCode"` // Not officially documented :(
} }
// GetID returns a normalized ID of the item // GetID returns a normalized ID of the item

View File

@ -276,10 +276,9 @@ listing, set this option.`,
Default: false, Default: false,
Help: `Allow server-side operations (e.g. copy) to work across different onedrive configs. Help: `Allow server-side operations (e.g. copy) to work across different onedrive configs.
This can be useful if you wish to do a server-side copy between two This will only work if you are copying between two OneDrive *Personal* drives AND
different Onedrives. Note that this isn't enabled by default the files to copy are already shared between them. In other cases, rclone will
because it isn't easy to tell if it will work between any two fall back to normal copy (which will be slightly slower).`,
configurations.`,
Advanced: true, Advanced: true,
}, { }, {
Name: "no_versions", Name: "no_versions",
@ -429,6 +428,7 @@ var retryErrorCodes = []int{
} }
var gatewayTimeoutError sync.Once var gatewayTimeoutError sync.Once
var errAsyncJobAccessDenied = errors.New("async job failed - access denied")
// shouldRetry returns a boolean as to whether this resp and err // shouldRetry returns a boolean as to whether this resp and err
// deserve to be retried. It returns the err as a convenience // deserve to be retried. It returns the err as a convenience
@ -1000,10 +1000,12 @@ func (f *Fs) waitForJob(ctx context.Context, location string, o *Object) error {
switch status.Status { switch status.Status {
case "failed": case "failed":
case "deleteFailed": if strings.HasPrefix(status.ErrorCode, "AccessDenied_") {
{ return errAsyncJobAccessDenied
return errors.Errorf("%s: async operation returned %q", o.remote, status.Status)
} }
fallthrough
case "deleteFailed":
return errors.Errorf("%s: async operation returned %q", o.remote, status.Status)
case "completed": case "completed":
err = o.readMetaData(ctx) err = o.readMetaData(ctx)
return errors.Wrapf(err, "async operation completed but readMetaData failed") return errors.Wrapf(err, "async operation completed but readMetaData failed")
@ -1029,6 +1031,11 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
fs.Debugf(src, "Can't copy - not same remote type") fs.Debugf(src, "Can't copy - not same remote type")
return nil, fs.ErrorCantCopy return nil, fs.ErrorCantCopy
} }
if f.driveType != srcObj.fs.driveType {
fs.Debugf(src, "Can't server-side copy - not both drives are OneDrive Personal")
return nil, fs.ErrorCantCopy
}
err := srcObj.readMetaData(ctx) err := srcObj.readMetaData(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1081,6 +1088,10 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
// Wait for job to finish // Wait for job to finish
err = f.waitForJob(ctx, location, dstObj) err = f.waitForJob(ctx, location, dstObj)
if err == errAsyncJobAccessDenied {
fs.Debugf(src, "Server-side copy failed - file not shared between drives")
return nil, fs.ErrorCantCopy
}
if err != nil { if err != nil {
return nil, err return nil, err
} }