about summary refs log tree commit homepage
path: root/mnt.c
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2012-01-31 13:58:18 -0800
committerEric Wong <normalperson@yhbt.net>2012-01-31 14:09:14 -0800
commit3a51cf662cb8457d22b97a1703bdd4581053ab7f (patch)
treeaaf44cbb42b21fc9c800c66c0b50768801ce16cb /mnt.c
parentc628c03c7063cb2a513b25b6bb6eb5e536b6cbf1 (diff)
downloadcmogstored-3a51cf662cb8457d22b97a1703bdd4581053ab7f.tar.gz
On my recent Debian testing machine, I end up with two
mount points pointing to "/" in /proc/mounts:

	rootfs / rootfs rw 0 0
	/dev/root / ext3 rw,noatime,barrier=1,data=writeback 0 0

This caused confusion in reporting utilization for two
(unrelated) reasons:

1) "/" is mounted twice, so we need to chain identical mounts
   in our hash structure.

2) "/dev/root" is a symlink (to "/dev/sda3" in my case), but
   iostat(1) returns "sda", so we now resolve symlinks for
   the device name.

We could use /proc/partitions on Linux like the Perl version
does, but I (would like to) believe our current code is more
portable.  Perhaps we will eventually have a specialized version
of mnt.c for Linux-only.
Diffstat (limited to 'mnt.c')
-rw-r--r--mnt.c101
1 files changed, 85 insertions, 16 deletions
diff --git a/mnt.c b/mnt.c
index 77e95d3..6bc2810 100644
--- a/mnt.c
+++ b/mnt.c
@@ -17,16 +17,23 @@ struct mog_mntent {
         char util[8];
 };
 
-static void me_free(void *entry)
+static void mount_entry_free(struct mount_entry *me)
 {
-        struct mog_mntent *mntent = entry;
-        struct mount_entry *me = mntent->me;
-
         free(me->me_devname);
         free(me->me_mountdir);
         if (me->me_type_malloced)
                 free(me->me_type);
+        if (me->me_next)
+                mount_entry_free(me->me_next);
         free(me);
+}
+
+static void me_free(void *entry)
+{
+        struct mog_mntent *mntent = entry;
+        struct mount_entry *me = mntent->me;
+
+        mount_entry_free(me);
         free(mntent);
 }
 
@@ -58,11 +65,28 @@ static void mnt_once(void)
         atexit(mnt_atexit);
 }
 
+static bool resolve_symlink(char **orig)
+{
+        char *p = canonicalize_filename_mode(*orig, CAN_EXISTING);
+
+        if (p) {
+                free(*orig);
+                *orig = p;
+                return true;
+        }
+        return false;
+}
+
 void mog_mnt_refresh(void)
 {
         struct mount_entry *head = read_file_system_list(false);
+        struct mount_entry *next;
         struct mog_mntent *mntent;
         struct stat sb;
+        union {
+                const void *ptr;
+                struct mog_mntent *oldent;
+        } exist;
 
         CHECK(int, 0, pthread_mutex_lock(&mnt_lock) );
 
@@ -71,25 +95,44 @@ void mog_mnt_refresh(void)
         else
                 mnt_once();
 
-        while (head) {
-                struct mount_entry *next = head->me_next;
-
+        for ( ; (head && (next = head->me_next)); head = next) {
+                head->me_next = NULL;
                 mntent = xmalloc(sizeof(struct mog_mntent));
                 mntent->me = head;
                 mntent->util[0] = '-';
                 mntent->util[1] = 0;
 
                 /* the device number may not have been populated, do it */
-                if ((head->me_dev == (dev_t)-1) &&
-                    (stat(head->me_mountdir, &sb) == 0))
+                if ((head->me_dev == (dev_t)-1)) {
+                        if (stat(head->me_mountdir, &sb) != 0)
+                                goto skip;
                         head->me_dev = sb.st_dev;
+                }
+
+                if (lstat(head->me_devname, &sb) != 0)
+                        goto skip;
+                if (S_ISLNK(sb.st_mode) && ! resolve_symlink(&head->me_devname))
+                        goto skip;
 
-                switch (hash_insert_if_absent(by_dev, mntent, NULL)) {
-                case 0: me_free(mntent);
-                case 1: break;
+                switch (hash_insert_if_absent(by_dev, mntent, &exist.ptr)) {
+                case 0: {
+                        struct mount_entry *me = exist.oldent->me;
+
+                        while (me->me_next)
+                                me = me->me_next;
+                        assert(me != head);
+                        me->me_next = head;
+                        free(mntent);
+                }
+                        continue;
+                case 1:
+                        continue;
                 default: mog_oom();
                 }
-                head = next;
+                assert(0 && "compiler bug?");
+skip:
+                free(mntent);
+                mount_entry_free(head);
         }
 
         CHECK(int, 0, pthread_mutex_unlock(&mnt_lock) );
@@ -135,6 +178,28 @@ struct mnt_update {
         const char *util;
 };
 
+/* attempt to resolve multiple mounts mapped to the same mount point */
+static bool
+update_util_chain(struct mog_mntent *mntent, struct mnt_update *update)
+{
+        struct mount_entry *me;
+        const char *devname;
+        size_t devnamelen;
+
+        for (me = mntent->me->me_next; me; me = me->me_next) {
+                devname = me->me_devname;
+                devnamelen = strlen(devname);
+
+                if (devnamelen < update->prefixlen)
+                        continue;
+                if (memcmp(update->prefix, devname, update->prefixlen) != 0)
+                        continue;
+                memcpy(mntent->util, update->util, 8);
+        }
+
+        return true;
+}
+
 static bool update_util_each(void *ent, void *upd)
 {
         struct mog_mntent *mntent = ent;
@@ -142,9 +207,13 @@ static bool update_util_each(void *ent, void *upd)
         const char *devname = mntent->me->me_devname;
         size_t devnamelen = strlen(devname);
 
-        if (devnamelen >= update->prefixlen &&
-            memcmp(update->prefix, devname, update->prefixlen) == 0)
-                memcpy(mntent->util, update->util, 8);
+        if (devnamelen < update->prefixlen)
+                return update_util_chain(mntent, update);
+        if (memcmp(update->prefix, devname, update->prefixlen) != 0)
+                return update_util_chain(mntent, update);
+
+        memcpy(mntent->util, update->util, 8);
+
         return true; /* continue */
 }