fstools: add partname volume driver
authorDaniel Golle <daniel@makrotopia.org>
Fri, 26 Feb 2021 19:45:55 +0000 (19:45 +0000)
committerDaniel Golle <daniel@makrotopia.org>
Sat, 27 Feb 2021 22:15:58 +0000 (22:15 +0000)
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 <daniel@makrotopia.org>
CMakeLists.txt
libfstools/common.c [new file with mode: 0644]
libfstools/common.h [new file with mode: 0644]
libfstools/libfstools.h
libfstools/partname.c [new file with mode: 0644]
libfstools/rootdisk.c
libfstools/ubi.c

index a586577d07ee0d1588a30218baf4435d0153b03c..422f27d3b3f5375ad866573733bffcf756037353 100644 (file)
@@ -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 (file)
index 0000000..f2d415d
--- /dev/null
@@ -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 (file)
index 0000000..6798a54
--- /dev/null
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#define _FILE_OFFSET_BITS 64
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <glob.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libgen.h>
+
+#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);
index 3da151de9b73e02873cb43637997a259acc79381..6aa0e41ae53481acf90e85be3a6387d35b044560 100644 (file)
@@ -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 (file)
index 0000000..9073a0d
--- /dev/null
@@ -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);
index dc861a96caaa73dea6d94337b6e76a4abef00f0f..9f2317f14e8d8f12c71b30944138d7a6c877b406 100644 (file)
  * GNU General Public License for more details.
  */
 
-#define _FILE_OFFSET_BITS 64
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/mount.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "libfstools.h"
-#include "volume.h"
+#include "common.h"
 
 #include <linux/loop.h>
 
@@ -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 = {
index 091ccf6e5bbf0dc561853be7873290a6df3165d9..4772909968c69dd3846f6a4c6886acad3cb63b37 100644 (file)
  * GNU General Public License for more details.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dirent.h>
-
-#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;
        }