]> arthur.barton.de Git - bup.git/blob - t/test-meta.sh
Call add_error() with one arg on readlink failure
[bup.git] / t / test-meta.sh
1 #!/usr/bin/env bash
2 . wvtest.sh || exit $?
3 . t/lib.sh || exit $?
4
5 set -o pipefail
6
7 TOP="$(WVPASS pwd)" || exit $?
8 export BUP_DIR="$TOP/buptest.tmp"
9
10 WVPASS force-delete "$TOP/bupmeta.tmp"
11 timestamp_resolutions="$(t/ns-timestamp-resolutions "$TOP/bupmeta.tmp")" \
12     || exit $?
13 WVPASS rm "$TOP/bupmeta.tmp"
14 atime_resolution="$(echo $timestamp_resolutions | WVPASS cut -d' ' -f 1)" \
15     || exit $?
16 mtime_resolution="$(echo $timestamp_resolutions | WVPASS cut -d' ' -f 2)" \
17     || exit $?
18
19 root_status="$(t/root-status)" || exit $?
20
21 bup()
22 {
23     "$TOP/bup" "$@"
24 }
25
26 hardlink-sets()
27 {
28     "$TOP/t/hardlink-sets" "$@"
29 }
30
31 id-other-than()
32 {
33     "$TOP/t/id-other-than" "$@"
34 }
35
36 # Very simple metadata tests -- create a test tree then check that bup
37 # meta can reproduce the metadata correctly (according to bup xstat)
38 # via create, extract, start-extract, and finish-extract.  The current
39 # tests are crude, and this does not fully test devices, varying
40 # users/groups, acls, attrs, etc.
41
42 genstat()
43 {
44     (
45         export PATH="$TOP:$PATH" # pick up bup
46         # Skip atime (test elsewhere) to avoid the observer effect.
47         WVPASS find . | WVPASS sort \
48             | WVPASS xargs bup xstat \
49             --mtime-resolution "$mtime_resolution"ns \
50             --exclude-fields ctime,atime,size
51     )
52 }
53
54 test-src-create-extract()
55 {
56     # Test bup meta create/extract for ./src -> ./src-restore.
57     # Also writes to ./src-stat and ./src-restore-stat.
58     (
59         (WVPASS cd src; WVPASS genstat) > src-stat || exit $?
60         WVPASS bup meta --create --recurse --file src.meta src
61         # Test extract.
62         WVPASS force-delete src-restore
63         WVPASS mkdir src-restore
64         WVPASS cd src-restore
65         WVPASS bup meta --extract --file ../src.meta
66         WVPASS test -d src
67         (WVPASS cd src; WVPASS genstat >../../src-restore-stat) || exit $?
68         WVPASS diff -U5 ../src-stat ../src-restore-stat
69         # Test start/finish extract.
70         WVPASS force-delete src
71         WVPASS bup meta --start-extract --file ../src.meta
72         WVPASS test -d src
73         WVPASS bup meta --finish-extract --file ../src.meta
74         (WVPASS cd src; WVPASS genstat >../../src-restore-stat) || exit $?
75         WVPASS diff -U5 ../src-stat ../src-restore-stat
76     )
77 }
78
79 test-src-save-restore()
80 {
81     # Test bup save/restore metadata for ./src -> ./src-restore.  Also
82     # writes to ./src.bup.  Note that for now this just tests the
83     # restore below src/, in order to avoid having to worry about
84     # operations that require root (like chown /home).
85     (
86         WVPASS rm -rf src.bup
87         WVPASS mkdir src.bup
88         export BUP_DIR=$(pwd)/src.bup || exit $?
89         WVPASS bup init
90         WVPASS bup index src
91         WVPASS bup save -t -n src src
92         # Test extract.
93         WVPASS force-delete src-restore
94         WVPASS mkdir src-restore
95         WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
96         WVPASS test -d src-restore/src
97         WVPASS "$TOP/t/compare-trees" -c src/ src-restore/src/
98         WVPASS rm -rf src.bup
99     )
100 }
101
102 universal-cleanup()
103 {
104     if [ "$root_status" != root ]; then return 0; fi
105     umount "$TOP/bupmeta.tmp/testfs" || true
106     umount "$TOP/bupmeta.tmp/testfs-limited" || true
107 }
108
109 WVPASS universal-cleanup
110 trap universal-cleanup EXIT
111
112 setup-test-tree()
113 {
114     WVPASS force-delete "$BUP_DIR"
115     WVPASS force-delete "$TOP/bupmeta.tmp"
116     WVPASS mkdir -p "$TOP/bupmeta.tmp/src"
117     WVPASS cp -pPR Documentation cmd config lib "$TOP/bupmeta.tmp"/src
118
119     # Add some hard links for the general tests.
120     (
121         WVPASS cd "$TOP/bupmeta.tmp"/src
122         WVPASS touch hardlink-target
123         WVPASS ln hardlink-target hardlink-1
124         WVPASS ln hardlink-target hardlink-2
125         WVPASS ln hardlink-target hardlink-3
126     ) || exit $?
127
128     # Add some trivial files for the index, modify, save tests.
129     (
130         WVPASS cd "$TOP/bupmeta.tmp"/src
131         WVPASS mkdir volatile
132         WVPASS touch volatile/{1,2,3}
133     ) || exit $?
134
135     # Regression test for metadata sort order.  Previously, these two
136     # entries would sort in the wrong order because the metadata
137     # entries were being sorted by mangled name, but the index isn't.
138     WVPASS dd if=/dev/zero of="$TOP/bupmeta.tmp"/src/foo bs=1k count=33
139     WVPASS touch -t 201111111111 "$TOP/bupmeta.tmp"/src/foo
140     WVPASS touch -t 201112121111 "$TOP/bupmeta.tmp"/src/foo-bar
141
142     t/mksock "$TOP/bupmeta.tmp/src/test-socket" || true
143 }
144
145 # Use the test tree to check bup meta.
146 WVSTART 'meta --create/--extract'
147 (
148     WVPASS setup-test-tree
149     WVPASS cd "$TOP/bupmeta.tmp"
150     WVPASS test-src-create-extract
151
152     # Test a top-level file (not dir).
153     WVPASS touch src-file
154     WVPASS bup meta -cf src-file.meta src-file
155     WVPASS mkdir dest
156     WVPASS cd dest
157     WVPASS bup meta -xf ../src-file.meta
158 ) || exit $?
159
160 # Use the test tree to check bup save/restore metadata.
161 WVSTART 'metadata save/restore (general)'
162 (
163     WVPASS setup-test-tree
164     WVPASS cd "$TOP/bupmeta.tmp"
165     WVPASS test-src-save-restore
166
167     # Test a deeper subdir/ to make sure top-level non-dir metadata is
168     # restored correctly.  We need at least one dir and one non-dir at
169     # the "top-level".
170     WVPASS test -f src/lib/__init__.py
171     WVPASS test -d src/lib/bup
172     WVPASS rm -rf src.bup
173     WVPASS mkdir src.bup
174     export BUP_DIR=$(pwd)/src.bup || exit $?
175     WVPASS bup init
176     WVPASS touch -t 201111111111 src-restore # Make sure the top won't match.
177     WVPASS bup index src
178     WVPASS bup save -t -n src src
179     WVPASS force-delete src-restore
180     WVPASS bup restore -C src-restore "/src/latest$(pwd)/src/lib/"
181     WVPASS touch -t 201211111111 src-restore # Make sure the top won't match.
182     # Check that the only difference is the top dir.
183     WVFAIL $TOP/t/compare-trees -c src/lib/ src-restore/ > tmp-compare-trees
184     WVPASSEQ $(cat tmp-compare-trees | wc -l) 2
185     WVPASS tail -n +2 tmp-compare-trees | WVPASS grep -qE '^\.d[^ ]+ \./$'
186 ) || exit $?
187
188 # Test that we pull the index (not filesystem) metadata for any
189 # unchanged files whenever we're saving other files in a given
190 # directory.
191 WVSTART 'metadata save/restore (using index metadata)'
192 (
193     WVPASS setup-test-tree
194     WVPASS cd "$TOP/bupmeta.tmp"
195
196     # ...for now -- might be a problem with hardlink restores that was
197     # causing noise wrt this test.
198     WVPASS rm -rf src/hardlink*
199
200     # Pause here to keep the filesystem changes far enough away from
201     # the first index run that bup won't cap their index timestamps
202     # (see "bup help index" for more information).  Without this
203     # sleep, the compare-trees test below "Bup should *not* pick up
204     # these metadata..." may fail.
205     WVPASS sleep 1
206
207     WVPASS rm -rf src.bup
208     WVPASS mkdir src.bup
209     export BUP_DIR=$(pwd)/src.bup || exit $?
210     WVPASS bup init
211     WVPASS bup index src
212     WVPASS bup save -t -n src src
213
214     WVPASS force-delete src-restore-1
215     WVPASS mkdir src-restore-1
216     WVPASS bup restore -C src-restore-1 "/src/latest$(pwd)/"
217     WVPASS test -d src-restore-1/src
218     WVPASS "$TOP/t/compare-trees" -c src/ src-restore-1/src/
219
220     WVPASS echo "blarg" > src/volatile/1
221     WVPASS cp -a src/volatile/1 src-restore-1/src/volatile/
222     WVPASS bup index src
223
224     # Bup should *not* pick up these metadata changes.
225     WVPASS touch src/volatile/2
226
227     WVPASS bup save -t -n src src
228
229     WVPASS force-delete src-restore-2
230     WVPASS mkdir src-restore-2
231     WVPASS bup restore -C src-restore-2 "/src/latest$(pwd)/"
232     WVPASS test -d src-restore-2/src
233     WVPASS "$TOP/t/compare-trees" -c src-restore-1/src/ src-restore-2/src/
234
235     WVPASS rm -rf src.bup
236 ) || exit $?
237
238 setup-hardlink-test()
239 {
240     WVPASS rm -rf "$TOP/bupmeta.tmp"/src "$TOP/bupmeta.tmp"/src.bup
241     WVPASS mkdir "$TOP/bupmeta.tmp"/src "$TOP/bupmeta.tmp"/src.bup
242     WVPASS bup init
243 }
244
245 hardlink-test-run-restore()
246 {
247     WVPASS force-delete src-restore
248     WVPASS mkdir src-restore
249     WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
250     WVPASS test -d src-restore/src
251 }
252
253 # Test hardlinks more carefully.
254 WVSTART 'metadata save/restore (hardlinks)'
255 (
256     export BUP_DIR="$TOP/bupmeta.tmp/src.bup"
257     WVPASS force-delete "$TOP/bupmeta.tmp"
258     WVPASS mkdir -p "$TOP/bupmeta.tmp"
259
260     WVPASS cd "$TOP/bupmeta.tmp"
261
262     # Test trivial case - single hardlink.
263     WVPASS setup-hardlink-test
264     (
265         WVPASS cd "$TOP/bupmeta.tmp"/src
266         WVPASS touch hardlink-target
267         WVPASS ln hardlink-target hardlink-1
268     ) || exit $?
269     WVPASS bup index src
270     WVPASS bup save -t -n src src
271     WVPASS hardlink-test-run-restore
272     WVPASS "$TOP/t/compare-trees" -c src/ src-restore/src/
273
274     # Test the case where the hardlink hasn't changed, but the tree
275     # needs to be saved again. i.e. the save-cmd.py "if hashvalid:"
276     # case.
277     (
278         WVPASS cd "$TOP/bupmeta.tmp"/src
279         WVPASS echo whatever > something-new
280     ) || exit $?
281     WVPASS bup index src
282     WVPASS bup save -t -n src src
283     WVPASS hardlink-test-run-restore
284     WVPASS "$TOP/t/compare-trees" -c src/ src-restore/src/
285
286     # Test hardlink changes between index runs.
287     #
288     WVPASS setup-hardlink-test
289     WVPASS cd "$TOP/bupmeta.tmp"/src
290     WVPASS touch hardlink-target-a
291     WVPASS touch hardlink-target-b
292     WVPASS ln hardlink-target-a hardlink-b-1
293     WVPASS ln hardlink-target-a hardlink-a-1
294     WVPASS cd ..
295     WVPASS bup index -vv src
296     WVPASS rm src/hardlink-b-1
297     WVPASS ln src/hardlink-target-b src/hardlink-b-1
298     WVPASS bup index -vv src
299     WVPASS bup save -t -n src src
300     WVPASS hardlink-test-run-restore
301     WVPASS echo ./src/hardlink-a-1 > hardlink-sets.expected
302     WVPASS echo ./src/hardlink-target-a >> hardlink-sets.expected
303     WVPASS echo >> hardlink-sets.expected
304     WVPASS echo ./src/hardlink-b-1 >> hardlink-sets.expected
305     WVPASS echo ./src/hardlink-target-b >> hardlink-sets.expected
306     (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
307         || exit $?
308     WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
309
310     # Test hardlink changes between index and save -- hardlink set [a
311     # b c d] changes to [a b] [c d].  At least right now bup should
312     # notice and recreate the latter.
313     WVPASS setup-hardlink-test
314     WVPASS cd "$TOP/bupmeta.tmp"/src
315     WVPASS touch a
316     WVPASS ln a b
317     WVPASS ln a c
318     WVPASS ln a d
319     WVPASS cd ..
320     WVPASS bup index -vv src
321     WVPASS rm src/c src/d
322     WVPASS touch src/c
323     WVPASS ln src/c src/d
324     WVPASS bup save -t -n src src
325     WVPASS hardlink-test-run-restore
326     WVPASS echo ./src/a > hardlink-sets.expected
327     WVPASS echo ./src/b >> hardlink-sets.expected
328     WVPASS echo >> hardlink-sets.expected
329     WVPASS echo ./src/c >> hardlink-sets.expected
330     WVPASS echo ./src/d >> hardlink-sets.expected
331     (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
332         || exit $?
333     WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
334
335     # Test that we don't link outside restore tree.
336     WVPASS setup-hardlink-test
337     WVPASS cd "$TOP/bupmeta.tmp"
338     WVPASS mkdir src/a src/b
339     WVPASS touch src/a/1
340     WVPASS ln src/a/1 src/b/1
341     WVPASS bup index -vv src
342     WVPASS bup save -t -n src src
343     WVPASS force-delete src-restore
344     WVPASS mkdir src-restore
345     WVPASS bup restore -C src-restore "/src/latest$(pwd)/src/a/"
346     WVPASS test -e src-restore/1
347     WVPASS echo -n > hardlink-sets.expected
348     (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
349         || exit $?
350     WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
351
352     # Test that we do link within separate sub-trees.
353     WVPASS setup-hardlink-test
354     WVPASS cd "$TOP/bupmeta.tmp"
355     WVPASS mkdir src/a src/b
356     WVPASS touch src/a/1
357     WVPASS ln src/a/1 src/b/1
358     WVPASS bup index -vv src/a src/b
359     WVPASS bup save -t -n src src/a src/b
360     WVPASS hardlink-test-run-restore
361     WVPASS echo ./src/a/1 > hardlink-sets.expected
362     WVPASS echo ./src/b/1 >> hardlink-sets.expected
363     (WVPASS cd src-restore; WVPASS hardlink-sets .) > hardlink-sets.restored \
364         || exit $?
365     WVPASS diff -u hardlink-sets.expected hardlink-sets.restored
366 ) || exit $?
367
368 WVSTART 'meta --edit'
369 (
370     WVPASS force-delete "$TOP/bupmeta.tmp"
371     WVPASS mkdir "$TOP/bupmeta.tmp"
372     WVPASS cd "$TOP/bupmeta.tmp"
373     WVPASS mkdir src
374     WVPASS bup meta -cf src.meta src
375
376     WVPASS bup meta --edit --set-uid 0 src.meta | WVPASS bup meta -tvvf - \
377         | WVPASS grep -qE '^uid: 0'
378     WVPASS bup meta --edit --set-uid 1000 src.meta | WVPASS bup meta -tvvf - \
379         | WVPASS grep -qE '^uid: 1000'
380
381     WVPASS bup meta --edit --set-gid 0 src.meta | WVPASS bup meta -tvvf - \
382         | WVPASS grep -qE '^gid: 0'
383     WVPASS bup meta --edit --set-gid 1000 src.meta | WVPASS bup meta -tvvf - \
384         | WVPASS grep -qE '^gid: 1000'
385
386     WVPASS bup meta --edit --set-user foo src.meta | WVPASS bup meta -tvvf - \
387         | WVPASS grep -qE '^user: foo'
388     WVPASS bup meta --edit --set-user bar src.meta | WVPASS bup meta -tvvf - \
389         | WVPASS grep -qE '^user: bar'
390     WVPASS bup meta --edit --unset-user src.meta | WVPASS bup meta -tvvf - \
391         | WVPASS grep -qE '^user:'
392     WVPASS bup meta --edit --set-user bar --unset-user src.meta \
393         | WVPASS bup meta -tvvf - | WVPASS grep -qE '^user:'
394     WVPASS bup meta --edit --unset-user --set-user bar src.meta \
395         | WVPASS bup meta -tvvf - | WVPASS grep -qE '^user: bar'
396
397     WVPASS bup meta --edit --set-group foo src.meta | WVPASS bup meta -tvvf - \
398         | WVPASS grep -qE '^group: foo'
399     WVPASS bup meta --edit --set-group bar src.meta | WVPASS bup meta -tvvf - \
400         | WVPASS grep -qE '^group: bar'
401     WVPASS bup meta --edit --unset-group src.meta | WVPASS bup meta -tvvf - \
402         | WVPASS grep -qE '^group:'
403     WVPASS bup meta --edit --set-group bar --unset-group src.meta \
404         | WVPASS bup meta -tvvf - | WVPASS grep -qE '^group:'
405     WVPASS bup meta --edit --unset-group --set-group bar src.meta \
406         | WVPASS bup meta -tvvf - | grep -qE '^group: bar'
407 ) || exit $?
408
409 WVSTART 'meta --no-recurse'
410 (
411     set +e
412     WVPASS force-delete "$TOP/bupmeta.tmp"
413     WVPASS mkdir "$TOP/bupmeta.tmp"
414     WVPASS cd "$TOP/bupmeta.tmp"
415     WVPASS mkdir src
416     WVPASS mkdir src/foo
417     WVPASS touch src/foo/{1,2,3}
418     WVPASS bup meta -cf src.meta src
419     WVPASSEQ "$(LC_ALL=C; bup meta -tf src.meta | sort)" "src/
420 src/foo/
421 src/foo/1
422 src/foo/2
423 src/foo/3"
424     WVPASS bup meta --no-recurse -cf src.meta src
425     WVPASSEQ "$(LC_ALL=C; bup meta -tf src.meta | sort)" "src/"
426 ) || exit $?
427
428 # Test ownership restoration (when not root or fakeroot).
429 (
430     if [ "$root_status" != none ]; then
431         exit 0
432     fi
433
434     first_group="$(WVPASS python -c 'import os,grp; \
435       print grp.getgrgid(os.getgroups()[0])[0]')" || exit $?
436     last_group="$(python -c 'import os,grp; \
437       print grp.getgrgid(os.getgroups()[-1])[0]')" || exit $?
438     last_group_erx="$(escape-erx "$last_group")"
439
440     WVSTART 'metadata (restoration of ownership)'
441     WVPASS force-delete "$TOP/bupmeta.tmp"
442     WVPASS mkdir "$TOP/bupmeta.tmp"
443     WVPASS cd "$TOP/bupmeta.tmp"
444     WVPASS touch src
445     # Some systems always assign the parent dir group to new paths
446     # (sgid).  Make sure the group is one we're in.
447     WVPASS chgrp -R "$first_group" src
448
449     WVPASS bup meta -cf src.meta src
450
451     WVPASS mkdir dest
452     WVPASS cd dest
453     # Make sure we don't change (or try to change) the user when not root.
454     WVPASS bup meta --edit --set-user root ../src.meta | WVPASS bup meta -x
455     WVPASS bup xstat src | WVPASS grep -qvE '^user: root'
456     WVPASS rm -rf src
457     WVPASS bup meta --edit --unset-user --set-uid 0 ../src.meta \
458         | WVPASS bup meta -x
459     WVPASS bup xstat src | WVPASS grep -qvE '^user: root'
460
461     # Make sure we can restore one of the user's groups.
462     WVPASS rm -rf src
463     WVPASS bup meta --edit --set-group "$last_group" ../src.meta \
464         | WVPASS bup meta -x
465     WVPASS bup xstat src | WVPASS grep -qE "^group: $last_group_erx"
466
467     # Make sure we can restore one of the user's gids.
468     user_gids="$(id -G)" || exit $?
469     last_gid="$(echo ${user_gids/* /})" || exit $?
470     WVPASS rm -rf src
471     WVPASS bup meta --edit --unset-group --set-gid "$last_gid" ../src.meta \
472         | WVPASS bup meta -x
473     WVPASS bup xstat src | WVPASS grep -qE "^gid: $last_gid"
474
475     # Test --numeric-ids (gid).
476     WVPASS rm -rf src
477     current_gidx=$(bup meta -tvvf ../src.meta | grep -e '^gid:') || exit $?
478     WVPASS bup meta --edit --set-group "$last_group" ../src.meta \
479         | WVPASS bup meta -x --numeric-ids
480     new_gidx=$(bup xstat src | grep -e '^gid:') || exit $?
481     WVPASSEQ "$current_gidx" "$new_gidx"
482
483     # Test that restoring an unknown user works.
484     unknown_user=$("$TOP"/t/unknown-owner --user) || exit $?
485     WVPASS rm -rf src
486     current_uidx=$(bup meta -tvvf ../src.meta | grep -e '^uid:') || exit $?
487     WVPASS bup meta --edit --set-user "$unknown_user" ../src.meta \
488         | WVPASS bup meta -x
489     new_uidx=$(bup xstat src | grep -e '^uid:') || exit $?
490     WVPASSEQ "$current_uidx" "$new_uidx"
491
492     # Test that restoring an unknown group works.
493     unknown_group=$("$TOP"/t/unknown-owner --group) || exit $?
494     WVPASS rm -rf src
495     current_gidx=$(bup meta -tvvf ../src.meta | grep -e '^gid:') || exit $?
496     WVPASS bup meta --edit --set-group "$unknown_group" ../src.meta \
497         | WVPASS bup meta -x
498     new_gidx=$(bup xstat src | grep -e '^gid:') || exit $?
499     WVPASSEQ "$current_gidx" "$new_gidx"
500 ) || exit $?
501
502 # Test ownership restoration (when root or fakeroot).
503 (
504     if [ "$root_status" = none ]; then
505         exit 0
506     fi
507
508     uid=$(WVPASS id -un) || exit $?
509     gid=$(WVPASS id -gn) || exit $?
510
511     WVSTART 'metadata (restoration of ownership as root)'
512     WVPASS force-delete "$TOP/bupmeta.tmp"
513     WVPASS mkdir "$TOP/bupmeta.tmp"
514     WVPASS cd "$TOP/bupmeta.tmp"
515     WVPASS touch src
516     WVPASS chown "$uid:$gid" src # In case the parent dir is sgid, etc.
517     WVPASS bup meta -cf src.meta src
518
519     WVPASS mkdir dest
520     WVPASS chmod 700 dest # so we can't accidentally do something insecure
521     WVPASS cd dest
522
523     other_uinfo="$(id-other-than --user "$uid")" || exit $?
524     other_user="${other_uinfo%%:*}"
525     other_uid="${other_uinfo##*:}"
526
527     other_ginfo="$(id-other-than --group "$gid")" || exit $?
528     other_group="${other_ginfo%%:*}"
529     other_gid="${other_ginfo##*:}"
530
531     # Make sure we can restore a uid (must be in /etc/passwd b/c cygwin).
532     WVPASS bup meta --edit --unset-user --set-uid "$other_uid" ../src.meta \
533         | WVPASS bup meta -x
534     WVPASS bup xstat src | WVPASS grep -qE "^uid: $other_uid"
535
536     # Make sure we can restore a gid (must be in /etc/group b/c cygwin).
537     WVPASS bup meta --edit --unset-group --set-gid "$other_gid" ../src.meta \
538         | WVPASS bup meta -x
539     WVPASS bup xstat src | WVPASS grep -qE "^gid: $other_gid"
540
541     other_uinfo2="$(id-other-than --user "$(id -un)" "$other_user")" || exit $?
542     other_user2="${other_uinfo2%%:*}"
543     other_user2_erx="$(escape-erx "$other_user2")" || exit $?
544     other_uid2="${other_uinfo2##*:}"
545
546     other_ginfo2="$(id-other-than --group "$(id -gn)" "$other_group")" || exit $?
547     other_group2="${other_ginfo2%%:*}"
548     other_group2_erx="$(escape-erx "$other_group2")" || exit $?
549     other_gid2="${other_ginfo2##*:}"
550
551     # Try to restore a user (and see that user trumps uid when uid is not 0).
552     WVPASS bup meta --edit \
553         --set-uid "$other_uid" --set-user "$other_user2" ../src.meta \
554         | WVPASS bup meta -x
555     WVPASS bup xstat src | WVPASS grep -qE "^user: $other_user2_erx"
556
557     # Try to restore a group (and see that group trumps gid when gid is not 0).
558     WVPASS bup meta --edit \
559         --set-gid "$other_gid" --set-group "$other_group2" ../src.meta \
560         | WVPASS bup meta -x
561     WVPASS bup xstat src | WVPASS grep -qE "^group: $other_group2_erx"
562
563     # Test --numeric-ids (uid).  Note the name 'root' is not handled
564     # specially, so we use that here as the test user name.  We assume
565     # that the root user's uid is never 42.
566     WVPASS rm -rf src
567     WVPASS bup meta --edit --set-user root --set-uid "$other_uid" ../src.meta \
568         | WVPASS bup meta -x --numeric-ids
569     new_uidx=$(bup xstat src | grep -e '^uid:') || exit $?
570     WVPASSEQ "$new_uidx" "uid: $other_uid"
571
572     # Test --numeric-ids (gid).  Note the name 'root' is not handled
573     # specially, so we use that here as the test group name.  We
574     # assume that the root group's gid is never 42.
575     WVPASS rm -rf src
576     WVPASS bup meta --edit --set-group root --set-gid "$other_gid" ../src.meta \
577         | WVPASS bup meta -x --numeric-ids
578     new_gidx=$(bup xstat src | grep -e '^gid:') || exit $?
579     WVPASSEQ "$new_gidx" "gid: $other_gid"
580
581     # Test that restoring an unknown user works.
582     unknown_user=$("$TOP"/t/unknown-owner --user) || exit $?
583     WVPASS rm -rf src
584     WVPASS bup meta --edit \
585         --set-uid "$other_uid" --set-user "$unknown_user" ../src.meta \
586         | WVPASS bup meta -x
587     new_uidx=$(bup xstat src | grep -e '^uid:') || exit $?
588     WVPASSEQ "$new_uidx" "uid: $other_uid"
589
590     # Test that restoring an unknown group works.
591     unknown_group=$("$TOP"/t/unknown-owner --group) || exit $?
592     WVPASS rm -rf src
593     WVPASS bup meta --edit \
594         --set-gid "$other_gid" --set-group "$unknown_group" ../src.meta \
595         | WVPASS bup meta -x
596     new_gidx=$(bup xstat src | grep -e '^gid:') || exit $?
597     WVPASSEQ "$new_gidx" "gid: $other_gid"
598
599     if ! [[ $(uname) =~ CYGWIN ]]; then
600         # For now, skip these on Cygwin because it doesn't allow
601         # restoring an unknown uid/gid.
602
603         # Make sure a uid of 0 trumps a non-root user.
604         WVPASS bup meta --edit --set-user "$other_user2" ../src.meta \
605             | WVPASS bup meta -x
606         WVPASS bup xstat src | WVPASS grep -qvE "^user: $other_user2_erx"
607         WVPASS bup xstat src | WVPASS grep -qE "^uid: 0"
608
609         # Make sure a gid of 0 trumps a non-root group.
610         WVPASS bup meta --edit --set-group "$other_group2" ../src.meta \
611             | WVPASS bup meta -x
612         WVPASS bup xstat src | WVPASS grep -qvE "^group: $other_group2_erx"
613         WVPASS bup xstat src | WVPASS grep -qE "^gid: 0"
614     fi
615 ) || exit $?
616
617
618 # Root-only tests that require an FS with all the trimmings: ACLs,
619 # Linux attr, Linux xattr, etc.
620 if [ "$root_status" = root ]; then
621     (
622         # Some cleanup handled in universal-cleanup() above.
623         # These tests are only likely to work under Linux for now
624         # (patches welcome).
625         [[ $(uname) =~ Linux ]] || exit 0
626
627         WVSTART 'meta - general (as root)'
628         WVPASS setup-test-tree
629         WVPASS cd "$TOP/bupmeta.tmp"
630
631         umount testfs
632         WVPASS dd if=/dev/zero of=testfs.img bs=1M count=32
633         # Make sure we have all the options the chattr test needs
634         # (i.e. create a "normal" ext4 filesystem).
635         WVPASS mke2fs -F -m 0 \
636             -I 256 \
637             -O has_journal,extent,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize \
638             testfs.img
639         WVPASS mkdir testfs
640         WVPASS mount -o loop,acl,user_xattr testfs.img testfs
641         # Hide, so that tests can't create risks.
642         WVPASS chown root:root testfs
643         WVPASS chmod 0700 testfs
644
645         umount testfs-limited
646         WVPASS dd if=/dev/zero of=testfs-limited.img bs=1M count=32
647         WVPASS mkfs -t vfat testfs-limited.img
648         WVPASS mkdir testfs-limited
649         WVPASS mount -o loop,uid=root,gid=root,umask=0077 \
650             testfs-limited.img testfs-limited
651
652         WVPASS cp -pPR src testfs/src
653         (WVPASS cd testfs; WVPASS test-src-create-extract) || exit $?
654
655         WVSTART 'meta - atime (as root)'
656         WVPASS force-delete testfs/src
657         WVPASS mkdir testfs/src
658         (
659             WVPASS mkdir testfs/src/foo
660             WVPASS touch testfs/src/bar
661             PYTHONPATH="$TOP/lib" \
662                 WVPASS python -c "from bup import xstat; \
663                 x = xstat.timespec_to_nsecs((42, 0));\
664                    xstat.utime('testfs/src/foo', (x, x));\
665                    xstat.utime('testfs/src/bar', (x, x));"
666             WVPASS cd testfs
667             WVPASS bup meta -v --create --recurse --file src.meta src
668             WVPASS bup meta -tvf src.meta
669             # Test extract.
670             WVPASS force-delete src-restore
671             WVPASS mkdir src-restore
672             WVPASS cd src-restore
673             WVPASS bup meta --extract --file ../src.meta
674             WVPASSEQ "$(bup xstat --include-fields=atime src/foo)" "atime: 42"
675             WVPASSEQ "$(bup xstat --include-fields=atime src/bar)" "atime: 42"
676             # Test start/finish extract.
677             WVPASS force-delete src
678             WVPASS bup meta --start-extract --file ../src.meta
679             WVPASS test -d src
680             WVPASS bup meta --finish-extract --file ../src.meta
681             WVPASSEQ "$(bup xstat --include-fields=atime src/foo)" "atime: 42"
682             WVPASSEQ "$(bup xstat --include-fields=atime src/bar)" "atime: 42"
683         ) || exit $?
684
685         WVSTART 'meta - Linux attr (as root)'
686         WVPASS force-delete testfs/src
687         WVPASS mkdir testfs/src
688         (
689             WVPASS touch testfs/src/foo
690             WVPASS mkdir testfs/src/bar
691             WVPASS chattr +acdeijstuADST testfs/src/foo
692             WVPASS chattr +acdeijstuADST testfs/src/bar
693             (WVPASS cd testfs; WVPASS test-src-create-extract) || exit $?
694             # Test restoration to a limited filesystem (vfat).
695             (
696                 WVPASS bup meta --create --recurse --file testfs/src.meta \
697                     testfs/src
698                 WVPASS force-delete testfs-limited/src-restore
699                 WVPASS mkdir testfs-limited/src-restore
700                 WVPASS cd testfs-limited/src-restore
701                 WVFAIL bup meta --extract --file ../../testfs/src.meta 2>&1 \
702                     | WVPASS grep -e '^Linux chattr:' \
703                     | WVPASS python -c \
704                     'import sys; exit(not len(sys.stdin.readlines()) == 3)'
705             ) || exit $?
706         ) || exit $?
707
708         WVSTART 'meta - Linux xattr (as root)'
709         WVPASS force-delete testfs/src
710         WVPASS mkdir testfs/src
711         WVPASS touch testfs/src/foo
712         WVPASS mkdir testfs/src/bar
713         WVPASS attr -s foo -V bar testfs/src/foo
714         WVPASS attr -s foo -V bar testfs/src/bar
715         (WVPASS cd testfs; WVPASS test-src-create-extract) || exit $?
716
717         # Test restoration to a limited filesystem (vfat).
718         (
719             WVPASS bup meta --create --recurse --file testfs/src.meta \
720                 testfs/src
721             WVPASS force-delete testfs-limited/src-restore
722             WVPASS mkdir testfs-limited/src-restore
723             WVPASS cd testfs-limited/src-restore
724             WVFAIL bup meta --extract --file ../../testfs/src.meta
725             WVFAIL bup meta --extract --file ../../testfs/src.meta 2>&1 \
726                 | WVPASS grep -e "^xattr\.set '" \
727                 | WVPASS python -c \
728                 'import sys; exit(not len(sys.stdin.readlines()) == 2)'
729         ) || exit $?
730
731         WVSTART 'meta - POSIX.1e ACLs (as root)'
732         WVPASS force-delete testfs/src
733         WVPASS mkdir testfs/src
734         WVPASS touch testfs/src/foo
735         WVPASS mkdir testfs/src/bar
736         WVPASS setfacl -m u:root:r testfs/src/foo
737         WVPASS setfacl -m u:root:r testfs/src/bar
738         (WVPASS cd testfs; WVPASS test-src-create-extract) || exit $?
739
740         # Test restoration to a limited filesystem (vfat).
741         (
742             WVPASS bup meta --create --recurse --file testfs/src.meta \
743                 testfs/src
744             WVPASS force-delete testfs-limited/src-restore
745             WVPASS mkdir testfs-limited/src-restore
746             WVPASS cd testfs-limited/src-restore
747             WVFAIL bup meta --extract --file ../../testfs/src.meta 2>&1 \
748                 | WVPASS grep -e '^POSIX1e ACL applyto:' \
749                 | WVPASS python -c \
750                 'import sys; exit(not len(sys.stdin.readlines()) == 2)'
751         ) || exit $?
752     ) || exit $?
753 fi