1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #define __SANE_USERSPACE_TYPES__ // Use ll64
4
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
sys_fsopen(const char * fsname,unsigned int flags)21 static int sys_fsopen(const char *fsname, unsigned int flags)
22 {
23 return syscall(__NR_fsopen, fsname, flags);
24 }
25
sys_fsconfig(int fd,unsigned int cmd,const char * key,const char * value,int aux)26 static 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
sys_fsmount(int fd,unsigned int flags,unsigned int attr_flags)31 static int sys_fsmount(int fd, unsigned int flags, unsigned int attr_flags)
32 {
33 return syscall(__NR_fsmount, fd, flags, attr_flags);
34 }
sys_mount(const char * src,const char * tgt,const char * fst,unsigned long flags,const void * data)35 static 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 }
sys_move_mount(int from_dfd,const char * from_pathname,int to_dfd,const char * to_pathname,unsigned int flags)40 static 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
get_file_dev_and_inode(void * addr,struct statx * stx)47 static 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
ovl_mount(void)75 static 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 values
121 * returned by stat(2).
122 */
test(void)123 static 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
main(int argc,char ** argv)157 int 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