[PATCH v2] fstools: block: allow multiple mount points for a single device
Shen Wei
shinkosame at gmail.com
Wed Mar 11 20:44:50 PDT 2026
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>
---
block.c | 417 ++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 268 insertions(+), 149 deletions(-)
diff --git a/block.c b/block.c
index 37f5bfb..848cda0 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,31 @@ static void parse_mount_options(struct mount *m, char *optstr)
free(optstr);
}
+static char* _concat_key(char *key1, char *key2)
+{
+ char *nkey;
+ if (key2) {
+ int klen1 = strlen(key1);
+ int klen2 = strlen(key2);
+ nkey = (char *) malloc(klen1 + klen2 + 2);
+ if (!nkey) {
+ ULOG_ERR("failed to allocate memory: %d (%m)\n", errno);
+ exit(EXIT_FAILURE);
+ }
+ memcpy(nkey, key1, klen1);
+ nkey[klen1] = ',';
+ memcpy(nkey + klen1 + 1, key2, klen2);
+ nkey[klen1 + klen2 + 1] = '\0';
+ } else {
+ nkey = strdup(key1);
+ if (!nkey) {
+ ULOG_ERR("failed to allocate memory: %d (%m)\n", errno);
+ exit(EXIT_FAILURE);
+ }
+ }
+ return nkey;
+}
+
static int mount_add(struct uci_section *s)
{
struct blob_attr *tb[__MOUNT_MAX] = { 0 };
@@ -294,11 +320,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 +413,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 +664,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 +734,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 +749,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 +805,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);
- printf(" TYPE=\"%s\"\n", pr->type);
+ if (pr->label)
+ printf(" LABEL=\"%s\"", pr->label);
+
+ if (pr->version)
+ printf(" VERSION=\"%s\"", pr->version);
+
+ printf(" TYPE=\"%s\"\n", pr->type);
+ }
return 0;
}
@@ -1080,12 +1162,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 +1188,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 +1352,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 +1397,24 @@ 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 +1676,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 +1684,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 +1869,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;
--
2.51.0
More information about the openwrt-devel
mailing list