blob: b61c1eebe9f3d1e83d1da75117291a3309f96f16 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Jan Schära6da1712024-08-21 15:12:11 +02004//go:build linux
5
6package blockdev
7
8import (
Jan Schäre0db72c2025-06-18 18:14:07 +00009 "io"
Jan Schära6da1712024-08-21 15:12:11 +020010 "os"
11 "testing"
12
13 "source.monogon.dev/osbase/loop"
14)
15
16const loopBlockSize = 1024
17const loopBlockCount = 8
18
19func TestLoopDevice(t *testing.T) {
20 if os.Getenv("IN_KTEST") != "true" {
21 t.Skip("Not in ktest")
22 }
23 underlying, err := os.CreateTemp("/tmp", "")
24 if err != nil {
25 t.Fatalf("CreateTemp failed: %v", err)
26 }
27 defer os.Remove(underlying.Name())
28
29 _, err = underlying.Write(make([]byte, loopBlockSize*loopBlockCount))
30 if err != nil {
31 t.Fatalf("Write failed: %v", err)
32 }
33
34 loopDev, err := loop.Create(underlying, loop.Config{
35 BlockSize: loopBlockSize,
36 })
37 if err != nil {
38 t.Fatalf("loop.Create failed: %v", err)
39 }
40 defer loopDev.Remove()
41
42 devPath, err := loopDev.DevPath()
43 if err != nil {
44 t.Fatalf("loopDev.DevPath failed: %v", err)
45 }
46
47 loopDev.Close()
48 blk, err := Open(devPath)
49 if err != nil {
50 t.Fatalf("Failed to open loop device: %v", err)
51 }
52 defer blk.Close()
53
54 ValidateBlockDev(t, blk, loopBlockCount, loopBlockSize, loopBlockSize)
55}
56
57const fileBlockSize = 1024
58const fileBlockCount = 8
59
60func TestFile(t *testing.T) {
61 if os.Getenv("IN_KTEST") != "true" {
62 t.Skip("Not in ktest")
63 }
64
65 blk, err := CreateFile("/tmp/testfile", fileBlockSize, fileBlockCount)
66 if err != nil {
67 t.Fatalf("Failed to create file: %v", err)
68 }
69 defer os.Remove("/tmp/testfile")
Jan Schäre0db72c2025-06-18 18:14:07 +000070 defer blk.Close()
Jan Schära6da1712024-08-21 15:12:11 +020071
72 ValidateBlockDev(t, blk, fileBlockCount, fileBlockSize, fileBlockSize)
Jan Schäre0db72c2025-06-18 18:14:07 +000073
74 // ReadFromAt
75 srcFile, err := os.Create("/tmp/copysrc")
76 if err != nil {
77 t.Fatalf("Failed to create source file: %v", err)
78 }
79 defer os.Remove("/tmp/copysrc")
80 defer srcFile.Close()
81 var size int64 = fileBlockSize * fileBlockCount
82 readFromAtTests := []struct {
83 name string
84 offset int64
85 data string
86 limit int64
87 ok bool
88 }{
89 {"empty start", 0, "", -1, true},
90 {"empty end", size, "", -1, true},
91 {"normal", 3, "abcdef", -1, true},
92 {"limited", 3, "abcdef", 4, true},
93 {"large limit", 3, "abcdef", size, true},
94 {"ends at the end", size - 4, "abcd", -1, true},
95 {"ends past the end", size - 4, "abcde", -1, false},
96 {"ends past the end with limit", size - 4, "abcde", 10, false},
97 {"offset negative", -1, "abc", -1, false},
98 {"starts at the end", size, "abc", -1, false},
99 {"starts past the end", size + 4, "abc", -1, false},
100 }
101 for _, tt := range readFromAtTests {
102 t.Run("readFromAt "+tt.name, func(t *testing.T) {
103 checkBlockDevOp(t, blk, func(content []byte) {
104 // Prepare source file
105 err = srcFile.Truncate(0)
106 if err != nil {
107 t.Fatalf("Failed to truncate source file: %v", err)
108 }
109 _, err = srcFile.WriteAt([]byte("123"+tt.data), 0)
110 if err != nil {
111 t.Fatalf("Failed to write source file: %v", err)
112 }
113 _, err = srcFile.Seek(3, io.SeekStart)
114 if err != nil {
115 t.Fatalf("Failed to seek source file: %v", err)
116 }
117
118 // Do ReadFromAt
119 r := io.Reader(srcFile)
120 lr := &io.LimitedReader{R: srcFile, N: tt.limit}
121 if tt.limit != -1 {
122 r = lr
123 }
124 n, err := blk.ReadFromAt(r, tt.offset)
125 if (err == nil) != tt.ok {
126 t.Errorf("expected error %v, got %v", tt.ok, err)
127 }
128 expectedN := 0
129 if tt.offset >= 0 && tt.offset < size {
130 c := content[tt.offset:]
131 if tt.limit != -1 && tt.limit < int64(len(c)) {
132 c = c[:tt.limit]
133 }
134 expectedN = copy(c, tt.data)
135 }
136 if n != int64(expectedN) {
137 t.Errorf("got n = %d, expected %d; err: %v", n, expectedN, err)
138 }
139
140 // Check new offset
141 newOffset, err := srcFile.Seek(0, io.SeekCurrent)
142 if err != nil {
143 t.Fatalf("Failed to get source file position: %v", err)
144 }
145 newOffset -= 3
146 minOffset := n
147 maxOffset := n
148 if !tt.ok {
149 maxOffset = int64(len(tt.data))
150 if tt.limit != -1 {
151 maxOffset = min(maxOffset, tt.limit)
152 }
153 }
154 if minOffset > newOffset || newOffset > maxOffset {
155 t.Errorf("Got newOffset = %d, expected between %d and %d", newOffset, minOffset, maxOffset)
156 }
157 remaining := tt.limit - newOffset
158 if tt.limit != -1 && lr.N != remaining {
159 t.Errorf("Got lr.N = %d, expected %d", lr.N, remaining)
160 }
161 })
162 })
163 }
Jan Schära6da1712024-08-21 15:12:11 +0200164}