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