blob: 7ab4f515de78268ca8f336861b875dd23decdb1c [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
Lorenz Brun378a4452021-01-26 13:47:41 +01002// SPDX-License-Identifier: Apache-2.0
Lorenz Brun378a4452021-01-26 13:47:41 +01003
4package erofs
5
6import (
7 "io"
Lorenz Brun378a4452021-01-26 13:47:41 +01008 "log"
9 "math/rand"
10 "os"
11 "testing"
12
13 "github.com/stretchr/testify/assert"
14 "github.com/stretchr/testify/require"
15 "golang.org/x/sys/unix"
16)
17
18func TestKernelInterop(t *testing.T) {
19 if os.Getenv("IN_KTEST") != "true" {
20 t.Skip("Not in ktest")
21 }
22
23 type testCase struct {
24 name string
25 setup func(w *Writer) error
26 validate func(t *testing.T) error
27 }
28
29 tests := []testCase{
30 {
31 name: "SimpleFolder",
32 setup: func(w *Writer) error {
33 return w.Create(".", &Directory{
34 Base: Base{GID: 123, UID: 124, Permissions: 0753},
35 Children: []string{},
36 })
37 },
38 validate: func(t *testing.T) error {
39 var stat unix.Stat_t
40 if err := unix.Stat("/test", &stat); err != nil {
41 t.Errorf("failed to stat output: %v", err)
42 }
43 require.EqualValues(t, 124, stat.Uid, "wrong Uid")
44 require.EqualValues(t, 123, stat.Gid, "wrong Gid")
45 require.EqualValues(t, 0753, stat.Mode&^unix.S_IFMT, "wrong mode")
46 return nil
47 },
48 },
49 {
50 name: "FolderHierarchy",
51 setup: func(w *Writer) error {
52 if err := w.Create(".", &Directory{
53 Base: Base{GID: 123, UID: 124, Permissions: 0753},
54 Children: []string{"subdir"},
55 }); err != nil {
56 return err
57 }
58 if err := w.Create("subdir", &Directory{
59 Base: Base{GID: 123, UID: 124, Permissions: 0753},
60 Children: []string{},
61 }); err != nil {
62 return err
63 }
64 return nil
65 },
66 validate: func(t *testing.T) error {
Lorenz Brun764a2de2021-11-22 16:26:36 +010067 dirInfo, err := os.ReadDir("/test")
Lorenz Brun378a4452021-01-26 13:47:41 +010068 if err != nil {
69 t.Fatalf("Failed to read top-level directory: %v", err)
70 }
71 require.Len(t, dirInfo, 1, "more subdirs than expected")
72 require.Equal(t, "subdir", dirInfo[0].Name(), "unexpected subdir")
73 require.True(t, dirInfo[0].IsDir(), "subdir not a directory")
Lorenz Brun764a2de2021-11-22 16:26:36 +010074 subdirInfo, err := os.ReadDir("/test/subdir")
Lorenz Brun378a4452021-01-26 13:47:41 +010075 assert.NoError(t, err, "cannot read empty subdir")
76 require.Len(t, subdirInfo, 0, "unexpected subdirs in empty directory")
77 return nil
78 },
79 },
80 {
81 name: "SmallFile",
82 setup: func(w *Writer) error {
83 if err := w.Create(".", &Directory{
84 Base: Base{GID: 123, UID: 123, Permissions: 0755},
85 Children: []string{"test.bin"},
86 }); err != nil {
87 return err
88 }
89 writer := w.CreateFile("test.bin", &FileMeta{
90 Base: Base{GID: 123, UID: 124, Permissions: 0644},
91 })
92 r := rand.New(rand.NewSource(0)) // Random but deterministic data
93 if _, err := io.CopyN(writer, r, 128); err != nil {
94 return err
95 }
96 if err := writer.Close(); err != nil {
97 return err
98 }
99 return nil
100 },
101 validate: func(t *testing.T) error {
102 var stat unix.Stat_t
103 err := unix.Stat("/test/test.bin", &stat)
104 assert.NoError(t, err, "failed to stat file")
105 require.EqualValues(t, 124, stat.Uid, "wrong Uid")
106 require.EqualValues(t, 123, stat.Gid, "wrong Gid")
107 require.EqualValues(t, 0644, stat.Mode&^unix.S_IFMT, "wrong mode")
108 file, err := os.Open("/test/test.bin")
109 assert.NoError(t, err, "failed to open test file")
110 defer file.Close()
111 r := io.LimitReader(rand.New(rand.NewSource(0)), 128) // Random but deterministic data
Lorenz Brun764a2de2021-11-22 16:26:36 +0100112 expected, _ := io.ReadAll(r)
113 actual, err := io.ReadAll(file)
Lorenz Brun378a4452021-01-26 13:47:41 +0100114 assert.NoError(t, err, "failed to read test file")
115 assert.Equal(t, expected, actual, "content not identical")
116 return nil
117 },
118 },
119 {
Serge Bazanski8ca9c292022-04-04 16:29:26 +0200120 name: "Chardev",
121 setup: func(w *Writer) error {
122 if err := w.Create(".", &Directory{
123 Base: Base{GID: 123, UID: 123, Permissions: 0755},
124 Children: []string{"ttyS0"},
125 }); err != nil {
126 return err
127 }
128 err := w.Create("ttyS0", &CharacterDevice{
129 Base: Base{GID: 0, UID: 0, Permissions: 0600},
130 Major: 4,
131 Minor: 64,
132 })
133 if err != nil {
134 return err
135 }
136 return nil
137 },
138 validate: func(t *testing.T) error {
139 var stat unix.Statx_t
140 err := unix.Statx(0, "/test/ttyS0", 0, unix.STATX_ALL, &stat)
141 assert.NoError(t, err, "failed to statx file")
142 require.EqualValues(t, 0, stat.Uid, "wrong Uid")
143 require.EqualValues(t, 0, stat.Gid, "wrong Gid")
144 require.EqualValues(t, 0600, stat.Mode&^unix.S_IFMT, "wrong mode")
145 require.EqualValues(t, unix.S_IFCHR, stat.Mode&unix.S_IFMT, "wrong file type")
146 require.EqualValues(t, 4, stat.Rdev_major, "wrong dev major")
147 require.EqualValues(t, 64, stat.Rdev_minor, "wrong dev minor")
148 return nil
149 },
150 },
151 {
Lorenz Brun378a4452021-01-26 13:47:41 +0100152 name: "LargeFile",
153 setup: func(w *Writer) error {
154 if err := w.Create(".", &Directory{
155 Base: Base{GID: 123, UID: 123, Permissions: 0755},
156 Children: []string{"test.bin"},
157 }); err != nil {
158 return err
159 }
160 writer := w.CreateFile("test.bin", &FileMeta{
161 Base: Base{GID: 123, UID: 124, Permissions: 0644},
162 })
163 r := rand.New(rand.NewSource(1)) // Random but deterministic data
164 if _, err := io.CopyN(writer, r, 6500); err != nil {
165 return err
166 }
167 if err := writer.Close(); err != nil {
168 return err
169 }
170 return nil
171 },
172 validate: func(t *testing.T) error {
173 var stat unix.Stat_t
Lorenz Brun764a2de2021-11-22 16:26:36 +0100174 rawContents, err := os.ReadFile("/dev/ram0")
Lorenz Brun378a4452021-01-26 13:47:41 +0100175 assert.NoError(t, err, "failed to read test data")
176 log.Printf("%x", rawContents)
177 err = unix.Stat("/test/test.bin", &stat)
178 assert.NoError(t, err, "failed to stat file")
179 require.EqualValues(t, 124, stat.Uid, "wrong Uid")
180 require.EqualValues(t, 123, stat.Gid, "wrong Gid")
181 require.EqualValues(t, 0644, stat.Mode&^unix.S_IFMT, "wrong mode")
182 require.EqualValues(t, 6500, stat.Size, "wrong size")
183 file, err := os.Open("/test/test.bin")
184 assert.NoError(t, err, "failed to open test file")
185 defer file.Close()
186 r := io.LimitReader(rand.New(rand.NewSource(1)), 6500) // Random but deterministic data
Lorenz Brun764a2de2021-11-22 16:26:36 +0100187 expected, _ := io.ReadAll(r)
188 actual, err := io.ReadAll(file)
Lorenz Brun378a4452021-01-26 13:47:41 +0100189 assert.NoError(t, err, "failed to read test file")
190 assert.Equal(t, expected, actual, "content not identical")
191 return nil
192 },
193 },
194 {
195 name: "MultipleMetaBlocks",
196 setup: func(w *Writer) error {
197 testFileNames := []string{"test1.bin", "test2.bin", "test3.bin"}
198 if err := w.Create(".", &Directory{
199 Base: Base{GID: 123, UID: 123, Permissions: 0755},
200 Children: testFileNames,
201 }); err != nil {
202 return err
203 }
204 for i, fileName := range testFileNames {
205 writer := w.CreateFile(fileName, &FileMeta{
206 Base: Base{GID: 123, UID: 124, Permissions: 0644},
207 })
208 r := rand.New(rand.NewSource(int64(i))) // Random but deterministic data
209 if _, err := io.CopyN(writer, r, 2053); err != nil {
210 return err
211 }
212 if err := writer.Close(); err != nil {
213 return err
214 }
215 }
216 return nil
217 },
218 validate: func(t *testing.T) error {
219 testFileNames := []string{"test1.bin", "test2.bin", "test3.bin"}
220 for i, fileName := range testFileNames {
221 file, err := os.Open("/test/" + fileName)
222 assert.NoError(t, err, "failed to open test file")
223 defer file.Close()
224 r := io.LimitReader(rand.New(rand.NewSource(int64(i))), 2053) // Random but deterministic data
Lorenz Brun764a2de2021-11-22 16:26:36 +0100225 expected, _ := io.ReadAll(r)
226 actual, err := io.ReadAll(file)
Lorenz Brun378a4452021-01-26 13:47:41 +0100227 assert.NoError(t, err, "failed to read test file")
228 require.Equal(t, expected, actual, "content not identical")
229 }
230 return nil
231 },
232 },
233 }
234
235 for _, test := range tests {
236 t.Run(test.name, func(t *testing.T) {
237 file, err := os.OpenFile("/dev/ram0", os.O_WRONLY, 0644)
238 if err != nil {
239 t.Fatalf("failed to create test image: %v", err)
240 }
241 defer file.Close()
242 w, err := NewWriter(file)
243 if err != nil {
244 t.Fatalf("failed to initialize EROFS writer: %v", err)
245 }
246 if err := test.setup(w); err != nil {
247 t.Fatalf("setup failed: %v", err)
248 }
249 if err := w.Close(); err != nil {
250 t.Errorf("failed close: %v", err)
251 }
252 _ = file.Close()
253 if err := os.MkdirAll("/test", 0755); err != nil {
Tim Windelschmidtd0d5d9d2025-03-26 22:07:11 +0100254 t.Fatal(err)
Lorenz Brun378a4452021-01-26 13:47:41 +0100255 }
256 if err := unix.Mount("/dev/ram0", "/test", "erofs", unix.MS_NOEXEC|unix.MS_NODEV, ""); err != nil {
257 t.Fatal(err)
258 }
259 if err := test.validate(t); err != nil {
260 t.Errorf("validation failure: %v", err)
261 }
262 if err := unix.Unmount("/test", 0); err != nil {
263 t.Fatalf("failed to unmount: %v", err)
264 }
265 })
266
267 }
268}