From: Rob Browning Date: Sat, 14 Sep 2013 18:47:23 +0000 (-0500) Subject: Fix path ownership restoration problems on Cygwin. X-Git-Tag: 0.25-rc3~10 X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=bup.git;a=commitdiff_plain;h=b836113e503ab3f8e3c30c3283eb59f936fb0023 Fix path ownership restoration problems on Cygwin. It turns out that Cygwin won't allow you to chown() a path to an unknown uid or gid, even when "root". For now, make that a deferred error on Cygwin, rework the tests to avoid it when possible, and disable the tests (on Cygwin) that require it. For the record, it appears that tar doesn't normally hit this problem on Cygwin because it uses "geteuid() == 0" to detect super-user status, which won't be true in the normal case, even if the user is an administrator. Signed-off-by: Rob Browning --- diff --git a/Documentation/bup-restore.md b/Documentation/bup-restore.md index ab98c00..5208b30 100644 --- a/Documentation/bup-restore.md +++ b/Documentation/bup-restore.md @@ -56,7 +56,9 @@ metadata contains a user or group name that doesn't exist on the current system. The use of user and group names can be disabled via `--numeric-ids` (which can be important when restoring a chroot, for example), and as a special case, a uid or gid of 0 will never be -remapped by name. +remapped by name. Additionally, some systems don't allow setting a +uid/gid that doesn't correspond with a known user/group. On those +systems, bup will log an error for each relevant path. Hardlinks will also be restored when possible, but at least currently, no links will be made to targets outside the restore tree, and if the diff --git a/lib/bup/metadata.py b/lib/bup/metadata.py index 7f51cfd..81c234f 100644 --- a/lib/bup/metadata.py +++ b/lib/bup/metadata.py @@ -394,6 +394,10 @@ class Metadata: except OSError, e: if e.errno == errno.EPERM: add_error('lchown: %s' % e) + elif sys.platform.startswith('cygwin') \ + and e.errno == errno.EINVAL: + add_error('lchown: unknown uid/gid (%d/%d) for %s' + % (uid, gid, path)) else: raise diff --git a/t/id-other-than b/t/id-other-than new file mode 100755 index 0000000..25e7fbf --- /dev/null +++ b/t/id-other-than @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +import grp +import pwd +import sys + +def usage(): + print >> sys.stderr, "Usage: id-other-than ID [ID ...]" + +if len(sys.argv) < 2: + usage() + sys.exit(1) + +def is_integer(x): + try: + int(x) + return True + except ValueError, e: + return False + +excluded_ids = frozenset(int(x) for x in sys.argv[2:] if is_integer(x)) +excluded_names = frozenset(x for x in sys.argv[2:] if not is_integer(x)) + +if sys.argv[1] == '--user': + for x in pwd.getpwall(): + if x.pw_name not in excluded_names and x.pw_uid not in excluded_ids: + print x.pw_name + ':' + str(x.pw_uid) + sys.exit(0) +elif sys.argv[1] == '--group': + for x in grp.getgrall(): + if x.gr_name not in excluded_names and x.gr_gid not in excluded_ids: + print x.gr_name + ':' + str(x.gr_gid) + sys.exit(0) +else: + usage() + sys.exit(1) diff --git a/t/some-owner b/t/some-owner deleted file mode 100755 index e200fae..0000000 --- a/t/some-owner +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -import grp -import pwd -import sys - -def usage(): - print >> sys.stderr, "Usage: some-owners (--user | --group)" - -if len(sys.argv) != 2: - usage() - sys.exit(1) - -if sys.argv[1] == '--user': - non_root_users = [x.pw_name for x in pwd.getpwall() if x.pw_name != 'root'] - print non_root_users[0] -elif sys.argv[1] == '--group': - non_root_groups = [x.gr_name for x in grp.getgrall() if x.gr_name != 'root'] - print non_root_groups[0] -else: - usage() - sys.exit(1) diff --git a/t/test-meta.sh b/t/test-meta.sh index 1d7e467..6f28cd0 100755 --- a/t/test-meta.sh +++ b/t/test-meta.sh @@ -15,6 +15,11 @@ hardlink-sets() "$TOP/t/hardlink-sets" "$@" } +id-other-than() +{ + "$TOP/t/id-other-than" "$@" +} + # Very simple metadata tests -- create a test tree then check that bup # meta can reproduce the metadata correctly (according to bup xstat) # via create, extract, start-extract, and finish-extract. The current @@ -456,75 +461,96 @@ WVSTART 'meta --edit' chmod 700 dest # so we can't accidentally do something insecure cd dest - # Make sure we can restore a uid. - WVPASS bup meta --edit --unset-user --set-uid 42 ../src.meta \ + other_uinfo="$(id-other-than --user "$(id -un)")" + other_user="${other_uinfo%%:*}" + other_uid="${other_uinfo##*:}" + + other_ginfo="$(id-other-than --group "$(id -gn)")" + other_group="${other_ginfo%%:*}" + other_gid="${other_ginfo##*:}" + + # Make sure we can restore a uid (must be in /etc/passwd b/c cygwin). + WVPASS bup meta --edit --unset-user --set-uid "$other_uid" ../src.meta \ | WVPASS bup meta -x - WVPASS bup xstat src | WVPASS grep -qE '^uid: 42' + WVPASS bup xstat src | WVPASS grep -qE "^uid: $other_uid" - # Make sure we can restore a gid. - WVPASS bup meta --edit --unset-group --set-gid 42 ../src.meta \ + # Make sure we can restore a gid (must be in /etc/group b/c cygwin). + WVPASS bup meta --edit --unset-group --set-gid "$other_gid" ../src.meta \ | WVPASS bup meta -x - WVPASS bup xstat src | WVPASS grep -qE '^gid: 42' + WVPASS bup xstat src | WVPASS grep -qE "^gid: $other_gid" + + other_uinfo2="$(id-other-than --user "$(id -un)" "$other_user")" + other_user2="${other_uinfo2%%:*}" + other_uid2="${other_uinfo2##*:}" - some_user=$("$TOP"/t/some-owner --user) - some_group=$("$TOP"/t/some-owner --group) + other_ginfo2="$(id-other-than --group "$(id -gn)" "$other_group")" + other_group2="${other_ginfo2%%:*}" + other_gid2="${other_ginfo2##*:}" # Try to restore a user (and see that user trumps uid when uid is not 0). - WVPASS bup meta --edit --set-uid 42 --set-user "$some_user" ../src.meta \ + WVPASS bup meta --edit \ + --set-uid "$other_uid2" --set-user "$some_user" ../src.meta \ | WVPASS bup meta -x WVPASS bup xstat src | WVPASS grep -qE "^user: $some_user" # Try to restore a group (and see that group trumps gid when gid is not 0). - WVPASS bup meta --edit --set-gid 42 --set-group "$some_group" ../src.meta \ + WVPASS bup meta --edit \ + --set-gid "$other_gid2" --set-group "$some_group" ../src.meta \ | WVPASS bup meta -x WVPASS bup xstat src | WVPASS grep -qE "^group: $some_user" - # Make sure a uid of 0 trumps a non-root user. - WVPASS bup meta --edit --set-user "$some_user" ../src.meta \ - | WVPASS bup meta -x - WVPASS bup xstat src | WVPASS grep -qvE "^user: $some_user" - WVPASS bup xstat src | WVPASS grep -qE "^uid: 0" - - # Make sure a gid of 0 trumps a non-root group. - WVPASS bup meta --edit --set-group "$some_user" ../src.meta \ - | WVPASS bup meta -x - WVPASS bup xstat src | WVPASS grep -qvE "^group: $some_group" - WVPASS bup xstat src | WVPASS grep -qE "^gid: 0" + # Test --numeric-ids (uid). Note the name 'root' is not handled + # specially, so we use that here as the test user name. We assume + # that the root user's uid is never 42. + rm -rf src + WVPASS bup meta --edit --set-user root --set-uid "$other_uid" ../src.meta \ + | WVPASS bup meta -x --numeric-ids + new_uidx=$(bup xstat src | grep -e '^uid:') + WVPASSEQ "$new_uidx" "uid: $other_uid" # Test --numeric-ids (gid). Note the name 'root' is not handled # specially, so we use that here as the test group name. We # assume that the root group's gid is never 42. rm -rf src - WVPASS bup meta --edit --set-group root --set-gid 42 ../src.meta \ + WVPASS bup meta --edit --set-group root --set-gid "$other_gid" ../src.meta \ | WVPASS bup meta -x --numeric-ids new_gidx=$(bup xstat src | grep -e '^gid:') - WVPASSEQ "$new_gidx" 'gid: 42' - - # Test --numeric-ids (uid). Note the name 'root' is not handled - # specially, so we use that here as the test user name. We assume - # that the root user's uid is never 42. - rm -rf src - WVPASS bup meta --edit --set-user root --set-uid 42 ../src.meta \ - | WVPASS bup meta -x --numeric-ids - new_uidx=$(bup xstat src | grep -e '^uid:') - WVPASSEQ "$new_uidx" 'uid: 42' + WVPASSEQ "$new_gidx" "gid: $other_gid" # Test that restoring an unknown user works. unknown_user=$("$TOP"/t/unknown-owners --user) rm -rf src - WVPASS bup meta --edit --set-uid 42 --set-user "$unknown_user" ../src.meta \ + WVPASS bup meta --edit \ + --set-uid "$other_uid" --set-user "$unknown_user" ../src.meta \ | WVPASS bup meta -x new_uidx=$(bup xstat src | grep -e '^uid:') - WVPASSEQ "$new_uidx" 'uid: 42' + WVPASSEQ "$new_uidx" "uid: $other_uid" # Test that restoring an unknown group works. unknown_group=$("$TOP"/t/unknown-owners --group) rm -rf src WVPASS bup meta --edit \ - --set-gid 42 --set-group "$unknown_group" ../src.meta \ + --set-gid "$other_gid" --set-group "$unknown_group" ../src.meta \ | WVPASS bup meta -x new_gidx=$(bup xstat src | grep -e '^gid:') - WVPASSEQ "$new_gidx" 'gid: 42' + WVPASSEQ "$new_gidx" "gid: $other_gid" + + if ! [[ $(uname) =~ CYGWIN ]]; then + # For now, skip these on Cygwin because it doesn't allow + # restoring an unknown uid/gid. + + # Make sure a uid of 0 trumps a non-root user. + WVPASS bup meta --edit --set-user "$some_user" ../src.meta \ + | WVPASS bup meta -x + WVPASS bup xstat src | WVPASS grep -qvE "^user: $some_user" + WVPASS bup xstat src | WVPASS grep -qE "^uid: 0" + + # Make sure a gid of 0 trumps a non-root group. + WVPASS bup meta --edit --set-group "$some_user" ../src.meta \ + | WVPASS bup meta -x + WVPASS bup xstat src | WVPASS grep -qvE "^group: $some_group" + WVPASS bup xstat src | WVPASS grep -qE "^gid: 0" + fi ) # Root-only tests that require an FS with all the trimmings: ACLs,