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