Brackup

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

1.10 (2010-10-31)

  - permit 0 as a filename.  https://rt.cpan.org/Ticket/Display.html?id=62004

  - add Riak target, allowing backups to a riak cluster (Gavin Carr)

  - add uid/gid info to metafile, and use in restores (where possible)
    (Gavin Carr)

  - allow multiple gpg recipients to be specified, any can restore (Alex 
    Vandiver)

  - if IO::Compress::Gzip is available, write a compressed brackup metafile in
    unencrypted mode, and handle properly for reads and restores (Gavin Carr)

  - remove orphaned chunks from inventory as part of garbage collection 
    (Gavin Carr)

lib/Brackup/Backup.pm  view on Meta::CPAN


    $self->{root}    = delete $opts{root};     # Brackup::Root
    $self->{target}  = delete $opts{target};   # Brackup::Target
    $self->{dryrun}  = delete $opts{dryrun};   # bool
    $self->{verbose} = delete $opts{verbose};  # bool
    $self->{inventory} = delete $opts{inventory};  # bool
    $self->{savefiles} = delete $opts{savefiles};  # bool
    $self->{zenityprogress} = delete $opts{zenityprogress};  # bool

    $self->{modecounts} = {}; # type -> mode(octal) -> count
    $self->{idcounts}   = {}; # type -> uid/gid -> count

    $self->{_uid_map} = {};   # uid -> username
    $self->{_gid_map} = {};   # gid -> group

    $self->{saved_files} = [];   # list of Brackup::File objects backed up
    $self->{unflushed_files} = [];   # list of Brackup::File objects not in backup_file

    croak("Unknown options: " . join(', ', keys %opts)) if %opts;

    return $self;
}

# returns true (a Brackup::BackupStats object) on success, or dies with error

lib/Brackup/Backup.pm  view on Meta::CPAN

    my ($self, $type) = @_;
    my $map = $self->{modecounts}{$type} || {};
    return (sort { $map->{$b} <=> $map->{$a} } keys %$map)[0];
}

sub default_uid {
    my $self = shift;
    return $self->{_def_uid} ||= $self->_default_id('u');
}

sub default_gid {
    my $self = shift;
    return $self->{_def_gid} ||= $self->_default_id('g');
}

sub _default_id {
    my ($self, $type) = @_;
    my $map = $self->{idcounts}{$type} || {};
    return (sort { $map->{$b} <=> $map->{$a} } keys %$map)[0];
}

# space-separated list of local uid:username mappings
sub uid_map {

lib/Brackup/Backup.pm  view on Meta::CPAN

    my @map;
    my $uidcounts = $self->{idcounts}{u};
    for my $uid (sort { $a <=> $b } keys %$uidcounts) {
      if (my $name = getpwuid($uid)) {
        push @map, "$uid:$name";
      }
    }
    return join(' ', @map);
}

# space-separated list of local gid:group mappings
sub gid_map {
    my $self = shift;
    my @map;
    my $gidcounts = $self->{idcounts}{g};
    for my $gid (sort { $a <=> $b } keys %$gidcounts) {
      if (my $name = getgrgid($gid)) {
        push @map, "$gid:$name";
      }
    }
    return join(' ', @map);
}

sub backup_time {
    my $self = shift;
    return $self->{backup_time} ||= time();
}

lib/Brackup/Backup.pm  view on Meta::CPAN

            die "Bogus header value from driver" if $val =~ /[\r\n]/;
            $ret .= "Driver-$k: $val\n";
        }
    }
    $ret .= "RootName: " . $self->{root}->name . "\n";
    $ret .= "RootPath: " . $self->{root}->path . "\n";
    $ret .= "TargetName: " . $self->{target}->name . "\n";
    $ret .= "DefaultFileMode: " . $self->default_file_mode . "\n";
    $ret .= "DefaultDirMode: " . $self->default_directory_mode . "\n";
    $ret .= "DefaultUID: " . $self->default_uid . "\n";
    $ret .= "DefaultGID: " . $self->default_gid . "\n";
    $ret .= "UIDMap: " . $self->uid_map . "\n";
    $ret .= "GIDMap: " . $self->gid_map . "\n";
    $ret .= "GPG-Recipient: $_\n" for $self->{root}->gpg_rcpts;
    $ret .= "\n";
    return $ret;
}

sub record_mode_ids {
    my ($self, $file) = @_;
    $self->{modecounts}{$file->type}{$file->mode}++;
    $self->{idcounts}{u}{$file->uid}++;
    $self->{idcounts}{g}{$file->gid}++;
}

sub add_unflushed_file {
    my ($self, $file, $handlelist) = @_;
    push @{ $self->{unflushed_files} }, [ $file, $handlelist ];
}   

sub flush_files {
    my ($self, $fh) = @_;
    while (my $rec = shift @{ $self->{unflushed_files} }) {

lib/Brackup/File.pm  view on Meta::CPAN

sub mode {
    my $self = shift;
    return sprintf('%#o', $self->stat->mode & 0777);
}

sub uid {
    my $self = shift;
    return $self->stat->uid;
}

sub gid {
    my $self = shift;
    return $self->stat->gid;
}

sub as_rfc822 {
    my ($self, $schunk_list, $backup) = @_;
    my $ret = "";
    my $set = sub {
        my ($key, $val) = @_;
        return unless length $val;
        $ret .= "$key: $val\n";
    };

lib/Brackup/File.pm  view on Meta::CPAN

        unless (($type eq "d" && $mode eq $backup->default_directory_mode) ||
                ($type eq "f" && $mode eq $backup->default_file_mode)) {
            $set->("Mode", $mode);
        }
    }

    my $uid = $self->uid;
    unless ($uid eq $backup->default_uid) {
      $set->("UID", $uid);
    }
    my $gid = $self->gid;
    unless ($gid eq $backup->default_gid) {
      $set->("GID", $gid);
    }

    return $ret . "\n";
}

1;

lib/Brackup/Mount.pm  view on Meta::CPAN

            my ($path) = @_;
            my $record = $meta->{$path};
            return -ENOENT unless $record;

            return (
                0,     # device number (?)
                0,     # inode
                $record->{mode},
                0,     # nlink
                $<,    # uid
                0,     # gid
                0,     # rdev
                $record->{size},
                $record->{atime},
                $record->{mtime},
                $record->{mtime}, # ctime
                1024,  # blocksize
                1,     # blocks
            );
        },

lib/Brackup/Restore.pm  view on Meta::CPAN

    my ($class, %opts) = @_;
    my $self = bless {}, $class;

    $self->{to}      = delete $opts{to};      # directory we're restoring to
    $self->{prefix}  = delete $opts{prefix};  # directory/file filename prefix, or "" for all
    $self->{filename}= delete $opts{file};    # filename we're restoring from
    $self->{config}  = delete $opts{config};  # brackup config (if available)
    $self->{verbose} = delete $opts{verbose};

    $self->{_local_uid_map} = {};  # remote/metafile uid -> local uid
    $self->{_local_gid_map} = {};  # remote/metafile gid -> local gid

    $self->{prefix} =~ s/\/$// if $self->{prefix};

    $self->{_stats_to_run} = [];  # stack (push/pop) of subrefs to reset stat info on

    die "Destination directory doesn't exist" unless $self->{to} && -d $self->{to};
    croak("Unknown options: " . join(', ', keys %opts)) if %opts;

    $self->{metafile} = Brackup::DecryptedFile->new(filename => $self->{filename});

lib/Brackup/Restore.pm  view on Meta::CPAN

    if (my $remote_user = $self->{_remote_user_map}->{$remote_uid}) {
        my $local_uid = getpwnam($remote_user);
        return $self->{_local_uid_map}->{$remote_uid} = $local_uid
            if defined $local_uid;
    }

    # if remote username missing locally, fallback to $remote_uid
    return $self->{_local_uid_map}->{$remote_uid} = $remote_uid;
}

sub _lookup_remote_gid {
    my ($self, $remote_gid, $meta) = @_;

    return $self->{_local_gid_map}->{$remote_gid} 
        if defined $self->{_local_gid_map}->{$remote_gid};

    # meta remote group map - remote_gid => remote group
    $self->{_remote_group_map} ||= { map { split /:/, $_, 2 } split /\s+/, $meta->{GIDMap} };

    # try and lookup local gid using remote group
    if (my $remote_group = $self->{_remote_group_map}->{$remote_gid}) {
        my $local_gid = getgrnam($remote_group);
        return $self->{_local_gid_map}->{$remote_gid} = $local_gid
            if defined $local_gid;
    }

    # if remote group missing locally, fallback to $remote_gid
    return $self->{_local_gid_map}->{$remote_gid} = $remote_gid;
}

sub _chown {
    my ($self, $full, $it, $type, $meta) = @_;

    my $uid = $self->_lookup_remote_uid($it->{UID}, $meta) if $it->{UID};
    my $gid = $self->_lookup_remote_gid($it->{GID}, $meta) if $it->{GID};

    if ($type eq 'l') {
        if (! defined $self->{_lchown}) {
            no strict 'subs';
            $self->{_lchown} = eval { require Lchown } && Lchown::LCHOWN_AVAILABLE;
        }
        if ($self->{_lchown}) {
            Lchown::lchown($uid, -1, $full) if defined $uid;
            Lchown::lchown(-1, $gid, $full) if defined $gid;
        }
    } else {
        # ignore errors, but change uid and gid separately to sidestep unprivileged failures
        chown $uid, -1, $full if defined $uid;
        chown -1, $gid, $full if defined $gid;
    }
}

sub _update_statinfo {
    my ($self, $full, $it) = @_;

    push @{ $self->{_stats_to_run} }, sub {
        if (defined $it->{Mode}) {
            chmod(oct $it->{Mode}, $full) or
                die "Failed to change mode of $full: $!";



( run in 2.534 seconds using v1.01-cache-2.11-cpan-97f6503c9c8 )