X-Git-Url: http://git.archive.openwrt.org/?a=blobdiff_plain;f=block.c;h=c72a9df334ad04b325f8f6e20274172025fffaa0;hb=4018f4215ddd8ab8d15839128bab48dc544eacb4;hp=9122ebf400e5aaa07f20ee5c5b1d8e15b5d2db2c;hpb=d292ffc85f48dd9c8774a0860f50397dd2966e7a;p=project%2Ffstools.git diff --git a/block.c b/block.c index 9122ebf..c72a9df 100644 --- a/block.c +++ b/block.c @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #include #include @@ -36,9 +39,39 @@ #include "libblkid-tiny/libblkid-tiny.h" +#ifdef UBIFS_EXTROOT +#include "libubi/libubi.h" +#endif + +static int kmsg = 0; + +static void klog(int prio, const char *fmt, ...) +{ + va_list ap; + FILE *f = fopen("/dev/kmsg", "w"); + + if (f) { + fprintf(f, "<%d>", prio); + + va_start(ap, fmt); + vfprintf(f, fmt, ap); + va_end(ap); + + fclose(f); + } +} + +#define KINFO(fmt, ...) do { \ + if (kmsg) klog(LOG_INFO, "block: " fmt, ## __VA_ARGS__); \ + } while (0) + #define ERROR(fmt, ...) do { \ - syslog(LOG_ERR, fmt, ## __VA_ARGS__); \ - fprintf(stderr, "block: "fmt, ## __VA_ARGS__); \ + if (kmsg) \ + klog(LOG_ERR, "block: " fmt, ## __VA_ARGS__); \ + else { \ + syslog(LOG_ERR, fmt, ## __VA_ARGS__); \ + fprintf(stderr, "block: "fmt, ## __VA_ARGS__); \ + } \ } while (0) enum { @@ -354,7 +387,7 @@ static struct mount* find_swap(const char *uuid, const char *label, const char * vlist_for_each_element(&mounts, m, node) { if (m->type != TYPE_SWAP) continue; - if (uuid && m->uuid && !strcmp(m->uuid, uuid)) + if (uuid && m->uuid && !strcasecmp(m->uuid, uuid)) return m; if (label && m->label && !strcmp(m->label, label)) return m; @@ -373,7 +406,7 @@ static struct mount* find_block(const char *uuid, const char *label, const char vlist_for_each_element(&mounts, m, node) { if (m->type != TYPE_MOUNT) continue; - if (m->uuid && uuid && !strcmp(m->uuid, uuid)) + if (m->uuid && uuid && !strcasecmp(m->uuid, uuid)) return m; if (m->label && label && !strcmp(m->label, label)) return m; @@ -391,28 +424,53 @@ static void mounts_update(struct vlist_tree *tree, struct vlist_node *node_new, { } -static int config_load(char *cfg) +static struct uci_package * config_try_load(struct uci_context *ctx, char *path) { - struct uci_context *ctx; + char *file = basename(path); + char *dir = dirname(path); + char *err; struct uci_package *pkg; + + uci_set_confdir(ctx, dir); + KINFO("attempting to load %s/%s\n", dir, file); + + if (uci_load(ctx, file, &pkg)) { + uci_get_errorstr(ctx, &err, file); + ERROR("unable to load configuration (%s)\n", err); + + free(err); + return NULL; + } + + return pkg; +} + +static int config_load(char *cfg) +{ + struct uci_context *ctx = uci_alloc_context(); + struct uci_package *pkg = NULL; struct uci_element *e; + char path[64]; vlist_init(&mounts, avl_strcmp, mounts_update); - ctx = uci_alloc_context(); if (cfg) { - char path[32]; - snprintf(path, 32, "%s/etc/config", cfg); - uci_set_confdir(ctx, path); + snprintf(path, sizeof(path), "%s/upper/etc/config/fstab", cfg); + pkg = config_try_load(ctx, path); + + if (!pkg) { + snprintf(path, sizeof(path), "%s/etc/config/fstab", cfg); + pkg = config_try_load(ctx, path); + } } - if (uci_load(ctx, "fstab", &pkg)) - { - char *err; - uci_get_errorstr(ctx, &err, "fstab"); - ERROR("extroot: failed to load %s/etc/config/%s\n", - cfg ? cfg : "", err); - free(err); + if (!pkg) { + snprintf(path, sizeof(path), "/etc/config/fstab"); + pkg = config_try_load(ctx, path); + } + + if (!pkg) { + ERROR("no usable configuration\n"); return -1; } @@ -474,12 +532,16 @@ static int _cache_load(const char *path) static void cache_load(int mtd) { - if (mtd) + if (mtd) { _cache_load("/dev/mtdblock*"); + _cache_load("/dev/ubiblock*"); + _cache_load("/dev/ubi?*_?*"); + } _cache_load("/dev/mmcblk*"); _cache_load("/dev/sd*"); _cache_load("/dev/hd*"); _cache_load("/dev/md*"); + _cache_load("/dev/vd*"); _cache_load("/dev/mapper/*"); } @@ -526,7 +588,7 @@ static struct blkid_struct_probe* find_block_info(char *uuid, char *label, char if (uuid) list_for_each_entry(pr, &devices, list) - if (!strcmp(pr->uuid, uuid)) + if (!strcasecmp(pr->uuid, uuid)) return pr; if (label) @@ -590,6 +652,10 @@ static void check_filesystem(struct blkid_struct_probe *pr) struct stat statbuf; char *e2fsck = "/usr/sbin/e2fsck"; + /* UBIFS does not need stuff like fsck */ + if (!strncmp(pr->id->name, "ubifs", 5)) + return; + if (strncmp(pr->id->name, "ext", 3)) { ERROR("check_filesystem: %s is not supported\n", pr->id->name); return; @@ -693,7 +759,7 @@ static int mount_device(struct blkid_struct_probe *pr, int hotplug) (m->options) ? (m->options) : ("")); if (err) ERROR("mounting %s (%s) as %s failed (%d) - %s\n", - pr->dev, pr->id->name, target, err, strerror(err)); + pr->dev, pr->id->name, target, err, strerror(err)); else handle_swapfiles(true); return err; @@ -820,26 +886,152 @@ static int find_block_mtd(char *name, char *part, int plen) return 0; } +#ifdef UBIFS_EXTROOT +static int find_ubi_vol(libubi_t libubi, char *name, int *dev_num, int *vol_id) +{ + int dev = 0; + + while (ubi_dev_present(libubi, dev)) + { + struct ubi_dev_info dev_info; + struct ubi_vol_info vol_info; + + if (ubi_get_dev_info1(libubi, dev++, &dev_info)) + continue; + if (ubi_get_vol_info1_nm(libubi, dev_info.dev_num, name, &vol_info)) + continue; + + *dev_num = dev_info.dev_num; + *vol_id = vol_info.vol_id; + + return 0; + } + + return -1; +} + +static int find_block_ubi(libubi_t libubi, char *name, char *part, int plen) +{ + int dev_num; + int vol_id; + int err = -1; + + err = find_ubi_vol(libubi, name, &dev_num, &vol_id); + if (!err) + snprintf(part, plen, "/dev/ubi%d_%d", dev_num, vol_id); + + return err; +} + +static int find_block_ubi_RO(libubi_t libubi, char *name, char *part, int plen) +{ + int dev_num; + int vol_id; + int err = -1; + + err = find_ubi_vol(libubi, name, &dev_num, &vol_id); + if (!err) + snprintf(part, plen, "/dev/ubiblock%d_%d", dev_num, vol_id); + + return err; +} +#endif + +static int find_root_dev(char *buf, int len) +{ + DIR *d; + dev_t root; + struct stat s; + struct dirent *e; + + if (stat("/", &s)) + return -1; + + if (!(d = opendir("/dev"))) + return -1; + + root = s.st_dev; + + while ((e = readdir(d)) != NULL) { + snprintf(buf, len, "/dev/%s", e->d_name); + + if (stat(buf, &s) || s.st_rdev != root) + continue; + + closedir(d); + return 0; + } + + closedir(d); + return -1; +} + +static int test_fs_support(const char *name) +{ + char line[128], *p; + int rv = -1; + FILE *f; + + if ((f = fopen("/proc/filesystems", "r")) != NULL) { + while (fgets(line, sizeof(line), f)) { + p = strtok(line, "\t\n"); + + if (p && !strcmp(p, "nodev")) + p = strtok(NULL, "\t\n"); + + if (p && !strcmp(p, name)) { + rv = 0; + break; + } + } + fclose(f); + } + + return rv; +} + static int check_extroot(char *path) { struct blkid_struct_probe *pr = NULL; - char fs[32]; + char devpath[32]; - if (find_block_mtd("rootfs", fs, sizeof(fs))) - return -1; +#ifdef UBIFS_EXTROOT + if (find_block_mtd("rootfs", devpath, sizeof(devpath))) { + int err = -1; + libubi_t libubi; + + libubi = libubi_open(); + err = find_block_ubi_RO(libubi, "rootfs", devpath, sizeof(devpath)); + libubi_close(libubi); + if (err) + return -1; + } +#else + if (find_block_mtd("rootfs", devpath, sizeof(devpath))) { + if (find_root_dev(devpath, sizeof(devpath))) { + ERROR("extroot: unable to determine root device\n"); + return -1; + } + } +#endif list_for_each_entry(pr, &devices, list) { - if (!strcmp(pr->dev, fs)) { + if (!strcmp(pr->dev, devpath)) { struct stat s; FILE *fp = NULL; char tag[64]; char uuid[64] = { 0 }; + snprintf(tag, sizeof(tag), "%s/etc", path); + if (stat(tag, &s)) + mkdir_p(tag); + snprintf(tag, sizeof(tag), "%s/etc/.extroot-uuid", path); if (stat(tag, &s)) { fp = fopen(tag, "w+"); if (!fp) { - ERROR("extroot: failed to write uuid tag file\n"); + ERROR("extroot: failed to write UUID to %s: %d (%s)\n", + tag, errno, strerror(errno)); /* return 0 to continue boot regardless of error */ return 0; } @@ -850,20 +1042,30 @@ static int check_extroot(char *path) fp = fopen(tag, "r"); if (!fp) { - ERROR("extroot: failed to open uuid tag file\n"); + ERROR("extroot: failed to read UUID from %s: %d (%s)\n", + tag, errno, strerror(errno)); return -1; } fgets(uuid, sizeof(uuid), fp); fclose(fp); - if (!strcmp(uuid, pr->uuid)) + + if (!strcasecmp(uuid, pr->uuid)) return 0; - ERROR("extroot: uuid tag does not match rom uuid\n"); + + ERROR("extroot: UUID mismatch (root: %s, %s: %s)\n", + pr->uuid, basename(path), uuid); + return -1; } } + + ERROR("extroot: unable to lookup root device %s\n", devpath); return -1; } +/* + * Read info about extroot from UCI (using prefix) and mount it. + */ static int mount_extroot(char *cfg) { char overlay[] = "/tmp/extroot/overlay"; @@ -873,33 +1075,43 @@ static int mount_extroot(char *cfg) struct mount *m; int err = -1; + /* Load @cfg/etc/config/fstab */ if (config_load(cfg)) return -2; + /* See if there is extroot-specific mount config */ m = find_block(NULL, NULL, NULL, "/"); if (!m) m = find_block(NULL, NULL, NULL, "/overlay"); if (!m || !m->extroot) { - ERROR("extroot: no root or overlay mount defined\n"); + KINFO("extroot: not configured\n"); return -1; } + /* Find block device pointed by the mount config */ pr = find_block_info(m->uuid, m->label, m->device); if (!pr && delay_root){ - ERROR("extroot: is not ready yet, retrying in %u seconds\n", delay_root); + KINFO("extroot: device not present, retrying in %u seconds\n", delay_root); sleep(delay_root); mkblkdev(); cache_load(0); pr = find_block_info(m->uuid, m->label, m->device); } if (pr) { - if (strncmp(pr->id->name, "ext", 3)) { - ERROR("extroot: %s is not supported, try ext4\n", pr->id->name); + if (strncmp(pr->id->name, "ext", 3) && + strncmp(pr->id->name, "ubifs", 5)) { + ERROR("extroot: unsupported filesystem %s, try ext4\n", pr->id->name); return -1; } + + if (test_fs_support(pr->id->name)) { + ERROR("extroot: filesystem %s not supported by kernel\n", pr->id->name); + return -1; + } + if (m->overlay) path = overlay; mkdir_p(path); @@ -910,15 +1122,17 @@ static int mount_extroot(char *cfg) err = mount(pr->dev, path, pr->id->name, 0, (m->options) ? (m->options) : ("")); if (err) { - ERROR("mounting %s (%s) as %s failed (%d) - %s\n", - pr->dev, pr->id->name, path, err, strerror(err)); + ERROR("extroot: mounting %s (%s) on %s failed: %d (%s)\n", + pr->dev, pr->id->name, path, err, strerror(err)); } else if (m->overlay) { err = check_extroot(path); if (err) umount(path); } } else { - ERROR("extroot: cannot find block device\n"); + ERROR("extroot: cannot find device %s%s\n", + (m->uuid ? "with UUID " : (m->label ? "with label " : "")), + (m->uuid ? m->uuid : (m->label ? m->label : m->device))); } return err; @@ -927,41 +1141,43 @@ static int mount_extroot(char *cfg) static int main_extroot(int argc, char **argv) { struct blkid_struct_probe *pr; - char fs[32] = { 0 }; - char fs_data[32] = { 0 }; + char blkdev_path[32] = { 0 }; int err = -1; +#ifdef UBIFS_EXTROOT + libubi_t libubi; +#endif if (!getenv("PREINIT")) return -1; if (argc != 2) { - fprintf(stderr, "Usage: block extroot mountpoint\n"); + fprintf(stderr, "Usage: block extroot\n"); return -1; } + kmsg = 1; + mkblkdev(); cache_load(1); - find_block_mtd("rootfs", fs, sizeof(fs)); - if (!fs[0]) { - ERROR("extroot: unable to locate rootfs mtdblock\n"); - return -2; - } - - pr = find_block_info(NULL, NULL, fs); - if (!pr) { - ERROR("extroot: unable to retrieve rootfs information\n"); - return -3; - } + /* + * Look for "rootfs_data". We will want to mount it and check for + * extroot configuration. + */ - find_block_mtd("rootfs_data", fs_data, sizeof(fs_data)); - if (fs_data[0]) { - pr = find_block_info(NULL, NULL, fs_data); + /* Start with looking for MTD partition */ + find_block_mtd("rootfs_data", blkdev_path, sizeof(blkdev_path)); + if (blkdev_path[0]) { + pr = find_block_info(NULL, NULL, blkdev_path); if (pr && !strcmp(pr->id->name, "jffs2")) { char cfg[] = "/tmp/jffs_cfg"; + /* + * Mount MTD part and try extroot (using + * /etc/config/fstab from that partition) + */ mkdir_p(cfg); - if (!mount(fs_data, cfg, "jffs2", MS_NOATIME, NULL)) { + if (!mount(blkdev_path, cfg, "jffs2", MS_NOATIME, NULL)) { err = mount_extroot(cfg); umount2(cfg, MNT_DETACH); } @@ -972,6 +1188,28 @@ static int main_extroot(int argc, char **argv) } } +#ifdef UBIFS_EXTROOT + /* ... but it also could be an UBI volume */ + memset(blkdev_path, 0, sizeof(blkdev_path)); + libubi = libubi_open(); + find_block_ubi(libubi, "rootfs_data", blkdev_path, sizeof(blkdev_path)); + libubi_close(libubi); + if (blkdev_path[0]) { + char cfg[] = "/tmp/ubifs_cfg"; + + /* Mount volume and try extroot (using fstab from that vol) */ + mkdir_p(cfg); + if (!mount(blkdev_path, cfg, "ubifs", MS_NOATIME, NULL)) { + err = mount_extroot(cfg); + umount2(cfg, MNT_DETACH); + } + if (err < 0) + rmdir("/tmp/overlay"); + rmdir(cfg); + return err; + } +#endif + return mount_extroot(NULL); }