[PATCH] fstools: block: allow multiple mount points for a single device
Hartmut Knaack
knaack.h at gmx.de
Wed Mar 11 14:53:10 PDT 2026
Shen Wei schrieb am 11.03.26 um 01:39:
> From: shinkosame <shinkosame at gmail.com>
>
> Previously, if multiple 'mount' sections in /etc/config/fstab referred
> to the same physical device (via UUID, Label, or device path), the
> latter configuration would override the previous ones, resulting in
> only one active mount point.
>
> This patch modifies the device matching logic to allow a single
> source device to be mounted to multiple target paths. This is
> particularly useful for users who need to expose the same partition
> in different directory structures.
>
> Signed-off-by: Shen Wei <shinkosame at gmail.com>
Hi,
I have not looked completely into the code, mainly the memory allocations
caught my attention. Please keep in mind, that malloc and strdup may fail
and return a NULL pointer.
Could you briefly explain how/where the dynamic memory is freed?
Thanks
Hartmut
> ---
> block.c | 409 +++++++++++++++++++++++++++++++++++---------------------
> 1 file changed, 260 insertions(+), 149 deletions(-)
>
> diff --git a/block.c b/block.c
> index 37f5bfb..64a8af3 100644
> --- a/block.c
> +++ b/block.c
> @@ -47,6 +47,7 @@
> #include "probe.h"
>
> #define AUTOFS_MOUNT_PATH "/tmp/run/blockd/"
> +#define MAX_MOUNT_POINT_COUNT 10
>
> #ifdef UBIFS_EXTROOT
> #include "libubi/libubi.h"
> @@ -253,6 +254,22 @@ static void parse_mount_options(struct mount *m, char *optstr)
> free(optstr);
> }
>
> +static char* _concat_key(char *key1, char *key2)
> +{
> + if (key2) {
> + int klen1 = strlen(key1);
> + int klen2 = strlen(key2);
> + char *nkey = (char *) malloc(klen1 + klen2 + 2);
> + memcpy(nkey, key1, klen1);
> + nkey[klen1] = ',';
> + memcpy(nkey + klen1 + 1, key2, klen2);
> + nkey[klen1 + klen2 + 1] = '\0';
> + return nkey;
> + } else {
> + return strdup(key1);
> + }
> +}
> +
> static int mount_add(struct uci_section *s)
> {
> struct blob_attr *tb[__MOUNT_MAX] = { 0 };
> @@ -294,11 +311,11 @@ static int mount_add(struct uci_section *s)
> }
>
> if (m->uuid)
> - vlist_add(&mounts, &m->node, m->uuid);
> + vlist_add(&mounts, &m->node, _concat_key(m->uuid, m->target));
> else if (m->label)
> - vlist_add(&mounts, &m->node, m->label);
> + vlist_add(&mounts, &m->node, _concat_key(m->label, m->target));
> else if (m->device)
> - vlist_add(&mounts, &m->node, m->device);
> + vlist_add(&mounts, &m->node, _concat_key(m->device, m->target));
>
> return 0;
> }
> @@ -387,25 +404,44 @@ static struct mount* find_swap(const char *uuid, const char *label, const char *
> return NULL;
> }
>
> -static struct mount* find_block(const char *uuid, const char *label, const char *device,
> +static struct mount** find_block(const char *uuid, const char *label, const char *device,
> const char *target)
> {
> + int msi = 0;
> + static struct mount *ms[MAX_MOUNT_POINT_COUNT + 1];
> struct mount *m;
>
> vlist_for_each_element(&mounts, m, node) {
> if (m->type != TYPE_MOUNT)
> continue;
> - if (m->uuid && uuid && !strcasecmp(m->uuid, uuid))
> - return m;
> - if (m->label && label && !strcmp(m->label, label))
> - return m;
> - if (m->target && target && !strcmp(m->target, target))
> - return m;
> - if (m->device && device && !strcmp(m->device, device))
> - return m;
> + if (m->uuid && uuid && !strcasecmp(m->uuid, uuid)) {
> + if (msi < MAX_MOUNT_POINT_COUNT)
> + ms[msi++] = m;
> + else
> + ULOG_WARN("Max mount point limit reached, skipping remaining objects\n");
> + }
> + if (m->label && label && !strcmp(m->label, label)) {
> + if (msi < MAX_MOUNT_POINT_COUNT)
> + ms[msi++] = m;
> + else
> + ULOG_WARN("Max mount point limit reached, skipping remaining objects\n");
> + }
> + if (m->target && target && !strcmp(m->target, target)) {
> + if (msi < MAX_MOUNT_POINT_COUNT)
> + ms[msi++] = m;
> + else
> + ULOG_WARN("Max mount point limit reached, skipping remaining objects\n");
> + }
> + if (m->device && device && !strcmp(m->device, device)) {
> + if (msi < MAX_MOUNT_POINT_COUNT)
> + ms[msi++] = m;
> + else
> + ULOG_WARN("Max mount point limit reached, skipping remaining objects\n");
> + }
> }
>
> - return NULL;
> + ms[msi] = NULL;
> + return ms;
> }
>
> static void mounts_update(struct vlist_tree *tree, struct vlist_node *node_new,
> @@ -619,11 +655,13 @@ static struct probe_info* find_block_info(char *uuid, char *label, char *path)
> return NULL;
> }
>
> -static char* find_mount_point(char *block)
> +static char** find_mount_point(char *block)
> {
> FILE *fp = fopen("/proc/self/mountinfo", "r");
> static char line[256];
> - char *point = NULL, *pos, *tmp, *cpoint, *devname;
> + static char *points[MAX_MOUNT_POINT_COUNT + 1];
> + char *pos, *tmp, *cpoint, *devname;
> + int point_idx = 0;
> struct stat s;
> int rstat;
> unsigned int minor, major;
> @@ -687,8 +725,11 @@ static char* find_mount_point(char *block)
> *pos = '\0';
> devname = tmp;
> if (!strcmp(block, devname)) {
> - point = strdup(cpoint);
> - break;
> + if (point_idx < MAX_MOUNT_POINT_COUNT)
> + points[point_idx++] = strdup(cpoint);
> + else
> + ULOG_WARN("Max mount point limit reached, skipping remaining objects\n");
> + continue;;
> }
>
> if (rstat)
> @@ -699,36 +740,54 @@ static char* find_mount_point(char *block)
>
> if (major == major(s.st_rdev) &&
> minor == minor(s.st_rdev)) {
> - point = strdup(cpoint);
> - break;
> + if (point_idx < MAX_MOUNT_POINT_COUNT)
> + points[point_idx++] = strdup(cpoint);
> + else
> + ULOG_WARN("Max mount point limit reached, skipping remaining objects\n");
> + continue;;
> }
> }
>
> fclose(fp);
>
> - return point;
> + points[point_idx] = NULL;
> + return points;
> }
>
> static int print_block_uci(struct probe_info *pr)
> {
> + char *mp;
> if (!strcmp(pr->type, "swap")) {
> printf("config 'swap'\n");
> + if (pr->uuid)
> + printf("\toption\tuuid\t'%s'\n", pr->uuid);
> + else
> + printf("\toption\tdevice\t'%s'\n", pr->dev);
> + printf("\toption\tenabled\t'0'\n\n");
> } else {
> - char *mp = find_mount_point(pr->dev);
> -
> - printf("config 'mount'\n");
> - if (mp) {
> - printf("\toption\ttarget\t'%s'\n", mp);
> - free(mp);
> + char **mps = find_mount_point(pr->dev);
> +
> + if (*mps) {
> + while ((mp = *mps++)) {
> + printf("config 'mount'\n");
> + printf("\toption\ttarget\t'%s'\n", mp);
> + if (pr->uuid)
> + printf("\toption\tuuid\t'%s'\n", pr->uuid);
> + else
> + printf("\toption\tdevice\t'%s'\n", pr->dev);
> + printf("\toption\tenabled\t'0'\n\n");
> + free(mp);
> + }
> } else {
> + printf("config 'mount'\n");
> printf("\toption\ttarget\t'/mnt/%s'\n", basename(pr->dev));
> + if (pr->uuid)
> + printf("\toption\tuuid\t'%s'\n", pr->uuid);
> + else
> + printf("\toption\tdevice\t'%s'\n", pr->dev);
> + printf("\toption\tenabled\t'0'\n\n");
> }
> }
> - if (pr->uuid)
> - printf("\toption\tuuid\t'%s'\n", pr->uuid);
> - else
> - printf("\toption\tdevice\t'%s'\n", pr->dev);
> - printf("\toption\tenabled\t'0'\n\n");
>
> return 0;
> }
> @@ -737,23 +796,37 @@ static int print_block_info(struct probe_info *pr)
> {
> static char *mp;
>
> - mp = find_mount_point(pr->dev);
> - printf("%s:", pr->dev);
> - if (pr->uuid)
> - printf(" UUID=\"%s\"", pr->uuid);
> + char **mps = find_mount_point(pr->dev);
> + if (*mps) {
> + while ((mp = *mps++)) {
> + printf("%s:", pr->dev);
> + if (pr->uuid)
> + printf(" UUID=\"%s\"", pr->uuid);
>
> - if (pr->label)
> - printf(" LABEL=\"%s\"", pr->label);
> + if (pr->label)
> + printf(" LABEL=\"%s\"", pr->label);
>
> - if (pr->version)
> - printf(" VERSION=\"%s\"", pr->version);
> + if (pr->version)
> + printf(" VERSION=\"%s\"", pr->version);
>
> - if (mp) {
> - printf(" MOUNT=\"%s\"", mp);
> - free(mp);
> - }
> + printf(" MOUNT=\"%s\"", mp);
> + free(mp);
> +
> + printf(" TYPE=\"%s\"\n", pr->type);
> + }
> + } else {
> + printf("%s:", pr->dev);
> + if (pr->uuid)
> + printf(" UUID=\"%s\"", pr->uuid);
> +
> + if (pr->label)
> + printf(" LABEL=\"%s\"", pr->label);
>
> - printf(" TYPE=\"%s\"\n", pr->type);
> + if (pr->version)
> + printf(" VERSION=\"%s\"", pr->version);
> +
> + printf(" TYPE=\"%s\"\n", pr->type);
> + }
>
> return 0;
> }
> @@ -1080,12 +1153,16 @@ static int blockd_notify(const char *method, char *device, struct mount *m,
> static int mount_device(struct probe_info *pr, int type)
> {
> struct mount *m;
> + struct mount **ms;
> struct stat st;
> char *_target = NULL;
> char *target;
> char *device;
> char *mp;
> - int err;
> + char **mps;
> + bool is_mounted;
> + int err = 0, err2;
> + int is_mnt_exist = 0;
>
> if (!pr)
> return -1;
> @@ -1102,131 +1179,155 @@ static int mount_device(struct probe_info *pr, int type)
> return 0;
> }
>
> - m = find_block(pr->uuid, pr->label, device, NULL);
> - if (m && m->extroot)
> - return -1;
> + ms = find_block(pr->uuid, pr->label, device, NULL);
> + while ((m = *ms++)) {
> + if (m && m->extroot)
> + continue;
>
> - mp = find_mount_point(pr->dev);
> - if (mp) {
> - if (m && m->type == TYPE_MOUNT && m->target && strcmp(m->target, mp)) {
> - ULOG_ERR("%s is already mounted on %s\n", pr->dev, mp);
> - err = -1;
> - } else
> - err = 0;
> - free(mp);
> - return err;
> - }
> + is_mounted = false;
> + mps = find_mount_point(pr->dev);
> + while ((mp = *mps++)) {
> + if (!is_mounted && m && m->type == TYPE_MOUNT && m->target && !strcmp(m->target, mp)) {
> + is_mounted = true;
> + }
> + free(mp);
> + }
> + if (is_mounted) {
> + continue;
> + }
>
> - if (type == TYPE_HOTPLUG)
> - blockd_notify("hotplug", device, m, pr);
> + if (type == TYPE_HOTPLUG)
> + blockd_notify("hotplug", device, m, pr);
>
> - /* Check if device should be mounted & set the target directory */
> - if (m) {
> - switch (type) {
> - case TYPE_HOTPLUG:
> - if (m->autofs)
> - return 0;
> - if (!auto_mount)
> - return -1;
> - break;
> - case TYPE_AUTOFS:
> - if (!m->autofs)
> - return -1;
> - break;
> - case TYPE_DEV:
> - if (m->autofs)
> - return -1;
> - break;
> - }
> + /* Check if device should be mounted & set the target directory */
> + if (m) {
> + switch (type) {
> + case TYPE_HOTPLUG:
> + if (m->autofs)
> + continue;;
> + if (!auto_mount) {
> + err = -1;
> + continue;
> + }
> + break;
> + case TYPE_AUTOFS:
> + if (!m->autofs) {
> + err = -1;
> + continue;
> + }
> + break;
> + case TYPE_DEV:
> + if (m->autofs) {
> + err = -1;
> + continue;
> + }
> + break;
> + }
>
> - if (m->autofs) {
> - if (asprintf(&_target, "/tmp/run/blockd/%s", device) == -1)
> - exit(ENOMEM);
> + if (m->autofs) {
> + if (asprintf(&_target, "/tmp/run/blockd/%s", device) == -1)
> + exit(ENOMEM);
>
> - target = _target;
> - } else if (m->target) {
> - target = m->target;
> - } else {
> + target = _target;
> + } else if (m->target) {
> + target = m->target;
> + } else {
> + if (asprintf(&_target, "/mnt/%s", device) == -1)
> + exit(ENOMEM);
> +
> + target = _target;
> + }
> + } else if (anon_mount) {
> if (asprintf(&_target, "/mnt/%s", device) == -1)
> exit(ENOMEM);
>
> target = _target;
> + } else {
> + /* No reason to mount this device */
> + continue;
> }
> - } else if (anon_mount) {
> - if (asprintf(&_target, "/mnt/%s", device) == -1)
> - exit(ENOMEM);
>
> - target = _target;
> - } else {
> - /* No reason to mount this device */
> - return 0;
> - }
> + /* Mount the device */
>
> - /* Mount the device */
> + if (check_fs)
> + check_filesystem(pr);
>
> - if (check_fs)
> - check_filesystem(pr);
> + mkdir_p(target, 0755);
> + if (!lstat(target, &st) && S_ISLNK(st.st_mode))
> + unlink(target);
>
> - mkdir_p(target, 0755);
> - if (!lstat(target, &st) && S_ISLNK(st.st_mode))
> - unlink(target);
> + err2 = handle_mount(pr->dev, target, pr->type, m);
> + if (err2) {
> + ULOG_ERR("mounting %s (%s) as %s failed (%d) - %m\n",
> + pr->dev, pr->type, target, errno);
>
> - err = handle_mount(pr->dev, target, pr->type, m);
> - if (err) {
> - ULOG_ERR("mounting %s (%s) as %s failed (%d) - %m\n",
> - pr->dev, pr->type, target, errno);
> + if (_target)
> + free(_target);
> +
> + err = err2;
> + continue;
> + }
>
> if (_target)
> free(_target);
>
> - return err;
> - }
> -
> - if (_target)
> - free(_target);
> + handle_swapfiles(true);
>
> - handle_swapfiles(true);
> -
> - if (type != TYPE_AUTOFS)
> - blockd_notify("mount", device, NULL, NULL);
> + if (type != TYPE_AUTOFS)
> + blockd_notify("mount", device, NULL, NULL);
> +
> + is_mnt_exist = 1;
> + }
>
> - return 0;
> + if (!is_mnt_exist)
> + return -1;
> + else
> + return err;
> }
>
> -static int umount_device(char *path, int type, bool all)
> +static int umount_device(char *path, int type, bool all, bool skip_extroot)
> {
> char *mp, *devpath;
> - int err;
> + char **mps;
> + int err = 0, err2;
>
> if (strlen(path) > 5 && !strncmp("/dev/", path, 5)) {
> - mp = find_mount_point(path);
> + mps = find_mount_point(path);
> } else {
> devpath = malloc(strlen(path) + 6);
> strcpy(devpath, "/dev/");
> strcat(devpath, path);
> - mp = find_mount_point(devpath);
> + mps = find_mount_point(devpath);
> free(devpath);
> }
>
> - if (!mp)
> + if (!mps[0])
> return -1;
> - if (!strcmp(mp, "/") && !all) {
> - free(mp);
> - return 0;
> - }
> - if (type != TYPE_AUTOFS)
> - blockd_notify("umount", basename(path), NULL, NULL);
> + while ((mp = *mps++)) {
> + if (skip_extroot && (!strcmp(mp, "/") || !strcmp(mp, "/overlay"))) {
> + free(mp);
> + continue;
> + }
>
> - err = umount2(mp, MNT_DETACH);
> - if (err) {
> - ULOG_ERR("unmounting %s (%s) failed (%d) - %m\n", path, mp,
> - errno);
> - } else {
> - ULOG_INFO("unmounted %s (%s)\n", path, mp);
> - rmdir(mp);
> - }
> + if (!strcmp(mp, "/") && !all) {
> + free(mp);
> + continue;
> + }
> + if (type != TYPE_AUTOFS)
> + blockd_notify("umount", basename(path), NULL, NULL);
> +
> + err2 = umount2(mp, MNT_DETACH);
> + if (err2) {
> + ULOG_ERR("unmounting %s (%s) failed (%d) - %m\n", path, mp,
> + errno);
> + err = err2;
> + } else {
> + ULOG_INFO("unmounted %s (%s)\n", path, mp);
> + rmdir(mp);
> + }
>
> - free(mp);
> + free(mp);
> + }
> return err;
> }
>
> @@ -1242,7 +1343,7 @@ static int mount_action(char *action, char *device, int type)
> if (type == TYPE_HOTPLUG)
> blockd_notify("hotplug", device, NULL, NULL);
>
> - umount_device(device, type, true);
> + umount_device(device, type, true, false);
>
> return 0;
> } else if (strcmp(action, "add")) {
> @@ -1287,19 +1388,25 @@ static int main_autofs(int argc, char **argv)
> cache_load(1);
> list_for_each_entry(pr, &devices, list) {
> struct mount *m;
> + struct mount **ms;
> char *mp;
> + char **mps;
>
> if (!strcmp(pr->type, "swap"))
> continue;
>
> - m = find_block(pr->uuid, pr->label, NULL, NULL);
> - if (m && m->extroot)
> - continue;
> + ms = find_block(pr->uuid, pr->label, NULL, NULL);
> + while ((m = *ms++)) {
> +
> + if (m && m->extroot)
> + continue;
>
> - blockd_notify("hotplug", pr->dev, m, pr);
> - if ((!m || !m->autofs) && (mp = find_mount_point(pr->dev))) {
> - blockd_notify("mount", pr->dev, NULL, NULL);
> - free(mp);
> + blockd_notify("hotplug", pr->dev, m, pr);
> + if ((!m || !m->autofs) && *(mps = find_mount_point(pr->dev))) {
> + blockd_notify("mount", pr->dev, NULL, NULL);
> + while ((mp = *mps++))
> + free(mp);
> + }
> }
> }
> } else {
> @@ -1561,6 +1668,7 @@ static int mount_extroot(char *cfg)
> char *path = mnt;
> struct probe_info *pr;
> struct mount *m;
> + struct mount **ms;
> int err = -1;
>
> /* Load @cfg/etc/config/fstab */
> @@ -1568,9 +1676,12 @@ static int mount_extroot(char *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");
> + ms = find_block(NULL, NULL, NULL, "/");
> + m = ms[0];
> + if (!m) {
> + ms = find_block(NULL, NULL, NULL, "/overlay");
> + m = ms[0];
> + }
>
> if (!m || !m->extroot)
> {
> @@ -1750,16 +1861,16 @@ static int main_umount(int argc, char **argv)
> all = !strcmp(argv[2], "-a");
>
> list_for_each_entry(pr, &devices, list) {
> - struct mount *m;
> + // struct mount *m;
>
> if (!strcmp(pr->type, "swap"))
> continue;
>
> - m = find_block(pr->uuid, pr->label, basename(pr->dev), NULL);
> - if (m && m->extroot)
> - continue;
> + // m = find_block(pr->uuid, pr->label, basename(pr->dev), NULL);
> + // if (m && m->extroot)
> + // continue;
>
> - umount_device(pr->dev, TYPE_DEV, all);
> + umount_device(pr->dev, TYPE_DEV, all, true);
> }
>
> return 0;
>
More information about the openwrt-devel
mailing list