blob: fa5f2481a57bd86ff040cca442c8fa57ab4260a0 [file] [log] [blame]
Lorenz Brun378a4452021-01-26 13:47:41 +01001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package erofs
18
19import (
20 "io"
Lorenz Brun378a4452021-01-26 13:47:41 +010021 "log"
22 "math/rand"
23 "os"
24 "testing"
25
26 "github.com/stretchr/testify/assert"
27 "github.com/stretchr/testify/require"
28 "golang.org/x/sys/unix"
29)
30
31func TestKernelInterop(t *testing.T) {
32 if os.Getenv("IN_KTEST") != "true" {
33 t.Skip("Not in ktest")
34 }
35
36 type testCase struct {
37 name string
38 setup func(w *Writer) error
39 validate func(t *testing.T) error
40 }
41
42 tests := []testCase{
43 {
44 name: "SimpleFolder",
45 setup: func(w *Writer) error {
46 return w.Create(".", &Directory{
47 Base: Base{GID: 123, UID: 124, Permissions: 0753},
48 Children: []string{},
49 })
50 },
51 validate: func(t *testing.T) error {
52 var stat unix.Stat_t
53 if err := unix.Stat("/test", &stat); err != nil {
54 t.Errorf("failed to stat output: %v", err)
55 }
56 require.EqualValues(t, 124, stat.Uid, "wrong Uid")
57 require.EqualValues(t, 123, stat.Gid, "wrong Gid")
58 require.EqualValues(t, 0753, stat.Mode&^unix.S_IFMT, "wrong mode")
59 return nil
60 },
61 },
62 {
63 name: "FolderHierarchy",
64 setup: func(w *Writer) error {
65 if err := w.Create(".", &Directory{
66 Base: Base{GID: 123, UID: 124, Permissions: 0753},
67 Children: []string{"subdir"},
68 }); err != nil {
69 return err
70 }
71 if err := w.Create("subdir", &Directory{
72 Base: Base{GID: 123, UID: 124, Permissions: 0753},
73 Children: []string{},
74 }); err != nil {
75 return err
76 }
77 return nil
78 },
79 validate: func(t *testing.T) error {
Lorenz Brun764a2de2021-11-22 16:26:36 +010080 dirInfo, err := os.ReadDir("/test")
Lorenz Brun378a4452021-01-26 13:47:41 +010081 if err != nil {
82 t.Fatalf("Failed to read top-level directory: %v", err)
83 }
84 require.Len(t, dirInfo, 1, "more subdirs than expected")
85 require.Equal(t, "subdir", dirInfo[0].Name(), "unexpected subdir")
86 require.True(t, dirInfo[0].IsDir(), "subdir not a directory")
Lorenz Brun764a2de2021-11-22 16:26:36 +010087 subdirInfo, err := os.ReadDir("/test/subdir")
Lorenz Brun378a4452021-01-26 13:47:41 +010088 assert.NoError(t, err, "cannot read empty subdir")
89 require.Len(t, subdirInfo, 0, "unexpected subdirs in empty directory")
90 return nil
91 },
92 },
93 {
94 name: "SmallFile",
95 setup: func(w *Writer) error {
96 if err := w.Create(".", &Directory{
97 Base: Base{GID: 123, UID: 123, Permissions: 0755},
98 Children: []string{"test.bin"},
99 }); err != nil {
100 return err
101 }
102 writer := w.CreateFile("test.bin", &FileMeta{
103 Base: Base{GID: 123, UID: 124, Permissions: 0644},
104 })
105 r := rand.New(rand.NewSource(0)) // Random but deterministic data
106 if _, err := io.CopyN(writer, r, 128); err != nil {
107 return err
108 }
109 if err := writer.Close(); err != nil {
110 return err
111 }
112 return nil
113 },
114 validate: func(t *testing.T) error {
115 var stat unix.Stat_t
116 err := unix.Stat("/test/test.bin", &stat)
117 assert.NoError(t, err, "failed to stat file")
118 require.EqualValues(t, 124, stat.Uid, "wrong Uid")
119 require.EqualValues(t, 123, stat.Gid, "wrong Gid")
120 require.EqualValues(t, 0644, stat.Mode&^unix.S_IFMT, "wrong mode")
121 file, err := os.Open("/test/test.bin")
122 assert.NoError(t, err, "failed to open test file")
123 defer file.Close()
124 r := io.LimitReader(rand.New(rand.NewSource(0)), 128) // Random but deterministic data
Lorenz Brun764a2de2021-11-22 16:26:36 +0100125 expected, _ := io.ReadAll(r)
126 actual, err := io.ReadAll(file)
Lorenz Brun378a4452021-01-26 13:47:41 +0100127 assert.NoError(t, err, "failed to read test file")
128 assert.Equal(t, expected, actual, "content not identical")
129 return nil
130 },
131 },
132 {
Serge Bazanski8ca9c292022-04-04 16:29:26 +0200133 name: "Chardev",
134 setup: func(w *Writer) error {
135 if err := w.Create(".", &Directory{
136 Base: Base{GID: 123, UID: 123, Permissions: 0755},
137 Children: []string{"ttyS0"},
138 }); err != nil {
139 return err
140 }
141 err := w.Create("ttyS0", &CharacterDevice{
142 Base: Base{GID: 0, UID: 0, Permissions: 0600},
143 Major: 4,
144 Minor: 64,
145 })
146 if err != nil {
147 return err
148 }
149 return nil
150 },
151 validate: func(t *testing.T) error {
152 var stat unix.Statx_t
153 err := unix.Statx(0, "/test/ttyS0", 0, unix.STATX_ALL, &stat)
154 assert.NoError(t, err, "failed to statx file")
155 require.EqualValues(t, 0, stat.Uid, "wrong Uid")
156 require.EqualValues(t, 0, stat.Gid, "wrong Gid")
157 require.EqualValues(t, 0600, stat.Mode&^unix.S_IFMT, "wrong mode")
158 require.EqualValues(t, unix.S_IFCHR, stat.Mode&unix.S_IFMT, "wrong file type")
159 require.EqualValues(t, 4, stat.Rdev_major, "wrong dev major")
160 require.EqualValues(t, 64, stat.Rdev_minor, "wrong dev minor")
161 return nil
162 },
163 },
164 {
Lorenz Brun378a4452021-01-26 13:47:41 +0100165 name: "LargeFile",
166 setup: func(w *Writer) error {
167 if err := w.Create(".", &Directory{
168 Base: Base{GID: 123, UID: 123, Permissions: 0755},
169 Children: []string{"test.bin"},
170 }); err != nil {
171 return err
172 }
173 writer := w.CreateFile("test.bin", &FileMeta{
174 Base: Base{GID: 123, UID: 124, Permissions: 0644},
175 })
176 r := rand.New(rand.NewSource(1)) // Random but deterministic data
177 if _, err := io.CopyN(writer, r, 6500); err != nil {
178 return err
179 }
180 if err := writer.Close(); err != nil {
181 return err
182 }
183 return nil
184 },
185 validate: func(t *testing.T) error {
186 var stat unix.Stat_t
Lorenz Brun764a2de2021-11-22 16:26:36 +0100187 rawContents, err := os.ReadFile("/dev/ram0")
Lorenz Brun378a4452021-01-26 13:47:41 +0100188 assert.NoError(t, err, "failed to read test data")
189 log.Printf("%x", rawContents)
190 err = unix.Stat("/test/test.bin", &stat)
191 assert.NoError(t, err, "failed to stat file")
192 require.EqualValues(t, 124, stat.Uid, "wrong Uid")
193 require.EqualValues(t, 123, stat.Gid, "wrong Gid")
194 require.EqualValues(t, 0644, stat.Mode&^unix.S_IFMT, "wrong mode")
195 require.EqualValues(t, 6500, stat.Size, "wrong size")
196 file, err := os.Open("/test/test.bin")
197 assert.NoError(t, err, "failed to open test file")
198 defer file.Close()
199 r := io.LimitReader(rand.New(rand.NewSource(1)), 6500) // Random but deterministic data
Lorenz Brun764a2de2021-11-22 16:26:36 +0100200 expected, _ := io.ReadAll(r)
201 actual, err := io.ReadAll(file)
Lorenz Brun378a4452021-01-26 13:47:41 +0100202 assert.NoError(t, err, "failed to read test file")
203 assert.Equal(t, expected, actual, "content not identical")
204 return nil
205 },
206 },
207 {
208 name: "MultipleMetaBlocks",
209 setup: func(w *Writer) error {
210 testFileNames := []string{"test1.bin", "test2.bin", "test3.bin"}
211 if err := w.Create(".", &Directory{
212 Base: Base{GID: 123, UID: 123, Permissions: 0755},
213 Children: testFileNames,
214 }); err != nil {
215 return err
216 }
217 for i, fileName := range testFileNames {
218 writer := w.CreateFile(fileName, &FileMeta{
219 Base: Base{GID: 123, UID: 124, Permissions: 0644},
220 })
221 r := rand.New(rand.NewSource(int64(i))) // Random but deterministic data
222 if _, err := io.CopyN(writer, r, 2053); err != nil {
223 return err
224 }
225 if err := writer.Close(); err != nil {
226 return err
227 }
228 }
229 return nil
230 },
231 validate: func(t *testing.T) error {
232 testFileNames := []string{"test1.bin", "test2.bin", "test3.bin"}
233 for i, fileName := range testFileNames {
234 file, err := os.Open("/test/" + fileName)
235 assert.NoError(t, err, "failed to open test file")
236 defer file.Close()
237 r := io.LimitReader(rand.New(rand.NewSource(int64(i))), 2053) // Random but deterministic data
Lorenz Brun764a2de2021-11-22 16:26:36 +0100238 expected, _ := io.ReadAll(r)
239 actual, err := io.ReadAll(file)
Lorenz Brun378a4452021-01-26 13:47:41 +0100240 assert.NoError(t, err, "failed to read test file")
241 require.Equal(t, expected, actual, "content not identical")
242 }
243 return nil
244 },
245 },
246 }
247
248 for _, test := range tests {
249 t.Run(test.name, func(t *testing.T) {
250 file, err := os.OpenFile("/dev/ram0", os.O_WRONLY, 0644)
251 if err != nil {
252 t.Fatalf("failed to create test image: %v", err)
253 }
254 defer file.Close()
255 w, err := NewWriter(file)
256 if err != nil {
257 t.Fatalf("failed to initialize EROFS writer: %v", err)
258 }
259 if err := test.setup(w); err != nil {
260 t.Fatalf("setup failed: %v", err)
261 }
262 if err := w.Close(); err != nil {
263 t.Errorf("failed close: %v", err)
264 }
265 _ = file.Close()
266 if err := os.MkdirAll("/test", 0755); err != nil {
267 t.Error(err)
268 }
269 if err := unix.Mount("/dev/ram0", "/test", "erofs", unix.MS_NOEXEC|unix.MS_NODEV, ""); err != nil {
270 t.Fatal(err)
271 }
272 if err := test.validate(t); err != nil {
273 t.Errorf("validation failure: %v", err)
274 }
275 if err := unix.Unmount("/test", 0); err != nil {
276 t.Fatalf("failed to unmount: %v", err)
277 }
278 })
279
280 }
281}