]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
umount: don't use mountinfo if possible
authorKarel Zak <kzak@redhat.com>
Mon, 3 Mar 2014 09:36:15 +0000 (10:36 +0100)
committerKarel Zak <kzak@redhat.com>
Mon, 3 Mar 2014 09:36:15 +0000 (10:36 +0100)
The umount(8) always parses /proc/self/mountinfo to get fstype and to
merge kernel mount options with userspace mount options from
/run/mount/utab. This behavior is overkill in many cases and it's
pretty expensive as kernel has to always compose *whole* mountinfo.
This performance disadvantage is visible for crazy use-cases with huge
number of mountpoints and frequently called umount(8).

It seems that we can bypass /proc/self/mountinfo by statfs() to get
filesystem type (statfs.f_type magic) and analyze /run/mount/utab
before we parse mountinfo.

This optimization is not used when:

 * umount(8) executed by non-root (as user= in utab is expected)
 * umount --lazy / --force (target is probably unreachable NFS, then
   use statfs() is pretty bad idea)
 * target is not a directory (e.g. umount /dev/sda1)
 * there is (deprecated) writeable mtab

Reported-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/src/context.c
libmount/src/context_umount.c
libmount/src/mountP.h
libmount/src/tab_parse.c
libmount/src/utils.c

index 2ef3d3001ec964d00ca893f95031c0f4bcfcfa94..f1077968f2391ca9cd0d99dd42060ba4b4de3d98 100644 (file)
@@ -136,12 +136,14 @@ int mnt_reset_context(struct libmnt_context *cxt)
 
        mnt_unref_fs(cxt->fs);
        mnt_unref_table(cxt->mtab);
+       mnt_unref_table(cxt->utab);
 
        free(cxt->helper);
        free(cxt->orig_user);
 
        cxt->fs = NULL;
        cxt->mtab = NULL;
+       cxt->utab = NULL;
        cxt->helper = NULL;
        cxt->orig_user = NULL;
        cxt->mountflags = 0;
@@ -158,7 +160,9 @@ int mnt_reset_context(struct libmnt_context *cxt)
        }
 
        mnt_context_reset_status(cxt);
-       mnt_context_set_tabfilter(cxt, NULL, NULL);
+
+       if (cxt->table_fltrcb)
+               mnt_context_set_tabfilter(cxt, NULL, NULL);
 
        /* restore non-resettable flags */
        cxt->flags |= (fl & MNT_FL_NOMTAB);
@@ -980,7 +984,12 @@ int mnt_context_get_mtab(struct libmnt_context *cxt, struct libmnt_table **tb)
                                        cxt->table_fltrcb_data);
 
                mnt_table_set_cache(cxt->mtab, mnt_context_get_cache(cxt));
-               rc = mnt_table_parse_mtab(cxt->mtab, cxt->mtab_path);
+               if (cxt->utab)
+                       /* utab already parsed, don't parse it again */
+                       rc = __mnt_table_parse_mtab(cxt->mtab,
+                                                   cxt->mtab_path, cxt->utab);
+               else
+                       rc = mnt_table_parse_mtab(cxt->mtab, cxt->mtab_path);
                if (rc)
                        return rc;
        }
@@ -1013,7 +1022,7 @@ int mnt_context_set_tabfilter(struct libmnt_context *cxt,
                                cxt->table_fltrcb,
                                cxt->table_fltrcb_data);
 
-       DBG(CXT, mnt_debug_h(cxt, "tabfiler %s", fltr ? "ENABLED!" : "disabled"));
+       DBG(CXT, mnt_debug_h(cxt, "tabfilter %s", fltr ? "ENABLED!" : "disabled"));
        return 0;
 }
 
index dc16852c0d6e611888bffceaa974a5ee756ef3b9..2e22389f409a1aacc7739cc6595b282ed111df0c 100644 (file)
@@ -98,7 +98,6 @@ int mnt_context_find_umount_fs(struct libmnt_context *cxt,
                struct stat st;
 
                if (stat(tgt, &st) == 0 && S_ISDIR(st.st_mode)) {
-                       /* we'll canonicalize /proc/self/mountinfo */
                        cache = mnt_context_get_cache(cxt);
                        cn_tgt = mnt_resolve_path(tgt, cache);
                        if (cn_tgt)
@@ -190,14 +189,52 @@ err:
        return rc;
 }
 
+/* Check if there is something important in the utab file. The parsed utab is
+ * stored in context->utab and deallocated by mnt_free_context().
+ */
+static int has_utab_entry(struct libmnt_context *cxt, const char *target)
+{
+       struct libmnt_cache *cache = NULL;
+       struct libmnt_fs *fs;
+       struct libmnt_iter itr;
+       char *cn = NULL;
+
+       assert(cxt);
+
+       if (!cxt->utab) {
+               const char *path = mnt_get_utab_path();
+
+               if (!path || is_file_empty(path))
+                       return 0;
+               cxt->utab = mnt_new_table();
+               if (!cxt->utab)
+                       return 0;
+               cxt->utab->fmt = MNT_FMT_UTAB;
+               if (mnt_table_parse_file(cxt->utab, path))
+                       return 0;
+       }
+
+       /* paths in utab are canonicalized */
+       cache = mnt_context_get_cache(cxt);
+       cn = mnt_resolve_path(target, cache);
+       mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
+
+       while (mnt_table_next_fs(cxt->utab, &itr, &fs) == 0) {
+               if (mnt_fs_streq_target(fs, cn))
+                       return 1;
+       }
+       return 0;
+}
+
 /* this is umount replacement to mnt_context_apply_fstab(), use
  * mnt_context_tab_applied() to check result.
  */
 static int lookup_umount_fs(struct libmnt_context *cxt)
 {
        const char *tgt;
+       struct stat st;
        struct libmnt_fs *fs = NULL;
-       int rc;
+       int rc = 0;
 
        assert(cxt);
        assert(cxt->fs);
@@ -208,9 +245,53 @@ static int lookup_umount_fs(struct libmnt_context *cxt)
                return -EINVAL;
        }
 
+       /*
+        * Let's try to avoid mountinfo usage at all to minimize performance
+        * degradation. Don't forget that kernel has to compose *whole*
+        * mountinfo about all mountpoints although we look for only one entry.
+        *
+        * All we need is fstype and to check if there is no userspace mount
+        * options for the target (e.g. helper=udisks to call /sbin/umount.udisks).
+        *
+        * So, let's use statfs() if possible (it's bad idea for --lazy/--force
+        * umounts as target is probably unreachable NFS).
+        */
+       if (!mnt_context_is_restricted(cxt)
+           && *tgt == '/'
+           && !(cxt->flags & MNT_FL_HELPER)
+           && !cxt->mtab_writable
+           && !mnt_context_is_force(cxt)
+           && !mnt_context_is_lazy(cxt)
+           && stat(tgt, &st) == 0 && S_ISDIR(st.st_mode)
+           && !has_utab_entry(cxt, tgt)) {
+
+               const char *type = mnt_fs_get_fstype(cxt->fs);
+
+               /* !cxt->mtab_writable && has_utab_entry() verified that there
+                * is no stuff in utab, so disable all mtab/utab related actions */
+               mnt_context_disable_mtab(cxt, TRUE);
+
+               if (!type) {
+                       struct statfs vfs;
+                       if (statfs(tgt, &vfs) == 0)
+                               type = mnt_statfs_get_fstype(&vfs);
+                       if (type) {
+                               rc = mnt_fs_set_fstype(cxt->fs, type);
+                               if (rc)
+                                       return rc;
+                       }
+               }
+               if (type) {
+                       DBG(CXT, mnt_debug_h(cxt,
+                               "umount: mountinfo unnecessary [type=%s]", type));
+                       return 0;
+               }
+       }
+
        rc = mnt_context_find_umount_fs(cxt, tgt, &fs);
        if (rc < 0)
                return rc;
+
        if (rc == 1 || !fs) {
                DBG(CXT, mnt_debug_h(cxt, "umount: cannot find '%s' in mtab", tgt));
                return 0;       /* this is correct! */
index adc245af4a949662d0fbab121396dce159b4adde..a580ae4388095ed242baabf8eba44f90afde59a3 100644 (file)
@@ -15,6 +15,7 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/vfs.h>
 #include <unistd.h>
 
 #include "c.h"
@@ -142,6 +143,7 @@ extern char *stripoff_last_component(char *path);
 extern int mnt_valid_tagname(const char *tagname);
 extern int append_string(char **a, const char *b);
 
+extern const char *mnt_statfs_get_fstype(struct statfs *vfs);
 extern int is_file_empty(const char *name);
 
 extern int mkdir_p(const char *path, mode_t mode);
@@ -178,6 +180,11 @@ extern struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb,
                                         struct libmnt_fs *fs,
                                         unsigned long mountflags,
                                         char **fsroot);
+extern int __mnt_table_parse_mtab(struct libmnt_table *tb,
+                                       const char *filename,
+                                       struct libmnt_table *u_tb);
+
+
 /*
  * Generic iterator
  */
@@ -327,6 +334,7 @@ struct libmnt_context
 
        struct libmnt_table *fstab;     /* fstab (or mtab for some remounts) entries */
        struct libmnt_table *mtab;      /* mtab entries */
+       struct libmnt_table *utab;      /* rarely used by umount only */
 
        int     (*table_errcb)(struct libmnt_table *tb, /* callback for libmnt_table structs */
                         const char *filename, int line);
index 81232736cacb62d05e8cc85360142a4dbf95db4d..7c73cc5535996dfdf3ccd7321d5648af014757e9 100644 (file)
@@ -1036,23 +1036,10 @@ static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct
        return fs;
 }
 
-/**
- * mnt_table_parse_mtab:
- * @tb: table
- * @filename: overwrites default (/etc/mtab or $LIBMOUNT_MTAB) or NULL
- *
- * This function parses /etc/mtab or /proc/self/mountinfo +
- * /run/mount/utabs or /proc/mounts.
- *
- * See also mnt_table_set_parser_errcb().
- *
- * Returns: 0 on success or negative number in case of error.
- */
-int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename)
+int __mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename,
+                          struct libmnt_table *u_tb)
 {
-       int rc;
-       const char *utab = NULL;
-       struct libmnt_table *u_tb;
+       int rc = 0, priv_utab = 0;
 
        assert(tb);
 
@@ -1083,18 +1070,23 @@ int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename)
        /*
         * try to read the user specific information from /run/mount/utabs
         */
-       utab = mnt_get_utab_path();
-       if (!utab || is_file_empty(utab))
-               return 0;
+       if (!u_tb) {
+               const char *utab = mnt_get_utab_path();
+               if (!utab || is_file_empty(utab))
+                       return 0;
+
+               u_tb = mnt_new_table();
+               if (!u_tb)
+                       return -ENOMEM;
 
-       u_tb = mnt_new_table();
-       if (!u_tb)
-               return -ENOMEM;
+               u_tb->fmt = MNT_FMT_UTAB;
+               mnt_table_set_parser_fltrcb(u_tb, tb->fltrcb, tb->fltrcb_data);
 
-       u_tb->fmt = MNT_FMT_UTAB;
-       mnt_table_set_parser_fltrcb(u_tb, tb->fltrcb, tb->fltrcb_data);
+               rc = mnt_table_parse_file(u_tb, utab);
+               priv_utab = 1;
+       }
 
-       if (mnt_table_parse_file(u_tb, utab) == 0) {
+       if (rc == 0) {
                struct libmnt_fs *u_fs;
                struct libmnt_iter itr;
 
@@ -1105,6 +1097,24 @@ int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename)
                        mnt_table_merge_user_fs(tb, u_fs);
        }
 
-       mnt_unref_table(u_tb);
+
+       if (priv_utab)
+               mnt_unref_table(u_tb);
        return 0;
 }
+/**
+ * mnt_table_parse_mtab:
+ * @tb: table
+ * @filename: overwrites default (/etc/mtab or $LIBMOUNT_MTAB) or NULL
+ *
+ * This function parses /etc/mtab or /proc/self/mountinfo +
+ * /run/mount/utabs or /proc/mounts.
+ *
+ * See also mnt_table_set_parser_errcb().
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename)
+{
+       return __mnt_table_parse_mtab(tb, filename, NULL);
+}
index 4111b595b9ba0171da76ba13d07934a945831653..6170752bc2a3ebcb7a105b063b69639d4b231175 100644 (file)
@@ -15,6 +15,7 @@
 #include <pwd.h>
 #include <grp.h>
 #include <blkid.h>
+#include <linux/magic.h>
 
 #include "strutils.h"
 #include "pathnames.h"
@@ -320,6 +321,92 @@ int mnt_fstype_is_netfs(const char *type)
        return 0;
 }
 
+#ifndef CIFS_SUPER_MAGIC
+# define CIFS_SUPER_MAGIC      0xFF534D42
+#endif
+#ifndef XFS_SUPER_MAGIC
+# define XFS_SUPER_MAGIC       0x58465342
+#endif
+#ifndef CGROUP_SUPER_MAGIC
+# define CGROUP_SUPER_MAGIC    0x1021994
+#endif
+#ifndef MQUEUE_SUPER_MAGIC
+# define MQUEUE_SUPER_MAGIC    0x19800202
+#endif
+#ifndef CONFIGFS_SUPER_MAGIC
+# define CONFIGFS_SUPER_MAGIC  0x62656570
+#endif
+
+const char *mnt_statfs_get_fstype(struct statfs *vfs)
+{
+       assert(vfs);
+
+       switch (vfs->f_type) {
+       case ADFS_SUPER_MAGIC:          return "adfs";
+       case AFFS_SUPER_MAGIC:          return "affs";
+       case AFS_SUPER_MAGIC:           return "afs";
+       case AUTOFS_SUPER_MAGIC:        return "autofs";
+       case CIFS_SUPER_MAGIC:          return "cifs";
+       case CODA_SUPER_MAGIC:          return "coda";
+       case CRAMFS_MAGIC:              return "cramfs";
+       case DEBUGFS_MAGIC:             return "debugfs";
+       case SECURITYFS_MAGIC:          return "securityfs";
+       case SELINUX_MAGIC:             return "selinuxfs";
+       case SMACK_MAGIC:               return "smackfs";
+       case RAMFS_MAGIC:               return "ramfs";
+       case TMPFS_MAGIC:               return "tmpfs";
+       case HUGETLBFS_MAGIC:           return "hugetlbfs";
+       case SQUASHFS_MAGIC:            return "squashfs";
+       case ECRYPTFS_SUPER_MAGIC:      return "ecryptfs";
+       case EFS_SUPER_MAGIC:           return "efs";
+       case EXT4_SUPER_MAGIC:          return "ext4";
+       case BTRFS_SUPER_MAGIC:         return "btrfs";
+       case NILFS_SUPER_MAGIC:         return "nilfs2";
+       case F2FS_SUPER_MAGIC:          return "f2fs";
+       case HPFS_SUPER_MAGIC:          return "hpfs";
+       case ISOFS_SUPER_MAGIC:         return "iso9660";
+       case JFFS2_SUPER_MAGIC:         return "jffs";
+       case EFIVARFS_MAGIC:            return "efivarfs";
+       case HOSTFS_SUPER_MAGIC:        return "hostfs";
+
+       case MINIX_SUPER_MAGIC:
+       case MINIX_SUPER_MAGIC2:
+       case MINIX2_SUPER_MAGIC:        return "minix";
+
+       case MSDOS_SUPER_MAGIC:         return "vfat";
+       case NCP_SUPER_MAGIC:           return "ncp";
+       case NFS_SUPER_MAGIC:           return "nfs";
+       case OPENPROM_SUPER_MAGIC:      return "openprom";
+       case PROC_SUPER_MAGIC:          return "proc";
+       case SOCKFS_MAGIC:              return "sockfs";
+       case SYSFS_MAGIC:               return "sysfs";
+       case USBDEVICE_SUPER_MAGIC:     return "usbdevice";
+       case CGROUP_SUPER_MAGIC:        return "cgroup";
+       case PSTOREFS_MAGIC:            return "pstore";
+       case BINFMTFS_MAGIC:            return "binfmt_misc";
+       case XENFS_SUPER_MAGIC:         return "xenfs";
+
+       case QNX4_SUPER_MAGIC:          return "qnx4";
+       case QNX6_SUPER_MAGIC:          return "qnx4";
+       case REISERFS_SUPER_MAGIC:      return "reiser4";
+       case SMB_SUPER_MAGIC:           return "smb";
+
+       case DEVPTS_SUPER_MAGIC:        return "devpts";
+       case FUTEXFS_SUPER_MAGIC:       return "futexfs";
+       case PIPEFS_MAGIC:              return "pipefs";
+       case MQUEUE_SUPER_MAGIC:        return "mqueue";
+       case CONFIGFS_SUPER_MAGIC:      return "configfs";
+
+       case BTRFS_TEST_MAGIC:          return "btrfs";
+       case XFS_SUPER_MAGIC:           return "xfs";
+       default:
+               break;
+       }
+
+       return NULL;
+}
+
+
 /**
  * mnt_match_fstype:
  * @type: filesystem type
@@ -1195,6 +1282,21 @@ int test_mkdir(struct libmnt_test *ts, int argc, char *argv[])
        return rc;
 }
 
+int test_statfs_type(struct libmnt_test *ts, int argc, char *argv[])
+{
+       struct statfs vfs;
+       int rc;
+
+       rc = statfs(argv[1], &vfs);
+       if (rc)
+               printf("%s: statfs failed: %m\n", argv[1]);
+       else
+               printf("%-30s: statfs type: %-12s [0x%lx]\n", argv[1],
+                               mnt_statfs_get_fstype(&vfs),
+                               (long) vfs.f_type);
+       return rc;
+}
+
 
 int main(int argc, char *argv[])
 {
@@ -1210,6 +1312,7 @@ int main(int argc, char *argv[])
        { "--cd-parent",     test_chdir,           "<path>" },
        { "--kernel-cmdline",test_kernel_cmdline,  "<option> | <option>=" },
        { "--mkdir",         test_mkdir,           "<path>" },
+       { "--statfs-type",   test_statfs_type,     "<path>" },
 
        { NULL }
        };