#!/usr/bin/perl -w # # Copyright (c) 2002 Steve Slaven, All Rights Reserved. # # 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, # MA 02111-1307 USA # use strict; use vars qw( @cds @cdsizes $cdcount $cdcount $VERSION $relocspec $gpath @mkiso_opts ); use Getopt::Std; $VERSION = 0.75; my %o; getopts( 'hJ', \%o ); @mkiso_opts = ( "-quiet", "-D", "-R" # Preserve everything, including uid/gid/etc ); push( @mkiso_opts, "-J" ) unless $o{J}; my $image = shift( @ARGV ); my @paths = @ARGV; my $cdmax = 1024 * 1024 * 620; # Max size in bytes $o{ h } = 1 unless @paths; die( qq{ Makes multiple uncompressed CD's to backup files Author: Steve Slaven - http://hoopajoo.net Usage: $0 [-hJL] BASENAME PATH [PATH...] -h This help -J Exclude joliet filesystem build, sometimes the joliet names are not long enough to allow all files to be put on the ISO, causing the backup to fail } ) if $o{ h }; # Generate CD/File lists print qq| This uses the -D option for mkisofs, because of deep directory relocation. This may produce images that are unreadable on some systems as it breaks the ISO standard. Generating CD include lists.... |; @cds = (); # This will be arrayrefs, each arrayref will contain pathspecs for mkisofs $cdcount = 0; $cds[ $cdcount ] = []; $cdsizes[ $cdcount ] = 0; # Do a recursive search for all the files in the paths for ( @paths ) { # Pathspec is local=cdpath, if no = then assume cdpath is / if ( /=/ ) { ( $gpath, $relocspec ) = $_ =~ /^(.*)=(.*)/; } else { $gpath = $_; $relocspec = $gpath; } $relocspec =~ s!/$!!g; # Verify the path exists, then pass on to handler if ( ! -e $gpath ) { print "path $gpath does not exist, skipping\n"; } else { handle_path( $gpath ); } } print "\n"; print "Creating image(s)\n"; $cdcount = 0; my( $isoname, $volid, $cdnumber, $realpath ); my $pathlist = "PATHSPEC.LIST.$$"; my $filelist = "FILES.LIST.$$"; my $time = localtime(); my $cdtotal = scalar( @cds ); my $hostname = `uname -a`; my $cdpath; my $localpath; chomp( $hostname ); for ( @cds ) { $cdnumber = $cdcount + 1; print "CD number $cdnumber size is " . $cdsizes[ $cdcount ] . "\n"; print "Generating pathspec files...\n"; open( OUT, ">$pathlist" ) || die( "Could not open pathspec file" ); open( OUT2, ">$filelist" ) || die( "Could not open file list" ); for ( @{ $cds[ $cdcount ] } ) { print OUT2 $_ -> { localpath } . "\n"; $cdpath = $_ -> { cdpath }; $localpath = $_ -> { localpath }; $cdpath =~ s/\\/\\\\/g; $localpath =~ s/\\/\\\\/g; $cdpath =~ s/=/\\=/g; $localpath =~ s/=/\\=/g; $cdpath =~ s#//#/#; $localpath =~ s#//#/#g; print OUT sprintf( "%s=%s", $cdpath, $localpath ); print OUT "\n"; } close( OUT ); close( OUT2 ); $isoname = sprintf( "%s-%03d.iso", $image, $cdcount ); $volid = sprintf( "B%3d", $cdcount ); print "Beginning ISO generation...\n"; system( "mkisofs", @mkiso_opts, "-P", "mkcdbackup v$VERSION by Steve Slaven - http://hoopajoo.net", "-p", "mkcdbackup v$VERSION by Steve Slaven - http://hoopajoo.net", "-V", $volid, "-A", "Backup for $hostname at $time, disc $cdnumber of $cdtotal ", "-o", $isoname, '-graft-points', '-path-list', $pathlist, "PATHSPEC.TXT=$pathlist", "FILES.TXT=$filelist" ); $cdcount++; } unlink( $pathlist ); unlink( $filelist ); print "$cdtotal CD's created\n"; # Follows the path and puts all files in an array sub handle_path { my $path = shift; my( $size, $localpath ); $path =~ s|/$||g; # Handles 1 patchspec entry, if a file it processes it, if a dir it calls recurse if ( -f $path ) { $size = -s $path; if ( $cdsizes[ $cdcount ] + $size > $cdmax ) { # Spill into new cd print "Spilling into new cd, size topped out at " . $cdsizes[ $cdcount ] . " with " . scalar( @{ $cds[ $cdcount ] } ) . " entries"; $cdcount ++; $cdsizes[ $cdcount ] = 0; $cds[ $cdcount ] = []; } # Add file now $localpath = $path; $localpath =~ s/^$gpath//; push( @{ $cds[ $cdcount ] }, { cdpath => "$relocspec/$localpath", localpath => $path } ); $cdsizes[ $cdcount ] += $size; } else { # Directory recurse_path( $path ); } } # Actual recursion sub recurse_path { my $path = shift; my( $ps, $full ); local *DIR; eval { # To make the output not be line-buffered local $|; $| = 1; print "."; }; opendir( DIR, $path ); while ( $ps = readdir( DIR ) ) { $full = "$path/$ps"; if ( ( $ps ne "." ) && ( $ps ne ".." ) && # No symlinks ( ! -l $full ) ) { handle_path( $full ); } } closedir( DIR ); }