From 807f1cedaac3de6c527acbae2c7e4c40cab35fbd Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Sat, 12 Mar 2022 16:45:25 +0000 Subject: [PATCH] hasher: fix crash on object not found Before this fix `NewObject` could return a wrapped `fs.Object(nil)` which caused a crash. This was caused by `wrapObject` returning a `nil` `*Object` which was cast into an `fs.Object`. This changes the interface of `wrapObject` so it returns an `fs.Object` instead of a `*Object` and an error which must be checked. This forces the callers to return a `nil` object rather than an `fs.Object(nil)`. See: https://forum.rclone.org/t/panic-in-hasher-when-mounting-with-vfs-cache-and-not-synced-data-in-the-cache/29697/11 --- backend/hasher/hasher.go | 28 ++++++++++++++++++---------- backend/hasher/object.go | 8 ++++---- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/backend/hasher/hasher.go b/backend/hasher/hasher.go index 2d94895ee..44b403f36 100644 --- a/backend/hasher/hasher.go +++ b/backend/hasher/hasher.go @@ -202,7 +202,11 @@ func (f *Fs) wrapEntries(baseEntries fs.DirEntries) (hashEntries fs.DirEntries, for _, entry := range baseEntries { switch x := entry.(type) { case fs.Object: - hashEntries = append(hashEntries, f.wrapObject(x, nil)) + obj, err := f.wrapObject(x, nil) + if err != nil { + return nil, err + } + hashEntries = append(hashEntries, obj) default: hashEntries = append(hashEntries, entry) // trash in - trash out } @@ -251,7 +255,7 @@ func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, opt if do := f.Fs.Features().PutStream; do != nil { _ = f.pruneHash(src.Remote()) oResult, err := do(ctx, in, src, options...) - return f.wrapObject(oResult, err), err + return f.wrapObject(oResult, err) } return nil, errors.New("PutStream not supported") } @@ -261,7 +265,7 @@ func (f *Fs) PutUnchecked(ctx context.Context, in io.Reader, src fs.ObjectInfo, if do := f.Fs.Features().PutUnchecked; do != nil { _ = f.pruneHash(src.Remote()) oResult, err := do(ctx, in, src, options...) - return f.wrapObject(oResult, err), err + return f.wrapObject(oResult, err) } return nil, errors.New("PutUnchecked not supported") } @@ -348,7 +352,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, return nil, fs.ErrorCantCopy } oResult, err := do(ctx, o.Object, remote) - return f.wrapObject(oResult, err), err + return f.wrapObject(oResult, err) } // Move src to this remote using server-side move operations. @@ -371,7 +375,7 @@ func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, dir: false, fs: f, }) - return f.wrapObject(oResult, nil), nil + return f.wrapObject(oResult, nil) } // DirMove moves src, srcRemote to this remote at dstRemote using server-side move operations. @@ -410,7 +414,7 @@ func (f *Fs) Shutdown(ctx context.Context) (err error) { // NewObject finds the Object at remote. func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) { o, err := f.Fs.NewObject(ctx, remote) - return f.wrapObject(o, err), err + return f.wrapObject(o, err) } // @@ -424,11 +428,15 @@ type Object struct { } // Wrap base object into hasher object -func (f *Fs) wrapObject(o fs.Object, err error) *Object { - if err != nil || o == nil { - return nil +func (f *Fs) wrapObject(o fs.Object, err error) (obj fs.Object, outErr error) { + // log.Trace(o, "err=%v", err)("obj=%#v, outErr=%v", &obj, &outErr) + if err != nil { + return nil, err } - return &Object{Object: o, f: f} + if o == nil { + return nil, fs.ErrorObjectNotFound + } + return &Object{Object: o, f: f}, nil } // Fs returns read only access to the Fs that this object is part of diff --git a/backend/hasher/object.go b/backend/hasher/object.go index 1233740e6..6b9a8e4d0 100644 --- a/backend/hasher/object.go +++ b/backend/hasher/object.go @@ -184,7 +184,7 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (r io.ReadC // Put data into the remote path with given modTime and size func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) { var ( - o *Object + o fs.Object common hash.Set rehash bool hashes hashMap @@ -210,8 +210,8 @@ func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options . _ = f.pruneHash(src.Remote()) oResult, err := f.Fs.Put(ctx, wrapIn, src, options...) - o = f.wrapObject(oResult, err) - if o == nil { + o, err = f.wrapObject(oResult, err) + if err != nil { return nil, err } @@ -224,7 +224,7 @@ func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options . } } if len(hashes) > 0 { - err := o.putHashes(ctx, hashes) + err := o.(*Object).putHashes(ctx, hashes) fs.Debugf(o, "Applied %d source hashes, err: %v", len(hashes), err) } return o, err