186 lines · c
1// SPDX-License-Identifier: GPL-2.02#define _GNU_SOURCE3#define __SANE_USERSPACE_TYPES__ // Use ll644 5#include <inttypes.h>6#include <unistd.h>7#include <stdio.h>8 9#include <linux/unistd.h>10#include <linux/types.h>11#include <linux/mount.h>12#include <sys/syscall.h>13#include <sys/stat.h>14#include <sys/mman.h>15#include <sched.h>16#include <fcntl.h>17 18#include "../../kselftest.h"19#include "log.h"20 21static int sys_fsopen(const char *fsname, unsigned int flags)22{23 return syscall(__NR_fsopen, fsname, flags);24}25 26static int sys_fsconfig(int fd, unsigned int cmd, const char *key, const char *value, int aux)27{28 return syscall(__NR_fsconfig, fd, cmd, key, value, aux);29}30 31static int sys_fsmount(int fd, unsigned int flags, unsigned int attr_flags)32{33 return syscall(__NR_fsmount, fd, flags, attr_flags);34}35static int sys_mount(const char *src, const char *tgt, const char *fst,36 unsigned long flags, const void *data)37{38 return syscall(__NR_mount, src, tgt, fst, flags, data);39}40static int sys_move_mount(int from_dfd, const char *from_pathname,41 int to_dfd, const char *to_pathname,42 unsigned int flags)43{44 return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags);45}46 47static long get_file_dev_and_inode(void *addr, struct statx *stx)48{49 char buf[4096];50 FILE *mapf;51 52 mapf = fopen("/proc/self/maps", "r");53 if (mapf == NULL)54 return pr_perror("fopen(/proc/self/maps)");55 56 while (fgets(buf, sizeof(buf), mapf)) {57 unsigned long start, end;58 uint32_t maj, min;59 __u64 ino;60 61 if (sscanf(buf, "%lx-%lx %*s %*s %x:%x %llu",62 &start, &end, &maj, &min, &ino) != 5)63 return pr_perror("unable to parse: %s", buf);64 if (start == (unsigned long)addr) {65 stx->stx_dev_major = maj;66 stx->stx_dev_minor = min;67 stx->stx_ino = ino;68 return 0;69 }70 }71 72 return pr_err("unable to find the mapping");73}74 75static int ovl_mount(void)76{77 int tmpfs, fsfd, ovl;78 79 fsfd = sys_fsopen("tmpfs", 0);80 if (fsfd == -1)81 return pr_perror("fsopen(tmpfs)");82 83 if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) == -1)84 return pr_perror("FSCONFIG_CMD_CREATE");85 86 tmpfs = sys_fsmount(fsfd, 0, 0);87 if (tmpfs == -1)88 return pr_perror("fsmount");89 90 close(fsfd);91 92 /* overlayfs can't be constructed on top of a detached mount. */93 if (sys_move_mount(tmpfs, "", AT_FDCWD, "/tmp", MOVE_MOUNT_F_EMPTY_PATH))94 return pr_perror("move_mount");95 close(tmpfs);96 97 if (mkdir("/tmp/w", 0755) == -1 ||98 mkdir("/tmp/u", 0755) == -1 ||99 mkdir("/tmp/l", 0755) == -1)100 return pr_perror("mkdir");101 102 fsfd = sys_fsopen("overlay", 0);103 if (fsfd == -1)104 return pr_perror("fsopen(overlay)");105 if (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "test", 0) == -1 ||106 sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "lowerdir", "/tmp/l", 0) == -1 ||107 sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "upperdir", "/tmp/u", 0) == -1 ||108 sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "workdir", "/tmp/w", 0) == -1)109 return pr_perror("fsconfig");110 if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) == -1)111 return pr_perror("fsconfig");112 ovl = sys_fsmount(fsfd, 0, 0);113 if (ovl == -1)114 return pr_perror("fsmount");115 116 return ovl;117}118 119/*120 * Check that the file device and inode shown in /proc/pid/maps match values121 * returned by stat(2).122 */123static int test(void)124{125 struct statx stx, mstx;126 int ovl, fd;127 void *addr;128 129 ovl = ovl_mount();130 if (ovl == -1)131 return -1;132 133 fd = openat(ovl, "test", O_RDWR | O_CREAT, 0644);134 if (fd == -1)135 return pr_perror("openat");136 137 addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0);138 if (addr == MAP_FAILED)139 return pr_perror("mmap");140 141 if (get_file_dev_and_inode(addr, &mstx))142 return -1;143 if (statx(fd, "", AT_EMPTY_PATH | AT_STATX_SYNC_AS_STAT, STATX_INO, &stx))144 return pr_perror("statx");145 146 if (stx.stx_dev_major != mstx.stx_dev_major ||147 stx.stx_dev_minor != mstx.stx_dev_minor ||148 stx.stx_ino != mstx.stx_ino)149 return pr_fail("unmatched dev:ino %x:%x:%llx (expected %x:%x:%llx)\n",150 mstx.stx_dev_major, mstx.stx_dev_minor, mstx.stx_ino,151 stx.stx_dev_major, stx.stx_dev_minor, stx.stx_ino);152 153 ksft_test_result_pass("devices are matched\n");154 return 0;155}156 157int main(int argc, char **argv)158{159 int fsfd;160 161 fsfd = sys_fsopen("overlay", 0);162 if (fsfd == -1) {163 ksft_test_result_skip("unable to create overlay mount\n");164 return 1;165 }166 close(fsfd);167 168 /* Create a new mount namespace to not care about cleaning test mounts. */169 if (unshare(CLONE_NEWNS) == -1) {170 ksft_test_result_skip("unable to create a new mount namespace\n");171 return 1;172 }173 if (sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) == -1) {174 pr_perror("mount");175 return 1;176 }177 178 ksft_set_plan(1);179 180 if (test())181 return 1;182 183 ksft_exit_pass();184 return 0;185}186