]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-mount-util.c
util-lib: use trailing slash in chase_symlinks, fd_is_mount_point, path_is_mount_point
[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 == -EOPNOTSUPP) { /* kernel or file system too old? */
84 log_debug("%s doesn't support mount IDs\n", p);
85 continue;
86 }
87 if (IN_SET(r, -EACCES, -EPERM)) {
88 log_debug("Can't access %s\n", p);
89 continue;
90 }
91
92 log_debug("mnt id of %s is %i\n", p, mnt_id2);
93
94 if (mnt_id == mnt_id2)
95 continue;
96
97 /* The ids don't match? If so, then there are two mounts on the same path, let's check if that's really
98 * the case */
99 assert_se(path_equal_ptr(hashmap_get(h, INT_TO_PTR(mnt_id2)), p));
100 }
101
102 hashmap_free_free(h);
103 }
104
105 static void test_path_is_mount_point(void) {
106 int fd;
107 char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
108 _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
109 _cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
110 _cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
111
112 assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0);
113 assert_se(path_is_mount_point("/", NULL, 0) > 0);
114 assert_se(path_is_mount_point("//", NULL, AT_SYMLINK_FOLLOW) > 0);
115 assert_se(path_is_mount_point("//", NULL, 0) > 0);
116
117 assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0);
118 assert_se(path_is_mount_point("/proc", NULL, 0) > 0);
119 assert_se(path_is_mount_point("/proc/", NULL, AT_SYMLINK_FOLLOW) > 0);
120 assert_se(path_is_mount_point("/proc/", NULL, 0) > 0);
121
122 assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0);
123 assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0);
124 assert_se(path_is_mount_point("/proc/1/", NULL, AT_SYMLINK_FOLLOW) == 0);
125 assert_se(path_is_mount_point("/proc/1/", NULL, 0) == 0);
126
127 assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
128 assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
129 assert_se(path_is_mount_point("/sys/", NULL, AT_SYMLINK_FOLLOW) > 0);
130 assert_se(path_is_mount_point("/sys/", NULL, 0) > 0);
131
132 /* we'll create a hierarchy of different kinds of dir/file/link
133 * layouts:
134 *
135 * <tmp>/file1, <tmp>/file2
136 * <tmp>/link1 -> file1, <tmp>/link2 -> file2
137 * <tmp>/dir1/
138 * <tmp>/dir1/file
139 * <tmp>/dirlink1 -> dir1
140 * <tmp>/dirlink1file -> dirlink1/file
141 * <tmp>/dir2/
142 * <tmp>/dir2/file
143 */
144
145 /* file mountpoints */
146 assert_se(mkdtemp(tmp_dir) != NULL);
147 file1 = path_join(NULL, tmp_dir, "file1");
148 assert_se(file1);
149 file2 = path_join(NULL, tmp_dir, "file2");
150 assert_se(file2);
151 fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
152 assert_se(fd > 0);
153 close(fd);
154 fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
155 assert_se(fd > 0);
156 close(fd);
157 link1 = path_join(NULL, tmp_dir, "link1");
158 assert_se(link1);
159 assert_se(symlink("file1", link1) == 0);
160 link2 = path_join(NULL, tmp_dir, "link2");
161 assert_se(link1);
162 assert_se(symlink("file2", link2) == 0);
163
164 assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0);
165 assert_se(path_is_mount_point(file1, NULL, 0) == 0);
166 assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0);
167 assert_se(path_is_mount_point(link1, NULL, 0) == 0);
168
169 /* directory mountpoints */
170 dir1 = path_join(NULL, tmp_dir, "dir1");
171 assert_se(dir1);
172 assert_se(mkdir(dir1, 0755) == 0);
173 dirlink1 = path_join(NULL, tmp_dir, "dirlink1");
174 assert_se(dirlink1);
175 assert_se(symlink("dir1", dirlink1) == 0);
176 dirlink1file = path_join(NULL, tmp_dir, "dirlink1file");
177 assert_se(dirlink1file);
178 assert_se(symlink("dirlink1/file", dirlink1file) == 0);
179 dir2 = path_join(NULL, tmp_dir, "dir2");
180 assert_se(dir2);
181 assert_se(mkdir(dir2, 0755) == 0);
182
183 assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0);
184 assert_se(path_is_mount_point(dir1, NULL, 0) == 0);
185 assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0);
186 assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0);
187
188 /* file in subdirectory mountpoints */
189 dir1file = path_join(NULL, dir1, "file");
190 assert_se(dir1file);
191 fd = open(dir1file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
192 assert_se(fd > 0);
193 close(fd);
194
195 assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0);
196 assert_se(path_is_mount_point(dir1file, NULL, 0) == 0);
197 assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0);
198 assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0);
199
200 /* these tests will only work as root */
201 if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
202 int rf, rt, rdf, rdt, rlf, rlt, rl1f, rl1t;
203 const char *file2d;
204
205 /* files */
206 /* capture results in vars, to avoid dangling mounts on failure */
207 log_info("%s: %s", __func__, file2);
208 rf = path_is_mount_point(file2, NULL, 0);
209 rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW);
210
211 file2d = strjoina(file2, "/");
212 log_info("%s: %s", __func__, file2d);
213 rdf = path_is_mount_point(file2d, NULL, 0);
214 rdt = path_is_mount_point(file2d, NULL, AT_SYMLINK_FOLLOW);
215
216 log_info("%s: %s", __func__, link2);
217 rlf = path_is_mount_point(link2, NULL, 0);
218 rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW);
219
220 assert_se(umount(file2) == 0);
221
222 assert_se(rf == 1);
223 assert_se(rt == 1);
224 assert_se(rdf == -ENOTDIR);
225 assert_se(rdt == -ENOTDIR);
226 assert_se(rlf == 0);
227 assert_se(rlt == 1);
228
229 /* dirs */
230 dir2file = path_join(NULL, dir2, "file");
231 assert_se(dir2file);
232 fd = open(dir2file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
233 assert_se(fd > 0);
234 close(fd);
235
236 assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0);
237
238 log_info("%s: %s", __func__, dir1);
239 rf = path_is_mount_point(dir1, NULL, 0);
240 rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW);
241 log_info("%s: %s", __func__, dirlink1);
242 rlf = path_is_mount_point(dirlink1, NULL, 0);
243 rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW);
244 log_info("%s: %s", __func__, dirlink1file);
245 /* its parent is a mount point, but not /file itself */
246 rl1f = path_is_mount_point(dirlink1file, NULL, 0);
247 rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW);
248
249 assert_se(umount(dir1) == 0);
250
251 assert_se(rf == 1);
252 assert_se(rt == 1);
253 assert_se(rlf == 0);
254 assert_se(rlt == 1);
255 assert_se(rl1f == 0);
256 assert_se(rl1t == 0);
257
258 } else
259 printf("Skipping bind mount file test: %m\n");
260
261 assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
262 }
263
264 int main(int argc, char *argv[]) {
265
266 log_set_max_level(LOG_DEBUG);
267
268 test_mount_propagation_flags("shared", 0, MS_SHARED);
269 test_mount_propagation_flags("slave", 0, MS_SLAVE);
270 test_mount_propagation_flags("private", 0, MS_PRIVATE);
271 test_mount_propagation_flags(NULL, 0, 0);
272 test_mount_propagation_flags("", 0, 0);
273 test_mount_propagation_flags("xxxx", -EINVAL, 0);
274 test_mount_propagation_flags(" ", -EINVAL, 0);
275
276 test_mnt_id();
277 test_path_is_mount_point();
278
279 return 0;
280 }