]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/mount-util.c
basic: turn off stdio locking for a couple of helper calls
[thirdparty/systemd.git] / src / basic / mount-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
4349cd7c
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 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
11c3a366 21#include <errno.h>
35bbbf85 22#include <stdio_ext.h>
11c3a366 23#include <stdlib.h>
4349cd7c
LP
24#include <string.h>
25#include <sys/mount.h>
11c3a366 26#include <sys/stat.h>
4349cd7c 27#include <sys/statvfs.h>
11c3a366 28#include <unistd.h>
4349cd7c 29
b5efdb8a 30#include "alloc-util.h"
4349cd7c
LP
31#include "escape.h"
32#include "fd-util.h"
33#include "fileio.h"
e1873695 34#include "fs-util.h"
93cc7779 35#include "hashmap.h"
4349cd7c
LP
36#include "mount-util.h"
37#include "parse-util.h"
38#include "path-util.h"
39#include "set.h"
15a5e950 40#include "stdio-util.h"
4349cd7c 41#include "string-util.h"
6b7c9f8b 42#include "strv.h"
4349cd7c 43
01a7e0a1
LP
44/* This is the original MAX_HANDLE_SZ definition from the kernel, when the API was introduced. We use that in place of
45 * any more currently defined value to future-proof things: if the size is increased in the API headers, and our code
46 * is recompiled then it would cease working on old kernels, as those refuse any sizes larger than this value with
47 * EINVAL right-away. Hence, let's disconnect ourselves from any such API changes, and stick to the original definition
48 * from when it was introduced. We use it as a start value only anyway (see below), and hence should be able to deal
49 * with large file handles anyway. */
50#define ORIGINAL_MAX_HANDLE_SZ 128
51
cbfb8679
LP
52int name_to_handle_at_loop(
53 int fd,
54 const char *path,
55 struct file_handle **ret_handle,
56 int *ret_mnt_id,
57 int flags) {
58
93719c6b 59 _cleanup_free_ struct file_handle *h = NULL;
01a7e0a1 60 size_t n = ORIGINAL_MAX_HANDLE_SZ;
cbfb8679
LP
61
62 /* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified
63 * buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a
64 * start value, it is not an upper bound on the buffer size required.
65 *
66 * This improves on raw name_to_handle_at() also in one other regard: ret_handle and ret_mnt_id can be passed
67 * as NULL if there's no interest in either. */
68
cbfb8679
LP
69 for (;;) {
70 int mnt_id = -1;
cbfb8679 71
93719c6b
LP
72 h = malloc0(offsetof(struct file_handle, f_handle) + n);
73 if (!h)
74 return -ENOMEM;
75
76 h->handle_bytes = n;
77
cbfb8679
LP
78 if (name_to_handle_at(fd, path, h, &mnt_id, flags) >= 0) {
79
80 if (ret_handle) {
81 *ret_handle = h;
82 h = NULL;
83 }
84
85 if (ret_mnt_id)
86 *ret_mnt_id = mnt_id;
87
88 return 0;
89 }
90 if (errno != EOVERFLOW)
91 return -errno;
92
93 if (!ret_handle && ret_mnt_id && mnt_id >= 0) {
94
fc010b01
LP
95 /* As it appears, name_to_handle_at() fills in mnt_id even when it returns EOVERFLOW when the
96 * buffer is too small, but that's undocumented. Hence, let's make use of this if it appears to
97 * be filled in, and the caller was interested in only the mount ID an nothing else. */
cbfb8679
LP
98
99 *ret_mnt_id = mnt_id;
100 return 0;
101 }
102
fc010b01
LP
103 /* If name_to_handle_at() didn't increase the byte size, then this EOVERFLOW is caused by something
104 * else (apparently EOVERFLOW is returned for untriggered nfs4 mounts sometimes), not by the too small
105 * buffer. In that case propagate EOVERFLOW */
106 if (h->handle_bytes <= n)
cbfb8679 107 return -EOVERFLOW;
fc010b01
LP
108
109 /* The buffer was too small. Size the new buffer by what name_to_handle_at() returned. */
110 n = h->handle_bytes;
111 if (offsetof(struct file_handle, f_handle) + n < n) /* check for addition overflow */
cbfb8679 112 return -EOVERFLOW;
cbfb8679 113
93719c6b 114 h = mfree(h);
cbfb8679
LP
115 }
116}
117
4349cd7c
LP
118static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
119 char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
120 _cleanup_free_ char *fdinfo = NULL;
121 _cleanup_close_ int subfd = -1;
122 char *p;
123 int r;
124
125 if ((flags & AT_EMPTY_PATH) && isempty(filename))
126 xsprintf(path, "/proc/self/fdinfo/%i", fd);
127 else {
c4b69156 128 subfd = openat(fd, filename, O_CLOEXEC|O_PATH);
4349cd7c
LP
129 if (subfd < 0)
130 return -errno;
131
132 xsprintf(path, "/proc/self/fdinfo/%i", subfd);
133 }
134
135 r = read_full_file(path, &fdinfo, NULL);
136 if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
137 return -EOPNOTSUPP;
138 if (r < 0)
0d9bcb7c 139 return r;
4349cd7c
LP
140
141 p = startswith(fdinfo, "mnt_id:");
142 if (!p) {
143 p = strstr(fdinfo, "\nmnt_id:");
144 if (!p) /* The mnt_id field is a relatively new addition */
145 return -EOPNOTSUPP;
146
147 p += 8;
148 }
149
150 p += strspn(p, WHITESPACE);
151 p[strcspn(p, WHITESPACE)] = 0;
152
153 return safe_atoi(p, mnt_id);
154}
155
4349cd7c 156int fd_is_mount_point(int fd, const char *filename, int flags) {
cbfb8679 157 _cleanup_free_ struct file_handle *h = NULL, *h_parent = NULL;
4349cd7c
LP
158 int mount_id = -1, mount_id_parent = -1;
159 bool nosupp = false, check_st_dev = true;
160 struct stat a, b;
161 int r;
162
163 assert(fd >= 0);
164 assert(filename);
165
166 /* First we will try the name_to_handle_at() syscall, which
167 * tells us the mount id and an opaque file "handle". It is
168 * not supported everywhere though (kernel compile-time
169 * option, not all file systems are hooked up). If it works
170 * the mount id is usually good enough to tell us whether
171 * something is a mount point.
172 *
173 * If that didn't work we will try to read the mount id from
174 * /proc/self/fdinfo/<fd>. This is almost as good as
175 * name_to_handle_at(), however, does not return the
176 * opaque file handle. The opaque file handle is pretty useful
177 * to detect the root directory, which we should always
178 * consider a mount point. Hence we use this only as
179 * fallback. Exporting the mnt_id in fdinfo is a pretty recent
180 * kernel addition.
181 *
182 * As last fallback we do traditional fstat() based st_dev
183 * comparisons. This is how things were traditionally done,
61233823 184 * but unionfs breaks this since it exposes file
4349cd7c
LP
185 * systems with a variety of st_dev reported. Also, btrfs
186 * subvolumes have different st_dev, even though they aren't
187 * real mounts of their own. */
188
cbfb8679 189 r = name_to_handle_at_loop(fd, filename, &h, &mount_id, flags);
976c0478 190 if (IN_SET(r, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL))
c83b20d7
LP
191 /* This kernel does not support name_to_handle_at() at all (ENOSYS), or the syscall was blocked
192 * (EACCES/EPERM; maybe through seccomp, because we are running inside of a container?), or the mount
976c0478
LP
193 * point is not triggered yet (EOVERFLOW, think nfs4), or some general name_to_handle_at() flakiness
194 * (EINVAL): fall back to simpler logic. */
cbfb8679
LP
195 goto fallback_fdinfo;
196 else if (r == -EOPNOTSUPP)
197 /* This kernel or file system does not support name_to_handle_at(), hence let's see if the upper fs
198 * supports it (in which case it is a mount point), otherwise fallback to the traditional stat()
199 * logic */
200 nosupp = true;
201 else if (r < 0)
202 return r;
203
204 r = name_to_handle_at_loop(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
205 if (r == -EOPNOTSUPP) {
206 if (nosupp)
207 /* Neither parent nor child do name_to_handle_at()? We have no choice but to fall back. */
4349cd7c 208 goto fallback_fdinfo;
4349cd7c 209 else
cbfb8679
LP
210 /* The parent can't do name_to_handle_at() but the directory we are interested in can? If so,
211 * it must be a mount point. */
212 return 1;
213 } else if (r < 0)
4739fc55 214 return r;
4349cd7c
LP
215
216 /* The parent can do name_to_handle_at() but the
217 * directory we are interested in can't? If so, it
218 * must be a mount point. */
219 if (nosupp)
220 return 1;
221
222 /* If the file handle for the directory we are
223 * interested in and its parent are identical, we
224 * assume this is the root directory, which is a mount
225 * point. */
226
cbfb8679
LP
227 if (h->handle_bytes == h_parent->handle_bytes &&
228 h->handle_type == h_parent->handle_type &&
229 memcmp(h->f_handle, h_parent->f_handle, h->handle_bytes) == 0)
4349cd7c
LP
230 return 1;
231
232 return mount_id != mount_id_parent;
233
234fallback_fdinfo:
235 r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
059c35f5 236 if (IN_SET(r, -EOPNOTSUPP, -EACCES, -EPERM))
4349cd7c
LP
237 goto fallback_fstat;
238 if (r < 0)
239 return r;
240
241 r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
242 if (r < 0)
243 return r;
244
245 if (mount_id != mount_id_parent)
246 return 1;
247
248 /* Hmm, so, the mount ids are the same. This leaves one
249 * special case though for the root file system. For that,
250 * let's see if the parent directory has the same inode as we
251 * are interested in. Hence, let's also do fstat() checks now,
252 * too, but avoid the st_dev comparisons, since they aren't
253 * that useful on unionfs mounts. */
254 check_st_dev = false;
255
256fallback_fstat:
257 /* yay for fstatat() taking a different set of flags than the other
258 * _at() above */
259 if (flags & AT_SYMLINK_FOLLOW)
260 flags &= ~AT_SYMLINK_FOLLOW;
261 else
262 flags |= AT_SYMLINK_NOFOLLOW;
263 if (fstatat(fd, filename, &a, flags) < 0)
264 return -errno;
265
266 if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
267 return -errno;
268
269 /* A directory with same device and inode as its parent? Must
270 * be the root directory */
271 if (a.st_dev == b.st_dev &&
272 a.st_ino == b.st_ino)
273 return 1;
274
275 return check_st_dev && (a.st_dev != b.st_dev);
276}
277
278/* flags can be AT_SYMLINK_FOLLOW or 0 */
e1873695 279int path_is_mount_point(const char *t, const char *root, int flags) {
4349cd7c 280 _cleanup_free_ char *canonical = NULL, *parent = NULL;
e1873695
LP
281 _cleanup_close_ int fd = -1;
282 int r;
4349cd7c
LP
283
284 assert(t);
b12d25a8 285 assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
4349cd7c
LP
286
287 if (path_equal(t, "/"))
288 return 1;
289
290 /* we need to resolve symlinks manually, we can't just rely on
291 * fd_is_mount_point() to do that for us; if we have a structure like
292 * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
293 * look at needs to be /usr, not /. */
294 if (flags & AT_SYMLINK_FOLLOW) {
c4f4fce7 295 r = chase_symlinks(t, root, 0, &canonical);
e1873695
LP
296 if (r < 0)
297 return r;
4349cd7c
LP
298
299 t = canonical;
300 }
301
302 parent = dirname_malloc(t);
303 if (!parent)
304 return -ENOMEM;
305
c4b69156 306 fd = openat(AT_FDCWD, parent, O_DIRECTORY|O_CLOEXEC|O_PATH);
4349cd7c
LP
307 if (fd < 0)
308 return -errno;
309
b12d25a8 310 return fd_is_mount_point(fd, last_path_component(t), flags);
4349cd7c
LP
311}
312
c2a986d5
LP
313int path_get_mnt_id(const char *path, int *ret) {
314 int r;
315
316 r = name_to_handle_at_loop(AT_FDCWD, path, NULL, ret, 0);
976c0478 317 if (IN_SET(r, -EOPNOTSUPP, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL)) /* kernel/fs don't support this, or seccomp blocks access, or untriggered mount, or name_to_handle_at() is flaky */
c2a986d5
LP
318 return fd_fdinfo_mnt_id(AT_FDCWD, path, 0, ret);
319
320 return r;
321}
322
4349cd7c
LP
323int umount_recursive(const char *prefix, int flags) {
324 bool again;
325 int n = 0, r;
326
327 /* Try to umount everything recursively below a
328 * directory. Also, take care of stacked mounts, and keep
329 * unmounting them until they are gone. */
330
331 do {
332 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
333
334 again = false;
335 r = 0;
336
337 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
338 if (!proc_self_mountinfo)
339 return -errno;
340
35bbbf85
LP
341 (void) __fsetlocking(proc_self_mountinfo, FSETLOCKING_BYCALLER);
342
4349cd7c
LP
343 for (;;) {
344 _cleanup_free_ char *path = NULL, *p = NULL;
345 int k;
346
347 k = fscanf(proc_self_mountinfo,
348 "%*s " /* (1) mount id */
349 "%*s " /* (2) parent id */
350 "%*s " /* (3) major:minor */
351 "%*s " /* (4) root */
352 "%ms " /* (5) mount point */
353 "%*s" /* (6) mount options */
354 "%*[^-]" /* (7) optional fields */
355 "- " /* (8) separator */
356 "%*s " /* (9) file system type */
357 "%*s" /* (10) mount source */
358 "%*s" /* (11) mount options 2 */
359 "%*[^\n]", /* some rubbish at the end */
360 &path);
361 if (k != 1) {
362 if (k == EOF)
363 break;
364
365 continue;
366 }
367
368 r = cunescape(path, UNESCAPE_RELAX, &p);
369 if (r < 0)
370 return r;
371
372 if (!path_startswith(p, prefix))
373 continue;
374
375 if (umount2(p, flags) < 0) {
6b7c9f8b 376 r = log_debug_errno(errno, "Failed to umount %s: %m", p);
4349cd7c
LP
377 continue;
378 }
379
6b7c9f8b
LP
380 log_debug("Successfully unmounted %s", p);
381
4349cd7c
LP
382 again = true;
383 n++;
384
385 break;
386 }
387
388 } while (again);
389
390 return r ? r : n;
391}
392
393static int get_mount_flags(const char *path, unsigned long *flags) {
394 struct statvfs buf;
395
396 if (statvfs(path, &buf) < 0)
397 return -errno;
398 *flags = buf.f_flag;
399 return 0;
400}
401
ac9de0b3
TR
402/* Use this function only if do you have direct access to /proc/self/mountinfo
403 * and need the caller to open it for you. This is the case when /proc is
404 * masked or not mounted. Otherwise, use bind_remount_recursive. */
405int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **blacklist, FILE *proc_self_mountinfo) {
4349cd7c
LP
406 _cleanup_set_free_free_ Set *done = NULL;
407 _cleanup_free_ char *cleaned = NULL;
408 int r;
409
ac9de0b3
TR
410 assert(proc_self_mountinfo);
411
6b7c9f8b
LP
412 /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already
413 * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
414 * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to
415 * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each
416 * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We
417 * do not have any effect on future submounts that might get propagated, they migt be writable. This includes
418 * future submounts that have been triggered via autofs.
419 *
420 * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the
421 * remount operation. Note that we'll ignore the blacklist for the top-level path. */
4349cd7c
LP
422
423 cleaned = strdup(prefix);
424 if (!cleaned)
425 return -ENOMEM;
426
427 path_kill_slashes(cleaned);
428
429 done = set_new(&string_hash_ops);
430 if (!done)
431 return -ENOMEM;
432
433 for (;;) {
4349cd7c
LP
434 _cleanup_set_free_free_ Set *todo = NULL;
435 bool top_autofs = false;
436 char *x;
437 unsigned long orig_flags;
438
439 todo = set_new(&string_hash_ops);
440 if (!todo)
441 return -ENOMEM;
442
ac9de0b3 443 rewind(proc_self_mountinfo);
4349cd7c
LP
444
445 for (;;) {
446 _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL;
447 int k;
448
449 k = fscanf(proc_self_mountinfo,
450 "%*s " /* (1) mount id */
451 "%*s " /* (2) parent id */
452 "%*s " /* (3) major:minor */
453 "%*s " /* (4) root */
454 "%ms " /* (5) mount point */
455 "%*s" /* (6) mount options (superblock) */
456 "%*[^-]" /* (7) optional fields */
457 "- " /* (8) separator */
458 "%ms " /* (9) file system type */
459 "%*s" /* (10) mount source */
460 "%*s" /* (11) mount options (bind mount) */
461 "%*[^\n]", /* some rubbish at the end */
462 &path,
463 &type);
464 if (k != 2) {
465 if (k == EOF)
466 break;
467
468 continue;
469 }
470
471 r = cunescape(path, UNESCAPE_RELAX, &p);
472 if (r < 0)
473 return r;
474
6b7c9f8b
LP
475 if (!path_startswith(p, cleaned))
476 continue;
477
478 /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall
479 * operate on. */
480 if (!path_equal(cleaned, p)) {
481 bool blacklisted = false;
482 char **i;
483
484 STRV_FOREACH(i, blacklist) {
485
486 if (path_equal(*i, cleaned))
487 continue;
488
489 if (!path_startswith(*i, cleaned))
490 continue;
491
492 if (path_startswith(p, *i)) {
493 blacklisted = true;
494 log_debug("Not remounting %s, because blacklisted by %s, called for %s", p, *i, cleaned);
495 break;
496 }
497 }
498 if (blacklisted)
499 continue;
500 }
501
4349cd7c
LP
502 /* Let's ignore autofs mounts. If they aren't
503 * triggered yet, we want to avoid triggering
504 * them, as we don't make any guarantees for
505 * future submounts anyway. If they are
506 * already triggered, then we will find
507 * another entry for this. */
508 if (streq(type, "autofs")) {
509 top_autofs = top_autofs || path_equal(cleaned, p);
510 continue;
511 }
512
6b7c9f8b 513 if (!set_contains(done, p)) {
4349cd7c
LP
514 r = set_consume(todo, p);
515 p = NULL;
4349cd7c
LP
516 if (r == -EEXIST)
517 continue;
518 if (r < 0)
519 return r;
520 }
521 }
522
523 /* If we have no submounts to process anymore and if
524 * the root is either already done, or an autofs, we
525 * are done */
526 if (set_isempty(todo) &&
527 (top_autofs || set_contains(done, cleaned)))
528 return 0;
529
530 if (!set_contains(done, cleaned) &&
531 !set_contains(todo, cleaned)) {
6b7c9f8b 532 /* The prefix directory itself is not yet a mount, make it one. */
4349cd7c
LP
533 if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
534 return -errno;
535
536 orig_flags = 0;
537 (void) get_mount_flags(cleaned, &orig_flags);
538 orig_flags &= ~MS_RDONLY;
539
540 if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
541 return -errno;
542
6b7c9f8b
LP
543 log_debug("Made top-level directory %s a mount point.", prefix);
544
4349cd7c
LP
545 x = strdup(cleaned);
546 if (!x)
547 return -ENOMEM;
548
549 r = set_consume(done, x);
550 if (r < 0)
551 return r;
552 }
553
554 while ((x = set_steal_first(todo))) {
555
556 r = set_consume(done, x);
4c701096 557 if (IN_SET(r, 0, -EEXIST))
4349cd7c
LP
558 continue;
559 if (r < 0)
560 return r;
561
6b7c9f8b 562 /* Deal with mount points that are obstructed by a later mount */
e1873695 563 r = path_is_mount_point(x, NULL, 0);
4c701096 564 if (IN_SET(r, 0, -ENOENT))
98df8089
AC
565 continue;
566 if (r < 0)
567 return r;
568
569 /* Try to reuse the original flag set */
4349cd7c
LP
570 orig_flags = 0;
571 (void) get_mount_flags(x, &orig_flags);
572 orig_flags &= ~MS_RDONLY;
573
98df8089
AC
574 if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
575 return -errno;
4349cd7c 576
6b7c9f8b 577 log_debug("Remounted %s read-only.", x);
4349cd7c
LP
578 }
579 }
580}
581
ac9de0b3
TR
582int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) {
583 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
584
585 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
586 if (!proc_self_mountinfo)
587 return -errno;
588
35bbbf85
LP
589 (void) __fsetlocking(proc_self_mountinfo, FSETLOCKING_BYCALLER);
590
ac9de0b3
TR
591 return bind_remount_recursive_with_mountinfo(prefix, ro, blacklist, proc_self_mountinfo);
592}
593
4349cd7c
LP
594int mount_move_root(const char *path) {
595 assert(path);
596
597 if (chdir(path) < 0)
598 return -errno;
599
600 if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
601 return -errno;
602
603 if (chroot(".") < 0)
604 return -errno;
605
606 if (chdir("/") < 0)
607 return -errno;
608
609 return 0;
610}
4e036b7a
LP
611
612bool fstype_is_network(const char *fstype) {
4e036b7a
LP
613 const char *x;
614
615 x = startswith(fstype, "fuse.");
616 if (x)
617 fstype = x;
618
5991ce44
ZJS
619 return STR_IN_SET(fstype,
620 "afs",
621 "cifs",
622 "smbfs",
623 "sshfs",
624 "ncpfs",
625 "ncp",
626 "nfs",
627 "nfs4",
628 "gfs",
629 "gfs2",
630 "glusterfs",
631 "pvfs2", /* OrangeFS */
632 "ocfs2",
633 "lustre");
4e036b7a 634}
3f2c0bec 635
e2be442e 636bool fstype_is_api_vfs(const char *fstype) {
5991ce44
ZJS
637 return STR_IN_SET(fstype,
638 "autofs",
639 "bpf",
640 "cgroup",
641 "cgroup2",
642 "configfs",
643 "cpuset",
644 "debugfs",
645 "devpts",
646 "devtmpfs",
647 "efivarfs",
648 "fusectl",
649 "hugetlbfs",
650 "mqueue",
651 "proc",
652 "pstore",
653 "ramfs",
654 "securityfs",
655 "sysfs",
656 "tmpfs",
657 "tracefs");
e2be442e
YW
658}
659
896f937f 660bool fstype_is_ro(const char *fstype) {
896f937f 661 /* All Linux file systems that are necessarily read-only */
5991ce44
ZJS
662 return STR_IN_SET(fstype,
663 "DM_verity_hash",
664 "iso9660",
665 "squashfs");
896f937f
LP
666}
667
154d2269 668bool fstype_can_discard(const char *fstype) {
5991ce44
ZJS
669 return STR_IN_SET(fstype,
670 "btrfs",
671 "ext4",
672 "vfat",
673 "xfs");
154d2269
LP
674}
675
2d3a5a73
LP
676bool fstype_can_uid_gid(const char *fstype) {
677
678 /* All file systems that have a uid=/gid= mount option that fixates the owners of all files and directories,
679 * current and future. */
680
681 return STR_IN_SET(fstype,
682 "adfs",
683 "fat",
684 "hfs",
685 "hpfs",
686 "iso9660",
687 "msdos",
688 "ntfs",
689 "vfat");
690}
691
3f2c0bec
LP
692int repeat_unmount(const char *path, int flags) {
693 bool done = false;
694
695 assert(path);
696
697 /* If there are multiple mounts on a mount point, this
698 * removes them all */
699
700 for (;;) {
701 if (umount2(path, flags) < 0) {
702
703 if (errno == EINVAL)
704 return done;
705
706 return -errno;
707 }
708
709 done = true;
710 }
711}
c4b41707
AP
712
713const char* mode_to_inaccessible_node(mode_t mode) {
b3d1d516
AP
714 /* This function maps a node type to the correspondent inaccessible node type.
715 * Character and block inaccessible devices may not be created (because major=0 and minor=0),
716 * in such case we map character and block devices to the inaccessible node type socket. */
c4b41707
AP
717 switch(mode & S_IFMT) {
718 case S_IFREG:
719 return "/run/systemd/inaccessible/reg";
720 case S_IFDIR:
721 return "/run/systemd/inaccessible/dir";
722 case S_IFCHR:
b3d1d516
AP
723 if (access("/run/systemd/inaccessible/chr", F_OK) == 0)
724 return "/run/systemd/inaccessible/chr";
725 return "/run/systemd/inaccessible/sock";
c4b41707 726 case S_IFBLK:
b3d1d516
AP
727 if (access("/run/systemd/inaccessible/blk", F_OK) == 0)
728 return "/run/systemd/inaccessible/blk";
729 return "/run/systemd/inaccessible/sock";
c4b41707
AP
730 case S_IFIFO:
731 return "/run/systemd/inaccessible/fifo";
732 case S_IFSOCK:
733 return "/run/systemd/inaccessible/sock";
734 }
735 return NULL;
736}
60e76d48
ZJS
737
738#define FLAG(name) (flags & name ? STRINGIFY(name) "|" : "")
739static char* mount_flags_to_string(long unsigned flags) {
740 char *x;
741 _cleanup_free_ char *y = NULL;
742 long unsigned overflow;
743
744 overflow = flags & ~(MS_RDONLY |
745 MS_NOSUID |
746 MS_NODEV |
747 MS_NOEXEC |
748 MS_SYNCHRONOUS |
749 MS_REMOUNT |
750 MS_MANDLOCK |
751 MS_DIRSYNC |
752 MS_NOATIME |
753 MS_NODIRATIME |
754 MS_BIND |
755 MS_MOVE |
756 MS_REC |
757 MS_SILENT |
758 MS_POSIXACL |
759 MS_UNBINDABLE |
760 MS_PRIVATE |
761 MS_SLAVE |
762 MS_SHARED |
763 MS_RELATIME |
764 MS_KERNMOUNT |
765 MS_I_VERSION |
766 MS_STRICTATIME |
767 MS_LAZYTIME);
768
769 if (flags == 0 || overflow != 0)
770 if (asprintf(&y, "%lx", overflow) < 0)
771 return NULL;
772
773 x = strjoin(FLAG(MS_RDONLY),
774 FLAG(MS_NOSUID),
775 FLAG(MS_NODEV),
776 FLAG(MS_NOEXEC),
777 FLAG(MS_SYNCHRONOUS),
778 FLAG(MS_REMOUNT),
779 FLAG(MS_MANDLOCK),
780 FLAG(MS_DIRSYNC),
781 FLAG(MS_NOATIME),
782 FLAG(MS_NODIRATIME),
783 FLAG(MS_BIND),
784 FLAG(MS_MOVE),
785 FLAG(MS_REC),
786 FLAG(MS_SILENT),
787 FLAG(MS_POSIXACL),
788 FLAG(MS_UNBINDABLE),
789 FLAG(MS_PRIVATE),
790 FLAG(MS_SLAVE),
791 FLAG(MS_SHARED),
792 FLAG(MS_RELATIME),
793 FLAG(MS_KERNMOUNT),
794 FLAG(MS_I_VERSION),
795 FLAG(MS_STRICTATIME),
796 FLAG(MS_LAZYTIME),
605405c6 797 y);
60e76d48
ZJS
798 if (!x)
799 return NULL;
800 if (!y)
801 x[strlen(x) - 1] = '\0'; /* truncate the last | */
802 return x;
803}
804
805int mount_verbose(
806 int error_log_level,
807 const char *what,
808 const char *where,
809 const char *type,
810 unsigned long flags,
811 const char *options) {
812
813 _cleanup_free_ char *fl = NULL;
814
815 fl = mount_flags_to_string(flags);
816
817 if ((flags & MS_REMOUNT) && !what && !type)
818 log_debug("Remounting %s (%s \"%s\")...",
819 where, strnull(fl), strempty(options));
820 else if (!what && !type)
821 log_debug("Mounting %s (%s \"%s\")...",
822 where, strnull(fl), strempty(options));
823 else if ((flags & MS_BIND) && !type)
824 log_debug("Bind-mounting %s on %s (%s \"%s\")...",
825 what, where, strnull(fl), strempty(options));
afe682bc
LP
826 else if (flags & MS_MOVE)
827 log_debug("Moving mount %s → %s (%s \"%s\")...",
828 what, where, strnull(fl), strempty(options));
60e76d48
ZJS
829 else
830 log_debug("Mounting %s on %s (%s \"%s\")...",
831 strna(type), where, strnull(fl), strempty(options));
832 if (mount(what, where, type, flags, options) < 0)
833 return log_full_errno(error_log_level, errno,
834 "Failed to mount %s on %s (%s \"%s\"): %m",
835 strna(type), where, strnull(fl), strempty(options));
836 return 0;
837}
838
839int umount_verbose(const char *what) {
840 log_debug("Umounting %s...", what);
841 if (umount(what) < 0)
842 return log_error_errno(errno, "Failed to unmount %s: %m", what);
843 return 0;
844}
83555251
LP
845
846const char *mount_propagation_flags_to_string(unsigned long flags) {
847
848 switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
c7383828
ZJS
849 case 0:
850 return "";
83555251
LP
851 case MS_SHARED:
852 return "shared";
83555251
LP
853 case MS_SLAVE:
854 return "slave";
83555251
LP
855 case MS_PRIVATE:
856 return "private";
857 }
858
859 return NULL;
860}
861
83555251 862
c7383828 863int mount_propagation_flags_from_string(const char *name, unsigned long *ret) {
83555251 864
c7383828
ZJS
865 if (isempty(name))
866 *ret = 0;
867 else if (streq(name, "shared"))
868 *ret = MS_SHARED;
869 else if (streq(name, "slave"))
870 *ret = MS_SLAVE;
871 else if (streq(name, "private"))
872 *ret = MS_PRIVATE;
873 else
874 return -EINVAL;
83555251
LP
875 return 0;
876}