[git commit] fdisk: several fixes for 4K sector size

Denys Vlasenko vda.linux at googlemail.com
Wed Feb 4 08:46:54 UTC 2026


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

function                                             old     new   delta
bb_getsize_in_512sect                                  -     187    +187
list_disk_name_and_sizes                               -      80     +80
get_geometry                                         511     535     +24
.rodata                                           107066  107088     +22
msg_building_new_label                               143     142      -1
fdisk_main                                          4781    4775      -6
list_table                                          1423    1366     -57
list_disk_geometry                                   143      68     -75
bb_BLKGETSIZE_sectors                                191       -    -191
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 2/4 up/down: 313/-330)          Total: -17 bytes

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 libbb/xfuncs.c                     |  41 ---------
 miscutils/hdparm.c                 |  10 ++-
 util-linux/blkdiscard.c            |   1 +
 util-linux/blockdev.c              |   5 +-
 util-linux/fdisk.c                 | 180 ++++++++++++++++++++++++-------------
 util-linux/fdisk_gpt.c             |  18 ++--
 util-linux/volume_id/get_devname.c |   1 +
 7 files changed, 133 insertions(+), 123 deletions(-)

diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c
index b03af8542..b41b40a7e 100644
--- a/libbb/xfuncs.c
+++ b/libbb/xfuncs.c
@@ -176,47 +176,6 @@ char* FAST_FUNC hex2bin(char *dst, const char *str, int count)
 	return dst;
 }
 
-/* Return how long the file at fd is, if there's any way to determine it. */
-#ifdef UNUSED
-off_t FAST_FUNC fdlength(int fd)
-{
-	off_t bottom = 0, top = 0, pos;
-	long size;
-
-	// If the ioctl works for this, return it.
-
-	if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512;
-
-	// FIXME: explain why lseek(SEEK_END) is not used here!
-
-	// If not, do a binary search for the last location we can read.  (Some
-	// block devices don't do BLKGETSIZE right.)
-
-	do {
-		char temp;
-
-		pos = bottom + (top - bottom) / 2;
-
-		// If we can read from the current location, it's bigger.
-
-		if (lseek(fd, pos, SEEK_SET)>=0 && safe_read(fd, &temp, 1)==1) {
-			if (bottom == top) bottom = top = (top+1) * 2;
-			else bottom = pos;
-
-		// If we can't, it's smaller.
-		} else {
-			if (bottom == top) {
-				if (!top) return 0;
-				bottom = top/2;
-			}
-			else top = pos;
-		}
-	} while (bottom + 1 != top);
-
-	return pos + 1;
-}
-#endif
-
 int FAST_FUNC bb_putchar_stderr(char ch)
 {
 	return write(STDERR_FILENO, &ch, 1);
diff --git a/miscutils/hdparm.c b/miscutils/hdparm.c
index 83e2f8d53..8b844717f 100644
--- a/miscutils/hdparm.c
+++ b/miscutils/hdparm.c
@@ -1464,15 +1464,17 @@ static void read_big_block(/*int fd,*/ char *buf)
 static unsigned dev_size_mb(/*int fd*/ void)
 {
 	union {
-		unsigned long long blksize64;
-		unsigned blksize32;
+		/* BLKGETSIZE64 takes pointer to uint64_t, not ullong */
+		uint64_t blksize64;
+		unsigned long blksize_long;
 	} u;
 
 	if (0 == ioctl(fd, BLKGETSIZE64, &u.blksize64)) { // bytes
 		u.blksize64 /= (1024 * 1024);
 	} else {
-		xioctl(fd, BLKGETSIZE, &u.blksize32); // sectors
-		u.blksize64 = u.blksize32 / (2 * 1024);
+		/* returns size in 512 blocks (not ioctl(BLKSSZ) sized blocks!) */
+		xioctl(fd, BLKGETSIZE, &u.blksize_long);
+		u.blksize64 = u.blksize_long / (2 * 1024);
 	}
 	if (u.blksize64 > UINT_MAX)
 		return UINT_MAX;
diff --git a/util-linux/blkdiscard.c b/util-linux/blkdiscard.c
index 70fd34af3..38c563b1a 100644
--- a/util-linux/blkdiscard.c
+++ b/util-linux/blkdiscard.c
@@ -70,6 +70,7 @@ int blkdiscard_main(int argc UNUSED_PARAM, char **argv)
 	if (opts & OPT_LENGTH)
 		length = xatoull_sfx(length_str, kMG_suffixes);
 	else {
+		/* BLKGETSIZE64 takes pointer to uint64_t, not ullong */
 		xioctl(fd, BLKGETSIZE64, &length);
 		length -= offset;
 	}
diff --git a/util-linux/blockdev.c b/util-linux/blockdev.c
index d47e43e55..606f0c16c 100644
--- a/util-linux/blockdev.c
+++ b/util-linux/blockdev.c
@@ -25,8 +25,11 @@
 //usage:     "\n	--getbsz	Get block size"
 //usage:     "\n	--setbsz BYTES	Set block size"
 //usage:     "\n	--getsz		Get device size in 512-byte sectors"
-///////:     "\n	--getsize	Get device size in sectors (deprecated)"
+///////:     "\n	--getsize	Get device size in 512-byte sectors (deprecated)"
 ///////^^^^^ supported, but not shown in help ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+/////// --getsz is BLKGETSIZE64 / 512
+/////// --getsize is BLKGETSIZE which returns size in 512 blocks (not ioctl(BLKSSZ) sized blocks!)
+/////// they should be the same, modulo BLKGETSIZE not fitting its result into unsigned long (32 bit longs + >2TB disk)
 //usage:     "\n	--getsize64	Get device size in bytes"
 //usage:     "\n	--getra		Get readahead in 512-byte sectors"
 //usage:     "\n	--setra SECTORS	Set readahead"
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c
index 820992801..33a8d1896 100644
--- a/util-linux/fdisk.c
+++ b/util-linux/fdisk.c
@@ -60,6 +60,7 @@
 //config:	Enabling this option allows you to create or change SUN disklabels.
 //config:	Most people can safely leave this option disabled.
 //config:
+//TODO: retain only support for GPT and maybe OSF (BSD), the rest is dead for 20+ years.
 //config:config FEATURE_OSF_LABEL
 //config:	bool "Support BSD disklabels"
 //config:	default n
@@ -121,6 +122,7 @@
 #endif
 #include "libbb.h"
 #include "unicode.h"
+#define HDIO_GETGEO     0x0301  /* get device geometry */
 
 #if BB_LITTLE_ENDIAN
 # define inline_if_little_endian ALWAYS_INLINE
@@ -128,6 +130,14 @@
 # define inline_if_little_endian /* nothing */
 #endif
 
+/* You can test 4K sector size in QEMU a-la:
+ * qemu-img create -f qcow2 disk.qcow2 1g
+ * qemu-system-x86_64 -enable-kvm \
+ *   -device virtio-scsi-pci,id=scsi0 \
+ *   -drive file=disk.qcow2,if=none,id=drv0,format=qcow2 \
+ *   -device scsi-hd,drive=drv0,logical_block_size=4096,physical_block_size=4096
+ */
+
 #define DEFAULT_SECTOR_SIZE      512
 #define DEFAULT_SECTOR_SIZE_STR "512"
 #define MAX_SECTOR_SIZE         2048
@@ -145,7 +155,6 @@
 #define LINUX_LVM               0x8e
 #define LINUX_RAID              0xfd
 
-
 enum {
 	OPT_b = 1 << 0,
 	OPT_C = 1 << 1,
@@ -156,7 +165,6 @@ enum {
 	OPT_s = (1 << 6) * ENABLE_FEATURE_FDISK_BLKSIZE,
 };
 
-
 typedef unsigned long long ullong;
 /* Used for sector numbers. Partition formats we know
  * do not support more than 2^32 sectors
@@ -179,8 +187,6 @@ struct hd_geometry {
 	unsigned long start;
 };
 
-#define HDIO_GETGEO     0x0301  /* get device geometry */
-
 /* TODO: just #if ENABLE_FEATURE_FDISK_WRITABLE */
 /* (currently fdisk_sun/sgi.c do not have proper WRITABLE #ifs) */
 #if ENABLE_FEATURE_FDISK_WRITABLE \
@@ -189,12 +195,18 @@ struct hd_geometry {
 static const char msg_building_new_label[] ALIGN1 =
 "Building a new %s. Changes will remain in memory only,\n"
 "until you decide to write them. After that the previous content\n"
-"won't be recoverable.\n\n";
+"won't be recoverable.\n";
 
 static const char msg_part_already_defined[] ALIGN1 =
 "Partition %u is already defined, delete it before re-adding\n";
 #endif
 
+#define SUPPORT_DISKLABELS (0 \
+	| ENABLE_FEATURE_AIX_LABEL \
+	| ENABLE_FEATURE_SGI_LABEL \
+	| ENABLE_FEATURE_OSF_LABEL \
+	| ENABLE_FEATURE_GPT_LABEL \
+)
 
 struct partition {
 	unsigned char boot_ind;         /* 0x80 - active */
@@ -296,9 +308,9 @@ static int get_boot(enum action what);
 #else
 static int get_boot(void);
 #endif
-
 static sector_t get_start_sect(const struct partition *p);
 static sector_t get_nr_sects(const struct partition *p);
+static void list_disk_name_and_sizes(void);
 
 /* DOS partition types */
 
@@ -420,9 +432,9 @@ struct globals {
 	int g_partitions; // = 4;       /* maximum partition + 1 */
 	unsigned units_per_sector; // = 1;
 	unsigned sector_size; // = DEFAULT_SECTOR_SIZE;
-	unsigned user_set_sector_size;
 	unsigned sector_offset; // = 1;
 	unsigned g_heads, g_sectors, g_cylinders;
+	smallint user_set_sector_size;
 	smallint /* enum label_type */ current_label_type;
 	smallint display_in_cyl_units;
 #if ENABLE_FEATURE_OSF_LABEL
@@ -508,48 +520,73 @@ struct globals {
 	dos_compatible_flag = 1; \
 } while (0)
 
-
 /* TODO: move to libbb? */
-/* TODO: return unsigned long long, FEATURE_FDISK_BLKSIZE _can_ handle
- * disks > 2^32 sectors
- */
-static sector_t bb_BLKGETSIZE_sectors(int fd)
+static uint64_t bb_getsize_in_512sect(int fd)
 {
-	uint64_t v64;
+	const char *how;
+	uint64_t sz64;
+	/* BLKGETSIZE64 takes pointer to uint64_t, not ullong: */
+	uint64_t sz_get64;
+	/* BLKGETSIZE takes pointer to ulong: */
 	unsigned long longsectors;
 
-	if (ioctl(fd, BLKGETSIZE64, &v64) == 0) {
-		/* Got bytes, convert to 512 byte sectors */
-		v64 >>= 9;
-//FIXME: should be "v64 /= sector_size" instead?
-		if (v64 != (sector_t)v64) {
- ret_trunc:
-			/* Not only DOS, but all other partition tables
-			 * we support can't record more than 32 bit
-			 * sector counts or offsets
-			 */
-			bb_simple_error_msg("device has more than 2^32 sectors, can't use all of them");
-			v64 = (sector_t)-1L;
-		}
-		return v64;
-	}
-	/* Needs temp of type long */
-	if (ioctl(fd, BLKGETSIZE, &longsectors) != 0) {
-		/* Perhaps this is a disk image */
-		off_t sz = lseek(fd, 0, SEEK_END);
-		longsectors = 0;
-		if (sz > 0)
-			longsectors = (uoff_t)sz / sector_size;
-		lseek(fd, 0, SEEK_SET);
-	}
-	if (sizeof(long) > sizeof(sector_t)
-	 && longsectors != (sector_t)longsectors
+	if (ioctl(fd, BLKGETSIZE, &longsectors) == 0
 	) {
-		goto ret_trunc;
+		/* If in "fdisk -b 4096 /dev/512byte_sect_dev" scenario,
+		 * IOW: sector size is OVERRIDDEN by user,
+		 * the user tries to create 4K sector layout on a 512 sector
+		 * device (presumably because the device can't operate
+		 * in 4k mode on this machine, but can on another one?):
+		 * __do NOT assume BLKGETSIZE returns 4K sectors__ -
+		 * it always returns 512-sized "sectors"!
+		 */
+		sz64 = (uint64_t)longsectors * 512;
+		how = "BLKGETSIZE";
+	} else {
+		/* BLKGETSIZE failed, assume this is a disk image */
+#if ENABLE_FDISK_SUPPORT_LARGE_DISKS
+		off64_t sz = xlseek64(fd, 0, SEEK_END);
+#else
+		off_t sz = xlseek(fd, 0, SEEK_END);
+#endif
+		xlseek(fd, 0, SEEK_SET);
+		sz64 = sz;
+		how = "seek";
 	}
-	return longsectors;
+	/* We did the above in case BLKGETSIZE64 is not available.
+	 * If it is, double-check:
+	 */
+	if (ioctl(fd, BLKGETSIZE64, &sz_get64) == 0
+	 && sz_get64 != sz64
+	) {
+		/* Something is very fishy. Abort. */
+		bb_error_msg_and_die(
+			"bad sizes: BLKGETSIZE64:%llu %s:%llu",
+			(ullong)sz_get64,
+			how, (ullong)sz64
+		);
+	}
+	if ((sz64 & 0x1ff) != 0)
+		bb_error_msg("size %llu not a multiple of 512 bytes", (ullong)sz64);
+//bb_error_msg("%s: %llu", how, (ullong)sz64);
+
+	/* Got bytes, convert to 512 byte blocks */
+	return sz64 >> 9;
 }
 
+static sector_t bb_getsize_in_sectors(int fd)
+{
+	uint64_t sz64 = bb_getsize_in_512sect(fd) / (sector_size >> 9);
+	if (sz64 != (sector_t)sz64) {
+		/* Most partition tables we support can't record
+		 * more than 32 bit sector counts or offsets.
+		 * (GPT can)
+		 */
+		bb_simple_error_msg("device has more than 2^32 sectors, can't use all of them");
+		sz64 = (sector_t)-1LL;
+	}
+	return (sector_t)sz64;
+}
 
 #define IS_EXTENDED(i) \
 	((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED)
@@ -1348,7 +1385,7 @@ get_sectorsize(void)
 {
 	if (!user_set_sector_size) {
 		int arg;
-		if (ioctl(dev_fd, BLKSSZGET, &arg) == 0)
+		if (ioctl(dev_fd, BLKSSZGET, &arg) == 0 && arg >= 512)
 			sector_size = arg;
 		if (sector_size != DEFAULT_SECTOR_SIZE)
 			printf("Note: sector size is %u "
@@ -1405,10 +1442,7 @@ get_partition_table_geometry(void)
 static void
 get_geometry(void)
 {
-	int sec_fac;
-
 	get_sectorsize();
-	sec_fac = sector_size / 512;
 #if ENABLE_FEATURE_SUN_LABEL
 	sun_guess_device_type();
 #endif
@@ -1425,13 +1459,14 @@ get_geometry(void)
 	g_sectors = user_sectors ? user_sectors :
 		pt_sectors ? pt_sectors :
 		kern_sectors ? kern_sectors : 63;
-	total_number_of_sectors = bb_BLKGETSIZE_sectors(dev_fd);
+	total_number_of_sectors = bb_getsize_in_sectors(dev_fd);
 
 	sector_offset = 1;
 	if (dos_compatible_flag)
 		sector_offset = g_sectors;
 
-	g_cylinders = total_number_of_sectors / (g_heads * g_sectors * sec_fac);
+	g_cylinders = total_number_of_sectors / (g_heads * g_sectors);
+//TODO? if (total_number_of_sectors % (g_heads * g_sectors) != 0) g_cylinders++;
 	if (!g_cylinders)
 		g_cylinders = user_cylinders;
 }
@@ -1553,9 +1588,19 @@ static int get_boot(void)
 #else
 	if (!valid_part_table_flag(MBRbuffer)) {
 		if (what == OPEN_MAIN) {
-			puts("Device contains neither a valid DOS "
-			     "partition table, nor Sun, SGI, OSF or GPT "
-			     "disklabel");
+			puts("Device has no valid DOS partition table"
+#if SUPPORT_DISKLABELS
+				", nor "
+				IF_FEATURE_SUN_LABEL("Sun, ")
+				IF_FEATURE_SGI_LABEL("SGI, ")
+				IF_FEATURE_OSF_LABEL("OSF, ")
+				IF_FEATURE_AIX_LABEL("AIX, ")
+				IF_FEATURE_GPT_LABEL("GPT ")
+				"disklabel."
+#else
+				"."
+#endif
+			);
 #ifdef __sparc__
 			IF_FEATURE_SUN_LABEL(create_sunlabel();)
 #else
@@ -2045,22 +2090,30 @@ check_consistency(const struct partition *p, int partition)
 }
 
 static void
-list_disk_geometry(void)
+list_disk_name_and_sizes(void)
 {
-	ullong xbytes = total_number_of_sectors / (1024*1024 / 512);
-	char x = 'M';
+	char numstr6[6];
+	ullong total_bytes;
 
-	if (xbytes >= 10000) {
-		xbytes += 512; /* fdisk util-linux 2.28 does this */
-		xbytes /= 1024;
-		x = 'G';
-	}
-	printf("Disk %s: %llu %cB, %llu bytes, %"SECT_FMT"u sectors\n"
+	total_bytes = (ullong)total_number_of_sectors * sector_size;
+	smart_ulltoa5(total_bytes, numstr6, " KMGTPEZY")[0] = '\0';
+
+	printf("Disk %s: %s, %llu bytes, %"SECT_FMT"u sectors\n",
+		disk_device,
+		skip_whitespace(numstr6),
+		total_bytes,
+		(SECT_TYPE)total_number_of_sectors
+	);
+}
+
+static void
+list_disk_geometry(void)
+{
+	list_disk_name_and_sizes();
+	printf(
 		"%u cylinders, %u heads, %u sectors/track\n"
 		"Units: %ss of %u * %u = %u bytes\n"
 		"\n",
-		disk_device, xbytes, x,
-		((ullong)total_number_of_sectors * 512), total_number_of_sectors,
 		g_cylinders, g_heads, g_sectors,
 		str_units(),
 		units_per_sector, sector_size, units_per_sector * sector_size
@@ -3068,14 +3121,13 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv)
 	if (opt & OPT_s) {
 		int j;
 
-		sector_size = 512;
 		nowarn = 1;
 		if (!argv[0])
 			bb_show_usage();
 		for (j = 0; argv[j]; j++) {
-			unsigned long long size;
+			ullong size;
 			int fd = xopen(argv[j], O_RDONLY);
-			size = bb_BLKGETSIZE_sectors(fd) / 2;
+			size = bb_getsize_in_512sect(fd) / 2;
 //NB: util-linux 2.41.1 says: "-s,--getsz: display device size in 512-byte sectors"
 //but in fact, util-linux 2.41.1 shows the size in KILOBYTES!
 			close(fd);
diff --git a/util-linux/fdisk_gpt.c b/util-linux/fdisk_gpt.c
index ec8818ec3..251c9e202 100644
--- a/util-linux/fdisk_gpt.c
+++ b/util-linux/fdisk_gpt.c
@@ -101,16 +101,8 @@ gpt_list_table(int xtra UNUSED_PARAM)
 {
 	int i;
 	char numstr6[6];
-	unsigned long long total_bytes;
 
-	total_bytes = (unsigned long long)total_number_of_sectors * sector_size;
-	smart_ulltoa5(total_bytes, numstr6, " KMGTPEZY")[0] = '\0';
-
-	printf("Disk %s: %s, %llu bytes, %"SECT_FMT"u sectors\n", disk_device,
-		skip_whitespace(numstr6),
-		total_bytes,
-		(SECT_TYPE)total_number_of_sectors
-	);
+	list_disk_name_and_sizes();
 	printf("Logical sector size: %u\n", sector_size);
 	printf("Disk identifier (GUID): ");
 //util-linux 2.41.1 does not print " (GUID)" in above line,
@@ -119,8 +111,8 @@ gpt_list_table(int xtra UNUSED_PARAM)
 	printf("\nPartition table holds up to %u entries\n",
 		(int)SWAP_LE32(G.gpt_hdr->n_parts));
 	printf("First usable sector is %llu, last usable sector is %llu\n\n",
-		(unsigned long long)SWAP_LE64(G.gpt_hdr->first_usable_lba),
-		(unsigned long long)SWAP_LE64(G.gpt_hdr->last_usable_lba)
+		(ullong)SWAP_LE64(G.gpt_hdr->first_usable_lba),
+		(ullong)SWAP_LE64(G.gpt_hdr->last_usable_lba)
 	);
 
 /* "GPT fdisk" has a concept of 16-bit extension of the original MBR 8-bit type codes,
@@ -140,8 +132,8 @@ gpt_list_table(int xtra UNUSED_PARAM)
 				numstr6, " KMGTPEZY")[0] = '\0';
 			printf("%6u %15llu %15llu %s ",
 				i + 1,
-				(unsigned long long)SWAP_LE64(p->lba_start),
-				(unsigned long long)SWAP_LE64(p->lba_end),
+				(ullong)SWAP_LE64(p->lba_start),
+				(ullong)SWAP_LE64(p->lba_end),
 				numstr6
 			);
 			gpt_print_wide36(p->name36);
diff --git a/util-linux/volume_id/get_devname.c b/util-linux/volume_id/get_devname.c
index 00cfb2826..39155f256 100644
--- a/util-linux/volume_id/get_devname.c
+++ b/util-linux/volume_id/get_devname.c
@@ -43,6 +43,7 @@ static int
 get_label_uuid(int fd, char **label, char **uuid, const char **type)
 {
 	int rv = 1;
+	/* BLKGETSIZE64 takes pointer to uint64_t, not ullong */
 	uint64_t size;
 	struct volume_id *vid;
 


More information about the busybox-cvs mailing list