* [PATCH 0/4] extend cdev alias support
@ 2026-08-31 9:48 Sascha Hauer
2026-08-31 9:48 ` [PATCH 1/4] fs: devfs: remove aliases from the list when freeing them Sascha Hauer
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Sascha Hauer @ 2026-08-31 9:48 UTC (permalink / raw)
To: BAREBOX
We recently introduced creating a "usbdisk" alias for USB storage
devices. This so far was incomplete: The alias was only created for the
whole raw device, but not for the partitions. As it's usually the
partitions one is interested in the alias was rather useless. Also
block devices automatically get an automount point registered under
/mnt/, and here the aliases were not added.
This series fixes both issues. A nice side effect is that GPT partitions
which already have a /dev/diskx.<partname> alias now get an automount
point registered with the same name as well. Just like the aliases in
devfs the aliases under /mnt/ just appear as symbolic links.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
Sascha Hauer (4):
fs: devfs: remove aliases from the list when freeing them
fs: remove the default automount when a cdev is removed
fs: devfs: propagate cdev aliases to the partitions
fs: devfs: make the default automount reachable under cdev aliases
fs/devfs-core.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
fs/fs.c | 20 +++++++-
include/driver.h | 6 +++
3 files changed, 169 insertions(+), 2 deletions(-)
---
base-commit: 42e510a258d7c276c35f06c103e685c80d376eab
change-id: 20260831-usbdisk-aliases-c14b1584b7d0
Best regards,
--
Sascha Hauer <s.hauer@pengutronix.de>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/4] fs: devfs: remove aliases from the list when freeing them
2026-08-31 9:48 [PATCH 0/4] extend cdev alias support Sascha Hauer
@ 2026-08-31 9:48 ` Sascha Hauer
2026-08-31 9:48 ` [PATCH 2/4] fs: remove the default automount when a cdev is removed Sascha Hauer
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Sascha Hauer @ 2026-08-31 9:48 UTC (permalink / raw)
To: BAREBOX
devfs_remove_aliases() frees each struct cdev_alias without unlinking it
from cdev->aliases first, so on return the list head still points to
freed memory. Nothing trips over this today: the cdevs that get their
aliases removed are either freed right afterwards by cdev_free(), or the
list is re-initialized by devfs_create() when the cdev is registered
again. It is a trap waiting for the next caller though, so unlink the
entries properly.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Assisted-by: Claude:claude-opus-5
---
fs/devfs-core.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/fs/devfs-core.c b/fs/devfs-core.c
index 522d883e1c..b9e34f83bb 100644
--- a/fs/devfs-core.c
+++ b/fs/devfs-core.c
@@ -583,6 +583,7 @@ static void devfs_remove_aliases(struct cdev *cdev)
list_for_each_entry_safe(alias, tmp, &cdev->aliases, list) {
devfs_unlink(alias->name);
+ list_del(&alias->list);
free(alias->name);
free(alias);
}
--
2.47.3
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/4] fs: remove the default automount when a cdev is removed
2026-08-31 9:48 [PATCH 0/4] extend cdev alias support Sascha Hauer
2026-08-31 9:48 ` [PATCH 1/4] fs: devfs: remove aliases from the list when freeing them Sascha Hauer
@ 2026-08-31 9:48 ` Sascha Hauer
2026-08-31 9:48 ` [PATCH 3/4] fs: devfs: propagate cdev aliases to the partitions Sascha Hauer
2026-08-31 9:48 ` [PATCH 4/4] fs: devfs: make the default automount reachable under cdev aliases Sascha Hauer
3 siblings, 0 replies; 5+ messages in thread
From: Sascha Hauer @ 2026-08-31 9:48 UTC (permalink / raw)
To: BAREBOX
cdev_create_default_automount() registers an automount point at
/mnt/<cdevname> which runs "mount <cdevname>" when the directory is first
walked into. It never had a counterpart, so when the cdev goes away the
directory and the automount entry stay behind, pointing at a device that
no longer exists.
Unplugging a USB mass storage device leaves /mnt/disk0 and /mnt/disk0.0
around, and repartitioning at runtime leaves an entry for every partition
that doesn't come back - remove the two partitions of a disk and create a
single one instead and /mnt/disk0.1 is still there afterwards. The stale
entries are replaced once the same cdev name is registered again, since
automount_add() drops a previous entry for the same dentry, which is why
this went unnoticed for so long. Names that are not reused stay stale
forever though.
Add cdev_remove_default_automount() and call it from devfs_remove().
Whether a cdev has an automount is remembered in a new DEVFS_HAS_AUTOMOUNT
flag rather than being guessed from the path, so that an automount point
somebody registered by hand is never removed just because a cdev happens
to share its name.
Removal is safe while a filesystem is mounted: mounting holds the cdev
open via fsdev_open_cdev(), so devfs_remove() bails out with -EBUSY long
before we get here. automount_remove() looks the path up without
LOOKUP_DIRECTORY or LOOKUP_PARENT, so follow_automount() returns -EISDIR
and the lookup doesn't trigger the automount it is about to remove.
Fixes: 85dffaacfa ("fs: Create automount entries for the default mount pathes")
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Assisted-by: Claude:claude-opus-5
---
fs/devfs-core.c | 2 ++
fs/fs.c | 20 +++++++++++++++++++-
include/driver.h | 5 +++++
3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/fs/devfs-core.c b/fs/devfs-core.c
index b9e34f83bb..8b023009a0 100644
--- a/fs/devfs-core.c
+++ b/fs/devfs-core.c
@@ -605,6 +605,8 @@ int devfs_remove(struct cdev *cdev)
devfs_remove_aliases(cdev);
+ cdev_remove_default_automount(cdev);
+
list_for_each_entry_safe(c, tmp, &cdev->partitions, partition_entry)
cdevfs_del_partition(c);
diff --git a/fs/fs.c b/fs/fs.c
index ce41f23f88..f109e31075 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -3493,12 +3493,30 @@ void cdev_create_default_automount(struct cdev *cdev)
cmd = basprintf("mount %s", cdev->name);
make_directory(path);
- automount_add(path, cmd);
+ if (!automount_add(path, cmd))
+ cdev->flags |= DEVFS_HAS_AUTOMOUNT;
free(cmd);
free(path);
}
+void cdev_remove_default_automount(struct cdev *cdev)
+{
+ char *path;
+
+ if (!(cdev->flags & DEVFS_HAS_AUTOMOUNT))
+ return;
+
+ path = basprintf("/mnt/%s", cdev->name);
+
+ automount_remove(path);
+ rmdir(path);
+
+ cdev->flags &= ~DEVFS_HAS_AUTOMOUNT;
+
+ free(path);
+}
+
void automount_print(void)
{
struct automount *am;
diff --git a/include/driver.h b/include/driver.h
index 77b9b87695..b0602e6f16 100644
--- a/include/driver.h
+++ b/include/driver.h
@@ -572,6 +572,7 @@ extern struct list_head cdev_list;
#define DEVFS_PARTITION_FOR_FIXUP (1U << 13)
#define DEVFS_WRITE_AUTOERASE (1U << 14)
#define DEVFS_PARTITION_CAN_OVERLAP (1U << 15)
+#define DEVFS_HAS_AUTOMOUNT (1U << 16)
/**
* cdev_write_requires_erase - Check whether writes must be done to erased blocks
@@ -615,10 +616,14 @@ cdev_find_child_by_gpt_typeuuid(struct cdev *cdev, const guid_t *typeuuid);
#ifdef CONFIG_FS_AUTOMOUNT
void cdev_create_default_automount(struct cdev *cdev);
+void cdev_remove_default_automount(struct cdev *cdev);
#else
static inline void cdev_create_default_automount(struct cdev *cdev)
{
}
+static inline void cdev_remove_default_automount(struct cdev *cdev)
+{
+}
#endif
#define DEVFS_PARTITION_APPEND 0
--
2.47.3
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 3/4] fs: devfs: propagate cdev aliases to the partitions
2026-08-31 9:48 [PATCH 0/4] extend cdev alias support Sascha Hauer
2026-08-31 9:48 ` [PATCH 1/4] fs: devfs: remove aliases from the list when freeing them Sascha Hauer
2026-08-31 9:48 ` [PATCH 2/4] fs: remove the default automount when a cdev is removed Sascha Hauer
@ 2026-08-31 9:48 ` Sascha Hauer
2026-08-31 9:48 ` [PATCH 4/4] fs: devfs: make the default automount reachable under cdev aliases Sascha Hauer
3 siblings, 0 replies; 5+ messages in thread
From: Sascha Hauer @ 2026-08-31 9:48 UTC (permalink / raw)
To: BAREBOX
USB mass storage registers a disk as "disk0" and additionally aliases it
to "usbdisk0" so it can be told apart from other block devices at a
glance. That is only half of the work though: the partitions are named
after their master cdev, so /dev/disk0.0 and /dev/disk0.data have no
counterpart under the alias and the whole point of having one is lost as
soon as you want to address a partition.
Derive the missing aliases in devfs_add_alias_node() by replacing the
master's name at the start of a partition name with the alias, so that
"disk0" -> "usbdisk0" also gives:
/dev/usbdisk0.0 -> disk0.0
/dev/usbdisk0.data -> disk0.0
The master alias and the partitions can appear in either order, so both
directions have to be covered. USB storage adds the alias only after the
partition table has been parsed, hence a new master alias propagates
down to the partitions that are already there. A repartitioning at
runtime on the other hand removes the partitions - which takes the
derived aliases with them - and creates new ones on a master that
already has aliases, hence a new partition inherits from its master.
The same applies to the "disk0.<label>" alias a partition receives from
register_one_partition() after it has been created.
Aliases created this way are marked as derived and are never used as a
source for further derivation. Without that an alias that contains a dot
itself - "diskuuid.<uuid>" from storage-by-alias does - would keep
deriving new aliases from the ones it just created and never terminate.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Assisted-by: Claude:claude-opus-5
---
fs/devfs-core.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
include/driver.h | 1 +
2 files changed, 110 insertions(+), 1 deletion(-)
diff --git a/fs/devfs-core.c b/fs/devfs-core.c
index 8b023009a0..d647be9b9d 100644
--- a/fs/devfs-core.c
+++ b/fs/devfs-core.c
@@ -553,7 +553,26 @@ int devfs_create(struct cdev *new)
return 0;
}
-int devfs_add_alias_node(struct cdev *cdev, const char *name, struct device_node *np)
+/*
+ * An alias for a cdev is only half of the work when the cdev is partitioned:
+ * The partitions are named after their master cdev, so they have to become
+ * reachable under the alias as well. Registering "disk0" as "usbdisk0" thus
+ * results in:
+ *
+ * /dev/disk0
+ * /dev/disk0.0
+ * /dev/disk0.data -> disk0.0
+ * /dev/usbdisk0 -> disk0
+ * /dev/usbdisk0.0 -> disk0.0
+ * /dev/usbdisk0.data -> disk0.0
+ *
+ * The alias of the master and the partitions can show up in any order: USB
+ * mass storage registers the alias only once the whole device including its
+ * partitions is there, while repartitioning at runtime adds partitions to a
+ * master that already has aliases. Both directions are handled below.
+ */
+static int __devfs_add_alias(struct cdev *cdev, const char *name,
+ struct device_node *np, bool derived)
{
struct cdev *conflict;
struct cdev_alias *alias;
@@ -565,6 +584,7 @@ int devfs_add_alias_node(struct cdev *cdev, const char *name, struct device_node
alias = xzalloc(sizeof(*alias));
alias->name = xstrdup(name);
alias->device_node = np;
+ alias->derived = derived;
list_add_tail(&alias->list, &cdev->aliases);
cdev_symlink(cdev, name);
@@ -572,6 +592,89 @@ int devfs_add_alias_node(struct cdev *cdev, const char *name, struct device_node
return 0;
}
+/*
+ * Add an alias for @cdev derived from @name by replacing the leading @stem
+ * with @alias, i.e. stem "disk0", alias "usbdisk0" and name "disk0.data"
+ * yields the alias "usbdisk0.data".
+ */
+static void devfs_add_derived_alias(struct cdev *cdev, const char *stem,
+ const char *alias, const char *name)
+{
+ size_t stemlen = strlen(stem);
+ char *derived;
+
+ /* Only names of the form "<stem>.<partition>" can be translated */
+ if (strncmp(name, stem, stemlen) || name[stemlen] != '.')
+ return;
+
+ derived = xasprintf("%s%s", alias, name + stemlen);
+
+ /* A name clash only means we can't offer this alias, that's ok */
+ __devfs_add_alias(cdev, derived, NULL, true);
+
+ free(derived);
+}
+
+/*
+ * The partition @cdev has become known as @name. Make it known under the
+ * aliases of its master as well.
+ */
+static void devfs_inherit_master_aliases(struct cdev *cdev, const char *name)
+{
+ struct cdev *master = cdev->master;
+ struct cdev_alias *alias;
+
+ if (!master)
+ return;
+
+ cdev_for_each_alias(alias, master)
+ devfs_add_derived_alias(cdev, master->name, alias->name, name);
+}
+
+/*
+ * The master @cdev has become known as @alias. Make its partitions known
+ * under that alias as well, both by their name and by their own aliases.
+ */
+static void devfs_propagate_alias(struct cdev *cdev, const char *alias)
+{
+ struct cdev_alias *partalias;
+ struct cdev *partcdev;
+
+ for_each_cdev_partition(partcdev, cdev) {
+ devfs_add_derived_alias(partcdev, cdev->name, alias,
+ partcdev->name);
+
+ /*
+ * Appending to the list we are walking is fine here: the
+ * aliases added above and below are derived ones and those
+ * are skipped. Without that, an alias containing a dot
+ * itself (like "diskuuid.<uuid>") would endlessly derive
+ * new aliases from the ones it just created.
+ */
+ cdev_for_each_alias(partalias, partcdev) {
+ if (partalias->derived)
+ continue;
+
+ devfs_add_derived_alias(partcdev, cdev->name, alias,
+ partalias->name);
+ }
+ }
+}
+
+int devfs_add_alias_node(struct cdev *cdev, const char *name, struct device_node *np)
+{
+ int ret;
+
+ ret = __devfs_add_alias(cdev, name, np, false);
+ if (ret)
+ return ret;
+
+ devfs_inherit_master_aliases(cdev, name);
+ devfs_propagate_alias(cdev, name);
+
+ return 0;
+}
+
int devfs_add_alias(struct cdev *cdev, const char *name)
{
return devfs_add_alias_node(cdev, name, NULL);
@@ -735,6 +838,9 @@ static struct cdev *__devfs_add_partition(struct cdev *cdev,
return (void *)mtd;
list_add_tail(&mtd->cdev.partition_entry, &cdev->partitions);
+
+ devfs_inherit_master_aliases(&mtd->cdev, mtd->cdev.name);
+
return &mtd->cdev;
}
@@ -757,6 +863,8 @@ static struct cdev *__devfs_add_partition(struct cdev *cdev,
cdev_create_default_automount(new);
+ devfs_inherit_master_aliases(new, new->name);
+
return new;
}
diff --git a/include/driver.h b/include/driver.h
index b0602e6f16..c26a89c5ce 100644
--- a/include/driver.h
+++ b/include/driver.h
@@ -458,6 +458,7 @@ struct cdev_alias {
char *name;
struct device_node *device_node;
struct list_head list;
+ bool derived; /* automatically derived from an alias of the master cdev */
};
static inline struct device_node *cdev_of_node(const struct cdev *cdev)
--
2.47.3
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 4/4] fs: devfs: make the default automount reachable under cdev aliases
2026-08-31 9:48 [PATCH 0/4] extend cdev alias support Sascha Hauer
` (2 preceding siblings ...)
2026-08-31 9:48 ` [PATCH 3/4] fs: devfs: propagate cdev aliases to the partitions Sascha Hauer
@ 2026-08-31 9:48 ` Sascha Hauer
3 siblings, 0 replies; 5+ messages in thread
From: Sascha Hauer @ 2026-08-31 9:48 UTC (permalink / raw)
To: BAREBOX
A cdev with a default automount is mounted by walking into
/mnt/<cdevname>, so /mnt only ever knows a cdev under its real name. With
a USB disk registered as "disk0" and aliased to "usbdisk0" you can read
/dev/usbdisk0.data, but mounting it still requires knowing that it really
is disk0.0.
Link the aliases next to the automount directory, so that /mnt/usbdisk0.data
points to disk0.0 and walking into it triggers the same automount as
/mnt/disk0.0 does. This also covers the aliases a partition has always
had: /mnt/disk0.data did not exist so far even though /dev/disk0.data
did.
The links are created and removed along with the alias itself, hence
devfs_remove() has to remove the aliases before dropping the automount -
cdev_mnt_unlink() needs DEVFS_HAS_AUTOMOUNT to still be set to know that
there is anything to unlink.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Assisted-by: Claude:claude-opus-5
---
fs/devfs-core.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/fs/devfs-core.c b/fs/devfs-core.c
index d647be9b9d..35a4bdfdc4 100644
--- a/fs/devfs-core.c
+++ b/fs/devfs-core.c
@@ -520,6 +520,35 @@ static void devfs_unlink(const char *name)
free(path);
}
+/*
+ * A cdev with a default automount is mounted by walking into
+ * /mnt/<cdevname>. Link the aliases next to it so that an alias can be used
+ * for mounting just as well.
+ */
+static void cdev_mnt_symlink(struct cdev *cdev, const char *linkname)
+{
+ char *path;
+
+ if (!(cdev->flags & DEVFS_HAS_AUTOMOUNT))
+ return;
+
+ path = xasprintf("/mnt/%s", linkname);
+ symlink(cdev->name, path);
+ free(path);
+}
+
+static void cdev_mnt_unlink(struct cdev *cdev, const char *linkname)
+{
+ char *path;
+
+ if (!(cdev->flags & DEVFS_HAS_AUTOMOUNT))
+ return;
+
+ path = xasprintf("/mnt/%s", linkname);
+ unlink(path);
+ free(path);
+}
+
void devfs_init(void)
{
struct cdev *cdev;
@@ -588,6 +617,7 @@ static int __devfs_add_alias(struct cdev *cdev, const char *name,
list_add_tail(&alias->list, &cdev->aliases);
cdev_symlink(cdev, name);
+ cdev_mnt_symlink(cdev, name);
return 0;
}
@@ -686,6 +716,7 @@ static void devfs_remove_aliases(struct cdev *cdev)
list_for_each_entry_safe(alias, tmp, &cdev->aliases, list) {
devfs_unlink(alias->name);
+ cdev_mnt_unlink(cdev, alias->name);
list_del(&alias->list);
free(alias->name);
free(alias);
@@ -706,6 +737,7 @@ int devfs_remove(struct cdev *cdev)
devfs_unlink(cdev->name);
+ /* Must happen before the automount is dropped below */
devfs_remove_aliases(cdev);
cdev_remove_default_automount(cdev);
--
2.47.3
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-31 9:49 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-31 9:48 [PATCH 0/4] extend cdev alias support Sascha Hauer
2026-08-31 9:48 ` [PATCH 1/4] fs: devfs: remove aliases from the list when freeing them Sascha Hauer
2026-08-31 9:48 ` [PATCH 2/4] fs: remove the default automount when a cdev is removed Sascha Hauer
2026-08-31 9:48 ` [PATCH 3/4] fs: devfs: propagate cdev aliases to the partitions Sascha Hauer
2026-08-31 9:48 ` [PATCH 4/4] fs: devfs: make the default automount reachable under cdev aliases Sascha Hauer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox