aboutsummaryrefslogtreecommitdiff
path: root/tests/reverse
diff options
context:
space:
mode:
authorJakob Unterwurzacher2020-05-03 13:23:00 +0200
committerJakob Unterwurzacher2020-05-03 14:49:32 +0200
commitdb93a6c54cfd615561207f1bbcf7e665ebc296b6 (patch)
treeda3148e0bc191834a2f4b01e525c2e2f51ae6751 /tests/reverse
parent483054efaa1bd965b41265c681a78fb98e51b739 (diff)
tests: reverse: add inode mapping test (TestVirtualFileIno)
Verify that virtual files get assigned inode numbers we expect.
Diffstat (limited to 'tests/reverse')
-rw-r--r--tests/reverse/correctness_test.go7
-rw-r--r--tests/reverse/inomap_test.go143
2 files changed, 150 insertions, 0 deletions
diff --git a/tests/reverse/correctness_test.go b/tests/reverse/correctness_test.go
index 931be42..67edeb6 100644
--- a/tests/reverse/correctness_test.go
+++ b/tests/reverse/correctness_test.go
@@ -201,6 +201,13 @@ func TestTooLongSymlink(t *testing.T) {
if err != nil {
t.Fatal(err)
}
+ // save later tests the trouble of dealing with ENAMETOOLONG errors
+ defer func() {
+ os.Remove(fn)
+ // immediately create a new symlink so the inode number is not
+ // reused for something else
+ os.Symlink("/tmp", fn)
+ }()
t.Logf("Created symlink of length %d", l)
_, err = os.Readlink(dirC + "/TooLongSymlink")
if err == nil {
diff --git a/tests/reverse/inomap_test.go b/tests/reverse/inomap_test.go
new file mode 100644
index 0000000..e3bd207
--- /dev/null
+++ b/tests/reverse/inomap_test.go
@@ -0,0 +1,143 @@
+package reverse_test
+
+import (
+ "bytes"
+ "os"
+ "strings"
+ "syscall"
+ "testing"
+)
+
+// findIno looks for the file having inode number `ino` in `dir`.
+// Returns "" if not found.
+func findIno(dir string, ino uint64) string {
+ fd, err := os.Open(dir)
+ if err != nil {
+ return ""
+ }
+ dirents, err := fd.Readdirnames(0)
+ if err != nil {
+ return ""
+ }
+ fd.Close()
+ for _, entry := range dirents {
+ var st syscall.Stat_t
+ err = syscall.Lstat(dir+"/"+entry, &st)
+ if err != nil {
+ continue
+ }
+ if ino == st.Ino {
+ return entry
+ }
+ }
+ return ""
+}
+
+// TestVirtualFileIno creates a directory tree like this:
+//
+// TestVirtualFileIno <---- parent
+// └── xxxxxxx[...] <---- child
+//
+// Which looks like this encrypted:
+//
+// OLUKdPMg6l87EiKVlufgwIkQL8MD6JdUgOR3a8nEZ-w <---- parent
+// ├── gocryptfs.diriv <---- diriv
+// ├── gocryptfs.longname.e31v1ax4h_F0l4jhlN8kCjaWWMq8rO9VVBZ15IYsV50 <---- child
+// └── gocryptfs.longname.e31v1ax4h_F0l4jhlN8kCjaWWMq8rO9VVBZ15IYsV50.name <---- name
+//
+// And verifies that the inode numbers match what we expect.
+func TestVirtualFileIno(t *testing.T) {
+ if plaintextnames {
+ t.Skip("plaintextnames mode does not have virtual files")
+ }
+
+ type inoTable struct {
+ parent uint64
+ diriv uint64
+ child uint64
+ name uint64
+ }
+ var origInos inoTable
+ var cipherInos inoTable
+
+ parent := dirA + "/TestVirtualFileIno"
+ name := string(bytes.Repeat([]byte("x"), 240))
+ err := os.MkdirAll(parent+"/"+name, 0700)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var st syscall.Stat_t
+ err = syscall.Lstat(parent+"/"+name, &st)
+ if err != nil {
+ t.Fatal(err)
+ }
+ origInos.child = st.Ino
+ // get inode number of plain parent
+ err = syscall.Lstat(parent, &st)
+ if err != nil {
+ t.Fatal(err)
+ }
+ origInos.parent = st.Ino
+ // find it in encrypted `dirB`
+ fd, err := os.Open(dirB)
+ if err != nil {
+ t.Fatal(err)
+ }
+ dirents, err := fd.Readdirnames(0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fd.Close()
+ encryptedParent := findIno(dirB, origInos.parent)
+ if encryptedParent == "" {
+ t.Fatalf("could not find ino %d in %q", origInos.parent, dirB)
+ }
+ encryptedParent = dirB + "/" + encryptedParent
+ err = syscall.Stat(encryptedParent, &st)
+ if err != nil {
+ t.Fatal(err)
+ }
+ cipherInos.parent = st.Ino
+ fd, err = os.Open(encryptedParent)
+ if err != nil {
+ t.Fatal(err)
+ }
+ dirents, err = fd.Readdirnames(0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fd.Close()
+ for _, entry := range dirents {
+ var st2 syscall.Stat_t
+ err = syscall.Lstat(encryptedParent+"/"+entry, &st2)
+ if err != nil {
+ t.Logf("stat %q: %v", entry, err)
+ continue
+ }
+ if entry == "gocryptfs.diriv" {
+ cipherInos.diriv = st2.Ino
+ } else if strings.HasSuffix(entry, ".name") {
+ cipherInos.name = st2.Ino
+ } else {
+ cipherInos.child = st2.Ino
+ }
+ }
+ if origInos.parent != cipherInos.parent {
+ t.Errorf("parent ino mismatch: %d != %d", origInos.parent, cipherInos.parent)
+ }
+ if origInos.parent == cipherInos.diriv {
+ t.Errorf("diriv ino collision: %d == %d", origInos.parent, cipherInos.diriv)
+ }
+ if origInos.parent != cipherInos.diriv-1000000000000000000 {
+ t.Errorf("diriv ino mismatch: %d != %d", origInos.parent, cipherInos.diriv)
+ }
+ if origInos.child != cipherInos.child {
+ t.Errorf("child ino mismatch: %d vs %d", origInos.child, cipherInos.child)
+ }
+ if origInos.child == cipherInos.name {
+ t.Errorf("name ino collision: %d == %d", origInos.child, cipherInos.name)
+ }
+ if origInos.child != cipherInos.name-2000000000000000000 {
+ t.Errorf("name ino mismatch: %d vs %d", origInos.child, cipherInos.name)
+ }
+}