[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