diff options
Diffstat (limited to '')
-rw-r--r-- | tests/m_rootgnutar/expect | 141 | ||||
-rw-r--r-- | tests/m_rootgnutar/mkgnutar.pl | 138 | ||||
-rw-r--r-- | tests/m_rootgnutar/output.sed | 5 | ||||
-rw-r--r-- | tests/m_rootgnutar/script | 169 |
4 files changed, 453 insertions, 0 deletions
diff --git a/tests/m_rootgnutar/expect b/tests/m_rootgnutar/expect new file mode 100644 index 0000000..10def6b --- /dev/null +++ b/tests/m_rootgnutar/expect @@ -0,0 +1,141 @@ +Creating regular file test.img +Exit status is 0 +Filesystem volume name: <none> +Last mounted on: <not available> +Filesystem magic number: 0xEF53 +Filesystem revision #: 1 (dynamic) +Filesystem features: has_journal ext_attr resize_inode dir_index filetype extent 64bit flex_bg sparse_super huge_file dir_nlink extra_isize metadata_csum +Default mount options: (none) +Filesystem state: clean +Errors behavior: Continue +Filesystem OS type: Linux +Inode count: 1024 +Block count: 16384 +Reserved block count: 819 +Overhead clusters: 1543 +Free blocks: 14791 +Free inodes: 1005 +First block: 1 +Block size: 1024 +Fragment size: 1024 +Group descriptor size: 64 +Reserved GDT blocks: 127 +Blocks per group: 8192 +Fragments per group: 8192 +Inodes per group: 512 +Inode blocks per group: 128 +Flex block group size: 16 +Mount count: 0 +Check interval: 15552000 (6 months) +Reserved blocks uid: 0 +Reserved blocks gid: 0 +First inode: 11 +Inode size: 256 +Required extra isize: 32 +Desired extra isize: 32 +Journal inode: 8 +Default directory hash: half_md4 +Journal backup: inode blocks +Checksum type: crc32c +Journal features: (none) +Total journal size: 1024k +Total journal blocks: 1024 +Max transaction length: 1024 +Fast commit length: 0 +Journal sequence: 0x00000001 +Journal start: 0 + + +Group 0: (Blocks 1-8192) + Primary superblock at 1, Group descriptors at 2-2 + Reserved GDT blocks at 3-129 + Block bitmap at 130 (+129) + Inode bitmap at 132 (+131) + Inode table at 134-261 (+133) + 7753 free blocks, 493 free inodes, 5 directories, 493 unused inodes + Free blocks: 440-8192 + Free inodes: 20-512 +Group 1: (Blocks 8193-16383) [INODE_UNINIT] + Backup superblock at 8193, Group descriptors at 8194-8194 + Reserved GDT blocks at 8195-8321 + Block bitmap at 131 (bg #0 + 130) + Inode bitmap at 133 (bg #0 + 132) + Inode table at 262-389 (bg #0 + 261) + 7038 free blocks, 512 free inodes, 0 directories, 512 unused inodes + Free blocks: 9346-16383 + Free inodes: 513-1024 +Exit status is 0 +debugfs: stat /test/emptyfile +Inode: III Type: regular +Size: 0 +Links: 1 Blockcount: 0 +Fragment: Address: 0 Number: 0 Size: 0 +debugfs: stat /test/bigfile +Inode: III Type: regular +Size: 32768 +Links: 1 Blockcount: 64 +Fragment: Address: 0 Number: 0 Size: 0 +debugfs: stat /test/zerofile +Inode: III Type: regular +Size: 1025 +Links: 1 Blockcount: 0 +Fragment: Address: 0 Number: 0 Size: 0 +debugfs: stat /test/silly_bs_link +Inode: III Type: symlink +Size: 14 +Links: 1 Blockcount: 0 +Fragment: Address: 0 Number: 0 Size: 0 +debugfs: stat /test/emptydir +Inode: III Type: directory +Size: 1024 +Links: 2 Blockcount: 2 +Fragment: Address: 0 Number: 0 Size: 0 +debugfs: stat /test/dir +Inode: III Type: directory +Size: 1024 +Links: 2 Blockcount: 2 +Fragment: Address: 0 Number: 0 Size: 0 +debugfs: stat /test/dir/file +Inode: III Type: regular +Size: 8 +Links: 1 Blockcount: 2 +Fragment: Address: 0 Number: 0 Size: 0 +Exit status is 0 +debugfs: ex /test/emptyfile +Level Entries Logical Physical Length Flags +debugfs: ex /test/bigfile +Level Entries Logical Physical Length Flags +X 0/0 1/1 0-31 AAA-BBB 32 +debugfs: ex /test/zerofile +Level Entries Logical Physical Length Flags +debugfs: ex /test/silly_bs_link +/test/silly_bs_link: does not uses extent block maps +debugfs: ex /test/emptydir +Level Entries Logical Physical Length Flags +X 0/0 1/1 0-0 AAA-BBB 1 +debugfs: ex /test/dir +Level Entries Logical Physical Length Flags +X 0/0 1/1 0-0 AAA-BBB 1 +debugfs: ex /test/dir/file +Level Entries Logical Physical Length Flags +X 0/0 1/1 0-0 AAA-BBB 1 +Exit status is 0 +Exit status is 0 +Exit status is 0 +test/ +test/bigfile +test/dir/ +test/dir/file +test/emptydir/ +test/emptyfile +test/silly_bs_link +test/zerofile +Exit status is 0 +Pass 1: Checking inodes, blocks, and sizes +Pass 2: Checking directory structure +Pass 3: Checking directory connectivity +Pass 4: Checking reference counts +Pass 5: Checking group summary information +test.img: 19/1024 files (0.0% non-contiguous), 1593/16384 blocks +Exit status is 0 +Exit status is 0 diff --git a/tests/m_rootgnutar/mkgnutar.pl b/tests/m_rootgnutar/mkgnutar.pl new file mode 100644 index 0000000..516124c --- /dev/null +++ b/tests/m_rootgnutar/mkgnutar.pl @@ -0,0 +1,138 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use Getopt::Long; +use Fcntl ':mode'; + +my ($directory, $mtime, $nopadding, $norec, $verbose); +GetOptions( + "directory=s" => \$directory, + "mtime=i" => \$mtime, + "nopadding" => \$nopadding, + "no-recursion" => \$norec, + "verbose" => \$verbose, +); + +chdir($directory) || die "cannot chdir"; + +my $num_entries = 0; + +sub recurse_dir { + my $path = shift; + my @results = ("$path/"); + opendir my $dh, $path or die "cannot open $path"; + while (my $entry = readdir $dh) { + next if $entry eq "."; + next if $entry eq ".."; + if (-d "$path/$entry") { + push @results, (&recurse_dir("$path/$entry")); + } else { + push @results, "$path/$entry"; + } + } + closedir $dh; + return @results; +} + +my @entries; +if (!-e $ARGV[0]) { + die "does not exist: $ARGV[0]"; +} elsif (-d $ARGV[0] && !$norec) { + @entries = sort (recurse_dir($ARGV[0])); +} else { + @entries = ($ARGV[0]); +} + +foreach my $fname (@entries) { + if ($verbose) { + print STDERR "$fname\n"; + } + my ( + $dev, $ino, $mode, $nlink, $uid, $gid, $rdev, + $size, $atime, $mtime_, $ctime, $blksize, $blocks + ) = lstat($fname); + if (!defined $mode) { + die "failed to stat $fname"; + } + my $content = ""; + my $type; + my $linkname = ""; + my $username = $ENV{LOGNAME} || $ENV{USER} || getpwuid($<); + if (S_ISLNK($mode)) { + $type = 2; + $linkname = readlink $fname; + } elsif (S_ISREG($mode)) { + $type = 0; + open(my $fh, '<', $fname); + $content = do { local $/; <$fh> }; + close($fh); + } elsif (S_ISDIR($mode)) { + $type = 5; + } + my $entry = pack( + 'a100 a8 a8 a8 a12 a12 A8 a1 a100 a6 a2 a32 a32 a8 a8 a155 x12', + $fname, + sprintf('%07o', $mode & 07777), + sprintf('%07o', $<), # uid + sprintf('%07o', $(), # gid + sprintf('%011o', length $content), # size + sprintf('%011o', $mtime), + # mtime + '', # checksum + $type, + $linkname, # linkname + "ustar ", # magic + " ", # version + "$username", # username + "$username", # groupname + '', # dev major + '', # dev minor + '', # prefix + ); + + # compute and insert checksum + substr($entry, 148, 7) + = sprintf("%06o\0", unpack("%16C*", $entry)); + print $entry; + $num_entries += 1; + + if (length $content) { + my $num_blocks = int((length $content) / 512); + if ((length $content) % 512 != 0) { + $num_blocks += 1; + } + print $content; + print(("\x00") x ($num_blocks * 512 - (length $content))); + $num_entries += $num_blocks; + } +} + +if (!$nopadding) { + # https://www.gnu.org/software/tar/manual/html_node/Standard.html + # + # Physically, an archive consists of a series of file entries terminated + # by an end-of-archive entry, which consists of two 512 blocks of zero + # bytes. At the end of the archive file there are two 512-byte blocks + # filled with binary zeros as an end-of-file marker. + print(pack 'a512', ''); + print(pack 'a512', ''); + $num_entries += 2; + + # https://www.gnu.org/software/tar/manual/html_section/tar_76.html + # + # Some devices requires that all write operations be a multiple of a + # certain size, and so, tar pads the archive out to the next record + # boundary. + # + # The default blocking factor is 20. With a block size of 512 bytes, we + # get a record size of 10240. + my $num_records = int($num_entries * 512 / 10240); + if (($num_entries * 512) % 10240 != 0) { + $num_records += 1; + } + for (my $i = $num_entries ; $i < $num_records * 10240 / 512 ; $i++) { + print(pack 'a512', ''); + } +} diff --git a/tests/m_rootgnutar/output.sed b/tests/m_rootgnutar/output.sed new file mode 100644 index 0000000..2e76967 --- /dev/null +++ b/tests/m_rootgnutar/output.sed @@ -0,0 +1,5 @@ +s/^[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)[[:space:]]*-[[:space:]]*\([0-9]*\)[[:space:]]*[0-9]*[[:space:]]*-[[:space:]]*[0-9]*[[:space:]]*\([0-9]*\)/X \1\/\2 \3\/\4 \5-\6 AAA-BBB \7/g +s/^[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)\/[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*\)[[:space:]]*-[[:space:]]*\([0-9]*\)[[:space:]]*[0-9]*[[:space:]]*\([0-9]*\)/Y \1\/\2 \3\/\4 \5-\6 AAA \7/g +s/Mode:.*$//g +s/User:.*Size:/Size:/g +s/^Inode: [0-9]*/Inode: III/g diff --git a/tests/m_rootgnutar/script b/tests/m_rootgnutar/script new file mode 100644 index 0000000..79fd222 --- /dev/null +++ b/tests/m_rootgnutar/script @@ -0,0 +1,169 @@ +# vim: filetype=sh + +use_mkgnutar= + +test_description="create fs image from GNU tarball" +if ! test -x "$DEBUGFS_EXE"; then + echo "$test_name: $test_description: skipped (no debugfs)" + return 0 +fi +if [ "$(grep -c 'define HAVE_ARCHIVE_H' ../lib/config.h)" -eq 0 ]; then + echo "$test_name: $test_description: skipped (no libarchive)" + exit 0 +fi + +if test -z "$use_mkgnutar" ; then + if type ztar > /dev/null 2>&1 && \ + tar --version 2>&1 | head -1 | grep -q "GNU tar" ; then + TAR=tar + elif type gtar > /dev/null 2>&1 && \ + gtar --version 2>&1 | head -1 | grep -q "GNU tar" ; then + TAR=gtar + else + # if GNU tar is not available, fall back to using mkgnutar.pl + use_mkgnutar=yes +# echo "$test_name: $test_description: skipped (no GNU tar)" +# exit 0 + fi +fi + +MKFS_TAR="$TMPFILE.tar" +MKFS_DIR="$TMPFILE.dir" +OUT="$test_name.log" +EXP="$test_dir/expect" +DEBUGFS_EXE_MTIME=$(perl -e 'print((stat ($ARGV[0]))[9])' "$DEBUGFS_EXE") + +# we put everything in a subdir because we cannot rdump the root as that would +# require permissions to changing ownership of /lost+found +rm -rf "$MKFS_DIR" +mkdir -p "$MKFS_DIR/test" +touch "$MKFS_DIR/test/emptyfile" +dd if=/dev/zero bs=1024 count=32 2> /dev/null | tr '\0' 'a' > "$MKFS_DIR/test/bigfile" +dd if=/dev/zero of="$MKFS_DIR/test/zerofile" bs=1 count=1 seek=1024 2> /dev/null +ln -s /silly_bs_link "$MKFS_DIR/test/silly_bs_link" +mkdir "$MKFS_DIR/test/emptydir" +mkdir "$MKFS_DIR/test/dir" +echo "will be overwritten" > "$MKFS_DIR/test/dir/file" + +if test -z "$use_mkgnutar"; then + # debugfs rdump does not preserve the timestamps when it extracts the + # files so we ignore them by using tar --clamp-mtime + # first write a partial tar + $TAR --sort=name -C "$MKFS_DIR" --mtime="$DEBUGFS_EXE" --clamp-mtime \ + --format=gnu -cf "$MKFS_TAR.dupl" test + # now overwrite the contents of a file + echo "Test me" > "$MKFS_DIR/test/dir/file" + # and update the tar so that it contains two entries for the same file + # we need this to test the code path that first unlinks and then overwrites an + # existing file + $TAR -C "$MKFS_DIR" --mtime="$DEBUGFS_EXE" --clamp-mtime \ + --format=gnu -rf "$MKFS_TAR.dupl" test/dir/file + # also add a duplicate directory entry because those must not be unlinked + echo test | $TAR -C "$MKFS_DIR" --mtime="$DEBUGFS_EXE" --clamp-mtime \ + --format=gnu -rf "$MKFS_TAR.dupl" --no-recursion \ + --verbatim-files-from --files-from=- + # also create a tarball of the directory with only one entry per file + $TAR --sort=name -C "$MKFS_DIR" --mtime="$DEBUGFS_EXE" --clamp-mtime \ + --format=gnu -cf "$MKFS_TAR.uniq" test +else + # same as above but without using GNU tar + perl $test_dir/mkgnutar.pl --nopadding --directory="$MKFS_DIR" --mtime "$DEBUGFS_EXE_MTIME" test > "$MKFS_TAR.dupl" + echo "Test me" > "$MKFS_DIR/test/dir/file" + perl $test_dir/mkgnutar.pl --nopadding --directory="$MKFS_DIR" --mtime "$DEBUGFS_EXE_MTIME" test/dir/file >> "$MKFS_TAR.dupl" + perl $test_dir/mkgnutar.pl --nopadding --directory="$MKFS_DIR" --mtime "$DEBUGFS_EXE_MTIME" --no-recursion test/ >> "$MKFS_TAR.dupl" + # add end-of-archive entry + truncate -s +1024 "$MKFS_TAR.dupl" + # pad to a multiple of the record size + truncate -s %10240 "$MKFS_TAR.dupl" + perl $test_dir/mkgnutar.pl --directory="$MKFS_DIR" --mtime "$DEBUGFS_EXE_MTIME" test > "$MKFS_TAR.uniq" +fi + +rm -r "$MKFS_DIR" + +cat > "$TMPFILE.cmd1" << ENDL +stat /test/emptyfile +stat /test/bigfile +stat /test/zerofile +stat /test/silly_bs_link +stat /test/emptydir +stat /test/dir +stat /test/dir/file +ENDL + +cat > "$TMPFILE.cmd2" << ENDL +ex /test/emptyfile +ex /test/bigfile +ex /test/zerofile +ex /test/silly_bs_link +ex /test/emptydir +ex /test/dir +ex /test/dir/file +ENDL + +# Create two file systems, one for each tar that was created above. The +# tarballs differ but should result in the same filesystem contents +# +for ext in uniq dupl; do + mkdir "$MKFS_DIR" + { + $MKE2FS -q -F -o Linux -T ext4 -O metadata_csum,64bit -E lazy_itable_init=1 -b 1024 -d "$MKFS_TAR.$ext" "$TMPFILE.$ext" 16384 2>&1; + echo Exit status is $?; + $DUMPE2FS "$TMPFILE.$ext" 2>&1; + echo Exit status is $?; + $DEBUGFS -f "$TMPFILE.cmd1" "$TMPFILE.$ext" 2>&1 | grep -E "(stat|Size:|Type:|Links:|Blockcount:)" + echo Exit status is $?; + $DEBUGFS -f "$TMPFILE.cmd2" "$TMPFILE.$ext" 2>&1; + echo Exit status is $?; + $DEBUGFS -R "dump /test/dir/file $TMPFILE.testme" "$TMPFILE.$ext" 2>&1; + echo Exit status is $?; + # extract the files and directories from the image and tar them + # again to make sure that a tarball from the image contents is + # bit-by-bit identical to the tarball the image was created + # from -- essentially this checks whether a roundtrip from tar + # to ext4 to tar remains identical + $DEBUGFS -R "rdump /test $MKFS_DIR" "$TMPFILE.$ext" 2>&1; + echo Exit status is $?; + # debugfs rdump does not preserve the timestamps when it extracts the + if test -z "$use_mkgnutar"; then + # files so we ignore them by using tar --clamp-mtime + $TAR --sort=name -C "$MKFS_DIR" \ + --mtime="$DEBUGFS_EXE" --clamp-mtime --format=gnu \ + -cvf "$TMPFILE.new.tar" test 2>&1; + else + perl $test_dir/mkgnutar.pl --verbose --directory="$MKFS_DIR" --mtime "$DEBUGFS_EXE_MTIME" test 2>&1 > "$TMPFILE.new.tar"; + fi; + echo Exit status is $?; + $FSCK -f -n "$TMPFILE.$ext" 2>&1; + echo Exit status is $?; + # independent from which tarball the ext4 image was created, + # the tarball created from the files in it should be bit-by-bit + # identical to the tarball without duplicate entries + cmp "$MKFS_TAR.uniq" "$TMPFILE.new.tar" 2>&1; + echo Exit status is $?; + } | sed -f "$cmd_dir/filter.sed" -f "$test_dir/output.sed" -e "s;$TMPFILE.$ext;test.img;" | { + # In the first pass, store the output and append to the log + # file. In the second pass, compare the output to the output + # to the one from the first. + case $ext in + uniq) tee "$TMPFILE.log" >> "$OUT";; + dupl) cmp - "$TMPFILE.log" >> "$OUT" 2>&1 || echo "cmp failed" >> "$OUT";; + esac + } + rm -r "$MKFS_DIR" "$TMPFILE.new.tar" +done + +# Do the verification +cmp -s "$OUT" "$EXP" +status=$? + +if [ "$status" = 0 ] ; then + echo "$test_name: $test_description: ok" + touch "$test_name.ok" +else + echo "$test_name: $test_description: failed" + diff $DIFF_OPTS "$EXP" "$OUT" > "$test_name.failed" +fi + +rm -rf "$MKFS_TAR.dupl" "$MKFS_TAR.uniq" "$TMPFILE.cmd1" "$TMPFILE.cmd2" \ + "$TMPFILE.log" "$TMPFILE.dupl" "$TMPFILE.uniq" "$TMPFILE.testme" +unset MKFS_TAR MKFS_DIR OUT EXP |