#!/usr/bin/perl

# Benchmark EncFS against eCryptfs

use File::Temp;
use warnings;

require("integration/common.pl");

# Create a new empty working directory
sub newWorkingDir {
    my $prefix     = shift;
    my $workingDir = mkdtemp("$prefix/encfs-performance-XXXX")
      || die("Could not create temporary directory");

    return $workingDir;
}

sub cleanup {
    print "cleaning up...";
    my $workingDir = shift;
    system("umount $workingDir/ecryptfs_plaintext");
    system("fusermount -u $workingDir/encfs_plaintext");
    system("rm -Rf $workingDir");
    print "done\n";
}

sub mount_encfs {
    my $workingDir = shift;

    my $c = "$workingDir/encfs_ciphertext";
    my $p = "$workingDir/encfs_plaintext";

    mkdir($c);
    mkdir($p);

    delete $ENV{"ENCFS6_CONFIG"};
    system("./build/encfs --extpass=\"echo test\" --standard $c $p > /dev/null");
    waitForFile("$c/.encfs6.xml") or die("Control file not created");

    print "# encfs mounted on $p\n";

    return $p;
}

sub mount_ecryptfs {

    if(system("which mount.ecryptfs > /dev/null") != 0) {
        print "skipping ecryptfs\n";
        return "";
    }

    my $workingDir = shift;

    my $c = "$workingDir/ecryptfs_ciphertext";
    my $p = "$workingDir/ecryptfs_plaintext";

    mkdir($c);
    mkdir($p);

    system("expect -c \"spawn mount -t ecryptfs $c $p\" ./integration/mount-ecryptfs.expect > /dev/null") == 0
      or die("ecryptfs mount failed - are you root?");

    print "# ecryptfs mounted on $p\n";

    return $p;
}

sub benchmark {
    my $dir = shift;
    our $linuxgz;

    my @results = ();

    system("sync");
    stopwatch_start("stream_write");
        writeZeroes( "$dir/zero", 1024 * 1024 * 100 );
        system("sync");
    stopwatch_stop(\@results);
    unlink("$dir/zero");

    system("sync");
    system("cat $linuxgz > /dev/null");
    stopwatch_start("extract");
        system("tar xzf $linuxgz -C $dir");
        system("sync");
    stopwatch_stop(\@results);

    $du = qx(du -sm $dir | cut -f1);
    push( @results, [ 'du', $du, 'MiB' ] );
    printf( "# disk space used: %d MiB\n", $du );

    system("echo 3 > /proc/sys/vm/drop_caches");
    stopwatch_start("rsync");
        system("rsync -an $dir $dir/empty-rsync-target");
    stopwatch_stop(\@results);

    system("echo 3 > /proc/sys/vm/drop_caches");
    system("sync");
    stopwatch_start("rm");
        system("rm -Rf $dir/*");
        system("sync");
    stopwatch_stop(\@results);

    return \@results;
}

sub tabulate {
    my $r;

    $r = shift;
    my @encfs = @{$r};
    $r = shift;
    my @ecryptfs;
    if($r) {
        @ecryptfs = @{$r};
    }

    print " Test           | EncFS        | eCryptfs     | EncFS advantage\n";
    print ":---------------|-------------:|-------------:|---------------:\n";

    for ( my $i = 0 ; $i <= $#encfs ; $i++ ) {
        my $test = $encfs[$i][0];
        my $unit = $encfs[$i][2];

        my $en = $encfs[$i][1];
        my $ec = 0;
        my $ratio = 0;

        if( @ecryptfs ) {
            $ec = $ecryptfs[$i][1];
            $ratio = $ec / $en;
            if ( $unit =~ m!/s! ) {
                $ratio = $en / $ec;
            }
        }

        printf( "%-15s | %6d %-5s | %6d %-5s | %2.2f\n",
            $test, $en, $unit, $ec, $unit, $ratio );
    }
}

sub main {
    if ( $#ARGV < 0 ) {
        print "Usage: test/benchmark.pl DIR1 [DIR2] [...]\n";
        print "\n";
        print "Arguments:\n";
        print "  DIRn ... Working directory. This is where the encrypted files\n";
        print "           are stored. Specifying multiple directories will run\n";
        print "           the benchmark in each.\n";
        print "\n";
        print "For details about the testcases see PERFORMANCE.md.\n";

        exit(1);
    }

    if ( $> != 0 ) {
        print("This test must be run as root!\n");
    }

    dl_linuxgz();
    my $workingDir;
    my $mountpoint;
    my $prefix;

    while ( $prefix = shift(@ARGV) ) {
        $workingDir = newWorkingDir($prefix);

        print "# mounting encfs\n";
        $mountpoint = mount_encfs($workingDir);
        my $encfs_results = benchmark($mountpoint);

        print "# mounting ecryptfs\n";
        $mountpoint = mount_ecryptfs($workingDir);
        my $ecryptfs_results;
        if($mountpoint) {
            $ecryptfs_results = benchmark($mountpoint);
        }

        cleanup($workingDir);

        print "\nResults for $prefix\n";
        print "==============================\n\n";
        tabulate( $encfs_results, $ecryptfs_results );
        print "\n";
    }
}

main();
