// Tests and benchmarks performed with default settings only. package defaults import ( "bytes" "io" "io/ioutil" "os" "os/exec" "runtime" "strings" "sync" "testing" "github.com/rfjakob/gocryptfs/tests/test_helpers" ) func TestMain(m *testing.M) { test_helpers.ResetTmpDir(true) test_helpers.MountOrExit(test_helpers.DefaultCipherDir, test_helpers.DefaultPlainDir, "-zerokey") r := m.Run() test_helpers.UnmountPanic(test_helpers.DefaultPlainDir) os.Exit(r) } // Test that we get the right timestamp when extracting a tarball. func Test1980Tar(t *testing.T) { c := exec.Command("tar", "xzf", "1980.tar.gz", "-C", test_helpers.DefaultPlainDir) c.Stderr = os.Stderr c.Stdout = os.Stdout err := c.Run() if err != nil { t.Fatal(err) } fi, err := os.Stat(test_helpers.DefaultPlainDir + "/1980.txt") if err != nil { t.Fatal(err) } m := fi.ModTime().Unix() if m != 315619323 { t.Errorf("Wrong mtime: %d", m) } } // In gocryptfs before v1.2, the file header was only read once for each // open. But truncating a file to zero will generate a new random file ID. // The sequence below caused an I/O error to be returned. func TestOpenTruncateRead(t *testing.T) { fn := test_helpers.DefaultPlainDir + "/TestTruncateWrite" // First FD is used for write and truncate. writeFd, err := os.Create(fn) if err != nil { t.Fatal(err) } defer writeFd.Close() abc := []byte("abc") _, err = writeFd.WriteAt(abc, 0) if err != nil { t.Fatal(err) } // Second FD is just for reading. readFd, err := os.Open(fn) if err != nil { t.Fatal(err) } defer readFd.Close() content := make([]byte, 3) _, err = readFd.ReadAt(content, 0) if err != nil { t.Fatal(err) } if !bytes.Equal(content, abc) { t.Fatalf("wrong content: %s", string(content)) } // Truncate to zero to generate a new file ID and write new content. err = writeFd.Truncate(0) if err != nil { t.Fatal(err) } xyz := []byte("xyz") _, err = writeFd.WriteAt(xyz, 0) if err != nil { t.Fatal(err) } // Try to read from the other FD. _, err = readFd.ReadAt(content, 0) if err != nil { t.Fatal(err) } if !bytes.Equal(content, xyz) { t.Fatalf("wrong content: %s", string(content)) } } // TestWORead tries to read from a write-only FD. func TestWORead(t *testing.T) { fn := test_helpers.DefaultPlainDir + "/TestWORead" fd, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0600) if err != nil { t.Fatal(err) } defer fd.Close() buf := make([]byte, 10) _, err = fd.Read(buf) if err == nil { t.Error("Reading from write-only file should fail, but did not") } } // xfstests generic/124 triggers this warning: // cipherSize 18 == header size: interrupted write? // This test reproduces the problem. func TestXfs124(t *testing.T) { // GOMAXPROCS=8 and N=5000 seem to reliably trigger the problem. With N=1000, // the test passes sometimes. runtime.GOMAXPROCS(8) N := 5000 fn := test_helpers.DefaultPlainDir + "/TestXfs124" fd, err := os.Create(fn) if err != nil { t.Fatal(err) } defer fd.Close() var wg sync.WaitGroup wg.Add(2) go func() { buf := make([]byte, 10) var err2 error for i := 0; i < N; i++ { err2 = fd.Truncate(0) if err2 != nil { panic(err2) } _, err2 = fd.WriteAt(buf, 0) if err2 != nil { panic(err2) } } wg.Done() }() fd2, err := os.Open(fn) if err != nil { t.Fatal(err) } defer fd2.Close() go func() { buf := make([]byte, 10) var err3 error for i := 0; i < N; i++ { _, err3 = fd2.ReadAt(buf, 0) if err3 == io.EOF { continue } if err3 != nil { panic(err3) } } wg.Done() }() wg.Wait() } func TestWrite0200File(t *testing.T) { fn := test_helpers.DefaultPlainDir + "/TestWrite0200File" err := ioutil.WriteFile(fn, nil, 0200) if err != nil { t.Fatalf("creating empty file failed: %v", err) } fd, err := os.OpenFile(fn, os.O_WRONLY, 0) if err != nil { t.Fatal(err) } fi, err := fd.Stat() if err != nil { t.Fatal(err) } perms := fi.Mode().Perm() if perms != 0200 { t.Fatal("wrong initial permissions") } defer fd.Close() _, err = fd.Write(make([]byte, 10)) if err != nil { t.Fatal(err) } perms = fi.Mode().Perm() if perms != 0200 { t.Fatal("wrong restored permissions") } } // When xattr support was introduced, mv threw warnings like these: // mv: preserving permissions for ‘b/x’: Operation not permitted // because we returned EPERM when it tried to set system.posix_acl_access. // Now we return EOPNOTSUPP and mv is happy. func TestMvWarnings(t *testing.T) { fn := test_helpers.TmpDir + "/TestMvWarnings" err := ioutil.WriteFile(fn, nil, 0600) if err != nil { t.Fatalf("creating file failed: %v", err) } cmd := exec.Command("mv", fn, test_helpers.DefaultPlainDir) out, err := cmd.CombinedOutput() if err != nil { t.Log(string(out)) t.Fatal(err) } if len(out) != 0 { t.Fatalf("Got warnings from mv:\n%s", string(out)) } } // Check for this bug in symlink handling: // $ ln -s /asd/asdasd/asdasd b/foo // $ mv b/foo . // mv: listing attributes of 'b/foo': No such file or directory // strace shows: // llistxattr("b/foo", NULL, 0) = -1 ENOENT (No such file or directory) func TestMvWarningSymlink(t *testing.T) { fn := test_helpers.DefaultPlainDir + "/TestMvWarningSymlink" err := os.Symlink("/foo/bar/baz", fn) if err != nil { t.Fatal(err) } cmd := exec.Command("mv", fn, test_helpers.TmpDir) out, err := cmd.CombinedOutput() if err != nil { t.Log(string(out)) t.Fatal(err) } if len(out) != 0 { t.Log(strings.TrimSpace(string(out))) t.Fatal("Got warnings") } } // See TestCpWarnings. func TestCpWarnings(t *testing.T) { fn := test_helpers.TmpDir + "/TestCpWarnings" err := ioutil.WriteFile(fn, []byte("foo"), 0600) if err != nil { t.Fatalf("creating file failed: %v", err) } cmd := exec.Command("cp", "-a", fn, test_helpers.DefaultPlainDir) out, err := cmd.CombinedOutput() if err != nil { t.Log(string(out)) t.Fatal(err) } if len(out) != 0 { t.Fatalf("Got warnings from cp -a:\n%s", string(out)) } }