Browse Source

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 <daniel@makrotopia.org>
Daniel Golle 3 years ago
parent
commit
bad1835f27
7 changed files with 327 additions and 126 deletions
  1. 2 0
      CMakeLists.txt
  2. 124 0
      libfstools/common.c
  3. 28 0
      libfstools/common.h
  4. 1 0
      libfstools/libfstools.h
  5. 163 0
      libfstools/partname.c
  6. 4 63
      libfstools/rootdisk.c
  7. 5 63
      libfstools/ubi.c

+ 2 - 0
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)

+ 124 - 0
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;
+}

+ 28 - 0
libfstools/common.h

@@ -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);

+ 1 - 0
libfstools/libfstools.h

@@ -29,6 +29,7 @@ enum {
 	FS_UBIFS,
 	FS_F2FS,
 	FS_EXT4,
+	FS_TARGZ,
 };
 
 enum fs_state {

+ 163 - 0
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);

+ 4 - 63
libfstools/rootdisk.c

@@ -11,21 +11,7 @@
  * 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 = {

+ 5 - 63
libfstools/ubi.c

@@ -11,13 +11,7 @@
  * 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;
 	}