diff options
| author | Jakob Unterwurzacher | 2020-05-03 13:23:00 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2020-05-03 14:49:32 +0200 | 
| commit | db93a6c54cfd615561207f1bbcf7e665ebc296b6 (patch) | |
| tree | da3148e0bc191834a2f4b01e525c2e2f51ae6751 | |
| parent | 483054efaa1bd965b41265c681a78fb98e51b739 (diff) | |
tests: reverse: add inode mapping test (TestVirtualFileIno)
Verify that virtual files get assigned inode numbers
we expect.
| -rw-r--r-- | tests/reverse/correctness_test.go | 7 | ||||
| -rw-r--r-- | tests/reverse/inomap_test.go | 143 | 
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) +	} +} | 
