shutdown: use libmount to enumerate /proc/self/mountinfo
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 14 Mar 2018 10:32:30 +0000 (11:32 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 16 Mar 2018 09:09:46 +0000 (10:09 +0100)
This is analogous to 8d3ae2bd4c9bf9fc2e57f7b3776325a1c750ca30, except that now
src/core/umount.c not src/core/mount.c is converted.

Might help with https://bugzilla.redhat.com/show_bug.cgi?id=1554943, or not.

In the patch, mnt_free_tablep and mnt_free_iterp are declared twice. It'd
be nicer to define them just once in mount-setup.h, but then libmount.h would
have to be included there. libmount.h seems to be buggy, and declares some
defines which break other headers, and working around this is more pain than
the two duplicate lines. So let's live with the duplication for now.

This fixes memleak of MountPoint in mount_points_list_get() on error, not that
it matters any.

meson.build
src/core/mount.c
src/core/umount.c

index 56e3d00..aac783f 100644 (file)
@@ -2404,6 +2404,7 @@ executable('systemd-shutdown',
            systemd_shutdown_sources,
            include_directories : includes,
            link_with : [libshared],
+           dependencies : [libmount],
            install_rpath : rootlibexecdir,
            install : true,
            install_dir : rootlibexecdir)
index cfe8ec9..0e755da 100644 (file)
@@ -1608,11 +1608,8 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
         assert(m);
 
         t = mnt_new_table();
-        if (!t)
-                return log_oom();
-
         i = mnt_new_iter(MNT_ITER_FORWARD);
-        if (!i)
+        if (!t || !i)
                 return log_oom();
 
         r = mnt_table_parse_mtab(t, NULL);
@@ -1621,9 +1618,9 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
 
         r = 0;
         for (;;) {
+                struct libmnt_fs *fs;
                 const char *device, *path, *options, *fstype;
                 _cleanup_free_ char *d = NULL, *p = NULL;
-                struct libmnt_fs *fs;
                 int k;
 
                 k = mnt_table_next_fs(t, i, &fs);
index ab2d4ff..8ac40e5 100644 (file)
@@ -25,6 +25,9 @@
 #include <sys/mount.h>
 #include <sys/swap.h>
 
+/* This needs to be after sys/mount.h :( */
+#include <libmount.h>
+
 #include "libudev.h"
 
 #include "alloc-util.h"
@@ -46,6 +49,9 @@
 #include "util.h"
 #include "virt.h"
 
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_table*, mnt_free_table);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter);
+
 typedef struct MountPoint {
         char *path;
         char *remount_options;
@@ -74,46 +80,45 @@ static void mount_points_list_free(MountPoint **head) {
 }
 
 static int mount_points_list_get(MountPoint **head) {
-        _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
-        unsigned int i;
+        _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL;
+        _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL;
         int r;
 
         assert(head);
 
-        proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
-        if (!proc_self_mountinfo)
-                return -errno;
+        t = mnt_new_table();
+        i = mnt_new_iter(MNT_ITER_FORWARD);
+        if (!t || !i)
+                return log_oom();
 
-        for (i = 1;; i++) {
-                _cleanup_free_ char *path = NULL, *options = NULL, *flags = NULL, *type = NULL, *p = NULL;
+        r = mnt_table_parse_mtab(t, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m");
+
+        for (;;) {
+                struct libmnt_fs *fs;
+                const char *path, *options, *fstype;
+                _cleanup_free_ char *d = NULL, *p = NULL;
+                unsigned long remount_flags = 0u;
+                _cleanup_free_ char *remount_options = NULL;
+                bool try_remount_ro;
                 MountPoint *m;
-                int k;
 
-                k = fscanf(proc_self_mountinfo,
-                           "%*s "       /* (1) mount id */
-                           "%*s "       /* (2) parent id */
-                           "%*s "       /* (3) major:minor */
-                           "%*s "       /* (4) root */
-                           "%ms "       /* (5) mount point */
-                           "%ms"        /* (6) mount flags */
-                           "%*[^-]"     /* (7) optional fields */
-                           "- "         /* (8) separator */
-                           "%ms "       /* (9) file system type */
-                           "%*s"        /* (10) mount source */
-                           "%ms"        /* (11) mount options */
-                           "%*[^\n]",   /* some rubbish at the end */
-                           &path, &flags, &type, &options);
-                if (k != 4) {
-                        if (k == EOF)
-                                break;
+                r = mnt_table_next_fs(t, i, &fs);
+                if (r == 1)
+                        break;
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
 
-                        log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
+                path = mnt_fs_get_target(fs);
+                if (!path)
                         continue;
-                }
 
-                r = cunescape(path, UNESCAPE_RELAX, &p);
-                if (r < 0)
-                        return r;
+                if (cunescape(path, UNESCAPE_RELAX, &p) < 0)
+                        return log_oom();
+
+                options = mnt_fs_get_options(fs);
+                fstype = mnt_fs_get_fstype(fs);
 
                 /* Ignore mount points we can't unmount because they
                  * are API or because we are keeping them open (like
@@ -129,11 +134,6 @@ static int mount_points_list_get(MountPoint **head) {
                     path_startswith(p, "/proc"))
                         continue;
 
-                m = new0(MountPoint, 1);
-                if (!m)
-                        return -ENOMEM;
-
-                free_and_replace(m->path, p);
 
                 /* If we are in a container, don't attempt to
                  * read-only mount anything as that brings no real
@@ -145,35 +145,44 @@ static int mount_points_list_get(MountPoint **head) {
                  * a "dirty fs") and could hang if the network is down.
                  * Note that umount2() is more careful and will not
                  * hang because of the network being down. */
-                m->try_remount_ro = detect_container() <= 0 &&
-                                    !fstype_is_network(type) &&
-                                    !fstype_is_api_vfs(type) &&
-                                    !fstype_is_ro(type) &&
-                                    !fstab_test_yes_no_option(options, "ro\0rw\0");
-
-                if (m->try_remount_ro) {
-                        _cleanup_free_ char *unknown_flags = NULL;
+                try_remount_ro = detect_container() <= 0 &&
+                                 !fstype_is_network(fstype) &&
+                                 !fstype_is_api_vfs(fstype) &&
+                                 !fstype_is_ro(fstype) &&
+                                 !fstab_test_yes_no_option(options, "ro\0rw\0");
 
+                if (try_remount_ro) {
                         /* mount(2) states that mount flags and options need to be exactly the same
                          * as they were when the filesystem was mounted, except for the desired
                          * changes. So we reconstruct both here and adjust them for the later
                          * remount call too. */
 
-                        r = mount_option_mangle(flags, 0, &m->remount_flags, &unknown_flags);
-                        if (r < 0)
-                                return r;
-                        if (!isempty(unknown_flags))
-                                log_warning("Ignoring unknown mount flags '%s'.", unknown_flags);
+                        r = mnt_fs_get_propagation(fs, &remount_flags);
+                        if (r < 0) {
+                                log_warning_errno(r, "mnt_fs_get_propagation() failed for %s, ignoring: %m", path);
+                                continue;
+                        }
 
-                        r = mount_option_mangle(options, m->remount_flags, &m->remount_flags, &m->remount_options);
-                        if (r < 0)
-                                return r;
+                        r = mount_option_mangle(options, remount_flags, &remount_flags, &remount_options);
+                        if (r < 0) {
+                                log_warning_errno(r, "mount_option_mangle failed for %s, ignoring: %m", path);
+                                continue;
+                        }
 
                         /* MS_BIND is special. If it is provided it will only make the mount-point
                          * read-only. If left out, the super block itself is remounted, which we want. */
-                        m->remount_flags = (m->remount_flags|MS_REMOUNT|MS_RDONLY) & ~MS_BIND;
+                        remount_flags = (remount_flags|MS_REMOUNT|MS_RDONLY) & ~MS_BIND;
                 }
 
+                m = new0(MountPoint, 1);
+                if (!m)
+                        return log_oom();
+
+                free_and_replace(m->path, p);
+                free_and_replace(m->remount_options, remount_options);
+                m->remount_flags = remount_flags;
+                m->try_remount_ro = try_remount_ro;
+
                 LIST_PREPEND(mount_point, *head, m);
         }