e-smith-samba/root/etc/e-smith/events/actions/shadow-copy-rotate

131 lines
3.9 KiB
Perl

#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw(strftime);
use File::Path;
use File::Basename;
use esmith::ConfigDB;
use esmith::AccountsDB;
# Routines taken from powershift of rlbackup
sub stagger;
sub powershift;
sub shadowdir;
sub rmshadow;
my $cdb = esmith::ConfigDB->open_ro;
my $adb = esmith::AccountsDB->open_ro();
my $smb = $cdb->get('smb') or die "No smb db entry found\n";
my $shadowdir = $smb->prop('ShadowDir') || '/home/e-smith/files/.shadow';
my $shadowcopy = $smb->prop('ShadowCopy') || 'disabled';
my $offset = ($smb->prop('ShadowCount') || 2) - 2;
$offset = 0 if $offset < 0;
exit unless -d $shadowdir;
exit if $shadowcopy eq 'disabled';
my $filesdir = '/home/e-smith/files';
my $snapfmt = '@GMT-%Y.%m.%d-%H.%M.%S';
# Switch old shadow directories to new format
opendir(SHADOW, $shadowdir);
foreach my $s ( grep { /^\d/ && ! -l "$shadowdir/$_" && -d "$shadowdir/$_" } readdir SHADOW ) {
my @stat = stat("$shadowdir/$s");
rename "$shadowdir/$s", "$shadowdir/".strftime($snapfmt, gmtime($stat[9]));
symlink strftime($snapfmt, gmtime($stat[9])), "$shadowdir/$s";
}
closedir(SHADOW);
# remove old symlinks in ibays
foreach my $ibay ($adb->ibays()) {
my $ibaydir = 'ibays/' . $ibay->key . ( $ibay->prop('PublicAccess') eq 'none' ? '/files' : '' );
opendir(IBAY, "$filesdir/$ibaydir") || next;
unlink "$filesdir/$ibaydir/$_" foreach (grep /^\@GMT-/, readdir(IBAY));
closedir(IBAY);
}
# remove old symlinks in ibays
foreach my $user ($adb->users()) {
my $userdir = 'users/' . $user->key . '/home';
opendir(USER, "$filesdir/$userdir") || next;
unlink "$filesdir/$userdir/$_" foreach (grep /^\@GMT-/, readdir(USER));
closedir(USER);
}
# Create sync point if it doesn't already exist
my $snapdir = strftime($snapfmt, gmtime(time));
if ( -d "$shadowdir/0" ) {
rename "$shadowdir/".readlink("$shadowdir/0"), "$shadowdir/$snapdir";
unlink "$shadowdir/0";
} else {
mkdir "$shadowdir/$snapdir";
}
symlink "$snapdir", "$shadowdir/0";
# Create list of ibays and users to shadow
my ($ibays, $users, $link) = ('','','');
my @ibays = grep { ($_->prop('ShadowCopy') || 'enabled') ne 'disabled' } $adb->ibays();
$ibays = "$filesdir/./ibays/{" . join(',', map { $_->key } @ibays) . "}/" if scalar @ibays > 1;
$ibays = "$filesdir/./ibays/" . $ibays[0]->key . "/" if scalar @ibays == 1;
my @users = grep { ($_->prop('ShadowCopy') || 'enabled') ne 'disabled' } $adb->users();
$users = "$filesdir/./users/{" . join(',', map { $_->key } @users) . "}/home/" if scalar @users > 1;
$users = "$filesdir/./users/" . $users[0]->key . "/home/" if scalar @users == 1;
$link = "--link-dest ../1" if -d "$shadowdir/1";
# Sync directories to shadow directory
if ( $ibays || $users) {
system("rsync -aHmR --partial --delete --delete-excluded --exclude 'aquota.*' $link $ibays $users $shadowdir/0/") == 0
or die "Couldn't sync directories";
}
# Shift directories using geometric roll-off (only if different)
if ( -d "$shadowdir/1" ) {
if (system("diff -qr $shadowdir/0 $shadowdir/1 &> /dev/null") == 0) {
rmshadow("$shadowdir/0");
} else {
powershift(2) if -d shadowdir(-$offset);
for (my $i=2; $i >= -$offset; $i--) {
rename shadowdir($i), shadowdir($i+1)
}
}
} else {
rename "$shadowdir/0", "$shadowdir/1";
}
sub rmshadow {
my $d = shift;
if ( -l "$d" ) {
rmtree dirname($d)."/".readlink($d);
unlink "$d";
} elsif ( -d "$d" ) {
rmtree "$d"
}
}
sub shadowdir {
my $i = shift;
return "$shadowdir/".($i+$offset);
}
sub stagger {
my $i = shift;
return $i + ($i >> 1);
}
sub powershift {
my $i = shift;
if ( -d shadowdir(stagger($i)) ) {
my $n = powershift($i << 1);
$i = $n >> 1;
rename shadowdir(stagger($i)), shadowdir($n) if -d shadowdir(stagger($i));
rmshadow(shadowdir($i));
} else {
rename shadowdir($i), shadowdir(stagger($i)) if -d shadowdir($i);
}
return $i;
}