[PATCH 02/13] fs: devfs: count an open partition as an open device

Sascha Hauer s.hauer at pengutronix.de
Mon Aug 31 06:20:09 PDT 2026


devfs_remove() refuses to remove a cdev that is open, but opening a
partition only increments the open count of the partition itself:
cdev_open() passes the master to the ->open operation, but counts on the
cdev it was given. A disk whose partition is mounted therefore does not
look busy at all.

Removing it then gets half done. The master is unlinked from cdev_list
and its aliases and automount are dropped, then the loop over the
partitions calls cdevfs_del_partition(), which does return -EBUSY for the
mounted partition, but nobody looks at the return value. The partition
stays in cdev_list with its ->master and ->priv pointing at the block
device the caller is about to free, and the next access walks
cdev_get_master() into freed memory.

A device with an open partition is in use, so count the open along the
whole chain of masters instead of only on the cdev that was opened. The
open count then means what it says and devfs_remove() needs no special
case.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
Assisted-by: Claude:claude-opus-5
---
 fs/devfs-core.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/fs/devfs-core.c b/fs/devfs-core.c
index 522d883e1c..c106e65a04 100644
--- a/fs/devfs-core.c
+++ b/fs/devfs-core.c
@@ -249,6 +249,7 @@ static struct cdev *cdev_get_master(struct cdev *cdev)
 int cdev_open(struct cdev *cdev, unsigned long flags)
 {
 	struct cdev *master = cdev_get_master(cdev);
+	struct cdev *c;
 	int ret;
 
 	if (cdev->ops->open) {
@@ -257,7 +258,14 @@ int cdev_open(struct cdev *cdev, unsigned long flags)
 			return ret;
 	}
 
-	cdev->open++;
+	/*
+	 * A device with an open partition is in use itself, so count the
+	 * open along the whole chain up to the device the partition lives
+	 * on. Without that a disk with a mounted partition looks idle and
+	 * could be removed from under the filesystem.
+	 */
+	for (c = cdev; c; c = c->master)
+		c->open++;
 
 	return 0;
 }
@@ -314,6 +322,7 @@ struct cdev *cdev_open_by_path_name(const char *name, unsigned long flags)
 int cdev_close(struct cdev *cdev)
 {
 	struct cdev *master = cdev_get_master(cdev);
+	struct cdev *c;
 
 	if (cdev->ops->close) {
 		int ret = cdev->ops->close(master);
@@ -321,7 +330,8 @@ int cdev_close(struct cdev *cdev)
 			return ret;
 	}
 
-	cdev->open--;
+	for (c = cdev; c; c = c->master)
+		c->open--;
 
 	return 0;
 }

-- 
2.47.3




More information about the barebox mailing list