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