Brackup

 view release on metacpan or  search on metacpan

lib/Brackup/Target/Filesystem.pm  view on Meta::CPAN

    return $self;
}

sub new_from_backup_header {
    my ($class, $header) = @_;
    my $self = bless {}, $class;
    $self->{path} = $header->{"BackupPath"} or
        die "No BackupPath specified in the backup metafile.\n";
    $self->{nocolons} = $header->{"NoColons"} or 0;
    unless (-d $self->{path}) {
        die "Restore path $self->{path} doesn't exist.\n";
    }
    return $self;
}

sub nocolons {
    my ($self) = @_;
    return $self->{nocolons};
}

sub backup_header {
    my $self = shift;
    return {
        "BackupPath" => $self->{path},
        "NoColons" => $self->{nocolons}?"1":"0",
    };
}

# 1.05 and before stored files on disk as: xxxx/xxxx/xxxxxxxxxx.brackup
# (that is, two levels of directories, each 4 hex digits long, or 65536
# files per directory, which is 2x what ext3 can store, leading to errors.
# in 1.06 and above, xx/xx/xxxxxx is used.  that is, two levels of 2 hex
# digits.  this function
sub _upgrade_layout {
    my $self = shift;
    my $clean_limit = shift; # optional; if set, max top-level dirs to clean

    my $root = $self->{path};

    opendir(my $dh, $root) or die "Error opening $root: $!";

    # read the current state of things in the root directory
    # (which is presumably maxed out on files, at 32k or whatnot)
    my %exist_twodir;    # two_dir -> 1 (which two-letter directories exist)
    my %exist_fourdir;   # four_dir -> 1 (which four-letter directories exist)
    my %four_of_two;     # two_dir -> [ four_dir, four_dir, ... ]
    while (my $dir = readdir($dh)) {
        next unless -d "$root/$dir";
        if ($dir =~ /^[0-9a-f]{2}$/) {
            $exist_twodir{$dir} = 1;
            next;
        }
        if ($dir =~ /^([0-9a-f]{2})([0-9a-f]{2})$/) {
            $exist_fourdir{"$1$2"} = 1;
            push @{ $four_of_two{$1} ||= [] }, "$1$2";
        }
    }

    # for each 4-digit directory, sorted by number of four-digit directories
    # that exist for their leading 2-digit prefix (to most quickly free up
    # a link in root, in 2 iterations),
    # see if the "01/" directory exists (the leading two bytes).
    # if not,
    #    move it to some random other 'xxxx' directory,
    #    as, say, "abcd/tmp-was-root-0123".
    # now, for either the "0123" directory or "tmp-was-root-0123"
    # directory, file all chunks, and move them to the
    # right locations "01/23/*.chunk", making "01/23" if needed.
    # (shouldn't be any out-of-link problems down one level)
    my @four_dirs = map {
        sort @{ $four_of_two{$_} }
    }
    sort {
        scalar(@{ $four_of_two{$b} }) <=> scalar(@{ $four_of_two{$a} })
    } keys %four_of_two;
    my $n_done;
    while (my $four_dir = shift @four_dirs) {
        my $leading_two = substr($four_dir, 0, 2);
        my $migrate_source;
        if ($exist_twodir{$leading_two}) {
            # top-level destination already exists.  no need for more
            # links in the top-level
            $migrate_source = $four_dir;
        } elsif (@four_dirs) {
            # we need to move four_dir away, into another four_dir,
            # to make room to create a new two_dir in the root
            my $holder_four_dir = $four_dirs[0];
            $migrate_source = "$holder_four_dir/tmp-was-root-$four_dir";
            my $temp_dir = "$root/$migrate_source";
            rename "$root/$four_dir", $temp_dir
                or die "Rename of $root/$four_dir -> $temp_dir failed: $!";
        } else {
            # no four_dirs left?  then I bet we aren't out of links
            # anymore.  just migrate.
            $migrate_source = $four_dir;
        }

        $self->_upgrade_chunks_in_directory($four_dir, $migrate_source);
        if (-e "$root/$four_dir") {
            die "Upgrade of $root/$four_dir/* didn't seem to have worked.";
        }
        $n_done++;
        last if $clean_limit && $n_done >= $clean_limit;
    }
}

sub _upgrade_chunks_in_directory {
    my $self = shift;
    my $four_dig = shift;  # first four hex digits of all files being moved
    my $rel_dir = shift;   # directory (relative to root) to move files from, and then remove
    die "not relative" unless $rel_dir =~ m!^[^/]!;

    my $root = $self->{path};

    my ($hex12, $hex34) = $four_dig =~ /^([0-9a-f]{2})([0-9a-f]{2})$/
        or die "four_dig not four hex digits";

    my $dest_dir0 = "$root/$hex12";
    my $dest_dir  = "$root/$hex12/$hex34";
    for ($dest_dir0, $dest_dir) {
        next if -d $_;



( run in 0.299 second using v1.01-cache-2.11-cpan-5511b514fd6 )