From fcec4bedbe55c674aa4d0ad2ba34eb803c2fd732 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 16 May 2022 17:20:48 +0100 Subject: [PATCH] drive: fix 404 errors on copy/server side copy objects from public folder Before this change, copying objects from a public folder shared with a resource key failed with a 404 error. This is because rclone wasn't supplying the resource Key where it should have been. After this change rclone adds the resource Key when trying to download or server side copy an object. There may be more places rclone needs to supply the resource key as this is barely documented in the API documentation. See: https://forum.rclone.org/t/copying-files-from-a-publicly-accessible-google-drive-folder-added-as-a-shortcut-getting-error-404-server-side-copies-are-disabled/30811 --- backend/drive/drive.go | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/backend/drive/drive.go b/backend/drive/drive.go index 17d102ab0..dc26a39d7 100644 --- a/backend/drive/drive.go +++ b/backend/drive/drive.go @@ -70,7 +70,7 @@ const ( // 1<<18 is the minimum size supported by the Google uploader, and there is no maximum. minChunkSize = fs.SizeSuffix(googleapi.MinUploadChunkSize) defaultChunkSize = 8 * fs.Mebi - partialFields = "id,name,size,md5Checksum,trashed,explicitlyTrashed,modifiedTime,createdTime,mimeType,parents,webViewLink,shortcutDetails,exportLinks" + partialFields = "id,name,size,md5Checksum,trashed,explicitlyTrashed,modifiedTime,createdTime,mimeType,parents,webViewLink,shortcutDetails,exportLinks,resourceKey" listRGrouping = 50 // number of IDs to search at once when using ListR listRInputBuffer = 1000 // size of input buffer when using ListR defaultXDGIcon = "text-html" @@ -660,6 +660,7 @@ type baseObject struct { mimeType string // The object MIME type bytes int64 // size of the object parents []string // IDs of the parent directories + resourceKey *string // resourceKey is needed for link shared objects } type documentObject struct { baseObject @@ -1319,12 +1320,16 @@ func (f *Fs) newRegularObject(remote string, info *drive.File) fs.Object { } } } - return &Object{ + o := &Object{ baseObject: f.newBaseObject(remote, info), url: fmt.Sprintf("%sfiles/%s?alt=media", f.svc.BasePath, actualID(info.Id)), md5sum: strings.ToLower(info.Md5Checksum), v2Download: f.opt.V2DownloadMinSize != -1 && info.Size >= int64(f.opt.V2DownloadMinSize), } + if info.ResourceKey != "" { + o.resourceKey = &info.ResourceKey + } + return o } // newDocumentObject creates an fs.Object for a google docs drive.File @@ -2429,11 +2434,12 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, var info *drive.File err = f.pacer.Call(func() (bool, error) { - info, err = f.svc.Files.Copy(id, createInfo). + copy := f.svc.Files.Copy(id, createInfo). Fields(partialFields). SupportsAllDrives(true). - KeepRevisionForever(f.opt.KeepRevisionForever). - Context(ctx).Do() + KeepRevisionForever(f.opt.KeepRevisionForever) + srcObj.addResourceKey(copy.Header()) + info, err = copy.Context(ctx).Do() return f.shouldRetry(ctx, err) }) if err != nil { @@ -3530,6 +3536,14 @@ func (o *baseObject) Storable() bool { return true } +// addResourceKey adds a X-Goog-Drive-Resource-Keys header for this +// object if required. +func (o *baseObject) addResourceKey(header http.Header) { + if o.resourceKey != nil { + header.Add("X-Goog-Drive-Resource-Keys", fmt.Sprintf("%s/%s", o.id, *o.resourceKey)) + } +} + // httpResponse gets an http.Response object for the object // using the url and method passed in func (o *baseObject) httpResponse(ctx context.Context, url, method string, options []fs.OpenOption) (req *http.Request, res *http.Response, err error) { @@ -3545,6 +3559,7 @@ func (o *baseObject) httpResponse(ctx context.Context, url, method string, optio // Don't supply range requests for 0 length objects as they always fail delete(req.Header, "Range") } + o.addResourceKey(req.Header) err = o.fs.pacer.Call(func() (bool, error) { res, err = o.fs.client.Do(req) if err == nil {