Bug summary
rdiff-backup crashes due to a Python OSError exception while trying to restore a subdirectory if I specify include/exclude filters, and the files I'm trying to restore are completely gone from the destination directory.
Version, Python, Operating System
rdiff-backup 2.0.5, Python 3.8.5, Fedora 32
Call rdiff-backup -v9 and paste the output between the backquotes below, repeat for each environment impacted:
2020-09-11 22:59:21.708502 +0200 <CLIENT-7215> Fatal Error: No arguments given
See the rdiff-backup manual page for more information.
2020-09-11 22:59:21.703167 +0200 <CLIENT-7215> Using rdiff-backup version 2.0.5
2020-09-11 22:59:21.703273 +0200 <CLIENT-7215> with cpython /usr/bin/python3 version 3.8.5
2020-09-11 22:59:21.708363 +0200 <CLIENT-7215> on Linux-5.8.6-201.fc32.x86_64-x86_64-with-glibc2.2.5, fs encoding utf-8
rdiff-backup call
I have a fully reproducible test case. First, let's create a few files and directories to play with:
$ mkdir -p backup original/dir{1,2}
$ cd original
$ echo file1 | tee dir1/file1.txt > dir2/file1.txt
$ echo file2 | tee dir1/file2.txt > dir2/file2.txt
$ echo base > base.txt
$ cd -
Create our first backup in the empty backup directory, then overwrite a file with garbage and :
$ rdiff-backup original backup
$ echo garbage > original/dir1/file2.txt
We can now try to selectively restore dir1:
$ rdiff-backup -r now --force --include original/dir1/file2.txt --exclude '**' backup/dir1 original/dir1
Exception '[Errno 39] Directory not empty: b'original/dir1'' raised of class '<class 'OSError'>':
File "/usr/lib64/python3.8/site-packages/rdiff_backup/Main.py", line 395, in error_check_Main
Main(arglist)
File "/usr/lib64/python3.8/site-packages/rdiff_backup/Main.py", line 417, in Main
take_action(rps)
File "/usr/lib64/python3.8/site-packages/rdiff_backup/Main.py", line 373, in take_action
Restore(rps[0], rps[1], 1)
File "/usr/lib64/python3.8/site-packages/rdiff_backup/Main.py", line 720, in Restore
restore.Restore(
File "/usr/lib64/python3.8/site-packages/rdiff_backup/restore.py", line 42, in Restore
TargetS.patch(target, diff_iter)
File "/usr/lib64/python3.8/site-packages/rdiff_backup/restore.py", line 337, in patch
ITR.Finish()
File "/usr/lib64/python3.8/site-packages/rdiff_backup/rorpiter.py", line 277, in Finish
to_be_finished.end_process()
File "/usr/lib64/python3.8/site-packages/rdiff_backup/restore.py", line 762, in end_process
self.base_rp.rmdir()
File "/usr/lib64/python3.8/site-packages/rdiff_backup/rpath.py", line 1222, in rmdir
self.conn.os.rmdir(self.path)
Traceback (most recent call last):
File "/usr/bin/rdiff-backup", line 32, in <module>
rdiff_backup.Main.error_check_Main(sys.argv[1:])
File "/usr/lib64/python3.8/site-packages/rdiff_backup/Main.py", line 395, in error_check_Main
Main(arglist)
File "/usr/lib64/python3.8/site-packages/rdiff_backup/Main.py", line 417, in Main
take_action(rps)
File "/usr/lib64/python3.8/site-packages/rdiff_backup/Main.py", line 373, in take_action
Restore(rps[0], rps[1], 1)
File "/usr/lib64/python3.8/site-packages/rdiff_backup/Main.py", line 720, in Restore
restore.Restore(
File "/usr/lib64/python3.8/site-packages/rdiff_backup/restore.py", line 42, in Restore
TargetS.patch(target, diff_iter)
File "/usr/lib64/python3.8/site-packages/rdiff_backup/restore.py", line 337, in patch
ITR.Finish()
File "/usr/lib64/python3.8/site-packages/rdiff_backup/rorpiter.py", line 277, in Finish
to_be_finished.end_process()
File "/usr/lib64/python3.8/site-packages/rdiff_backup/restore.py", line 762, in end_process
self.base_rp.rmdir()
File "/usr/lib64/python3.8/site-packages/rdiff_backup/rpath.py", line 1222, in rmdir
self.conn.os.rmdir(self.path)
OSError: [Errno 39] Directory not empty: b'original/dir1'
What happened and what did you expect?
I expected rdiff-backup to restore the dir1/file1.txt instead of crashing. Even worse, dir1/file2.txt disappeared completely after the crash, but it can fortunately still be recovered from the backup directory:
$ find original/ -type f
original/dir2/file2.txt
original/dir2/file1.txt
original/dir1/file1.txt
$ ls backup/dir1
file1.txt file2.txt
More information
Here's the -v9 output:
rdiff-backup.log
Possible workarounds:
- restore the entire directory, losing changes to the other files:
rdiff-backup -r now --force backup/dir1 original/dir1
- use include/exclude filters without subdirectories:
rdiff-backup -r now --force --include 'original/dir1/file2.txt' --exclude '**' backup original
- use rsync, which offers its own filters (this only works for the latest backed up version, not older ones):
rsync -a --include 'file2.txt' --exclude '**' backup/dir1/ original/dir1
The test case might seem contrived, since rdiff-backup backup/dir1/file1.txt original/dir1/file1.txt is easier, but I discovered this using rdiff-backup with a globbing filelist to restore several file in the /root directory - it was a bit scary to see it tried to remove the /root directory, running with root privileges.
I've been using rdiff-backup since at least 2009, many thanks for reviving the project!
Bug summary
rdiff-backup crashes due to a Python OSError exception while trying to restore a subdirectory if I specify include/exclude filters, and the files I'm trying to restore are completely gone from the destination directory.
Version, Python, Operating System
rdiff-backup 2.0.5, Python 3.8.5, Fedora 32
Call
rdiff-backup -v9and paste the output between the backquotes below, repeat for each environment impacted:rdiff-backup call
I have a fully reproducible test case. First, let's create a few files and directories to play with:
Create our first backup in the empty backup directory, then overwrite a file with garbage and :
We can now try to selectively restore dir1:
What happened and what did you expect?
I expected
rdiff-backupto restore thedir1/file1.txtinstead of crashing. Even worse,dir1/file2.txtdisappeared completely after the crash, but it can fortunately still be recovered from the backup directory:More information
Here's the
-v9output:rdiff-backup.log
Possible workarounds:
rdiff-backup -r now --force backup/dir1 original/dir1rdiff-backup -r now --force --include 'original/dir1/file2.txt' --exclude '**' backup originalrsync -a --include 'file2.txt' --exclude '**' backup/dir1/ original/dir1The test case might seem contrived, since
rdiff-backup backup/dir1/file1.txt original/dir1/file1.txtis easier, but I discovered this usingrdiff-backupwith a globbing filelist to restore several file in the/rootdirectory - it was a bit scary to see it tried to remove the/rootdirectory, running with root privileges.I've been using rdiff-backup since at least 2009, many thanks for reviving the project!