I already sent a preliminary version of this patch to selected people.
Now that packages use long names when needed, we can pinpoint every file
in the archive based on its archived name.

This patch changes pkg_add to be able to extract archives that contain
elements out-of-order, e.g., match package contents to plist in whatever
order. Once the temporary elements are extracted, the actual "extract"
routine will put everything in place, run scripts, etc.

This is really important. Test/read/report failures to me.

Once this is in, there is another patch that will allow pkg_create to
create out-of-order archives.

The neat effect will be to speed up updates, by relegating unchanged files
to the end of the archive, so they don't need to be fetched at all.

the effect should be drastic on monsters like texlive, where most of 
the files do not change at all from version to version.

Index: OpenBSD/Add.pm
===================================================================
RCS file: /cvs/src/usr.sbin/pkg_add/OpenBSD/Add.pm,v
retrieving revision 1.153
diff -u -p -r1.153 Add.pm
--- OpenBSD/Add.pm      20 May 2014 05:55:43 -0000      1.153
+++ OpenBSD/Add.pm      12 Jun 2014 09:50:44 -0000
@@ -113,7 +113,33 @@ sub perform_extraction
        $state->{partial} = $handle->{partial};
        $state->{archive} = $handle->{location};
        $state->{check_digest} = $handle->{plist}{check_digest};
-       $state->progress->visit_with_size($handle->{plist}, 'extract', $state);
+       my ($wanted, $tied) = {};
+       $handle->{plist}->find_extractible($state, $wanted, $tied);
+       my $p = $state->progress->new_sizer($handle->{plist}, $state);
+       while (my $file = $state->{archive}->next) {
+               my $e = $tied->{$file->name};
+               if (defined $e) {
+                       delete $tied->{$file->name};
+                       $p->advance($e);
+                       $state->{archive}->skip;
+                       # skip to next;
+               }
+               $e = $wanted->{$file->name};
+               if (!defined $e) {
+                       $state->Fatal("archive member not found #1",
+                           $file->name);
+               }
+               delete $wanted->{$file->name};
+               $e->prepare_to_extract($state, $file);
+               $e->extract($state, $file);
+               $p->advance($e);
+               if (keys %$wanted == 0) {
+                       return;
+               }
+       }
+       if (keys %$wanted > 0) {
+               $state->fatal("Truncated archive");
+       }
 }
 
 my $user_tagged = {};
@@ -181,6 +207,10 @@ sub prepare_for_addition
 {
 }
 
+sub find_extractible
+{
+}
+
 sub extract
 {
        my ($self, $state) = @_;
@@ -236,6 +266,16 @@ sub set_modes
        }
 }
 
+package OpenBSD::PackingElement::Meta;
+
+# XXX stuff that's invisible to find_extractible should be considered extracted
+# for the most part, otherwise we create broken partial packages
+sub find_extractible
+{
+       my ($self, $state, $wanted, $tied) = @_;
+       $state->{partial}{$self} = 1;
+}
+
 package OpenBSD::PackingElement::ExtraInfo;
 use OpenBSD::Error;
 
@@ -360,6 +400,18 @@ use File::Basename;
 use File::Path;
 use OpenBSD::Temp;
 
+sub find_extractible
+{
+       my ($self, $state, $wanted, $tied) = @_;
+       $wanted->{$self->name} = $self;
+       return;
+       if ($self->{tied}) {
+               $tied->{$self->name} = $self;
+       } else {
+               $wanted->{$self->name} = $self;
+       }
+}
+
 sub prepare_for_addition
 {
        my ($self, $state, $pkgname) = @_;
@@ -384,19 +436,11 @@ sub prepare_for_addition
 
 sub prepare_to_extract
 {
-       my ($self, $state) = @_;
+       my ($self, $state, $file) = @_;
        my $fullname = $self->fullname;
        my $destdir = $state->{destdir};
 
-       my $file=$state->{archive}->next;
-       if (!defined $file) {
-               $state->fatal("truncated archive");
-       }
        $file->{cwd} = $self->cwd;
-       if (!$file->check_name($self)) {
-               $state->fatal("archive does not match #1 != #2",
-                   $file->name, $self->name);
-       }
        if (defined $self->{symlink} || $file->isSymLink) {
                unless (defined $self->{symlink} && $file->isSymLink) {
                        $state->fatal("bogus symlink #1", $self->name);
@@ -427,14 +471,11 @@ sub prepare_to_extract
        if (defined $self->{symlink} && $state->{do_faked}) {
                $file->{linkname} = $destdir.$file->{linkname};
        }
-       return $file;
 }
 
 sub extract
 {
-       my ($self, $state) = @_;
-
-       my $file = $self->prepare_to_extract($state);
+       my ($self, $state, $file) = @_;
 
        if (defined $self->{link} || defined $self->{symlink}) {
                $state->{archive}->skip;
@@ -483,6 +524,7 @@ sub extract
 
                $state->say("extracting #1", $tempname) if $state->verbose >= 3;
 
+
                if (!$file->isFile) {
                        $state->fatal("can't extract #1, it's not a file", 
                            $self->stringize);
@@ -558,6 +600,10 @@ sub prepare_for_addition
        if ($s->avail < 0) {
                $s->report_overflow($state, $fname);
        }
+}
+
+sub find_extractible
+{
 }
 
 sub extract
Index: OpenBSD/ProgressMeter.pm
===================================================================
RCS file: /cvs/src/usr.sbin/pkg_add/OpenBSD/ProgressMeter.pm,v
retrieving revision 1.40
diff -u -p -r1.40 ProgressMeter.pm
--- OpenBSD/ProgressMeter.pm    23 Dec 2013 15:04:37 -0000      1.40
+++ OpenBSD/ProgressMeter.pm    12 Jun 2014 09:50:44 -0000
@@ -47,6 +47,15 @@ sub errprint
        print STDERR @_;
 }
 
+sub new_sizer
+{
+       return shift;
+}
+
+sub advance
+{
+}
+
 sub for_list
 {
        my ($self, $msg, $l, $code) = @_;
Index: OpenBSD/ProgressMeter/Term.pm
===================================================================
RCS file: /cvs/src/usr.sbin/pkg_add/OpenBSD/ProgressMeter/Term.pm,v
retrieving revision 1.23
diff -u -p -r1.23 Term.pm
--- OpenBSD/ProgressMeter/Term.pm       18 Mar 2014 18:53:29 -0000      1.23
+++ OpenBSD/ProgressMeter/Term.pm       12 Jun 2014 09:50:44 -0000
@@ -20,11 +20,8 @@ use warnings;
 package OpenBSD::PackingElement;
 sub size_and
 {
-       my ($self, $progress, $donesize, $totsize, $method, @r) = @_;
-       if (defined $self->{size}) {
-               $$donesize += $self->{size};
-               $progress->show($$donesize, $totsize);
-       }
+       my ($self, $p, $method, @r) = @_;
+       $p->advance($self);
        $self->$method(@r);
 }
 
@@ -80,18 +77,28 @@ sub compute_count
 sub visit_with_size
 {
        my ($progress, $plist, $method, $state, @r) = @_;
+       my $p = $progress->new_sizer($plist, $state);
+       $plist->size_and($p, $method, $state, @r);
+}
+
+sub new_sizer
+{
+       my ($progress, $plist, $state) = @_;
        $plist->{totsize} //= compute_size($plist);
-       my $donesize = 0;
-       my $totsize = $plist->{totsize};
-       $progress->show($donesize, $totsize);
+       my $p = bless {
+           progress => $progress, 
+           totsize => $plist->{totsize},
+           donesize => 0,
+           state => $state
+           }, "_SizeCounter";
+       $progress->show(0, $p->{totsize});
        if (defined $state->{archive}) {
                $state->{archive}{callback} = sub {
                    my $done = shift;
-                   $progress->show($donesize + $done, $totsize);
+                   $progress->show($p->{donesize} + $done, $p->{totsize});
                };
        }
-       $plist->size_and($progress, \$donesize, $totsize,
-           $method, $state, @r);
+       return $p;
 }
 
 sub visit_with_count
@@ -304,6 +311,20 @@ sub next
 
        $todo //= 'ok';
        print "\r$self->{header}: $todo\n";
+}
+
+package _SizeCounter;
+sub new
+{
+}
+
+sub advance
+{
+       my ($self, $e) = @_;
+       if (defined $e->{size}) {
+               $self->{donesize} += $e->{size};
+               $self->{progress}->show($self->{donesize}, $self->{totsize});
+       }
 }
 
 1;

Reply via email to