blob: d02c2dd0e1f902dd7fe4c8d58774c44bee318a87 [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"
21 "io/ioutil"
22 "log"
23 "math/rand"
24 "os"
25 "testing"
26
27 "github.com/stretchr/testify/assert"
28 "github.com/stretchr/testify/require"
29 "golang.org/x/sys/unix"
30)
31
32func TestKernelInterop(t *testing.T) {
33 if os.Getenv("IN_KTEST") != "true" {
34 t.Skip("Not in ktest")
35 }
36
37 type testCase struct {
38 name string
39 setup func(w *Writer) error
40 validate func(t *testing.T) error
41 }
42
43 tests := []testCase{
44 {
45 name: "SimpleFolder",
46 setup: func(w *Writer) error {
47 return w.Create(".", &Directory{
48 Base: Base{GID: 123, UID: 124, Permissions: 0753},
49 Children: []string{},
50 })
51 },
52 validate: func(t *testing.T) error {
53 var stat unix.Stat_t
54 if err := unix.Stat("/test", &stat); err != nil {
55 t.Errorf("failed to stat output: %v", err)
56 }
57 require.EqualValues(t, 124, stat.Uid, "wrong Uid")
58 require.EqualValues(t, 123, stat.Gid, "wrong Gid")
59 require.EqualValues(t, 0753, stat.Mode&^unix.S_IFMT, "wrong mode")
60 return nil
61 },
62 },
63 {
64 name: "FolderHierarchy",
65 setup: func(w *Writer) error {
66 if err := w.Create(".", &Directory{
67 Base: Base{GID: 123, UID: 124, Permissions: 0753},
68 Children: []string{"subdir"},
69 }); err != nil {
70 return err
71 }
72 if err := w.Create("subdir", &Directory{
73 Base: Base{GID: 123, UID: 124, Permissions: 0753},
74 Children: []string{},
75 }); err != nil {
76 return err
77 }
78 return nil
79 },
80 validate: func(t *testing.T) error {
81 dirInfo, err := ioutil.ReadDir("/test")
82 if err != nil {
83 t.Fatalf("Failed to read top-level directory: %v", err)
84 }
85 require.Len(t, dirInfo, 1, "more subdirs than expected")
86 require.Equal(t, "subdir", dirInfo[0].Name(), "unexpected subdir")
87 require.True(t, dirInfo[0].IsDir(), "subdir not a directory")
88 subdirInfo, err := ioutil.ReadDir("/test/subdir")
89 assert.NoError(t, err, "cannot read empty subdir")
90 require.Len(t, subdirInfo, 0, "unexpected subdirs in empty directory")
91 return nil
92 },
93 },
94 {
95 name: "SmallFile",
96 setup: func(w *Writer) error {
97 if err := w.Create(".", &Directory{
98 Base: Base{GID: 123, UID: 123, Permissions: 0755},
99 Children: []string{"test.bin"},
100 }); err != nil {
101 return err
102 }
103 writer := w.CreateFile("test.bin", &FileMeta{
104 Base: Base{GID: 123, UID: 124, Permissions: 0644},
105 })
106 r := rand.New(rand.NewSource(0)) // Random but deterministic data
107 if _, err := io.CopyN(writer, r, 128); err != nil {
108 return err
109 }
110 if err := writer.Close(); err != nil {
111 return err
112 }
113 return nil
114 },
115 validate: func(t *testing.T) error {
116 var stat unix.Stat_t
117 err := unix.Stat("/test/test.bin", &stat)
118 assert.NoError(t, err, "failed to stat file")
119 require.EqualValues(t, 124, stat.Uid, "wrong Uid")
120 require.EqualValues(t, 123, stat.Gid, "wrong Gid")
121 require.EqualValues(t, 0644, stat.Mode&^unix.S_IFMT, "wrong mode")
122 file, err := os.Open("/test/test.bin")
123 assert.NoError(t, err, "failed to open test file")
124 defer file.Close()
125 r := io.LimitReader(rand.New(rand.NewSource(0)), 128) // Random but deterministic data
126 expected, _ := ioutil.ReadAll(r)
127 actual, err := ioutil.ReadAll(file)
128 assert.NoError(t, err, "failed to read test file")
129 assert.Equal(t, expected, actual, "content not identical")
130 return nil
131 },
132 },
133 {
134 name: "LargeFile",
135 setup: func(w *Writer) error {
136 if err := w.Create(".", &Directory{
137 Base: Base{GID: 123, UID: 123, Permissions: 0755},
138 Children: []string{"test.bin"},
139 }); err != nil {
140 return err
141 }
142 writer := w.CreateFile("test.bin", &FileMeta{
143 Base: Base{GID: 123, UID: 124, Permissions: 0644},
144 })
145 r := rand.New(rand.NewSource(1)) // Random but deterministic data
146 if _, err := io.CopyN(writer, r, 6500); err != nil {
147 return err
148 }
149 if err := writer.Close(); err != nil {
150 return err
151 }
152 return nil
153 },
154 validate: func(t *testing.T) error {
155 var stat unix.Stat_t
156 rawContents, err := ioutil.ReadFile("/dev/ram0")
157 assert.NoError(t, err, "failed to read test data")
158 log.Printf("%x", rawContents)
159 err = unix.Stat("/test/test.bin", &stat)
160 assert.NoError(t, err, "failed to stat file")
161 require.EqualValues(t, 124, stat.Uid, "wrong Uid")
162 require.EqualValues(t, 123, stat.Gid, "wrong Gid")
163 require.EqualValues(t, 0644, stat.Mode&^unix.S_IFMT, "wrong mode")
164 require.EqualValues(t, 6500, stat.Size, "wrong size")
165 file, err := os.Open("/test/test.bin")
166 assert.NoError(t, err, "failed to open test file")
167 defer file.Close()
168 r := io.LimitReader(rand.New(rand.NewSource(1)), 6500) // Random but deterministic data
169 expected, _ := ioutil.ReadAll(r)
170 actual, err := ioutil.ReadAll(file)
171 assert.NoError(t, err, "failed to read test file")
172 assert.Equal(t, expected, actual, "content not identical")
173 return nil
174 },
175 },
176 {
177 name: "MultipleMetaBlocks",
178 setup: func(w *Writer) error {
179 testFileNames := []string{"test1.bin", "test2.bin", "test3.bin"}
180 if err := w.Create(".", &Directory{
181 Base: Base{GID: 123, UID: 123, Permissions: 0755},
182 Children: testFileNames,
183 }); err != nil {
184 return err
185 }
186 for i, fileName := range testFileNames {
187 writer := w.CreateFile(fileName, &FileMeta{
188 Base: Base{GID: 123, UID: 124, Permissions: 0644},
189 })
190 r := rand.New(rand.NewSource(int64(i))) // Random but deterministic data
191 if _, err := io.CopyN(writer, r, 2053); err != nil {
192 return err
193 }
194 if err := writer.Close(); err != nil {
195 return err
196 }
197 }
198 return nil
199 },
200 validate: func(t *testing.T) error {
201 testFileNames := []string{"test1.bin", "test2.bin", "test3.bin"}
202 for i, fileName := range testFileNames {
203 file, err := os.Open("/test/" + fileName)
204 assert.NoError(t, err, "failed to open test file")
205 defer file.Close()
206 r := io.LimitReader(rand.New(rand.NewSource(int64(i))), 2053) // Random but deterministic data
207 expected, _ := ioutil.ReadAll(r)
208 actual, err := ioutil.ReadAll(file)
209 assert.NoError(t, err, "failed to read test file")
210 require.Equal(t, expected, actual, "content not identical")
211 }
212 return nil
213 },
214 },
215 }
216
217 for _, test := range tests {
218 t.Run(test.name, func(t *testing.T) {
219 file, err := os.OpenFile("/dev/ram0", os.O_WRONLY, 0644)
220 if err != nil {
221 t.Fatalf("failed to create test image: %v", err)
222 }
223 defer file.Close()
224 w, err := NewWriter(file)
225 if err != nil {
226 t.Fatalf("failed to initialize EROFS writer: %v", err)
227 }
228 if err := test.setup(w); err != nil {
229 t.Fatalf("setup failed: %v", err)
230 }
231 if err := w.Close(); err != nil {
232 t.Errorf("failed close: %v", err)
233 }
234 _ = file.Close()
235 if err := os.MkdirAll("/test", 0755); err != nil {
236 t.Error(err)
237 }
238 if err := unix.Mount("/dev/ram0", "/test", "erofs", unix.MS_NOEXEC|unix.MS_NODEV, ""); err != nil {
239 t.Fatal(err)
240 }
241 if err := test.validate(t); err != nil {
242 t.Errorf("validation failure: %v", err)
243 }
244 if err := unix.Unmount("/test", 0); err != nil {
245 t.Fatalf("failed to unmount: %v", err)
246 }
247 })
248
249 }
250}