Explorar el Código

libdpkg: Add new tar extractor test cases

Test the different supported tar formats, and verify that we can parse
them correctly.
Guillem Jover hace 12 años
padre
commit
f71e02c8e9
Se han modificado 5 ficheros con 288 adiciones y 3 borrados
  1. 1 0
      debian/changelog
  2. 2 0
      lib/dpkg/test/.gitignore
  3. 19 3
      lib/dpkg/test/Makefile.am
  4. 141 0
      lib/dpkg/test/t-tar.t
  5. 125 0
      lib/dpkg/test/t-tarextract.c

+ 1 - 0
debian/changelog

@@ -104,6 +104,7 @@ dpkg (1.17.14) UNRELEASED; urgency=low
   * Test suite:
     - Do not leave temporary files behind on failure or when interrupted.
     - Add basic unit tests for the FNV hashing function.
+    - Add unit tests for the libdpkg tar extractor.
   * Add --build and --extract command aliases to dpkg-source.
   * Print file or package names instead of pointers in dpkg debug output,
     to make it more meaningful and reproducible.

+ 2 - 0
lib/dpkg/test/.gitignore

@@ -13,6 +13,8 @@ t-pkg-queue
 t-progname
 t-string
 t-subproc
+t-tar/
+t-tarextract
 t-test
 t-test-skip
 t-trigger

+ 19 - 3
lib/dpkg/test/Makefile.am

@@ -9,9 +9,13 @@ LDADD = \
 	$(top_builddir)/lib/dpkg/libdpkg.la \
 	$(LIBINTL)
 
+EXTRA_DIST = \
+	$(test_scripts) \
+	$(nil)
+
 
 # The tests are sorted in order of increasing complexity.
-check_PROGRAMS = \
+test_programs = \
 	t-test \
 	t-test-skip \
 	t-macros \
@@ -31,8 +35,20 @@ check_PROGRAMS = \
 	t-pkg-list \
 	t-pkg-queue \
 	t-trigger \
-	t-mod-db
+	t-mod-db \
+	$(nil)
+
+test_scripts = \
+	t-tar.t \
+	$(nil)
 
-test_programs = $(check_PROGRAMS)
+check_PROGRAMS = \
+	$(test_programs) \
+	t-tarextract \
+	$(nil)
+
+test_tmpdir = t.tmp
 
 include $(top_srcdir)/check.am
+
+clean-local: check-clean

+ 141 - 0
lib/dpkg/test/t-tar.t

@@ -0,0 +1,141 @@
+#!/usr/bin/perl
+#
+# Copyright © 2014 Guillem Jover <guillem@debian.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+use Test::More tests => 12;
+use Cwd;
+use File::Path qw(make_path remove_tree);
+use File::Temp qw(tempdir);
+use File::Spec;
+use File::Find;
+use POSIX qw(mkfifo);
+
+use Dpkg::IPC;
+
+use strict;
+use warnings;
+
+my $srcdir = $ENV{srcdir} || '.';
+my $builddir = $ENV{builddir} || '.';
+my $tmpdir = 't.tmp/t-tar';
+
+sub create {
+    my ($pathname) = @_;
+
+    open my $fh, '>>', $pathname or die "cannot touch $pathname: $!";
+    close $fh;
+}
+
+sub tar_create_tree {
+    my $type = shift;
+
+    my $long_a = 'a' x 29;
+    my $long_b = 'b' x 29;
+    my $long_c = 'c' x 29;
+    my $long_d = 'd' x 29;
+    my $long_e = 'e' x 29;
+    my $long_f = 'f' x 22;
+
+    # Popullate tar hierarchy
+    create('file');
+    link 'file', 'hardlink';
+
+    make_path("$long_a/$long_b/$long_c/$long_d/$long_e/");
+    make_path("$long_a/$long_b/$long_c/$long_d/$long_e/$long_f/");
+    create("$long_a/$long_b/$long_c/$long_d/$long_e/$long_f/long");
+    symlink "$long_a/$long_b/$long_c/$long_d/$long_e/$long_f/long",
+            'symlink-long';
+
+    symlink 'file', 'symlink-a';
+    symlink 'hardlink', 'symlink-b';
+    symlink 'dangling', 'symlink-c';
+
+    mkdir 'directory';
+    mkfifo('fifo', 0770);
+
+    # FIXME: Need root.
+    # system 'mknod', 'chardev', 'c', '1', '3';
+    # system 'mknod', 'blockdev', 'b', '0', '0';
+}
+
+sub test_tar_extractor {
+    my $stdout;
+    my $stderr;
+
+    my $expected_tar = <<TAR;
+. mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./fifo mode=10750 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=fifo
+./file mode=100644 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=file size=0
+./hardlink mode=100644 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=hardlink linkto=./file size=0
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd/eeeeeeeeeeeeeeeeeeeeeeeeeeeee mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd/eeeeeeeeeeeeeeeeeeeeeeeeeeeee/ffffffffffffffffffffff mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd/eeeeeeeeeeeeeeeeeeeeeeeeeeeee/ffffffffffffffffffffff/long mode=100644 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=file size=0
+./directory mode=40755 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=dir
+./symlink-a mode=120777 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=symlink linkto=file size=0
+./symlink-b mode=120777 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=symlink linkto=hardlink size=0
+./symlink-c mode=120777 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=symlink linkto=dangling size=0
+./symlink-long mode=120777 time=100000000.000000000 uid=100 gid=200 uname=user gname=group type=symlink linkto=aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ccccccccccccccccccccccccccccc/ddddddddddddddddddddddddddddd/eeeeeeeeeeeeeeeeeeeeeeeeeeeee/ffffffffffffffffffffff/long size=0
+TAR
+
+    make_path($tmpdir);
+
+    my $cwd = cwd();
+
+    # Check generated tarballs.
+    foreach my $type (qw(v7 ustar oldgnu gnu)) {
+        my $dirtree = "$tmpdir/$type";
+        my @paths;
+
+        mkdir $dirtree;
+        chdir $dirtree;
+        tar_create_tree($type);
+        find({ no_chdir => 1, wanted => sub {
+                   return if $type eq 'v7' and length > 99;
+                   return if $type eq 'v7' and -l and length readlink > 99;
+                   return if $type eq 'v7' and not (-f or -l or -d);
+                   return if $type eq 'ustar' and length > 256;
+                   return if $type eq 'ustar' and -l and length readlink > 100;
+                   push @paths, $_;
+               },
+               preprocess => sub { my (@files) = sort @_; @files } }, '.');
+        chdir $cwd;
+
+        my $paths_list = join "\0", @paths;
+        spawn(exec => [ 'tar', '-cf', "$dirtree.tar", '--format', $type,
+                        '-C', $dirtree, "--mtime=\@100000000",
+                        "--owner=user:100", "--group=group:200",
+                        '--null', '--no-unquote', '--no-recursion', '-T-' ],
+              wait_child => 1, from_string => \$paths_list);
+
+        my $expected = $expected_tar;
+        $expected =~ s/[ug]name=[^ ]+ //g if $type eq 'v7';
+        $expected =~ s/\n^.*fifo.*$//mg if $type eq 'v7';
+        $expected =~ s/\n^.*dddd.*$//mg if $type eq 'v7';
+        $expected =~ s/\n^.*symlink-long.*$//mg if $type eq 'ustar';
+
+        spawn(exec => [ './t-tarextract', "$dirtree.tar" ],
+              nocheck => 1, to_string => \$stdout, to_error => \$stderr);
+        ok($? == 0, "tar extractor $type should succeed");
+        is($stderr, undef, "tar extractor $type stderr is empty");
+        is($stdout, $expected, "tar extractor $type is ok");
+    }
+}
+
+test_tar_extractor();

+ 125 - 0
lib/dpkg/test/t-tarextract.c

@@ -0,0 +1,125 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-tarextract.c - test tar extractor
+ *
+ * Copyright © 2014 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <dpkg/ehandle.h>
+#include <dpkg/fdio.h>
+#include <dpkg/tarfn.h>
+
+struct tar_context {
+	int tar_fd;
+};
+
+static int
+tar_read(void *ctx, char *buffer, int size)
+{
+	struct tar_context *tc = ctx;
+
+	return fd_read(tc->tar_fd, buffer, size);
+}
+
+static int
+tar_object(void *ctx, struct tar_entry *te)
+{
+	printf("%s mode=%o time=%ld.%.9d uid=%d gid=%d", te->name,
+	       te->stat.mode, te->mtime, 0, te->stat.uid, te->stat.gid);
+	if (te->stat.uname)
+		printf(" uname=%s", te->stat.uname);
+	if (te->stat.gname)
+		printf(" gname=%s", te->stat.gname);
+
+	switch (te->type) {
+	case TAR_FILETYPE_FILE0:
+	case TAR_FILETYPE_FILE:
+		printf(" type=file size=%jd", te->size);
+		break;
+	case TAR_FILETYPE_HARDLINK:
+		printf(" type=hardlink linkto=%s size=%jd",
+		       te->linkname, te->size);
+		break;
+	case TAR_FILETYPE_SYMLINK:
+		printf(" type=symlink linkto=%s size=%jd",
+		       te->linkname, te->size);
+		break;
+	case TAR_FILETYPE_DIR:
+		printf(" type=dir");
+		break;
+	case TAR_FILETYPE_CHARDEV:
+	case TAR_FILETYPE_BLOCKDEV:
+		printf(" type=device id=%d.%d", major(te->dev), minor(te->dev));
+		break;
+	case TAR_FILETYPE_FIFO:
+		printf(" type=fifo");
+		break;
+	default:
+		ohshit("unexpected tar entry type '%c'", te->type);
+	}
+
+	printf("\n");
+
+	return 0;
+}
+
+struct tar_operations tar_ops = {
+	.read = tar_read,
+	.extract_file = tar_object,
+	.link = tar_object,
+	.symlink = tar_object,
+	.mkdir = tar_object,
+	.mknod = tar_object,
+};
+
+int
+main(int argc, char **argv)
+{
+	struct tar_context ctx;
+	const char *tar_name = argv[1];
+
+	setvbuf(stdout, NULL, _IOLBF, 0);
+
+	push_error_context();
+
+	if (tar_name) {
+		ctx.tar_fd = open(tar_name, O_RDONLY);
+		if (ctx.tar_fd < 0)
+			ohshite("cannot open file '%s'", tar_name);
+	} else {
+		ctx.tar_fd = STDIN_FILENO;
+	}
+
+	if (tar_extractor(&ctx, &tar_ops))
+		ohshite("extracting tar");
+
+	if (tar_name)
+		close(ctx.tar_fd);
+
+	pop_error_context(ehflag_normaltidy);
+
+	return 0;
+}