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