summaryrefslogtreecommitdiffstats
path: root/tests/m_rootgnutar
diff options
context:
space:
mode:
Diffstat (limited to 'tests/m_rootgnutar')
-rw-r--r--tests/m_rootgnutar/expect141
-rw-r--r--tests/m_rootgnutar/mkgnutar.pl138
-rw-r--r--tests/m_rootgnutar/output.sed5
-rw-r--r--tests/m_rootgnutar/script169
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