X-Git-Url: http://git.ozlabs.org/?p=petitboot;a=blobdiff_plain;f=discover%2Fdevice-handler.c;h=1c69de02e7f85a8f9dfe5166af9d4bb2cbaab95a;hp=5df070073b4d298c8a49a5488010434b73ff4f32;hb=1a72ffef7837cd2aa3e6be945ec03fb6b5caa51a;hpb=f155a58a64c660e46e74123293482561b816a39a diff --git a/discover/device-handler.c b/discover/device-handler.c index 5df0700..1c69de0 100644 --- a/discover/device-handler.c +++ b/discover/device-handler.c @@ -396,7 +396,7 @@ void device_handler_remove(struct device_handler *handler, talloc_free(device); } -static void boot_status(void *arg, struct boot_status *status) +void device_handler_boot_status(void *arg, struct boot_status *status) { struct device_handler *handler = arg; @@ -445,8 +445,11 @@ static int default_timeout(void *arg) pb_log("Timeout expired, booting default option %s\n", opt->option->id); + platform_pre_boot(); + handler->pending_boot = boot(handler, handler->default_boot_option, - NULL, handler->dry_run, boot_status, handler); + NULL, handler->dry_run, device_handler_boot_status, + handler); handler->pending_boot_is_default = true; return 0; } @@ -871,7 +874,7 @@ int device_handler_discover(struct device_handler *handler, status->message = talloc_asprintf(status, _("Processing %s device %s"), device_type_display_name(dev->device->type), dev->device->id); - boot_status(handler, status); + device_handler_boot_status(handler, status); process_boot_option_queue(handler); @@ -899,7 +902,7 @@ out: */ status->message = talloc_asprintf(status,_("Processing %s complete"), dev->device->id); - boot_status(handler, status); + device_handler_boot_status(handler, status); talloc_free(status); talloc_free(ctx); @@ -922,7 +925,7 @@ int device_handler_dhcp(struct device_handler *handler, */ status->message = talloc_asprintf(status, _("Processing dhcp event on %s"), dev->device->id); - boot_status(handler, status); + device_handler_boot_status(handler, status); /* create our context */ ctx = device_handler_discover_context_create(handler, dev); @@ -938,7 +941,7 @@ int device_handler_dhcp(struct device_handler *handler, */ status->message = talloc_asprintf(status,_("Processing %s complete"), dev->device->id); - boot_status(handler, status); + device_handler_boot_status(handler, status); talloc_free(status); talloc_free(ctx); @@ -956,7 +959,7 @@ int device_handler_conf(struct device_handler *handler, status = talloc_zero(handler, struct boot_status); status->type = BOOT_STATUS_INFO; status->message = talloc_asprintf(status, _("Processing user config")); - boot_status(handler, status); + device_handler_boot_status(handler, status); /* create our context */ ctx = device_handler_discover_context_create(handler, dev); @@ -968,7 +971,7 @@ int device_handler_conf(struct device_handler *handler, status->message = talloc_asprintf(status, _("Processing user config complete")); - boot_status(handler, status); + device_handler_boot_status(handler, status); talloc_free(status); talloc_free(ctx); @@ -1007,7 +1010,7 @@ void device_handler_boot(struct device_handler *handler, platform_pre_boot(); handler->pending_boot = boot(handler, opt, cmd, handler->dry_run, - boot_status, handler); + device_handler_boot_status, handler); handler->pending_boot_is_default = false; } @@ -1099,10 +1102,12 @@ static char *device_from_addr(void *ctx, struct pb_url *url) rc = process_run_sync(p); - if (rc) { + if (rc || p->exit_status) { /* ip has complained for some reason; most likely * there is no route to the host - bail out */ - pb_debug("%s: No route to %s\n",__func__,url->host); + pb_debug("%s: `ip` returns non-zero exit status\n", __func__); + pb_debug("ip buf: %s\n", p->stdout_buf); + process_release(p); return NULL; } @@ -1173,13 +1178,17 @@ void device_handler_process_url(struct device_handler *handler, } pb_url = pb_url_parse(event, event->params->value); - if (!pb_url || !pb_url->host) { + if (!pb_url || (pb_url->scheme != pb_url_file && !pb_url->host)) { status->message = talloc_asprintf(handler, _("Invalid config URL!")); goto msg; } - event->device = device_from_addr(event, pb_url); + if (pb_url->scheme == pb_url_file) + event->device = talloc_asprintf(event, "local"); + else + event->device = device_from_addr(event, pb_url); + if (!event->device) { status->message = talloc_asprintf(status, _("Unable to route to host %s"), @@ -1188,6 +1197,8 @@ void device_handler_process_url(struct device_handler *handler, } dev = discover_device_create(handler, event->device); + if (pb_url->scheme == pb_url_file) + dev->device->type = DEVICE_TYPE_ANY; ctx = device_handler_discover_context_create(handler, dev); ctx->event = event; @@ -1201,7 +1212,7 @@ void device_handler_process_url(struct device_handler *handler, status->message = talloc_asprintf(status, _("Config file %s parsed"), pb_url->file); msg: - boot_status(handler, status); + device_handler_boot_status(handler, status); talloc_free(status); } @@ -1256,33 +1267,31 @@ static void device_handler_reinit_sources(struct device_handler *handler) handler->dry_run); } -static const char *fs_parameters(struct discover_device *dev, - unsigned int rw_flags) +static inline const char *get_device_path(struct discover_device *dev) { - const char *fstype = discover_device_get_param(dev, "ID_FS_TYPE"); - - /* XFS journals are not cross-endian compatible; don't try recovery - * even if we have a snapshot */ - if (!strncmp(fstype, "xfs", strlen("xfs"))) - return "norecovery"; - - /* If we have a snapshot available allow touching the filesystem */ - if (dev->ramdisk) - return ""; + return dev->ramdisk ? dev->ramdisk->snapshot : dev->device_path; +} - if ((rw_flags | MS_RDONLY) != MS_RDONLY) - return ""; +static char *check_subvols(struct discover_device *dev) +{ + const char *fstype = discover_device_get_param(dev, "ID_FS_TYPE"); + struct stat sb; + char *path; + int rc; - /* Avoid writes due to journal replay if we don't have a snapshot */ - if (!strncmp(fstype, "ext4", strlen("ext4"))) - return "norecovery"; + if (strncmp(fstype, "btrfs", strlen("btrfs"))) + return dev->mount_path; - return ""; -} + /* On btrfs a device's root may be under a subvolume path */ + path = join_paths(dev, dev->mount_path, "@"); + rc = stat(path, &sb); + if (!rc && S_ISDIR(sb.st_mode)) { + pb_debug("Using '%s' for btrfs root path\n", path); + return path; + } -static inline const char *get_device_path(struct discover_device *dev) -{ - return dev->ramdisk ? dev->ramdisk->snapshot : dev->device_path; + talloc_free(path); + return dev->mount_path; } static bool check_existing_mount(struct discover_device *dev) @@ -1326,6 +1335,7 @@ static bool check_existing_mount(struct discover_device *dev) if (mntstat.st_rdev == devstat.st_rdev) { dev->mount_path = talloc_strdup(dev, mnt->mnt_dir); + dev->root_path = check_subvols(dev); dev->mounted_rw = !!hasmntopt(mnt, "rw"); dev->mounted = true; dev->unmount = false; @@ -1343,6 +1353,52 @@ static bool check_existing_mount(struct discover_device *dev) return mnt != NULL; } +/* + * Attempt to mount a filesystem safely, while handling certain filesytem- + * specific options + */ +static int try_mount(const char *device_path, const char *mount_path, + const char *fstype, unsigned long flags, + bool have_snapshot) +{ + const char *fs, *safe_opts; + int rc; + + /* Mount ext3 as ext4 instead so 'norecovery' can be used */ + if (strncmp(fstype, "ext3", strlen("ext3")) == 0) { + pb_debug("Mounting ext3 filesystem as ext4\n"); + fs = "ext4"; + } else + fs = fstype; + + if (strncmp(fs, "xfs", strlen("xfs")) == 0 || + strncmp(fs, "ext4", strlen("ext4")) == 0) + safe_opts = "norecovery"; + else + safe_opts = NULL; + + errno = 0; + /* If no snapshot is available don't attempt recovery */ + if (!have_snapshot) + return mount(device_path, mount_path, fs, flags, safe_opts); + + rc = mount(device_path, mount_path, fs, flags, NULL); + + if (!rc) + return rc; + + /* Mounting failed; some filesystems will fail to mount if a recovery + * journal exists (eg. cross-endian XFS), so try again with norecovery + * where that option is available. + * If mounting read-write just return the error as norecovery is not a + * valid option */ + if ((flags & MS_RDONLY) != MS_RDONLY || !safe_opts) + return rc; + + errno = 0; + return mount(device_path, mount_path, fs, flags, safe_opts); +} + static int mount_device(struct discover_device *dev) { const char *fstype, *device_path; @@ -1361,13 +1417,6 @@ static int mount_device(struct discover_device *dev) if (!fstype) return 0; - /* ext3 treats the norecovery option as an error, so mount the device - * as an ext4 filesystem instead */ - if (!strncmp(fstype, "ext3", strlen("ext3"))) { - pb_debug("Mounting ext3 filesystem as ext4\n"); - fstype = talloc_asprintf(dev, "ext4"); - } - dev->mount_path = join_paths(dev, mount_base(), dev->device_path); @@ -1380,14 +1429,14 @@ static int mount_device(struct discover_device *dev) device_path = get_device_path(dev); pb_log("mounting device %s read-only\n", dev->device_path); - errno = 0; - rc = mount(device_path, dev->mount_path, fstype, - MS_RDONLY | MS_SILENT, - fs_parameters(dev, MS_RDONLY)); + rc = try_mount(device_path, dev->mount_path, fstype, + MS_RDONLY | MS_SILENT, dev->ramdisk); + if (!rc) { dev->mounted = true; dev->mounted_rw = false; dev->unmount = true; + dev->root_path = check_subvols(dev); return 0; } @@ -1426,6 +1475,7 @@ static int umount_device(struct discover_device *dev) talloc_free(dev->mount_path); dev->mount_path = NULL; + dev->root_path = NULL; return 0; } @@ -1461,9 +1511,8 @@ int device_request_write(struct discover_device *dev, bool *release) return -1; } - rc = mount(device_path, dev->mount_path, fstype, - MS_SILENT, - fs_parameters(dev, MS_REMOUNT)); + rc = try_mount(device_path, dev->mount_path, fstype, + MS_SILENT, dev->ramdisk); if (rc) goto mount_ro; @@ -1474,9 +1523,9 @@ int device_request_write(struct discover_device *dev, bool *release) mount_ro: pb_log("Unable to remount device %s read-write: %s\n", device_path, strerror(errno)); - if (mount(device_path, dev->mount_path, fstype, - MS_RDONLY | MS_SILENT, - fs_parameters(dev, MS_RDONLY))) + rc = try_mount(device_path, dev->mount_path, fstype, + MS_RDONLY | MS_SILENT, dev->ramdisk); + if (rc) pb_log("Unable to recover mount for %s: %s\n", device_path, strerror(errno)); return -1; @@ -1507,9 +1556,8 @@ void device_release_write(struct discover_device *dev, bool release) device_path = get_device_path(dev); } - if (mount(device_path, dev->mount_path, fstype, - MS_RDONLY | MS_SILENT, - fs_parameters(dev, MS_RDONLY))) + if (try_mount(device_path, dev->mount_path, fstype, + MS_RDONLY | MS_SILENT, dev->ramdisk)) pb_log("Failed to remount %s read-only: %s\n", device_path, strerror(errno)); else