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