Skip to content

When restoring, cannot rewrite files that are readonly #4757

@toh995

Description

@toh995

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 $DIR1

Expected behavior

  1. Successfully restores the new file contents, for the readonly file
  2. The restore returns a 0 exit code
  3. No error messages displayed

Actual behavior

  1. Does NOT restore the new file contents
  2. The restore returns a 1 exit code
  3. 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?

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.go
    • Restorer - 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?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions