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