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