mount: call hotplug-call with ACTION=remove before trying to unmount
[project/mountd.git] / mount.c
diff --git a/mount.c b/mount.c
index 2d34a91..00fe91d 100644 (file)
--- a/mount.c
+++ b/mount.c
@@ -1,3 +1,4 @@
+#include <errno.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -11,6 +12,7 @@
 #include <dirent.h>
 #include <sys/wait.h>
 #include <sys/inotify.h>
+#include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <glob.h>
@@ -38,11 +40,13 @@ static struct list_head mounts;
  *
  * @STATUS_UNMOUNTED: currently not mounted
  * @STATUS_MOUNTED: mounted & ready for usage
+ * @STATUS_EXPIRED: mount expired & *temporary* unmounted
  * @STATUS_IGNORE: entry should be ignored and never mounted
  */
 enum status {
        STATUS_UNMOUNTED = 0,
        STATUS_MOUNTED,
+       STATUS_EXPIRED,
        STATUS_IGNORE,
 };
 
@@ -150,7 +154,7 @@ static void mount_add_list(char *name, char *dev, char *serial,
        char *vendor, char *model, char *rev, int ignore, char *size, char *sector_size, int fs)
 {
        struct mount *mount;
-       char tmp[64], tmp2[64];
+       char dev_path[64], dev_link[64], tmp[64];
 
        mount  = malloc(sizeof(struct mount));
        INIT_LIST_HEAD(&mount->list);
@@ -169,10 +173,20 @@ static void mount_add_list(char *name, char *dev, char *serial,
        if (ignore) {
                mount->status = STATUS_IGNORE;
        } else {
+               struct stat st;
+
                log_printf("new mount : %s -> %s (%s)\n", name, dev, fs_names[mount->fs]);
-               snprintf(tmp, 64, "%s%s", uci_path, name);
-               snprintf(tmp2, 64, "/tmp/run/mountd/%s", dev);
-               symlink(tmp2, tmp);
+
+               snprintf(dev_link, sizeof(dev_link), "%s%s", uci_path, name);
+               snprintf(dev_path, sizeof(dev_path), "%s%s", "/tmp/run/mountd/", dev);
+               /* If link aleady exists - replace it */
+               if (lstat(dev_link, &st) ==  0 && S_ISLNK(st.st_mode)) {
+                       snprintf(tmp, sizeof(tmp), "%s%s", uci_path, "tmp");
+                       symlink(dev_path, tmp);
+                       rename(tmp, dev_link);
+               } else {
+                       symlink(dev_path, dev_link);
+               }
                if (!mount_new("/tmp/run/mountd/", dev))
                        system_printf("ACTION=add DEVICE=%s NAME=%s /sbin/hotplug-call mount", dev, name);
        }
@@ -323,14 +337,14 @@ int mount_remove(char *path, char *dev)
        char tmp[256];
        int ret;
        snprintf(tmp, 256, "%s%s", path, dev);
-       log_printf("%s has expired... unmounting\n", tmp);
+       log_printf("device %s has expired... unmounting %s\n", dev, tmp);
        ret = system_printf("/bin/umount %s", tmp);
        if(ret != 0)
                return 0;
        rmdir(tmp);
        mount = mount_find(0, dev);
        if(mount)
-               mount->status = STATUS_UNMOUNTED;
+               mount->status = STATUS_EXPIRED;
        log_printf("finished unmounting\n");
        mount_dump_uci_state();
        return 0;
@@ -580,20 +594,23 @@ static void mount_dev_add(char *dev)
        }
 }
 
-static void mount_dev_del(struct mount *mount)
+static int mount_dev_del(struct mount *mount)
 {
        char tmp[256];
+       int err = 0;
 
        if (mount->status == STATUS_MOUNTED) {
-               snprintf(tmp, 256, "%s%s", "/tmp/run/mountd/", mount->name);
-               log_printf("%s has dissappeared ... unmounting\n", tmp);
                snprintf(tmp, 256, "%s%s", "/tmp/run/mountd/", mount->dev);
-               system_printf("/bin/umount %s", tmp);
+               log_printf("device %s has disappeared ... unmounting %s\n", mount->dev, tmp);
+               if (umount(tmp)) {
+                       err = -errno;
+                       umount2(tmp, MNT_DETACH);
+               }
                rmdir(tmp);
-               snprintf(tmp, 64, "%s%s", uci_path, mount->name);
-               unlink(tmp);
                mount_dump_uci_state();
        }
+
+       return err;
 }
 
 void mount_dump_list(void)
@@ -757,19 +774,33 @@ static void mount_enum_drives(void)
                }
                if(!check_block(q->dev)||del)
                {
-                       mount_dev_del(q);
+                       if (q->status == STATUS_MOUNTED || q->status == STATUS_EXPIRED) {
+                               char dev_link[64];
+                               int err;
+
+                               system_printf("ACTION=remove DEVICE=%s NAME=%s /sbin/hotplug-call mount", q->dev, q->name);
+
+                               err = mount_dev_del(q);
+
+                               snprintf(dev_link, sizeof(dev_link), "%s%s", uci_path, q->name);
+                               if (err == -EBUSY) {
+                                       /* Create "tmp" symlink to non-existing path */
+                                       snprintf(tmp, sizeof(tmp), "%s%s", uci_path, "tmp");
+                                       symlink("## DEVICE MISSING ##", tmp);
+
+                                       /* Replace old symlink with the not working one */
+                                       rename(tmp, dev_link);
+                               } else {
+                                       log_printf("unlinking %s\n", dev_link);
+                                       unlink(dev_link);
+                               }
+                       }
+
                        p->prev->next = p->next;
                        p->next->prev = p->prev;
                        p = p->next;
-                       log_printf("removing %s\n", q->dev);
-                       if (q->status == STATUS_MOUNTED) {
-                               snprintf(tmp, 64, "%s%s", "/tmp/run/mountd/", q->dev);
-                               rmdir(tmp);
-                               snprintf(tmp, 64, "%s%s", uci_path, q->name);
-                               unlink(tmp);
-                               system_printf("ACTION=remove DEVICE=%s NAME=%s /sbin/hotplug-call mount", q->dev, q->name);
-                       }
                        free(q);
+
                        mount_dump_uci_state();
                        system_printf("/etc/fonstated/ReloadSamba");
                } else p = p->next;