+ # Test the case where the hardlink hasn't changed, but the tree
+ # needs to be saved again. i.e. the save-cmd.py "if hashvalid:"
+ # case.
+ (
+ WVPASS cd src
+ WVPASS echo whatever > something-new
+ ) || exit $?
+ WVPASS bup index src
+ WVPASS bup save -t -n src src
+ WVPASS hardlink-test-run-restore
+ WVPASS "$TOP/t/compare-trees" -c src/ src-restore/src/
+
+ # Test hardlink changes between index runs.
+ #
+ WVPASS setup-hardlink-test
+ WVPASS cd src
+ WVPASS touch hardlink-target-a
+ WVPASS touch hardlink-target-b
+ WVPASS ln hardlink-target-a hardlink-b-1
+ WVPASS ln hardlink-target-a hardlink-a-1
+ WVPASS cd ..
+ WVPASS bup index -vv src
+ WVPASS rm src/hardlink-b-1
+ WVPASS ln src/hardlink-target-b src/hardlink-b-1
+ WVPASS bup index -vv src
+ WVPASS bup save -t -n src src
+ WVPASS hardlink-test-run-restore
+ WVPASS echo ./src/hardlink-a-1 > hardlink-sets.expected
+ WVPASS echo ./src/hardlink-target-a >> hardlink-sets.expected
+ WVPASS echo >> hardlink-sets.expected
+ WVPASS echo ./src/hardlink-b-1 >> hardlink-sets.expected
+ WVPASS echo ./src/hardlink-target-b >> hardlink-sets.expected
+ (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
+ || exit $?
+ WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
+
+ # Test hardlink changes between index and save -- hardlink set [a
+ # b c d] changes to [a b] [c d]. At least right now bup should
+ # notice and recreate the latter.
+ WVPASS setup-hardlink-test
+ WVPASS cd "$tmpdir"/src
+ WVPASS touch a
+ WVPASS ln a b
+ WVPASS ln a c
+ WVPASS ln a d
+ WVPASS cd ..
+ WVPASS bup index -vv src
+ WVPASS rm src/c src/d
+ WVPASS touch src/c
+ WVPASS ln src/c src/d
+ WVPASS bup save -t -n src src
+ WVPASS hardlink-test-run-restore
+ WVPASS echo ./src/a > hardlink-sets.expected
+ WVPASS echo ./src/b >> hardlink-sets.expected
+ WVPASS echo >> hardlink-sets.expected
+ WVPASS echo ./src/c >> hardlink-sets.expected
+ WVPASS echo ./src/d >> hardlink-sets.expected
+ (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
+ || exit $?
+ WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
+
+ # Test that we don't link outside restore tree.
+ WVPASS setup-hardlink-test
+ WVPASS cd "$tmpdir"
+ WVPASS mkdir src/a src/b
+ WVPASS touch src/a/1
+ WVPASS ln src/a/1 src/b/1
+ WVPASS bup index -vv src
+ WVPASS bup save -t -n src src
+ WVPASS force-delete src-restore
+ WVPASS mkdir src-restore
+ WVPASS bup restore -C src-restore "/src/latest$(pwd)/src/a/"
+ WVPASS test -e src-restore/1
+ WVPASS echo -n > hardlink-sets.expected
+ (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
+ || exit $?
+ WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
+
+ # Test that we do link within separate sub-trees.
+ WVPASS setup-hardlink-test
+ WVPASS cd "$tmpdir"
+ WVPASS mkdir src/a src/b
+ WVPASS touch src/a/1
+ WVPASS ln src/a/1 src/b/1
+ WVPASS bup index -vv src/a src/b
+ WVPASS bup save -t -n src src/a src/b
+ WVPASS hardlink-test-run-restore
+ WVPASS echo ./src/a/1 > hardlink-sets.expected
+ WVPASS echo ./src/b/1 >> hardlink-sets.expected
+ (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
+ || exit $?
+ WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
+
+ WVPASS rm -r "$tmpdir"
+
+) || exit $?
+
+WVSTART 'meta --edit'
+(
+ tmpdir="$(WVPASS wvmktempdir)" || exit $?
+ WVPASS cd "$tmpdir"
+ WVPASS mkdir src
+
+ WVPASS bup meta -cf src.meta src
+
+ WVPASS bup meta --edit --set-uid 0 src.meta | WVPASS bup meta -tvvf - \
+ | WVPASS grep -qE '^uid: 0'
+ WVPASS bup meta --edit --set-uid 1000 src.meta | WVPASS bup meta -tvvf - \
+ | WVPASS grep -qE '^uid: 1000'
+
+ WVPASS bup meta --edit --set-gid 0 src.meta | WVPASS bup meta -tvvf - \
+ | WVPASS grep -qE '^gid: 0'
+ WVPASS bup meta --edit --set-gid 1000 src.meta | WVPASS bup meta -tvvf - \
+ | WVPASS grep -qE '^gid: 1000'
+
+ WVPASS bup meta --edit --set-user foo src.meta | WVPASS bup meta -tvvf - \
+ | WVPASS grep -qE '^user: foo'
+ WVPASS bup meta --edit --set-user bar src.meta | WVPASS bup meta -tvvf - \
+ | WVPASS grep -qE '^user: bar'
+ WVPASS bup meta --edit --unset-user src.meta | WVPASS bup meta -tvvf - \
+ | WVPASS grep -qE '^user:'
+ WVPASS bup meta --edit --set-user bar --unset-user src.meta \
+ | WVPASS bup meta -tvvf - | WVPASS grep -qE '^user:'
+ WVPASS bup meta --edit --unset-user --set-user bar src.meta \
+ | WVPASS bup meta -tvvf - | WVPASS grep -qE '^user: bar'
+
+ WVPASS bup meta --edit --set-group foo src.meta | WVPASS bup meta -tvvf - \
+ | WVPASS grep -qE '^group: foo'
+ WVPASS bup meta --edit --set-group bar src.meta | WVPASS bup meta -tvvf - \
+ | WVPASS grep -qE '^group: bar'
+ WVPASS bup meta --edit --unset-group src.meta | WVPASS bup meta -tvvf - \
+ | WVPASS grep -qE '^group:'
+ WVPASS bup meta --edit --set-group bar --unset-group src.meta \
+ | WVPASS bup meta -tvvf - | WVPASS grep -qE '^group:'
+ WVPASS bup meta --edit --unset-group --set-group bar src.meta \
+ | WVPASS bup meta -tvvf - | grep -qE '^group: bar'
+
+ WVPASS rm -r "$tmpdir"
+
+) || exit $?
+
+WVSTART 'meta --no-recurse'
+(
+ tmpdir="$(WVPASS wvmktempdir)" || exit $?
+ WVPASS cd "$tmpdir"
+ WVPASS mkdir src
+ WVPASS mkdir src/foo
+ WVPASS touch src/foo/{1,2,3}
+ WVPASS bup meta -cf src.meta src
+ WVPASSEQ "$(bup meta -tf src.meta | LC_ALL=C sort)" "src/
+src/foo/
+src/foo/1
+src/foo/2
+src/foo/3"
+ WVPASS bup meta --no-recurse -cf src.meta src
+ WVPASSEQ "$(bup meta -tf src.meta | LC_ALL=C sort)" "src/"
+ WVPASS rm -r "$tmpdir"
+) || exit $?
+
+# Test ownership restoration (when not root or fakeroot).
+(
+ if [ "$root_status" != none ]; then
+ exit 0
+ fi
+
+ tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+ # FIXME: binary groups
+ first_group="$(WVPASS bup-cfg-py -c 'import os,grp; \
+ print(grp.getgrgid(os.getgroups()[0])[0])')" || exit $?
+ last_group="$(bup-cfg-py -c 'import os,grp; \
+ print(grp.getgrgid(os.getgroups()[-1])[0])')" || exit $?
+ last_group_erx="$(escape-erx "$last_group")"
+
+ WVSTART 'metadata (restoration of ownership)'
+ WVPASS cd "$tmpdir"
+ WVPASS touch src
+ # Some systems always assign the parent dir group to new paths
+ # (sgid). Make sure the group is one we're in.
+ WVPASS chgrp -R "$first_group" src
+
+ WVPASS bup meta -cf src.meta src
+
+ WVPASS mkdir dest
+ WVPASS cd dest
+ # Make sure we don't change (or try to change) the user when not root.
+ WVPASS bup meta --edit --set-user root ../src.meta | WVPASS bup meta -x
+ WVPASS bup xstat src | WVPASS grep -qvE '^user: root'
+ WVPASS rm -rf src
+ WVPASS bup meta --edit --unset-user --set-uid 0 ../src.meta \
+ | WVPASS bup meta -x
+ WVPASS bup xstat src | WVPASS grep -qvE '^user: root'
+
+ # Make sure we can restore one of the user's groups.
+ WVPASS rm -rf src
+ WVPASS bup meta --edit --set-group "$last_group" ../src.meta \
+ | WVPASS bup meta -x
+ WVPASS bup xstat src | WVPASS grep -qE "^group: $last_group_erx"
+
+ # Make sure we can restore one of the user's gids.
+ user_gids="$(id -G)" || exit $?
+ last_gid="$(echo ${user_gids/* /})" || exit $?
+ WVPASS rm -rf src
+ WVPASS bup meta --edit --unset-group --set-gid "$last_gid" ../src.meta \
+ | WVPASS bup meta -x
+ WVPASS bup xstat src | WVPASS grep -qE "^gid: $last_gid"
+
+ # Test --numeric-ids (gid).
+ WVPASS rm -rf src
+ current_gidx=$(bup meta -tvvf ../src.meta | grep -e '^gid:') || exit $?
+ WVPASS bup meta --edit --set-group "$last_group" ../src.meta \
+ | WVPASS bup meta -x --numeric-ids
+ new_gidx=$(bup xstat src | grep -e '^gid:') || exit $?
+ WVPASSEQ "$current_gidx" "$new_gidx"
+
+ # Test that restoring an unknown user works.
+ unknown_user=$("$TOP"/t/unknown-owner --user) || exit $?
+ WVPASS rm -rf src
+ current_uidx=$(bup meta -tvvf ../src.meta | grep -e '^uid:') || exit $?
+ WVPASS bup meta --edit --set-user "$unknown_user" ../src.meta \
+ | WVPASS bup meta -x
+ new_uidx=$(bup xstat src | grep -e '^uid:') || exit $?
+ WVPASSEQ "$current_uidx" "$new_uidx"
+
+ # Test that restoring an unknown group works.
+ unknown_group=$("$TOP"/t/unknown-owner --group) || exit $?
+ WVPASS rm -rf src
+ current_gidx=$(bup meta -tvvf ../src.meta | grep -e '^gid:') || exit $?
+ WVPASS bup meta --edit --set-group "$unknown_group" ../src.meta \
+ | WVPASS bup meta -x
+ new_gidx=$(bup xstat src | grep -e '^gid:') || exit $?
+ WVPASSEQ "$current_gidx" "$new_gidx"
+
+ WVPASS rm -r "$tmpdir"
+
+) || exit $?
+
+# Test ownership restoration (when root or fakeroot).
+(
+ if [ "$root_status" = none ]; then
+ exit 0
+ fi
+
+ tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+ uid=$(WVPASS id -un) || exit $?
+ gid=$(WVPASS id -gn) || exit $?
+
+ WVSTART 'metadata (restoration of ownership as root)'
+ WVPASS cd "$tmpdir"
+ WVPASS touch src
+ WVPASS chown "$uid:$gid" src # In case the parent dir is sgid, etc.
+ WVPASS bup meta -cf src.meta src
+
+ WVPASS mkdir dest
+ WVPASS chmod 700 dest # so we can't accidentally do something insecure
+ WVPASS cd dest
+
+ other_uinfo="$(id-other-than --user "$uid")" || exit $?
+ other_user="${other_uinfo%%:*}"
+ other_uid="${other_uinfo##*:}"
+
+ other_ginfo="$(id-other-than --group "$gid")" || exit $?
+ 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: $other_uid"
+
+ # 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: $other_gid"
+
+ other_uinfo2="$(id-other-than --user "$(id -un)" "$other_user")" || exit $?
+ other_user2="${other_uinfo2%%:*}"
+ other_user2_erx="$(escape-erx "$other_user2")" || exit $?
+ other_uid2="${other_uinfo2##*:}"
+
+ other_ginfo2="$(id-other-than --group "$(id -gn)" "$other_group")" || exit $?
+ other_group2="${other_ginfo2%%:*}"
+ other_group2_erx="$(escape-erx "$other_group2")" || exit $?
+ 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 "$other_uid" --set-user "$other_user2" ../src.meta \
+ | WVPASS bup meta -x
+ WVPASS bup xstat src | WVPASS grep -qE "^user: $other_user2_erx"
+
+ # Try to restore a group (and see that group trumps gid when gid is not 0).
+ WVPASS bup meta --edit \
+ --set-gid "$other_gid" --set-group "$other_group2" ../src.meta \
+ | WVPASS bup meta -x
+ WVPASS bup xstat src | WVPASS grep -qE "^group: $other_group2_erx"
+
+ # 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.
+ WVPASS 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:') || exit $?
+ 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.
+ WVPASS rm -rf src
+ 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:') || exit $?
+ WVPASSEQ "$new_gidx" "gid: $other_gid"
+
+ # Test that restoring an unknown user works.
+ unknown_user=$("$TOP"/t/unknown-owner --user) || exit $?
+ WVPASS rm -rf src
+ 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:') || exit $?
+ WVPASSEQ "$new_uidx" "uid: $other_uid"
+
+ # Test that restoring an unknown group works.
+ unknown_group=$("$TOP"/t/unknown-owner --group) || exit $?
+ WVPASS rm -rf src
+ WVPASS bup meta --edit \
+ --set-gid "$other_gid" --set-group "$unknown_group" ../src.meta \
+ | WVPASS bup meta -x
+ new_gidx=$(bup xstat src | grep -e '^gid:') || exit $?
+ 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 "$other_user2" ../src.meta \
+ | WVPASS bup meta -x
+ WVPASS bup xstat src | WVPASS grep -qvE "^user: $other_user2_erx"
+ 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 "$other_group2" ../src.meta \
+ | WVPASS bup meta -x
+ WVPASS bup xstat src | WVPASS grep -qvE "^group: $other_group2_erx"
+ WVPASS bup xstat src | WVPASS grep -qE "^gid: 0"
+ fi
+
+ WVPASS rm -r "$tmpdir"
+
+) || exit $?
+
+
+# Root-only tests that require an FS with all the trimmings: ACLs,
+# Linux attr, Linux xattr, etc.
+if [ "$root_status" = root ]; then
+ (
+ # Some cleanup handled in universal-cleanup() above.
+ # These tests are only likely to work under Linux for now
+ # (patches welcome).
+ [[ $(uname) =~ Linux ]] || exit 0
+
+ if ! modprobe loop; then
+ echo 'Unable to load loopback module; skipping dependent tests.' 1>&2
+ exit 0
+ fi
+
+ testfs="$(WVPASS wvmkmountpt)" || exit $?
+ testfs_limited="$(WVPASS wvmkmountpt)" || exit $?
+ tmpdir="$(WVPASS wvmktempdir)" || exit $?
+ export BUP_DIR="$tmpdir/bup"