blob: d02c2dd0e1f902dd7fe4c8d58774c44bee318a87 [file] [log] [blame]
// Copyright 2020 The Monogon Project Authors.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package erofs
import (
"io"
"io/ioutil"
"log"
"math/rand"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
)
func TestKernelInterop(t *testing.T) {
if os.Getenv("IN_KTEST") != "true" {
t.Skip("Not in ktest")
}
type testCase struct {
name string
setup func(w *Writer) error
validate func(t *testing.T) error
}
tests := []testCase{
{
name: "SimpleFolder",
setup: func(w *Writer) error {
return w.Create(".", &Directory{
Base: Base{GID: 123, UID: 124, Permissions: 0753},
Children: []string{},
})
},
validate: func(t *testing.T) error {
var stat unix.Stat_t
if err := unix.Stat("/test", &stat); err != nil {
t.Errorf("failed to stat output: %v", err)
}
require.EqualValues(t, 124, stat.Uid, "wrong Uid")
require.EqualValues(t, 123, stat.Gid, "wrong Gid")
require.EqualValues(t, 0753, stat.Mode&^unix.S_IFMT, "wrong mode")
return nil
},
},
{
name: "FolderHierarchy",
setup: func(w *Writer) error {
if err := w.Create(".", &Directory{
Base: Base{GID: 123, UID: 124, Permissions: 0753},
Children: []string{"subdir"},
}); err != nil {
return err
}
if err := w.Create("subdir", &Directory{
Base: Base{GID: 123, UID: 124, Permissions: 0753},
Children: []string{},
}); err != nil {
return err
}
return nil
},
validate: func(t *testing.T) error {
dirInfo, err := ioutil.ReadDir("/test")
if err != nil {
t.Fatalf("Failed to read top-level directory: %v", err)
}
require.Len(t, dirInfo, 1, "more subdirs than expected")
require.Equal(t, "subdir", dirInfo[0].Name(), "unexpected subdir")
require.True(t, dirInfo[0].IsDir(), "subdir not a directory")
subdirInfo, err := ioutil.ReadDir("/test/subdir")
assert.NoError(t, err, "cannot read empty subdir")
require.Len(t, subdirInfo, 0, "unexpected subdirs in empty directory")
return nil
},
},
{
name: "SmallFile",
setup: func(w *Writer) error {
if err := w.Create(".", &Directory{
Base: Base{GID: 123, UID: 123, Permissions: 0755},
Children: []string{"test.bin"},
}); err != nil {
return err
}
writer := w.CreateFile("test.bin", &FileMeta{
Base: Base{GID: 123, UID: 124, Permissions: 0644},
})
r := rand.New(rand.NewSource(0)) // Random but deterministic data
if _, err := io.CopyN(writer, r, 128); err != nil {
return err
}
if err := writer.Close(); err != nil {
return err
}
return nil
},
validate: func(t *testing.T) error {
var stat unix.Stat_t
err := unix.Stat("/test/test.bin", &stat)
assert.NoError(t, err, "failed to stat file")
require.EqualValues(t, 124, stat.Uid, "wrong Uid")
require.EqualValues(t, 123, stat.Gid, "wrong Gid")
require.EqualValues(t, 0644, stat.Mode&^unix.S_IFMT, "wrong mode")
file, err := os.Open("/test/test.bin")
assert.NoError(t, err, "failed to open test file")
defer file.Close()
r := io.LimitReader(rand.New(rand.NewSource(0)), 128) // Random but deterministic data
expected, _ := ioutil.ReadAll(r)
actual, err := ioutil.ReadAll(file)
assert.NoError(t, err, "failed to read test file")
assert.Equal(t, expected, actual, "content not identical")
return nil
},
},
{
name: "LargeFile",
setup: func(w *Writer) error {
if err := w.Create(".", &Directory{
Base: Base{GID: 123, UID: 123, Permissions: 0755},
Children: []string{"test.bin"},
}); err != nil {
return err
}
writer := w.CreateFile("test.bin", &FileMeta{
Base: Base{GID: 123, UID: 124, Permissions: 0644},
})
r := rand.New(rand.NewSource(1)) // Random but deterministic data
if _, err := io.CopyN(writer, r, 6500); err != nil {
return err
}
if err := writer.Close(); err != nil {
return err
}
return nil
},
validate: func(t *testing.T) error {
var stat unix.Stat_t
rawContents, err := ioutil.ReadFile("/dev/ram0")
assert.NoError(t, err, "failed to read test data")
log.Printf("%x", rawContents)
err = unix.Stat("/test/test.bin", &stat)
assert.NoError(t, err, "failed to stat file")
require.EqualValues(t, 124, stat.Uid, "wrong Uid")
require.EqualValues(t, 123, stat.Gid, "wrong Gid")
require.EqualValues(t, 0644, stat.Mode&^unix.S_IFMT, "wrong mode")
require.EqualValues(t, 6500, stat.Size, "wrong size")
file, err := os.Open("/test/test.bin")
assert.NoError(t, err, "failed to open test file")
defer file.Close()
r := io.LimitReader(rand.New(rand.NewSource(1)), 6500) // Random but deterministic data
expected, _ := ioutil.ReadAll(r)
actual, err := ioutil.ReadAll(file)
assert.NoError(t, err, "failed to read test file")
assert.Equal(t, expected, actual, "content not identical")
return nil
},
},
{
name: "MultipleMetaBlocks",
setup: func(w *Writer) error {
testFileNames := []string{"test1.bin", "test2.bin", "test3.bin"}
if err := w.Create(".", &Directory{
Base: Base{GID: 123, UID: 123, Permissions: 0755},
Children: testFileNames,
}); err != nil {
return err
}
for i, fileName := range testFileNames {
writer := w.CreateFile(fileName, &FileMeta{
Base: Base{GID: 123, UID: 124, Permissions: 0644},
})
r := rand.New(rand.NewSource(int64(i))) // Random but deterministic data
if _, err := io.CopyN(writer, r, 2053); err != nil {
return err
}
if err := writer.Close(); err != nil {
return err
}
}
return nil
},
validate: func(t *testing.T) error {
testFileNames := []string{"test1.bin", "test2.bin", "test3.bin"}
for i, fileName := range testFileNames {
file, err := os.Open("/test/" + fileName)
assert.NoError(t, err, "failed to open test file")
defer file.Close()
r := io.LimitReader(rand.New(rand.NewSource(int64(i))), 2053) // Random but deterministic data
expected, _ := ioutil.ReadAll(r)
actual, err := ioutil.ReadAll(file)
assert.NoError(t, err, "failed to read test file")
require.Equal(t, expected, actual, "content not identical")
}
return nil
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
file, err := os.OpenFile("/dev/ram0", os.O_WRONLY, 0644)
if err != nil {
t.Fatalf("failed to create test image: %v", err)
}
defer file.Close()
w, err := NewWriter(file)
if err != nil {
t.Fatalf("failed to initialize EROFS writer: %v", err)
}
if err := test.setup(w); err != nil {
t.Fatalf("setup failed: %v", err)
}
if err := w.Close(); err != nil {
t.Errorf("failed close: %v", err)
}
_ = file.Close()
if err := os.MkdirAll("/test", 0755); err != nil {
t.Error(err)
}
if err := unix.Mount("/dev/ram0", "/test", "erofs", unix.MS_NOEXEC|unix.MS_NODEV, ""); err != nil {
t.Fatal(err)
}
if err := test.validate(t); err != nil {
t.Errorf("validation failure: %v", err)
}
if err := unix.Unmount("/test", 0); err != nil {
t.Fatalf("failed to unmount: %v", err)
}
})
}
}