lib/model: Don't panic on failed chmod-back on directory (fixes #5836) (#5896)

* lib/model: Don't panic on failed chmod-back on directory (fixes #5836)

This makes the "in writable dir"-wrapper log chmod-back errors instead
of panicking. To do that we need a logger so the function moved into the
model package which is also the only place it's used. The tests came
along.

(The test also exercised osutil.RenameOrCopy like some sort of
piggybacking. I removed that.)
This commit is contained in:
Jakob Borg
2019-07-28 10:25:05 +02:00
committed by GitHub
parent 159d1a68e1
commit c1c976aa2b
5 changed files with 243 additions and 235 deletions

View File

@@ -8,8 +8,12 @@ package model
import (
"fmt"
"path/filepath"
"sync"
"time"
"github.com/pkg/errors"
"github.com/syncthing/syncthing/lib/fs"
)
type Holdable interface {
@@ -59,3 +63,35 @@ func (d *deadlockDetector) Watch(name string, mut sync.Locker) {
}
}()
}
// inWritableDir calls fn(path), while making sure that the directory
// containing `path` is writable for the duration of the call.
func inWritableDir(fn func(string) error, targetFs fs.Filesystem, path string) error {
dir := filepath.Dir(path)
info, err := targetFs.Stat(dir)
if err != nil {
return err
}
if !info.IsDir() {
return errors.New("Not a directory: " + path)
}
if info.Mode()&0200 == 0 {
// A non-writeable directory (for this user; we assume that's the
// relevant part). Temporarily change the mode so we can delete the
// file or directory inside it.
if err := targetFs.Chmod(dir, 0755); err == nil {
// Chmod succeeded, we should change the permissions back on the way
// out. If we fail we log the error as we have irrevocably messed up
// at this point. :( (The operation we were called to wrap has
// succeeded or failed on its own so returning an error to the
// caller is inappropriate.)
defer func() {
if err := targetFs.Chmod(dir, info.Mode()); err != nil && !fs.IsNotExist(err) {
l.Warnln("Failed to restore directory permissions after gaining write access:", err)
}
}()
}
}
return fn(path)
}