mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 14:53:58 -04:00
empty_mntns.h includes ../statmount/statmount.h which provides a
4-argument statmount_alloc(mnt_id, mnt_ns_id, mask, flags), but then
redefines its own 3-argument version without the flags parameter. This
causes a build failure due to conflicting types.
Remove the duplicate definition from empty_mntns.h and update all
callers to pass 0 for the flags argument.
Fixes: 32f54f2bbc ("selftests/filesystems: add tests for empty mount namespaces")
Signed-off-by: Christian Brauner <brauner@kernel.org>
226 lines
5.4 KiB
C
226 lines
5.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Test: rootfs overmounted multiple times with chroot into topmost
|
|
*
|
|
* This test creates a scenario where:
|
|
* 1. A new mount namespace is created with a tmpfs root (via pivot_root)
|
|
* 2. A mountpoint is created and overmounted multiple times
|
|
* 3. The caller chroots into the topmost mount layer
|
|
*
|
|
* The test verifies that:
|
|
* - Multiple overmounts create separate mount layers
|
|
* - Each layer's files are isolated
|
|
* - chroot correctly sets the process's root to the topmost layer
|
|
* - After chroot, only the topmost layer's files are visible
|
|
*
|
|
* Copyright (c) 2024 Christian Brauner <brauner@kernel.org>
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <fcntl.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/stat.h>
|
|
#include <sched.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
#include "../utils.h"
|
|
#include "empty_mntns.h"
|
|
#include "kselftest_harness.h"
|
|
|
|
#define NR_OVERMOUNTS 5
|
|
|
|
/*
|
|
* Setup a proper root filesystem using pivot_root.
|
|
* This ensures we own the root directory in our user namespace.
|
|
*/
|
|
static int setup_root(void)
|
|
{
|
|
char tmpdir[] = "/tmp/overmount_test.XXXXXX";
|
|
char oldroot[256];
|
|
|
|
if (!mkdtemp(tmpdir))
|
|
return -1;
|
|
|
|
/* Mount tmpfs at the temporary directory */
|
|
if (mount("tmpfs", tmpdir, "tmpfs", 0, "size=10M"))
|
|
return -1;
|
|
|
|
/* Create directory for old root */
|
|
snprintf(oldroot, sizeof(oldroot), "%s/oldroot", tmpdir);
|
|
if (mkdir(oldroot, 0755))
|
|
return -1;
|
|
|
|
/* pivot_root to use the tmpfs as new root */
|
|
if (syscall(SYS_pivot_root, tmpdir, oldroot))
|
|
return -1;
|
|
|
|
if (chdir("/"))
|
|
return -1;
|
|
|
|
/* Unmount old root */
|
|
if (umount2("/oldroot", MNT_DETACH))
|
|
return -1;
|
|
|
|
/* Remove oldroot directory */
|
|
if (rmdir("/oldroot"))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Test scenario:
|
|
* 1. Enter a user namespace to gain CAP_SYS_ADMIN
|
|
* 2. Create a new mount namespace
|
|
* 3. Setup a tmpfs root via pivot_root
|
|
* 4. Create a mountpoint /newroot and overmount it multiple times
|
|
* 5. Create a marker file in each layer
|
|
* 6. Chroot into /newroot (the topmost overmount)
|
|
* 7. Verify we're in the topmost layer (only topmost marker visible)
|
|
*/
|
|
TEST(overmount_chroot)
|
|
{
|
|
pid_t pid;
|
|
|
|
pid = fork();
|
|
ASSERT_GE(pid, 0);
|
|
|
|
if (pid == 0) {
|
|
ssize_t nr_mounts;
|
|
uint64_t mnt_ids[NR_OVERMOUNTS + 1];
|
|
uint64_t root_id_before, root_id_after;
|
|
struct statmount *sm;
|
|
char marker[64];
|
|
int fd, i;
|
|
|
|
/* Step 1: Enter user namespace for privileges */
|
|
if (enter_userns())
|
|
_exit(1);
|
|
|
|
/* Step 2: Create a new mount namespace */
|
|
if (unshare(CLONE_NEWNS))
|
|
_exit(2);
|
|
|
|
/* Step 3: Make the mount tree private */
|
|
if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL))
|
|
_exit(3);
|
|
|
|
/* Step 4: Setup a proper tmpfs root via pivot_root */
|
|
if (setup_root())
|
|
_exit(4);
|
|
|
|
/* Create the base mount point for overmounting */
|
|
if (mkdir("/newroot", 0755))
|
|
_exit(5);
|
|
|
|
/* Mount base tmpfs on /newroot */
|
|
if (mount("tmpfs", "/newroot", "tmpfs", 0, "size=1M"))
|
|
_exit(6);
|
|
|
|
/* Record base mount ID */
|
|
mnt_ids[0] = get_unique_mnt_id("/newroot");
|
|
if (!mnt_ids[0])
|
|
_exit(7);
|
|
|
|
/* Create marker in base layer */
|
|
fd = open("/newroot/layer_0", O_CREAT | O_RDWR, 0644);
|
|
if (fd < 0)
|
|
_exit(8);
|
|
if (write(fd, "layer_0", 7) != 7) {
|
|
close(fd);
|
|
_exit(9);
|
|
}
|
|
close(fd);
|
|
|
|
/* Step 5: Overmount /newroot multiple times with tmpfs */
|
|
for (i = 0; i < NR_OVERMOUNTS; i++) {
|
|
if (mount("tmpfs", "/newroot", "tmpfs", 0, "size=1M"))
|
|
_exit(10);
|
|
|
|
/* Record mount ID for this layer */
|
|
mnt_ids[i + 1] = get_unique_mnt_id("/newroot");
|
|
if (!mnt_ids[i + 1])
|
|
_exit(11);
|
|
|
|
/* Create a marker file in each layer */
|
|
snprintf(marker, sizeof(marker), "/newroot/layer_%d", i + 1);
|
|
fd = open(marker, O_CREAT | O_RDWR, 0644);
|
|
if (fd < 0)
|
|
_exit(12);
|
|
|
|
if (write(fd, marker, strlen(marker)) != (ssize_t)strlen(marker)) {
|
|
close(fd);
|
|
_exit(13);
|
|
}
|
|
close(fd);
|
|
}
|
|
|
|
/* Verify mount count increased */
|
|
nr_mounts = count_mounts();
|
|
if (nr_mounts < NR_OVERMOUNTS + 2)
|
|
_exit(14);
|
|
|
|
/* Record root mount ID before chroot */
|
|
root_id_before = get_unique_mnt_id("/newroot");
|
|
|
|
/* Verify this is the topmost layer's mount */
|
|
if (root_id_before != mnt_ids[NR_OVERMOUNTS])
|
|
_exit(15);
|
|
|
|
/* Step 6: Chroot into /newroot (the topmost overmount) */
|
|
if (chroot("/newroot"))
|
|
_exit(16);
|
|
|
|
/* Change to root directory within the chroot */
|
|
if (chdir("/"))
|
|
_exit(17);
|
|
|
|
/* Step 7: Verify we're in the topmost layer */
|
|
root_id_after = get_unique_mnt_id("/");
|
|
|
|
/* The mount ID should be the same as the topmost layer */
|
|
if (root_id_after != mnt_ids[NR_OVERMOUNTS])
|
|
_exit(18);
|
|
|
|
/* Verify the topmost layer's marker file exists */
|
|
snprintf(marker, sizeof(marker), "/layer_%d", NR_OVERMOUNTS);
|
|
if (access(marker, F_OK))
|
|
_exit(19);
|
|
|
|
/* Verify we cannot see markers from lower layers (they're hidden) */
|
|
for (i = 0; i < NR_OVERMOUNTS; i++) {
|
|
snprintf(marker, sizeof(marker), "/layer_%d", i);
|
|
if (access(marker, F_OK) == 0)
|
|
_exit(20);
|
|
}
|
|
|
|
/* Verify the root mount is tmpfs */
|
|
sm = statmount_alloc(root_id_after, 0,
|
|
STATMOUNT_MNT_BASIC | STATMOUNT_MNT_ROOT |
|
|
STATMOUNT_MNT_POINT | STATMOUNT_FS_TYPE, 0);
|
|
if (!sm)
|
|
_exit(21);
|
|
|
|
if (sm->mask & STATMOUNT_FS_TYPE) {
|
|
if (strcmp(sm->str + sm->fs_type, "tmpfs") != 0) {
|
|
free(sm);
|
|
_exit(22);
|
|
}
|
|
}
|
|
|
|
free(sm);
|
|
_exit(0);
|
|
}
|
|
|
|
ASSERT_EQ(wait_for_pid(pid), 0);
|
|
}
|
|
|
|
TEST_HARNESS_MAIN
|