"Fossies" - the Fresh Open Source Software Archive

Member "encfs-1.9.5/integration/normal.t.pl" (27 Apr 2018, 13894 Bytes) of package /linux/misc/encfs-1.9.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Perl source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "normal.t.pl": 1.9.4_vs_1.9.5.

    1 #!/usr/bin/perl -w
    2 
    3 # Test EncFS normal and paranoid mode
    4 
    5 use Test::More tests => 136;
    6 use File::Path;
    7 use File::Copy;
    8 use File::Temp;
    9 use IO::Handle;
   10 
   11 require("integration/common.pl");
   12 
   13 my $tempDir = $ENV{'TMPDIR'} || "/tmp";
   14 
   15 if($^O eq "linux" and $tempDir eq "/tmp") {
   16    # On Linux, /tmp is often a tmpfs mount that does not support
   17    # extended attributes. Use /var/tmp instead.
   18    $tempDir = "/var/tmp";
   19 }
   20 
   21 # Find attr binary
   22 # Linux
   23 my $setattr = "attr -s encfs -V hello";
   24 my $getattr = "attr -g encfs";
   25 if(system("which xattr > /dev/null 2>&1") == 0)
   26 {
   27     # Mac OS X
   28     $setattr = "xattr -sw encfs hello";
   29     $getattr = "xattr -sp encfs";
   30 }
   31 if(system("which lsextattr > /dev/null 2>&1") == 0)
   32 {
   33     # FreeBSD
   34     $setattr = "setextattr -h user encfs hello";
   35     $getattr = "getextattr -h user encfs";
   36 }
   37 
   38 # Do we support xattr ?
   39 my $have_xattr = 1;
   40 if(system("./build/encfs --verbose --version 2>&1 | grep -q HAVE_XATTR") != 0)
   41 {
   42     $have_xattr = 0;
   43 }
   44 
   45 # Did we ask, or are we simply able to run "sudo" tests ?
   46 my $sudo_cmd;
   47 if ($> == 0)
   48 {
   49     $sudo_cmd="";
   50 }
   51 elsif (defined($ENV{'SUDO_TESTS'}))
   52 {
   53     $sudo_cmd="sudo";
   54 }
   55 
   56 # test filesystem in standard config mode
   57 &runTests('standard');
   58 
   59 # test in paranoia mode
   60 &runTests('paranoia');
   61 
   62 # Wrapper function - runs all tests in the specified mode
   63 sub runTests
   64 {
   65     my $mode = shift;
   66     print STDERR "\nrunTests: mode=$mode sudo=";
   67     print STDERR (defined($sudo_cmd) ? "on" : "off")."\n";
   68 
   69     &newWorkingDir;
   70 
   71     my $hardlinks = 1;
   72     if($mode eq 'standard')
   73     {
   74         &mount("--standard");
   75     } elsif($mode eq 'paranoia')
   76     {
   77         &mount("--paranoia");
   78         $hardlinks = 0; # no hardlinks in paranoia mode
   79         &corruption;
   80     } else
   81     {
   82         die "invalid test mode";
   83     }
   84 
   85     # tests..
   86     &fileCreation;
   87     &links($hardlinks);
   88     &truncate;
   89     &renames;
   90     &internalModification;
   91     &grow;
   92     &umask0777;
   93     &create_unmount_remount;
   94     &checkReadError;
   95     &checkWriteError;
   96 
   97     &configFromPipe;
   98     &cleanup;
   99 }
  100 
  101 # Helper function
  102 # Create a new empty working directory
  103 sub newWorkingDir
  104 {
  105     our $workingDir = mkdtemp("$tempDir/encfs-tests-XXXX")
  106         || BAIL_OUT("Could not create temporary directory");
  107 
  108     our $raw = "$workingDir/raw";
  109     our $crypt = "$workingDir/crypt";
  110     if ($^O eq "cygwin")
  111     {
  112         $crypt = "/cygdrive/x";
  113     }
  114 }
  115 
  116 # Test Corruption
  117 # Modify the encrypted file and verify that the MAC check detects it
  118 sub corruption
  119 {
  120     ok( open(OUT, "+> $crypt/corrupt") && print(OUT "12345678901234567890")
  121         && close(OUT), "create corruption-test file" );
  122 
  123 
  124     $e = encName("corrupt");
  125     ok( open(OUT, ">> $raw/$e") && print(OUT "garbage") && close(OUT),
  126         "corrupting raw file");
  127 
  128     ok( open(IN, "< $crypt/corrupt"), "open corrupted file");
  129     my $content;
  130     $result = read(IN, $content, 20);
  131     # Cygwin returns EINVAL for now
  132     ok(($!{EBADMSG} || $!{EINVAL}) && (! defined $result), "corrupted file with MAC returns read error: $!");
  133 }
  134 
  135 # Test internal modification
  136 # Create a file of fixed size and overwrite data at different offsets
  137 # (like a database would do)
  138 sub internalModification
  139 {
  140     $ofile = "$workingDir/crypt-internal-$$";
  141     writeZeroes($ofile, 2*1024);
  142     ok(copy($ofile, "$crypt/internal"), "copying crypt-internal file");
  143 
  144     open(my $out1, "+<", "$crypt/internal");
  145     open(my $out2, "+<", $ofile);
  146 
  147     @fhs = ($out1, $out2);
  148 
  149     $ori = md5fh($out1);
  150     $b = md5fh($out2);
  151 
  152     ok( $ori eq $b, "random file md5 matches");
  153 
  154     my @offsets = (10, 30, 1020, 1200);
  155     foreach my $o (@offsets)
  156     {
  157         foreach my $fh(@fhs) {
  158             seek($fh, $o, 0);
  159             print($fh "garbagegarbagegarbagegarbagegarbage");
  160         }
  161         $a=md5fh($out1);
  162         $b=md5fh($out2);
  163         ok( ($a eq $b) && ($a ne $ori), "internal modification at $o");
  164     }
  165 
  166     close($out1);
  167     close($out2);
  168 }
  169 
  170 # Test renames
  171 sub renames
  172 {
  173     ok( open(F, ">$crypt/orig-name") && close F, "create file for rename test");
  174     ok( -f "$crypt/orig-name", "file exists");
  175 
  176     ok( rename("$crypt/orig-name", "$crypt/2nd-name"), "rename");
  177     ok( ! -f "$crypt/orig-name", "file exists");
  178     ok( -f "$crypt/2nd-name", "file exists");
  179 
  180     # rename directory with contents
  181     ok( mkpath("$crypt/orig-dir/foo"), "mkdir for rename test");
  182     ok( open(F, ">$crypt/orig-dir/foo/bar") && close F, "make file");
  183 
  184     ok( rename("$crypt/orig-dir", "$crypt/new-dir"), "rename dir");
  185     ok( -f "$crypt/new-dir/foo/bar", "dir rename contents");
  186 
  187     # TODO: rename failure? (check undo works)
  188 
  189     # check time stamps of files on rename
  190     my $mtime = (stat "$crypt/2nd-name")[9];
  191     # change time to 60 seconds earlier
  192     my $olderTime = $mtime - 60;
  193     ok( utime($olderTime, $olderTime, "$crypt/2nd-name"), "change time");
  194 
  195     ok( rename("$crypt/2nd-name", "$crypt/3rd-name"), "rename");
  196     is( (stat "$crypt/3rd-name")[9], $olderTime, "time unchanged by rename");
  197 }
  198 
  199 # Test truncate and grow
  200 sub truncate
  201 {
  202     # write to file, then truncate it
  203     ok( open(OUT, "+> $crypt/trunc"), "create truncate-test file");
  204     autoflush OUT 1;
  205     print OUT "12345678901234567890";
  206 
  207     is( -s "$crypt/trunc", 20, "initial file size" );
  208 
  209     ok( truncate(OUT, 10), "truncate" );
  210 
  211     is( -s "$crypt/trunc", 10, "truncated file size");
  212     is( qx(cat "$crypt/trunc"), "1234567890", "truncated file contents");
  213 
  214     # try growing the file as well.
  215     ok( truncate(OUT, 30), "truncate extend");
  216     is( -s "$crypt/trunc", 30, "truncated file size");
  217 
  218     seek(OUT, 30, 0);
  219     print OUT "12345";
  220     is( -s "$crypt/trunc", 35, "truncated file size");
  221 
  222     is( md5fh(*OUT), "5f170cc34b1944d75d86cc01496292df",
  223         "content digest");
  224 
  225     # try crossing block boundaries
  226     seek(OUT, 10000,0);
  227     print OUT "abcde";
  228 
  229     is( md5fh(*OUT), "117a51c980b64dcd21df097d02206f98",
  230         "content digest");
  231 
  232     # then truncate back to 35 chars
  233     truncate(OUT, 35);
  234     is( md5fh(*OUT), "5f170cc34b1944d75d86cc01496292df",
  235         "content digest");
  236 
  237     close OUT;
  238 }
  239 
  240 # Test file creation and removal
  241 sub fileCreation
  242 {
  243     # first be sure .encfs6.xml does not show up
  244     my $f = encName(".encfs6.xml");
  245     cmp_ok( length($f), '>', 8, "encrypted name ok" );
  246     ok( ! -f "$raw/$f", "configuration file .encfs6.xml not visible in $raw" );
  247 
  248     # create a file
  249     qx(date > "$crypt/df.txt");
  250     ok( -f "$crypt/df.txt", "file created" ) || BAIL_OUT("file create failed");
  251 
  252     # ensure there is an encrypted version.
  253     my $c = encName("df.txt");
  254     cmp_ok( length($c), '>', 8, "encrypted name ok" );
  255     ok( -f "$raw/$c", "encrypted file $raw/$c created" );
  256 
  257     # check contents
  258     my $count = qx(grep -c crypt-$$ "$crypt/df.txt");
  259     isnt(scalar($count), 0, "encrypted file readable");
  260 
  261     unlink "$crypt/df.txt";
  262     ok( ! -f "$crypt/df.txt", "file removal" );
  263     ok( ! -f "$raw/$c", "file removal" );
  264 }
  265 
  266 # Test file growth
  267 sub grow
  268 {
  269     open(my $fh_a, "+>$crypt/grow");
  270     open(my $fh_b, "+>$workingDir/grow");
  271 
  272     my $d = "1234567"; # Length 7 so we are not aligned to the block size
  273     my $len = 7;
  274 
  275     my $old = "";
  276     my $errs = 0;
  277 
  278     my $i;
  279     for($i=1; $i<1000; $i++)
  280     {
  281         print($fh_a $d);
  282         print($fh_b $d);
  283 
  284         my $a = md5fh($fh_a);
  285         my $b = md5fh($fh_b);
  286 
  287         my $size = $len * $i;
  288 
  289         # md5sums must be identical but must have changed
  290         if($a ne $b || $a eq $old)
  291         {
  292             $errs++;
  293         }
  294 
  295         $old = $a;
  296     }
  297 
  298     ok($errs == 0, "grow file by $len bytes, $i times");
  299 
  300     close($fh_a);
  301     close($fh_b);
  302 }
  303 
  304 # Helper function
  305 # Check a file's content
  306 sub checkContents
  307 {
  308     my ($file, $expected, $testName) = @_;
  309 
  310     open(IN, "< $file");
  311     my $line = <IN>;
  312     is( $line, $expected, $testName );
  313 
  314     close IN;
  315 }
  316 
  317 # Helper function
  318 # Convert plain-text filename to encrypted filename
  319 sub encName
  320 {
  321     my $plain = shift;
  322     my $enc = qx(./build/encfsctl encode --extpass="echo test" $raw $plain);
  323     chomp($enc);
  324     return $enc;
  325 }
  326 
  327 # Test symlinks & hardlinks, and extended attributes
  328 sub links
  329 {
  330     my $hardlinkTests = shift;
  331 
  332     my $contents = "hello world";
  333     ok( open(OUT, "> $crypt/data"), "create file for link test" );
  334     print OUT $contents;
  335     close OUT;
  336 
  337     # symlinks
  338     ok( symlink("$crypt/data", "$crypt/data-fqn") , "fqn symlink");
  339     checkContents("$crypt/data-fqn", $contents, "fqn link traversal");
  340     is( readlink("$crypt/data-fqn"), "$crypt/data", "read fqn symlink");
  341 
  342     ok( symlink("data", "$crypt/data-rel"), "local symlink");
  343     checkContents("$crypt/data-rel", $contents, "rel link traversal");
  344     is( readlink("$crypt/data-rel"), "data", "read rel symlink");
  345 
  346     SKIP: {
  347         skip "No hardlink support", 2 unless $hardlinkTests;
  348 
  349         ok( link("$crypt/data", "$crypt/data.2"), "hard link");
  350         checkContents("$crypt/data.2", $contents, "hardlink read");
  351     };
  352 
  353     # extended attributes
  354     my $return_code = ($have_xattr) ? system("$setattr $crypt/data") : 0;
  355     is($return_code, 0, "extended attributes can be set (return code was $return_code)");
  356     $return_code = ($have_xattr) ? system("$getattr $crypt/data") : 0;
  357     is($return_code, 0, "extended attributes can be get (return code was $return_code)");
  358     # this is suppused to fail, so get rid of the error message
  359     $return_code = ($have_xattr) ? system("$getattr $crypt/data-rel 2> /dev/null") : 1;
  360     isnt($return_code, 0, "extended attributes operations do not follow symlinks (return code was $return_code)");
  361 }
  362 
  363 # Test mount
  364 # Leaves the filesystem mounted - also used as a helper function
  365 sub mount
  366 {
  367     my $args = shift;
  368 
  369     # When these fail, the rest of the tests makes no sense
  370     mkdir($raw) || BAIL_OUT("Could not create $raw: $!");
  371     if ($^O ne "cygwin")
  372     {
  373         mkdir($crypt)  || BAIL_OUT("Could not create $crypt: $!");
  374     }
  375 
  376     delete $ENV{"ENCFS6_CONFIG"};
  377     remount($args);
  378     ok( $? == 0, "encfs command returns 0") || BAIL_OUT("");
  379     ok( -f "$raw/.encfs6.xml",  "created control file") || BAIL_OUT("");
  380 }
  381 
  382 # Helper function
  383 # Mount without any prior checks
  384 sub remount
  385 {
  386     my $args = shift;
  387     my $cmdline = "./build/encfs --extpass=\"echo test\" $args $raw $crypt 2>&1";
  388     #                                  This makes sure we get to see stderr ^
  389     system($cmdline);
  390 }
  391 
  392 # Helper function
  393 # Unmount and delete mountpoint
  394 sub cleanup
  395 {
  396     portable_unmount($crypt);
  397 
  398     rmdir $crypt;
  399     ok( ! -d $crypt, "unmount ok, mount point removed");
  400 
  401     rmtree($workingDir);
  402     ok( ! -d $workingDir, "working dir removed");
  403 }
  404 
  405 # Test that we can create and write to a a file even if umask is set to 0777
  406 # Regression test for bug https://github.com/vgough/encfs/issues/181
  407 sub umask0777
  408 {
  409     my $old = umask(0777);
  410     ok(open(my $fh, "+>$crypt/umask0777"), "open with umask 0777");
  411     close($fh);
  412     umask($old);
  413 }
  414 
  415 # Test that we can read the configuration from a named pipe
  416 # Regression test for https://github.com/vgough/encfs/issues/253
  417 sub configFromPipe
  418 {
  419     portable_unmount($crypt);
  420     rename("$raw/.encfs6.xml", "$raw/.encfs6.xml.orig");
  421     system("mkfifo $raw/.encfs6.xml");
  422     my $child = fork();
  423     unless ($child) {
  424         &remount("--standard");
  425         exit;
  426     }
  427     system("cat $raw/.encfs6.xml.orig > $raw/.encfs6.xml");
  428     waitpid($child, 0);
  429     ok( 0 == $?, "encfs mount with named pipe based config failed");
  430 }
  431 
  432 sub create_unmount_remount
  433 {
  434     my $crypt = "$workingDir/create_remount.crypt";
  435     my $mnt = "$workingDir/create_remount.mnt";
  436     if ($^O eq "cygwin")
  437     {
  438         $mnt = "/cygdrive/y";
  439     }
  440     mkdir($crypt) || BAIL_OUT($!);
  441     if ($^O ne "cygwin")
  442     {
  443         mkdir($mnt)  || BAIL_OUT($!);
  444     }
  445 
  446     system("./build/encfs --standard --extpass=\"echo test\" $crypt $mnt 2>&1");
  447     ok( $? == 0, "encfs command returns 0") || return;
  448     ok( -f "$crypt/.encfs6.xml",  "created control file") || return;
  449 
  450     # Write some text
  451     my $contents = "hello world\n";
  452     ok( open(OUT, "> $mnt/test_file_1"), "write content");
  453     print OUT $contents;
  454     close OUT;
  455 
  456     # Unmount
  457     portable_unmount($mnt);
  458 
  459     # Mount again, testing -c at the same time
  460     rename("$crypt/.encfs6.xml", "$crypt/.encfs6_moved.xml");
  461     system("./build/encfs -c $crypt/.encfs6_moved.xml --extpass=\"echo test\" $crypt $mnt 2>&1");
  462     ok( $? == 0, "encfs command returns 0") || return;
  463 
  464     # Check if content is still there
  465     checkContents("$mnt/test_file_1", $contents);
  466 
  467     portable_unmount($mnt);
  468 }
  469 
  470 # Test that read errors are correctly thrown up to us
  471 sub checkReadError
  472 {
  473     # Not sure how to implement this, so feel free !
  474     ok(1, "read error");
  475 }
  476 
  477 # Test that write errors are correctly thrown up to us
  478 sub checkWriteError
  479 {
  480     # No OSX impl (for now, feel free to find how to), and requires "sudo".
  481     if($^O eq "darwin" || !defined($sudo_cmd)) {
  482         ok(1, "write error");
  483         ok(1, "write error");
  484         ok(1, "write error");
  485         ok(1, "write error");
  486     }
  487     else {
  488             my $crypt = "$workingDir/checkWriteError.crypt";
  489             my $mnt = "$workingDir/checkWriteError.mnt";
  490             if ($^O eq "cygwin")
  491             {
  492                 $mnt = "/cygdrive/z";
  493             }
  494             mkdir($crypt) || BAIL_OUT($!);
  495             if ($^O ne "cygwin")
  496             {
  497                 mkdir($mnt)  || BAIL_OUT($!);
  498             }
  499             system("$sudo_cmd mount -t tmpfs -o size=1m tmpfs $crypt");
  500             ok( $? == 0, "mount command returns 0") || return;
  501             system("./build/encfs --standard --extpass=\"echo test\" $crypt $mnt 2>&1");
  502             ok( $? == 0, "encfs command returns 0") || return;
  503             ok(open(OUT , "> $mnt/file"), "write content");
  504             while(print OUT "0123456789") {}
  505             ok ($!{ENOSPC}, "write returned $! instead of ENOSPC");
  506             close OUT;
  507             portable_unmount($mnt);
  508             system("$sudo_cmd umount $crypt");
  509     }
  510 }