Subject: | Protect against corruption when fork and write compressed files |
The following perl script creates a gzipped file which causes the following error message when decompressed using zcat:
gzip: /tmp/bla.gz: decompression OK, trailing garbage ignored
The script:
#!/usr/bin/perl
use strict;
use IO::Compress::Gzip;
my $ofh = IO::Compress::Gzip->new("/tmp/bla.gz") or die;
print $ofh "before fork\n";
if (fork == 0) {
exit;
} else {
# sleep 1;
print $ofh "in the parent\n";
}
__END__
Dependending on timing (i.e. if the "sleep 1" in the above example is activated), the file contents could even be incomplete.
This problem occurs in all perl classes which do some cleanup & flush stuff in their DESTROY methods. Traditionally people were told to use POSIX::_exit in the child, but this prevents running all DESTROY methods, not the "dangerous" ones. One could use Acme::Damn to "unbless" the problematic objects (this works here, too).
Different modules have aditional approaches to protect from this problem. DBI for example has the InactiveDestroy and AutoInactiveDestroy attributes available, so a user can control which of the forked processes should "take over control", or automatically give control to the parent only. Perl/Tk remembers the $$ when creating the MainWindow, and does cleanup stuff only in the parent process (see "parent_pid" in Event/Event.xs in the Perl/Tk distribution).
Something like the following would work for IO::Compress::Gzip:
package MyGzip;
use base 'IO::Compress::Gzip';
my %fh2pid;
sub new {
my($class, @args) = @_;
my $self = $class->SUPER::new(@args);
$fh2pid{$self} = $$; # $self is not hash-based, so store the pid elsewhere
$self;
}
sub DESTROY {
my $self = shift;
my $fhpid = delete $fh2pid{$self};
if ($fhpid == $$) {
$self->SUPER::DESTROY(@_);
} else {
warn "DESTROY not allowed in child process ($$ != $fhpid)"; # probably be silent here
}
}
But it would be nice if something like this (maybe only activated with some options, e.g. the same ones like DBI uses) would be part of IO-Compress.
Regards,
Slaven