]> arthur.barton.de Git - bup.git/blob - t/test-meta.sh
aea9b77146330a8ebfd7efdb1bfae57ddb510a70
[bup.git] / t / test-meta.sh
1 #!/usr/bin/env bash
2 . wvtest.sh
3
4 TOP="$(pwd)"
5 export BUP_DIR="$TOP/buptest.tmp"
6
7 bup()
8 {
9     "$TOP/bup" "$@"
10 }
11
12 # Very simple metadata tests -- create a test tree then check that bup
13 # meta can reproduce the metadata correctly (according to bup xstat)
14 # via create, extract, start-extract, and finish-extract.  The current
15 # tests are crude, and this does not fully test devices, varying
16 # users/groups, acls, attrs, etc.
17
18 genstat()
19 {
20     (
21         export PATH="$TOP:$PATH" # pick up bup
22         # Skip atime (test elsewhere) to avoid the observer effect.
23         find . | sort | xargs bup xstat --exclude-fields ctime,atime,size
24     )
25 }
26
27 actually-root()
28 {
29     test "$(whoami)" == root -a -z "$FAKEROOTKEY"
30 }
31
32 force-delete()
33 {
34     if ! actually-root; then
35         rm -rf "$@"
36     else
37         # Go to greater lengths to deal with any test detritus.
38         for f in "$@"; do
39             test -e "$@" || continue
40             chattr -fR = "$@" || true
41             setfacl -Rb "$@"
42             rm -r "$@"
43         done
44     fi
45 }
46
47 compare-trees()
48 {
49     (
50         set -e
51         set -o pipefail
52         tmpfile="$(mktemp)"
53         trap "rm -rf '${tmpfile}'" EXIT
54         rsync -ni -aHAX "$1" "$2" > "${tmpfile}"
55         if test $(wc -l < "${tmpfile}") != 0; then
56             echo "ERROR: detected differences between $1 and $2"
57             cat "${tmpfile}"
58             false
59         fi
60     )
61 }
62
63 test-src-create-extract()
64 {
65     # Test bup meta create/extract for ./src -> ./src-restore.
66     # Also writes to ./src-stat and ./src-restore-stat.
67     (
68         (cd src && WVPASS genstat) > src-stat
69         WVPASS bup meta --create --recurse --file src.meta src
70         # Test extract.
71         force-delete src-restore
72         mkdir src-restore
73         cd src-restore
74         WVPASS bup meta --extract --file ../src.meta
75         WVPASS test -d src
76         (cd src && genstat >../../src-restore-stat) || WVFAIL
77         WVPASS diff -U5 ../src-stat ../src-restore-stat
78         # Test start/finish extract.
79         force-delete src
80         WVPASS bup meta --start-extract --file ../src.meta
81         WVPASS test -d src
82         WVPASS bup meta --finish-extract --file ../src.meta
83         (cd src && genstat >../../src-restore-stat) || WVFAIL
84         WVPASS diff -U5 ../src-stat ../src-restore-stat
85     )
86 }
87
88 test-src-save-restore()
89 {
90     # Test bup save/restore metadata for ./src -> ./src-restore.  Also
91     # writes to ./src.bup.  Note that for now this just tests the
92     # restore below src/, in order to avoid having to worry about
93     # operations that require root (like chown /home).
94     (
95         set -x
96         rm -rf src.bup
97         mkdir src.bup
98         export BUP_DIR=$(pwd)/src.bup
99         WVPASS bup init
100         WVPASS bup index src
101         WVPASS bup save -t -n src src
102         # Test extract.
103         force-delete src-restore
104         mkdir src-restore
105         WVPASS bup restore -C src-restore "/src/latest$(pwd)/"
106         WVPASS test -d src-restore/src
107         WVPASS compare-trees src/ src-restore/src/
108         rm -rf src.bup
109         set +x
110     )
111 }
112
113 universal-cleanup()
114 {
115     if ! actually-root; then return 0; fi
116     cd "$TOP"
117     umount "$TOP/bupmeta.tmp/testfs" || true
118     umount "$TOP/bupmeta.tmp/testfs-limited" || true
119 }
120
121 universal-cleanup
122 trap universal-cleanup EXIT
123
124 setup-test-tree()
125 (
126     set -e
127     force-delete "$BUP_DIR"
128     force-delete "$TOP/bupmeta.tmp"
129     mkdir -p "$TOP/bupmeta.tmp/src"
130     cp -pPR Documentation cmd lib t "$TOP/bupmeta.tmp"/src
131
132     # Regression test for metadata sort order.  Previously, these two
133     # entries would sort in the wrong order because the metadata
134     # entries were being sorted by mangled name, but the index isn't.
135     dd if=/dev/zero of="$TOP/bupmeta.tmp"/src/foo bs=1k count=33
136     touch -d 2011-11-11 "$TOP/bupmeta.tmp"/src/foo
137     touch -d 2011-12-12 "$TOP/bupmeta.tmp"/src/foo-bar
138
139     t/mksock "$TOP/bupmeta.tmp/src/test-socket" || true
140 ) || WVFAIL
141
142 # Use the test tree to check bup meta.
143 WVSTART 'meta --create/--extract'
144 (
145     setup-test-tree
146     cd "$TOP/bupmeta.tmp"
147     test-src-create-extract
148
149     # Test a top-level file (not dir).
150     touch src-file
151     WVPASS bup meta -cf src-file.meta src-file
152     mkdir dest
153     cd dest
154     WVPASS bup meta -xf ../src-file.meta
155 )
156
157 # Use the test tree to check bup save/restore metadata.
158 WVSTART 'metadata save/restore (general)'
159 (
160     setup-test-tree
161     cd "$TOP/bupmeta.tmp"
162     test-src-save-restore
163 )
164
165 WVSTART 'meta --edit'
166 (
167     force-delete "$TOP/bupmeta.tmp"
168     mkdir "$TOP/bupmeta.tmp"
169     cd "$TOP/bupmeta.tmp"
170     mkdir src
171     WVPASS bup meta -cf src.meta src
172
173     WVPASS bup meta --edit --set-uid 0 src.meta | WVPASS bup meta -tvvf - \
174         | WVPASS grep -qE '^uid: 0'
175     WVPASS bup meta --edit --set-uid 1000 src.meta | WVPASS bup meta -tvvf - \
176         | WVPASS grep -qE '^uid: 1000'
177
178     WVPASS bup meta --edit --set-gid 0 src.meta | WVPASS bup meta -tvvf - \
179         | WVPASS grep -qE '^gid: 0'
180     WVPASS bup meta --edit --set-gid 1000 src.meta | WVPASS bup meta -tvvf - \
181         | WVPASS grep -qE '^gid: 1000'
182
183     WVPASS bup meta --edit --set-user foo src.meta | WVPASS bup meta -tvvf - \
184         | WVPASS grep -qE '^user: foo'
185     WVPASS bup meta --edit --set-user bar src.meta | WVPASS bup meta -tvvf - \
186         | WVPASS grep -qE '^user: bar'
187     WVPASS bup meta --edit --unset-user src.meta | WVPASS bup meta -tvvf - \
188         | WVPASS grep -qE '^user:'
189     WVPASS bup meta --edit --set-user bar --unset-user src.meta \
190         | WVPASS bup meta -tvvf - | WVPASS grep -qE '^user:'
191     WVPASS bup meta --edit --unset-user --set-user bar src.meta \
192         | WVPASS bup meta -tvvf - | WVPASS grep -qE '^user: bar'
193
194     WVPASS bup meta --edit --set-group foo src.meta | WVPASS bup meta -tvvf - \
195         | WVPASS grep -qE '^group: foo'
196     WVPASS bup meta --edit --set-group bar src.meta | WVPASS bup meta -tvvf - \
197         | WVPASS grep -qE '^group: bar'
198     WVPASS bup meta --edit --unset-group src.meta | WVPASS bup meta -tvvf - \
199         | WVPASS grep -qE '^group:'
200     WVPASS bup meta --edit --set-group bar --unset-group src.meta \
201         | WVPASS bup meta -tvvf - | WVPASS grep -qE '^group:'
202     WVPASS bup meta --edit --unset-group --set-group bar src.meta \
203         | WVPASS bup meta -tvvf - | grep -qE '^group: bar'
204 )
205
206 # Test ownership restoration (when not root or fakeroot).
207 (
208     if test "$(whoami)" == root; then
209         exit 0
210     fi
211
212     WVSTART 'metadata (restoration of ownership)'
213     force-delete "$TOP/bupmeta.tmp"
214     mkdir "$TOP/bupmeta.tmp"
215     cd "$TOP/bupmeta.tmp"
216     touch src
217     WVPASS bup meta -cf src.meta src
218
219     mkdir dest
220     cd dest
221     # Make sure we don't change (or try to change) the user when not root.
222     WVPASS bup meta --edit --set-user root ../src.meta | WVPASS bup meta -x
223     WVPASS bup xstat src | WVPASS grep -qvE '^user: root'
224     rm -rf src
225     WVPASS bup meta --edit --unset-user --set-uid 0 ../src.meta \
226         | WVPASS bup meta -x
227     WVPASS bup xstat src | grep -qvE '^user: root'
228
229     # Make sure we can restore one of the user's groups.
230     last_group="$(python -c 'import os,grp; \
231       print grp.getgrgid(os.getgroups()[0])[0]')"
232     rm -rf src
233     WVPASS bup meta --edit --set-group "$last_group" ../src.meta \
234         | WVPASS bup meta -x
235     WVPASS bup xstat src | WVPASS grep -qE "^group: $last_group"
236
237     # Make sure we can restore one of the user's gids.
238     user_gids="$(id -G)"
239     last_gid="$(echo ${user_gids/* /})"
240     rm -rf src
241     WVPASS bup meta --edit --unset-group --set-gid "$last_gid" ../src.meta \
242         | WVPASS bup meta -x
243     WVPASS bup xstat src | WVPASS grep -qE "^gid: $last_gid"
244
245     # Test --numeric-ids (gid).
246     rm -rf src
247     current_gidx=$(bup meta -tvvf ../src.meta | grep -e '^gid:')
248     WVPASS bup meta --edit --set-group "$last_group" ../src.meta \
249         | WVPASS bup meta -x --numeric-ids
250     new_gidx=$(bup xstat src | grep -e '^gid:')
251     WVPASSEQ "$current_gidx" "$new_gidx"
252
253     # Test that restoring an unknown user works.
254     unknown_user=$("$TOP"/t/unknown-owner --user)
255     rm -rf src
256     current_uidx=$(bup meta -tvvf ../src.meta | grep -e '^uid:')
257     WVPASS bup meta --edit --set-user "$unknown_user" ../src.meta \
258         | WVPASS bup meta -x
259     new_uidx=$(bup xstat src | grep -e '^uid:')
260     WVPASSEQ "$current_uidx" "$new_uidx"
261
262     # Test that restoring an unknown group works.
263     unknown_group=$("$TOP"/t/unknown-owner --group)
264     rm -rf src
265     current_gidx=$(bup meta -tvvf ../src.meta | grep -e '^gid:')
266     WVPASS bup meta --edit --set-group "$unknown_group" ../src.meta \
267         | WVPASS bup meta -x
268     new_gidx=$(bup xstat src | grep -e '^gid:')
269     WVPASSEQ "$current_gidx" "$new_gidx"
270 )
271
272 # Test ownership restoration (when root or fakeroot).
273 (
274     if test "$(whoami)" != root; then
275         exit 0
276     fi
277
278     WVSTART 'metadata (restoration of ownership as root)'
279     force-delete "$TOP/bupmeta.tmp"
280     mkdir "$TOP/bupmeta.tmp"
281     cd "$TOP/bupmeta.tmp"
282     touch src
283     chown 0:0 src # In case the parent dir is sgid, etc.
284     WVPASS bup meta -cf src.meta src
285
286     mkdir dest
287     chmod 700 dest # so we can't accidentally do something insecure
288     cd dest
289
290     # Make sure we can restore a uid.
291     WVPASS bup meta --edit --unset-user --set-uid 42 ../src.meta \
292         | WVPASS bup meta -x
293     WVPASS bup xstat src | WVPASS grep -qE '^uid: 42'
294
295     # Make sure we can restore a gid.
296     WVPASS bup meta --edit --unset-group --set-gid 42 ../src.meta \
297         | WVPASS bup meta -x
298     WVPASS bup xstat src | WVPASS grep -qE '^gid: 42'
299
300     some_user=$("$TOP"/t/some-owner --user)
301     some_group=$("$TOP"/t/some-owner --group)
302
303     # Try to restore a user (and see that user trumps uid when uid is not 0).
304     WVPASS bup meta --edit --set-uid 42 --set-user "$some_user" ../src.meta \
305         | WVPASS bup meta -x
306     WVPASS bup xstat src | WVPASS grep -qE "^user: $some_user"
307
308     # Try to restore a group (and see that group trumps gid when gid is not 0).
309     WVPASS bup meta --edit --set-gid 42 --set-group "$some_group" ../src.meta \
310         | WVPASS bup meta -x
311     WVPASS bup xstat src | WVPASS grep -qE "^group: $some_user"
312
313     # Make sure a uid of 0 trumps a non-root user.
314     WVPASS bup meta --edit --set-user "$some_user" ../src.meta \
315         | WVPASS bup meta -x
316     WVPASS bup xstat src | WVPASS grep -qvE "^user: $some_user"
317     WVPASS bup xstat src | WVPASS grep -qE "^uid: 0"
318
319     # Make sure a gid of 0 trumps a non-root group.
320     WVPASS bup meta --edit --set-group "$some_user" ../src.meta \
321         | WVPASS bup meta -x
322     WVPASS bup xstat src | WVPASS grep -qvE "^group: $some_group"
323     WVPASS bup xstat src | WVPASS grep -qE "^gid: 0"
324
325     # Test --numeric-ids (gid).  Note the name 'root' is not handled
326     # specially, so we use that here as the test group name.  We
327     # assume that the root group's gid is never 42.
328     rm -rf src
329     WVPASS bup meta --edit --set-group root --set-gid 42 ../src.meta \
330         | WVPASS bup meta -x --numeric-ids
331     new_gidx=$(bup xstat src | grep -e '^gid:')
332     WVPASSEQ "$new_gidx" 'gid: 42'
333
334     # Test --numeric-ids (uid).  Note the name 'root' is not handled
335     # specially, so we use that here as the test user name.  We assume
336     # that the root user's uid is never 42.
337     rm -rf src
338     WVPASS bup meta --edit --set-user root --set-uid 42 ../src.meta \
339         | WVPASS bup meta -x --numeric-ids
340     new_uidx=$(bup xstat src | grep -e '^uid:')
341     WVPASSEQ "$new_uidx" 'uid: 42'
342
343     # Test that restoring an unknown user works.
344     unknown_user=$("$TOP"/t/unknown-owners --user)
345     rm -rf src
346     WVPASS bup meta --edit --set-uid 42 --set-user "$unknown_user" ../src.meta \
347         | WVPASS bup meta -x
348     new_uidx=$(bup xstat src | grep -e '^uid:')
349     WVPASSEQ "$new_uidx" 'uid: 42'
350
351     # Test that restoring an unknown group works.
352     unknown_group=$("$TOP"/t/unknown-owners --group)
353     rm -rf src
354     WVPASS bup meta --edit \
355         --set-gid 42 --set-group "$unknown_group" ../src.meta \
356         | WVPASS bup meta -x
357     new_gidx=$(bup xstat src | grep -e '^gid:')
358     WVPASSEQ "$new_gidx" 'gid: 42'
359 )
360
361 # Root-only tests that require an FS with all the trimmings: ACLs,
362 # Linux attr, Linux xattr, etc.
363 if actually-root; then
364     (
365         set -e
366         # Some cleanup handled in universal-cleanup() above.
367         # These tests are only likely to work under Linux for now
368         # (patches welcome).
369         [[ $(uname) =~ Linux ]] || exit 0
370
371         WVSTART 'meta - general (as root)'
372         setup-test-tree
373         cd "$TOP/bupmeta.tmp"
374
375         umount testfs || true
376         dd if=/dev/zero of=testfs.img bs=1M count=32
377         mke2fs -F -j -m 0 testfs.img
378         mkdir testfs
379         mount -o loop,acl,user_xattr testfs.img testfs
380         # Hide, so that tests can't create risks.
381         chown root:root testfs
382         chmod 0700 testfs
383
384         umount testfs-limited || true
385         dd if=/dev/zero of=testfs-limited.img bs=1M count=32
386         mkfs -t vfat testfs-limited.img
387         mkdir testfs-limited
388         mount -o loop,uid=root,gid=root,umask=0077 \
389             testfs-limited.img testfs-limited
390
391         #cp -a src testfs/src
392         cp -pPR src testfs/src
393         (cd testfs && test-src-create-extract)
394
395         WVSTART 'meta - atime (as root)'
396         force-delete testfs/src
397         mkdir testfs/src
398         (
399             mkdir testfs/src/foo
400             touch testfs/src/bar
401             PYTHONPATH="$TOP/lib" \
402                 python -c "from bup import xstat; \
403                 x = xstat.timespec_to_nsecs((42, 0));\
404                    xstat.utime('testfs/src/foo', (x, x));\
405                    xstat.utime('testfs/src/bar', (x, x));"
406             cd testfs
407             WVPASS bup meta -v --create --recurse --file src.meta src
408             bup meta -tvf src.meta
409             # Test extract.
410             force-delete src-restore
411             mkdir src-restore
412             cd src-restore
413             WVPASS bup meta --extract --file ../src.meta
414             WVPASSEQ "$(bup xstat --include-fields=atime src/foo)" "atime: 42"
415             WVPASSEQ "$(bup xstat --include-fields=atime src/bar)" "atime: 42"
416             # Test start/finish extract.
417             force-delete src
418             WVPASS bup meta --start-extract --file ../src.meta
419             WVPASS test -d src
420             WVPASS bup meta --finish-extract --file ../src.meta
421             WVPASSEQ "$(bup xstat --include-fields=atime src/foo)" "atime: 42"
422             WVPASSEQ "$(bup xstat --include-fields=atime src/bar)" "atime: 42"
423         )
424
425         WVSTART 'meta - Linux attr (as root)'
426         force-delete testfs/src
427         mkdir testfs/src
428         (
429             touch testfs/src/foo
430             mkdir testfs/src/bar
431             chattr +acdeijstuADST testfs/src/foo
432             chattr +acdeijstuADST testfs/src/bar
433             (cd testfs && test-src-create-extract)
434             # Test restoration to a limited filesystem (vfat).
435             (
436                 WVPASS bup meta --create --recurse --file testfs/src.meta \
437                     testfs/src
438                 force-delete testfs-limited/src-restore
439                 mkdir testfs-limited/src-restore
440                 cd testfs-limited/src-restore
441                 WVFAIL bup meta --extract --file ../../testfs/src.meta 2>&1 \
442                     | WVPASS grep -e '^Linux chattr:' \
443                     | WVPASS python -c \
444                       'import sys; exit(not len(sys.stdin.readlines()) == 2)'
445             )
446         )
447
448         WVSTART 'meta - Linux xattr (as root)'
449         force-delete testfs/src
450         mkdir testfs/src
451         (
452             touch testfs/src/foo
453             mkdir testfs/src/bar
454             attr -s foo -V bar testfs/src/foo
455             attr -s foo -V bar testfs/src/bar
456             (cd testfs && test-src-create-extract)
457
458             # Test restoration to a limited filesystem (vfat).
459             (
460                 WVPASS bup meta --create --recurse --file testfs/src.meta \
461                     testfs/src
462                 force-delete testfs-limited/src-restore
463                 mkdir testfs-limited/src-restore
464                 cd testfs-limited/src-restore
465                 WVFAIL bup meta --extract --file ../../testfs/src.meta 2>&1 \
466                     | WVPASS grep -e '^xattr\.set:' \
467                     | WVPASS python -c \
468                       'import sys; exit(not len(sys.stdin.readlines()) == 2)'
469             )
470         )
471
472         WVSTART 'meta - POSIX.1e ACLs (as root)'
473         force-delete testfs/src
474         mkdir testfs/src
475         (
476             touch testfs/src/foo
477             mkdir testfs/src/bar
478             setfacl -m u:root:r testfs/src/foo
479             setfacl -m u:root:r testfs/src/bar
480             (cd testfs && test-src-create-extract)
481
482             # Test restoration to a limited filesystem (vfat).
483             (
484                 WVPASS bup meta --create --recurse --file testfs/src.meta \
485                     testfs/src
486                 force-delete testfs-limited/src-restore
487                 mkdir testfs-limited/src-restore
488                 cd testfs-limited/src-restore
489                 WVFAIL bup meta --extract --file ../../testfs/src.meta 2>&1 \
490                     | WVPASS grep -e '^POSIX1e ACL applyto:' \
491                     | WVPASS python -c \
492                       'import sys; exit(not len(sys.stdin.readlines()) == 2)'
493             )
494         )
495     )
496 fi
497
498 exit 0