X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=perl%2FAmanda%2FHolding.pm;h=a0108fc2b017441e4bd62e25132fac8df9a78682;hb=refs%2Ftags%2Fupstream%2F3.3.1;hp=b397cce8e79faaf2c69b7d81b52c4d16c03aeb07;hpb=cd0b924f27312d57bd42f6c4fae2b795139e2d0b;p=debian%2Famanda diff --git a/perl/Amanda/Holding.pm b/perl/Amanda/Holding.pm index b397cce..a0108fc 100644 --- a/perl/Amanda/Holding.pm +++ b/perl/Amanda/Holding.pm @@ -197,29 +197,43 @@ sub _is_datestr { } sub _walk { - my ($file_fn) = @_; + my ($file_fn, $verbose) = @_; # walk disks, directories, and files with nested loops for my $disk (disks()) { my $diskh = IO::Dir->new($disk); - next unless defined $diskh; + if (!defined $diskh) { + print $verbose "could not open holding dir '$disk': $!\n" if $verbose; + next; + } while (defined(my $datestr = $diskh->read())) { - next unless (_is_datestr($datestr)); + next if $datestr eq '.' or $datestr eq '..'; + + my $dirfn = File::Spec->catfile($disk, $datestr); + + if (!_is_datestr($datestr)) { + print $verbose "holding dir '$dirfn' is not a datestamp\n" if $verbose; + next; + } + if (!-d $dirfn) { + print $verbose "holding dir '$dirfn' is not a directory\n" if $verbose; + next; + } - my $dirh = IO::Dir->new(File::Spec->catfile($disk, $datestr)); + my $dirh = IO::Dir->new($dirfn); while (defined(my $dirent = $dirh->read)) { next if $dirent eq '.' or $dirent eq '..'; my $filename = File::Spec->catfile($disk, $datestr, $dirent); - next unless -f $filename; + if (!-f $filename) { + print $verbose "holding file '$filename' is not a file\n" if $verbose; + next; + } my $hdr = get_header($filename); next unless defined($hdr); - # ignore chunks and anything bogus - next if ($hdr->{'type'} != $Amanda::Header::F_DUMPFILE); - $file_fn->($filename, $hdr); } } @@ -246,17 +260,78 @@ sub disks { } sub files { + my $verbose = shift; my @results; my $each_file_fn = sub { my ($filename, $header) = @_; + return if $header->{'type'} != $Amanda::Header::F_DUMPFILE; + push @results, $filename; }; - _walk($each_file_fn); + _walk($each_file_fn, $verbose); return @results; } +sub all_files { + my $verbose = shift; + my @results; + + my $each_file_fn = sub { + my ($filename, $header) = @_; + push @results, { filename => $filename, header => $header }; + }; + _walk($each_file_fn, $verbose); + + return @results; +} + +sub merge_all_files { + my @files = @_; + my %hfiles; + my @result; + + for my $file (@files) { + $hfiles{$file->{'filename'}} = $file->{'header'}; + } + + foreach my $filename (keys %hfiles) { + next if !exists $hfiles{$filename}; + if ($hfiles{$filename}->{'type'} == $Amanda::Header::F_DUMPFILE) { + push @result, {filename => $filename, header => $hfiles{$filename}}; + my $is_tmp = ($filename =~ /\.tmp$/); + my $cont_filename = $filename; + my $cfilename = $hfiles{$cont_filename}->{'cont_filename'}; + my $cf = $cfilename; + $cf .= ".tmp" if $is_tmp; + while (defined $cfilename && $cfilename ne "" && -f $cf) { + delete $hfiles{$cont_filename}; + $cont_filename = $cf; + $cfilename = $hfiles{$cont_filename}->{'cont_filename'}; + $cf = $cfilename; + $cf .= ".tmp" if $is_tmp; + } + delete $hfiles{$cont_filename}; + } elsif ($hfiles{$filename}->{'type'} != $Amanda::Header::F_CONT_DUMPFILE) { + push @result, {filename => $filename, header => $hfiles{$filename}}; + delete $hfiles{$filename} + } else { + # do nothing for F_CONTFILE + } + } + + foreach my $filename (keys %hfiles) { + next if !exists $hfiles{$filename}; + if ($hfiles{$filename}->{'type'} == $Amanda::Header::F_CONT_DUMPFILE) { + push @result, {filename => $filename, header => $hfiles{$filename}}; + } else { + delete $hfiles{$filename} + } + } + return @result; +} + sub file_chunks { my ($filename) = @_; my @results; @@ -279,6 +354,47 @@ sub file_chunks { return @results; } +sub file_tmp_chunks { + my ($filename) = @_; + my @results; + + while (1) { + last unless -f $filename; + my $hdr = get_header($filename); + last unless defined($hdr); + + push @results, $filename; + + if ($hdr->{'cont_filename'}) { + $filename = $hdr->{'cont_filename'} . ".tmp"; + } else { + # no continuation -> we're done + last; + } + } + + return @results; +} + +sub rename_tmp { + my ($filename) = shift; + my ($complete) = shift; + + my @files = file_tmp_chunks($filename); + while (my $tmp_filename = pop @files) { + my $hdr = get_header($tmp_filename); + if ($hdr->{'is_partial'} == 0 and $complete == 0) { + $hdr->{'is_partial'} = 1; + write_header($tmp_filename, $hdr); + } + my $hfilename = $tmp_filename; + $hfilename =~ s/\.tmp$//; + rename $tmp_filename, $hfilename; + } + + return +} + sub get_header { my ($filename) = @_; return unless -f $filename; @@ -288,13 +404,31 @@ sub get_header { my $hdr_bytes = Amanda::Util::full_read($fd, DISK_BLOCK_BYTES); POSIX::close($fd); - if (length($hdr_bytes) < DISK_BLOCK_BYTES) { - return; + if (length($hdr_bytes) == 0) { + my $hdr = Amanda::Header->new(); + $hdr->{'type'} = $Amanda::Header::F_EMPTY; + return $hdr; + } elsif (length($hdr_bytes) < DISK_BLOCK_BYTES) { + my $hdr = Amanda::Header->new(); + $hdr->{'type'} = $Amanda::Header::F_UNKNOWN; + return $hdr; } return Amanda::Header->from_string($hdr_bytes); } +sub write_header { + my $filename = shift; + my $hdr = shift; + + return unless -f $filename; + my $fd = POSIX::open($filename, O_RDWR); + return unless $fd; + my $header = $hdr->to_string(DISK_BLOCK_BYTES, DISK_BLOCK_BYTES); + Amanda::Util::full_write($fd, $header, DISK_BLOCK_BYTES); + POSIX::close($fd); +} + sub file_unlink { my ($filename) = @_; @@ -305,6 +439,33 @@ sub file_unlink { return 1; } +sub filetmp_unlink { + my ($filename) = @_; + + for my $chunk (filetmp_chunks($filename)) { + unlink($chunk) or return 0; + } + + return 1; +} + +sub dir_unlink { + # walk disks, directories, and files with nested loops + for my $disk (disks()) { + my $diskh = IO::Dir->new($disk); + next unless defined $diskh; + + while (defined(my $datestr = $diskh->read())) { + next if $datestr eq '.' or $datestr eq '..'; + + my $dirfn = File::Spec->catfile($disk, $datestr); + next unless _is_datestr($datestr); + next unless -d $dirfn; + rmdir $dirfn; + } + } +} + sub file_size { my ($filename, $ignore_headers) = @_; my $total = Math::BigInt->new(0); @@ -327,6 +488,8 @@ sub get_files_for_flush { my $each_file_fn = sub { my ($filename, $header) = @_; + return if $header->{'type'} != $Amanda::Header::F_DUMPFILE; + if (@dateargs && !grep { $_ eq $header->{'datestamp'}; } @dateargs) { return; } @@ -337,7 +500,7 @@ sub get_files_for_flush { push @results, $filename; }; - _walk($each_file_fn); + _walk($each_file_fn, 0); return sort @results; } @@ -347,9 +510,11 @@ sub get_all_datestamps { my $each_file_fn = sub { my ($filename, $header) = @_; + return if $header->{'type'} != $Amanda::Header::F_DUMPFILE; + $datestamps{$header->{'datestamp'}} = 1; }; - _walk($each_file_fn); + _walk($each_file_fn, 0); return sort keys %datestamps; }