[git commit] volume_id: improve handling of too-small (usually zero-byte or erroring) blockdevs

Denys Vlasenko vda.linux at googlemail.com
Wed Feb 4 14:59:33 UTC 2026


commit: https://git.busybox.net/busybox/commit/?id=cdcb4ce314531bfea23b844be7df28ab8c0818da
branch: https://git.busybox.net/busybox/log/?h=master

function                                             old     new   delta
volume_id_get_buffer                                 327     372     +45
volume_id_open_node                                   17      24      +7
volume_id_probe_all                                  133     123     -10
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/1 up/down: 52/-10)             Total: 42 bytes

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 util-linux/volume_id/util.c               | 29 ++++++++++++++++++++++-------
 util-linux/volume_id/volume_id.c          | 25 +++++++++++++++----------
 util-linux/volume_id/volume_id_internal.h | 16 +++++++++++++++-
 3 files changed, 52 insertions(+), 18 deletions(-)

diff --git a/util-linux/volume_id/util.c b/util-linux/volume_id/util.c
index b154f9378..b59aa99b2 100644
--- a/util-linux/volume_id/util.c
+++ b/util-linux/volume_id/util.c
@@ -176,6 +176,12 @@ void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len)
 	unsigned small_off;
 	ssize_t read_len;
 
+	if (id->known_size != UNKNOWN_SIZE
+	 && id->known_size < off + len
+	) {
+		return NULL;
+	}
+
 	dbg("get buffer off 0x%llx(%llu), len 0x%zx",
 		(unsigned long long) off, (unsigned long long) off, len);
 
@@ -224,20 +230,29 @@ void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len)
  do_read:
 	if (lseek(id->fd, off, SEEK_SET) != off) {
 		dbg("seek(0x%llx) failed", (unsigned long long) off);
+		off = lseek(id->fd, 0, SEEK_END);
+		if (off < 0)
+			off = 0;
 		goto err;
 	}
 	read_len = full_read(id->fd, dst, len);
 	if (read_len != len) {
 		dbg("requested 0x%x bytes, got 0x%x bytes",
 				(unsigned) len, (unsigned) read_len);
+		if (read_len > 0)
+			off += read_len;
  err:
-		/* No filesystem can be this tiny. It's most likely
-		 * non-associated loop device, empty drive and so on.
-		 * Flag it, avoiding future accesses. Rationale:
-		 * users complained of slow blkid due to empty floppy drives.
-		 */
-		if (off <= 1024)
-			id->error = 1;
+		/* The image is definitely only OFF bytes large */
+		if (off < UNKNOWN_SIZE) {
+			/* ...and OFF is small, we can use
+			 * that knowledge to skip future probes:
+			 * cases such as non-associated loop devices,
+			 * empty (floppy) drives and so on.
+			 * Record it, avoiding future accesses. Users
+			 * complained of slow blkid due to empty floppys.
+			 */
+			id->known_size = off;
+		}
 		/* id->seekbuf_len or id->sbbuf_len is wrong now! Fixing. */
 		volume_id_free_buffer(id);
 		return NULL;
diff --git a/util-linux/volume_id/volume_id.c b/util-linux/volume_id/volume_id.c
index 8ceb61bde..53715121b 100644
--- a/util-linux/volume_id/volume_id.c
+++ b/util-linux/volume_id/volume_id.c
@@ -180,6 +180,7 @@ static const probe_fptr fs2[] ALIGN_PTR = {
 
 int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size)
 {
+	int retval = 0;
 	unsigned i;
 
 	/* probe for raid first, cause fs probes may be successful on raid members */
@@ -187,24 +188,26 @@ int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64
 		for (i = 0; i < ARRAY_SIZE(raid1); i++) {
 			if (raid1[i](id, /*off,*/ size) == 0)
 				goto ret;
-			if (id->error)
-				goto ret;
+			//if (id->known_size < MIN_VALID_FS_SIZE)
+			//	goto ret_bad;
+//Redundant? none of the subsequent probers will succeed
+//(or even attempt reads) if the above is true.
 		}
 	}
 
 	for (i = 0; i < ARRAY_SIZE(raid2); i++) {
 		if (raid2[i](id /*,off*/) == 0)
 			goto ret;
-		if (id->error)
-			goto ret;
+		//if (id->known_size < MIN_VALID_FS_SIZE)
+		//	goto ret_bad;
 	}
 
 	/* signature in the first block, only small buffer needed */
 	for (i = 0; i < ARRAY_SIZE(fs1); i++) {
 		if (fs1[i](id /*,off*/) == 0)
 			goto ret;
-		if (id->error)
-			goto ret;
+		//if (id->known_size < MIN_VALID_FS_SIZE)
+		//	goto ret_bad;
 	}
 
 	/* fill buffer with maximum */
@@ -213,13 +216,14 @@ int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64
 	for (i = 0; i < ARRAY_SIZE(fs2); i++) {
 		if (fs2[i](id /*,off*/) == 0)
 			goto ret;
-		if (id->error)
-			goto ret;
+		//if (id->known_size < MIN_VALID_FS_SIZE)
+		//	goto ret_bad;
 	}
-
+ //ret_bad:
+	retval = -1; /* "not found" */
  ret:
 	volume_id_free_buffer(id);
-	return (- id->error); /* 0 or -1 */
+	return retval;
 }
 
 /* open volume by device node */
@@ -229,6 +233,7 @@ struct volume_id* FAST_FUNC volume_id_open_node(int fd)
 
 	id = xzalloc(sizeof(struct volume_id));
 	id->fd = fd;
+	id->known_size = UNKNOWN_SIZE;
 	///* close fd on device close */
 	//id->fd_close = 1;
 	return id;
diff --git a/util-linux/volume_id/volume_id_internal.h b/util-linux/volume_id/volume_id_internal.h
index b1e44481f..39699ef72 100644
--- a/util-linux/volume_id/volume_id_internal.h
+++ b/util-linux/volume_id/volume_id_internal.h
@@ -60,7 +60,9 @@ struct volume_id_partition {
 struct volume_id {
 	int		fd;
 //	int		fd_close:1;
-	int		error;
+	/* UNKNOWN_SIZE: unknown, N: seek+read stopped at N prematurely */
+	unsigned	known_size;
+#define UNKNOWN_SIZE UINT_MAX
 	size_t		sbbuf_len;
 	size_t		seekbuf_len;
 	uint8_t		*sbbuf;
@@ -85,6 +87,18 @@ struct volume_id {
 //	const char	*usage;
 };
 
+// Technically, the tiniest possible linux FS image (romfs) with only "." and ".." is:
+//00000000 2D 72 6F 6D 31 66 73 2D 00 00 00 60 A1 27 1D 06 -rom1fs-...`.'..
+//00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+//00000020 00 00 00 49 00 00 00 20 00 00 00 00 D1 FF FF 97 ...I... ........
+//00000030 2E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+//00000040 00 00 00 00 00 00 00 20 00 00 00 00 D1 D1 FF E0 ....... ........
+//00000050 2E 2E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+// but kernel won't mount it unless it's padded to 1K.
+// Stop trying new FS types in volume_id_probe_all() outright
+// if a previous probe had a read which stopped before 1K:
+#define MIN_VALID_FS_SIZE 1024
+
 struct volume_id* FAST_FUNC volume_id_open_node(int fd);
 int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size);
 void FAST_FUNC free_volume_id(struct volume_id *id);


More information about the busybox-cvs mailing list