#!/usr/local/bin/perl

# This test only works with the MS-DOS FS. Other FS types (MINIX or EXT)
# have different naming semantics and will fail several tests.

sub section
{
    ($section) = @_;

    print "$section";
    $cnt = 0;
}

sub pass
{
    local ($status) = @_;

    if (!$status) { die("\nPASS failed: $section.$cnt$sub (errno=$!)\n"); }
    if ($sub) { $sub++; }
    else { $cnt++; }
    $num_tests++;
}

sub fail
{
    local ($status) = @_;

    if ($status) { die("\nFAIL passed: $section.$cnt$sub\n"); }
    if ($sub) { $sub++; }
    else { $cnt++; }
    $num_tests++;
}

sub touch
{
    local ($name) = @_;

    open(FILE,">$name") || return 0;
    close FILE;
    return 1;
}

sub create
{
    local ($name) = @_;

    open(FILE,">$name") || return 0;
    printf(FILE "%20480s","") || return 0;
    close FILE || return 0;
    return 1;
}

sub same
{
    local ($a,$b) = @_;
    local ($s_a,$s_b);

    $sub = "a";
    &pass(seek($a,0,2));
    &pass(seek($b,0,2));
    &pass(($s_a = tell($a)) >= 0);
    &pass(($s_b = tell($b)) >= 0);
    &pass(print $a ".");
    &pass(seek($b,0,2));
    &pass(($s_nb = tell($b)) >= 0);
    $sub = "";
    if ($s_a != $s_b) { return 0; }
    return $s_b != $s_nb;
}

sub mode
{
    local ($name,$set,$get) = @_;
    local ($mode);

    return chmod($set,$name) && (($mode = (stat($name))[2]),
	$mode == ($get & ~umask));
}

sub rename
{
    local ($from,$to) = @_;

    return !system("$cwd/rename $from $to");
}

sub links
{
    local ($name) = @_;

    return (stat($name))[3];
}

sub r_pass
{
    local ($status) = @_;

    if ($conv eq "r") { &pass($status); }
    else { &fail($status); }
}

sub ns_pass
{
    local ($status) = @_;

    if ($conv ne "s") { &pass($status); }
    else { &fail($status); }
}


if ($#ARGV != 1) {
    print "usage: test.pl mount_point conv_mode\n";
    exit(1);
}
open(PWD,"pwd|") || die "pwd: $!";
chop($cwd = <PWD>);
close(PWD);
&pass(chdir($ARGV[0]));
if ($ARGV[1] eq "relaxed" || $ARGV[1] eq "r") { $conv = "r"; }
elsif ($ARGV[1] eq "normal" || $ARGV[1] eq "n") { $conv = "n"; }
elsif ($ARGV[1] eq "strict" || $ARGV[1] eq "s") { $conv = "s"; }
else { die "conv_mode must be either relaxed, normal or strict\n"; }
$| = 1;

# PERL test

if (0) { # we work around this ...
opendir(DIR,".") || die "opendir failed: $!";
if (!readdir(DIR)) {
    print "\nThis is an obsolete version of PERL that doesn't use the\n";
    print "kernel rename and readdir. Please update or re-compile it to\n";
    print "run this regression test script.\n";
    exit(0);
}
closedir(DIR);
}

# self test

&section("A");
&pass(1);
&fail(0);
&pass(&touch("test"));
&pass(unlink("test"));
&fail(unlink("test"));
&pass(open(A,">a"));
&pass(open(B,">b"));
&pass(&same(A,A));
&fail(&same(A,B));
&pass(&same(B,B));
&pass(close(A));
&pass(close(B));
&pass(unlink("a"));
&pass(unlink("b"));

# test valid names

&section("B");
&pass(&touch("x.baz"));
&pass(&touch("foo"));
&pass(&touch("bar."));
&pass(&touch("foo.bar"));
&pass(unlink("foo"));
&pass(unlink("bar"));
&pass(unlink("foo.bar"));
&pass(unlink("x.baz"));
# &r_pass(&touch(" leading")); -- perl problem
&ns_pass(&touch("embed ded"));
&ns_pass(&touch("f o o.b a"));
&ns_pass(&touch("fo   o"));
&r_pass(&touch("*?,="));
&fail(&touch("trail .ing"));
# &fail(&touch("trailin.g ")); -- perl problem
# &r_pass(unlink(" leading")); -- perl problem
&ns_pass(unlink("embed de"));
&ns_pass(unlink("f o o.b a"));
&ns_pass(unlink("fo   o"));
&r_pass(unlink("*?,="));

# test name folding

&section("C");
&pass(&touch("eightchr"));
&ns_pass(&touch("foobar.xyzzy"));
&pass(&touch("longname"));
&ns_pass(&touch("verylongname"));
&ns_pass(&touch("longname.xxx12"));
&pass(&touch("longname.yyy"));
&ns_pass(&touch("verylongname.x"));
&ns_pass(&touch("verylongname.extension"));
&ns_pass(&touch("foo.bar.baz"));
&pass(unlink("eightchr"));
&ns_pass(unlink("foobar.xyz"));
&pass(unlink("longname."));
&ns_pass(unlink("longname.xxx"));
&pass(unlink("longname.yyy"));
&ns_pass(unlink("verylong"));
&ns_pass(unlink("verylong.x"));
&ns_pass(unlink("verylong.ext"));
&ns_pass(unlink("foo.bar"));

# test rejection of invalid names

&section("D");
&fail(&touch(".baz"));
&ns_pass(&touch("Test"));
&ns_pass(unlink("test"));
&fail(&touch("con"));
&fail(&touch("nul.txt"));
&fail(&touch("foo.b.ar"));
&fail(&touch("foo\\bar"));
&fail(&touch(""));
#&fail(&touch("trail ")); -- perl problem
&fail(&touch("foo .bar"));
#&fail(&touch("f.ba ")); -- perl problem
&fail(&touch("foobar  .gna"));
&fail(&touch("foobar   .gna"));

# test unlinking an open file

&section("E");
&pass(open(A,">a"));
&pass(unlink("a"));
&pass(open(B,">b"));
&fail(&same(A,B));
&pass(close(A));
&pass(close(B));

# test mkdir and rmdir

&section("F");
&pass(mkdir("a",0777));
&fail(mkdir("a",0777));
&pass(mkdir("a/b",0777));
&pass(mkdir("a/b/../../a/b/c",0777));
&pass(mkdir("a/./c",0777));
&fail(rmdir("a"));
&fail(rmdir("a/b"));
&pass(rmdir("a/b/c"));
&pass(rmdir("a/b"));
&pass(chdir("a"));
&pass(rmdir("./c"));
&fail(rmdir("."));
&fail(rmdir(".."));
&pass(chdir(".."));
&pass(rmdir("a"));

# test unlinking files with data

&section("G");
&pass(&create("a"));
&pass(&create("b"));
&pass(unlink("a"));
&pass(&create("c"));
&pass(unlink("c"));
&pass(unlink("b"));

# test moving things around (rename, part 1)

&section("H");
&pass(&touch("a"));
&pass(&rename("a","a"));
&pass(&rename("a","b"));
&pass(unlink("b"));
&pass(mkdir("a",0777));
&pass(&create("b"));
&pass(&rename("b","a/b"));
&pass(&touch("b"));
&pass(&rename("b","a/b"));
&pass(mkdir("b",0777));
&fail(&rename("b","a/b"));
&pass(unlink("a/b"));
&pass(&rename("b","a/b"));
&pass(mkdir("c",0777));
&pass(&rename("c","a/b"));
&pass(mkdir("a/b/c",0777));
&fail(&rename("a","a/a"));
&fail(&rename("a","a/b/c/a"));
&fail(&rename("a/b","a/b/c/b"));
&pass(&rename("a/b/c","c"));
&pass(rmdir("c"));
&pass(&rename("a","c"));
&pass(rmdir("c/b"));
&pass(rmdir("c"));

# test for full POSIX.1 complicance (rename, part 2)

&section("I");
&pass(&touch("a"));		# file -> file
&pass(&touch("b"));
&pass(&rename("a","b"));
&pass(mkdir("a",0777));		# dir -> file
&fail(&rename("a","b"));
&pass(unlink("b"));
&pass(mkdir("b",0777));		# dir -> empty dir
&pass(&rename("a","b"));
&pass(&touch("a"));		# file -> dir
&fail(&rename("a","b"));
&pass(&rename("a","b/a"));	# dir -> non-empty dir
&pass(mkdir("a",0777));
&fail(&rename("a","b"));
&pass(unlink("b/a"));		# cleanup
&pass(rmdir("a"));
&pass(rmdir("b"));

# test moving busy directories (rename, part 3)

&section("J");
&pass(mkdir("a",0777));
&pass(mkdir("b",0777));
&pass(&touch("c"));
&pass(&touch("b/d"));
&pass(chdir("a"));
&pass(&rename("../a","../b/a"));
&pass(unlink("../../c"));
&pass(chdir(".."));
&fail(unlink("c"));
&pass(unlink("d"));
&pass(rmdir("a"));
&pass(chdir(".."));
&pass(rmdir("b"));

# test handling of holes

&section("K");
&pass(open(A,">test"));
&pass(seek(A,10000,0));
&pass(print A ".");
&pass(open(B,">probe"));
&pass(print B ".");
&pass(close(A));
&pass(close(B));
&pass(unlink("test"));
&pass(unlink("probe"));

# test link counts

&section("L");
&pass(&links(".") == 2);
&pass(mkdir("a",0777));
&pass(&links(".") == 3);
&pass(&links("a") == 2);
&pass(mkdir("b",0777));
&pass(&links("a/..") == 4);
&pass(mkdir("a/a",0777));
&pass(mkdir("a/b",0777));
&pass(&links("a") == 4);
&pass(rmdir("a/a"));
&pass(rmdir("a/b"));
&pass(&links("a") == 2);
&pass(rmdir("b"));
&pass(&links("a/..") == 3);
&pass(&touch("c"));
&pass(&links(".") == 3);
&pass(unlink("c"));
&pass(rmdir("a"));
&pass(&links(".") == 2);

# test link counts after rename

&section("M");
&pass(mkdir("x",0777));
&pass(mkdir("x/y",0777));
&pass(&links(".") == 3);
&pass(&links("x") == 3);
&pass(&rename("x/y","x/z"));
&pass(&links(".") == 3);
&pass(&links("x") == 3);
&pass(&rename("x/z","z"));
&pass(&links(".") == 4);
&pass(&links("x") == 2);
&pass(rmdir("x"));
&pass(rmdir("z"));
&pass(&links(".") == 2);

# test link counts after directory renames

&section("N");
&pass(mkdir("a",0777));
&pass(mkdir("b",0777));
&pass(mkdir("c",0777));
&pass(&links(".") == 5);
&pass(&links("a/..") == 5);
&pass(&links("b/..") == 5);
&pass(&rename("b","a/b"));
&pass(&links("a/b/..") == 3);
&pass(&links("c/..") == 4);
&pass(&rename("c","a/c"));
&pass(&links("a/c/.") == 2);
&pass(&links("a/c/..") == 4);
&pass(&links(".") == 3);
&pass(rmdir("a/b"));
&pass(&links("a/c/..") == 3);
&pass(rmdir("a/c"));
&pass(rmdir("a"));
&pass(&links(".") == 2);

# test for accidentially dropping i_nlink to zero

&section("O");
&pass(mkdir("a",0777));
&pass(mkdir("b",0777));
&pass(mkdir("c",0777));
&pass(mkdir("d",0777));
&pass(mkdir("e",0777));
&pass(mkdir("f",0777));
&pass(mkdir("g",0777));
&pass(mkdir("h",0777));
&pass(mkdir("i",0777));
&pass(mkdir("j",0777));
&pass(mkdir("x",0777));
&pass(&rename("a","x/a"));
&pass(&rename("b","x/b"));
&pass(&rename("c","x/c"));
&pass(&rename("d","x/d"));
&pass(&rename("e","x/e"));
&pass(&rename("f","x/f"));
&pass(&rename("g","x/g"));
&pass(&rename("h","x/h"));
&pass(&rename("i","x/i"));
&pass(&rename("j","x/j"));
&pass(rmdir("x/a"));
&pass(rmdir("x/b"));
&pass(rmdir("x/c"));
&pass(rmdir("x/d"));
&pass(rmdir("x/e"));
&pass(rmdir("x/f"));
&pass(rmdir("x/g"));
&pass(rmdir("x/h"));
&pass(rmdir("x/i"));
&pass(rmdir("x/j"));
&pass(rmdir("x"));

# test automatic directory extension on rename

&section("P");
&pass(mkdir("src",0777));
&pass(mkdir("dst",0777));
$dpc = (stat("src"))[7]/32;
for ($c = 0; $c < $dpc; $c++) { &pass(&touch("src/$c")); }
for ($c = 0; $c < $dpc; $c++) { &pass(&rename("src/$c","dst/$c")); }
for ($c = 0; $c < $dpc; $c++) { &pass(unlink("dst/$c")); }
&pass(rmdir("src"));
&pass(rmdir("dst"));

# test rejection of bad permissions

&section("Q");
&pass(&touch("test"));
&pass(chmod(0666,"test"));
&fail(chmod(01666,"test"));
&fail(chmod(02666,"test"));
&fail(chmod(04666,"test"));
($uid,$gid) = (stat("test"))[4,5];
&pass(chown($uid,$gid,"test"));
&fail(chown($uid ^ 1,$gid,"test"));
&fail(chown($uid,$gid ^ 1,"test"));
&pass(unlink("test"));

# test mode normalization

&section("R");
&pass(&touch("file"));
&pass(mkdir("dir",0777));
&pass(&mode("file",0,0555));
&pass(&mode("file",0777,0777));
&pass(&mode("file",0770,0777));
&pass(&mode("file",0707,0777));
&pass(&mode("file",0577,0555));
&pass(&mode("file",0333,0777));
&pass(&mode("dir",0,0555));
&pass(&mode("dir",0700,0777));
&pass(unlink("file"));
&pass(rmdir("dir"));

# test denial of creation of . and ..

&section("S");
&fail(mkdir(".",0777));
&fail(mkdir("..",0777));

# done

print "\nPassed all $num_tests tests.\n";
