chiark / gitweb /
Link: port to new ethtool ETHTOOL_xLINKSETTINGS
[elogind.git] / src / basic / mount-util.c
1 /***
2   This file is part of systemd.
3
4   Copyright 2010 Lennart Poettering
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/mount.h>
24 #include <sys/stat.h>
25 #include <sys/statvfs.h>
26 #include <unistd.h>
27
28 #include "alloc-util.h"
29 #include "escape.h"
30 #include "fd-util.h"
31 #include "fileio.h"
32 #include "hashmap.h"
33 #include "mount-util.h"
34 #include "parse-util.h"
35 #include "path-util.h"
36 #include "set.h"
37 #include "stdio-util.h"
38 #include "string-util.h"
39
40 static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
41         char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
42         _cleanup_free_ char *fdinfo = NULL;
43         _cleanup_close_ int subfd = -1;
44         char *p;
45         int r;
46
47         if ((flags & AT_EMPTY_PATH) && isempty(filename))
48                 xsprintf(path, "/proc/self/fdinfo/%i", fd);
49         else {
50                 subfd = openat(fd, filename, O_CLOEXEC|O_PATH);
51                 if (subfd < 0)
52                         return -errno;
53
54                 xsprintf(path, "/proc/self/fdinfo/%i", subfd);
55         }
56
57         r = read_full_file(path, &fdinfo, NULL);
58         if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
59                 return -EOPNOTSUPP;
60         if (r < 0)
61                 return -errno;
62
63         p = startswith(fdinfo, "mnt_id:");
64         if (!p) {
65                 p = strstr(fdinfo, "\nmnt_id:");
66                 if (!p) /* The mnt_id field is a relatively new addition */
67                         return -EOPNOTSUPP;
68
69                 p += 8;
70         }
71
72         p += strspn(p, WHITESPACE);
73         p[strcspn(p, WHITESPACE)] = 0;
74
75         return safe_atoi(p, mnt_id);
76 }
77
78 int fd_is_mount_point(int fd, const char *filename, int flags) {
79         union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
80         int mount_id = -1, mount_id_parent = -1;
81         bool nosupp = false, check_st_dev = true;
82         struct stat a, b;
83         int r;
84
85         assert(fd >= 0);
86         assert(filename);
87
88         /* First we will try the name_to_handle_at() syscall, which
89          * tells us the mount id and an opaque file "handle". It is
90          * not supported everywhere though (kernel compile-time
91          * option, not all file systems are hooked up). If it works
92          * the mount id is usually good enough to tell us whether
93          * something is a mount point.
94          *
95          * If that didn't work we will try to read the mount id from
96          * /proc/self/fdinfo/<fd>. This is almost as good as
97          * name_to_handle_at(), however, does not return the
98          * opaque file handle. The opaque file handle is pretty useful
99          * to detect the root directory, which we should always
100          * consider a mount point. Hence we use this only as
101          * fallback. Exporting the mnt_id in fdinfo is a pretty recent
102          * kernel addition.
103          *
104          * As last fallback we do traditional fstat() based st_dev
105          * comparisons. This is how things were traditionally done,
106          * but unionfs breaks this since it exposes file
107          * systems with a variety of st_dev reported. Also, btrfs
108          * subvolumes have different st_dev, even though they aren't
109          * real mounts of their own. */
110
111         r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
112         if (r < 0) {
113                 if (errno == ENOSYS)
114                         /* This kernel does not support name_to_handle_at()
115                          * fall back to simpler logic. */
116                         goto fallback_fdinfo;
117                 else if (errno == EOPNOTSUPP)
118                         /* This kernel or file system does not support
119                          * name_to_handle_at(), hence let's see if the
120                          * upper fs supports it (in which case it is a
121                          * mount point), otherwise fallback to the
122                          * traditional stat() logic */
123                         nosupp = true;
124                 else
125                         return -errno;
126         }
127
128         r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
129         if (r < 0) {
130                 if (errno == EOPNOTSUPP) {
131                         if (nosupp)
132                                 /* Neither parent nor child do name_to_handle_at()?
133                                    We have no choice but to fall back. */
134                                 goto fallback_fdinfo;
135                         else
136                                 /* The parent can't do name_to_handle_at() but the
137                                  * directory we are interested in can?
138                                  * If so, it must be a mount point. */
139                                 return 1;
140                 } else
141                         return -errno;
142         }
143
144         /* The parent can do name_to_handle_at() but the
145          * directory we are interested in can't? If so, it
146          * must be a mount point. */
147         if (nosupp)
148                 return 1;
149
150         /* If the file handle for the directory we are
151          * interested in and its parent are identical, we
152          * assume this is the root directory, which is a mount
153          * point. */
154
155         if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
156             h.handle.handle_type == h_parent.handle.handle_type &&
157             memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
158                 return 1;
159
160         return mount_id != mount_id_parent;
161
162 fallback_fdinfo:
163         r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
164         if (r == -EOPNOTSUPP)
165                 goto fallback_fstat;
166         if (r < 0)
167                 return r;
168
169         r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
170         if (r < 0)
171                 return r;
172
173         if (mount_id != mount_id_parent)
174                 return 1;
175
176         /* Hmm, so, the mount ids are the same. This leaves one
177          * special case though for the root file system. For that,
178          * let's see if the parent directory has the same inode as we
179          * are interested in. Hence, let's also do fstat() checks now,
180          * too, but avoid the st_dev comparisons, since they aren't
181          * that useful on unionfs mounts. */
182         check_st_dev = false;
183
184 fallback_fstat:
185         /* yay for fstatat() taking a different set of flags than the other
186          * _at() above */
187         if (flags & AT_SYMLINK_FOLLOW)
188                 flags &= ~AT_SYMLINK_FOLLOW;
189         else
190                 flags |= AT_SYMLINK_NOFOLLOW;
191         if (fstatat(fd, filename, &a, flags) < 0)
192                 return -errno;
193
194         if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
195                 return -errno;
196
197         /* A directory with same device and inode as its parent? Must
198          * be the root directory */
199         if (a.st_dev == b.st_dev &&
200             a.st_ino == b.st_ino)
201                 return 1;
202
203         return check_st_dev && (a.st_dev != b.st_dev);
204 }
205
206 /* flags can be AT_SYMLINK_FOLLOW or 0 */
207 int path_is_mount_point(const char *t, int flags) {
208         _cleanup_close_ int fd = -1;
209         _cleanup_free_ char *canonical = NULL, *parent = NULL;
210
211         assert(t);
212
213         if (path_equal(t, "/"))
214                 return 1;
215
216         /* we need to resolve symlinks manually, we can't just rely on
217          * fd_is_mount_point() to do that for us; if we have a structure like
218          * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
219          * look at needs to be /usr, not /. */
220         if (flags & AT_SYMLINK_FOLLOW) {
221                 canonical = canonicalize_file_name(t);
222                 if (!canonical)
223                         return -errno;
224
225                 t = canonical;
226         }
227
228         parent = dirname_malloc(t);
229         if (!parent)
230                 return -ENOMEM;
231
232         fd = openat(AT_FDCWD, parent, O_DIRECTORY|O_CLOEXEC|O_PATH);
233         if (fd < 0)
234                 return -errno;
235
236         return fd_is_mount_point(fd, basename(t), flags);
237 }
238
239 #if 0 /// UNNEEDED by elogind
240 int umount_recursive(const char *prefix, int flags) {
241         bool again;
242         int n = 0, r;
243
244         /* Try to umount everything recursively below a
245          * directory. Also, take care of stacked mounts, and keep
246          * unmounting them until they are gone. */
247
248         do {
249                 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
250
251                 again = false;
252                 r = 0;
253
254                 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
255                 if (!proc_self_mountinfo)
256                         return -errno;
257
258                 for (;;) {
259                         _cleanup_free_ char *path = NULL, *p = NULL;
260                         int k;
261
262                         k = fscanf(proc_self_mountinfo,
263                                    "%*s "       /* (1) mount id */
264                                    "%*s "       /* (2) parent id */
265                                    "%*s "       /* (3) major:minor */
266                                    "%*s "       /* (4) root */
267                                    "%ms "       /* (5) mount point */
268                                    "%*s"        /* (6) mount options */
269                                    "%*[^-]"     /* (7) optional fields */
270                                    "- "         /* (8) separator */
271                                    "%*s "       /* (9) file system type */
272                                    "%*s"        /* (10) mount source */
273                                    "%*s"        /* (11) mount options 2 */
274                                    "%*[^\n]",   /* some rubbish at the end */
275                                    &path);
276                         if (k != 1) {
277                                 if (k == EOF)
278                                         break;
279
280                                 continue;
281                         }
282
283                         r = cunescape(path, UNESCAPE_RELAX, &p);
284                         if (r < 0)
285                                 return r;
286
287                         if (!path_startswith(p, prefix))
288                                 continue;
289
290                         if (umount2(p, flags) < 0) {
291                                 r = log_debug_errno(errno, "Failed to umount %s: %m", p);
292                                 continue;
293                         }
294
295                         log_debug("Successfully unmounted %s", p);
296
297                         again = true;
298                         n++;
299
300                         break;
301                 }
302
303         } while (again);
304
305         return r ? r : n;
306 }
307
308 static int get_mount_flags(const char *path, unsigned long *flags) {
309         struct statvfs buf;
310
311         if (statvfs(path, &buf) < 0)
312                 return -errno;
313         *flags = buf.f_flag;
314         return 0;
315 }
316
317 int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) {
318         _cleanup_set_free_free_ Set *done = NULL;
319         _cleanup_free_ char *cleaned = NULL;
320         int r;
321
322         /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already
323          * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
324          * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to
325          * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each
326          * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We
327          * do not have any effect on future submounts that might get propagated, they migt be writable. This includes
328          * future submounts that have been triggered via autofs.
329          *
330          * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the
331          * remount operation. Note that we'll ignore the blacklist for the top-level path. */
332
333         cleaned = strdup(prefix);
334         if (!cleaned)
335                 return -ENOMEM;
336
337         path_kill_slashes(cleaned);
338
339         done = set_new(&string_hash_ops);
340         if (!done)
341                 return -ENOMEM;
342
343         for (;;) {
344                 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
345                 _cleanup_set_free_free_ Set *todo = NULL;
346                 bool top_autofs = false;
347                 char *x;
348                 unsigned long orig_flags;
349
350                 todo = set_new(&string_hash_ops);
351                 if (!todo)
352                         return -ENOMEM;
353
354                 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
355                 if (!proc_self_mountinfo)
356                         return -errno;
357
358                 for (;;) {
359                         _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL;
360                         int k;
361
362                         k = fscanf(proc_self_mountinfo,
363                                    "%*s "       /* (1) mount id */
364                                    "%*s "       /* (2) parent id */
365                                    "%*s "       /* (3) major:minor */
366                                    "%*s "       /* (4) root */
367                                    "%ms "       /* (5) mount point */
368                                    "%*s"        /* (6) mount options (superblock) */
369                                    "%*[^-]"     /* (7) optional fields */
370                                    "- "         /* (8) separator */
371                                    "%ms "       /* (9) file system type */
372                                    "%*s"        /* (10) mount source */
373                                    "%*s"        /* (11) mount options (bind mount) */
374                                    "%*[^\n]",   /* some rubbish at the end */
375                                    &path,
376                                    &type);
377                         if (k != 2) {
378                                 if (k == EOF)
379                                         break;
380
381                                 continue;
382                         }
383
384                         r = cunescape(path, UNESCAPE_RELAX, &p);
385                         if (r < 0)
386                                 return r;
387
388                         if (!path_startswith(p, cleaned))
389                                 continue;
390
391                         /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall
392                          * operate on. */
393                         if (!path_equal(cleaned, p)) {
394                                 bool blacklisted = false;
395                                 char **i;
396
397                                 STRV_FOREACH(i, blacklist) {
398
399                                         if (path_equal(*i, cleaned))
400                                                 continue;
401
402                                         if (!path_startswith(*i, cleaned))
403                                                 continue;
404
405                                         if (path_startswith(p, *i)) {
406                                                 blacklisted = true;
407                                                 log_debug("Not remounting %s, because blacklisted by %s, called for %s", p, *i, cleaned);
408                                                 break;
409                                         }
410                                 }
411                                 if (blacklisted)
412                                         continue;
413                         }
414
415                         /* Let's ignore autofs mounts.  If they aren't
416                          * triggered yet, we want to avoid triggering
417                          * them, as we don't make any guarantees for
418                          * future submounts anyway.  If they are
419                          * already triggered, then we will find
420                          * another entry for this. */
421                         if (streq(type, "autofs")) {
422                                 top_autofs = top_autofs || path_equal(cleaned, p);
423                                 continue;
424                         }
425
426                         if (!set_contains(done, p)) {
427                                 r = set_consume(todo, p);
428                                 p = NULL;
429                                 if (r == -EEXIST)
430                                         continue;
431                                 if (r < 0)
432                                         return r;
433                         }
434                 }
435
436                 /* If we have no submounts to process anymore and if
437                  * the root is either already done, or an autofs, we
438                  * are done */
439                 if (set_isempty(todo) &&
440                     (top_autofs || set_contains(done, cleaned)))
441                         return 0;
442
443                 if (!set_contains(done, cleaned) &&
444                     !set_contains(todo, cleaned)) {
445                         /* The prefix directory itself is not yet a mount, make it one. */
446                         if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
447                                 return -errno;
448
449                         orig_flags = 0;
450                         (void) get_mount_flags(cleaned, &orig_flags);
451                         orig_flags &= ~MS_RDONLY;
452
453                         if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
454                                 return -errno;
455
456                         log_debug("Made top-level directory %s a mount point.", prefix);
457
458                         x = strdup(cleaned);
459                         if (!x)
460                                 return -ENOMEM;
461
462                         r = set_consume(done, x);
463                         if (r < 0)
464                                 return r;
465                 }
466
467                 while ((x = set_steal_first(todo))) {
468
469                         r = set_consume(done, x);
470                         if (r == -EEXIST || r == 0)
471                                 continue;
472                         if (r < 0)
473                                 return r;
474
475                         /* Deal with mount points that are obstructed by a later mount */
476                         r = path_is_mount_point(x, 0);
477                         if (r == -ENOENT || r == 0)
478                                 continue;
479                         if (r < 0)
480                                 return r;
481
482                         /* Try to reuse the original flag set */
483                         orig_flags = 0;
484                         (void) get_mount_flags(x, &orig_flags);
485                         orig_flags &= ~MS_RDONLY;
486
487                         if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
488                                 return -errno;
489
490                         log_debug("Remounted %s read-only.", x);
491                 }
492         }
493 }
494
495 int mount_move_root(const char *path) {
496         assert(path);
497
498         if (chdir(path) < 0)
499                 return -errno;
500
501         if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
502                 return -errno;
503
504         if (chroot(".") < 0)
505                 return -errno;
506
507         if (chdir("/") < 0)
508                 return -errno;
509
510         return 0;
511 }
512
513 bool fstype_is_network(const char *fstype) {
514         static const char table[] =
515                 "afs\0"
516                 "cifs\0"
517                 "smbfs\0"
518                 "sshfs\0"
519                 "ncpfs\0"
520                 "ncp\0"
521                 "nfs\0"
522                 "nfs4\0"
523                 "gfs\0"
524                 "gfs2\0"
525                 "glusterfs\0"
526                 "pvfs2\0" /* OrangeFS */
527                 "ocfs2\0"
528                 ;
529
530         const char *x;
531
532         x = startswith(fstype, "fuse.");
533         if (x)
534                 fstype = x;
535
536         return nulstr_contains(table, fstype);
537 }
538
539 int repeat_unmount(const char *path, int flags) {
540         bool done = false;
541
542         assert(path);
543
544         /* If there are multiple mounts on a mount point, this
545          * removes them all */
546
547         for (;;) {
548                 if (umount2(path, flags) < 0) {
549
550                         if (errno == EINVAL)
551                                 return done;
552
553                         return -errno;
554                 }
555
556                 done = true;
557         }
558 }
559 #endif // 0
560
561 const char* mode_to_inaccessible_node(mode_t mode) {
562         /* This function maps a node type to the correspondent inaccessible node type.
563          * Character and block inaccessible devices may not be created (because major=0 and minor=0),
564          * in such case we map character and block devices to the inaccessible node type socket. */
565         switch(mode & S_IFMT) {
566                 case S_IFREG:
567                         return "/run/systemd/inaccessible/reg";
568                 case S_IFDIR:
569                         return "/run/systemd/inaccessible/dir";
570                 case S_IFCHR:
571                         if (access("/run/systemd/inaccessible/chr", F_OK) == 0)
572                                 return "/run/systemd/inaccessible/chr";
573                         return "/run/systemd/inaccessible/sock";
574                 case S_IFBLK:
575                         if (access("/run/systemd/inaccessible/blk", F_OK) == 0)
576                                 return "/run/systemd/inaccessible/blk";
577                         return "/run/systemd/inaccessible/sock";
578                 case S_IFIFO:
579                         return "/run/systemd/inaccessible/fifo";
580                 case S_IFSOCK:
581                         return "/run/systemd/inaccessible/sock";
582         }
583         return NULL;
584 }
585
586 #if 0 /// UNNEEDED by elogind
587 #define FLAG(name) (flags & name ? STRINGIFY(name) "|" : "")
588 static char* mount_flags_to_string(long unsigned flags) {
589         char *x;
590         _cleanup_free_ char *y = NULL;
591         long unsigned overflow;
592
593         overflow = flags & ~(MS_RDONLY |
594                              MS_NOSUID |
595                              MS_NODEV |
596                              MS_NOEXEC |
597                              MS_SYNCHRONOUS |
598                              MS_REMOUNT |
599                              MS_MANDLOCK |
600                              MS_DIRSYNC |
601                              MS_NOATIME |
602                              MS_NODIRATIME |
603                              MS_BIND |
604                              MS_MOVE |
605                              MS_REC |
606                              MS_SILENT |
607                              MS_POSIXACL |
608                              MS_UNBINDABLE |
609                              MS_PRIVATE |
610                              MS_SLAVE |
611                              MS_SHARED |
612                              MS_RELATIME |
613                              MS_KERNMOUNT |
614                              MS_I_VERSION |
615                              MS_STRICTATIME |
616                              MS_LAZYTIME);
617
618         if (flags == 0 || overflow != 0)
619                 if (asprintf(&y, "%lx", overflow) < 0)
620                         return NULL;
621
622         x = strjoin(FLAG(MS_RDONLY),
623                     FLAG(MS_NOSUID),
624                     FLAG(MS_NODEV),
625                     FLAG(MS_NOEXEC),
626                     FLAG(MS_SYNCHRONOUS),
627                     FLAG(MS_REMOUNT),
628                     FLAG(MS_MANDLOCK),
629                     FLAG(MS_DIRSYNC),
630                     FLAG(MS_NOATIME),
631                     FLAG(MS_NODIRATIME),
632                     FLAG(MS_BIND),
633                     FLAG(MS_MOVE),
634                     FLAG(MS_REC),
635                     FLAG(MS_SILENT),
636                     FLAG(MS_POSIXACL),
637                     FLAG(MS_UNBINDABLE),
638                     FLAG(MS_PRIVATE),
639                     FLAG(MS_SLAVE),
640                     FLAG(MS_SHARED),
641                     FLAG(MS_RELATIME),
642                     FLAG(MS_KERNMOUNT),
643                     FLAG(MS_I_VERSION),
644                     FLAG(MS_STRICTATIME),
645                     FLAG(MS_LAZYTIME),
646                     y);
647         if (!x)
648                 return NULL;
649         if (!y)
650                 x[strlen(x) - 1] = '\0'; /* truncate the last | */
651         return x;
652 }
653
654 int mount_verbose(
655                 int error_log_level,
656                 const char *what,
657                 const char *where,
658                 const char *type,
659                 unsigned long flags,
660                 const char *options) {
661
662         _cleanup_free_ char *fl = NULL;
663
664         fl = mount_flags_to_string(flags);
665
666         if ((flags & MS_REMOUNT) && !what && !type)
667                 log_debug("Remounting %s (%s \"%s\")...",
668                           where, strnull(fl), strempty(options));
669         else if (!what && !type)
670                 log_debug("Mounting %s (%s \"%s\")...",
671                           where, strnull(fl), strempty(options));
672         else if ((flags & MS_BIND) && !type)
673                 log_debug("Bind-mounting %s on %s (%s \"%s\")...",
674                           what, where, strnull(fl), strempty(options));
675         else
676                 log_debug("Mounting %s on %s (%s \"%s\")...",
677                           strna(type), where, strnull(fl), strempty(options));
678         if (mount(what, where, type, flags, options) < 0)
679                 return log_full_errno(error_log_level, errno,
680                                       "Failed to mount %s on %s (%s \"%s\"): %m",
681                                       strna(type), where, strnull(fl), strempty(options));
682         return 0;
683 }
684
685 int umount_verbose(const char *what) {
686         log_debug("Umounting %s...", what);
687         if (umount(what) < 0)
688                 return log_error_errno(errno, "Failed to unmount %s: %m", what);
689         return 0;
690 }
691 #endif // 0