vfs: fix fatal error: sync: unlock of unlocked mutex in panics

Before this change a panic could be overwritten with the message

    fatal error: sync: unlock of unlocked mutex

This was because we temporarily unlocked the mutex, but failed to lock
it again if there was a panic.

This is code is never the cause of an error but it masks the
underlying error by overwriting the panic cause.

See: https://forum.rclone.org/t/serve-webdav-is-crashing-fatal-error-sync-unlock-of-unlocked-mutex/46300
This commit is contained in:
Nick Craig-Wood 2024-06-06 09:39:00 +01:00
parent 0e85ba5080
commit 4ed4483bbc

View file

@ -573,6 +573,15 @@ func (item *Item) open(o fs.Object) (err error) {
return err
}
// Calls f with mu unlocked, re-locking mu if a panic is raised
//
// mu must be locked when calling this function
func unlockMutexForCall(mu *sync.Mutex, f func()) {
mu.Unlock()
defer mu.Lock()
f()
}
// Store stores the local cache file to the remote object, returning
// the new remote object. objOld is the old object if known.
//
@ -589,9 +598,9 @@ func (item *Item) _store(ctx context.Context, storeFn StoreFn) (err error) {
// Object has disappeared if cacheObj == nil
if cacheObj != nil {
o, name := item.o, item.name
item.mu.Unlock()
o, err := operations.Copy(ctx, item.c.fremote, o, name, cacheObj)
item.mu.Lock()
unlockMutexForCall(&item.mu, func() {
o, err = operations.Copy(ctx, item.c.fremote, o, name, cacheObj)
})
if err != nil {
if errors.Is(err, fs.ErrorCantUploadEmptyFiles) {
fs.Errorf(name, "Writeback failed: %v", err)