diff options
author | Eric Wong <normalperson@yhbt.net> | 2012-01-31 13:58:18 -0800 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2012-01-31 14:09:14 -0800 |
commit | 3a51cf662cb8457d22b97a1703bdd4581053ab7f (patch) | |
tree | aaf44cbb42b21fc9c800c66c0b50768801ce16cb /mnt.c | |
parent | c628c03c7063cb2a513b25b6bb6eb5e536b6cbf1 (diff) | |
download | cmogstored-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.c | 101 |
1 files changed, 85 insertions, 16 deletions
@@ -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 */ } |