]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-mount-util.c
Merge pull request #8399 from keszybz/systemctl-kexec
[thirdparty/systemd.git] / src / test / test-mount-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2016 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <sys/mount.h>
22
23 #include "alloc-util.h"
24 #include "def.h"
25 #include "fd-util.h"
26 #include "fileio.h"
27 #include "hashmap.h"
28 #include "log.h"
29 #include "log.h"
30 #include "mount-util.h"
31 #include "path-util.h"
32 #include "rm-rf.h"
33 #include "string-util.h"
34
35 static void test_mount_propagation_flags(const char *name, int ret, unsigned long expected) {
36 long unsigned flags;
37
38 assert_se(mount_propagation_flags_from_string(name, &flags) == ret);
39
40 if (ret >= 0) {
41 const char *c;
42
43 assert_se(flags == expected);
44
45 c = mount_propagation_flags_to_string(flags);
46 if (isempty(name))
47 assert_se(isempty(c));
48 else
49 assert_se(streq(c, name));
50 }
51 }
52
53 static void test_mnt_id(void) {
54 _cleanup_fclose_ FILE *f = NULL;
55 Hashmap *h;
56 Iterator i;
57 char *p;
58 void *k;
59 int r;
60
61 assert_se(f = fopen("/proc/self/mountinfo", "re"));
62 assert_se(h = hashmap_new(&trivial_hash_ops));
63
64 for (;;) {
65 _cleanup_free_ char *line = NULL, *path = NULL;
66 int mnt_id;
67
68 r = read_line(f, LONG_LINE_MAX, &line);
69 if (r == 0)
70 break;
71 assert_se(r > 0);
72
73 assert_se(sscanf(line, "%i %*s %*s %*s %ms", &mnt_id, &path) == 2);
74
75 assert_se(hashmap_put(h, INT_TO_PTR(mnt_id), path) >= 0);
76 path = NULL;
77 }
78
79 HASHMAP_FOREACH_KEY(p, k, h, i) {
80 int mnt_id = PTR_TO_INT(k), mnt_id2;
81
82 r = path_get_mnt_id(p, &mnt_id2);
83 if (r < 0) {
84 log_debug_errno(r, "Failed to get the mnt id of %s: %m\n", p);
85 continue;
86 }
87
88 log_debug("mnt id of %s is %i\n", p, mnt_id2);
89
90 if (mnt_id == mnt_id2)
91 continue;
92
93 /* The ids don't match? If so, then there are two mounts on the same path, let's check if that's really
94 * the case */
95 assert_se(path_equal_ptr(hashmap_get(h, INT_TO_PTR(mnt_id2)), p));
96 }
97
98 hashmap_free_free(h);
99 }
100
101 static void test_path_is_mount_point(void) {
102 int fd;
103 char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
104 _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
105 _cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
106 _cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
107
108 assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0);
109 assert_se(path_is_mount_point("/", NULL, 0) > 0);
110 assert_se(path_is_mount_point("//", NULL, AT_SYMLINK_FOLLOW) > 0);
111 assert_se(path_is_mount_point("//", NULL, 0) > 0);
112
113 assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0);
114 assert_se(path_is_mount_point("/proc", NULL, 0) > 0);
115 assert_se(path_is_mount_point("/proc/", NULL, AT_SYMLINK_FOLLOW) > 0);
116 assert_se(path_is_mount_point("/proc/", NULL, 0) > 0);
117
118 assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0);
119 assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0);
120 assert_se(path_is_mount_point("/proc/1/", NULL, AT_SYMLINK_FOLLOW) == 0);
121 assert_se(path_is_mount_point("/proc/1/", NULL, 0) == 0);
122
123 assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
124 assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
125 assert_se(path_is_mount_point("/sys/", NULL, AT_SYMLINK_FOLLOW) > 0);
126 assert_se(path_is_mount_point("/sys/", NULL, 0) > 0);
127
128 /* we'll create a hierarchy of different kinds of dir/file/link
129 * layouts:
130 *
131 * <tmp>/file1, <tmp>/file2
132 * <tmp>/link1 -> file1, <tmp>/link2 -> file2
133 * <tmp>/dir1/
134 * <tmp>/dir1/file
135 * <tmp>/dirlink1 -> dir1
136 * <tmp>/dirlink1file -> dirlink1/file
137 * <tmp>/dir2/
138 * <tmp>/dir2/file
139 */
140
141 /* file mountpoints */
142 assert_se(mkdtemp(tmp_dir) != NULL);
143 file1 = path_join(NULL, tmp_dir, "file1");
144 assert_se(file1);
145 file2 = path_join(NULL, tmp_dir, "file2");
146 assert_se(file2);
147 fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
148 assert_se(fd > 0);
149 close(fd);
150 fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
151 assert_se(fd > 0);
152 close(fd);
153 link1 = path_join(NULL, tmp_dir, "link1");
154 assert_se(link1);
155 assert_se(symlink("file1", link1) == 0);
156 link2 = path_join(NULL, tmp_dir, "link2");
157 assert_se(link1);
158 assert_se(symlink("file2", link2) == 0);
159
160 assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0);
161 assert_se(path_is_mount_point(file1, NULL, 0) == 0);
162 assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0);
163 assert_se(path_is_mount_point(link1, NULL, 0) == 0);
164
165 /* directory mountpoints */
166 dir1 = path_join(NULL, tmp_dir, "dir1");
167 assert_se(dir1);
168 assert_se(mkdir(dir1, 0755) == 0);
169 dirlink1 = path_join(NULL, tmp_dir, "dirlink1");
170 assert_se(dirlink1);
171 assert_se(symlink("dir1", dirlink1) == 0);
172 dirlink1file = path_join(NULL, tmp_dir, "dirlink1file");
173 assert_se(dirlink1file);
174 assert_se(symlink("dirlink1/file", dirlink1file) == 0);
175 dir2 = path_join(NULL, tmp_dir, "dir2");
176 assert_se(dir2);
177 assert_se(mkdir(dir2, 0755) == 0);
178
179 assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0);
180 assert_se(path_is_mount_point(dir1, NULL, 0) == 0);
181 assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0);
182 assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0);
183
184 /* file in subdirectory mountpoints */
185 dir1file = path_join(NULL, dir1, "file");
186 assert_se(dir1file);
187 fd = open(dir1file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
188 assert_se(fd > 0);
189 close(fd);
190
191 assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0);
192 assert_se(path_is_mount_point(dir1file, NULL, 0) == 0);
193 assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0);
194 assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0);
195
196 /* these tests will only work as root */
197 if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
198 int rf, rt, rdf, rdt, rlf, rlt, rl1f, rl1t;
199 const char *file2d;
200
201 /* files */
202 /* capture results in vars, to avoid dangling mounts on failure */
203 log_info("%s: %s", __func__, file2);
204 rf = path_is_mount_point(file2, NULL, 0);
205 rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW);
206
207 file2d = strjoina(file2, "/");
208 log_info("%s: %s", __func__, file2d);
209 rdf = path_is_mount_point(file2d, NULL, 0);
210 rdt = path_is_mount_point(file2d, NULL, AT_SYMLINK_FOLLOW);
211
212 log_info("%s: %s", __func__, link2);
213 rlf = path_is_mount_point(link2, NULL, 0);
214 rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW);
215
216 assert_se(umount(file2) == 0);
217
218 assert_se(rf == 1);
219 assert_se(rt == 1);
220 assert_se(rdf == -ENOTDIR);
221 assert_se(rdt == -ENOTDIR);
222 assert_se(rlf == 0);
223 assert_se(rlt == 1);
224
225 /* dirs */
226 dir2file = path_join(NULL, dir2, "file");
227 assert_se(dir2file);
228 fd = open(dir2file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
229 assert_se(fd > 0);
230 close(fd);
231
232 assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0);
233
234 log_info("%s: %s", __func__, dir1);
235 rf = path_is_mount_point(dir1, NULL, 0);
236 rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW);
237 log_info("%s: %s", __func__, dirlink1);
238 rlf = path_is_mount_point(dirlink1, NULL, 0);
239 rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW);
240 log_info("%s: %s", __func__, dirlink1file);
241 /* its parent is a mount point, but not /file itself */
242 rl1f = path_is_mount_point(dirlink1file, NULL, 0);
243 rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW);
244
245 assert_se(umount(dir1) == 0);
246
247 assert_se(rf == 1);
248 assert_se(rt == 1);
249 assert_se(rlf == 0);
250 assert_se(rlt == 1);
251 assert_se(rl1f == 0);
252 assert_se(rl1t == 0);
253
254 } else
255 printf("Skipping bind mount file test: %m\n");
256
257 assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
258 }
259
260 static void test_mount_option_mangle(void) {
261 char *opts = NULL;
262 unsigned long f;
263
264 assert_se(mount_option_mangle(NULL, MS_RDONLY|MS_NOSUID, &f, &opts) == 0);
265 assert_se(f == (MS_RDONLY|MS_NOSUID));
266 assert_se(opts == NULL);
267
268 assert_se(mount_option_mangle("", MS_RDONLY|MS_NOSUID, &f, &opts) == 0);
269 assert_se(f == (MS_RDONLY|MS_NOSUID));
270 assert_se(opts == NULL);
271
272 assert_se(mount_option_mangle("ro,nosuid,nodev,noexec", 0, &f, &opts) == 0);
273 assert_se(f == (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC));
274 assert_se(opts == NULL);
275
276 assert_se(mount_option_mangle("ro,nosuid,nodev,noexec,mode=755", 0, &f, &opts) == 0);
277 assert_se(f == (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC));
278 assert_se(streq(opts, "mode=755"));
279 opts = mfree(opts);
280
281 assert_se(mount_option_mangle("rw,nosuid,foo,hogehoge,nodev,mode=755", 0, &f, &opts) == 0);
282 assert_se(f == (MS_NOSUID|MS_NODEV));
283 assert_se(streq(opts, "foo,hogehoge,mode=755"));
284 opts = mfree(opts);
285
286 assert_se(mount_option_mangle("rw,nosuid,nodev,noexec,relatime,net_cls,net_prio", MS_RDONLY, &f, &opts) == 0);
287 assert_se(f == (MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME));
288 assert_se(streq(opts, "net_cls,net_prio"));
289 opts = mfree(opts);
290
291 assert_se(mount_option_mangle("rw,nosuid,nodev,relatime,size=1630748k,mode=700,uid=1000,gid=1000", MS_RDONLY, &f, &opts) == 0);
292 assert_se(f == (MS_NOSUID|MS_NODEV|MS_RELATIME));
293 assert_se(streq(opts, "size=1630748k,mode=700,uid=1000,gid=1000"));
294 opts = mfree(opts);
295
296 assert_se(mount_option_mangle("size=1630748k,rw,gid=1000,,,nodev,relatime,,mode=700,nosuid,uid=1000", MS_RDONLY, &f, &opts) == 0);
297 assert_se(f == (MS_NOSUID|MS_NODEV|MS_RELATIME));
298 assert_se(streq(opts, "size=1630748k,gid=1000,mode=700,uid=1000"));
299 opts = mfree(opts);
300
301 assert_se(mount_option_mangle("rw,exec,size=8143984k,nr_inodes=2035996,mode=755", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, &f, &opts) == 0);
302 assert_se(f == (MS_NOSUID|MS_NODEV));
303 assert_se(streq(opts, "size=8143984k,nr_inodes=2035996,mode=755"));
304 opts = mfree(opts);
305
306 assert_se(mount_option_mangle("rw,relatime,fmask=0022,,,dmask=0022", MS_RDONLY, &f, &opts) == 0);
307 assert_se(f == MS_RELATIME);
308 assert_se(streq(opts, "fmask=0022,dmask=0022"));
309 opts = mfree(opts);
310
311 assert_se(mount_option_mangle("rw,relatime,fmask=0022,dmask=0022,\"hogehoge", MS_RDONLY, &f, &opts) < 0);
312 }
313
314 int main(int argc, char *argv[]) {
315
316 log_set_max_level(LOG_DEBUG);
317
318 test_mount_propagation_flags("shared", 0, MS_SHARED);
319 test_mount_propagation_flags("slave", 0, MS_SLAVE);
320 test_mount_propagation_flags("private", 0, MS_PRIVATE);
321 test_mount_propagation_flags(NULL, 0, 0);
322 test_mount_propagation_flags("", 0, 0);
323 test_mount_propagation_flags("xxxx", -EINVAL, 0);
324 test_mount_propagation_flags(" ", -EINVAL, 0);
325
326 test_mnt_id();
327 test_path_is_mount_point();
328 test_mount_option_mangle();
329
330 return 0;
331 }