diff --git a/test/initramfs/src/apps/Makefile b/test/initramfs/src/apps/Makefile index eef78efe3..c8b7f2d20 100644 --- a/test/initramfs/src/apps/Makefile +++ b/test/initramfs/src/apps/Makefile @@ -15,7 +15,6 @@ TEST_BUILD_DIR ?= $(INITRAMFS)/test TEST_APPS := \ alarm \ capability \ - chroot \ clone3 \ cpu_affinity \ devfs \ @@ -27,6 +26,7 @@ TEST_APPS := \ fdatasync \ file_io \ fork_c \ + fs_isolation \ getcpu \ getpid \ hello_pie \ diff --git a/test/initramfs/src/apps/chroot/Makefile b/test/initramfs/src/apps/fs_isolation/Makefile similarity index 100% rename from test/initramfs/src/apps/chroot/Makefile rename to test/initramfs/src/apps/fs_isolation/Makefile diff --git a/test/initramfs/src/apps/chroot/chroot_jail.c b/test/initramfs/src/apps/fs_isolation/chroot.c similarity index 100% rename from test/initramfs/src/apps/chroot/chroot_jail.c rename to test/initramfs/src/apps/fs_isolation/chroot.c diff --git a/test/initramfs/src/apps/fs_isolation/pivot_root.c b/test/initramfs/src/apps/fs_isolation/pivot_root.c new file mode 100644 index 000000000..02dfea461 --- /dev/null +++ b/test/initramfs/src/apps/fs_isolation/pivot_root.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: MPL-2.0 + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../test.h" + +// --- Test Configuration --- +#define CHROOT_DIR "/new_root" +#define PIVOT_TARGET_DIR "/second_root" +#define PUT_OLD_DIR_NAME "old_root" + +// Marker directories and files to verify the pivot operation. +// These will be backed by tmpfs to ensure they are on distinct filesystems. +#define OLD_ROOT_MARKER_MNT "/old_root_marker_mnt" +#define OLD_ROOT_MARKER_FILE OLD_ROOT_MARKER_MNT "/old.txt" + +#define NEW_ROOT_MARKER_MNT "/new_root_marker_mnt" +#define NEW_ROOT_MARKER_FILE NEW_ROOT_MARKER_MNT "/new.txt" + +// Helper to create a directory if it doesn't exist. +static void ensure_dir(const char *path) +{ + CHECK_WITH(mkdir(path, 0755), errno == 0 || errno == EEXIST); +} + +// Helper to create a marker file on a tmpfs mount. +static void create_marker(const char *mount_point, const char *file_path) +{ + ensure_dir(mount_point); + CHECK(mount("tmpfs", mount_point, "tmpfs", 0, "")); + int fd = CHECK(open(file_path, O_CREAT | O_WRONLY, 0644)); + CHECK(close(fd)); +} + +FN_TEST(pivot_root_test) +{ + // --- Phase 1: Setup a chroot environment with a nested bind mount --- + + TEST_SUCC(unshare(CLONE_NEWNS)); + ensure_dir(CHROOT_DIR); + TEST_SUCC(mount("/", CHROOT_DIR, NULL, MS_BIND | MS_REC, NULL)); + + char pivot_target_full_path[256]; + snprintf(pivot_target_full_path, sizeof(pivot_target_full_path), "%s%s", + CHROOT_DIR, PIVOT_TARGET_DIR); + ensure_dir(pivot_target_full_path); + + // Negative test: pivot_root should fail if the root mount is the root mount of rootfs. + TEST_ERRNO(syscall(SYS_pivot_root, pivot_target_full_path, + pivot_target_full_path), + EINVAL); + + TEST_SUCC(chroot(CHROOT_DIR)); + TEST_SUCC(chdir("/")); // Change to the `new_root` after chroot. + + // --- Phase 2: Prepare for and execute pivot_root --- + + TEST_SUCC(mount("/", PIVOT_TARGET_DIR, NULL, MS_BIND | MS_REC, NULL)); + + // Create the directory where the old root will be placed. + char put_old_full_path[256]; + snprintf(put_old_full_path, sizeof(put_old_full_path), "%s/%s", + PIVOT_TARGET_DIR, PUT_OLD_DIR_NAME); + TEST_SUCC(mkdir(put_old_full_path, 0755)); + // Mount a tmpfs on `put_old_full_path`. This makes it a different mount + // from `new_root`. + TEST_SUCC(mount("tmpfs", put_old_full_path, "tmpfs", 0, NULL)); + + // Create marker files on separate tmpfs mounts to verify the pivot. + create_marker(OLD_ROOT_MARKER_MNT, OLD_ROOT_MARKER_FILE); + + char new_root_marker_mnt_full_path[256]; + snprintf(new_root_marker_mnt_full_path, + sizeof(new_root_marker_mnt_full_path), "%s%s", + PIVOT_TARGET_DIR, NEW_ROOT_MARKER_MNT); + char new_root_marker_file_full_path[256]; + snprintf(new_root_marker_file_full_path, + sizeof(new_root_marker_file_full_path), "%s%s", + PIVOT_TARGET_DIR, NEW_ROOT_MARKER_FILE); + create_marker(new_root_marker_mnt_full_path, + new_root_marker_file_full_path); + + // --- Phase 3: Negative tests for pivot_root --- + + // pivot_root fails with ENOTDIR if the `new_root` or `put_old` is not a directory. + TEST_ERRNO(syscall(SYS_pivot_root, new_root_marker_file_full_path, + put_old_full_path), + ENOTDIR); + // pivot_root fails with EINVAL if the `put_old` is not underneath the `new_root`. + TEST_ERRNO(syscall(SYS_pivot_root, "./proc", put_old_full_path), + EINVAL); + // pivot_root fails with EINVAL if the `new_root` is not a mount root. + TEST_ERRNO(syscall(SYS_pivot_root, "./sys/fs", "./sys/fs"), EINVAL); + // pivot_root fails with EBUSY if the `new_root` is the current root. + TEST_ERRNO(syscall(SYS_pivot_root, ".", "./bin"), EBUSY); + + // --- Phase 4: Do pivot_root and verification --- + + // Perform the pivot_root operation. + TEST_SUCC(syscall(SYS_pivot_root, PIVOT_TARGET_DIR, put_old_full_path)); + + // After pivot, the cwd is changed to the `new_root`. + char cwd[1024]; + TEST_RES(syscall(SYS_getcwd, cwd, sizeof(cwd)), strcmp(cwd, "/") == 0); + + // Verify that the `new_root` is active by checking for its marker file. + TEST_SUCC(access(NEW_ROOT_MARKER_FILE, F_OK)); + + // Verify that the old root has been moved. + char old_marker_in_new_path[256]; + snprintf(old_marker_in_new_path, sizeof(old_marker_in_new_path), + "/%s%s", PUT_OLD_DIR_NAME, OLD_ROOT_MARKER_FILE); + TEST_SUCC(access(old_marker_in_new_path, F_OK)); + + // Verify that the old root marker is no longer at the root. + TEST_ERRNO(access(OLD_ROOT_MARKER_FILE, F_OK), ENOENT); + + // --- Phase 5: Cleanup --- + + char old_root_path[256]; + snprintf(old_root_path, sizeof(old_root_path), "/%s", PUT_OLD_DIR_NAME); + TEST_SUCC(umount2(old_root_path, MNT_DETACH)); + TEST_SUCC(umount(old_root_path)); + TEST_SUCC(rmdir(old_root_path)); +} +END_TEST() \ No newline at end of file diff --git a/test/initramfs/src/apps/scripts/process.sh b/test/initramfs/src/apps/scripts/process.sh index d347cb079..ab69bec47 100755 --- a/test/initramfs/src/apps/scripts/process.sh +++ b/test/initramfs/src/apps/scripts/process.sh @@ -10,7 +10,6 @@ cd ${SCRIPT_DIR}/.. echo "Start process test......" # These test programs are sorted by name. tests=" -chroot/chroot_jail clone3/clone_exit_signal clone3/clone_files clone3/clone_no_exit_signal @@ -26,6 +25,8 @@ exit/exit_procfs eventfd2/eventfd2 fork/fork fork_c/fork +fs_isolation/chroot +fs_isolation/pivot_root getcpu/getcpu getpid/getpid hello_pie/hello