From 76b12e16dcf623973c31329d6f8234907d6a1074 Mon Sep 17 00:00:00 2001 From: Rob Browning Date: Sat, 18 Aug 2012 15:35:23 -0500 Subject: [PATCH] Add support for "bup restore --exclude-rx ...". When --exclude-rx is provided to bup restore, don't restore any path matching , which must be a Python regular expression (http://docs.python.org/library/re.html). The pattern will be compared against the full path, without anchoring, so "x/y" will match "ox/yard" or "box/yards". To exclude the contents of /tmp, but not the directory itself, use "^/tmp/.". You may check the behavior at runtime by setting BUP_DEBUG=2 in the environment. Thanks to Zoran Zaric for reporting a bug in an earlier version of this patch. Signed-off-by: Rob Browning Reviewed-by: Zoran Zaric --- Documentation/bup-restore.md | 25 ++++++++++- cmd/restore-cmd.py | 12 ++++- t/test.sh | 86 ++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 2 deletions(-) diff --git a/Documentation/bup-restore.md b/Documentation/bup-restore.md index 9862407..ab98c00 100644 --- a/Documentation/bup-restore.md +++ b/Documentation/bup-restore.md @@ -8,7 +8,8 @@ bup-restore - extract files from a backup set # SYNOPSIS -bup restore [\--outdir=*outdir*] [-v] [-q] \; +bup restore [\--outdir=*outdir*] [\--exclude-rx *pattern*] [-v] [-q] +\ # DESCRIPTION @@ -83,6 +84,28 @@ See the EXAMPLES section for a demonstration. \--numeric-ids : restore numeric IDs (user, group, etc.) rather than names. +\--exclude-rx=*pattern* +: exclude any path matching *pattern*, which must be a Python + regular expression (http://docs.python.org/library/re.html). The + pattern will be compared against the full path rooted at the top + of the restore tree, without anchoring, so "x/y" will match + "ox/yard" or "box/yards". To exclude the contents of /tmp, but + not the directory itself, use "^/tmp/.". (can be specified more + than once) + + Note that the root of the restore tree (which matches '^/') is the + top of the archive tree being restored, and has nothing to do with + the filesystem destination. Given "restore ... /foo/latest/etc/", + the pattern '^/passwd$' would match if a file named passwd had + been saved as '/foo/latest/etc/passwd'. + + Examples: + + * '/foo$' - exclude any file named foo + * '/foo/$' - exclude any directory named foo + * '/foo/.' - exclude the content of any directory named foo + * '^/tmp/.' - exclude root-level /tmp's content, but not /tmp itself + -v, \--verbose : increase log output. Given once, prints every directory as it is restored; given twice, prints every diff --git a/cmd/restore-cmd.py b/cmd/restore-cmd.py index 750b82f..124578e 100755 --- a/cmd/restore-cmd.py +++ b/cmd/restore-cmd.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -import errno, sys, stat +import errno, sys, stat, re from bup import options, git, metadata, vfs from bup.helpers import * @@ -8,6 +8,7 @@ bup restore [-C outdir] -- C,outdir= change to given outdir before extracting files numeric-ids restore numeric IDs (user, group, etc.) rather than names +exclude-rx= skip paths that match the unanchored regular expression v,verbose increase log output (can be used more than once) q,quiet don't show progress meter """ @@ -187,6 +188,12 @@ def do_node(top, n, meta=None): meta_stream = None try: fullname = n.fullname(stop_at=top) + # Match behavior of index --exclude-rx with respect to paths. + exclude_candidate = '/' + fullname + if(stat.S_ISDIR(n.mode)): + exclude_candidate += '/' + if should_rx_exclude_path(exclude_candidate, exclude_rxs): + return # If this is a directory, its metadata is the first entry in # any .bupm file inside the directory. Get it. if(stat.S_ISDIR(n.mode)): @@ -222,6 +229,7 @@ def do_node(top, n, meta=None): if meta_stream: meta_stream.close() + handle_ctrl_c() o = options.Options(optspec) @@ -233,6 +241,8 @@ top = vfs.RefList(None) if not extra: o.fatal('must specify at least one filename to restore') +exclude_rxs = parse_rx_excludes(flags, o.fatal) + if opt.outdir: mkdirp(opt.outdir) os.chdir(opt.outdir) diff --git a/t/test.sh b/t/test.sh index 362c519..58c9cca 100755 --- a/t/test.sh +++ b/t/test.sh @@ -795,3 +795,89 @@ $D/ ./sub ./sub/foo" ) || WVFAIL + + +# bup restore --exclude-rx ... +( + set -e + export BUP_DIR="$TOP/buptest.tmp" + D=bupdata.tmp + + WVSTART "restore --exclude-rx '^/foo' (root anchor)" + rm -rf "$D" "$BUP_DIR" buprestore.tmp + WVPASS bup init + mkdir $D + touch $D/a + touch $D/b + mkdir $D/sub1 + mkdir $D/sub2 + touch $D/sub1/a + touch $D/sub2/b + WVPASS bup index -u $D + bup save --strip -n bupdir $D + bup restore -C buprestore.tmp --exclude-rx "^/sub1/" /bupdir/latest/ + WVPASSEQ "$(cd buprestore.tmp && find . | sort)" ". +./a +./b +./sub2 +./sub2/b" + + WVSTART "restore --exclude-rx '/foo$' (non-dir, tail anchor)" + rm -rf "$D" "$BUP_DIR" buprestore.tmp + WVPASS bup init + mkdir $D + touch $D/a + touch $D/b + touch $D/foo + mkdir $D/sub + mkdir $D/sub/foo + touch $D/sub/foo/a + WVPASS bup index -u $D + bup save --strip -n bupdir $D + bup restore -C buprestore.tmp --exclude-rx '/foo$' /bupdir/latest/ + WVPASSEQ "$(cd buprestore.tmp && find . | sort)" ". +./a +./b +./sub +./sub/foo +./sub/foo/a" + + WVSTART "restore --exclude-rx '/foo/$' (dir, tail anchor)" + rm -rf "$D" "$BUP_DIR" buprestore.tmp + WVPASS bup init + mkdir $D + touch $D/a + touch $D/b + touch $D/foo + mkdir $D/sub + mkdir $D/sub/foo + touch $D/sub/foo/a + WVPASS bup index -u $D + bup save --strip -n bupdir $D + bup restore -C buprestore.tmp --exclude-rx '/foo/$' /bupdir/latest/ + WVPASSEQ "$(cd buprestore.tmp && find . | sort)" ". +./a +./b +./foo +./sub" + + WVSTART "restore --exclude-rx '/foo/.' (dir content)" + rm -rf "$D" "$BUP_DIR" buprestore.tmp + WVPASS bup init + mkdir $D + touch $D/a + touch $D/b + touch $D/foo + mkdir $D/sub + mkdir $D/sub/foo + touch $D/sub/foo/a + WVPASS bup index -u $D + bup save --strip -n bupdir $D + bup restore -C buprestore.tmp --exclude-rx '/foo/.' /bupdir/latest/ + WVPASSEQ "$(cd buprestore.tmp && find . | sort)" ". +./a +./b +./foo +./sub +./sub/foo" +) || WVFAIL -- 2.39.2