From bad1835f27ec31dbc30060b03cc714212275168a Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Fri, 26 Feb 2021 19:45:55 +0000 Subject: [PATCH] fstools: add partname volume driver Add driver to handle block devices with (GPT) partition table which can include a partition name. If 'root=' is set on the kernel cmdline, only search for partitions on the same device. Among with other changes (ptgen, image*.mk, base-files, ...) this allows for a much more straight forward storage model on block based devices. Signed-off-by: Daniel Golle --- CMakeLists.txt | 2 + libfstools/common.c | 124 ++++++++++++++++++++++++++++++ libfstools/common.h | 28 +++++++ libfstools/libfstools.h | 1 + libfstools/partname.c | 163 ++++++++++++++++++++++++++++++++++++++++ libfstools/rootdisk.c | 67 +---------------- libfstools/ubi.c | 68 ++--------------- 7 files changed, 327 insertions(+), 126 deletions(-) create mode 100644 libfstools/common.c create mode 100644 libfstools/common.h create mode 100644 libfstools/partname.c diff --git a/CMakeLists.txt b/CMakeLists.txt index a586577..422f27d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations -Wno- SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") ADD_LIBRARY(fstools SHARED + libfstools/common.c libfstools/snapshot.c libfstools/extroot.c libfstools/overlay.c @@ -14,6 +15,7 @@ ADD_LIBRARY(fstools SHARED libfstools/mount.c libfstools/ubi.c libfstools/rootdisk.c + libfstools/partname.c libfstools/find.c) TARGET_LINK_LIBRARIES(fstools ubox) INSTALL(TARGETS fstools LIBRARY DESTINATION lib) diff --git a/libfstools/common.c b/libfstools/common.c new file mode 100644 index 0000000..f2d415d --- /dev/null +++ b/libfstools/common.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common.h" +#define BUFLEN 128 + +int +read_uint_from_file(char *dirname, char *filename, unsigned int *i) +{ + FILE *f; + char fname[BUFLEN]; + int ret = -1; + + snprintf(fname, sizeof(fname), "%s/%s", dirname, filename); + + f = fopen(fname, "r"); + if (!f) + return ret; + + if (fscanf(f, "%u", i) == 1) + ret = 0; + + fclose(f); + return ret; +} + +char +*read_string_from_file(const char *dirname, const char *filename, char *buf, size_t bufsz) +{ + FILE *f; + char fname[BUFLEN]; + int i; + + snprintf(fname, sizeof(fname), "%s/%s", dirname, filename); + + f = fopen(fname, "r"); + if (!f) + return NULL; + + if (fgets(buf, bufsz, f) == NULL) + return NULL; + + fclose(f); + + /* make sure the string is \0 terminated */ + buf[bufsz - 1] = '\0'; + + /* remove trailing whitespace */ + i = strlen(buf) - 1; + while (i > 0 && buf[i] <= ' ') + buf[i--] = '\0'; + + return buf; +} + +int block_file_identify(FILE *f, uint64_t offset) +{ + uint32_t magic = 0; + size_t n; + + fseeko(f, offset, SEEK_SET); + n = fread(&magic, sizeof(magic), 1, f); + if (magic == cpu_to_le32(0x88b1f)) { + return FS_TARGZ; + } + + fseeko(f, offset + 0x400, SEEK_SET); + n = fread(&magic, sizeof(magic), 1, f); + if (n != 1) + return -1; + + if (magic == cpu_to_le32(0xF2F52010)) + return FS_F2FS; + + magic = 0; + fseeko(f, offset + 0x438, SEEK_SET); + n = fread(&magic, sizeof(magic), 1, f); + if (n != 1) + return -1; + + if ((le32_to_cpu(magic) & 0xffff) == 0xef53) + return FS_EXT4; + + return FS_NONE; +} + +static bool use_f2fs(struct volume *v, uint64_t offset, const char *bdev) +{ + uint64_t size = 0; + bool ret = false; + int fd; + + fd = open(bdev, O_RDONLY); + if (ioctl(fd, BLKGETSIZE64, &size) == 0) + ret = size - offset > F2FS_MINSIZE; + close(fd); + + return ret; +} + +int block_volume_format(struct volume *v, uint64_t offset, const char *bdev) +{ + int ret = 0; + char str[128]; + + switch (volume_identify(v)) { + case FS_TARGZ: + snprintf(str, sizeof(str), "gzip -cd %s > /tmp/sysupgrade.tar", v->blk); + system(str); + /* fall-through */ + case FS_NONE: + ULOG_INFO("overlay filesystem in %s has not been formatted yet\n", v->blk); + if (use_f2fs(v, offset, bdev)) + snprintf(str, sizeof(str), "mkfs.f2fs -q -l rootfs_data %s", v->blk); + else + snprintf(str, sizeof(str), "mkfs.ext4 -q -L rootfs_data %s", v->blk); + + ret = system(str); + break; + default: + break; + } + + return ret; +} diff --git a/libfstools/common.h b/libfstools/common.h new file mode 100644 index 0000000..6798a54 --- /dev/null +++ b/libfstools/common.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libfstools.h" +#include "volume.h" + +#define F2FS_MINSIZE (100ULL * 1024ULL * 1024ULL) + +int read_uint_from_file(char *dirname, char *filename, unsigned int *i); +char *read_string_from_file(const char *dirname, const char *filename, char *buf, size_t bufsz); +int block_file_identify(FILE *f, uint64_t offset); +int block_volume_format(struct volume *v, uint64_t offset, const char *bdev); diff --git a/libfstools/libfstools.h b/libfstools/libfstools.h index 3da151d..6aa0e41 100644 --- a/libfstools/libfstools.h +++ b/libfstools/libfstools.h @@ -29,6 +29,7 @@ enum { FS_UBIFS, FS_F2FS, FS_EXT4, + FS_TARGZ, }; enum fs_state { diff --git a/libfstools/partname.c b/libfstools/partname.c new file mode 100644 index 0000000..9073a0d --- /dev/null +++ b/libfstools/partname.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common.h" + +#define BUFLEN 64 + +const char *const block_dir_name = "/sys/class/block"; + +struct partname_volume { + struct volume v; + char devname[16]; + char parent_devname[16]; +}; + +static struct driver partname_driver; + +static int partname_volume_identify(struct volume *v) +{ + int ret = FS_NONE; + FILE *f; + + f = fopen(v->blk, "r"); + if (!f) + return ret; + + ret = block_file_identify(f, 0); + + fclose(f); + + return ret; +} + +static int partname_volume_init(struct volume *v) +{ + struct partname_volume *p = container_of(v, struct partname_volume, v); + char voldir[BUFLEN]; + char voldev[BUFLEN]; + char pdev[BUFLEN]; + unsigned int volsize; + + snprintf(voldir, sizeof(voldir), "%s/%s", block_dir_name, p->devname); + + if (read_uint_from_file(voldir, "size", &volsize)) + return -1; + + snprintf(voldev, sizeof(voldev), "/dev/%s", p->devname); + snprintf(pdev, sizeof(pdev), "/dev/%s", p->parent_devname); + + v->type = BLOCKDEV; + v->size = volsize << 9; /* size is returned in sectors of 512 bytes */ + v->blk = strdup(voldev); + + return block_volume_format(v, 0, pdev); +} + +/* from procd/utils.c -> should go to libubox */ +static char* get_cmdline_val(const char* name, char* out, int len) +{ + char line[1024], *c, *sptr; + int fd = open("/proc/cmdline", O_RDONLY); + ssize_t r = read(fd, line, sizeof(line) - 1); + close(fd); + + if (r <= 0) + return NULL; + + line[r] = 0; + + for (c = strtok_r(line, " \t\n", &sptr); c; + c = strtok_r(NULL, " \t\n", &sptr)) { + char *sep = strchr(c, '='); + if (sep == NULL) + continue; + + ssize_t klen = sep - c; + if (strncmp(name, c, klen) || name[klen] != 0) + continue; + + strncpy(out, &sep[1], len); + out[len-1] = 0; + return out; + } + + return NULL; +} + +static char *rootdevname(char *devpath) { + int l; + + l = strlen(devpath) - 1; + + /* strip partition suffix from root=/dev/... string */ + while (l > 0 && (devpath[l] >= '0' && devpath[l] <= '9')) + --l; + + if (devpath[l] != 'p') + ++l; + + devpath[l] = '\0'; + + return basename(devpath); +} + +static struct volume *partname_volume_find(char *name) +{ + struct partname_volume *p; + char volnamegstr[BUFLEN]; + char namebuf[BUFLEN]; + char rootparam[BUFLEN]; + char *rootdev = NULL, *devname, *tmp; + int j; + bool found = false; + glob_t gl; + + if (get_cmdline_val("root", rootparam, sizeof(rootparam))) { + rootdev = rootdevname(rootparam); + /* find partition on same device as rootfs */ + snprintf(volnamegstr, sizeof(volnamegstr), "%s/%s/*/name", block_dir_name, rootdev); + } else { + /* no 'root=' kernel cmdline parameter, find on any block device */ + snprintf(volnamegstr, sizeof(volnamegstr), "%s/*/name", block_dir_name); + } + + if (!glob(volnamegstr, GLOB_NOESCAPE, NULL, &gl)) + for (j = 0; j < gl.gl_pathc; j++) { + if (!read_string_from_file("", gl.gl_pathv[j], namebuf, sizeof(namebuf))) + continue; + if (!strncmp(namebuf, name, sizeof(namebuf))) { + found = 1; + break; + } + } + + if (!found) + return NULL; + + devname = gl.gl_pathv[j]; + tmp = strrchr(devname, '/'); + *tmp = '\0'; + devname = strrchr(devname, '/') + 1; + + p = calloc(1, sizeof(*p)); + strncpy(p->devname, devname, sizeof(p->devname)); + if (rootdev) + strncpy(p->parent_devname, rootdev, sizeof(p->devname)); + else + strncpy(p->parent_devname, rootdevname(devname), sizeof(p->devname)); + + p->devname[sizeof(p->devname)-1] = '\0'; + p->v.drv = &partname_driver; + p->v.name = strdup(namebuf); + + return &p->v; +} + +static struct driver partname_driver = { + .name = "partname", + .find = partname_volume_find, + .init = partname_volume_init, + .identify = partname_volume_identify, +}; + +DRIVER(partname_driver); diff --git a/libfstools/rootdisk.c b/libfstools/rootdisk.c index dc861a9..9f2317f 100644 --- a/libfstools/rootdisk.c +++ b/libfstools/rootdisk.c @@ -11,21 +11,7 @@ * GNU General Public License for more details. */ -#define _FILE_OFFSET_BITS 64 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libfstools.h" -#include "volume.h" +#include "common.h" #include @@ -107,20 +93,6 @@ static int get_squashfs(struct squashfs_super_block *sb) return 0; } -static bool rootdisk_use_f2fs(struct rootdev_volume *p) -{ - uint64_t size = 0; - bool ret = false; - int fd; - - fd = open(rootdev, O_RDONLY); - if (ioctl(fd, BLKGETSIZE64, &size) == 0) - ret = size - p->offset > F2FS_MINSIZE; - close(fd); - - return ret; -} - static struct volume *rootdisk_volume_find(char *name) { struct squashfs_super_block sb; @@ -160,30 +132,13 @@ static struct volume *rootdisk_volume_find(char *name) static int rootdisk_volume_identify(struct volume *v) { struct rootdev_volume *p = container_of(v, struct rootdev_volume, v); - int ret = FS_NONE; - uint32_t magic = 0; - size_t n; FILE *f; - + int ret = FS_NONE; f = fopen(rootdev, "r"); if (!f) return ret; - fseeko(f, p->offset + 0x400, SEEK_SET); - n = fread(&magic, sizeof(magic), 1, f); - if (n != 1) - return -1; - - if (magic == cpu_to_le32(0xF2F52010)) - ret = FS_F2FS; - - magic = 0; - fseeko(f, p->offset + 0x438, SEEK_SET); - n = fread(&magic, sizeof(magic), 1, f); - if (n != 1) - return -1; - if ((le32_to_cpu(magic) & 0xffff) == 0xef53) - ret = FS_EXT4; + ret = block_file_identify(f, p->offset); fclose(f); @@ -262,8 +217,6 @@ static int rootdisk_create_loop(struct rootdev_volume *p) static int rootdisk_volume_init(struct volume *v) { struct rootdev_volume *p = container_of(v, struct rootdev_volume, v); - char str[128]; - int ret = 0; if (!p->loop_name[0] && rootdisk_create_loop(p) != 0) { ULOG_ERR("unable to create loop device\n"); @@ -273,19 +226,7 @@ static int rootdisk_volume_init(struct volume *v) v->type = BLOCKDEV; v->blk = p->loop_name; - switch (rootdisk_volume_identify(v)) { - case FS_NONE: - ULOG_INFO("rootdisk overlay filesystem has not been formatted yet\n"); - if (rootdisk_use_f2fs(p)) - snprintf(str, sizeof(str), "mkfs.f2fs -q -l rootfs_data %s", v->blk); - else - snprintf(str, sizeof(str), "mkfs.ext4 -q -L rootfs_data %s", v->blk); - ret = system(str); - break; - default: - break; - } - return ret; + return block_volume_format(v, p->offset, rootdev); } static struct driver rootdisk_driver = { diff --git a/libfstools/ubi.c b/libfstools/ubi.c index 091ccf6..4772909 100644 --- a/libfstools/ubi.c +++ b/libfstools/ubi.c @@ -11,13 +11,7 @@ * GNU General Public License for more details. */ -#include -#include -#include -#include - -#include "libfstools.h" -#include "volume.h" +#include "common.h" /* fit for UBI_MAX_VOLUME_NAME and sysfs path lengths */ #define BUFLEN 128 @@ -34,56 +28,6 @@ struct ubi_volume { static struct driver ubi_driver; -static int -read_uint_from_file(char *dirname, char *filename, unsigned int *i) -{ - FILE *f; - char fname[BUFLEN]; - int ret = -1; - - snprintf(fname, sizeof(fname), "%s/%s", dirname, filename); - - f = fopen(fname, "r"); - if (!f) - return ret; - - if (fscanf(f, "%u", i) == 1) - ret = 0; - - fclose(f); - return ret; -} - -static char -*read_string_from_file(char *dirname, char *filename) -{ - FILE *f; - char fname[BUFLEN]; - char buf[BUFLEN]; - int i; - - snprintf(fname, sizeof(fname), "%s/%s", dirname, filename); - - f = fopen(fname, "r"); - if (!f) - return NULL; - - if (fgets(buf, sizeof(buf), f) == NULL) - return NULL; - - fclose(f); - - /* make sure the string is \0 terminated */ - buf[sizeof(buf) - 1] = '\0'; - - /* remove trailing whitespace */ - i = strlen(buf) - 1; - while (i > 0 && buf[i] <= ' ') - buf[i--] = '\0'; - - return strdup(buf); -} - static unsigned int test_open(char *filename) { @@ -100,7 +44,7 @@ test_open(char *filename) static int ubi_volume_init(struct volume *v) { struct ubi_volume *p = container_of(v, struct ubi_volume, v); - char voldir[BUFLEN], voldev[BUFLEN], *volname; + char voldir[BUFLEN], voldev[BUFLEN], volname[BUFLEN]; unsigned int volsize; snprintf(voldir, sizeof(voldir), "%s/ubi%u/ubi%u_%u", @@ -109,8 +53,7 @@ static int ubi_volume_init(struct volume *v) snprintf(voldev, sizeof(voldev), "/dev/ubi%u_%u", p->ubi_num, p->ubi_volid); - volname = read_string_from_file(voldir, "name"); - if (!volname) + if (!read_string_from_file(voldir, "name", volname, sizeof(volname))) return -1; if (read_uint_from_file(voldir, "data_bytes", &volsize)) @@ -126,7 +69,7 @@ static int ubi_volume_init(struct volume *v) static struct volume *ubi_volume_match(char *name, int ubi_num, int volid) { - char voldir[BUFLEN], volblkdev[BUFLEN], *volname; + char voldir[BUFLEN], volblkdev[BUFLEN], volname[BUFLEN]; struct ubi_volume *p; snprintf(voldir, sizeof(voldir), "%s/ubi%u/ubi%u_%u", @@ -141,8 +84,7 @@ static struct volume *ubi_volume_match(char *name, int ubi_num, int volid) /* todo: skip existing gluebi device for legacy support */ - volname = read_string_from_file(voldir, "name"); - if (!volname) { + if (!read_string_from_file(voldir, "name", volname, sizeof(volname))) { ULOG_ERR("Couldn't read %s/name\n", voldir); return NULL; } -- 2.30.2