[][OpenWrt Dev][Add dual-boot features for fstools]
[Description]
Add a new feature for fstools that disables splitting rootfs_data partition
from rootfs partition for rootdisk-type devices.
Also add partition key parsing support (PARTUUID=/PARTLABEL=)
[Release-log]
N/A
Change-Id: Id57707c9b586fc5acdca416e114bdd4dbeadd0f4
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/5595597
diff --git a/openwrt_patches-21.02/120-fstools-split-libblkid-tiny-as-dependency.patch b/openwrt_patches-21.02/120-fstools-split-libblkid-tiny-as-dependency.patch
new file mode 100644
index 0000000..227c3c0
--- /dev/null
+++ b/openwrt_patches-21.02/120-fstools-split-libblkid-tiny-as-dependency.patch
@@ -0,0 +1,50 @@
+--- a/package/system/fstools/Makefile
++++ b/package/system/fstools/Makefile
+@@ -36,10 +36,16 @@ CMAKE_OPTIONS += $(if $(CONFIG_FSTOOLS_UBIFS_EXTROOT),-DCMAKE_UBIFS_EXTROOT=y)
+ CMAKE_OPTIONS += $(if $(CONFIG_FSTOOLS_OVL_MOUNT_FULL_ACCESS_TIME),-DCMAKE_OVL_MOUNT_FULL_ACCESS_TIME=y)
+ CMAKE_OPTIONS += $(if $(CONFIG_FSTOOLS_OVL_MOUNT_COMPRESS_ZLIB),-DCMAKE_OVL_MOUNT_COMPRESS_ZLIB=y)
+
++define Package/libfstools-bootparam
++ SECTION:=libs
++ CATEGORY:=Libraries
++ TITLE:=Boot parameter library for OpenWrt filesystem tools
++endef
++
+ define Package/fstools
+ SECTION:=base
+ CATEGORY:=Base system
+- DEPENDS:=+ubox +USE_GLIBC:librt +NAND_SUPPORT:ubi-utils
++ DEPENDS:=+ubox +USE_GLIBC:librt +NAND_SUPPORT:ubi-utils +libfstools-bootparam
+ TITLE:=OpenWrt filesystem tools
+ MENU:=1
+ endef
+@@ -79,7 +85,7 @@ define Package/block-mount
+ SECTION:=base
+ CATEGORY:=Base system
+ TITLE:=Block device mounting and checking
+- DEPENDS:=+ubox +libubox +libuci +libblobmsg-json +libjson-c
++ DEPENDS:=+ubox +libubox +libuci +libblobmsg-json +libjson-c +libfstools-bootparam
+ endef
+
+ define Package/blockd
+@@ -89,6 +95,12 @@ define Package/blockd
+ DEPENDS:=+block-mount +fstools +libubus +kmod-fs-autofs4 +libblobmsg-json +libjson-c
+ endef
+
++define Package/libfstools-bootparam/install
++ $(INSTALL_DIR) $(1)/lib
++
++ $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libfstools-bootparam.so $(1)/lib/
++endef
++
+ define Package/fstools/install
+ $(INSTALL_DIR) $(1)/sbin $(1)/lib
+
+@@ -132,6 +144,7 @@ define Build/InstallDev
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/libubi-utils.a $(1)/usr/lib/
+ endef
+
++$(eval $(call BuildPackage,libfstools-bootparam))
+ $(eval $(call BuildPackage,fstools))
+ $(eval $(call BuildPackage,snapshot-tool))
+ $(eval $(call BuildPackage,block-mount))
diff --git a/package/system/fstools/patches/0001-add-support-for-dual-boot.patch b/package/system/fstools/patches/0001-add-support-for-dual-boot.patch
new file mode 100644
index 0000000..a4a9d6c
--- /dev/null
+++ b/package/system/fstools/patches/0001-add-support-for-dual-boot.patch
@@ -0,0 +1,574 @@
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -5,6 +5,10 @@ ADD_DEFINITIONS(-Os -ggdb -Wall -Werror
+
+ SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+
++ADD_LIBRARY(fstools-bootparam SHARED
++ boot_param.c)
++INSTALL(TARGETS fstools-bootparam LIBRARY DESTINATION lib)
++
+ ADD_LIBRARY(fstools SHARED
+ libfstools/snapshot.c
+ libfstools/extroot.c
+@@ -15,7 +19,7 @@ ADD_LIBRARY(fstools SHARED
+ libfstools/ubi.c
+ libfstools/rootdisk.c
+ libfstools/find.c)
+-TARGET_LINK_LIBRARIES(fstools ubox)
++TARGET_LINK_LIBRARIES(fstools ubox fstools-bootparam)
+ INSTALL(TARGETS fstools LIBRARY DESTINATION lib)
+
+ ADD_LIBRARY(blkid-tiny SHARED
+@@ -75,9 +79,9 @@ INSTALL(TARGETS blockd RUNTIME DESTINATI
+ ADD_EXECUTABLE(block block.c probe.c probe-libblkid.c)
+ IF(DEFINED CMAKE_UBIFS_EXTROOT)
+ ADD_DEFINITIONS(-DUBIFS_EXTROOT)
+- TARGET_LINK_LIBRARIES(block blkid-tiny dl uci ubox ubus blobmsg_json ubi-utils ${json})
++ TARGET_LINK_LIBRARIES(block blkid-tiny fstools-bootparam dl uci ubox ubus blobmsg_json ubi-utils ${json})
+ ELSE(DEFINED CMAKE_UBIFS_EXTROOT)
+- TARGET_LINK_LIBRARIES(block blkid-tiny dl uci ubox ubus blobmsg_json ${json})
++ TARGET_LINK_LIBRARIES(block blkid-tiny fstools-bootparam dl uci ubox ubus blobmsg_json ${json})
+ ENDIF(DEFINED CMAKE_UBIFS_EXTROOT)
+ INSTALL(TARGETS block RUNTIME DESTINATION sbin)
+
+--- a/block.c
++++ b/block.c
+@@ -47,6 +47,7 @@
+ #include <libubus.h>
+
+ #include "probe.h"
++#include "boot_param.h"
+
+ #define AUTOFS_MOUNT_PATH "/tmp/run/blockd/"
+
+@@ -89,6 +90,9 @@ static LIST_HEAD(devices);
+ static int anon_mount, anon_swap, auto_mount, auto_swap, check_fs;
+ static unsigned int delay_root;
+
++static char *hide_block_devs[3];
++static uint32_t num_hide_block_devs;
++
+ enum {
+ CFG_ANON_MOUNT,
+ CFG_ANON_SWAP,
+@@ -498,9 +502,12 @@ static struct probe_info* _probe_path(ch
+ return probe_path(path);
+ }
+
++static char* find_mount_point(char *block);
++
+ static int _cache_load(const char *path)
+ {
+ int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
++ uint32_t i;
+ int j;
+ glob_t gl;
+
+@@ -509,8 +516,30 @@ static int _cache_load(const char *path)
+
+ for (j = 0; j < gl.gl_pathc; j++) {
+ struct probe_info *pr = _probe_path(gl.gl_pathv[j]);
+- if (pr)
++ bool skip_curr = false;
++
++ if (pr) {
++ char *mp = find_mount_point(pr->dev);
++ if (mp) {
++ /* Skip blocks mounted as root or overlay */
++ if (!strcmp(mp, "/rom") ||
++ !strcmp(mp, "/overlay"))
++ continue;
++ }
++
++ for (i = 0; i < num_hide_block_devs; i++) {
++ /* Skip blocks used for dual boot */
++ if (!strcmp(hide_block_devs[i], pr->dev)) {
++ skip_curr = true;
++ break;
++ }
++ }
++
++ if (skip_curr)
++ continue;
++
+ list_add_tail(&pr->list, &devices);
++ }
+ }
+
+ globfree(&gl);
+@@ -1801,6 +1830,26 @@ static int main_swapoff(int argc, char *
+ return 0;
+ }
+
++static bool add_hide_block_dev(char *path)
++{
++ if (num_hide_block_devs >= ARRAY_SIZE(hide_block_devs))
++ return false;
++
++ hide_block_devs[num_hide_block_devs++] = path;
++ return true;
++}
++
++static void hide_boot_param_dev(const char *name)
++{
++ char *path;
++
++ path = boot_param_get_dev(name);
++ if (path) {
++ if (!add_hide_block_dev(path))
++ free(path);
++ }
++}
++
+ int main(int argc, char **argv)
+ {
+ char *base = basename(*argv);
+@@ -1810,6 +1859,10 @@ int main(int argc, char **argv)
+ ulog_open(-1, -1, "block");
+ ulog_threshold(LOG_NOTICE);
+
++ hide_boot_param_dev("rootfs_data_part");
++ hide_boot_param_dev("boot_rootfs_part");
++ hide_boot_param_dev("upgrade_rootfs_part");
++
+ if (!strcmp(base, "swapon"))
+ return main_swapon(argc, argv);
+
+--- a/boot_param.c
++++ b/boot_param.c
+@@ -0,0 +1,270 @@
++/* SPDX-License-Identifier: BSD-3-Clause */
++/*
++ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <stdio.h>
++#include <string.h>
++#include <dirent.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <glob.h>
++#include <dlfcn.h>
++
++#include <blkid/blkid.h>
++#include <libubox/ulog.h>
++#include "boot_param.h"
++
++#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
++
++#define BOOT_PARAM_STR_MAX_LEN 256
++
++static struct {
++ bool loaded;
++ blkid_probe (*new_probe_from_filename)(const char *);
++ int (*do_safeprobe)(blkid_probe);
++ int (*probe_lookup_value)(blkid_probe, const char *, const char **, size_t *);
++ void (*free_probe)(blkid_probe);
++ int (*probe_enable_partitions)(blkid_probe, int);
++ int (*probe_set_partitions_flags)(blkid_probe, int);
++} libblkid = {};
++
++bool read_boot_param_bool(const char *name)
++{
++ char path[BOOT_PARAM_STR_MAX_LEN], val;
++ size_t len;
++ FILE *f;
++
++ snprintf(path, sizeof(path), "/sys/module/boot_param/parameters/%s",
++ name);
++
++ f = fopen(path, "rb");
++ if (!f)
++ return false;
++
++ len = fread(&val, 1, 1, f);
++ fclose(f);
++
++ if (len != 1)
++ return false;
++
++ return val == 'Y';
++}
++
++int read_boot_param_string(const char *name, char *val, size_t maxsize)
++{
++ char path[BOOT_PARAM_STR_MAX_LEN];
++ size_t len;
++ FILE *f;
++
++ snprintf(path, sizeof(path), "/sys/module/boot_param/parameters/%s",
++ name);
++
++ f = fopen(path, "rb");
++ if (!f) {
++ val[0] = 0;
++ return -1;
++ }
++
++ len = fread(val, 1, maxsize, f);
++ fclose(f);
++
++ while (len > 0) {
++ if (val[len - 1] != '\n' && val[len - 1] != '\r')
++ break;
++
++ len--;
++ }
++
++ if (len < maxsize)
++ val[len] = 0;
++
++ return len;
++}
++
++int write_boot_param_string(const char *name, const char *val)
++{
++ size_t wlen, len = strlen(val);
++ char path[BOOT_PARAM_STR_MAX_LEN];
++ FILE *f;
++
++ if (len >= BOOT_PARAM_STR_MAX_LEN)
++ return -1;
++
++ snprintf(path, sizeof(path), "/sys/module/boot_param/parameters/%s",
++ name);
++
++ f = fopen(path, "wb");
++ if (!f)
++ return -1;
++
++ wlen = fwrite(val, 1, len, f);
++ fclose(f);
++
++ return wlen;
++}
++
++static bool load_libblkid(void)
++{
++ void *lib;
++
++ if (libblkid.loaded)
++ return true;
++
++ lib = dlopen("libblkid.so", RTLD_GLOBAL);
++
++ if (!lib)
++ lib = dlopen("libblkid.so.1", RTLD_GLOBAL);
++
++ if (!lib)
++ return false;
++
++ libblkid.new_probe_from_filename = dlsym(lib, "blkid_new_probe_from_filename");
++ if (!libblkid.new_probe_from_filename)
++ return false;
++
++ libblkid.do_safeprobe = dlsym(lib, "blkid_do_safeprobe");
++ if (!libblkid.do_safeprobe)
++ return false;
++
++ libblkid.probe_lookup_value = dlsym(lib, "blkid_probe_lookup_value");
++ if (!libblkid.probe_lookup_value)
++ return false;
++
++ libblkid.free_probe = dlsym(lib, "blkid_free_probe");
++ if (!libblkid.free_probe)
++ return false;
++
++ libblkid.probe_enable_partitions = dlsym(lib, "blkid_probe_enable_partitions");
++ if (!libblkid.probe_enable_partitions)
++ return false;
++
++ libblkid.probe_set_partitions_flags = dlsym(lib, "blkid_probe_set_partitions_flags");
++ if (!libblkid.probe_set_partitions_flags)
++ return false;
++
++ libblkid.loaded = true;
++ return true;
++}
++
++static char *lookup_block_dev(const char *path, const char *key, bool is_uuid)
++{
++ int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
++ const char *type, *value;
++ char *result = NULL;
++ size_t len;
++ glob_t gl;
++ int i;
++
++ if (glob(path, gl_flags, NULL, &gl) < 0)
++ return NULL;
++
++ type = is_uuid ? "PART_ENTRY_UUID" : "PART_ENTRY_NAME";
++
++ for (i = 0; i < gl.gl_pathc; i++) {
++ blkid_probe pr = libblkid.new_probe_from_filename(gl.gl_pathv[i]);
++ if (!pr)
++ continue;
++
++ libblkid.probe_enable_partitions(pr, 1);
++ libblkid.probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
++
++ if (libblkid.do_safeprobe(pr))
++ goto free_pr;
++
++ if (!libblkid.probe_lookup_value(pr, type, &value, &len)) {
++ if (!strcmp(value, key))
++ result = strdup(gl.gl_pathv[i]);
++ }
++
++ free_pr:
++ libblkid.free_probe(pr);
++
++ if (result)
++ break;
++ }
++
++ globfree(&gl);
++
++ return result;
++}
++
++static char *find_block_dev(const char *key, bool is_uuid)
++{
++ char *devpath = NULL;
++ int i;
++
++ static const char *block_pats[] = {
++ "/dev/loop*",
++ "/dev/mmcblk*",
++ "/dev/sd*",
++ "/dev/hd*",
++ "/dev/md*",
++ "/dev/nvme*",
++ "/dev/vd*",
++ "/dev/xvd*",
++ "/dev/mapper/*",
++ };
++
++ if (!load_libblkid())
++ return NULL;
++
++ for (i = 0; i < ARRAY_SIZE(block_pats); i++) {
++ devpath = lookup_block_dev(block_pats[i], key, is_uuid);
++ if (devpath)
++ break;
++ }
++
++ return devpath;
++}
++
++char *blockdev_parse(const char *name)
++{
++ char *e, *part_dev_path;
++ struct stat st;
++
++ if (!name)
++ return NULL;
++
++ e = strchr(name, '=');
++ if (e) {
++ *e = 0;
++ e++;
++ }
++
++ if (!e) {
++ if (stat(name, &st))
++ return NULL;
++
++ if (!S_ISBLK(st.st_mode))
++ return NULL;
++
++ part_dev_path = strdup(name);
++ } else if (!strcmp(name, "PARTLABEL")) {
++ part_dev_path = find_block_dev(e, false);
++ } else if (!strcmp(name, "PARTUUID")) {
++ if (strlen(e) != 36)
++ return NULL;
++ part_dev_path = find_block_dev(e, true);
++ } else {
++ return NULL;
++ }
++
++ return part_dev_path;
++}
++
++char *boot_param_get_dev(const char *name)
++{
++ char partkey[BOOT_PARAM_STR_MAX_LEN];
++
++ read_boot_param_string(name, partkey, sizeof(partkey));
++
++ if (!partkey[0])
++ return NULL;
++
++ return blockdev_parse(partkey);
++}
+--- a/boot_param.h
++++ b/boot_param.h
+@@ -0,0 +1,21 @@
++// SPDX-License-Identifier: BSD-3-Clause
++/*
++ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#ifndef _BOOT_PARAM_H_
++#define _BOOT_PARAM_H_
++
++#include <stddef.h>
++#include <stdbool.h>
++
++bool read_boot_param_bool(const char *name);
++int read_boot_param_string(const char *name, char *val, size_t maxsize);
++int write_boot_param_string(const char *name, const char *val);
++
++char *blockdev_parse(const char *name);
++char *boot_param_get_dev(const char *name);
++
++#endif /* _BOOT_PARAM_H_ */
+--- a/libfstools/rootdisk.c
++++ b/libfstools/rootdisk.c
+@@ -26,6 +26,7 @@
+
+ #include "libfstools.h"
+ #include "volume.h"
++#include "../boot_param.h"
+
+ #include <linux/loop.h>
+
+@@ -42,6 +43,7 @@ struct rootdev_volume {
+ struct volume v;
+ uint64_t offset;
+ char loop_name[32];
++ char *dev_path;
+ };
+
+ static const char *rootdev;
+@@ -109,11 +111,15 @@ static int get_squashfs(struct squashfs_
+
+ static bool rootdisk_use_f2fs(struct rootdev_volume *p)
+ {
++ const char *dev = rootdev;
+ uint64_t size = 0;
+ bool ret = false;
+ int fd;
+
+- fd = open(rootdev, O_RDONLY);
++ if (p->dev_path)
++ dev = p->dev_path;
++
++ fd = open(dev, O_RDONLY);
+ if (ioctl(fd, BLKGETSIZE64, &size) == 0)
+ ret = size - p->offset > F2FS_MINSIZE;
+ close(fd);
+@@ -121,6 +127,30 @@ static bool rootdisk_use_f2fs(struct roo
+ return ret;
+ }
+
++static struct volume *find_existed_rootfs_data(void)
++{
++ struct rootdev_volume *p;
++ char *rootfs_data_dev;
++
++ rootfs_data_dev = boot_param_get_dev("rootfs_data_part");
++
++ if (!rootfs_data_dev)
++ return NULL;
++
++ ULOG_NOTE("Using existed rootfs_data device %s\n", rootfs_data_dev);
++
++ write_boot_param_string("rootfs_data_part", rootfs_data_dev);
++
++ p = calloc(1, sizeof(*p));
++ p->v.drv = &rootdisk_driver;
++ p->v.name = "rootfs_data";
++
++ p->offset = 0;
++ p->dev_path = rootfs_data_dev;
++
++ return &p->v;
++}
++
+ static struct volume *rootdisk_volume_find(char *name)
+ {
+ struct squashfs_super_block sb;
+@@ -129,6 +159,9 @@ static struct volume *rootdisk_volume_fi
+ if (strcmp(name, "rootfs_data") != 0)
+ return NULL;
+
++ if (read_boot_param_bool("no_split_rootfs_data"))
++ return find_existed_rootfs_data();
++
+ if (!rootdev)
+ rootdev = get_rootdev("/");
+ if (!rootdev)
+@@ -160,12 +193,16 @@ static struct volume *rootdisk_volume_fi
+ static int rootdisk_volume_identify(struct volume *v)
+ {
+ struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
++ const char *dev = rootdev;
+ int ret = FS_NONE;
+ uint32_t magic = 0;
+ size_t n;
+ FILE *f;
+
+- f = fopen(rootdev, "r");
++ if (p->dev_path)
++ dev = p->dev_path;
++
++ f = fopen(dev, "r");
+ if (!f)
+ return ret;
+
+@@ -265,6 +302,13 @@ static int rootdisk_volume_init(struct v
+ char str[128];
+ int ret = 0;
+
++ if (p->dev_path) {
++ /* Do not create loop device with no_split_rootfs_data set */
++ v->type = BLOCKDEV;
++ v->blk = p->dev_path;
++ goto do_format;
++ }
++
+ if (!p->loop_name[0] && rootdisk_create_loop(p) != 0) {
+ ULOG_ERR("unable to create loop device\n");
+ return -1;
+@@ -273,6 +317,7 @@ static int rootdisk_volume_init(struct v
+ v->type = BLOCKDEV;
+ v->blk = p->loop_name;
+
++do_format:
+ switch (rootdisk_volume_identify(v)) {
+ case FS_NONE:
+ ULOG_INFO("rootdisk overlay filesystem has not been formatted yet\n");
+--- a/mount_root.c
++++ b/mount_root.c
+@@ -23,6 +23,8 @@
+ #include "libfstools/libfstools.h"
+ #include "libfstools/volume.h"
+
++#include "boot_param.h"
++
+ /*
+ * Called in the early (PREINIT) stage, when we immediately need some writable
+ * filesystem.
+@@ -58,6 +60,12 @@ start(int argc, char *argv[1])
+ /* There isn't extroot, so just try to mount "rootfs_data" */
+ volume_init(data);
+ switch (volume_identify(data)) {
++ case -1:
++ /* Use ramoverlay if no "rootfs_data" device found with no_split_rootfs_data set */
++ if (!read_boot_param_bool("no_split_rootfs_data"))
++ break;
++
++ /* fall through */
+ case FS_NONE:
+ ULOG_WARN("no usable overlay filesystem found, using tmpfs overlay\n");
+ return ramoverlay();
diff --git a/prepare_sdk.sh b/prepare_sdk.sh
index 16c2ae1..ae48710 100755
--- a/prepare_sdk.sh
+++ b/prepare_sdk.sh
@@ -32,6 +32,7 @@
sdk_patch
#cp mtk target to OpenWRT
cp -fpR ${MTK_FEEDS_DIR}/target ./
+cp -fpR ${MTK_FEEDS_DIR}/package ./
#remove patch if choose to not "keep" patch
if [ -z ${2} ]; then
remove_patches