mirror of
https://github.com/restic/restic.git
synced 2026-06-21 08:04:18 +00:00
Merge pull request #21897 from MichaelEischer/sftp-fix-chmod
backend/sftp: fix removing files on windows sftp
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
Bugfix: Remove read-only files via the SFTP backend on Windows servers
|
||||
|
||||
Since restic 0.19.0, repository files on the SFTP backend are marked
|
||||
read-only after save. On Windows SFTP servers, removing them failed
|
||||
with a permission error. The SFTP backend now clears the read-only flag
|
||||
before removing the file.
|
||||
|
||||
https://github.com/restic/restic/issues/21895
|
||||
https://github.com/restic/restic/pull/21897
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -37,7 +38,8 @@ type SFTP struct {
|
||||
cmd *exec.Cmd
|
||||
result <-chan error
|
||||
|
||||
posixRename bool
|
||||
posixRename bool
|
||||
chmodBeforeRemove atomic.Bool
|
||||
|
||||
layout.Layout
|
||||
Config
|
||||
@@ -405,12 +407,11 @@ func (r *SFTP) Save(_ context.Context, h backend.Handle, rd backend.RewindReader
|
||||
} else {
|
||||
err = r.c.Rename(tmpFilename, filename)
|
||||
}
|
||||
err = setFileReadonly(r.c, filename, r.Modes.File)
|
||||
if err != nil {
|
||||
return errors.Errorf("sftp setFileReadonly: %v", err)
|
||||
return errors.Wrapf(err, "Rename %v", tmpFilename)
|
||||
}
|
||||
|
||||
return errors.Wrapf(err, "Rename %v", tmpFilename)
|
||||
err = setFileReadonly(r.c, filename, r.Modes.File)
|
||||
return errors.Wrapf(err, "setFileReadonly %v", filename)
|
||||
}
|
||||
|
||||
// checkNoSpace checks if err was likely caused by lack of available space
|
||||
@@ -508,7 +509,37 @@ func (r *SFTP) Remove(_ context.Context, h backend.Handle) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return errors.Wrapf(r.c.Remove(r.Filename(h)), "Remove %v", r.Filename(h))
|
||||
path := r.Filename(h)
|
||||
|
||||
if r.chmodBeforeRemove.Load() {
|
||||
return r.removeWithChmod(path)
|
||||
}
|
||||
|
||||
// optimistically try to remove the file
|
||||
err := r.c.Remove(path)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if !errors.Is(err, os.ErrPermission) {
|
||||
return errors.Wrapf(err, "Remove %v", path)
|
||||
}
|
||||
|
||||
// fallback to chmod + remove
|
||||
// this is necessary on Windows where read-only files cannot be deleted without chmod.
|
||||
if err := r.removeWithChmod(path); err != nil {
|
||||
return err
|
||||
}
|
||||
r.chmodBeforeRemove.Store(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SFTP) removeWithChmod(path string) error {
|
||||
err := r.c.Chmod(path, r.Modes.File)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Chmod %v", path)
|
||||
}
|
||||
|
||||
return errors.Wrapf(r.c.Remove(path), "Remove %v", path)
|
||||
}
|
||||
|
||||
// List runs fn for each file in the backend which has the type t. When an
|
||||
|
||||
Reference in New Issue
Block a user