]> git.ozlabs.org Git - petitboot/blobdiff - discover/device-handler.c
discover: Don't continue discovery if mount fails
[petitboot] / discover / device-handler.c
index f9d2dbf5d53f1450d56c0c201738567cfb9b9166..8b4046edd83e52a686a7ee0b29768a4bffb1f36f 100644 (file)
@@ -1,9 +1,11 @@
 
 #include <assert.h>
 #include <stdlib.h>
+#include <stdbool.h>
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <mntent.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 
@@ -103,6 +105,12 @@ static int device_match_id(struct discover_device *dev, const char *id)
        return !strcmp(dev->device->id, id);
 }
 
+static int device_match_serial(struct discover_device *dev, const char *serial)
+{
+       const char *val = discover_device_get_param(dev, "ID_SERIAL");
+       return val && !strcmp(val, serial);
+}
+
 static struct discover_device *device_lookup(
                struct device_handler *device_handler,
                int (match_fn)(struct discover_device *, const char *),
@@ -154,6 +162,13 @@ struct discover_device *device_lookup_by_id(
        return device_lookup(device_handler, device_match_id, id);
 }
 
+struct discover_device *device_lookup_by_serial(
+               struct device_handler *device_handler,
+               const char *serial)
+{
+       return device_lookup(device_handler, device_match_serial, serial);
+}
+
 void device_handler_destroy(struct device_handler *handler)
 {
        talloc_free(handler);
@@ -327,6 +342,8 @@ static int default_timeout(void *arg)
                return 0;
        }
 
+       handler->timeout_waiter = NULL;
+
        pb_log("Timeout expired, booting default option %s\n", opt->option->id);
 
        boot(handler, handler->default_boot_option, NULL,
@@ -372,8 +389,14 @@ static void set_default(struct device_handler *handler,
                cur_prio = default_option_priority(
                                        handler->default_boot_option);
 
-               if (new_prio >= cur_prio)
-                       return;
+               if (new_prio < cur_prio) {
+                       handler->default_boot_option = opt;
+                       /* extend the timeout a little, so the user sees some
+                        * indication of the change */
+                       handler->sec_to_boot += 2;
+               }
+
+               return;
        }
 
        handler->sec_to_boot = config_get()->autoboot_timeout_sec;
@@ -563,13 +586,16 @@ int device_handler_discover(struct device_handler *handler,
                struct discover_device *dev, enum conf_method method)
 {
        struct discover_context *ctx;
+       int rc;
 
        process_boot_option_queue(handler);
 
        /* create our context */
        ctx = device_handler_discover_context_create(handler, dev);
 
-       mount_device(dev);
+       rc = mount_device(dev);
+       if (rc)
+               goto out;
 
        /* run the parsers. This will populate the ctx's boot_option list. */
        iterate_parsers(ctx, method);
@@ -577,6 +603,7 @@ int device_handler_discover(struct device_handler *handler,
        /* add discovered stuff to the handler */
        device_handler_discover_context_commit(handler, ctx);
 
+out:
        talloc_free(ctx);
 
        return 0;
@@ -656,6 +683,58 @@ void device_handler_cancel_default(struct device_handler *handler)
 }
 
 #ifndef PETITBOOT_TEST
+static bool check_existing_mount(struct discover_device *dev)
+{
+       struct stat devstat, mntstat;
+       struct mntent *mnt;
+       FILE *fp;
+       int rc;
+
+       rc = stat(dev->device_path, &devstat);
+       if (rc) {
+               pb_debug("%s: stat failed: %s\n", __func__, strerror(errno));
+               return false;
+       }
+
+       if (!S_ISBLK(devstat.st_mode)) {
+               pb_debug("%s: %s isn't a block device?\n", __func__,
+                               dev->device_path);
+               return false;
+       }
+
+       fp = fopen("/proc/self/mounts", "r");
+
+       for (;;) {
+               mnt = getmntent(fp);
+               if (!mnt)
+                       break;
+
+               if (!mnt->mnt_fsname || mnt->mnt_fsname[0] != '/')
+                       continue;
+
+               rc = stat(mnt->mnt_fsname, &mntstat);
+               if (rc)
+                       continue;
+
+               if (!S_ISBLK(mntstat.st_mode))
+                       continue;
+
+               if (mntstat.st_rdev == devstat.st_rdev) {
+                       pb_debug("%s: %s is already mounted at %s\n"
+                                       __func__, dev->device_path,
+                                       mnt->mnt_dir);
+                       dev->mount_path = talloc_strdup(dev, mnt->mnt_dir);
+                       dev->mounted = true;
+                       dev->unmount = false;
+                       break;
+               }
+       }
+
+       fclose(fp);
+
+       return mnt != NULL;
+}
+
 static int mount_device(struct discover_device *dev)
 {
        int rc;
@@ -663,29 +742,44 @@ static int mount_device(struct discover_device *dev)
        if (!dev->device_path)
                return -1;
 
-       if (!dev->mount_path)
-               dev->mount_path = join_paths(dev, mount_base(),
-                                               dev->device_path);
+       if (dev->mounted)
+               return 0;
+
+       if (check_existing_mount(dev))
+               return 0;
+
+       dev->mount_path = join_paths(dev, mount_base(),
+                                       dev->device_path);
 
-       if (pb_mkdir_recursive(dev->mount_path))
+       if (pb_mkdir_recursive(dev->mount_path)) {
                pb_log("couldn't create mount directory %s: %s\n",
                                dev->mount_path, strerror(errno));
+               goto err_free;
+       }
 
        rc = process_run_simple(dev, pb_system_apps.mount,
                        dev->device_path, dev->mount_path,
                        "-o", "ro", NULL);
-
-       if (!rc)
+       if (!rc) {
+               dev->mounted = true;
+               dev->unmount = true;
                return 0;
+       }
 
        /* Retry mount without ro option. */
        rc = process_run_simple(dev, pb_system_apps.mount,
                        dev->device_path, dev->mount_path, NULL);
 
-       if (!rc)
+       if (!rc) {
+               dev->mounted = true;
+               dev->unmount = true;
                return 0;
+       }
 
        pb_rmdir_recursive(mount_base(), dev->mount_path);
+err_free:
+       talloc_free(dev->mount_path);
+       dev->mount_path = NULL;
        return -1;
 }
 
@@ -693,7 +787,7 @@ static int umount_device(struct discover_device *dev)
 {
        int status;
 
-       if (!dev->mount_path)
+       if (!dev->mounted || !dev->unmount)
                return 0;
 
        status = process_run_simple(dev, pb_system_apps.umount,
@@ -702,6 +796,10 @@ static int umount_device(struct discover_device *dev)
        if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
                return -1;
 
+       dev->mounted = false;
+       talloc_free(dev->mount_path);
+       dev->mount_path = NULL;
+
        pb_rmdir_recursive(mount_base(), dev->mount_path);
 
        return 0;