// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: Copyright (c) 2024 Matthew Penner //go:build unix package ufs_test import ( "errors" "os" "path/filepath" "testing" "github.com/pterodactyl/wings/internal/ufs" ) type testUnixFS struct { *ufs.UnixFS TmpDir string Root string } func (fs *testUnixFS) Cleanup() { _ = fs.Close() _ = os.RemoveAll(fs.TmpDir) } func newTestUnixFS() (*testUnixFS, error) { tmpDir, err := os.MkdirTemp(os.TempDir(), "ufs") if err != nil { return nil, err } root := filepath.Join(tmpDir, "root") if err := os.Mkdir(root, 0o755); err != nil { return nil, err } // TODO: test both disabled and enabled. fs, err := ufs.NewUnixFS(root, false) if err != nil { return nil, err } tfs := &testUnixFS{ UnixFS: fs, TmpDir: tmpDir, Root: root, } return tfs, nil } func TestUnixFS_Remove(t *testing.T) { t.Parallel() fs, err := newTestUnixFS() if err != nil { t.Fatal(err) return } defer fs.Cleanup() t.Run("base directory", func(t *testing.T) { // Try to remove the base directory. if err := fs.Remove(""); !errors.Is(err, ufs.ErrBadPathResolution) { t.Errorf("expected an a bad path resolution error, but got: %v", err) return } }) t.Run("path traversal", func(t *testing.T) { // Try to remove the base directory. if err := fs.RemoveAll("../root"); !errors.Is(err, ufs.ErrBadPathResolution) { t.Errorf("expected an a bad path resolution error, but got: %v", err) return } }) } func TestUnixFS_RemoveAll(t *testing.T) { t.Parallel() fs, err := newTestUnixFS() if err != nil { t.Fatal(err) return } defer fs.Cleanup() t.Run("base directory", func(t *testing.T) { // Try to remove the base directory. if err := fs.RemoveAll(""); !errors.Is(err, ufs.ErrBadPathResolution) { t.Errorf("expected an a bad path resolution error, but got: %v", err) return } }) t.Run("path traversal", func(t *testing.T) { // Try to remove the base directory. if err := fs.RemoveAll("../root"); !errors.Is(err, ufs.ErrBadPathResolution) { t.Errorf("expected an a bad path resolution error, but got: %v", err) return } }) } func TestUnixFS_Rename(t *testing.T) { t.Parallel() fs, err := newTestUnixFS() if err != nil { t.Fatal(err) return } defer fs.Cleanup() t.Run("rename base directory", func(t *testing.T) { // Try to rename the base directory. if err := fs.Rename("", "yeet"); !errors.Is(err, ufs.ErrBadPathResolution) { t.Errorf("expected an a bad path resolution error, but got: %v", err) return } }) t.Run("rename over base directory", func(t *testing.T) { // Create a directory that we are going to try and move over top of the // existing base directory. if err := fs.Mkdir("overwrite_dir", 0o755); err != nil { t.Error(err) return } // Try to rename over the base directory. if err := fs.Rename("overwrite_dir", ""); !errors.Is(err, ufs.ErrBadPathResolution) { t.Errorf("expected an a bad path resolution error, but got: %v", err) return } }) t.Run("directory rename", func(t *testing.T) { // Create a directory to rename to something else. if err := fs.Mkdir("test_directory", 0o755); err != nil { t.Error(err) return } // Try to rename "test_directory" to "directory". if err := fs.Rename("test_directory", "directory"); err != nil { t.Errorf("expected no error, but got: %v", err) return } // Sanity check if _, err := os.Lstat(filepath.Join(fs.Root, "directory")); err != nil { t.Errorf("Lstat errored when performing sanity check: %v", err) return } }) t.Run("file rename", func(t *testing.T) { // Create a directory to rename to something else. if f, err := fs.Create("test_file"); err != nil { t.Error(err) return } else { _ = f.Close() } // Try to rename "test_file" to "file". if err := fs.Rename("test_file", "file"); err != nil { t.Errorf("expected no error, but got: %v", err) return } // Sanity check if _, err := os.Lstat(filepath.Join(fs.Root, "file")); err != nil { t.Errorf("Lstat errored when performing sanity check: %v", err) return } }) } func TestUnixFS_Touch(t *testing.T) { t.Parallel() fs, err := newTestUnixFS() if err != nil { t.Fatal(err) return } defer fs.Cleanup() t.Run("base directory", func(t *testing.T) { path := "i_touched_a_file" f, err := fs.Touch(path, ufs.O_RDWR, 0o644) if err != nil { t.Error(err) return } _ = f.Close() // Sanity check if _, err := os.Lstat(filepath.Join(fs.Root, path)); err != nil { t.Errorf("Lstat errored when performing sanity check: %v", err) return } }) t.Run("existing parent directory", func(t *testing.T) { dir := "some_parent_directory" if err := fs.Mkdir(dir, 0o755); err != nil { t.Errorf("error creating parent directory: %v", err) return } path := filepath.Join(dir, "i_touched_a_file") f, err := fs.Touch(path, ufs.O_RDWR, 0o644) if err != nil { t.Errorf("error touching file: %v", err) return } _ = f.Close() // Sanity check if _, err := os.Lstat(filepath.Join(fs.Root, path)); err != nil { t.Errorf("Lstat errored when performing sanity check: %v", err) return } }) t.Run("non-existent parent directory", func(t *testing.T) { path := "some_other_directory/i_touched_a_file" f, err := fs.Touch(path, ufs.O_RDWR, 0o644) if err != nil { t.Errorf("error touching file: %v", err) return } _ = f.Close() // Sanity check if _, err := os.Lstat(filepath.Join(fs.Root, path)); err != nil { t.Errorf("Lstat errored when performing sanity check: %v", err) return } }) t.Run("non-existent parent directories", func(t *testing.T) { path := "some_other_directory/some_directory/i_touched_a_file" f, err := fs.Touch(path, ufs.O_RDWR, 0o644) if err != nil { t.Errorf("error touching file: %v", err) return } _ = f.Close() // Sanity check if _, err := os.Lstat(filepath.Join(fs.Root, path)); err != nil { t.Errorf("Lstat errored when performing sanity check: %v", err) return } }) }