aboutsummaryrefslogtreecommitdiff
path: root/tests/plaintextnames/file_holes_test.go
diff options
context:
space:
mode:
authorJakob Unterwurzacher2021-05-29 16:05:36 +0200
committerJakob Unterwurzacher2021-05-29 16:05:36 +0200
commit738a9e006af6f5e43871c2d8e208601c18191965 (patch)
tree48e52a90a2d59b622453497bff18482badd3e2b2 /tests/plaintextnames/file_holes_test.go
parentc1d7e38761d35149f19524fa19d3afaaca73f302 (diff)
fusefrontend: rewrite Lseek SEEK_DATA / SEEK_HOLE
In response to the discussion of the xfstests mailing list [1], I looked at the Lseek implementation, which was naive and did not handle all cases correctly. The new implementation aligns the returned values to 4096 bytes as most callers expect. A lot of tests are added to verify that we handle all cases correctly now. [1]: https://www.spinics.net/lists/fstests/msg16554.html
Diffstat (limited to 'tests/plaintextnames/file_holes_test.go')
-rw-r--r--tests/plaintextnames/file_holes_test.go113
1 files changed, 84 insertions, 29 deletions
diff --git a/tests/plaintextnames/file_holes_test.go b/tests/plaintextnames/file_holes_test.go
index d046cea..9948715 100644
--- a/tests/plaintextnames/file_holes_test.go
+++ b/tests/plaintextnames/file_holes_test.go
@@ -2,9 +2,12 @@ package plaintextnames
import (
"fmt"
+ "math/rand"
"os"
"os/exec"
+ "syscall"
"testing"
+ "time"
"github.com/rfjakob/gocryptfs/tests/test_helpers"
@@ -26,38 +29,21 @@ func findHolesPretty(t *testing.T, path string) string {
return holes.PrettyPrint(segments)
}
-// TestFileHoleCopy creates a sparse times, copies it a few times, and check if
-// the copies are the same (including the location of holes and data sections).
-//
-// The test runs with -plaintextnames because that makes it easier to manipulate
-// cipherdir directly.
-func TestFileHoleCopy(t *testing.T) {
- n := "TestFileHoleCopy"
+func doTestFileHoleCopy(t *testing.T, name string, writeOffsets []int64) {
+ n := "TestFileHoleCopy." + name
pPath := []string{pDir + "/" + n}
cPath := []string{cDir + "/" + n}
- f, err := os.Create(pPath[0])
- if err != nil {
- t.Fatal(err)
- }
- // | hole | x | hole | x | hole |
- buf := []byte("x")
- f.WriteAt(buf, 10000)
- f.WriteAt(buf, 30000)
- f.Truncate(50000)
- f.Sync()
- f.Close()
-
- // You have to update this value manually when you change the sequence
- // above
+ os.Remove(pPath[0])
+ holes.Create(pPath[0])
+
+ // expected md6
md5 := test_helpers.Md5fn(pPath[0])
- if md5 != "4e8d0742bccfbcdbf1d71be688e4e81c" {
- t.Fatalf("wrong md5: %s", md5)
- }
pSegments := []string{findHolesPretty(t, pPath[0])}
cSegments := []string{findHolesPretty(t, cPath[0])}
+ // create 5 more copies
for i := 1; i < 5; i++ {
pPath = append(pPath, fmt.Sprintf("%s.%d", pPath[0], i))
cPath = append(cPath, fmt.Sprintf("%s.%d", cPath[0], i))
@@ -76,21 +62,90 @@ func TestFileHoleCopy(t *testing.T) {
cSegments = append(cSegments, findHolesPretty(t, cPath[i]))
}
+ // "cp --sparse=auto" checks of the file has fewer blocks on disk than it
+ // should have for its size. Only then it will try to create a sparse copy.
+ var st syscall.Stat_t
+ err := syscall.Stat(pPath[0], &st)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // convert 512 byte blocks to 4k blocks
+ blocks4k := st.Blocks / 8
+ // For more than a few fragments, ext4 allocates one extra block
+ blocks4k++
+ if blocks4k >= (st.Size+4095)/4096 {
+ t.Logf("file will look non-sparse to cp, skipping segment check")
+ return
+ }
+
+ // Check that size on disk stays the same across copies
+ var st0 syscall.Stat_t
+ if err := syscall.Stat(pPath[0], &st0); err != nil {
+ t.Fatal(err)
+ }
for i := range pSegments {
- t.Logf("pSegments[%d]: %s", i, pSegments[i])
+ var st syscall.Stat_t
+ if err := syscall.Stat(pPath[i], &st); err != nil {
+ t.Fatal(err)
+ }
+ // Sometimes the size on disk decreases by 4k due to less fragmentation
+ if st.Blocks != st0.Blocks && st.Blocks != st0.Blocks-8 {
+ t.Errorf("size changed: st0.Blocks=%d st%d.Blocks=%d", st0.Blocks, i, st.Blocks)
+ }
+ }
+
+ // Check that hole/data segments stays the same across copies
+ out := ""
+ same := true
+ for i := range pSegments {
+ out += fmt.Sprintf("pSegments[%d]:\n%s\n", i, pSegments[i])
if i < len(pSegments)-1 {
if pSegments[i+1] != pSegments[i] {
- t.Errorf("error: this is different than pSegments[%d]!", i+1)
+ same = false
+ t.Errorf("error: pSegments[%d] is different than pSegments[%d]!", i, i+1)
}
}
}
- t.Log("------------------------------------")
+ out += "------------------------------------\n"
for i := range cSegments {
- t.Logf("cSegments[%d]: %s", i, cSegments[i])
+ out += fmt.Sprintf("cSegments[%d]:\n%s\n", i, cSegments[i])
if i < len(pSegments)-1 {
if cSegments[i+1] != cSegments[i] {
- t.Errorf("error: this is different than cSegments[%d]!", i+1)
+ same = false
+ t.Errorf("error: cSegments[%d] is different than cSegments[%d]!", i, i+1)
}
}
}
+ if !same {
+ t.Log(out)
+ }
+}
+
+// TestFileHoleCopy creates a sparse times, copies it a few times, and check if
+// the copies are the same (including the location of holes and data sections).
+//
+// The test runs with -plaintextnames because that makes it easier to manipulate
+// cipherdir directly.
+func TestFileHoleCopy(t *testing.T) {
+ // | hole | x | hole | x | hole |
+ // truncate -s 50000 foo && dd if=/dev/zero of=foo bs=1 seek=10000 count=1 conv=notrunc && dd if=/dev/zero of=foo bs=1 seek=30000 count=1 conv=notrunc
+ name := "c0"
+ c0 := []int64{10000, 30000}
+ if !t.Run("c0", func(t *testing.T) { doTestFileHoleCopy(t, name, c0) }) {
+ t.Log("aborting further subtests")
+ return
+ }
+
+ rand.Seed(time.Now().UnixNano())
+ for k := 0; k < 100; k++ {
+ c1 := make([]int64, 10)
+ for i := range c1 {
+ c1[i] = int64(rand.Int31n(60000))
+ }
+ name := fmt.Sprintf("k%d", k)
+ if !t.Run(name, func(t *testing.T) { doTestFileHoleCopy(t, name, c1) }) {
+ t.Log("aborting further subtests")
+ return
+ }
+ }
}