-
Notifications
You must be signed in to change notification settings - Fork 1.7k
When restoring, cannot rewrite files that are readonly #4757
Description
Output of restic version
restic 0.16.4 compiled with go1.22.1 on linux/amd64
What backend/service did you use to store the repository?
Local
Problem description / Steps to reproduce
NOTE: the following script should be copy+pasted into bash! (zsh didn't work for me)
# Initialize a restic repo
export RESTIC_REPOSITORY=/tmp/restic
export RESTIC_PASSWORD=foo
mkdir -p $RESTIC_REPOSITORY
restic init
# Create two new directories
DIR1=/tmp/foo1 && mkdir -p $DIR1
DIR2=/tmp/foo2 && mkdir -p $DIR2
# Create a readonly file in DIR1
# RO == readonly
RO_FILE=readonly.txt
echo "first" > "${DIR1}/${RO_FILE}"
chmod 444 "${DIR1}/${RO_FILE}"
# Create a snapshot of DIR1
cd $DIR1 && restic backup .
# Restore the snapshot to DIR2:
restic restore latest --target $DIR2
# Within DIR2, replace the existing readonly file with a new one with different contents
rm --force "${DIR2}/${RO_FILE}"
echo "second" > "${DIR2}/${RO_FILE}"
chmod 444 "${DIR2}/${RO_FILE}"
# Create a new snapshot of DIR2:
cd $DIR2 && restic backup .
# Here's the error!
# Try to restore the DIR2 snapshot into DIR1
DEBUG_LOG=logfile.txt restic restore latest --target $DIR1Expected behavior
- Successfully restores the new file contents, for the readonly file
- The restore returns a 0 exit code
- No error messages displayed
Actual behavior
- Does NOT restore the new file contents
- The restore returns a 1 exit code
- Error messages are displayed
For (3), here's the output displayed in the terminal, for the final "restore" command, i.e. DEBUG_LOG=logfile.txt restic restore latest --target $DIR1
debug log file logfile.txt
debug enabled
repository 493cf5ac opened (version 2, compression level auto)
restoring <Snapshot caeea779 of [/tmp/foo2] at ${REDACTED} by ${REDACTED} to /tmp/foo1
ignoring error for /readonly.txt: open /tmp/foo1/readonly.txt: permission denied
Summary: Restored 1 files/dirs (7 B) in 0:00
Fatal: There were 1 errors
I've also attached the debug logs
Do you have any idea what may have caused this?
I'm not certain.
Maybe we should be hitting this codepath, but it's not for some reason?
restic/internal/restorer/fileswriter.go
Lines 56 to 64 in 6091029
| if f, err = os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600); err != nil { | |
| if fs.IsAccessDenied(err) { | |
| // If file is readonly, clear the readonly flag by resetting the | |
| // permissions of the file and try again | |
| // as the metadata will be set again in the second pass and the | |
| // readonly flag will be applied again if needed. | |
| if err = fs.ResetPermissions(path); err != nil { | |
| return nil, err | |
| } |
Also - is this related to this previous issue re: readonly directories? (The current issue is about readonly files)
Did restic help you today? Did it make you happy in any way?
It's a cool tool! 😄
I'm not sure if the maintainers have time to look at this - if not, I am willing to contribute some code to help fix this.
However, I will definitely need some help/guidance. In particular:
- I don't quite understand the code structure. Seems like there are multiple layers of abstraction:
cmd/restic/cmd_restore.goRestorer- seems like this also does some kind of traversal through the file directory tree?filesWriter
- If we were to write a test, is it more appropriate to write an "integration" test i.e. in
cmd/restic/cmd_restore_integration_test.go? Or, write more of a "unit-level" test?