#!/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; }