tests: reverse: add inode mapping test (TestVirtualFileIno)

Verify that virtual files get assigned inode numbers
we expect.
This commit is contained in:
Jakob Unterwurzacher 2020-05-03 13:23:00 +02:00
parent 483054efaa
commit db93a6c54c
2 changed files with 150 additions and 0 deletions

View File

@ -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 {

View File

@ -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)
}
}