From 22368b997c093bc51c6305fab120f07b7dea7daf Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Fri, 24 May 2019 16:07:22 +0100 Subject: [PATCH] b2: implement SetModTime #3210 SetModTime() is implemented by copying an object onto itself and updating the metadata in the process. --- backend/b2/b2.go | 67 +++++++++++++++++++++++++++++++++------------- docs/content/b2.md | 7 +++-- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/backend/b2/b2.go b/backend/b2/b2.go index 7a26c10a1..fffa5a6c3 100644 --- a/backend/b2/b2.go +++ b/backend/b2/b2.go @@ -1206,17 +1206,8 @@ func (o *Object) decodeMetaDataFileInfo(info *api.FileInfo) (err error) { return o.decodeMetaDataRaw(info.ID, info.SHA1, info.Size, info.UploadTimestamp, info.Info, info.ContentType) } -// readMetaData gets the metadata if it hasn't already been fetched -// -// Sets -// o.id -// o.modTime -// o.size -// o.sha1 -func (o *Object) readMetaData() (err error) { - if o.id != "" { - return nil - } +// getMetaData gets the metadata from the object unconditionally +func (o *Object) getMetaData() (info *api.File, err error) { maxSearched := 1 var timestamp api.Timestamp baseRemote := o.remote @@ -1224,7 +1215,6 @@ func (o *Object) readMetaData() (err error) { timestamp, baseRemote = api.RemoveVersion(baseRemote) maxSearched = maxVersions } - var info *api.File err = o.fs.list("", true, baseRemote, maxSearched, o.fs.opt.Versions, func(remote string, object *api.File, isDirectory bool) error { if isDirectory { return nil @@ -1239,12 +1229,30 @@ func (o *Object) readMetaData() (err error) { }) if err != nil { if err == fs.ErrorDirNotFound { - return fs.ErrorObjectNotFound + return nil, fs.ErrorObjectNotFound } - return err + return nil, err } if info == nil { - return fs.ErrorObjectNotFound + return nil, fs.ErrorObjectNotFound + } + return info, nil +} + +// readMetaData gets the metadata if it hasn't already been fetched +// +// Sets +// o.id +// o.modTime +// o.size +// o.sha1 +func (o *Object) readMetaData() (err error) { + if o.id != "" { + return nil + } + info, err := o.getMetaData() + if err != nil { + return err } return o.decodeMetaData(info) } @@ -1283,10 +1291,33 @@ func (o *Object) ModTime() (result time.Time) { return o.modTime } -// SetModTime sets the modification time of the local fs object +// SetModTime sets the modification time of the Object func (o *Object) SetModTime(modTime time.Time) error { - // Not possible with B2 - return fs.ErrorCantSetModTime + info, err := o.getMetaData() + if err != nil { + return err + } + info.Info[timeKey] = timeString(modTime) + opts := rest.Opts{ + Method: "POST", + Path: "/b2_copy_file", + } + var request = api.CopyFileRequest{ + SourceID: o.id, + Name: o.fs.root + o.remote, // copy to same name + MetadataDirective: "REPLACE", + ContentType: info.ContentType, + Info: info.Info, + } + var response api.FileInfo + err = o.fs.pacer.Call(func() (bool, error) { + resp, err := o.fs.srv.CallJSON(&opts, &request, &response) + return o.fs.shouldRetry(resp, err) + }) + if err != nil { + return err + } + return o.decodeMetaDataFileInfo(&response) } // Storable returns if this object is storable diff --git a/docs/content/b2.md b/docs/content/b2.md index 6f4b8de91..53aa791c2 100644 --- a/docs/content/b2.md +++ b/docs/content/b2.md @@ -124,10 +124,9 @@ The modified time is stored as metadata on the object as in the Backblaze standard. Other tools should be able to use this as a modified time. -Modified times are used in syncing and are fully supported except in -the case of updating a modification time on an existing object. In -this case the object will be uploaded again as B2 doesn't have an API -method to set the modification time independent of doing an upload. +Modified times are used in syncing and are fully supported. Note that +if a modification time needs to be updated on an object then it will +create a new version of the object. ### SHA1 checksums ###