+ # We skip this entry.
+ # We allready have a newer one and this
+ # isn't a continuation of the same file
+ next if ($complete_path eq $prev_complete_path
+ and $jobid != $prev_jobid);
+
+
+ if ($prev_is_file
+ and $complete_path =~ m|^\Q$prev_complete_path\E/|)
+ {
+ # We would be recursing inside a file.
+ # Just what we don't want (dir replaced by file
+ # between two backups
+ next;
+ }
+ elsif ($is_dir)
+ {
+ # It is a directory
+ push @restore_list,($refrow);
+
+ $prev_complete_path = $complete_path;
+ $prev_jobid = $jobid;
+ $prev_is_file = 0;
+ }
+ else
+ {
+ # It is a file
+ push @restore_list,($refrow);
+
+ $prev_complete_path = $complete_path;
+ $prev_jobid = $jobid;
+ $prev_is_file = 1;
+ }
+ }
+ # We get rid of @temp_list... save memory
+ @temp_list=();
+
+ # Ok everything is in the list. Let's sort it again in another way.
+ # This time it will be in the bsr file order
+
+ # we sort the results by
+ # volsessiontime, volsessionid, volindex, fileindex
+ # to get all files in right order...
+ # Reminder : The array rows look like this :
+ # complete_path,is_dir,fileindex,
+ # ref->(jobid,VolsessionId,VolsessionTime,File,FirstIndex,LastIndex,
+ # StartBlock-EndBlock,VolIndex,Volumename,MediaType)
+
+ @restore_list= sort { $a->[3]->[2] <=> $b->[3]->[2]
+ || $a->[3]->[1] <=> $b->[3]->[1]
+ || $a->[3]->[7] <=> $b->[3]->[7]
+ || $a->[2] <=> $b->[2] }
+ @restore_list;
+
+ # Now that everything is ready, we create the bsr
+ my $prev_fileindex=-1;
+ my $prev_volsessionid=-1;
+ my $prev_volsessiontime=-1;
+ my $prev_volumename=-1;
+ my $prev_volfile=-1;
+ my $prev_mediatype;
+ my $prev_volblocks;
+ my $count=0;
+ my $first_of_current_range=0;
+ my @fileindex_ranges;
+ my $bsr='';
+
+ foreach my $refrow (@restore_list)
+ {
+ my (undef,undef,$fileindex,$refother)=@{$refrow};
+ my (undef,$volsessionid,$volsessiontime,$volfile,undef,undef,
+ $volblocks,undef,$volumename,$mediatype)=@{$refother};
+
+ # We can specifiy the number of files in each section of the
+ # bsr to speedup restore (bacula can then jump over the
+ # end of tape files.
+ $count++;
+
+
+ if ($prev_volumename eq '-1')
+ {
+ # We only have to start the new range...
+ $first_of_current_range=$fileindex;
+ }
+ elsif ($prev_volsessionid != $volsessionid
+ or $prev_volsessiontime != $volsessiontime
+ or $prev_volumename ne $volumename
+ or $prev_volfile ne $volfile)
+ {
+ # We have to create a new section in the bsr...
+ #\81Â\81 We print the previous one ...
+ # (before that, save the current range ...)
+ if ($first_of_current_range != $prev_fileindex)
+ {
+ #\81Â\81 we are in a range
+ push @fileindex_ranges,
+ ("$first_of_current_range-$prev_fileindex");
+ }
+ else
+ {
+ # We are out of a range,
+ # but there is only one element in the range
+ push @fileindex_ranges,
+ ("$first_of_current_range");
+ }
+
+ $bsr.=print_bsr_section(\@fileindex_ranges,
+ $prev_volsessionid,
+ $prev_volsessiontime,
+ $prev_volumename,
+ $prev_volfile,
+ $prev_mediatype,
+ $prev_volblocks,
+ $count-1);
+ $count=1;
+ # Reset for next loop
+ @fileindex_ranges=();
+ $first_of_current_range=$fileindex;
+ }
+ elsif ($fileindex-1 != $prev_fileindex)
+ {
+ # End of a range of fileindexes
+ if ($first_of_current_range != $prev_fileindex)
+ {
+ #we are in a range
+ push @fileindex_ranges,
+ ("$first_of_current_range-$prev_fileindex");
+ }
+ else
+ {
+ # We are out of a range,
+ # but there is only one element in the range
+ push @fileindex_ranges,
+ ("$first_of_current_range");
+ }
+ $first_of_current_range=$fileindex;
+ }
+ $prev_fileindex=$fileindex;
+ $prev_volsessionid = $volsessionid;
+ $prev_volsessiontime = $volsessiontime;
+ $prev_volumename = $volumename;
+ $prev_volfile=$volfile;
+ $prev_mediatype=$mediatype;
+ $prev_volblocks=$volblocks;
+
+ }
+
+ # Ok, we're out of the loop. Alas, there's still the last record ...
+ if ($first_of_current_range != $prev_fileindex)
+ {
+ # we are in a range
+ push @fileindex_ranges,("$first_of_current_range-$prev_fileindex");
+
+ }
+ else
+ {
+ # We are out of a range,
+ # but there is only one element in the range
+ push @fileindex_ranges,("$first_of_current_range");
+
+ }
+ $bsr.=print_bsr_section(\@fileindex_ranges,
+ $prev_volsessionid,
+ $prev_volsessiontime,
+ $prev_volumename,
+ $prev_volfile,
+ $prev_mediatype,
+ $prev_volblocks,
+ $count);
+
+ return $bsr;
+}
+
+sub print_bsr_section
+{
+ my ($ref_fileindex_ranges,$volsessionid,
+ $volsessiontime,$volumename,$volfile,
+ $mediatype,$volblocks,$count)=@_;
+
+ my $bsr='';
+ $bsr .= "Volume=\"$volumename\"\n";
+ $bsr .= "MediaType=\"$mediatype\"\n";
+ $bsr .= "VolSessionId=$volsessionid\n";
+ $bsr .= "VolSessionTime=$volsessiontime\n";
+ $bsr .= "VolFile=$volfile\n";
+ $bsr .= "VolBlock=$volblocks\n";
+
+ foreach my $range (@{$ref_fileindex_ranges})
+ {
+ $bsr .= "FileIndex=$range\n";
+ }
+
+ $bsr .= "Count=$count\n";
+ return $bsr;
+}
+
+1;
+
+################################################################
+
+package Bvfs;
+use base qw/Bbase/;
+
+sub get_pathid
+{
+ my ($self, $dir) = @_;
+ my $query =
+ "SELECT PathId FROM Path WHERE Path = ?";
+ my $sth = $self->dbh_prepare($query);
+ $sth->execute($dir);
+ my $result = $sth->fetchall_arrayref();
+ $sth->finish();
+
+ return join(',', map { $_->[0] } @$result);
+}
+
+
+sub update_cache
+{
+ my ($self) = @_;
+
+ $self->{conf}->{dbh}->begin_work();
+
+ my $query = "
+ SELECT JobId from Job
+ WHERE JobId NOT IN (SELECT JobId FROM brestore_knownjobid) ORDER BY JobId";
+ my $jobs = $self->dbh_selectall_arrayref($query);
+
+ $self->update_brestore_table(map { $_->[0] } @$jobs);
+
+ print STDERR "Cleaning path visibility\n";
+
+ my $nb = $self->dbh_do("
+ DELETE FROM brestore_pathvisibility
+ WHERE NOT EXISTS
+ (SELECT 1 FROM Job WHERE JobId=brestore_pathvisibility.JobId)");
+
+ print STDERR "$nb rows affected\n";
+ print STDERR "Cleaning known jobid\n";
+
+ $nb = $self->dbh_do("
+ DELETE FROM brestore_knownjobid
+ WHERE NOT EXISTS
+ (SELECT 1 FROM Job WHERE JobId=brestore_knownjobid.JobId)");
+
+ print STDERR "$nb rows affected\n";
+
+ $self->{conf}->{dbh}->commit();
+}
+
+sub get_root
+{
+ my ($self, $dir) = @_;
+ return $self->get_pathid('');
+}
+
+sub ch_dir
+{
+ my ($self, $pathid) = @_;
+ $self->{cwdid} = $pathid;
+}
+
+sub up_dir
+{
+ my ($self) = @_ ;
+ my $query = "
+ SELECT PPathId
+ FROM brestore_pathhierarchy
+ WHERE PathId IN ($self->{cwdid}) ";
+
+ my $all = $self->dbh_selectall_arrayref($query);
+ return unless ($all); # already at root
+
+ my $dir = join(',', map { $_->[0] } @$all);
+ if ($dir) {
+ $self->ch_dir($dir);
+ }
+}
+
+sub pwd
+{
+ my ($self) = @_;
+ return $self->get_path($self->{cwdid});
+}
+
+sub get_path
+{
+ my ($self, $pathid) = @_;
+ $self->debug("Call with pathid = $pathid");
+ my $query =
+ "SELECT Path FROM Path WHERE PathId IN (?)";
+
+ my $sth = $self->dbh_prepare($query);
+ $sth->execute($pathid);
+ my $result = $sth->fetchrow_arrayref();
+ $sth->finish();
+ return $result->[0];
+}
+
+sub set_curjobids
+{
+ my ($self, @jobids) = @_;
+ $self->{curjobids} = join(',', @jobids);
+ $self->update_brestore_table(@jobids);
+}
+
+sub ls_files
+{
+ my ($self) = @_;
+
+ return undef unless ($self->{curjobids});
+
+ my $inclause = $self->{curjobids};
+ my $inlistpath = $self->{cwdid};
+
+ my $query =
+"SELECT File.FilenameId, listfiles.id, listfiles.Name, File.LStat, File.JobId
+ FROM
+ (SELECT Filename.Name, max(File.FileId) as id
+ FROM File, Filename
+ WHERE File.FilenameId = Filename.FilenameId
+ AND Filename.Name != ''
+ AND File.PathId IN ($inlistpath)
+ AND File.JobId IN ($inclause)
+ GROUP BY Filename.Name
+ ORDER BY Filename.Name) AS listfiles,
+File
+WHERE File.FileId = listfiles.id";
+
+ $self->debug($query);
+ my $result = $self->dbh_selectall_arrayref($query);
+ $self->debug($result);
+
+ return $result;