From vda.linux at googlemail.com Sun Sep 13 23:46:32 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 14 Sep 2026 01:46:32 +0200 Subject: [git commit] awk: allow OFMT='prefix:%SPEC suffix with %%' Message-ID: <20260913234707.7427A95C83@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=1b4f8e2a8759c5f2bb1496aa362b50d90f3dfeb3 branch: https://git.busybox.net/busybox/log/?h=master function old new delta fmt_num 380 394 +14 Signed-off-by: Denys Vlasenko --- editors/awk.c | 13 ++++++++++--- testsuite/awk.tests | 9 ++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index e58b5e923..731364975 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -982,6 +982,7 @@ static const char *fmt_num(const char *format, double n) snprintf(g_buf, MAXVARFMT, "%.0f", n); } else { const char *s = format; + const char *p2; char c; /* Find %SPEC in format string */ @@ -993,9 +994,15 @@ static const char *fmt_num(const char *format, double n) break; s += 2; /* skip "%%" */ } - if (strchr(s + 1, '%') != NULL) -//TODO: allow "%%"s in the suffix too? - goto INV_FMT; /* more than one %SPEC */ + /* We are at %SPEC. Is there a second %SPEC? (%% and trailing % aren't %SPEC) */ + p2 = s; + for (;;) { + p2 = strchr(p2 + 1, '%'); + if (!p2) break; /* no more %SPEC: ok */ + if (*++p2 == '\0') break; /* trailing lone %: ok */ + if (*p2 != '%') goto INV_FMT; /* more than one %SPEC: error */ + /* else: %% - ok, look for %SPEC past it by iterating once more */ + } //TODO: factor out awk_printf machinery to make %d print large numbers too. Example: // awk -v OFMT='R:%d!' 'BEGIN { print 184467440737095.1 }' diff --git a/testsuite/awk.tests b/testsuite/awk.tests index 671484131..43f352441 100755 --- a/testsuite/awk.tests +++ b/testsuite/awk.tests @@ -88,11 +88,18 @@ testing "awk %o -1.9" \ # OFMT can contain literals before and after %SPEC: testing "awk OFMT='R%%:%f!' -1.9" \ "awk -v OFMT='R%%:%f!' 'BEGIN { print -1.9; }'" "R%:-1.900000!\n" "" "" +testing "awk OFMT='R%%:%f!%%!!' -1.9" \ +"awk -v OFMT='R%%:%f!%%!!' 'BEGIN { print -1.9; }'" "R%:-1.900000!%!!\n" "" "" +# In our code, whether trailing lone % prints "%" or "" depends on what libc's sprintf does. +# GNU Awk 5.3.0 prints "%", I do not know whether it is libc-dependent in their code. +# Let's not test this: +#testing "awk OFMT='R%%:%f!%' -1.9" \ +#"awk -v OFMT='R%%:%f!%' 'BEGIN { print -1.9; }'" "R%:-1.900000!%\n" "" "" # OFMT is ignored if the value has no fractionals (even if it does not fit into uint64) testing "awk OFMT='R%%:%f!' 2**128" \ "awk -v OFMT='R%%:%f!' 'BEGIN { print 2**128; }'" "340282366920938463463374607431768211456\n" "" "" -testing "awk OFMT='R%%:%f!' 2**128" \ +testing "awk OFMT='R%%:%f!' -2**129" \ "awk -v OFMT='R%%:%f!' 'BEGIN { print -2**129; }'" "-680564733841876926926749214863536422912\n" "" "" # OFMT can contain no %SPEC (be a literal): From vda.linux at googlemail.com Mon Sep 21 01:20:39 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 03:20:39 +0200 Subject: [git commit] tls: fix undersized buffer calculation Message-ID: <20260921012051.42CE08DFC8@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=89ac82774f90e270d04ed5299e5cf05dfbc314c1 branch: https://git.busybox.net/busybox/log/?h=master All callers of pstm_montgomery_reduce() are currently in pstm_exptmod() and they all pass a preallocated temporary buffer, so the incorrect code is unreachable. But fix it anyway. function old new delta pstm_montgomery_reduce 431 430 -1 Signed-off-by: Denys Vlasenko --- networking/tls_pstm_montgomery_reduce.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/networking/tls_pstm_montgomery_reduce.c b/networking/tls_pstm_montgomery_reduce.c index e63e590db..36882425a 100644 --- a/networking/tls_pstm_montgomery_reduce.c +++ b/networking/tls_pstm_montgomery_reduce.c @@ -370,6 +370,7 @@ int32 FAST_FUNC pstm_montgomery_reduce(psPool_t *pool, pstm_int *a, pstm_int *m, pstm_digit *c, *_c, *tmpm, mu; int32 oldused, x, y; int pa; //bbox: was int16 + uint32 cSize; pa = m->used; if (pa > a->alloc) { @@ -377,11 +378,13 @@ int32 FAST_FUNC pstm_montgomery_reduce(psPool_t *pool, pstm_int *a, pstm_int *m, return PS_LIMIT_FAIL; } - if (paD && paDlen >= (uint32)2*pa+1) { + cSize = (2 * pa + 1) * sizeof(pstm_digit); + if (paD && paDlen >= cSize) { c = paD; memset(c, 0x0, paDlen); } else { - c = xzalloc(2*pa+1);//bbox + //bbox: dead code, all callers always pass paD: + c = xzalloc(cSize); //bbox } /* copy the input */ oldused = a->used; @@ -441,7 +444,8 @@ int32 FAST_FUNC pstm_montgomery_reduce(psPool_t *pool, pstm_int *a, pstm_int *m, x = PS_MEM_FAIL; } } - if (paDlen < (uint32)2*pa+1) { + //bbox: dead code, all callers always pass paD: + if (c != paD) { psFree(c, pool); } return x; From vda.linux at googlemail.com Mon Sep 21 03:20:06 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 05:20:06 +0200 Subject: [git commit] httpd: remove one unnecessary check Message-ID: <20260921031920.C1F0E8E128@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=d0143f8da1ea40a2982785adc587c1473e378e79 branch: https://git.busybox.net/busybox/log/?h=master function old new delta scan_ip 170 165 -5 Signed-off-by: Denys Vlasenko --- networking/httpd.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index bc446a838..a5dfb38af 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -653,14 +653,14 @@ static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr) #if ENABLE_FEATURE_HTTPD_ACL_IP /* Returns presumed mask width in bits or < 0 on error. * Updates strp, stores IP at provided pointer */ -static int scan_ip(const char **strp, uint32_t *ipp, unsigned char endc) +static int scan_ip(const char **strp, uint32_t *ipp, char endc) { const char *p = *strp; int auto_mask = 8; uint32_t ip = 0; int j; - if (*p == '/') + if (*p == '/') /* Disallow "D:/24" (empty IP??) */ return -auto_mask; for (j = 0; j < 4; j++) { @@ -678,8 +678,11 @@ static int scan_ip(const char **strp, uint32_t *ipp, unsigned char endc) } if (*p == '.') p++; - if (*p != '/' && *p) + if (*p + // && *p != '/' -- no need to check, for "IP/MASK" scan_ip_mask() ignores auto_mask value, incorrect +=8 does not matter in this case + ) { auto_mask += 8; + } ip = (ip << 8) | octet; } if (*p) { From vda.linux at googlemail.com Mon Sep 21 02:40:37 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 04:40:37 +0200 Subject: [git commit] httpd: fix handling of "D:1.2.3/999" Message-ID: <20260921031920.BA2098DD0D@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=339e3c62f5bcf252839c8abca5325f48a22f21aa branch: https://git.busybox.net/busybox/log/?h=master An error in allow/deny rule should be interpreted as D:0/0 ("deny all") but was interpreted as D:IP/0. Fix this. function old new delta parse_conf 1323 1330 +7 Signed-off-by: Denys Vlasenko --- networking/httpd.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index ad59493d7..bc446a838 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -41,7 +41,7 @@ * H:/serverroot # define the server root. It will override -h * A:172.20. # Allow address from 172.20.0.0/16 * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127 - * A:10.0.0.0/255.255.255.128 # Allow any address that previous set + * A:10.0.0.0/255.255.255.128 # Same as the previous * A:127.0.0.1 # Allow local loopback connections * D:* # Deny from other IP connections * E404:/path/e404.html # /path/e404.html is the 404 (not found) error page @@ -63,7 +63,7 @@ * Deny/Allow IP logic: * - Default is to allow all (Allow all (A:*) is a no-op). * - Deny rules take precedence over allow rules. - * - "Deny all" rule (D:*) is applied last. + * - However, "Deny all" rule (D:*) is applied last. * * Example: * 1. Allow only specified addresses @@ -381,8 +381,8 @@ typedef struct Htaccess { /* Must have "next" as a first member */ typedef struct Htaccess_IP { struct Htaccess_IP *next; - unsigned ip; - unsigned mask; + uint32_t ip; /* host-endian */ + uint32_t mask; /* host-endian */ int allow_deny; } Htaccess_IP; #endif @@ -653,11 +653,11 @@ static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr) #if ENABLE_FEATURE_HTTPD_ACL_IP /* Returns presumed mask width in bits or < 0 on error. * Updates strp, stores IP at provided pointer */ -static int scan_ip(const char **strp, unsigned *ipp, unsigned char endc) +static int scan_ip(const char **strp, uint32_t *ipp, unsigned char endc) { const char *p = *strp; int auto_mask = 8; - unsigned ip = 0; + uint32_t ip = 0; int j; if (*p == '/') @@ -695,10 +695,10 @@ static int scan_ip(const char **strp, unsigned *ipp, unsigned char endc) } /* Returns 0 on success. Stores IP and mask at provided pointers */ -static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp) +static int scan_ip_mask(const char *str, uint32_t *ipp, uint32_t *maskp) { int i; - unsigned mask; + uint32_t mask; char *p; i = scan_ip(&str, ipp, '/'); @@ -713,15 +713,16 @@ static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp) /* (return 0 (success) only if it has N.N.N.N form) */ return scan_ip(&str, maskp, '\0') - 32; } - if (*p) + if (*p) /* 'xxx' had something apart from just digits */ return -1; } + //else: i = "automask" (the count of explicit IP components: "10.0[.]" = 16) if (i > 32) return -1; - if (sizeof(unsigned) == 4 && i == 32) { - /* mask >>= 32 below may not work */ + if (i == 32) { + /* mask >>= 32 below may not work (according to C standard) */ mask = 0; } else { mask = 0xffffffff; @@ -890,15 +891,18 @@ static int parse_conf(const char *path, int flag) if (scan_ip_mask(after_colon, &pip->ip, &pip->mask)) { /* IP{/mask} syntax error detected, protect all */ ch = 'D'; + //bb_error_msg("ERR"); + pip->ip = 0; /* could be set before error is detected - zero it (again) */ pip->mask = 0; } + //bb_error_msg_and_die("ip:0x%08x mask:0x%08x", pip->ip, pip->mask); pip->allow_deny = ch; if (ch == 'D') { /* Deny:from_IP - prepend */ pip->next = G.ip_a_d; G.ip_a_d = pip; } else { - /* A:from_IP - append (thus all D's precedes A's) */ + /* A:from_IP - append (thus all D's precede A's) */ Htaccess_IP *prev_IP = G.ip_a_d; if (prev_IP == NULL) { G.ip_a_d = pip; @@ -2045,7 +2049,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what) } #if ENABLE_FEATURE_HTTPD_ACL_IP -static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip) +static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(uint32_t remote_ip) { Htaccess_IP *cur; @@ -2337,7 +2341,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) char *urlp; char *tptr; #if ENABLE_FEATURE_HTTPD_ACL_IP - unsigned remote_ip; + uint32_t remote_ip; #endif #if ENABLE_FEATURE_HTTPD_CGI unsigned total_headers_len; From vda.linux at googlemail.com Mon Sep 21 03:34:19 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 05:34:19 +0200 Subject: [git commit] volume_id/romfs: limit the maximum size of label to VOLUME_ID_LABEL_SIZE Message-ID: <20260921033402.B92A18DF42@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=9073c1d15c9953c6f1e39145815fc5dccfa6081c branch: https://git.busybox.net/busybox/log/?h=master function old new delta volume_id_set_label_string 71 81 +10 volume_id_probe_romfs 94 97 +3 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 13/0) Total: 13 bytes Signed-off-by: Denys Vlasenko --- util-linux/volume_id/romfs.c | 4 ++-- util-linux/volume_id/util.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/util-linux/volume_id/romfs.c b/util-linux/volume_id/romfs.c index bd74fda8d..475144c43 100644 --- a/util-linux/volume_id/romfs.c +++ b/util-linux/volume_id/romfs.c @@ -44,8 +44,8 @@ int FAST_FUNC volume_id_probe_romfs(struct volume_id *id /*,uint64_t off*/) if (rfs == NULL) return -1; - if (memcmp(rfs->magic, "-rom1fs-", 4) == 0) { - size_t len = strlen((char *)rfs->name); + if (memcmp(rfs->magic, "-rom1fs-", 8) == 0) { + size_t len = strnlen((char *)rfs->name, VOLUME_ID_LABEL_SIZE); if (len) { // volume_id_set_label_raw(id, rfs->name, len); diff --git a/util-linux/volume_id/util.c b/util-linux/volume_id/util.c index b59aa99b2..a2bee28ab 100644 --- a/util-linux/volume_id/util.c +++ b/util-linux/volume_id/util.c @@ -116,6 +116,10 @@ void volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t { unsigned i; + /* Do not overflow label[VOLUME_ID_LABEL_SIZE+1] */ + if (count > VOLUME_ID_LABEL_SIZE) + count = VOLUME_ID_LABEL_SIZE; + memcpy(id->label, buf, count); /* remove trailing whitespace */ From vda.linux at googlemail.com Mon Sep 21 10:15:19 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 12:15:19 +0200 Subject: [git commit] udhcpc: ensure packet.data.cookie exists before we access it Message-ID: <20260921213412.DD7139543A@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=fd1972099f28636b5deea200022b528f3a0bd523 branch: https://git.busybox.net/busybox/log/?h=master function old new delta d4_recv_raw_packet 497 500 +3 Signed-off-by: Denys Vlasenko --- networking/udhcp/dhcpc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index e44086c2e..936687e13 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -900,7 +900,12 @@ static NOINLINE int d4_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd) break; } - if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp))) { + if (bytes < (int) ( + sizeof(packet.ip) + sizeof(packet.udp) + + offsetof(struct dhcp_packet, options) + /* DHCP requires option 255 is always present, IOW: at least one byte in options[]: */ + + 1 + )) { log1s("packet is too short, ignoring"); return -2; } @@ -954,7 +959,7 @@ static NOINLINE int d4_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd) packet.ip.tot_len = packet.udp.len; /* yes, this is needed */ check = packet.udp.check; packet.udp.check = 0; - if (check && check != inet_cksum(&packet, bytes)) { + if (check != 0 && check != inet_cksum(&packet, bytes)) { log1s("packet with bad UDP checksum, ignoring"); return -2; } From vda.linux at googlemail.com Mon Sep 21 15:16:17 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 17:16:17 +0200 Subject: [git commit] dpkg: avoid strlen in fill_package_struct() Message-ID: <20260921213413.23C96954F0@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=21f9c36676634787d60a32dc09a1f1520f93124b branch: https://git.busybox.net/busybox/log/?h=master function old new delta fill_package_struct 306 291 -15 Signed-off-by: Denys Vlasenko --- archival/dpkg.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/archival/dpkg.c b/archival/dpkg.c index c97823933..b41b83a31 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -612,11 +612,10 @@ static unsigned fill_package_struct(char *control_buffer) common_node_t *new_node = xzalloc(sizeof(common_node_t)); int field_start = 0; - int buffer_length = strlen(control_buffer); int num; new_node->version = search_name_hashtable("unknown"); - while (field_start < buffer_length) { + while (control_buffer[field_start]) { char *field_name; char *field_value; unsigned field_num; From vda.linux at googlemail.com Mon Sep 21 14:16:47 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 16:16:47 +0200 Subject: [git commit] dpkg: free results of read_package_field(), preliminary cleanups in read_package_field() Message-ID: <20260921213412.E8AD1954F0@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=e2c5cc04206008acc82d28dac4fcb1963e8f0acf branch: https://git.busybox.net/busybox/log/?h=master function old new delta dpkg_main 3910 3943 +33 write_buffer_no_status 92 114 +22 read_package_field 204 184 -20 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 55/-20) Total: 35 bytes Signed-off-by: Denys Vlasenko --- archival/dpkg.c | 80 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/archival/dpkg.c b/archival/dpkg.c index eda5ec7eb..4ca1daf9b 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -531,26 +531,31 @@ static void free_package(common_node_t *node) } /* - * Gets the next package field from package_buffer, separated into the field name - * and field value, it returns the int offset to the first character of the next field + * Gets the next package field from package_buffer: + * "Name:VALUE{\n|NUL}" + * "Name:\n + * " VALUE{\n|NUL}" + * separated into "NAME" and "VALUE", both strdup()ed. + * Returns the int offset to the first character of the next field. + * The package_buffer parameter is NUL-terminated. */ static int read_package_field(const char *package_buffer, char **field_name, char **field_value) { int offset_name_start = 0; int offset_name_end = 0; int offset_value_start = 0; - int offset_value_end = 0; int offset = 0; int next_offset; int name_length; int value_length; int exit_flag = FALSE; + *field_name = NULL; + *field_value = NULL; if (package_buffer == NULL) { - *field_name = NULL; - *field_value = NULL; return -1; } + while (1) { next_offset = offset + 1; switch (package_buffer[offset]) { @@ -566,14 +571,14 @@ static int read_package_field(const char *package_buffer, char **field_name, cha * immediately after name */ break; case '\n': - /* TODO: The char next_offset may be out of bounds */ if (package_buffer[next_offset] != ' ') { exit_flag = TRUE; break; } + /* fall through */ case '\t': case ' ': - /* increment the value start point if its a just filler */ + /* increment start points if it is just a filler */ if (offset_name_start == offset) { offset_name_start++; } @@ -584,9 +589,8 @@ static int read_package_field(const char *package_buffer, char **field_name, cha } if (exit_flag) { /* Check that the names are valid */ - offset_value_end = offset; name_length = offset_name_end - offset_name_start; - value_length = offset_value_end - offset_value_start; + value_length = offset - offset_value_start; if (name_length == 0) { break; } @@ -594,27 +598,26 @@ static int read_package_field(const char *package_buffer, char **field_name, cha break; } - /* If not valid, start fresh with next field */ + /* Not valid: start fresh with next field */ exit_flag = FALSE; offset_name_start = offset + 1; offset_name_end = 0; offset_value_start = offset + 1; - offset_value_end = offset + 1; offset++; } offset++; - } - *field_name = NULL; - if (name_length) { + } /* while (1) */ + + if (name_length > 0) { *field_name = xstrndup(&package_buffer[offset_name_start], name_length); } - *field_value = NULL; if (value_length > 0) { *field_value = xstrndup(&package_buffer[offset_value_start], value_length); } return next_offset; } +/* The parameter is NUL-terminated */ static unsigned fill_package_struct(char *control_buffer) { static const char field_names[] ALIGN1 = @@ -623,14 +626,14 @@ static unsigned fill_package_struct(char *control_buffer) "Conflicts\0""Suggests\0""Recommends\0""Enhances\0"; common_node_t *new_node = xzalloc(sizeof(common_node_t)); - char *field_name; - char *field_value; int field_start = 0; - int num = -1; int buffer_length = strlen(control_buffer); + int num; new_node->version = search_name_hashtable("unknown"); while (field_start < buffer_length) { + char *field_name; + char *field_value; unsigned field_num; field_start += read_package_field(&control_buffer[field_start], @@ -799,7 +802,7 @@ static void write_buffer_no_status(FILE *new_status_file, const char *control_bu char *name; char *value; int start = 0; - while (1) { + while (control_buffer[start]) { start += read_package_field(&control_buffer[start], &name, &value); if (name == NULL) { break; @@ -807,6 +810,8 @@ static void write_buffer_no_status(FILE *new_status_file, const char *control_bu if (strcmp(name, "Status") != 0) { fprintf(new_status_file, "%s: %s\n", name, value); } + free(name); + free(value); } } @@ -815,34 +820,35 @@ static void write_status_file(deb_file_t **deb_file) { FILE *old_status_file = xfopen_for_read("/var/lib/dpkg/status"); FILE *new_status_file = xfopen_for_write("/var/lib/dpkg/status.udeb"); - char *package_name; - char *status_from_file; - char *control_buffer = NULL; - char *tmp_string; - int status_num; + char *control_buffer; int field_start = 0; - int write_flag; - int i = 0; + int status_num; + int i; /* Update previously known packages */ while ((control_buffer = xmalloc_fgetline_str(old_status_file, "\n\n")) != NULL) { + char *package_name; + char *status_from_file; + char *tmp_string; + int write_flag; +//FIXME: "int field_start = 0;" should be _here_, right? + tmp_string = strstr(control_buffer, "Package:"); if (tmp_string == NULL) { + free(control_buffer); continue; } - tmp_string += 8; tmp_string += strspn(tmp_string, " \n\t"); package_name = xstrndup(tmp_string, strcspn(tmp_string, "\n")); + write_flag = FALSE; + status_from_file = NULL; tmp_string = strstr(control_buffer, "Status:"); if (tmp_string != NULL) { - /* Separate the status value from the control buffer */ tmp_string += 7; tmp_string += strspn(tmp_string, " \n\t"); status_from_file = xstrndup(tmp_string, strcspn(tmp_string, "\n")); - } else { - status_from_file = NULL; } /* Find this package in the status hashtable */ @@ -870,7 +876,7 @@ static void write_status_file(deb_file_t **deb_file) } i++; } - /* This is temperary, debugging only */ + /* This is temporary, debugging only */ if (deb_file[i] == NULL) { bb_error_msg_and_die("ALERT: cannot find a control file, " "your status file may be broken, status may be " @@ -882,7 +888,7 @@ static void write_status_file(deb_file_t **deb_file) fprintf(new_status_file, "Package: %s\n", package_name); fprintf(new_status_file, "Status: %s\n", status_from_hashtable); - while (1) { + while (control_buffer[field_start]) { char *field_name; char *field_value; field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value); @@ -894,13 +900,15 @@ static void write_status_file(deb_file_t **deb_file) ) { fprintf(new_status_file, "%s: %s\n", field_name, field_value); } + free(field_name); + free(field_value); } write_flag = TRUE; fputs("\n", new_status_file); } else if (strcmp("config-files", name_hashtable[state_status]) == 0) { /* only change the status line */ - while (1) { + while (control_buffer[field_start]) { char *field_name; char *field_value; field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value); @@ -913,13 +921,15 @@ static void write_status_file(deb_file_t **deb_file) } else { fprintf(new_status_file, "%s: %s\n", field_name, field_value); } + free(field_name); + free(field_value); } write_flag = TRUE; fputs("\n", new_status_file); } } } - /* If the package from the status file wasn't handle above, do it now*/ + /* If the package from the status file wasn't handled above, do it now */ if (!write_flag) { fprintf(new_status_file, "%s\n\n", control_buffer); } @@ -927,7 +937,7 @@ static void write_status_file(deb_file_t **deb_file) free(status_from_file); free(package_name); free(control_buffer); - } + } /* while (control_buffer) */ /* Write any new packages */ for (i = 0; deb_file[i] != NULL; i++) { From vda.linux at googlemail.com Mon Sep 21 20:30:23 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 22:30:23 +0200 Subject: [git commit] dpkg: improve --help Message-ID: <20260921213413.2E94095513@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=bef7bcf4ce785d148a12810c2bbc4d64666e3a7c branch: https://git.busybox.net/busybox/log/?h=master function old new delta packed_usage 36243 36247 +4 Signed-off-by: Denys Vlasenko --- archival/dpkg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/archival/dpkg.c b/archival/dpkg.c index b41b83a31..970026620 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -41,12 +41,11 @@ //kbuild:lib-$(CONFIG_DPKG) += dpkg.o //usage:#define dpkg_trivial_usage -//usage: "[-ilCPru] [-F OPT] PACKAGE" +//usage: "[-iCPru] [-F OPT] PACKAGE | -l [PATTERN]" //usage:#define dpkg_full_usage "\n\n" //usage: "Install, remove and manage Debian packages\n" //usage: IF_LONG_OPTS( //usage: "\n -i,--install Install the package" -//usage: "\n -l,--list List of installed packages" //usage: "\n --configure Configure an unpackaged package" //usage: "\n -P,--purge Purge all files of a package" //usage: "\n -r,--remove Remove all but the configuration files for a package" @@ -54,10 +53,10 @@ //usage: "\n --force-depends Ignore dependency problems" //usage: "\n --force-confnew Overwrite existing config files when installing" //usage: "\n --force-confold Keep old config files when installing" +//usage: "\n -l,--list List installed packages" //usage: ) //usage: IF_NOT_LONG_OPTS( //usage: "\n -i Install the package" -//usage: "\n -l List of installed packages" //usage: "\n -C Configure an unpackaged package" //usage: "\n -P Purge all files of a package" //usage: "\n -r Remove all but the configuration files for a package" @@ -65,6 +64,7 @@ //usage: "\n -F depends Ignore dependency problems" //usage: "\n -F confnew Overwrite existing config files when installing" //usage: "\n -F confold Keep old config files when installing" +//usage: "\n -l List installed packages" //usage: ) #include "libbb.h" From vda.linux at googlemail.com Mon Sep 21 15:13:27 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 17:13:27 +0200 Subject: [git commit] dpkg: deindent read_package_field(), rename variables. No logic changes Message-ID: <20260921213413.17B269543A@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=7c611860df44c2e214579548795cf0c073936d2d branch: https://git.busybox.net/busybox/log/?h=master Signed-off-by: Denys Vlasenko --- archival/dpkg.c | 100 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/archival/dpkg.c b/archival/dpkg.c index b8d8eb648..c97823933 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -531,72 +531,72 @@ static void free_package(common_node_t *node) } /* - * Gets the next package field from package_buffer: + * Gets the next package field from pkg_buf: * "Name:VALUE{\n|NUL}" * "Name:\n * " VALUE{\n|NUL}" * separated into "NAME" and "VALUE", both strdup()ed. * Returns the int offset to the first character of the next field. - * The package_buffer parameter is NUL-terminated. + * The pkg_buf parameter is NUL-terminated. */ -static int read_package_field(const char *package_buffer, char **field_name, char **field_value) +static int read_package_field(const char *pkg_buf, char **pname, char **pvalue) { - int offset_name_start; - int offset_name_end; - int offset_value_start = offset_value_start; + int name_start; + int name_end; + int value_start = value_start; int offset; - *field_name = NULL; - *field_value = NULL; + *pname = NULL; + *pvalue = NULL; offset = 0; - offset_name_start = 0; - offset_name_end = 0; /* "we did not see ':' yet" */ + name_start = 0; + name_end = 0; /* "we did not see ':' yet" */ while (1) { - char ch = package_buffer[offset]; + char ch = pkg_buf[offset]; switch (ch) { - case ':': - if (offset_name_end == 0) { - offset_name_end = offset + 1; /* points AFTER ':' - think of empty NAME case */ - offset_value_start = offset + 1; - } - /* TODO: NAME might still have trailing spaces if ':' isn't - * immediately after name */ - break; - case '\n': - if (package_buffer[offset + 1] != ' ') - goto end_of_value; - /* fall through */ - case '\t': - case ' ': - /* increment start points if it is just a filler */ - if (offset_name_start == offset) - offset_name_start++; - if (offset_value_start == offset) - offset_value_start++; - break; - case '\0': + case ':': + if (name_end == 0) { + name_end = offset + 1; /* points AFTER ':' - think of empty NAME case */ + value_start = offset + 1; + } + /* TODO: NAME might still have trailing spaces if ':' isn't + * immediately after name */ + break; + case '\n': + if (pkg_buf[offset + 1] != ' ') + goto end_of_value; + /* fall through */ + case '\t': + case ' ': + /* increment start points if it is just a filler */ + if (name_start == offset) + name_start++; + if (value_start == offset) + value_start++; + break; + case '\0': end_of_value: - /* Did we see the ':'? */ - if (offset_name_end != 0) { - /* Yes. Check that NAME and VALUE exist and not empty */ - int name_length = (offset_name_end - 1) - offset_name_start; - int value_length = offset - offset_value_start; - if ((name_length > 0) && (value_length > 0)) { - *field_name = xstrndup(&package_buffer[offset_name_start], name_length); - *field_value = xstrndup(&package_buffer[offset_value_start], value_length); - if (ch) - offset++; /* skip '\n' */ - return offset; - } + /* Did we see the ':'? */ + if (name_end != 0) { + /* Yes. Check that NAME and VALUE exist and not empty */ + int nlen = (name_end - 1) - name_start; + int vlen = offset - value_start; + if ((nlen > 0) && (vlen > 0)) { + *pname = xstrndup(&pkg_buf[name_start], nlen); + *pvalue = xstrndup(&pkg_buf[value_start], vlen); + if (ch) + offset++; /* skip '\n' */ + return offset; } - if (!ch) - return offset; /* stop at NUL */ + } + if (!ch) + return offset; /* stop at NUL */ - /* Not valid: start fresh with next field */ - offset_name_start = offset + 1; - offset_name_end = 0; /* "we did not see ':' yet" */ - break; + /* Not valid: skip '\n', start fresh with next field */ + name_start = offset + 1; + name_end = 0; /* "we did not see ':' yet" */ + break; } offset++; } /* while (1) */ From vda.linux at googlemail.com Mon Sep 21 14:32:43 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 16:32:43 +0200 Subject: [git commit] dpkg: reformat code in read_package_field(), exposing why it's buggy Message-ID: <20260921213413.02044954FE@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=43a9d7d31560ad4e9d6a341207b1aa191cb9b0db branch: https://git.busybox.net/busybox/log/?h=master function old new delta read_package_field 184 169 -15 Signed-off-by: Denys Vlasenko --- archival/dpkg.c | 56 ++++++++++++++++++++++---------------------------------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/archival/dpkg.c b/archival/dpkg.c index 4ca1daf9b..860256f0e 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -548,20 +548,13 @@ static int read_package_field(const char *package_buffer, char **field_name, cha int next_offset; int name_length; int value_length; - int exit_flag = FALSE; *field_name = NULL; *field_value = NULL; - if (package_buffer == NULL) { - return -1; - } while (1) { next_offset = offset + 1; switch (package_buffer[offset]) { - case '\0': - exit_flag = TRUE; - break; case ':': if (offset_name_end == 0) { offset_name_end = offset; @@ -572,8 +565,7 @@ static int read_package_field(const char *package_buffer, char **field_name, cha break; case '\n': if (package_buffer[next_offset] != ' ') { - exit_flag = TRUE; - break; + goto end_of_value; } /* fall through */ case '\t': @@ -586,35 +578,31 @@ static int read_package_field(const char *package_buffer, char **field_name, cha offset_value_start++; } break; - } - if (exit_flag) { - /* Check that the names are valid */ - name_length = offset_name_end - offset_name_start; - value_length = offset - offset_value_start; - if (name_length == 0) { - break; - } - if ((name_length > 0) && (value_length > 0)) { + case '\0': + end_of_value: + /* Check that the names are valid */ + name_length = offset_name_end - offset_name_start; + value_length = offset - offset_value_start; + if ((name_length > 0) && (value_length > 0)) { + if (name_length > 0) { + *field_name = xstrndup(&package_buffer[offset_name_start], name_length); + } + if (value_length > 0) { + *field_value = xstrndup(&package_buffer[offset_value_start], value_length); + } + return next_offset; + } + if (!package_buffer[offset]) + return next_offset; //BUG! past NUL + /* Not valid: start fresh with next field */ + offset_name_start = offset + 1; + offset_name_end = 0; + offset_value_start = offset + 1; + offset++; //BUG? break; - } - - /* Not valid: start fresh with next field */ - exit_flag = FALSE; - offset_name_start = offset + 1; - offset_name_end = 0; - offset_value_start = offset + 1; - offset++; } offset++; } /* while (1) */ - - if (name_length > 0) { - *field_name = xstrndup(&package_buffer[offset_name_start], name_length); - } - if (value_length > 0) { - *field_value = xstrndup(&package_buffer[offset_value_start], value_length); - } - return next_offset; } /* The parameter is NUL-terminated */ From vda.linux at googlemail.com Mon Sep 21 14:59:56 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 16:59:56 +0200 Subject: [git commit] dpkg: fix cases where read_package_field() returns offset past NUL Message-ID: <20260921213413.0CE7995503@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=f5a4a02a157e250d28976e2ba4ea61a2fa7eb51f branch: https://git.busybox.net/busybox/log/?h=master function old new delta read_package_field 169 211 +42 Signed-off-by: Denys Vlasenko --- archival/dpkg.c | 63 +++++++++++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/archival/dpkg.c b/archival/dpkg.c index 860256f0e..b8d8eb648 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -532,8 +532,8 @@ static void free_package(common_node_t *node) /* * Gets the next package field from package_buffer: - * "Name:VALUE{\n|NUL}" - * "Name:\n + * "Name:VALUE{\n|NUL}" + * "Name:\n * " VALUE{\n|NUL}" * separated into "NAME" and "VALUE", both strdup()ed. * Returns the int offset to the first character of the next field. @@ -541,64 +541,61 @@ static void free_package(common_node_t *node) */ static int read_package_field(const char *package_buffer, char **field_name, char **field_value) { - int offset_name_start = 0; - int offset_name_end = 0; - int offset_value_start = 0; - int offset = 0; - int next_offset; - int name_length; - int value_length; + int offset_name_start; + int offset_name_end; + int offset_value_start = offset_value_start; + int offset; *field_name = NULL; *field_value = NULL; + offset = 0; + offset_name_start = 0; + offset_name_end = 0; /* "we did not see ':' yet" */ while (1) { - next_offset = offset + 1; - switch (package_buffer[offset]) { + char ch = package_buffer[offset]; + switch (ch) { case ':': if (offset_name_end == 0) { - offset_name_end = offset; - offset_value_start = next_offset; + offset_name_end = offset + 1; /* points AFTER ':' - think of empty NAME case */ + offset_value_start = offset + 1; } - /* TODO: Name might still have trailing spaces if ':' isn't + /* TODO: NAME might still have trailing spaces if ':' isn't * immediately after name */ break; case '\n': - if (package_buffer[next_offset] != ' ') { + if (package_buffer[offset + 1] != ' ') goto end_of_value; - } /* fall through */ case '\t': case ' ': /* increment start points if it is just a filler */ - if (offset_name_start == offset) { + if (offset_name_start == offset) offset_name_start++; - } - if (offset_value_start == offset) { + if (offset_value_start == offset) offset_value_start++; - } break; case '\0': end_of_value: - /* Check that the names are valid */ - name_length = offset_name_end - offset_name_start; - value_length = offset - offset_value_start; - if ((name_length > 0) && (value_length > 0)) { - if (name_length > 0) { + /* Did we see the ':'? */ + if (offset_name_end != 0) { + /* Yes. Check that NAME and VALUE exist and not empty */ + int name_length = (offset_name_end - 1) - offset_name_start; + int value_length = offset - offset_value_start; + if ((name_length > 0) && (value_length > 0)) { *field_name = xstrndup(&package_buffer[offset_name_start], name_length); - } - if (value_length > 0) { *field_value = xstrndup(&package_buffer[offset_value_start], value_length); + if (ch) + offset++; /* skip '\n' */ + return offset; } - return next_offset; } - if (!package_buffer[offset]) - return next_offset; //BUG! past NUL + if (!ch) + return offset; /* stop at NUL */ + /* Not valid: start fresh with next field */ offset_name_start = offset + 1; - offset_name_end = 0; - offset_value_start = offset + 1; - offset++; //BUG? + offset_name_end = 0; /* "we did not see ':' yet" */ break; } offset++; From vda.linux at googlemail.com Mon Sep 21 20:33:15 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 22:33:15 +0200 Subject: [git commit] dpkg: code shrink by improving make_hash() API Message-ID: <20260921213413.39F1C9543A@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=89a2512144cf1e830919a4c476c99b966e9900b9 branch: https://git.busybox.net/busybox/log/?h=master function old new delta search_status_hashtable 109 113 +4 search_name_hashtable 104 103 -1 make_hash 116 113 -3 search_package_hashtable 120 99 -21 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/3 up/down: 4/-25) Total: -21 bytes Signed-off-by: Denys Vlasenko --- archival/dpkg.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/archival/dpkg.c b/archival/dpkg.c index 970026620..b0e39e2fb 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -166,8 +166,9 @@ typedef struct deb_file_s { } deb_file_t; -static void make_hash(const char *key, unsigned *start, unsigned *decrement, const int hash_prime) +static unsigned make_hash(const char *key, unsigned *decrement, const int hash_prime) { + unsigned start; unsigned long hash_num = key[0]; int len = strlen(key); int i; @@ -181,8 +182,9 @@ static void make_hash(const char *key, unsigned *start, unsigned *decrement, con * no effect */ hash_num += (key[i] + key[i-1]) << ((key[i] * i) % 24); } - *start = (unsigned) hash_num % hash_prime; + start = (unsigned) hash_num % hash_prime; *decrement = (unsigned) 1 + (hash_num % (hash_prime - 1)); + return start; } /* this adds the key to the hash table */ @@ -191,7 +193,7 @@ static int search_name_hashtable(const char *key) unsigned probe_address; unsigned probe_decrement; - make_hash(key, &probe_address, &probe_decrement, NAME_HASH_PRIME); + probe_address = make_hash(key, &probe_decrement, NAME_HASH_PRIME); while (name_hashtable[probe_address] != NULL) { if (strcmp(name_hashtable[probe_address], key) == 0) { return probe_address; @@ -213,7 +215,7 @@ static unsigned search_status_hashtable(const char *key) unsigned probe_address; unsigned probe_decrement; - make_hash(key, &probe_address, &probe_decrement, STATUS_HASH_PRIME); + probe_address = make_hash(key, &probe_decrement, STATUS_HASH_PRIME); while (status_hashtable[probe_address] != NULL) { if (strcmp(key, name_hashtable[package_hashtable[status_hashtable[probe_address]->package]->name]) == 0) { break; @@ -358,7 +360,7 @@ static int search_package_hashtable(const unsigned name, const unsigned version, unsigned probe_address; unsigned probe_decrement; - make_hash(name_hashtable[name], &probe_address, &probe_decrement, PACKAGE_HASH_PRIME); + probe_address = make_hash(name_hashtable[name], &probe_decrement, PACKAGE_HASH_PRIME); while (package_hashtable[probe_address] != NULL) { if (package_hashtable[probe_address]->name == name) { if (operator == VER_ANY) { From vda.linux at googlemail.com Mon Sep 21 21:34:12 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 23:34:12 +0200 Subject: [git commit] dpkg-deb: add a rudimentary test Message-ID: <20260921213413.4EBF395503@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=e8a55de7ec1c8a0420356d921639a36139ba7201 branch: https://git.busybox.net/busybox/log/?h=master Signed-off-by: Denys Vlasenko --- testsuite/dpkg-deb.tests | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/testsuite/dpkg-deb.tests b/testsuite/dpkg-deb.tests new file mode 100755 index 000000000..711b27b9f --- /dev/null +++ b/testsuite/dpkg-deb.tests @@ -0,0 +1,79 @@ +#!/bin/sh +# Copyright 2026 by Denys Vlasenko +# Licensed under GPLv2, see file LICENSE in this source tree. + +. ./testing.sh + +rm -rf dpkg.testdir +mkdir -p dpkg.testdir +cd dpkg.testdir || exit 1 + +mk_hello_deb() +{ + mkdir mkdeb + cd mkdeb || exit 1 + + mkdir bin + printf '#!/bin/sh''\n''echo Hello world''\n' >bin/hello + chmod 755 bin/hello + tar czf data.tar.gz bin + + printf "\ +Package: hello +Version: 1.2-3 +Architecture: amd64 +Maintainer: Random Hacker +Depends: libc6 (>= 2.34) +Conflicts: hello-traditional +Breaks: hello-debhelper (<< 2.9) +Replaces: hello-debhelper (<< 2.9), hello-traditional +Section: devel +Priority: optional +Homepage: https://hacker.net/hello/ +Description: example package not based on GNU hello. + Seriously, though: this is an example of how to do a Debian package. + Multi-line description is here! +" >control + md5sum bin/hello >md5sums + tar czf control.tar.gz control md5sums + + # The file which identifies AR archive as a .deb verison 2.0 + printf '2.0\n' >debian-binary + + ar rc ../hello.deb debian-binary control.tar.gz data.tar.gz + cd .. || exit 1 + rm -rf mkdeb +} + +# testing "test name" "command" "expected result" "file input" "stdin" + +(set -e; mk_hello_deb) && { + +testing "dpkg-deb -e" \ + 'dpkg-deb -e hello.deb; echo $?; find DEBIAN; rm -rf DEBIAN' \ + "\ +0 +DEBIAN +DEBIAN/control +DEBIAN/md5sums +" \ + "" "" + +testing "dpkg-deb -X" \ + 'dpkg-deb -X hello.deb PKG; echo $?; find PKG; rm -rf PKG' \ + "\ +bin/ +bin/hello +0 +PKG +PKG/bin +PKG/bin/hello +" \ + "" "" + +} + +cd .. || exit 1 +rm -rf dpkg.testdir + +exit $FAILCOUNT From vda.linux at googlemail.com Mon Sep 21 21:32:37 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 21 Sep 2026 23:32:37 +0200 Subject: [git commit] dpkg: remove const from non-pointer variables Message-ID: <20260921213413.45490954FE@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=6b366627e137e0cb1d8e43fa1b9d3df06c0621ba branch: https://git.busybox.net/busybox/log/?h=master This does not improve anything, only makes code more verbose. Signed-off-by: Denys Vlasenko --- archival/dpkg.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/archival/dpkg.c b/archival/dpkg.c index b0e39e2fb..22f67ad66 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -166,7 +166,7 @@ typedef struct deb_file_s { } deb_file_t; -static unsigned make_hash(const char *key, unsigned *decrement, const int hash_prime) +static unsigned make_hash(const char *key, unsigned *decrement, int hash_prime) { unsigned start; unsigned long hash_num = key[0]; @@ -282,7 +282,7 @@ static int version_compare_part(const char *val, const char *ref) * if ver1 = ver2 return 0, * if ver1 > ver2 return 1, */ -static int version_compare(const unsigned ver1, const unsigned ver2) +static int version_compare(unsigned ver1, unsigned ver2) { char *ch_ver1 = name_hashtable[ver1]; char *ch_ver2 = name_hashtable[ver2]; @@ -335,9 +335,9 @@ static int version_compare(const unsigned ver1, const unsigned ver2) return result; } -static int test_version(const unsigned version1, const unsigned version2, const unsigned operator) +static int test_version(unsigned version1, unsigned version2, unsigned operator) { - const int version_result = version_compare(version1, version2); + int version_result = version_compare(version1, version2); switch (operator) { case VER_ANY: return TRUE; @@ -355,7 +355,7 @@ static int test_version(const unsigned version1, const unsigned version2, const return FALSE; } -static int search_package_hashtable(const unsigned name, const unsigned version, const unsigned operator) +static int search_package_hashtable(unsigned name, unsigned version, unsigned operator) { unsigned probe_address; unsigned probe_decrement; @@ -678,7 +678,7 @@ static unsigned fill_package_struct(char *control_buffer) } /* if num = 1, it returns the want status, 2 returns flag, 3 returns status */ -static unsigned get_status(const unsigned status_node, const int num) +static unsigned get_status(unsigned status_node, int num) { char *status_string = name_hashtable[status_hashtable[status_node]->status]; char *state_sub_string; @@ -700,9 +700,9 @@ static unsigned get_status(const unsigned status_node, const int num) return state_sub_num; } -static void set_status(const unsigned status_node_num, const char *new_value, const int position) +static void set_status(unsigned status_node_num, const char *new_value, int position) { - const unsigned new_value_num = search_name_hashtable(new_value); + unsigned new_value_num = search_name_hashtable(new_value); unsigned want = get_status(status_node_num, 1); unsigned flag = get_status(status_node_num, 2); unsigned status = get_status(status_node_num, 3); @@ -762,7 +762,7 @@ static void index_status_file(const char *filename) status_file = xfopen_for_read(filename); while ((control_buffer = xmalloc_fgetline_str(status_file, "\n\n")) != NULL) { - const unsigned package_num = fill_package_struct(control_buffer); + unsigned package_num = fill_package_struct(control_buffer); if (package_num != -1) { status_node = xmalloc(sizeof(status_node_t)); /* fill_package_struct doesn't handle the status field */ @@ -843,7 +843,7 @@ static void write_status_file(deb_file_t **deb_file) const char *status_from_hashtable = name_hashtable[status_hashtable[status_num]->status]; if (strcmp(status_from_file, status_from_hashtable) != 0) { /* New status isn't exactly the same as old status */ - const int state_status = get_status(status_num, 3); + int state_status = get_status(status_num, 3); if ((strcmp("installed", name_hashtable[state_status]) == 0) || (strcmp("unpacked", name_hashtable[state_status]) == 0) ) { @@ -990,14 +990,14 @@ static int check_deps(deb_file_t **deb_file, int deb_start /*, int dep_max_count /* Create array of package numbers to check against * installed package for conflicts*/ while (deb_file[i] != NULL) { - const unsigned package_num = deb_file[i]->package; + unsigned package_num = deb_file[i]->package; conflicts = xrealloc_vector(conflicts, 2, conflicts_num); conflicts[conflicts_num] = package_num; conflicts_num++; /* add provides to conflicts list */ for (j = 0; j < package_hashtable[package_num]->num_of_edges; j++) { if (package_hashtable[package_num]->edge[j]->type == EDGE_PROVIDES) { - const int conflicts_package_num = search_package_hashtable( + int conflicts_package_num = search_package_hashtable( package_hashtable[package_num]->edge[j]->name, package_hashtable[package_num]->edge[j]->version, package_hashtable[package_num]->edge[j]->operator); @@ -1032,7 +1032,7 @@ static int check_deps(deb_file_t **deb_file, int deb_start /*, int dep_max_count const edge_t *package_edge = package_node->edge[j]; if (package_edge->type == EDGE_CONFLICTS) { - const unsigned package_num = + unsigned package_num = search_package_hashtable(package_edge->name, package_edge->version, package_edge->operator); @@ -1061,7 +1061,7 @@ static int check_deps(deb_file_t **deb_file, int deb_start /*, int dep_max_count for (i = 0; i < PACKAGE_HASH_PRIME; i++) { int status_num = 0; int number_of_alternatives = 0; - const edge_t * root_of_alternatives = NULL; + const edge_t *root_of_alternatives = NULL; const common_node_t *package_node = package_hashtable[i]; /* If the package node does not exist then this @@ -1377,12 +1377,12 @@ static void list_packages(const char *pattern) } } -static void remove_package(const unsigned package_num, int noisy) +static void remove_package(unsigned package_num, int noisy) { const char *package_name = name_hashtable[package_hashtable[package_num]->name]; const char *package_version = name_hashtable[package_hashtable[package_num]->version]; - const unsigned status_num = search_status_hashtable(package_name); - const int package_name_length = strlen(package_name); + unsigned status_num = search_status_hashtable(package_name); + int package_name_length = strlen(package_name); char **remove_files; char **exclude_files; char list_name[package_name_length + 25]; @@ -1428,11 +1428,11 @@ static void remove_package(const unsigned package_num, int noisy) set_status(status_num, "config-files", 3); } -static void purge_package(const unsigned package_num) +static void purge_package(unsigned package_num) { const char *package_name = name_hashtable[package_hashtable[package_num]->name]; const char *package_version = name_hashtable[package_hashtable[package_num]->version]; - const unsigned status_num = search_status_hashtable(package_name); + unsigned status_num = search_status_hashtable(package_name); char **remove_files; char **exclude_files; char list_name[strlen(package_name) + 25]; @@ -1650,8 +1650,8 @@ enum { static void unpack_package(deb_file_t *deb_file) { const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name]; - const unsigned status_num = search_status_hashtable(package_name); - const unsigned status_package_num = status_hashtable[status_num]->package; + unsigned status_num = search_status_hashtable(package_name); + unsigned status_package_num = status_hashtable[status_num]->package; char *info_prefix; char *list_filename; archive_handle_t *archive_handle; @@ -1741,7 +1741,7 @@ static void configure_package(deb_file_t *deb_file) { const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name]; const char *package_version = name_hashtable[package_hashtable[deb_file->package]->version]; - const int status_num = search_status_hashtable(package_name); + int status_num = search_status_hashtable(package_name); printf("Setting up %s (%s)...\n", package_name, package_version); From vda.linux at googlemail.com Mon Sep 21 22:48:46 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 22 Sep 2026 00:48:46 +0200 Subject: [git commit] dpkg: improve --help Message-ID: <20260921230440.63CC29543A@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=7a055991de874c63c16a3aa7248ba7dbeac2babd branch: https://git.busybox.net/busybox/log/?h=master function old new delta packed_usage 36247 36282 +35 Signed-off-by: Denys Vlasenko --- archival/dpkg.c | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/archival/dpkg.c b/archival/dpkg.c index 22f67ad66..3abc810c8 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -41,26 +41,26 @@ //kbuild:lib-$(CONFIG_DPKG) += dpkg.o //usage:#define dpkg_trivial_usage -//usage: "[-iCPru] [-F OPT] PACKAGE | -l [PATTERN]" +//usage: IF_LONG_OPTS("-i|P|r|--unpack|--configure [--force-OPT]")IF_NOT_LONG_OPTS("-i|P|r|u|C [-F OPT]")" PACKAGE | -l [PATTERN]" //usage:#define dpkg_full_usage "\n\n" -//usage: "Install, remove and manage Debian packages\n" +//usage: "Manage Debian packages\n" //usage: IF_LONG_OPTS( -//usage: "\n -i,--install Install the package" -//usage: "\n --configure Configure an unpackaged package" -//usage: "\n -P,--purge Purge all files of a package" -//usage: "\n -r,--remove Remove all but the configuration files for a package" -//usage: "\n --unpack Unpack a package, but don't configure it" +//usage: "\n -i,--install Install PACKAGE.deb" +//usage: "\n -P,--purge Purge all files of PACKAGE" +//usage: "\n -r,--remove Remove all but configuration files of PACKAGE" +//usage: "\n --unpack Unpack PACKAGE.deb relative to /, don't configure" +//usage: "\n --configure Configure unpacked PACKAGE" //usage: "\n --force-depends Ignore dependency problems" //usage: "\n --force-confnew Overwrite existing config files when installing" //usage: "\n --force-confold Keep old config files when installing" //usage: "\n -l,--list List installed packages" //usage: ) //usage: IF_NOT_LONG_OPTS( -//usage: "\n -i Install the package" -//usage: "\n -C Configure an unpackaged package" -//usage: "\n -P Purge all files of a package" -//usage: "\n -r Remove all but the configuration files for a package" -//usage: "\n -u Unpack a package, but don't configure it" +//usage: "\n -i Install PACKAGE.deb" +//usage: "\n -P Purge all files of PACKAGE" +//usage: "\n -r Remove all but configuration files of PACKAGE" +//usage: "\n -u Unpack PACKAGE.deb relative to /, don't configure" +//usage: "\n -C Configure unpacked PACKAGE" //usage: "\n -F depends Ignore dependency problems" //usage: "\n -F confnew Overwrite existing config files when installing" //usage: "\n -F confold Keep old config files when installing" @@ -760,6 +760,7 @@ static void index_status_file(const char *filename) status_node_t *status_node = NULL; unsigned status_num; +//FIXME: dpkg 1.22.22 recreates empty /var/lib/dpkg/status if it's missing - does not fail status_file = xfopen_for_read(filename); while ((control_buffer = xmalloc_fgetline_str(status_file, "\n\n")) != NULL) { unsigned package_num = fill_package_struct(control_buffer); @@ -804,6 +805,7 @@ static void write_buffer_no_status(FILE *new_status_file, const char *control_bu /* This could do with a cleanup */ static void write_status_file(deb_file_t **deb_file) { +//FIXME: dpkg 1.22.22 recreates empty /var/lib/dpkg/status if it's missing - does not fail FILE *old_status_file = xfopen_for_read("/var/lib/dpkg/status"); FILE *new_status_file = xfopen_for_write("/var/lib/dpkg/status.udeb"); char *control_buffer; @@ -1782,6 +1784,24 @@ int dpkg_main(int argc UNUSED_PARAM, char **argv) "force-confold\0" No_argument "\xfd" ; #endif +//--admindir=DIR +// Set the administrative directory to DIR. This directory +// contains many files that give information about status of +// installed or uninstalled packages, etc. Defaults to +// /var/lib/dpkg if DPKG_ADMINDIR has not been set. +//--instdir=DIR +// Set the installation directory, which refers to the directory +// where packages are to be installed. DIR is also the +// directory passed to chroot(2) before running package's +// installation scripts, which means that the scripts see DIR +// as a root directory. Defaults to / if DPKG_ROOT has not +// been set (since dpkg 1.21.10). +//--root=DIR +// Set the root directory to DIR, which sets the +// installation directory to DIR and the administrative +// directory to DIR/var/lib/dpkg if DPKG_ROOT +// has not been set (since dpkg 1.21.10). +//TODO: implement --root=DIR? necessary for testing INIT_G(); From vda.linux at googlemail.com Mon Sep 21 23:05:07 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 22 Sep 2026 01:05:07 +0200 Subject: [git commit] dpkg: fix field handling in write_status_file() Message-ID: <20260921230440.717AF954F0@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=d5cc94063b2bb5b54fe1021e54600cbcbce6a350 branch: https://git.busybox.net/busybox/log/?h=master function old new delta dpkg_main 3943 3934 -9 Signed-off-by: Denys Vlasenko --- archival/dpkg.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/archival/dpkg.c b/archival/dpkg.c index 3abc810c8..6a65f16a4 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -534,8 +534,8 @@ static void free_package(common_node_t *node) /* * Gets the next package field from pkg_buf: - * "Name:VALUE{\n|NUL}" - * "Name:\n + * "NAME:VALUE{\n|NUL}" + * "NAME:\n * " VALUE{\n|NUL}" * separated into "NAME" and "VALUE", both strdup()ed. * Returns the int offset to the first character of the next field. @@ -809,7 +809,6 @@ static void write_status_file(deb_file_t **deb_file) FILE *old_status_file = xfopen_for_read("/var/lib/dpkg/status"); FILE *new_status_file = xfopen_for_write("/var/lib/dpkg/status.udeb"); char *control_buffer; - int field_start = 0; int status_num; int i; @@ -819,23 +818,20 @@ static void write_status_file(deb_file_t **deb_file) char *status_from_file; char *tmp_string; int write_flag; -//FIXME: "int field_start = 0;" should be _here_, right? tmp_string = strstr(control_buffer, "Package:"); if (tmp_string == NULL) { free(control_buffer); continue; } - tmp_string += 8; - tmp_string += strspn(tmp_string, " \n\t"); + tmp_string = skip_whitespace(tmp_string + 8); package_name = xstrndup(tmp_string, strcspn(tmp_string, "\n")); write_flag = FALSE; status_from_file = NULL; tmp_string = strstr(control_buffer, "Status:"); if (tmp_string != NULL) { - tmp_string += 7; - tmp_string += strspn(tmp_string, " \n\t"); + tmp_string = skip_whitespace(tmp_string + 7); status_from_file = xstrndup(tmp_string, strcspn(tmp_string, "\n")); } @@ -872,6 +868,8 @@ static void write_status_file(deb_file_t **deb_file) } } else if (strcmp("not-installed", name_hashtable[state_status]) == 0) { + int field_start = 0; + /* Only write the Package, Status, Priority and Section lines */ fprintf(new_status_file, "Package: %s\n", package_name); fprintf(new_status_file, "Status: %s\n", status_from_hashtable); @@ -880,6 +878,8 @@ static void write_status_file(deb_file_t **deb_file) char *field_name; char *field_value; field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value); +//FIXME: the questionable ": VALUE" lines (empty NAME) +//probably should not stop parsing of the entire file? if (field_name == NULL) { break; } @@ -895,6 +895,8 @@ static void write_status_file(deb_file_t **deb_file) fputs("\n", new_status_file); } else if (strcmp("config-files", name_hashtable[state_status]) == 0) { + int field_start = 0; + /* only change the status line */ while (control_buffer[field_start]) { char *field_name; From vda.linux at googlemail.com Mon Sep 21 23:14:47 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 22 Sep 2026 01:14:47 +0200 Subject: [git commit] httpd: allow yescrypt passwords ("$y$....") Message-ID: <20260921231431.51E3F9543A@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=9f5cbdcaa5972d58503c59cd0da13da1e5bb272b branch: https://git.busybox.net/busybox/log/?h=master function old new delta check_user_passwd 439 444 +5 Signed-off-by: Denys Vlasenko --- loginutils/Config.src | 2 +- networking/httpd.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/loginutils/Config.src b/loginutils/Config.src index a7812bd32..5559992da 100644 --- a/loginutils/Config.src +++ b/loginutils/Config.src @@ -96,7 +96,7 @@ config USE_BB_CRYPT_YES default y depends on USE_BB_CRYPT help - Enable this if you have passwords starting with "$y$" or + Enable this if you have passwords starting with "$y$" in your /etc/passwd or /etc/shadow files. These passwords are hashed using yescrypt algorithms. With this option off, login will fail password check for any diff --git a/networking/httpd.c b/networking/httpd.c index a5dfb38af..842574e0e 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -2247,7 +2247,7 @@ static int check_user_passwd(const char *path, char *user_and_passwd) } /* Else: passwd is from httpd.conf, it is either plaintext or encrypted */ - if (passwd[0] == '$' && isdigit(passwd[1])) { + if (passwd[0] == '$' && (isdigit(passwd[1]) || passwd[1] == 'y')) { char *encrypted; # if !ENABLE_PAM check_encrypted: From vda.linux at googlemail.com Mon Sep 21 23:55:03 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 22 Sep 2026 01:55:03 +0200 Subject: [git commit] mount: document a possible bug in comment, no code changes. Message-ID: <20260921235525.885BB9543A@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=556567f403bf492cedb371dad0606d867b0e22d0 branch: https://git.busybox.net/busybox/log/?h=master Signed-off-by: Denys Vlasenko --- util-linux/mount.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/util-linux/mount.c b/util-linux/mount.c index d0d109c09..4fac7e5d0 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c @@ -321,8 +321,8 @@ enum { // TODO: more "user" flag compatibility. // "user" option (from mount manpage): // Only the user that mounted a filesystem can unmount it again. -// If any user should be able to unmount, then use users instead of user -// in the fstab line. The owner option is similar to the user option, +// If any user should be able to unmount, then use "users" instead of "user" +// in the fstab line. The "owner" option is similar to the "user" option, // with the restriction that the user must be the owner of the special file. // This may be useful e.g. for /dev/fd if a login script makes // the console user owner of this device. @@ -595,7 +595,7 @@ static void append_mount_options(char **oldopts, const char *newopts) // Use the mount_options list to parse options into flags. // Also update list of unrecognized options if unrecognized != NULL -static unsigned long parse_mount_options(char *options, char **unrecognized, uint32_t *opt) +static unsigned long parse_mount_options(char *options, char **unrecognized, uint32_t *optmask32) { unsigned long flags = MS_SILENT; @@ -626,8 +626,8 @@ static unsigned long parse_mount_options(char *options, char **unrecognized, uin /* If we see "-o rw" on command line, it's the same as -w: * "do not try to fall back to RO mounts" */ - if (fl == ~MS_RDONLY && opt) - (*opt) |= OPT_w; + if (fl == ~MS_RDONLY && optmask32) + (*optmask32) |= OPT_w; goto found; } option_str += opt_len + 1; @@ -2368,6 +2368,23 @@ int mount_main(int argc UNUSED_PARAM, char **argv) cmdopt_flags = parse_mount_options(cmdopts, NULL, &option_mask32); if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags bb_simple_error_msg_and_die(bb_msg_you_must_be_root); + // NB: parse_mount_options() starts with just MS_SILENT and adds bits as it sees options. + // Some options clear a flag, not set it: + // suid ~MS_NOSUID + // dev ~MS_NODEV + // exec ~MS_NOEXEC + // async ~MS_SYNCHRONOUS + // atime ~MS_NOATIME + // diratime ~MS_NODIRATIME + // norelatime ~MS_RELATIME + // nostrictatime ~MS_STRICTATIME + // nolazytime ~MS_LAZYTIME + // nomand ~MS_MANDLOCK + // rw ~MS_RDONLY + // therefore the above check does not catch them, + // they are allowed for non-root (and ignored): they essentially request a default behavior. + // Is it a bug? + // Should they be disallowed for non-root, and override fstab options for "mount -o OPTS ONE_PARAM" form for root? // If we have a shared subtree flag, don't worry about fstab or mtab. if (ENABLE_FEATURE_MOUNT_FLAGS From vda.linux at googlemail.com Tue Sep 22 00:19:06 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 22 Sep 2026 02:19:06 +0200 Subject: [git commit] libpwdgrp: tokenizer fix for trailing whitespace removeal. Message-ID: <20260922001927.286C895543@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=c2dec52d37380d27d89827cf01a770f6b3b04e3a branch: https://git.busybox.net/busybox/log/?h=master The tokenize() function trims trailing whitespace via overlapping_strcpy() but continues to reference the stale *end pointer. Fixing this. function old new delta tokenize 120 119 -1 Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index 10debbcdb..48a49fc24 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -171,7 +171,7 @@ static int tokenize(char *buffer, int ch) if (p != end) overlapping_strcpy(p, end); num_fields++; - if (*end == '\0') { + if (*p == '\0') { S.tokenize_end = p + 1; return num_fields; } From vda.linux at googlemail.com Tue Sep 22 00:38:38 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 22 Sep 2026 02:38:38 +0200 Subject: [git commit] tls: fix off-by-one access past the end of the received packet. Message-ID: <20260922003829.52F4195555@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=e97224f8fbad11a4d00feaec3391c27ed773f147 branch: https://git.busybox.net/busybox/log/?h=master Signed-off-by: Denys Vlasenko --- networking/tls.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/networking/tls.c b/networking/tls.c index 91172f8bc..05dda491a 100644 --- a/networking/tls.c +++ b/networking/tls.c @@ -2467,7 +2467,9 @@ static void get_client_hello(tls_state_t *tls) p += 2; len -= 2; - if (len < cipher_list_len) { + if (len <= cipher_list_len) { + /* The "<=" is subtle: it aborts even if we do have cipher list, + * but have no compression size byte after it */ bb_simple_error_msg_and_die("malformed ClientHello"); } @@ -2522,7 +2524,7 @@ static void get_client_hello(tls_state_t *tls) len -= cipher_list_len; /* Skip compression methods */ - len -= 1 + p[0]; + len -= 1 + p[0]; /* touching p[0] is allowed: len was > cipher_list_len */ p += 1 + p[0]; /* Parse extensions if present */ From vda.linux at googlemail.com Tue Sep 22 11:26:19 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 22 Sep 2026 13:26:19 +0200 Subject: [git commit] httpd: document that P: proxying is not checking passwords Message-ID: <20260922112559.1BF4D95CB3@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=39c2dc8c49a52a096bbf5038f783e9949974f638 branch: https://git.busybox.net/busybox/log/?h=master Signed-off-by: Denys Vlasenko --- networking/httpd.c | 79 +++++++++++++++++++++++++++--------------------------- networking/wget.c | 2 +- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index 842574e0e..7469de1de 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -38,59 +38,60 @@ * * httpd.conf has the following format: * - * H:/serverroot # define the server root. It will override -h - * A:172.20. # Allow address from 172.20.0.0/16 - * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127 - * A:10.0.0.0/255.255.255.128 # Same as the previous - * A:127.0.0.1 # Allow local loopback connections - * D:* # Deny from other IP connections - * E404:/path/e404.html # /path/e404.html is the 404 (not found) error page - * I:index.html # Show index.html when a directory is requested - * - * P:/url:[http://]hostname[:port]/new/path - * # When /urlXXXXXX is requested, reverse proxy - * # it to http://hostname[:port]/new/pathXXXXXX + * H:/serverroot # define the server root. It will override -h + * I:index.html # Show index.html when a directory is requested + * .au:audio/basic # additional mime type for audio.au files + * *.php:/path/php # run xxx.php through an interpreter * - * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/ - * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ - * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ - * /adm:root:* # or user root, pwd from /etc/passwd on urls starting with /adm/ - * /wiki:*:* # or any user from /etc/passwd with according pwd on urls starting with /wiki/ - * .au:audio/basic # additional mime type for audio.au files - * *.php:/path/php # run xxx.php through an interpreter + * E404:/path/e404.html # /path/e404.html is the 404 (not found) error page + * Custom error pages can contain an absolute path or be relative to + * 'home_httpd'. Error pages are to be static files (no CGI or script). + * Error pages can only be defined in the main configuration file + * and are not taken into account in local (directories) config files. * - * A/D may be as a/d or allow/deny - only first char matters. + * A:172.20. # Allow address from 172.20.0.0/16 + * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127 + * A:10.0.0.0/255.255.255.128 # Same as the previous + * A:127.0.0.1 # Allow local loopback connections + * D:* # Deny from other IP connections + * A/D may be a/d or allow/deny - only first char matters. * Deny/Allow IP logic: * - Default is to allow all (Allow all (A:*) is a no-op). * - Deny rules take precedence over allow rules. * - However, "Deny all" rule (D:*) is applied last. - * * Example: - * 1. Allow only specified addresses - * A:172.20 # Allow any address that begins with 172.20. - * A:10.10. # Allow any address that begins with 10.10. - * A:127.0.0.1 # Allow local loopback connections - * D:* # Deny from other IP connections + * Allow only specified addresses + * A:172.20 # allow any address that begins with 172.20. + * A:10.10. # allow any address that begins with 10.10. + * A:127.0.0.1 # allow local loopback connections + * D:* # deny from other IP connections + * + * Only deny specified addresses + * D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255 + * D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255 + * A:* # (optional line added for clarity) + * + * P:/url:[http://]hostname[:port]/new/path + * When /urlXXXXXX is requested, reverse proxy it to http://hostname[:port]/new/pathXXXXXX + * This is done without checking any passwords for /urlXXXXXX. + * A:/D: directives work, but only those in the main configuration file. * - * 2. Only deny specified addresses - * D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255 - * D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255 - * A:* # (optional line added for clarity) + * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/ + * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ + * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ + * /adm:toor:$1$P/eKnWXS$aI1aPGxT.dJD5SzqAKWrF0 # encrypted paaswd + * /adm:root:* # or user root, pwd from /etc/passwd on urls starting with /adm/ + * /wiki:*:* # or any user from /etc/passwd with according pwd on urls starting with /wiki/ * - * If a sub directory contains config file, it is parsed and merged with + * If a subdirectory contains config file, it is parsed and merged with * any existing settings as if it was appended to the original configuration. * * subdir paths are relative to the containing subdir and thus cannot * affect the parent rules. * - * Note that since the sub dir is parsed in the forked thread servicing the - * subdir http request, any merge is discarded when the process exits. As a - * result, the subdir settings only have a lifetime of a single request. - * - * Custom error pages can contain an absolute path or be relative to - * 'home_httpd'. Error pages are to be static files (no CGI or script). Error - * page can only be defined in the root configuration file and are not taken - * into account in local (directories) config files. + * Note that since the subdir is parsed in the forked child servicing + * the subdir http request, any merge is discarded when the child exits. + * As a result, the subdir settings only have a lifetime of a single request. * * If -c is not set, an attempt will be made to open the default * root configuration file. If -c is set and the file is not found, the diff --git a/networking/wget.c b/networking/wget.c index ec3767793..0a83e56c2 100644 --- a/networking/wget.c +++ b/networking/wget.c @@ -95,7 +95,7 @@ //config: If you still think this is unacceptable, send patches. //config: //config: If you still think this is unacceptable, do not want to send -//config: patches, but do want to waste bandwidth expaining how wrong +//config: patches, but do want to waste bandwidth explaining how wrong //config: it is, you will be ignored. //config: //config: FEATURE_WGET_OPENSSL does implement TLS verification From vda.linux at googlemail.com Tue Sep 22 14:12:33 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 22 Sep 2026 16:12:33 +0200 Subject: [git commit] dpkk: stop allowing spaces in NAME: parts. Debian does not do this, it seems Message-ID: <20260922142012.5E09A960AC@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=61e244e5ee8ba054e8d89def585fb99eb751f4e2 branch: https://git.busybox.net/busybox/log/?h=master Add mode docs in comments. We probably do not follow them correctly yet. function old new delta read_package_field 211 170 -41 Signed-off-by: Denys Vlasenko --- archival/dpkg.c | 68 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/archival/dpkg.c b/archival/dpkg.c index 6a65f16a4..0787c8332 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -534,12 +534,39 @@ static void free_package(common_node_t *node) /* * Gets the next package field from pkg_buf: - * "NAME:VALUE{\n|NUL}" - * "NAME:\n - * " VALUE{\n|NUL}" + * "NAME:VALUE{\n|NUL}" + * "NAME:VALUE\n + * " VALUE_LINE2{\n|NUL}" * separated into "NAME" and "VALUE", both strdup()ed. - * Returns the int offset to the first character of the next field. + * (Debian does not allow whitespace before/after NAME). + * Returns offset to the first character of the next field (or to NUL). * The pkg_buf parameter is NUL-terminated. + * + * https://manpages.debian.org/testing/dpkg-dev/deb-control.5.en.html + * """ + * This file contains a number of fields. Each field begins with a tag, + * such as Package or Version (case insensitive), followed by a colon, + * and the body of the field (case sensitive unless stated otherwise). + * Fields are delimited only by field tags. In other words, field text + * may be multiple lines in length, but the installation tools will + * generally join lines when processing the body of the field (except + * in the case of the Description field, see below). + * ... + * Description: short-description (recommended) + * long-description + * The format for the package description is a short brief summary + * on the first line (after the Description field). The following lines + * should be used as a longer, more detailed description. Each line + * of the long description must be preceded by a space, and blank + * lines in the long description must contain a single '.' following + * the preceding space.""" + * + * Seen in libgtk-3-0t64_3.24.52: source package's control: + * Recommends: ibus-gtk3, + * libgtk-3-bin, + * librsvg2-common + * gets translated to this in binary package's control: + * Recommends: ibus-gtk3, libgtk-3-bin, librsvg2-common */ static int read_package_field(const char *pkg_buf, char **pname, char **pvalue) { @@ -559,34 +586,27 @@ static int read_package_field(const char *pkg_buf, char **pname, char **pvalue) switch (ch) { case ':': if (name_end == 0) { - name_end = offset + 1; /* points AFTER ':' - think of empty NAME case */ - value_start = offset + 1; + offset++; + name_end = offset; /* points AFTER ':' - think of empty NAME case */ + while (isblank(pkg_buf[offset])) /* skip spaces/tabs (not newlines!) */ + offset++; + value_start = offset; + continue; /* check next char */ } - /* TODO: NAME might still have trailing spaces if ':' isn't - * immediately after name */ break; case '\n': - if (pkg_buf[offset + 1] != ' ') - goto end_of_value; - /* fall through */ - case '\t': - case ' ': - /* increment start points if it is just a filler */ - if (name_start == offset) - name_start++; - if (value_start == offset) - value_start++; - break; + if (pkg_buf[offset + 1] == ' ') + break; + /* end of VALUE - fall through */ case '\0': - end_of_value: /* Did we see the ':'? */ if (name_end != 0) { - /* Yes. Check that NAME and VALUE exist and not empty */ + /* Yes. Check that NAME is not empty */ int nlen = (name_end - 1) - name_start; - int vlen = offset - value_start; - if ((nlen > 0) && (vlen > 0)) { + if (nlen > 0) { +//TODO: why do we disallow empty NAME? Imagine a ": VALUE\n" line *pname = xstrndup(&pkg_buf[name_start], nlen); - *pvalue = xstrndup(&pkg_buf[value_start], vlen); + *pvalue = xstrndup(&pkg_buf[value_start], offset - value_start); /* could be "" */ if (ch) offset++; /* skip '\n' */ return offset; From vda.linux at googlemail.com Tue Sep 22 22:36:36 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Wed, 23 Sep 2026 00:36:36 +0200 Subject: [git commit] wget: support multiple restarts, -t NUM controls how many Message-ID: <20260922223811.9E4BA98307@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=218f32862560d69e53cff0bb8fd472a27959fe16 branch: https://git.busybox.net/busybox/log/?h=master function old new delta retrieve_file_data 621 705 +84 wget_main 2581 2657 +76 ftpcmd 133 151 +18 packed_usage 36282 36298 +16 get_sanitized_hdr 150 156 +6 fgets_trim_sanitize 134 137 +3 reset_beg_range_to_zero 44 46 +2 .rodata 107342 107344 +2 progress_meter 189 182 -7 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 8/1 up/down: 207/-7) Total: 200 bytes Signed-off-by: Denys Vlasenko --- networking/wget.c | 125 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 47 deletions(-) diff --git a/networking/wget.c b/networking/wget.c index 0a83e56c2..853dde72d 100644 --- a/networking/wget.c +++ b/networking/wget.c @@ -135,37 +135,42 @@ //usage:#define wget_trivial_usage //usage: IF_FEATURE_WGET_LONG_OPTIONS( -//usage: "[-cqS] [--spider] [-O FILE] [-o LOGFILE] [--header STR]\n" -//usage: " [--post-data STR | --post-file FILE] [-Y on/off]\n" +//usage: "[-cqS] [--spider] [-O FILE] [-o LOGFILE] [--header STR]...\n" +//usage: " [-U AGENT] [--post-data STR | --post-file FILE] [-Y on/off]\n" /* Since we ignore these opts, we don't show them in --help */ -/* //usage: " [--no-cache] [--passive-ftp] [-t TRIES]" */ +/* //usage: " [--no-cache] [--passive-ftp]" */ /* //usage: " [-nv] [-nc] [-nH] [-np]" */ -//usage: " "IF_FEATURE_WGET_OPENSSL("[--no-check-certificate] ")"[-P DIR] [-U AGENT]"IF_FEATURE_WGET_TIMEOUT(" [-T SEC]")" URL..." +//usage: " "IF_FEATURE_WGET_OPENSSL("[--no-check-certificate] ")"[-P DIR] "IF_FEATURE_WGET_TIMEOUT("[-T SEC] ")"[-t TRIES] URL..." //usage: ) //usage: IF_NOT_FEATURE_WGET_LONG_OPTIONS( -//usage: "[-cqS] [-O FILE] [-o LOGFILE] [-Y on/off] [-P DIR] [-U AGENT]"IF_FEATURE_WGET_TIMEOUT(" [-T SEC]")" URL..." +//usage: "[-cqS] [-O FILE] [-o LOGFILE] [-P DIR] [-U AGENT] [-Y on/off]\n" +//usage: " "IF_FEATURE_WGET_TIMEOUT("[-T SEC] ")"[-t TRIES] URL..." + //usage: ) //usage:#define wget_full_usage "\n\n" //usage: "Retrieve files via HTTP or FTP\n" //usage: IF_FEATURE_WGET_LONG_OPTIONS( //usage: "\n --spider Only check URL existence: $? is 0 if exists" //usage: "\n --header STR Add STR (of form 'header: value') to headers" +//usage: ) +//usage: "\n -U AGENT Use AGENT for User-Agent header" +//usage: IF_FEATURE_WGET_LONG_OPTIONS( //usage: "\n --post-data STR Send STR using POST method" //usage: "\n --post-file FILE Send FILE using POST method" //usage: IF_FEATURE_WGET_OPENSSL( //usage: "\n --no-check-certificate Don't validate the server's certificate" //usage: ) //usage: ) -//usage: "\n -c Continue retrieval of aborted transfer" +//usage: "\n -c Continue retrieval of partial download" //usage: "\n -q Quiet" //usage: "\n -P DIR Save to DIR (default .)" //usage: "\n -S Show server response" +//usage: "\n -t TRIES Retry count (default 20)" //usage: IF_FEATURE_WGET_TIMEOUT( //usage: "\n -T SEC Network read timeout is SEC seconds" //usage: ) //usage: "\n -O FILE Save to FILE ('-' for stdout)" //usage: "\n -o LOGFILE Log messages to FILE" -//usage: "\n -U STR Use STR for User-Agent header" //usage: "\n -Y on/off Use proxy" #include "libbb.h" @@ -244,8 +249,9 @@ static const char wget_user_headers[] ALIGN1 = struct globals { off_t content_len; /* Content-length of the file */ off_t beg_range; /* Range at which continue begins */ -#if ENABLE_FEATURE_WGET_STATUSBAR off_t transferred; /* Number of bytes transferred so far */ + off_t reset_pos; /* Where to seek to if server refuses to continue (usually 0) */ +#if ENABLE_FEATURE_WGET_STATUSBAR const char *curfile; /* Name of current file being transferred */ bb_progress_t pmt; #endif @@ -260,6 +266,7 @@ struct globals { char *fname_log; /* where to direct log (-o) */ const char *proxy_flag; /* Use proxies if env vars are set */ const char *user_agent; /* "User-Agent" header field */ + int tries; /* -t/--tries NUM */ int output_fd; int log_fd; int o_flags; @@ -286,24 +293,22 @@ struct globals { /* Must match option string! */ enum { - WGET_OPT_CONTINUE = (1 << 0), - WGET_OPT_QUIET = (1 << 1), - WGET_OPT_SERVER_RESPONSE = (1 << 2), - WGET_OPT_OUTNAME = (1 << 3), - WGET_OPT_LOGNAME = (1 << 4), - WGET_OPT_PREFIX = (1 << 5), - WGET_OPT_PROXY = (1 << 6), - WGET_OPT_USER_AGENT = (1 << 7), - WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8), - WGET_OPT_RETRIES = (1 << 9), + WGET_OPT_CONTINUE = (1 << 0), // -c + WGET_OPT_QUIET = (1 << 1), // -q + WGET_OPT_SERVER_RESPONSE = (1 << 2), // -W + WGET_OPT_OUTNAME = (1 << 3), // -O FILE + WGET_OPT_LOGNAME = (1 << 4), // -o LOGFILE + WGET_OPT_PREFIX = (1 << 5), // -P DIR + WGET_OPT_PROXY = (1 << 6), // -Y on/off + WGET_OPT_USER_AGENT = (1 << 7), // -U USER_AGENT + WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8), // -T SEC + WGET_OPT_RETRIES = (1 << 9), // -t NUM WGET_OPT_nsomething = (1 << 10), WGET_OPT_HEADER = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS, WGET_OPT_POST_DATA = (1 << 12) * ENABLE_FEATURE_WGET_LONG_OPTIONS, WGET_OPT_SPIDER = (1 << 13) * ENABLE_FEATURE_WGET_LONG_OPTIONS, WGET_OPT_NO_CHECK_CERT = (1 << 14) * ENABLE_FEATURE_WGET_LONG_OPTIONS, WGET_OPT_POST_FILE = (1 << 15) * ENABLE_FEATURE_WGET_LONG_OPTIONS, - /* hijack this bit for other than opts purposes: */ - WGET_NO_FTRUNCATE = (1 << 31) }; #define WGET_OPT_POST (WGET_OPT_POST_DATA | WGET_OPT_POST_FILE) @@ -338,7 +343,6 @@ static void progress_meter(int flag) bb_progress_free(&G.pmt); if (notty == 0) bb_putchar_stderr('\n'); /* it's tty */ - G.transferred = 0; } } #else @@ -669,7 +673,13 @@ static void reset_beg_range_to_zero(void) { bb_simple_error_msg("restart failed"); G.beg_range = 0; - xlseek(G.output_fd, 0, SEEK_SET); + /* + * G.reset_pos is zero except for one corner case: + * "wget -O FILE URL1 URL2". If download of URL2 + * was partial, and next retry fails to restart from last position, + * we must rewind FILE to where URL2 data starts, not to 0! + */ + xlseek(G.output_fd, G.reset_pos, SEEK_SET); /* Done at the end instead: */ /* ftruncate(G.output_fd, 0); */ } @@ -914,8 +924,10 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_ } #endif -static void NOINLINE retrieve_file_data(FILE *dfp) +/* Return 0 if successful, !0 if partial file gotten and then got EOF or error */ +static int NOINLINE retrieve_file_data(FILE *dfp) { + off_t pos; #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT # if ENABLE_FEATURE_WGET_TIMEOUT unsigned second_cnt = G.timeout_seconds; @@ -931,6 +943,8 @@ static void NOINLINE retrieve_file_data(FILE *dfp) else fprintf(stderr, "saving to '%s'\n", G.fname_out); } + + G.transferred = 0; progress_meter(PROGRESS_START); if (G.chunked) @@ -977,9 +991,7 @@ static void NOINLINE retrieve_file_data(FILE *dfp) if (n > 0) { xwrite(G.output_fd, G.wget_buf, n); -#if ENABLE_FEATURE_WGET_STATUSBAR G.transferred += n; -#endif if (G.got_clen) { G.content_len -= n; if (G.content_len == 0) @@ -1000,9 +1012,10 @@ static void NOINLINE retrieve_file_data(FILE *dfp) if (errno != EAGAIN) { if (ferror(dfp)) { progress_meter(PROGRESS_END); - bb_simple_perror_msg_and_die(bb_msg_read_error); + bb_simple_perror_msg(bb_msg_read_error); + G.content_len = 1; /* treat end as error, not EOF */ } - break; /* EOF, not error */ + goto EOF_err; /* EOF / error */ } #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT @@ -1013,7 +1026,9 @@ static void NOINLINE retrieve_file_data(FILE *dfp) # if ENABLE_FEATURE_WGET_TIMEOUT if (second_cnt != 0 && --second_cnt == 0) { progress_meter(PROGRESS_END); - bb_simple_error_msg_and_die("download timed out"); + bb_simple_error_msg("download timed out"); + G.content_len = 1; /* treat end as error, not EOF */ + goto EOF_err; } # endif /* We used to loop back to poll here, @@ -1065,25 +1080,25 @@ static void NOINLINE retrieve_file_data(FILE *dfp) G.chunked = 0; /* makes it show 100% even for chunked download */ G.got_clen = 1; /* makes it show 100% even for download of (formerly) unknown size */ progress_meter(PROGRESS_END); + EOF_err: if (G.content_len != 0) { - bb_simple_perror_msg_and_die("connection closed prematurely"); /* GNU wget says "DATE TIME (NN MB/s) - Connection closed at byte NNN. Retrying." */ + bb_error_msg("connection closed at byte %llu", (unsigned long long)G.transferred); + return 1; /* "partial download" */ } - /* If -c failed, we restart from the beginning, + /* If partial restart failed, we restarted from the beginning, * but we do not truncate file then, we do it only now, at the end. * This lets user to ^C if his 99% complete 10 GB file download * failed to restart *without* losing the almost complete file. */ - { - off_t pos = lseek(G.output_fd, 0, SEEK_CUR); - if (pos != (off_t)-1) { - /* do not truncate if -O- is in use, a user complained about - * "wget -qO- 'http://example.com/empty' >>FILE" truncating FILE. - */ - if (!(option_mask32 & WGET_NO_FTRUNCATE)) - ftruncate(G.output_fd, pos); - } + pos = lseek(G.output_fd, 0, SEEK_CUR); + if (pos != (off_t)-1) { + /* do not truncate if -O- >>FILE is in use, a user complained about + * "wget -O- 'http://host.com/empty' >>FILE" truncating FILE. + */ + if (!(fcntl(G.output_fd, F_GETFL) & O_APPEND)) + ftruncate(G.output_fd, pos); } if (!(option_mask32 & WGET_OPT_QUIET)) { @@ -1092,12 +1107,14 @@ static void NOINLINE retrieve_file_data(FILE *dfp) else fprintf(stderr, "'%s' saved\n", G.fname_out); } + return 0; /* "full download" */ } static void download_one_url(const char *url) { bool use_proxy; /* Use proxies if env vars are set */ int redir_limit; + int retry_cnt = G.tries; len_and_sockaddr *lsa; FILE *sfp; /* socket to web/ftp server */ FILE *dfp; /* socket to ftp server (data) */ @@ -1166,6 +1183,7 @@ static void download_one_url(const char *url) * We are not sure it exists on remote side */ } + restart: redir_limit = 16; resolve_lsa: lsa = xhost2sockaddr(server.host, server.port); @@ -1310,8 +1328,8 @@ static void download_one_url(const char *url) str = G.wget_buf; str = skip_non_whitespace(str); str = skip_whitespace(str); - // FIXME: no error check - // xatou wouldn't work: "200 OK" +// FIXME: no error check +// xatou wouldn't work: "200 OK" status = atoi(str); switch (status) { case 0: @@ -1464,10 +1482,23 @@ However, in real world it was observed that some web servers if (!(option_mask32 & WGET_OPT_SPIDER)) { if (G.output_fd < 0) G.output_fd = xopen(G.fname_out, G.o_flags); - retrieve_file_data(dfp); + if (retrieve_file_data(dfp) != 0) { + /* Partial download */ + if (G.tries == 0 || retry_cnt-- != 0) { + G.beg_range += G.transferred; + goto restart; + } + /* else: -t TRIES exceeded */ +//FIXME: should we abort instead of continuing with next URL? + } if (!(option_mask32 & WGET_OPT_OUTNAME)) { xclose(G.output_fd); G.output_fd = -1; + } else { /* -O FILE receives _all_ URLs concatenated */ + /* + * For "wget -O FILE URL1 URL2": + * remember where URL1 data ends in FILE */ + G.reset_pos += G.beg_range + G.transferred; } } else { if (!(option_mask32 & WGET_OPT_QUIET)) @@ -1509,8 +1540,7 @@ int wget_main(int argc UNUSED_PARAM, char **argv) "user-agent\0" Required_argument "U" IF_FEATURE_WGET_TIMEOUT( "timeout\0" Required_argument "T") - /* Ignored: */ -IF_DESKTOP( "tries\0" Required_argument "t") + "tries\0" Required_argument "t" "header\0" Required_argument "\xff" "post-data\0" Required_argument "\xfe" "spider\0" No_argument "\xfd" @@ -1538,6 +1568,7 @@ IF_DESKTOP( "no-parent\0" No_argument "\xf0") INIT_G(); + G.tries = 20; #if ENABLE_FEATURE_WGET_TIMEOUT G.timeout_seconds = 900; signal(SIGALRM, alarm_handler); @@ -1547,7 +1578,7 @@ IF_DESKTOP( "no-parent\0" No_argument "\xf0") GETOPT32(argv, "^" "cqSO:o:P:Y:U:T:+" - /*ignored:*/ "t:" + "t:+" /*ignored:*/ "n::" /* wget has exactly four -n opts, all of which we can ignore: * -nv --no-verbose: be moderately quiet (-q is full quiet) @@ -1567,7 +1598,7 @@ IF_DESKTOP( "no-parent\0" No_argument "\xf0") , &G.fname_out, &G.fname_log, &G.dir_prefix, &G.proxy_flag, &G.user_agent, IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL), - NULL, /* -t RETRIES */ + &G.tries, /* -t RETRIES */ NULL /* -n[ARG] */ IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist) IF_FEATURE_WGET_LONG_OPTIONS(, &G.post_data) @@ -1622,7 +1653,7 @@ IF_DESKTOP( "no-parent\0" No_argument "\xf0") if (G.fname_out) { /* -O FILE ? */ if (LONE_DASH(G.fname_out)) { /* -O - ? */ G.output_fd = 1; - option_mask32 = (option_mask32 & (~WGET_OPT_CONTINUE)) | WGET_NO_FTRUNCATE; + option_mask32 = (option_mask32 & (~WGET_OPT_CONTINUE)); } /* compat with wget: -O FILE can overwrite */ G.o_flags = O_WRONLY | O_CREAT | O_TRUNC; From vda.linux at googlemail.com Tue Sep 22 23:25:45 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Wed, 23 Sep 2026 01:25:45 +0200 Subject: [git commit] wget: fix progress bar and message placement on unexpected EOF (and retry) Message-ID: <20260922232623.86F259830B@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=bf9ebfe16801323900e4bd3d9907928ac8f609bf branch: https://git.busybox.net/busybox/log/?h=master Correct behavior: "wget: connection closed..." on new line: $ wget http://127.0.0.1/50g -O/dev/null Connecting to 127.0.0.1 (127.0.0.1:80) saving to '/dev/null' null 8% |** | 4531M 0:00:30 ETA wget: connection closed at byte 4751158460 Connecting to 127.0.0.1 (127.0.0.1:80) saving to '/dev/null' null 10% |***_ function old new delta progress_meter 182 195 +13 retrieve_file_data 705 697 -8 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 13/-8) Total: 5 bytes Signed-off-by: Denys Vlasenko --- networking/wget.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/networking/wget.c b/networking/wget.c index 853dde72d..ab4121317 100644 --- a/networking/wget.c +++ b/networking/wget.c @@ -333,6 +333,9 @@ static void progress_meter(int flag) if (flag == PROGRESS_START) bb_progress_init(&G.pmt, G.curfile); + if (flag == PROGRESS_END) + G.pmt.last_update_sec = 0; /* force update (and correct "notty") */ + notty = bb_progress_update(&G.pmt, G.beg_range, G.transferred, @@ -1010,11 +1013,11 @@ static int NOINLINE retrieve_file_data(FILE *dfp) * fread does not distinguish between EOF and error. */ if (errno != EAGAIN) { + progress_meter(PROGRESS_END); if (ferror(dfp)) { - progress_meter(PROGRESS_END); bb_simple_perror_msg(bb_msg_read_error); - G.content_len = 1; /* treat end as error, not EOF */ } + G.content_len = 1; /* treat end as error, not EOF */ goto EOF_err; /* EOF / error */ } From vda.linux at googlemail.com Wed Sep 23 00:07:23 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Wed, 23 Sep 2026 02:07:23 +0200 Subject: [git commit] telnet: remove superfluous bit clearing Message-ID: <20260923000655.6C3A3976A6@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=5cea24cf6c22be00d59f10b388c9faa1f75710de branch: https://git.busybox.net/busybox/log/?h=master function old new delta write_to_net 625 621 -4 Signed-off-by: Denys Vlasenko --- networking/telnet.c | 1 - 1 file changed, 1 deletion(-) diff --git a/networking/telnet.c b/networking/telnet.c index 227a37907..07d1b59a7 100644 --- a/networking/telnet.c +++ b/networking/telnet.c @@ -433,7 +433,6 @@ static void handle_changes_in_options(stdin_to_net_t *conn) log1("C:DO SGA"); put_iac3_IAC_x_y(DO, TELOPT_SGA); G.flags |= FLAGS_SGA_SEEN; // remember we did it - G.changes_seen -= CHANGED_SGA; } G.changes_seen &= ~(CHANGED_ECHO|CHANGED_SGA); From vda.linux at googlemail.com Wed Sep 23 00:23:46 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Wed, 23 Sep 2026 02:23:46 +0200 Subject: [git commit] telnet: make put_iac_subopt_XYZ() more readable Message-ID: <20260923002312.B1EFD9831D@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=c9e11ef57095fa58528692371befdc50cfb01f56 branch: https://git.busybox.net/busybox/log/?h=master Signed-off-by: Denys Vlasenko --- networking/telnet.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/networking/telnet.c b/networking/telnet.c index 07d1b59a7..d1f768019 100644 --- a/networking/telnet.c +++ b/networking/telnet.c @@ -286,21 +286,19 @@ static void put_iac3_IAC_x_y_merged(unsigned wwdd_and_c) put_iac3_IAC_x_y_merged(((wwdd)<<8) + (c)) #if ENABLE_FEATURE_TELNET_TTYPE -static void put_iac_subopt(byte c, const char *str) +static void put_iac_subopt_TTYPE(const char *p) { - put_iac4_x_y_z_t(IAC, SB, c, 0); + put_iac4_x_y_z_t(IAC, SB, TELOPT_TTYPE, 0); - while (*str) { - put_iac_byte_escaped(*str); - str++; - } + while (*p) + put_iac_byte_escaped(*p++); put_iac2_x_y(IAC, SE); } #endif #if ENABLE_FEATURE_TELNET_AUTOLOGIN -static void put_iac_subopt_autologin(const char *p) +static void put_iac_subopt_NEW_ENVIRON(const char *p) { put_iac4_x_y_z_t(IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_IS); put_iac4_x_y_z_t(NEW_ENV_VAR, 'U', 'S', 'E'); /* "USER" */ @@ -490,7 +488,7 @@ static void handle_changes_in_options(stdin_to_net_t *conn) && remaining_free_bytes(conn->size) > 6 + 2 * strlen(G.ttype) ) { log1("C:SB %s '%s'", "TTYPE", G.ttype); - put_iac_subopt(TELOPT_TTYPE, G.ttype); + put_iac_subopt_TTYPE(G.ttype); G.ttype = NULL; // remember we did it G.changes_seen -= CHANGED_TTYPE; } @@ -500,7 +498,7 @@ static void handle_changes_in_options(stdin_to_net_t *conn) && remaining_free_bytes(conn->size) > 12 + 2 * strlen(G.autologin) ) { log1("C:SB %s '%s'", "NEW_ENVIRON", G.autologin); - put_iac_subopt_autologin(G.autologin); + put_iac_subopt_NEW_ENVIRON(G.autologin); G.autologin = NULL; // remember we did it G.changes_seen -= CHANGED_NEW_ENVIRON; } From vda.linux at googlemail.com Wed Sep 23 00:53:30 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Wed, 23 Sep 2026 02:53:30 +0200 Subject: [git commit] *: fix Unicode damage Message-ID: <20260923005234.5EFDD98324@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=a21eaf440d1e86f339d1e1515187240b2e9c613e branch: https://git.busybox.net/busybox/log/?h=master Signed-off-by: Denys Vlasenko --- modutils/modutils-24.c | 2 +- networking/ipcalc.c | 2 +- networking/telnetd.c | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modutils/modutils-24.c b/modutils/modutils-24.c index 37c303f90..290738f21 100644 --- a/modutils/modutils-24.c +++ b/modutils/modutils-24.c @@ -53,7 +53,7 @@ * New implementation contributed by Richard Henderson * Based on original work by Bjorn Ekwall * Restructured (and partly rewritten) by: - * Bj?rn Ekwall February 1999 + * Bj??rn Ekwall February 1999 * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ diff --git a/networking/ipcalc.c b/networking/ipcalc.c index 3ec473c6b..f98ac7537 100644 --- a/networking/ipcalc.c +++ b/networking/ipcalc.c @@ -86,7 +86,7 @@ static unsigned long get_netmask(unsigned long ipaddr) # if ENABLE_FEATURE_IPCALC_FANCY "prefix\0" No_argument "p" // prefix from IP[/prefix] [netmask] "hostname\0" No_argument "h" // hostname from IP - "silent\0" No_argument "s" // don???t ever display error messages + "silent\0" No_argument "s" // don't ever display error messages # endif ; # define GETOPT32 getopt32long diff --git a/networking/telnetd.c b/networking/telnetd.c index 880d3c63b..eca4e5136 100644 --- a/networking/telnetd.c +++ b/networking/telnetd.c @@ -1222,11 +1222,11 @@ static int test_net_to_pty_data_integrity(void) expected_data = xmalloc(TESTDATA_SIZE); /* Process input_data simulating net_to_pty transformations: - * - IAC IAC ??? single IAC - * - IAC SB TELOPT_NAWS [4 bytes] IAC SE ??? stripped - * - Other IAC sequences ??? stripped - * - \r\n ??? \r (skip the \n) - * - \r\0 ??? \r (skip the \0) + * - IAC IAC -> single IAC + * - IAC SB TELOPT_NAWS [4 bytes] IAC SE -> stripped + * - Other IAC sequences -> stripped + * - \r\n -> \r (skip the \n) + * - \r\0 -> \r (skip the \0) */ memset(input_data + input_size, 0, 64); expected_size = 0; @@ -1235,7 +1235,7 @@ static int test_net_to_pty_data_integrity(void) /* Tail of 64 extra zero bytes allows to safely read beyond the end */ if (input_data[i] == IAC) { if (input_data[i + 1] == IAC) { - /* IAC IAC ??? single IAC */ + /* IAC IAC -> single IAC */ expected_data[expected_size++] = IAC; i += 2; continue; @@ -1450,7 +1450,7 @@ static int test_pty_to_net_data_integrity(void) input_data = xzalloc(TESTDATA_SIZE); /* Generate random data - this time we want lots of 0xFF (IAC) bytes - * to test IAC escaping (IAC ??? IAC IAC) + * to test IAC escaping (IAC -> IAC IAC) */ randbits = 0; for (i = 0; i < TESTDATA_SIZE;) { @@ -1483,7 +1483,7 @@ static int test_pty_to_net_data_integrity(void) expected_data = xmalloc(TESTDATA_SIZE * 2); /* Process input_data simulating pty_to_net transformations: - * - IAC (0xFF) ??? IAC IAC (doubled) + * - IAC (0xFF) -> IAC IAC (doubled) * - All other bytes pass through unchanged */ expected_size = 0; From vda.linux at googlemail.com Wed Sep 23 04:41:53 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Wed, 23 Sep 2026 06:41:53 +0200 Subject: [git commit] tr: an escaped dash doesn't indicate a range Message-ID: <20260923044151.889F9959E6@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=0e09d2f8577e35c5888f16b467fc3935afb85a63 branch: https://git.busybox.net/busybox/log/?h=master Commit d683c5c2f1 (tr: support octal ranges) broke the previous behaviour that an escaped dash doesn't indicate a range: '[p\-r]' should match 'p', '-' or 'r', not 'p', 'q' or 'r'. Add a special case to handle this. function old new delta expand 641 657 +16 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/0 up/down: 16/0) Total: 16 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- coreutils/tr.c | 5 +++++ testsuite/tr.tests | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/coreutils/tr.c b/coreutils/tr.c index 8d779d8ea..4d9796f80 100644 --- a/coreutils/tr.c +++ b/coreutils/tr.c @@ -117,6 +117,11 @@ static unsigned expand(char *arg, char **buffer_p) arg++; z = arg; ac = bb_process_escape_sequence(&z); + if (ac == '\\' && *z == '-') { + /* An escaped dash isn't a range, don't fall through */ + buffer[pos++] = *z; + continue; + } arg = (char *)z; arg--; *arg = ac; diff --git a/testsuite/tr.tests b/testsuite/tr.tests index 5cca299ac..ab198d9f8 100755 --- a/testsuite/tr.tests +++ b/testsuite/tr.tests @@ -15,6 +15,10 @@ testing "tr understands 0-9A-F" \ "tr -cd '[0-9A-F]'" \ "19AF" "" "19AFH\n" +testing "tr does not treat [p\\-r] as a range" \ + "tr '[p\\-r]' '+'" \ + "o+q+s+\n" "" "opqrs-\n" + optional FEATURE_TR_CLASSES testing "tr understands [:xdigit:]" \ "tr -cd '[:xdigit:]'" \ From vda.linux at googlemail.com Wed Sep 23 04:41:53 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Wed, 23 Sep 2026 06:41:53 +0200 Subject: [git commit] tr: special-case "\-" before trying to parse it as escape Message-ID: <20260923044151.9820898338@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=d3c8cfa6745f6eba3fc4c5ece36da1d3762fd321 branch: https://git.busybox.net/busybox/log/?h=master Signed-off-by: Denys Vlasenko --- coreutils/tr.c | 27 +++++++++++++-------------- libbb/process_escape_sequence.c | 6 ++++++ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/coreutils/tr.c b/coreutils/tr.c index 4d9796f80..6e8ae3e18 100644 --- a/coreutils/tr.c +++ b/coreutils/tr.c @@ -113,20 +113,20 @@ static unsigned expand(char *arg, char **buffer_p) *buffer_p = buffer = xrealloc(buffer, size); } if (*arg == '\\') { - const char *z; - arg++; - z = arg; - ac = bb_process_escape_sequence(&z); - if (ac == '\\' && *z == '-') { - /* An escaped dash isn't a range, don't fall through */ - buffer[pos++] = *z; + char *z = ++arg; + if (*z == '-') { + /* "\-X" isn't a range, but a literal dash and X */ + buffer[pos++] = '-'; continue; } - arg = (char *)z; - arg--; + ac = bb_process_escape_sequence((void*)&z); + // if unknown seq "\k": ac='\', z->k: store '\' before 'k' + // if known seq "\n": ac='\n', z->past n: store '\n' into 'n' + // if known seq "\041": ac='!', z->past 1: store '!' into '1' + arg = z - 1; *arg = ac; /* - * fall through, there may be a range. + * fall through, there may be a range. Example: "\n-@" * If not, current char will be treated anyway. */ } @@ -139,10 +139,9 @@ static unsigned expand(char *arg, char **buffer_p) i = (unsigned char) *arg; arg += 3; /* skip 0-9 or 0-\ */ if (ac == '\\') { - const char *z; - z = arg; - ac = bb_process_escape_sequence(&z); - arg = (char *)z; + char *z = arg; + ac = bb_process_escape_sequence((void*)&z); + arg = z; } while (i <= ac) /* ok: i is unsigned _int_ */ buffer[pos++] = i++; diff --git a/libbb/process_escape_sequence.c b/libbb/process_escape_sequence.c index 13022b83e..bc0c3b8f8 100644 --- a/libbb/process_escape_sequence.c +++ b/libbb/process_escape_sequence.c @@ -15,6 +15,12 @@ #undef _tolower #define _tolower(X) ((X)|((char) 0x20)) +/* (*ptr) points to string directly after '\'. + * Analyze it, if it's special (n, 003, etc) return + * corresponding char ('\n', 0x03, etc) and advance + * (*ptr) to point after the special part of the string. + * Otherwise: return '\' and do not advance (*ptr). + */ char FAST_FUNC bb_process_escape_sequence(const char **ptr) { const char *q; From vda.linux at googlemail.com Thu Sep 24 00:21:18 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Thu, 24 Sep 2026 02:21:18 +0200 Subject: [git commit] swaplabel: new applet Message-ID: <20260924002152.BE75E95C2A@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=f5c0543fd156eef5ad40e04ae8a68a9f70517317 branch: https://git.busybox.net/busybox/log/?h=master While at it, make get_volume_size_in_bytes() messages more informative; do not write to file unless _have to_ extend it. function old new delta swaplabel_main - 274 +274 .rodata 107344 107413 +69 get_volume_size_in_bytes 221 262 +41 bb_SWAPSPACE2 - 10 +10 applet_names 2875 2885 +10 packed_usage 36298 36307 +9 applet_main 1656 1660 +4 applet_install_loc 207 208 +1 SWAPSPACE2 10 - -10 ------------------------------------------------------------------------------ (add/remove: 3/1 grow/shrink: 6/0 up/down: 418/-10) Total: 408 bytes Signed-off-by: Denys Vlasenko --- include/libbb.h | 4 ++ libbb/get_volsize.c | 35 +++++++++++------ libbb/messages.c | 5 +++ util-linux/mkswap.c | 5 +-- util-linux/swaplabel.c | 83 +++++++++++++++++++++++++++++++++++++++ util-linux/volume_id/linux_swap.c | 2 +- 6 files changed, 116 insertions(+), 18 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index 28e7f5c1f..52b0920a7 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -2485,6 +2485,10 @@ extern const char bb_PATH_root_path[] ALIGN1; /* BB_PATH_ROOT_PATH */ */ #define bb_default_path (bb_PATH_root_path + sizeof("PATH=/sbin:/usr/sbin")) +/* Stored without terminating NUL */ +extern const char bb_SWAPSPACE2[sizeof("SWAPSPACE2")-1]; + + extern const int const_int_0; //extern const int const_int_1; diff --git a/libbb/get_volsize.c b/libbb/get_volsize.c index 087efb3c7..704707887 100644 --- a/libbb/get_volsize.c +++ b/libbb/get_volsize.c @@ -14,35 +14,44 @@ uoff_t FAST_FUNC get_volume_size_in_bytes(int fd, int extend) { uoff_t result; +#define ULL(x) ((unsigned long long)(x)) if (override) { result = XATOOFF(override); if (result >= (uoff_t)(MAXINT(off_t)) / override_units) - bb_simple_error_msg_and_die("image size is too big"); + bb_error_msg_and_die("image size %lld is too big", ULL(result)); result *= override_units; - /* seek past end fails on block devices but works on files */ + if (result == 0) + goto insane; + // Do we need to extend the file? + // Seek past end fails on block devices but works on files! if (lseek(fd, result - 1, SEEK_SET) != (off_t)-1) { - if (extend) - xwrite(fd, "", 1); /* file grows if needed */ + char dummy; + if (extend && safe_read(fd, &dummy, 1) != 1) { + // the file is shorter + xwrite(fd, "", 1); // grow it + } } //else { // bb_error_msg("warning, block device is smaller"); //} } else { - /* more portable than BLKGETSIZE[64] */ + // more portable than BLKGETSIZE[64] result = xlseek(fd, 0, SEEK_END); } xlseek(fd, 0, SEEK_SET); - /* Prevent things like this: - * $ dd if=/dev/zero of=foo count=1 bs=1024 - * $ mkswap foo - * Setting up swapspace version 1, size = 18446744073709548544 bytes - * - * Picked 16k arbitrarily: */ - if (result < 16*1024) - bb_simple_error_msg_and_die("image is too small"); + // Prevent things like this: + // $ dd if=/dev/zero of=foo count=1 bs=1024 + // $ mkswap foo + // Setting up swapspace version 1, size = 18446744073709548544 bytes + // + // Picked 16k arbitrarily: + if (result < 16*1024) { + insane: + bb_error_msg_and_die("image is too small: %lld bytes", ULL(result)); + } return result; } diff --git a/libbb/messages.c b/libbb/messages.c index 6914d5701..a5d5ae9d1 100644 --- a/libbb/messages.c +++ b/libbb/messages.c @@ -33,6 +33,11 @@ const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL; * but I want to save a few bytes here. Check libbb.h before changing! */ const char bb_PATH_root_path[] ALIGN1 = BB_PATH_ROOT_PATH; +#if ENABLE_MKSWAP || ENABLE_SWAPLABEL +/* Stored without terminating NUL */ +const char bb_SWAPSPACE2[sizeof("SWAPSPACE2")-1] ALIGN1 = "SWAPSPACE2"; +#endif + //const int const_int_1 = 1; /* explicitly = 0, otherwise gcc may make it a common variable diff --git a/util-linux/mkswap.c b/util-linux/mkswap.c index 0c2e12951..0886f3b34 100644 --- a/util-linux/mkswap.c +++ b/util-linux/mkswap.c @@ -107,9 +107,6 @@ struct BUG_sizes { char bufsiz1_is_too_small[COMMON_BUFSIZE < (NWORDS * 4) ? -1 : 1]; }; -/* Stored without terminating NUL */ -static const char SWAPSPACE2[sizeof("SWAPSPACE2")-1] ALIGN1 = "SWAPSPACE2"; - int mkswap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mkswap_main(int argc UNUSED_PARAM, char **argv) { @@ -160,7 +157,7 @@ int mkswap_main(int argc UNUSED_PARAM, char **argv) * signature on disk (not in cache) during swapon. */ xwrite(fd, hdr, NWORDS * 4); xlseek(fd, pagesize - 10, SEEK_SET); - xwrite(fd, SWAPSPACE2, 10); + xwrite(fd, bb_SWAPSPACE2, 10); fsync(fd); if (ENABLE_FEATURE_CLEAN_UP) diff --git a/util-linux/swaplabel.c b/util-linux/swaplabel.c new file mode 100644 index 000000000..a93ebdff9 --- /dev/null +++ b/util-linux/swaplabel.c @@ -0,0 +1,83 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 2026 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config SWAPLABEL +//config: bool "swaplabel (1 kb)" +//config: default y +//config: help +//config: swaplabel shows or sets swap's label. + +//applet:IF_SWAPLABEL(APPLET(swaplabel, BB_DIR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_SWAPLABEL) += swaplabel.o + +//usage:#define swaplabel_trivial_usage +//usage: "[-L LBL] BLOCKDEV" +//usage:#define swaplabel_full_usage "\n\n" +//usage: "Show or set swap label on BLOCKDEV\n" +//usage: "\n -L LBL Label" + +#include "libbb.h" +#include "common_bufsiz.h" + +/* from Linux 2.6.23 */ +/* + * Magic header for a swap area. ... Note that the first + * kilobyte is reserved for boot loader or disk label stuff. + */ +struct swap_header_v1 { +/* char bootbits[1024]; Space for disklabel etc. */ + uint32_t version; /* second kbyte, word 0 */ + uint32_t last_page; /* 1 */ + uint32_t nr_badpages; /* 2 */ + uint8_t sws_uuid[16]; /* 3,4,5,6 */ + char sws_volume[16]; /* 7,8,9,10 */ + uint32_t padding[117]; /* 11..127 */ + uint32_t badpages[1]; /* 128 */ + /* total 129 32-bit words in 2nd kilobyte */ +} FIX_ALIASING; + +int swaplabel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int swaplabel_main(int argc UNUSED_PARAM, char **argv) +{ + char buf32[32]; + const char *label = NULL; + int fd; + + // TODO: -U UUID + getopt32(argv, "^" "L:" "\0" "=1"/*exactly one arg*/, &label); + argv += optind; + + fd = xopen(argv[0], label ? O_RDWR : O_RDONLY); + + xlseek(fd, bb_getpagesize() - 10, SEEK_SET); + xread(fd, buf32, 10); + if (memcmp(buf32, bb_SWAPSPACE2, 10) != 0) + bb_error_msg_and_die("%s: not a valid swap partition", argv[0]); + // TODO: also check hdr.version == 1? + + xlseek(fd, 1024 + offsetof(struct swap_header_v1, sws_uuid) + 16 * (!!label), SEEK_SET); + if (label) { + int sz = strnlen(label, 15) + 1; // includes NUL unless full 16 bytes + xwrite(fd, label, sz); + // BTW, writing to active swap partition fails with ETXTBUSY! + } else { + char uuid_string37[37]; + + xread(fd, buf32, 32); + if (buf32[16]) + printf("LABEL: %.16s\n", buf32 + 16); + + // f.e. dfd9c173-be52-4d27-99a5-c34c6c2ff55f + format_uuid_DCE_37_chars(uuid_string37, (uint8_t*)buf32); + printf("UUID: %s\n", uuid_string37); + } + + if (ENABLE_FEATURE_CLEAN_UP) + close(fd); + + return 0; +} diff --git a/util-linux/volume_id/linux_swap.c b/util-linux/volume_id/linux_swap.c index 78b339cf0..f6c0c9d13 100644 --- a/util-linux/volume_id/linux_swap.c +++ b/util-linux/volume_id/linux_swap.c @@ -58,7 +58,7 @@ int FAST_FUNC volume_id_probe_linux_swap(struct volume_id *id /*,uint64_t off*/) goto found; } - if (memcmp(buf, "SWAPSPACE2", 10) == 0 + if (memcmp(buf, bb_SWAPSPACE2, 10) == 0 || memcmp(buf, "S1SUSPEND", 9) == 0 || memcmp(buf, "S2SUSPEND", 9) == 0 || memcmp(buf, "ULSUSPEND", 9) == 0 From vda.linux at googlemail.com Thu Sep 24 09:52:58 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Thu, 24 Sep 2026 11:52:58 +0200 Subject: [git commit] fatlabel: new applet Message-ID: <20260924095502.99C3A9835E@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=30d2839a7c2a0c0efc5a40f0085c2f8785f3873f branch: https://git.busybox.net/busybox/log/?h=master function old new delta fatlabel_main - 392 +392 .rodata 107413 107469 +56 applet_names 2885 2894 +9 applet_main 1660 1664 +4 packed_usage 36307 36304 -3 swaplabel_main 274 270 -4 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 3/2 up/down: 461/-7) Total: 454 bytes Signed-off-by: Denys Vlasenko --- include/libbb.h | 2 + util-linux/fatlabel.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++++ util-linux/mkfs_vfat.c | 10 +- util-linux/swaplabel.c | 3 +- 4 files changed, 266 insertions(+), 7 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index 52b0920a7..43a44e192 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -2609,6 +2609,8 @@ do { \ BUG_wrong_field_size(); \ } while (0) +#define FETCH_LE16(field) \ + (sizeof(field) == 2 ? SWAP_LE16(field) : BUG_wrong_field_size()) #define FETCH_LE32(field) \ (sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size()) diff --git a/util-linux/fatlabel.c b/util-linux/fatlabel.c new file mode 100644 index 000000000..840c4e1f4 --- /dev/null +++ b/util-linux/fatlabel.c @@ -0,0 +1,258 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 2026 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config FATLABEL +//config: bool "fatlabel (1 kb)" +//config: default y +//config: help +//config: fatlabel shows or sets FAT33 label. + +//applet:IF_FATLABEL(APPLET(fatlabel, BB_DIR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_FATLABEL) += fatlabel.o + +//usage:#define fatlabel_trivial_usage +//usage: "BLOCKDEV [LABEL]" +//usage:#define fatlabel_full_usage "\n\n" +//usage: "Show or set FAT32 label (11 bytes at offset 0x47) on BLOCKDEV" + +#include "libbb.h" +#include "common_bufsiz.h" + +#define SECTOR_SIZE 512 + +#define ATTR_VOLUME 8 + +#define NUM_FATS 2 + +// FAT32 filesystem looks like this: +// sector -nn...-1: "hidden" sectors, all sectors before this partition +// (-h hidden-sectors sets it. Useful only for boot loaders, +// they need to know _disk_ offset in order to be able to correctly +// address sectors relative to start of disk) +// sector 0: boot sector +// sector 1: info sector +// sector 2: set aside for boot code which didn't fit into sector 0 +// ...(zero-filled sectors)... +// sector B: backup copy of sector 0 [B set by -b backup-boot-sector] +// sector B+1: backup copy of sector 1 +// sector B+2: backup copy of sector 2 +// ...(zero-filled sectors)... +// sector R: FAT#1 [R set by -R reserved-sectors] +// ...(FAT#1)... +// sector R+fat_size: FAT#2 +// ...(FAT#2)... +// sector R+fat_size*2: cluster #2 +// ...(cluster #2)... +// sector R+fat_size*2+clust_size: cluster #3 +// ...(the rest is filled by clusters till the end)... + +struct msdos_dir_entry { + char name[11]; // 000 name and extension + uint8_t attr; // 00b attribute bits + uint8_t lcase; // 00c case for base and extension + uint8_t ctime_cs; // 00d creation time, centiseconds (0-199) + uint16_t ctime; // 00e creation time + uint16_t cdate; // 010 creation date + uint16_t adate; // 012 last access date + uint16_t starthi; // 014 high 16 bits of cluster in FAT32 + uint16_t time; // 016 time + uint16_t date; // 018 date + uint16_t start; // 01a first cluster + uint32_t size; // 01c file size in bytes +} PACKED; + +/* Example of boot sector's beginning: +0000 eb 58 90 4d 53 57 49 4e 34 2e 31 00 02 08 26 00 |...MSWIN4.1...&.| +0010 02 00 00 00 00 f8 00 00 3f 00 ff 00 3f 00 00 00 |........?...?...| +0020 54 9b d0 00 0d 34 00 00 00 00 00 00 02 00 00 00 |T....4..........| +0030 01 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +0040 80 00 29 71 df 51 e0 4e 4f 20 4e 41 4d 45 20 20 |..)q.Q.NO NAME | +0050 20 20 46 41 54 33 32 20 20 20 33 c9 8e d1 bc f4 | FAT32 3.....| +*/ +struct msdos_volume_info { // (offsets are relative to start of boot sector) + uint8_t drive_number; // 040 BIOS drive number + uint8_t reserved; // 041 unused + uint8_t ext_boot_sign; // 042 0x29 if fields below exist (DOS 3.3+) + uint32_t volume_id32; // 043 volume ID number + char volume_label[11];// 047 volume label + char fs_type[8]; // 052 typically "FATnn" +} PACKED; // 05a end. Total size 26 (0x1a) bytes + +struct msdos_boot_sector { + // We use strcpy to fill both, and gcc-4.4.x complains if they are separate + char boot_jump_and_sys_id[3+8]; //000 short or near jump instruction + /*char system_id[8];*/ // 003 name - can be used to special case partition manager volumes + uint16_t bytes_per_sect; // 00b bytes per logical sector + uint8_t sect_per_clust; // 00d sectors/cluster + uint16_t reserved_sect; // 00e reserved sectors (sector offset of 1st FAT relative to volume start) + uint8_t fats; // 010 number of FATs + uint16_t dir_entries; // 011 root directory entries + uint16_t volume_size_sect; // 013 volume size in sectors + uint8_t media_byte; // 015 media code + uint16_t fat16_sect_per_fat; // 016 sectors/FAT, must be 0 for FAT32 + uint16_t sect_per_track; // 018 sectors per track + uint16_t heads; // 01a number of heads + uint32_t hidden; // 01c hidden sectors (sector offset of volume within physical disk) + uint32_t fat32_volume_size_sect; // 020 volume size in sectors (if volume_size_sect == 0) + uint32_t fat32_sect_per_fat; // 024 sectors/FAT + uint16_t fat32_flags; // 028 bit 8: fat mirroring, low 4: active fat + uint8_t fat32_version[2]; // 02a major, minor filesystem version (I see 0,0) + uint32_t fat32_root_cluster; // 02c first cluster in root directory + uint16_t fat32_info_sector; // 030 filesystem info sector (usually 1) + uint16_t fat32_backup_boot; // 032 backup boot sector (usually 6) + uint32_t reserved2[3]; // 034 unused + struct msdos_volume_info vi; // 040 + char boot_code[0x200 - 0x5a - 2]; // 05a +#define BOOT_SIGN 0xAA55 + uint16_t boot_sign; // 1fe +} PACKED; + +#define FAT_FSINFO_SIG1 0x41615252 +#define FAT_FSINFO_SIG2 0x61417272 +struct fat32_fsinfo { + uint32_t signature1; // 0x52,0x52,0x41,0x61, "RRaA" + uint32_t reserved1[128 - 8]; + uint32_t signature2; // 0x72,0x72,0x61,0x41, "rrAa" + uint32_t free_clusters; // free cluster count. -1 if unknown + uint32_t next_cluster; // most recently allocated cluster + uint32_t reserved2[3]; + uint16_t reserved3; // 1fc + uint16_t boot_sign; // 1fe +} PACKED; + +struct bug_check { + char BUG1[sizeof(struct msdos_dir_entry ) == 0x20 ? 1 : -1]; + char BUG2[sizeof(struct msdos_volume_info) == 0x1a ? 1 : -1]; + char BUG3[sizeof(struct msdos_boot_sector) == 0x200 ? 1 : -1]; + char BUG4[sizeof(struct fat32_fsinfo ) == 0x200 ? 1 : -1]; +}; + +// compat: dosfstools-4.2 +// $ fatlabel --version +// fatlabel 4.2 (2021-01-31) +// $ fatlabel --help +// Usage: fatlabel [OPTIONS] DEVICE [NEW] +// Change the FAT filesystem label or serial on DEVICE to NEW or display the +// existing label or serial if NEW is not given. +// Options: +// -i, --volume-id Work on serial number instead of label +// -r, --reset Remove label or generate new serial number +// -c N, --codepage=N use DOS codepage N to encode/decode label (default: 850) +// -V, --version Show version number and terminate +// -h, --help Print this message and terminate +static int bad_fat32(const struct msdos_boot_sector *boot_blk) +{ + uint16_t bytes_per_sect16; + + move_from_unaligned16(bytes_per_sect16, &boot_blk->bytes_per_sect); + return ((uint8_t)(boot_blk->fats - 1) > 1 // 0 or >2 + || boot_blk->fat16_sect_per_fat != 0 + // maybe also check boot_blk->dir_entries == 0? It's only for FAT16/12 + || boot_blk->vi.ext_boot_sign != 0x29 + || boot_blk->boot_sign != SWAP_LE16(BOOT_SIGN) + || bytes_per_sect16 != SWAP_LE16(SECTOR_SIZE) + || boot_blk->fat32_info_sector != SWAP_LE16(0x0001) + //WRONG: || boot_blk->fat32_backup_boot != SWAP_LE16(0x0006) + ); +} + +int fatlabel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int fatlabel_main(int argc UNUSED_PARAM, char **argv) +{ + char sector1[1 * SECTOR_SIZE]; + char sector2[1 * SECTOR_SIZE]; + struct msdos_boot_sector *boot_blk = (void*)sector1; + unsigned backup_boot_sect; + int fd; + + getopt32(argv, "^" "\0" "-1?2"/* 1 or 2 arg2*/); + argv += optind; + + fd = xopen(argv[0], argv[1] ? O_RDWR : O_RDONLY); + + xread(fd, sector1, sizeof(sector1)); + backup_boot_sect = FETCH_LE16(boot_blk->fat32_backup_boot); +#if 0 + fprintf(stderr, + "0x003: sys_id:'%.8s'\n" + "0x00b: bytes_per_sect: 0x%04x\n" // 0 + "0x00d: sect_per_clust: 0x%02x\n" // 0 + "0x010: fats: 0x%02x\n" // 1 or 2 + "0x011: dir_entries: 0x%04x\n" // always 0 for FAT32? + "0x015: media_byte: 0x%02x\n" // 0xf8 for "hard disk" + "0x016: fat16_sect_per_fat: 0x%04x\n" // 0 + "0x028: fat32_flags: 0x%04x\n" // 0,0 + "0x02a: fat32_version: 0x%02x,0x%02x\n" // 0,0 + "0x030: fat32_info_sector: 0x%04x\n" // always 1? + "0x032: fat32_backup_boot: 0x%04x\n" // usually 6 by microsoft, but our own mkfs.vfat uses 3 + "0x041: mounted: 0x%02x\n" // 0/1 + "0x042: ext_boot_sign: 0x%02x\n" // 0x29 + "0x043: volume_id32: 0x%08x\n" + "0x047: volume_label: '%.11s'\n" + "0x052: fs_type: '%.8s'\n" + "0x1fe: boot_sign: 0x%02x,0x%02x\n" // 0x55,0xaa + , 3 + boot_blk->boot_jump_and_sys_id + , FETCH_LE16(boot_blk->bytes_per_sect) // FIXME: unaligned fetch + , boot_blk->sect_per_clust + , boot_blk->fats + , FETCH_LE16(boot_blk->dir_entries) // FIXME: unaligned fetch + , boot_blk->media_byte + , FETCH_LE16(boot_blk->fat16_sect_per_fat) + , FETCH_LE16(boot_blk->fat32_flags) + , boot_blk->fat32_version[0], boot_blk->fat32_version[1] + , FETCH_LE16(boot_blk->fat32_info_sector) + , backup_boot_sect + , boot_blk->vi.reserved // Linux uses this as "mounted now" bit, some WinNT too? + , boot_blk->vi.ext_boot_sign + , FETCH_LE32(boot_blk->vi.volume_id32) // FIXME: unaligned fetch + , boot_blk->vi.volume_label + , boot_blk->vi.fs_type + , ((uint8_t*)&boot_blk->boot_sign)[0] + , ((uint8_t*)&boot_blk->boot_sign)[1] + ); +#endif + if (bad_fat32(boot_blk)) + bb_error_msg_and_die("'%s' is not FAT32", argv[0]); + + // backup_boot_sect can be 0 or 0xffff for 'disabled'. + // 1 is almost certainly bogus (we require that fat32_info_sector is 1). + // Let's also not accept suspiciously large values. + if (backup_boot_sect > 1 && backup_boot_sect < 0x100) { + xlseek(fd, backup_boot_sect * SECTOR_SIZE, SEEK_SET); + xread(fd, sector2, sizeof(sector2)); + if (boot_blk->vi.reserved == 1) // "mounted now"? + boot_blk->vi.reserved = 0; + if (memcmp(sector1, sector2, sizeof(sector1)) != 0) + bb_error_msg_and_die("'%s' backup sector %d mismatch", argv[0], backup_boot_sect); + } else { + backup_boot_sect = 0; + } + + if (argv[1]) { + // dosfstools-4.2 warns: + // "fatlabel: warning - lowercase labels might not work properly on some systems" + sprintf(boot_blk->vi.volume_label, "%-11.11s", argv[1]); + xlseek(fd, 0x047, SEEK_SET); + xwrite(fd, boot_blk->vi.volume_label, 11); + // Redundant is backup_boot_sect == 0, but no harm done: + xlseek(fd, 0x047 + backup_boot_sect * SECTOR_SIZE, SEEK_SET); + xwrite(fd, boot_blk->vi.volume_label, 11); + } else { + // Mimic dosfstools-4.2 + // But do not special-case "NO NAME" label as 'label not set'. + int sz = strnlen(boot_blk->vi.volume_label, 11); + while (sz != 0 && boot_blk->vi.volume_label[sz - 1] == ' ') + sz--; + if (sz != 0) + printf("%.*s\n", sz, boot_blk->vi.volume_label); + } + + if (ENABLE_FEATURE_CLEAN_UP) + close(fd); + + fflush_stdout_and_exit_SUCCESS(); +} diff --git a/util-linux/mkfs_vfat.c b/util-linux/mkfs_vfat.c index 7ae4fd520..8353e2831 100644 --- a/util-linux/mkfs_vfat.c +++ b/util-linux/mkfs_vfat.c @@ -142,7 +142,7 @@ struct msdos_boot_sector { uint16_t dir_entries; /* 011 root directory entries */ uint16_t volume_size_sect; /* 013 volume size in sectors */ uint8_t media_byte; /* 015 media code */ - uint16_t sect_per_fat; /* 016 sectors/FAT */ + uint16_t fat16_sect_per_fat; /* 016 sectors/FAT, must be 0 for FAT32 */ uint16_t sect_per_track; /* 018 sectors per track */ uint16_t heads; /* 01a number of heads */ uint32_t hidden; /* 01c hidden sectors (sector offset of volume within physical disk) */ @@ -489,7 +489,7 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv) strcpy(boot_blk->boot_jump_and_sys_id, "\xeb\x58\x90" "mkdosfs"); STORE_LE(boot_blk->bytes_per_sect, bytes_per_sect); STORE_LE(boot_blk->sect_per_clust, sect_per_clust); - // cast in needed on big endian to suppress a warning + // cast is needed on big endian to suppress a warning STORE_LE(boot_blk->reserved_sect, (uint16_t)reserved_sect); STORE_LE(boot_blk->fats, 2); //STORE_LE(boot_blk->dir_entries, 0); // for FAT32, stays 0 @@ -497,10 +497,10 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv) STORE_LE(boot_blk->volume_size_sect, volume_size_sect); STORE_LE(boot_blk->media_byte, media_byte); // wrong: this would make Linux think that it's fat12/16: - //if (sect_per_fat <= 0xffff) - // STORE_LE(boot_blk->sect_per_fat, sect_per_fat); + //if (fat16_sect_per_fat <= 0xffff) + // STORE_LE(boot_blk->fat16_sect_per_fat, sect_per_fat); // works: - //STORE_LE(boot_blk->sect_per_fat, 0); + //STORE_LE(boot_blk->fat16_sect_per_fat, 0); STORE_LE(boot_blk->sect_per_track, sect_per_track); STORE_LE(boot_blk->heads, heads); //STORE_LE(boot_blk->hidden, 0); diff --git a/util-linux/swaplabel.c b/util-linux/swaplabel.c index a93ebdff9..8c15b0c69 100644 --- a/util-linux/swaplabel.c +++ b/util-linux/swaplabel.c @@ -21,7 +21,6 @@ //usage: "\n -L LBL Label" #include "libbb.h" -#include "common_bufsiz.h" /* from Linux 2.6.23 */ /* @@ -79,5 +78,5 @@ int swaplabel_main(int argc UNUSED_PARAM, char **argv) if (ENABLE_FEATURE_CLEAN_UP) close(fd); - return 0; + fflush_stdout_and_exit_SUCCESS(); } From vda.linux at googlemail.com Thu Sep 24 14:20:36 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Thu, 24 Sep 2026 16:20:36 +0200 Subject: [git commit] volume_id: add FAST_FUNCs Message-ID: <20260924145715.DBC659836F@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=13562b93512795876aa280e2ee4e2f895322c47d branch: https://git.busybox.net/busybox/log/?h=master function old new delta volume_id_set_unicode16 167 181 +14 volume_id_probe_btrfs 131 139 +8 volume_id_probe_vfat 824 830 +6 volume_id_probe_sysv 188 193 +5 volume_id_probe_romfs 97 100 +3 volume_id_probe_iso9660 315 316 +1 volume_id_probe_nilfs 97 96 -1 volume_id_probe_lfs 59 58 -1 volume_id_probe_ext 122 121 -1 volume_id_probe_erofs 86 85 -1 volume_id_probe_reiserfs 243 241 -2 volume_id_probe_linux_raid 128 126 -2 volume_id_free_buffer 67 65 -2 volume_id_probe_xfs 98 95 -3 volume_id_probe_ocfs2 107 104 -3 volume_id_probe_linux_swap 248 245 -3 volume_id_probe_jfs 107 104 -3 volume_id_probe_bcache 104 101 -3 volume_id_set_label_string 81 77 -4 volume_id_probe_ubifs 64 60 -4 volume_id_probe_squashfs 83 79 -4 volume_id_probe_minix 82 78 -4 volume_id_probe_f2fs 104 100 -4 volume_id_probe_cramfs 64 60 -4 volume_id_get_buffer 372 368 -4 volume_id_probe_luks 89 83 -6 volume_id_probe_udf 538 531 -7 volume_id_probe_exfat 282 275 -7 volume_id_set_label_unicode16 31 23 -8 volume_id_probe_ntfs 285 277 -8 volume_id_probe_all 123 113 -10 volume_id_set_uuid 178 166 -12 volume_id_probe_hfs_hfsplus 494 470 -24 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 6/27 up/down: 37/-135) Total: -98 bytes Signed-off-by: Denys Vlasenko --- util-linux/volume_id/util.c | 18 +++++++++--------- util-linux/volume_id/volume_id_internal.h | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/util-linux/volume_id/util.c b/util-linux/volume_id/util.c index a2bee28ab..57215547a 100644 --- a/util-linux/volume_id/util.c +++ b/util-linux/volume_id/util.c @@ -20,7 +20,7 @@ #include "volume_id_internal.h" -void volume_id_set_unicode16(char *str, size_t len, const uint8_t *buf, enum endian endianess, size_t count) +void FAST_FUNC volume_id_set_unicode16(char *str, size_t len, const uint8_t *buf, enum endian endianess, size_t count) { unsigned i, j; unsigned c; @@ -82,19 +82,19 @@ static const char *usage_to_string(enum volume_id_usage usage_id) return NULL; } -void volume_id_set_usage_part(struct volume_id_partition *part, enum volume_id_usage usage_id) +void FAST_FUNC volume_id_set_usage_part(struct volume_id_partition *part, enum volume_id_usage usage_id) { part->usage_id = usage_id; part->usage = usage_to_string(usage_id); } -void volume_id_set_usage(struct volume_id *id, enum volume_id_usage usage_id) +void FAST_FUNC volume_id_set_usage(struct volume_id *id, enum volume_id_usage usage_id) { id->usage_id = usage_id; id->usage = usage_to_string(usage_id); } -void volume_id_set_label_raw(struct volume_id *id, const uint8_t *buf, size_t count) +void FAST_FUNC volume_id_set_label_raw(struct volume_id *id, const uint8_t *buf, size_t count) { memcpy(id->label_raw, buf, count); id->label_raw_len = count; @@ -112,7 +112,7 @@ static size_t strnlen(const char *s, size_t maxlen) } #endif -void volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t count) +void FAST_FUNC volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t count) { unsigned i; @@ -131,12 +131,12 @@ void volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t id->label[i+1] = '\0'; } -void volume_id_set_label_unicode16(struct volume_id *id, const uint8_t *buf, enum endian endianess, size_t count) +void FAST_FUNC volume_id_set_label_unicode16(struct volume_id *id, const uint8_t *buf, enum endian endianess, size_t count) { volume_id_set_unicode16(id->label, sizeof(id->label), buf, endianess, count); } -void volume_id_set_uuid(struct volume_id *id, const uint8_t *buf, enum uuid_format format) +void FAST_FUNC volume_id_set_uuid(struct volume_id *id, const uint8_t *buf, enum uuid_format format) { unsigned i; unsigned count = (format == UUID_DCE_STRING ? VOLUME_ID_UUID_SIZE : 4 << format); @@ -174,7 +174,7 @@ set: /* Do not use xlseek here. With it, single corrupted filesystem * may result in attempt to seek past device -> exit. * It's better to ignore such fs and continue. */ -void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len) +void* FAST_FUNC volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len) { uint8_t *dst; unsigned small_off; @@ -265,7 +265,7 @@ void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len) return dst + small_off; } -void volume_id_free_buffer(struct volume_id *id) +void FAST_FUNC volume_id_free_buffer(struct volume_id *id) { free(id->sbbuf); id->sbbuf = NULL; diff --git a/util-linux/volume_id/volume_id_internal.h b/util-linux/volume_id/volume_id_internal.h index 39699ef72..1f6540ae4 100644 --- a/util-linux/volume_id/volume_id_internal.h +++ b/util-linux/volume_id/volume_id_internal.h @@ -146,15 +146,15 @@ enum endian { BE = 1 }; -void volume_id_set_unicode16(char *str, size_t len, const uint8_t *buf, enum endian endianess, size_t count); -//void volume_id_set_usage(struct volume_id *id, enum volume_id_usage usage_id); -//void volume_id_set_usage_part(struct volume_id_partition *part, enum volume_id_usage usage_id); -//void volume_id_set_label_raw(struct volume_id *id, const uint8_t *buf, size_t count); -void volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t count); -void volume_id_set_label_unicode16(struct volume_id *id, const uint8_t *buf, enum endian endianess, size_t count); -void volume_id_set_uuid(struct volume_id *id, const uint8_t *buf, enum uuid_format format); -void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len); -void volume_id_free_buffer(struct volume_id *id); +void FAST_FUNC volume_id_set_unicode16(char *str, size_t len, const uint8_t *buf, enum endian endianess, size_t count); +//void FAST_FUNC volume_id_set_usage(struct volume_id *id, enum volume_id_usage usage_id); +//void FAST_FUNC volume_id_set_usage_part(struct volume_id_partition *part, enum volume_id_usage usage_id); +//void FAST_FUNC volume_id_set_label_raw(struct volume_id *id, const uint8_t *buf, size_t count); +void FAST_FUNC volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t count); +void FAST_FUNC volume_id_set_label_unicode16(struct volume_id *id, const uint8_t *buf, enum endian endianess, size_t count); +void FAST_FUNC volume_id_set_uuid(struct volume_id *id, const uint8_t *buf, enum uuid_format format); +void* FAST_FUNC volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len); +void FAST_FUNC volume_id_free_buffer(struct volume_id *id); /* Probe routines */ From vda.linux at googlemail.com Sat Sep 26 17:45:56 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sat, 26 Sep 2026 19:45:56 +0200 Subject: [git commit] nc: show connect() errors always, not only with -v or -vv Message-ID: <20260926174517.16BB68E7C1@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=69dd61ccbff81fdce7595d37a6acdfeef34e9cd4 branch: https://git.busybox.net/busybox/log/?h=master function old new delta display 1485 1481 -4 nc_main 1018 992 -26 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-30) Total: -30 bytes Signed-off-by: Denys Vlasenko --- networking/nc_bloaty.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/networking/nc_bloaty.c b/networking/nc_bloaty.c index d35c9e140..e61758946 100644 --- a/networking/nc_bloaty.c +++ b/networking/nc_bloaty.c @@ -747,6 +747,11 @@ int nc_main(int argc UNUSED_PARAM, char **argv) int x; unsigned cnt_l = 0; unsigned o_lport = 0; + //bbox: always Single=1 + //nc-1.10 allows "nc HOST PORT1 PORT2..." + //and "nc HOST PORT1-PORT2..." forms, in which cases + //it unsets this flag: + const int Single = 1; INIT_G(); @@ -912,7 +917,7 @@ int nc_main(int argc UNUSED_PARAM, char **argv) x = 1; /* exit status */ /* if we're scanning at a "one -v" verbosity level, don't print refusals. Give it another -v if you want to see everything. */ - if (o_verbose > 1 || (o_verbose && errno != ECONNREFUSED)) + if (Single || (o_verbose > 1) || (errno != ECONNREFUSED)) bb_perror_msg("%s (%s)", argv[0], themdotted); } } From vda.linux at googlemail.com Sat Sep 26 17:52:10 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sat, 26 Sep 2026 19:52:10 +0200 Subject: [git commit] *: users of get_volume_size_in_bytes() should open their file O_RDWR Message-ID: <20260926175136.62EA98E11D@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=274a087cdc6f2f003c419a129ca465967aad94bb branch: https://git.busybox.net/busybox/log/?h=master Signed-off-by: Denys Vlasenko --- libbb/get_volsize.c | 1 + util-linux/mkfs_ext2.c | 2 +- util-linux/mkfs_reiser.c | 2 +- util-linux/mkswap.c | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libbb/get_volsize.c b/libbb/get_volsize.c index 704707887..e64d51889 100644 --- a/libbb/get_volsize.c +++ b/libbb/get_volsize.c @@ -27,6 +27,7 @@ uoff_t FAST_FUNC get_volume_size_in_bytes(int fd, // Seek past end fails on block devices but works on files! if (lseek(fd, result - 1, SEEK_SET) != (off_t)-1) { char dummy; +//NB: here, fd needs a O_RDWR open, not O_WRONLY! if (extend && safe_read(fd, &dummy, 1) != 1) { // the file is shorter xwrite(fd, "", 1); // grow it diff --git a/util-linux/mkfs_ext2.c b/util-linux/mkfs_ext2.c index fcf374b2d..94832bf44 100644 --- a/util-linux/mkfs_ext2.c +++ b/util-linux/mkfs_ext2.c @@ -255,7 +255,7 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv) argv += optind; // argv[0] -- device // open the device, check the device is a block device - xmove_fd(xopen(argv[0], O_WRONLY), fd); + xmove_fd(xopen(argv[0], O_RDWR), fd); xfstat(fd, &st, argv[0]); if (!S_ISBLK(st.st_mode) && !(option_mask32 & OPT_F)) bb_error_msg_and_die("%s: not a block device", argv[0]); diff --git a/util-linux/mkfs_reiser.c b/util-linux/mkfs_reiser.c index 44a743147..c55eef2dc 100644 --- a/util-linux/mkfs_reiser.c +++ b/util-linux/mkfs_reiser.c @@ -168,7 +168,7 @@ int mkfs_reiser_main(int argc UNUSED_PARAM, char **argv) argv += optind; // argv[0] -- device // check the device is a block device - fd = xopen(argv[0], O_WRONLY | O_EXCL); + fd = xopen(argv[0], O_RDWR | O_EXCL); xfstat(fd, &st, argv[0]); if (!S_ISBLK(st.st_mode) && !(option_mask32 & OPT_f)) bb_error_msg_and_die("%s: not a block device", argv[0]); diff --git a/util-linux/mkswap.c b/util-linux/mkswap.c index 0886f3b34..4c62d9c56 100644 --- a/util-linux/mkswap.c +++ b/util-linux/mkswap.c @@ -121,7 +121,7 @@ int mkswap_main(int argc UNUSED_PARAM, char **argv) getopt32(argv, "^" "L:" "\0" "-1"/*at least one arg*/, &label); argv += optind; - fd = xopen(argv[0], O_WRONLY); + fd = xopen(argv[0], O_RDWR); /* Figure out how big the device is */ len = get_volume_size_in_bytes(fd, argv[1], 1024, /*extend:*/ 1); From vda.linux at googlemail.com Sun Sep 27 14:38:02 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sun, 27 Sep 2026 16:38:02 +0200 Subject: [git commit] fdisk: remove SUN and SGI labels support - obsolete (20+ years) Message-ID: <20260927143733.869538E49A@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=e1aeba03f42dfe1fb023fbf15d3977f4d1fb9480 branch: https://git.busybox.net/busybox/log/?h=master function old new delta static.list_table - 775 +775 packed_usage 36304 36323 +19 read_int 434 450 +16 fdisk_swap32 3 - -3 open_list_and_close 431 419 -12 verify 742 726 -16 partition_type 49 28 -21 get_partition 100 73 -27 menu 293 262 -31 read_maybe_empty 38 - -38 verify_sun_cmp 39 - -39 get_boot 1532 1477 -55 sun_sys_types 56 - -56 write_table 251 159 -92 delete_partition 432 334 -98 set_sun_partition 100 - -100 get_geometry 538 437 -101 print_buffer 102 - -102 fetch_sun 207 - -207 check_sun_label 208 - -208 sun_autoconfigure_scsi 395 - -395 verify_sun 447 - -447 sun_drives 456 - -456 new_partition 1066 - -1066 list_table 1370 - -1370 fdisk_main 4622 2543 -2079 .rodata 107469 104770 -2699 ------------------------------------------------------------------------------ (add/remove: 1/13 grow/shrink: 2/11 up/down: 810/-9718) Total: -8908 bytes Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 452 +++++-------------------- util-linux/fdisk_sgi.c | 889 ------------------------------------------------- util-linux/fdisk_sun.c | 739 ---------------------------------------- 3 files changed, 75 insertions(+), 2005 deletions(-) Patch is too large, so refusing to show it From vda.linux at googlemail.com Sun Sep 27 14:46:13 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sun, 27 Sep 2026 16:46:13 +0200 Subject: [git commit] fdisk: remove vestiges of SUN and SGI labels support Message-ID: <20260927170232.B214E954B5@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=946aba349c4b7b793b21daf842600bc365ef5513 branch: https://git.busybox.net/busybox/log/?h=master function old new delta menu 262 231 -31 .rodata 104770 104735 -35 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-66) Total: -66 bytes Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 41 +++++------------------------------------ 1 file changed, 5 insertions(+), 36 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 571fa5450..6648a3891 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -136,7 +136,7 @@ typedef unsigned long long ullong; #define DEFAULT_SECTOR_SIZE 512 #define DEFAULT_SECTOR_SIZE_STR "512" #define MAX_SECTOR_SIZE 2048 -#define SECTOR_SIZE 512 /* still used in osf/sgi/sun code */ +#define SECTOR_SIZE 512 /* still used in OSF code */ #define MAXIMUM_PARTS 60 #define ACTIVE_FLAG 0x80 @@ -179,8 +179,6 @@ enum { #define DISPLAY_IN_CYL_UNITS (!(option_mask32 & OPT_u)) #define TOGGLE_DISPLAY_IN_CYL_UNITS (option_mask32 ^= OPT_u) -/* TODO: just #if ENABLE_FEATURE_FDISK_WRITABLE */ -/* (currently fdisk_sun/sgi.c do not have proper WRITABLE #ifs) */ #if ENABLE_FEATURE_FDISK_WRITABLE static const char msg_building_new_label[] ALIGN1 = "Building a new %s. Changes will remain in memory only,\n" @@ -742,35 +740,6 @@ write_sector(sector_t secno, const void *buf) #include "fdisk_aix.c" -struct sun_partition { - unsigned char info[128]; /* Informative text string */ - unsigned char spare0[14]; - struct sun_info { - unsigned char spare1; - unsigned char id; - unsigned char spare2; - unsigned char flags; - } infos[8]; - unsigned char spare1[246]; /* Boot information etc. */ - unsigned short rspeed; /* Disk rotational speed */ - unsigned short pcylcount; /* Physical cylinder count */ - unsigned short sparecyl; /* extra sects per cylinder */ - unsigned char spare2[4]; /* More magic... */ - unsigned short ilfact; /* Interleave factor */ - unsigned short ncyl; /* Data cylinder count */ - unsigned short nacyl; /* Alt. cylinder count */ - unsigned short ntrks; /* Tracks per cylinder */ - unsigned short nsect; /* Sectors per track */ - unsigned char spare3[4]; /* Even more magic... */ - struct sun_partinfo { - uint32_t start_cylinder; - uint32_t num_sectors; - } partitions[8]; - unsigned short magic; /* Magic number */ - unsigned short csum; /* Label xor'd checksum */ -} FIX_ALIASING; -typedef struct sun_partition sun_partition; -#define sunlabel ((sun_partition *)MBRbuffer) STATIC_OSF void bsd_select(void); STATIC_OSF void xbsd_print_disklabel(int); #include "fdisk_osf.c" @@ -851,12 +820,12 @@ menu(void) if (LABEL_IS_AIX) { puts("o\tcreate a new empty DOS partition table"); puts("q\tquit without saving changes"); - puts("s\tcreate a new empty Sun disklabel"); /* sun */ + //deleted: puts("s\tcreate a new empty Sun disklabel"); /* sun */ } else if (LABEL_IS_GPT) { puts("o\tcreate a new empty DOS partition table"); puts("p\tprint the partition table"); puts("q\tquit without saving changes"); - puts("s\tcreate a new empty Sun disklabel"); /* sun */ + //deleted: puts("s\tcreate a new empty Sun disklabel"); /* sun */ } else { puts("a\ttoggle a bootable flag"); puts("b\tedit bsd disklabel"); @@ -867,7 +836,7 @@ menu(void) puts("o\tcreate a new empty DOS partition table"); puts("p\tprint the partition table"); puts("q\tquit without saving changes"); - puts("s\tcreate a new empty Sun disklabel"); /* sun */ + //deleted: puts("s\tcreate a new empty Sun disklabel"); /* sun */ puts("t\tchange a partition's system id"); puts("u\tchange display/entry units"); puts("v\tverify the partition table"); @@ -2094,7 +2063,7 @@ list_table(int xtra) /* Is partition table in disk order? It need not be, but... */ /* partition table entries are not checked for correct order - * if this is a sgi, sun or aix labeled disk... */ + * if this is an AIX labeled disk... */ if (LABEL_IS_DOS && wrong_p_order(NULL)) { /* FIXME */ puts("\nPartition table entries are not in disk order"); From vda.linux at googlemail.com Sun Sep 27 16:02:02 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sun, 27 Sep 2026 18:02:02 +0200 Subject: [git commit] fdisk: remove get_boot() -> create_doslabel() -> get_boot() recursion Message-ID: <20260927170232.C05AB954BE@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=fbfc949b26f10093ecd5d996fbc26271dcb9c81f branch: https://git.busybox.net/busybox/log/?h=master It works, but get_boot() [table is bad] -> create_doslabel() -> get_boot(CREATE_EMPTY_DOS) is a mind bender function old new delta dos_reset_primary_partition_info - 73 +73 get_existing_partition 113 110 -3 fdisk_main 2543 2537 -6 create_doslabel 108 99 -9 get_boot 1477 1406 -71 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 0/4 up/down: 73/-89) Total: -16 bytes Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 127 +++++++++++++++++++++-------------------------------- 1 file changed, 49 insertions(+), 78 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 6648a3891..09b23b5b1 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -260,7 +260,7 @@ enum label_type { #define STATIC_GPT extern #endif -enum action { OPEN_MAIN, TRY_ONLY, CREATE_EMPTY_DOS }; +enum action { OPEN_MAIN, TRY_ONLY }; static void update_units(void); #if ENABLE_FEATURE_FDISK_WRITABLE @@ -1145,9 +1145,8 @@ create_doslabel(void) printf(msg_building_new_label, "DOS disklabel"); current_label_type = LABEL_DOS; -#if ENABLE_FEATURE_OSF_LABEL - possibly_osf_label = 0; -#endif + IF_FEATURE_OSF_LABEL(possibly_osf_label = 0;) + g_partitions = 4; memset(&MBRbuffer[510 - 4*16], 0, 4*16); @@ -1155,7 +1154,6 @@ create_doslabel(void) extended_offset = 0; set_all_unchanged(); set_changed(0); - get_boot(CREATE_EMPTY_DOS); } #endif @@ -1248,34 +1246,10 @@ get_geometry(void) g_cylinders = user_cylinders; } -/* - * Opens disk_device and optionally reads MBR. - * If what == OPEN_MAIN: - * Open device, read MBR. Abort program on short read. Create empty - * disklabel if the on-disk structure is invalid (WRITABLE mode). - * If what == TRY_ONLY: - * Open device, read MBR. Return an error if anything is out of place. - * Do not create an empty disklabel. This is used for the "list" - * operations: "fdisk -l /dev/sda" and "fdisk -l" (all devices). - * If what == CREATE_EMPTY_*: - * This means that get_boot() was called recursively from create_*label(). - * Do not re-open the device; just set up the ptes array and print - * geometry warnings. - * - * Returns: - * -1: no 0xaa55 flag present (possibly entire disk BSD) - * 0: found or created label - * 1: I/O error - */ -#if ENABLE_FEATURE_FDISK_WRITABLE -static int get_boot(enum action what) -#else -static int get_boot(void) -#define get_boot(what) get_boot() -#endif +static void +dos_reset_primary_partition_info(void) { - int i, fd; - + int i; g_partitions = 4; for (i = 0; i < 4; i++) { struct pte *pe = &ptes[i]; @@ -1283,22 +1257,35 @@ static int get_boot(void) pe->ext_pointer = NULL; pe->offset_from_dev_start = 0; pe->sectorbuffer = MBRbuffer; + IF_FEATURE_FDISK_WRITABLE(pe->changed = 0;) + } +} + +// Opens disk_device and optionally reads MBR. +// If what == OPEN_MAIN: +// Open device, read MBR. Abort program on short read. Create empty +// disklabel if the on-disk structure is invalid (WRITABLE mode). +// If what == TRY_ONLY: +// Open device, read MBR. Return an error if anything is out of place. +// Do not create an empty disklabel. This is used for the "list" +// operations: "fdisk -l /dev/sda" and "fdisk -l" (all devices). +// Returns: +// -1: no 0xaa55 flag present (possibly entire disk BSD) +// 0: found or created label +// 1: I/O error, or can't read 512 bytes. #if ENABLE_FEATURE_FDISK_WRITABLE - pe->changed = (what == CREATE_EMPTY_DOS); +static int get_boot(enum action what) +#else +static int get_boot(void) +#define get_boot(what) get_boot() #endif - } +{ + int i, fd; -#if ENABLE_FEATURE_FDISK_WRITABLE -// ALERT! highly idiotic design! -// We end up here when we call get_boot() recursively -// via get_boot() [table is bad] -> create_doslabel() -> get_boot(CREATE_EMPTY_DOS). -// (just factor out re-init of ptes[0,1,2,3] in a separate fn instead?) -// So skip opening device _again_... - if (what == CREATE_EMPTY_DOS) - goto created_table; + dos_reset_primary_partition_info(); +#if ENABLE_FEATURE_FDISK_WRITABLE fd = open(disk_device, (option_mask32 & OPT_l) ? O_RDONLY : O_RDWR); - if (fd < 0) { fd = open(disk_device, O_RDONLY); if (fd < 0) { @@ -1355,28 +1342,21 @@ static int get_boot(void) return -1; #else if (!valid_part_table_flag(MBRbuffer)) { - if (what == OPEN_MAIN) { - puts("Device has no valid DOS" -#if SUPPORT_DISKLABELS - IF_FEATURE_OSF_LABEL(", OSF") - IF_FEATURE_AIX_LABEL(", AIX") - IF_FEATURE_GPT_LABEL(", GPT") -#endif - " partition table." - ); -#ifndef __sparc__ - create_doslabel(); -#endif - return 0; - } - /* TRY_ONLY: */ - return -1; + if (what == TRY_ONLY) + return -1; + /* OPEN_MAIN: */ + puts("Device has no valid DOS" +# if SUPPORT_DISKLABELS + IF_FEATURE_OSF_LABEL(", OSF") + IF_FEATURE_AIX_LABEL(", AIX") + IF_FEATURE_GPT_LABEL(", GPT") +# endif + " partition table." + ); + create_doslabel(); } - created_table: + warn_cylinders(); #endif /* FEATURE_FDISK_WRITABLE */ - - - IF_FEATURE_FDISK_WRITABLE(warn_cylinders();) warn_geometry(); for (i = 0; i < 4; i++) { @@ -1558,7 +1538,7 @@ get_existing_partition(int warn, unsigned max) return pno; } puts("No partition is defined yet!"); - return -1; + return pno; // -1 not_unique: return get_partition(warn, max); @@ -1735,8 +1715,6 @@ change_sysid(void) "to %x (%s)\n", i + 1, sys, partition_type(sys)); ptes[i].changed = 1; - //if (is_dos_partition(origsys) || is_dos_partition(sys)) - // dos_changed = 1; break; } } @@ -2437,14 +2415,6 @@ reread_partition_table(int leave) i = ioctl_or_perror(dev_fd, BLKRRPART, NULL, "WARNING: rereading partition table " "failed, kernel still uses old table"); -#if 0 - if (dos_changed) - puts( - "\nWARNING: If you have created or modified any DOS 6.x\n" - "partitions, please see the fdisk manual page for additional\n" - "information"); -#endif - if (leave) { if (ENABLE_FEATURE_CLEAN_UP) close_dev_fd(); @@ -2866,7 +2836,7 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv) if (LABEL_IS_DOS) toggle_active(get_partition(1, g_partitions)); else - unknown_command(c); + goto unknown_cmd; break; case 'b': # if ENABLE_FEATURE_OSF_LABEL @@ -2877,7 +2847,7 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv) if (LABEL_IS_DOS) toggle_dos_compatibility_flag(); else - unknown_command(c); + goto unknown_cmd; break; case 'd': { @@ -2888,8 +2858,7 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv) break; //deleted: //case 'i': - // if (LABEL_IS_SGI) create_sgiinfo(); else - // unknown_command(c); + // if (LABEL_IS_SGI) create_sgiinfo(); else goto unknown_cmd; case 'l': list_types(get_sys_types()); break; @@ -2901,6 +2870,7 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv) break; case 'o': create_doslabel(); + dos_reset_primary_partition_info(); break; case 'p': list_table(0); @@ -2932,6 +2902,7 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv) break; # endif default: + unknown_cmd: unknown_command(c); menu(); } From vda.linux at googlemail.com Sun Sep 27 16:10:34 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sun, 27 Sep 2026 18:10:34 +0200 Subject: [git commit] fdisk: make write_table_and_exit() NORETURN Message-ID: <20260927170232.CEFBE819D3@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=8c43e2409e56115621ed372574965d276cd1c662 branch: https://git.busybox.net/busybox/log/?h=master function old new delta write_table_and_exit - 153 +153 fdisk_main 2537 2522 -15 write_table 159 - -159 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 0/1 up/down: 153/-174) Total: -21 bytes Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 09b23b5b1..e532f3190 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -2422,8 +2422,8 @@ reread_partition_table(int leave) } } -static void -write_table(void) +static void NORETURN +write_table_and_exit(void) { int i; @@ -2441,7 +2441,8 @@ write_table(void) } puts("The partition table has been altered."); - reread_partition_table(1); + reread_partition_table(1); // 1: exit + bb_unreachable(abort()); } #endif /* FEATURE_FDISK_WRITABLE */ @@ -2577,7 +2578,7 @@ xselect(void) verify(); break; case 'w': - write_table(); /* does not return */ + write_table_and_exit(); break; //deleted: //case 'y': @@ -2894,7 +2895,7 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv) verify(); break; case 'w': - write_table(); /* does not return */ + write_table_and_exit(); break; # if ENABLE_FEATURE_FDISK_ADVANCED case 'x': From vda.linux at googlemail.com Sun Sep 27 17:00:36 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sun, 27 Sep 2026 19:00:36 +0200 Subject: [git commit] fdisk: consistently use uint8_t (not char) for binary data Message-ID: <20260927170233.324AB819D3@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=4839711dce97da78ef80958b8c36d2bea2aed9f7 branch: https://git.busybox.net/busybox/log/?h=master Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 52 ++++++++++++++++++++------------------------------ util-linux/fdisk_osf.c | 32 ++++++++++++++----------------- 2 files changed, 35 insertions(+), 49 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index cd4c8b6ed..4b912f4b5 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -209,10 +209,10 @@ struct dos_partition { struct pte { struct dos_partition *part_table; /* points into sectorbuffer */ struct dos_partition *ext_pointer; /* points into sectorbuffer */ - sector_t offset_from_dev_start; /* disk sector number */ - char *sectorbuffer; /* disk sector contents */ + sector_t offset_from_dev_start; /* disk sector number */ + uint8_t *sectorbuffer; /* disk sector contents */ #if ENABLE_FEATURE_FDISK_WRITABLE - char changed; /* boolean */ + char changed; /* boolean */ #endif }; @@ -261,7 +261,7 @@ static unsigned get_partition(int warn, unsigned max); static void list_types(const char *const *sys); static sector_t read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char *mesg); #endif -static const char *partition_type(unsigned char type); +static const char *partition_type(uint8_t type); static void get_geometry(void); static void read_pte(struct pte *pe, sector_t offset); #if ENABLE_FEATURE_FDISK_WRITABLE @@ -417,7 +417,7 @@ struct globals { const char *opt_t; #if ENABLE_FEATURE_GPT_LABEL struct gpt_header *gpt_hdr; - char *gpt_part_array; + uint8_t *gpt_part_array; unsigned gpt_n_parts; unsigned gpt_part_entry_len; #endif @@ -426,7 +426,7 @@ struct globals { char line_buffer[80]; /* Raw disk label. For DOS-type partition tables the MBR, * with descriptions of the primary partitions. */ - char MBRbuffer[MAX_SECTOR_SIZE]; + uint8_t MBRbuffer[MAX_SECTOR_SIZE]; /* Partition tables */ struct pte ptes[MAXIMUM_PARTS]; }; @@ -609,9 +609,9 @@ str_units(void) } static int -valid_part_table_flag(const char *mbuffer) +valid_part_table_flag(const uint8_t *mbuffer) { - return (mbuffer[510] == 0x55 && (uint8_t)mbuffer[511] == 0xaa); + return (mbuffer[510] == 0x55 && mbuffer[511] == 0xaa); } static void fdisk_fatal(const char *why) @@ -659,7 +659,7 @@ set_changed(int i) } static ALWAYS_INLINE void -write_part_table_flag(char *b) +write_part_table_flag(uint8_t *b) { b[510] = 0x55; b[511] = 0xaa; @@ -737,8 +737,8 @@ STATIC_OSF void xbsd_print_disklabel(int); STATIC_GPT void gpt_list_table(int xtra); #include "fdisk_gpt.c" -static inline_if_little_endian unsigned -read4_little_endian(const unsigned char *cp) +static inline_if_little_endian uint32_t +read4_little_endian(const uint8_t *cp) { uint32_t v; move_from_unaligned32(v, cp); @@ -761,7 +761,7 @@ get_nr_sects(const struct dos_partition *p) /* start_sect and nr_sects are stored little endian on all machines */ /* moreover, they are not aligned correctly */ static inline_if_little_endian void -store4_little_endian(unsigned char *cp, unsigned val) +store4_little_endian(uint8_t *cp, uint32_t val) { uint32_t v = SWAP_LE32(val); move_to_unaligned32(cp, v); @@ -790,9 +790,7 @@ read_pte(struct pte *pe, sector_t offset) /* xread would make us abort - bad for fdisk -l */ if (full_read(dev_fd, pe->sectorbuffer, sector_size) != sector_size) fdisk_fatal(unable_to_read); -#if ENABLE_FEATURE_FDISK_WRITABLE - pe->changed = 0; -#endif + IF_FEATURE_FDISK_WRITABLE(pe->changed = 0;) pe->part_table = pe->ext_pointer = NULL; } @@ -863,24 +861,16 @@ xmenu(void) } #endif /* ADVANCED mode */ -#if ENABLE_FEATURE_FDISK_WRITABLE -static const char *const * -get_sys_types(void) -{ - return i386_sys_types; -} -#else #define get_sys_types() i386_sys_types -#endif static const char * -partition_type(unsigned char type) +partition_type(uint8_t type) { int i; const char *const *types = get_sys_types(); for (i = 0; types[i]; i++) - if ((unsigned char)types[i][0] == type) + if ((uint8_t)types[i][0] == type) return types[i] + 1; return "Unknown"; @@ -890,7 +880,7 @@ static int is_cleared_partition(const struct dos_partition *p) { /* We consider partition "cleared" only if it has only zeros */ - const char *cp = (const char *)p; + const uint8_t *cp = (const uint8_t *)p; int cnt = sizeof(*p); char bits = 0; while (--cnt >= 0) @@ -1178,13 +1168,13 @@ get_kernel_geometry(void) static void get_partition_table_geometry(void) { - const unsigned char *bufp = (const unsigned char *)MBRbuffer; + const uint8_t *bufp = MBRbuffer; struct dos_partition *p; int i, h, s, hh, ss; int first = 1; int bad = 0; - if (!(valid_part_table_flag((char*)bufp))) + if (!(valid_part_table_flag(bufp))) return; hh = ss = 0; @@ -2442,14 +2432,14 @@ write_table_and_exit(void) #if ENABLE_FEATURE_FDISK_ADVANCED #define MAX_PER_LINE 16 static void -print_buffer(char *pbuffer) +print_buffer(uint8_t *pbuffer) { - int i,l; + int i, l; for (i = 0, l = 0; i < sector_size; i++, l++) { if (l == 0) printf("0x%03X:", i); - printf(" %02X", (unsigned char) pbuffer[i]); + printf(" %02X", pbuffer[i]); if (l == MAX_PER_LINE - 1) { bb_putchar('\n'); l = -1; diff --git a/util-linux/fdisk_osf.c b/util-linux/fdisk_osf.c index eaeebe482..609a61523 100644 --- a/util-linux/fdisk_osf.c +++ b/util-linux/fdisk_osf.c @@ -254,7 +254,18 @@ static int xbsd_readlabel(struct dos_partition *p); static int xbsd_writelabel(struct dos_partition *p); #if defined(__alpha__) -static void alpha_bootblock_checksum(char *boot); +static void +alpha_bootblock_checksum(uint8_t *boot) +{ + uint64_t *dp, sum; + int i; + + dp = (uint64_t *)boot; + sum = 0; + for (i = 0; i < 63; i++) + sum += dp[i]; + dp[63] = sum; +} #endif #if !defined(__alpha__) @@ -267,7 +278,7 @@ static void xbsd_link_part(void); struct bsd_globals { /* We access this through a uint64_t * when checksumming */ /* hopefully xmalloc gives us required alignment */ - char disklabelbuffer[BSD_BBSIZE]; + uint8_t disklabelbuffer[BSD_BBSIZE]; struct xbsd_disklabel xbsd_dlabel; }; @@ -714,7 +725,7 @@ xbsd_write_bootstrap(void) const char *bootdir = BSD_LINUX_BOOTDIR; const char *dkbasename; struct xbsd_disklabel dl; - char *d, *p, *e; + uint8_t *d, *p, *e; int sector; if (xbsd_dlabel.d_type == BSD_DTYPE_SCSI) @@ -1028,21 +1039,6 @@ xbsd_link_part(void) } #endif -#if defined(__alpha__) -static void -alpha_bootblock_checksum(char *boot) -{ - uint64_t *dp, sum; - int i; - - dp = (uint64_t *)boot; - sum = 0; - for (i = 0; i < 63; i++) - sum += dp[i]; - dp[63] = sum; -} -#endif /* __alpha__ */ - /* Undefine 'global' tricks */ #undef disklabelbuffer #undef xbsd_dlabel From vda.linux at googlemail.com Sun Sep 27 16:29:42 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sun, 27 Sep 2026 18:29:42 +0200 Subject: [git commit] fdisk: show "Changes will remain in memory only" more prominently Message-ID: <20260927170232.F19D290CAA@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=8ce86eb361660f20512c26ba11a68a7162a2a983 branch: https://git.busybox.net/busybox/log/?h=master function old new delta .rodata 104735 104928 +193 fdisk_main 2522 2534 +12 create_doslabel 99 93 -6 msg_part_already_defined 61 - -61 msg_building_new_label 142 - -142 ------------------------------------------------------------------------------ (add/remove: 0/2 grow/shrink: 2/1 up/down: 205/-209) Total: -4 bytes Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index e532f3190..1bc488a27 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -179,16 +179,6 @@ enum { #define DISPLAY_IN_CYL_UNITS (!(option_mask32 & OPT_u)) #define TOGGLE_DISPLAY_IN_CYL_UNITS (option_mask32 ^= OPT_u) -#if ENABLE_FEATURE_FDISK_WRITABLE -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"; - -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_OSF_LABEL \ @@ -1142,7 +1132,9 @@ read_extended(int ext) static void create_doslabel(void) { - printf(msg_building_new_label, "DOS disklabel"); + // util-linux 2.41.1 says: + // "Created a new DOS (MBR) disklabel with disk identifier 0xdeafbead." + puts("Created a new DOS (MBR) disklabel."); current_label_type = LABEL_DOS; IF_FEATURE_OSF_LABEL(possibly_osf_label = 0;) @@ -1344,7 +1336,8 @@ static int get_boot(void) if (!valid_part_table_flag(MBRbuffer)) { if (what == TRY_ONLY) return -1; - /* OPEN_MAIN: */ + // OPEN_MAIN: + // util-linux 2.41.1 says: "Device does not contain a recognized partition table." puts("Device has no valid DOS" # if SUPPORT_DISKLABELS IF_FEATURE_OSF_LABEL(", OSF") @@ -2209,7 +2202,7 @@ add_partition(int n, int sys) sector_t first[g_partitions], last[g_partitions]; if (p && p->sys_ind) { - printf(msg_part_already_defined, n + 1); + printf("Partition %u is already defined, delete it before re-adding\n", n + 1); return; } fill_bounds(first, last); @@ -2815,6 +2808,13 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv) if (!argv[0] || argv[1]) bb_show_usage(); + // Unless -l or -s, util-linux 2.41.1 prints this message + // even before trying to open the device (try "fdisk /dev/bogus"). + puts( //"Welcome to fdisk (busybox "BB_VER")\n" //should we print this too? + "Changes will remain in memory only, until you decide to write them.\n" + "Be careful before using the write command." + ); + disk_device = argv[0]; get_boot(OPEN_MAIN); From vda.linux at googlemail.com Sun Sep 27 16:34:08 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sun, 27 Sep 2026 18:34:08 +0200 Subject: [git commit] fdisk: rename struct partition to dos_partition Message-ID: <20260927170233.0B95C954BE@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=e5588888c8ec04aed1e907950d92ad9a81093696 branch: https://git.busybox.net/busybox/log/?h=master Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 96 +++++++++++++++++++++++++------------------------- util-linux/fdisk_gpt.c | 2 +- util-linux/fdisk_osf.c | 16 ++++----- 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 1bc488a27..cd4c8b6ed 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -185,17 +185,17 @@ enum { | ENABLE_FEATURE_GPT_LABEL \ ) -struct partition { - unsigned char boot_ind; /* 0x80 - active */ - unsigned char head; /* starting head */ - unsigned char sector; /* starting sector */ - unsigned char cyl; /* starting cylinder */ - unsigned char sys_ind; /* what partition type */ - unsigned char end_head; /* end head */ - unsigned char end_sector; /* end sector */ - unsigned char end_cyl; /* end cylinder */ - unsigned char start4[4]; /* starting sector counting from 0 */ - unsigned char size4[4]; /* nr of sectors in partition */ +struct dos_partition { + uint8_t boot_ind; /* 0x80 - active */ + uint8_t head; /* starting head */ + uint8_t sector; /* starting sector */ + uint8_t cyl; /* starting cylinder */ + uint8_t sys_ind; /* what partition type */ + uint8_t end_head; /* end head */ + uint8_t end_sector; /* end sector */ + uint8_t end_cyl; /* end cylinder */ + uint8_t start4[4]; /* starting sector counting from 0 */ + uint8_t size4[4]; /* nr of sectors in partition */ } PACKED; /* @@ -207,8 +207,8 @@ struct partition { * partition and one link to the next one. */ struct pte { - struct partition *part_table; /* points into sectorbuffer */ - struct partition *ext_pointer; /* points into sectorbuffer */ + struct dos_partition *part_table; /* points into sectorbuffer */ + struct dos_partition *ext_pointer; /* points into sectorbuffer */ sector_t offset_from_dev_start; /* disk sector number */ char *sectorbuffer; /* disk sector contents */ #if ENABLE_FEATURE_FDISK_WRITABLE @@ -269,8 +269,8 @@ 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 sector_t get_start_sect(const struct dos_partition *p); +static sector_t get_nr_sects(const struct dos_partition *p); static void list_disk_name_and_sizes(void); /* DOS partition types */ @@ -403,7 +403,7 @@ struct globals { smallint dos_compatible_flag; // = 1; #if ENABLE_FEATURE_OSF_LABEL # if !defined(__alpha__) - struct partition *xbsd_part; + struct dos_partition *xbsd_part; unsigned xbsd_part_index; # endif #endif @@ -545,7 +545,7 @@ static sector_t bb_getsize_in_sectors(int fd) #define scround(x) (((x)+units_per_sector-1)/units_per_sector) #define pt_offset(b, n) \ - ((struct partition *)((b) + 0x1be + (n) * sizeof(struct partition))) + ((struct dos_partition *)((b) + 0x1be + (n) * sizeof(struct dos_partition))) #define sector(s) ((s) & 0x3f) @@ -595,7 +595,7 @@ partname(const char *dev, int pno, int lth) } #if ENABLE_FEATURE_OSF_LABEL -static ALWAYS_INLINE struct partition * +static ALWAYS_INLINE struct dos_partition * get_part_table(int i) { return ptes[i].part_table; @@ -746,13 +746,13 @@ read4_little_endian(const unsigned char *cp) } static sector_t -get_start_sect(const struct partition *p) +get_start_sect(const struct dos_partition *p) { return read4_little_endian(p->start4); } static sector_t -get_nr_sects(const struct partition *p) +get_nr_sects(const struct dos_partition *p) { return read4_little_endian(p->size4); } @@ -768,13 +768,13 @@ store4_little_endian(unsigned char *cp, unsigned val) } static void -set_start_sect(struct partition *p, unsigned start_sect) +set_start_sect(struct dos_partition *p, unsigned start_sect) { store4_little_endian(p->start4, start_sect); } static void -set_nr_sects(struct partition *p, unsigned nr_sects) +set_nr_sects(struct dos_partition *p, unsigned nr_sects) { store4_little_endian(p->size4, nr_sects); } @@ -887,7 +887,7 @@ partition_type(unsigned char type) } static int -is_cleared_partition(const struct partition *p) +is_cleared_partition(const struct dos_partition *p) { /* We consider partition "cleared" only if it has only zeros */ const char *cp = (const char *)p; @@ -899,7 +899,7 @@ is_cleared_partition(const struct partition *p) } static void -clear_partition(struct partition *p) +clear_partition(struct dos_partition *p) { if (p) memset(p, 0, sizeof(*p)); @@ -954,7 +954,7 @@ list_types(const char *const *sys) s |= (sector >> 2) & 0xc0; \ } while (0) -static void set_hsc_start_end(struct partition *p, sector_t start, sector_t stop) +static void set_hsc_start_end(struct dos_partition *p, sector_t start, sector_t stop) { if (dos_compatible_flag && (start / (g_sectors * g_heads) > 1023)) start = g_heads * g_sectors * 1024 - 1; @@ -968,7 +968,7 @@ static void set_hsc_start_end(struct partition *p, sector_t start, sector_t stop static void set_partition(int i, int doext, sector_t start, sector_t stop, int sysid) { - struct partition *p; + struct dos_partition *p; sector_t offset; if (doext) { @@ -1040,7 +1040,7 @@ read_extended(int ext) { int i; struct pte *pex; - struct partition *p, *q; + struct dos_partition *p, *q; ext_index = ext; pex = &ptes[ext]; @@ -1179,7 +1179,7 @@ static void get_partition_table_geometry(void) { const unsigned char *bufp = (const unsigned char *)MBRbuffer; - struct partition *p; + struct dos_partition *p; int i, h, s, hh, ss; int first = 1; int bad = 0; @@ -1518,7 +1518,7 @@ get_existing_partition(int warn, unsigned max) for (i = 0; i < max; i++) { struct pte *pe = &ptes[i]; - struct partition *p = pe->part_table; + struct dos_partition *p = pe->part_table; if (p && !is_cleared_partition(p)) { if (pno >= 0) @@ -1546,7 +1546,7 @@ get_nonexisting_partition(void) for (i = 0; i < max; i++) { struct pte *pe = &ptes[i]; - struct partition *p = pe->part_table; + struct dos_partition *p = pe->part_table; if (p && is_cleared_partition(p)) { if (pno >= 0) @@ -1579,7 +1579,7 @@ static void toggle_active(int i) { struct pte *pe = &ptes[i]; - struct partition *p = pe->part_table; + struct dos_partition *p = pe->part_table; if (IS_EXTENDED(p->sys_ind) && !p->boot_ind) printf("WARNING: Partition %u is an extended partition\n", i + 1); @@ -1604,8 +1604,8 @@ static void delete_partition(int i) { struct pte *pe = &ptes[i]; - struct partition *p = pe->part_table; - struct partition *q = pe->ext_pointer; + struct dos_partition *p = pe->part_table; + struct dos_partition *q = pe->ext_pointer; /* Note that for the fifth partition (i == 4) we don't actually * decrement partitions. @@ -1669,7 +1669,7 @@ static void change_sysid(void) { int i, sys, origsys; - struct partition *p; + struct dos_partition *p; i = get_existing_partition(0, g_partitions); if (i == -1) @@ -1732,7 +1732,7 @@ linear2chs(unsigned ls, unsigned *c, unsigned *h, unsigned *s) } static void -check_consistency(const struct partition *p, int partition) +check_consistency(const struct dos_partition *p, int partition) { unsigned pbc, pbh, pbs; /* physical beginning c, h, s */ unsigned pec, peh, pes; /* physical ending c, h, s */ @@ -1815,7 +1815,7 @@ static int wrong_p_order(int *prev) { const struct pte *pe; - const struct partition *p; + const struct dos_partition *p; sector_t last_p_start_pos = 0, p_start_pos; unsigned i, last_i = 0; @@ -1860,7 +1860,7 @@ static void fix_chain_of_logicals(void) { int j, oj, ojj, sj, sjj; - struct partition *pj,*pjj,tmp; + struct dos_partition *pj,*pjj,tmp; /* Stage 1: sort sectors but leave sector of part 4 */ /* (Its sector is the global extended_offset.) */ @@ -1922,7 +1922,7 @@ fix_partition_table_order(void) while ((i = wrong_p_order(&k)) != 0 && i < 4) { /* partition i should have come earlier, move it */ /* We have to move data in the MBR */ - struct partition *pi, *pk, *pe, pbuf; + struct dos_partition *pi, *pk, *pe, pbuf; pei = &ptes[i]; pek = &ptes[k]; @@ -1933,9 +1933,9 @@ fix_partition_table_order(void) pi = pei->part_table; pk = pek->part_table; - memmove(&pbuf, pi, sizeof(struct partition)); - memmove(pi, pk, sizeof(struct partition)); - memmove(pk, &pbuf, sizeof(struct partition)); + memmove(&pbuf, pi, sizeof(struct dos_partition)); + memmove(pi, pk, sizeof(struct dos_partition)); + memmove(pk, &pbuf, sizeof(struct dos_partition)); pei->changed = pek->changed = 1; } @@ -1986,7 +1986,7 @@ list_table(int xtra) w-1, "Device"); for (i = 0; i < g_partitions; i++) { - const struct partition *p; + const struct dos_partition *p; const struct pte *pe = &ptes[i]; char boot4[4]; char numstr6[6]; @@ -2046,7 +2046,7 @@ static void x_list_table(int extend) { const struct pte *pe; - const struct partition *p; + const struct dos_partition *p; int i; printf("\nDisk %s: %u heads, %u sectors, %u cylinders\n\n", @@ -2081,7 +2081,7 @@ fill_bounds(sector_t *first, sector_t *last) { unsigned i; const struct pte *pe = &ptes[0]; - const struct partition *p; + const struct dos_partition *p; for (i = 0; i < g_partitions; pe++,i++) { p = pe->part_table; @@ -2126,7 +2126,7 @@ verify(void) sector_t total = 1; sector_t chs_size; sector_t first[g_partitions], last[g_partitions]; - struct partition *p; + struct dos_partition *p; if (warn_geometry()) return; @@ -2195,8 +2195,8 @@ add_partition(int n, int sys) { char mesg[256]; /* 48 does not suffice in Japanese */ int i, num_read = 0; - struct partition *p = ptes[n].part_table; - struct partition *q = ptes[ext_index].part_table; + struct dos_partition *p = ptes[n].part_table; + struct dos_partition *q = ptes[ext_index].part_table; sector_t limit, temp; sector_t start, stop = 0; sector_t first[g_partitions], last[g_partitions]; @@ -2474,7 +2474,7 @@ static void move_begin(unsigned i) { struct pte *pe = &ptes[i]; - struct partition *p = pe->part_table; + struct dos_partition *p = pe->part_table; sector_t new, first, nr_sects; if (warn_geometry()) diff --git a/util-linux/fdisk_gpt.c b/util-linux/fdisk_gpt.c index 696b4937c..ff22c566f 100644 --- a/util-linux/fdisk_gpt.c +++ b/util-linux/fdisk_gpt.c @@ -146,7 +146,7 @@ static int check_gpt_label(void) { unsigned part_array_len; - struct partition *first = pt_offset(MBRbuffer, 0); + struct dos_partition *first = pt_offset(MBRbuffer, 0); struct pte pe; uint32_t crc; diff --git a/util-linux/fdisk_osf.c b/util-linux/fdisk_osf.c index f34390e01..eaeebe482 100644 --- a/util-linux/fdisk_osf.c +++ b/util-linux/fdisk_osf.c @@ -249,9 +249,9 @@ static int xbsd_get_part_index(int max); static int xbsd_check_new_partition(int *i); static void xbsd_list_types(void); static uint16_t xbsd_dkcksum(struct xbsd_disklabel *lp); -static int xbsd_initlabel(struct partition *p); -static int xbsd_readlabel(struct partition *p); -static int xbsd_writelabel(struct partition *p); +static int xbsd_initlabel(struct dos_partition *p); +static int xbsd_readlabel(struct dos_partition *p); +static int xbsd_writelabel(struct dos_partition *p); #if defined(__alpha__) static void alpha_bootblock_checksum(char *boot); @@ -349,7 +349,7 @@ bsd_select(void) { #if !defined(__alpha__) int t, ss; - struct partition *p; + struct dos_partition *p; for (t = 0; t < 4; t++) { p = get_part_table(t); @@ -847,7 +847,7 @@ xbsd_dkcksum(struct xbsd_disklabel *lp) } static int -xbsd_initlabel(struct partition *p) +xbsd_initlabel(struct dos_partition *p) { struct xbsd_disklabel *d = &xbsd_dlabel; struct xbsd_partition *pp; @@ -915,7 +915,7 @@ xbsd_initlabel(struct partition *p) * If it has the right magic, return 1. */ static int -xbsd_readlabel(struct partition *p) +xbsd_readlabel(struct dos_partition *p) { struct xbsd_disklabel *d; int t, sector; @@ -955,7 +955,7 @@ xbsd_readlabel(struct partition *p) } static int -xbsd_writelabel(struct partition *p) +xbsd_writelabel(struct dos_partition *p) { struct xbsd_disklabel *d = &xbsd_dlabel; unsigned int sector; @@ -1013,7 +1013,7 @@ static void xbsd_link_part(void) { int k, i; - struct partition *p; + struct dos_partition *p; k = get_partition(1, g_partitions); From vda.linux at googlemail.com Sun Sep 27 20:50:02 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sun, 27 Sep 2026 22:50:02 +0200 Subject: [git commit] fdsik: reduce local char mesg[256] to [64] Message-ID: <20260928012234.0033C98339@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=3fc725d4132278a2a3981a6a51d8980ef2615133 branch: https://git.busybox.net/busybox/log/?h=master function old new delta add_partition 1246 1038 -208 Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 375ad65b9..2f67e1a5b 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -2271,8 +2271,8 @@ verify(void) static void add_partition(int n, int sys) { - char mesg[256]; /* 48 does not suffice in Japanese */ - int i, num_read = 0; + char mesg[64]; + int i, num_read; struct dos_partition *p = ptes[n].part_table; struct dos_partition *q = ptes[ext_index].part_table; sector_t limit, temp; @@ -2303,7 +2303,8 @@ add_partition(int n, int sys) for (i = 0; i < g_partitions; i++) first[i] = (cround(first[i]) - 1) * units_per_sector; - snprintf(mesg, sizeof(mesg), "First %s", str_units()); + sprintf(mesg, "First %s", str_units()); + num_read = 0; do { temp = start; for (i = 0; i < g_partitions; i++) { @@ -2363,7 +2364,7 @@ add_partition(int n, int sys) if (cround(start) == cround(limit)) { stop = limit; } else { - snprintf(mesg, sizeof(mesg), + sprintf(mesg, "Last %s or +size{,K,M,G,T}", str_units() ); From vda.linux at googlemail.com Sun Sep 27 20:57:52 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sun, 27 Sep 2026 22:57:52 +0200 Subject: [git commit] fdisk: remove dead code in open_list_and_close() Message-ID: <20260928012234.0AA5090C88@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=03293bc06cdfbbfdb5ee6dcd0cc22e85c6256208 branch: https://git.busybox.net/busybox/log/?h=master delete_partition() which deals with extended partition is unnecessary since we are inside "fdisk -l". "if (LABEL_IS_AIX) ..." is false since only after successful get_boot() we can have a detected AIX label. function old new delta open_list_and_close 424 392 -32 Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 2f67e1a5b..f155239fb 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -2071,23 +2071,21 @@ open_list_and_close(const char *device, int user_specified) if (is_ide_cdrom_or_tape(device)) return; - /* Open disk_device, save file descriptor to dev_fd */ + // Open disk_device, save file descriptor to dev_fd errno = 0; gb = get_boot(TRY_ONLY); - if (gb > 0) { /* I/O error */ - /* Ignore other errors, since we try IDE - and SCSI hard disks which may not be - installed on the system. */ + if (gb > 0) { // I/O error + // Ignore other errors, since we try IDE + // and SCSI hard disks which may not be + // installed on the system. if (user_specified || errno == EACCES) bb_perror_msg("can't open '%s'", device); return; } - if (gb < 0) { /* no DOS signature */ + if (gb < 0) { // no DOS/AIX signature print_disk_name_and_sizes(); print_disk_geometry(); - if (LABEL_IS_AIX) - goto ret; #if ENABLE_FEATURE_OSF_LABEL if (bsd_trydev(device) < 0) #endif @@ -2095,13 +2093,7 @@ open_list_and_close(const char *device, int user_specified) "partition table\n", device); } else { print_disklabel(0); -#if ENABLE_FEATURE_FDISK_WRITABLE - if (g_partitions > 4) { - delete_partition(ext_index); - } -#endif } - ret: close_dev_fd(); } From vda.linux at googlemail.com Mon Sep 28 00:49:08 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 28 Sep 2026 02:49:08 +0200 Subject: [git commit] fdisk: explain how extended partitions work on-disk Message-ID: <20260928012234.2492B9835D@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=659644d8d67407e4d7da38a6677dac20925b6645 branch: https://git.busybox.net/busybox/log/?h=master Improve messages about "DOS compatibility flag" function old new delta menu 231 261 +30 valid_55AA_signature - 26 +26 fdisk_main 2534 2547 +13 get_boot 1406 1412 +6 valid_part_table_flag 26 - -26 .rodata 104913 104879 -34 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 3/1 up/down: 75/-60) Total: 15 bytes Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 261 +++++++++++++++++++++++++++++-------------------- util-linux/fdisk_gpt.c | 2 +- 2 files changed, 157 insertions(+), 106 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 3c310cad8..9c6692e23 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -389,10 +389,16 @@ struct globals { char *line_ptr; const char *disk_device; - int g_partitions; // = 4; /* maximum partition + 1 */ + // NB: g_partitions is never less than 4. + // If some primary partitions are not populated, they still "exist". + int g_partitions; // = 4; // maximum partition + 1 unsigned units_per_sector; // = 1; unsigned sector_size; // = DEFAULT_SECTOR_SIZE; - unsigned sector_offset; // = 1; + // How much to advance after MBR or extended partition link + // before starting next partition? Minimum is 1, + // "DOS compatible" way is to skip to the end of track + // (set it to g_sectors). + unsigned offset_after_MBR_and_ext; // = 1; unsigned g_heads, g_sectors, g_cylinders; smallint /* enum label_type */ current_label_type; #if ENABLE_FEATURE_OSF_LABEL @@ -406,11 +412,11 @@ struct globals { unsigned xbsd_part_index; # endif #endif - int ext_index; /* the prime extended partition */ unsigned user_cylinders, user_heads, user_sectors; unsigned pt_heads, pt_sectors; unsigned kern_heads, kern_sectors; - sector_t extended_offset; /* offset of link pointers */ + int ext_index; // the prime extended partition (0-3) + sector_t extended_offset; // offset of extended partition start sector_t total_number_of_sectors; const char *opt_t; @@ -435,7 +441,7 @@ struct globals { #define g_partitions (G.g_partitions ) #define units_per_sector (G.units_per_sector ) #define sector_size (G.sector_size ) -#define sector_offset (G.sector_offset ) +#define offset_after_MBR_and_ext (G.offset_after_MBR_and_ext) #define g_heads (G.g_heads ) #define g_sectors (G.g_sectors ) #define g_cylinders (G.g_cylinders ) @@ -459,7 +465,7 @@ struct globals { #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ sector_size = DEFAULT_SECTOR_SIZE; \ - sector_offset = 1; \ + offset_after_MBR_and_ext = 1; \ g_partitions = 4; \ units_per_sector = 1; \ dos_compatible_flag = 1; \ @@ -599,7 +605,7 @@ str_units(void) } static int -valid_part_table_flag(const uint8_t *mbuffer) +valid_55AA_signature(const uint8_t *mbuffer) { return (mbuffer[510] == 0x55 && mbuffer[511] == 0xaa); } @@ -649,7 +655,7 @@ set_changed(int i) } static ALWAYS_INLINE void -write_part_table_flag(uint8_t *b) +write_55AA_signature(uint8_t *b) { b[510] = 0x55; b[511] = 0xaa; @@ -862,49 +868,7 @@ list_types(const char *const *sys) } while (done < last[0]); bb_putchar('\n'); } - -#define set_hsc(h, s, c, sector) do \ -{ \ - s = sector % g_sectors + 1; \ - sector /= g_sectors; \ - h = sector % g_heads; \ - sector /= g_heads; \ - c = sector & 0xff; \ - s |= (sector >> 2) & 0xc0; \ -} while (0) - -static void set_hsc_start_end(struct dos_partition *p, sector_t start, sector_t stop) -{ - if (dos_compatible_flag && (start / (g_sectors * g_heads) > 1023)) - start = g_heads * g_sectors * 1024 - 1; - set_hsc(p->head, p->sector, p->cyl, start); - - if (dos_compatible_flag && (stop / (g_sectors * g_heads) > 1023)) - stop = g_heads * g_sectors * 1024 - 1; - set_hsc(p->end_head, p->end_sector, p->end_cyl, stop); -} - -static void -set_partition(int i, int doext, sector_t start, sector_t stop, int sysid) -{ - struct dos_partition *p; - sector_t offset; - - if (doext) { - p = ptes[i].ext_pointer; - offset = extended_offset; - } else { - p = ptes[i].part_table; - offset = ptes[i].offset_from_dev_start; - } - p->boot_ind = 0; - p->sys_ind = sysid; - set_start_sect(p, start - offset); - set_nr_sects(p, stop - start + 1); - set_hsc_start_end(p, start, stop); - ptes[i].changed = 1; -} -#endif +#endif // WRITABLE static int warn_geometry(void) @@ -954,24 +918,58 @@ warn_cylinders(void) } #endif +// linux/block/partitions/msdos.c: +// ... +// The logical partitions form a linked list, with each entry being +// a partition table with two entries. The first entry +// is the real data partition (with a start relative to the partition +// table start). The second is a pointer to the next logical partition +// (with a start **relative to the entire extended partition**). +// We do not create a Linux partition for the partition tables, but +// only for the actual data partitions. +// ... +// Usually, the first entry is the real data partition, +// the 2nd entry is the next extended partition, or empty, +// and the 3rd and 4th entries are unused. +// However, DRDOS sometimes has the extended partition as +// the first entry (when the data partition is empty), +// and OS/2 **seems to use all four entries**. +// +// Due to the above, there can be up to four data partitions +// in a link of this "extended partition chain". +// ^^^ we do not handle this case +// +// However, kernel code uses only the first "next extended partition" entry +// it finds in the current link, and ignores any other "extended partitions": +// "tree of extended partitions" is not a thing. +// +// Kernel code requires that the links have nr_sects != 0 +// (otherwise it is not considered to be "extended partition entry"). +// The starting link, visible as one of first 4 partitions, +// is a real block device (!) but its size is capped to 2 sectors +// regardless of how large ns_sects field is. +// Kernel does not complain if this tiny partition intrudes into first sector +// of the next partition (!!!) which happens to start in the very next sector. static void -read_extended(int ext) +read_extended_chain(int ext) { int i; struct pte *pex; - struct dos_partition *p, *q; + struct dos_partition *p; ext_index = ext; pex = &ptes[ext]; pex->ext_pointer = pex->part_table; p = pex->part_table; - if (!get_start_sect(p)) { - puts("Bad offset in primary extended partition"); + if (get_start_sect(p) == 0) { + puts("Zero offset in primary extended partition"); return; } + //TODO: check nr_sect != 0 too? while (IS_EXTENDED(p->sys_ind)) { + struct dos_partition *pt0; struct pte *pe = &ptes[g_partitions]; if (g_partitions >= MAXIMUM_PARTS) { @@ -989,41 +987,48 @@ read_extended(int ext) } read_pte(pe, extended_offset + get_start_sect(p)); + // ^^^ does not return on failure to seek or read - if (!extended_offset) + if (extended_offset == 0) // remember base ofs of whole extended partition extended_offset = get_start_sect(p); - q = p = pt_offset(pe->sectorbuffer, 0); - for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) { + pt0 = p = pt_offset(pe->sectorbuffer, 0); + for (i = 0; i < 4; i++, p++) { + if (get_nr_sects(p) == 0) + continue; if (IS_EXTENDED(p->sys_ind)) { if (pe->ext_pointer) - printf("Warning: extra link " - "pointer in partition table" - " %u\n", g_partitions + 1); + printf("Warning: extra %s " + "pointer in ext.partition chain" + " %u\n", "link", g_partitions + 1); else pe->ext_pointer = p; - } else if (p->sys_ind) { + } else if (p->sys_ind != 0) { if (pe->part_table) - printf("Warning: ignoring extra " - "data in partition table" - " %u\n", g_partitions + 1); + printf("Warning: extra %s " + "pointer in ext.partition chain" + " %u\n", "data", g_partitions + 1); else pe->part_table = p; } } - /* very strange code here... */ + // Make sure that if not found, data/link pointers + // do terminate - point them to invalid entries + // (hopefully? Imagine a link with 4 "next ext" ptrs: + // none of them have manifestly invalid form!!!) +//FIXME: allow them to dangle? if (!pe->part_table) { - if (q != pe->ext_pointer) - pe->part_table = q; + if (pe->ext_pointer != pt0) + pe->part_table = pt0; else - pe->part_table = q + 1; + pe->part_table = pt0 + 1; } if (!pe->ext_pointer) { - if (q != pe->part_table) - pe->ext_pointer = q; + if (pe->part_table != pt0) + pe->ext_pointer = pt0; else - pe->ext_pointer = q + 1; + pe->ext_pointer = pt0 + 1; } p = pe->ext_pointer; @@ -1061,7 +1066,7 @@ create_doslabel(void) g_partitions = 4; memset(&MBRbuffer[510 - 4*16], 0, 4*16); - write_part_table_flag(MBRbuffer); + write_55AA_signature(MBRbuffer); extended_offset = 0; set_all_unchanged(); set_changed(0); @@ -1103,7 +1108,7 @@ get_partition_table_geometry(void) int first = 1; int bad = 0; - if (!(valid_part_table_flag(bufp))) + if (!(valid_55AA_signature(bufp))) return; hh = ss = 0; @@ -1147,9 +1152,9 @@ get_geometry(void) kern_sectors ? kern_sectors : 63; total_number_of_sectors = bb_getsize_in_sectors(dev_fd); - sector_offset = 1; + offset_after_MBR_and_ext = 1; if (dos_compatible_flag) - sector_offset = g_sectors; + offset_after_MBR_and_ext = g_sectors; g_cylinders = total_number_of_sectors / (g_heads * g_sectors); //TODO? if (total_number_of_sectors % (g_heads * g_sectors) != 0) g_cylinders++; @@ -1218,11 +1223,11 @@ static int get_boot(void) fd = open(disk_device, O_RDONLY); if (fd < 0) return 1; - if (512 != full_read(fd, MBRbuffer, 512)) { - close(fd); + xmove_fd(fd, dev_fd); + if (512 != full_read(dev_fd, MBRbuffer, 512)) { + close_dev_fd(); return 1; } - xmove_fd(fd, dev_fd); #endif get_geometry(); @@ -1239,7 +1244,7 @@ static int get_boot(void) #if ENABLE_FEATURE_OSF_LABEL if (check_osf_label()) { possibly_osf_label = 1; - if (!valid_part_table_flag(MBRbuffer)) { + if (!valid_55AA_signature(MBRbuffer)) { current_label_type = LABEL_OSF; return 0; } @@ -1249,10 +1254,10 @@ static int get_boot(void) #endif #if !ENABLE_FEATURE_FDISK_WRITABLE - if (!valid_part_table_flag(MBRbuffer)) + if (!valid_55AA_signature(MBRbuffer)) return -1; #else - if (!valid_part_table_flag(MBRbuffer)) { + if (!valid_55AA_signature(MBRbuffer)) { if (what == TRY_ONLY) return -1; // OPEN_MAIN: @@ -1271,19 +1276,22 @@ static int get_boot(void) #endif /* FEATURE_FDISK_WRITABLE */ warn_geometry(); + // We saw 55AA signature in MBR, this is considered enough to conclude + // partition table exists (for fdisk -l. Without -l, the requirement + // is that there are at least 512 bytes in this disk, that's all). for (i = 0; i < 4; i++) { if (IS_EXTENDED(ptes[i].part_table->sys_ind)) { if (g_partitions != 4) printf("Ignoring extra extended " "partition %u\n", i + 1); else - read_extended(i); + read_extended_chain(i); } } for (i = 3; i < g_partitions; i++) { struct pte *pe = &ptes[i]; - if (!valid_part_table_flag(pe->sectorbuffer)) { + if (!valid_55AA_signature(pe->sectorbuffer)) { printf("Warning: invalid flag 0x%02x,0x%02x of partition " "table %u will be corrected by w(rite)\n", pe->sectorbuffer[510], @@ -1511,11 +1519,11 @@ toggle_dos_compatibility_flag(void) { dos_compatible_flag = 1 - dos_compatible_flag; if (dos_compatible_flag) { - sector_offset = g_sectors; - printf("DOS Compatibility flag is %sset\n", ""); + offset_after_MBR_and_ext = g_sectors; + printf("DOS Compatibility flag (%u sector gap) is %sset\n", offset_after_MBR_and_ext, ""); } else { - sector_offset = 1; - printf("DOS Compatibility flag is %sset\n", "not "); + offset_after_MBR_and_ext = 1; + printf("DOS Compatibility flag (%u sector gap) is %sset\n", offset_after_MBR_and_ext, "not "); } } @@ -2251,6 +2259,47 @@ verify(void) } } +static void +set_hsc_start_end(struct dos_partition *p, sector_t start, sector_t stop) +{ +#define SET_HEAD_SECT_CYL(h, s, c, sector) do { \ + s = sector % g_sectors + 1; \ + sector /= g_sectors; \ + h = sector % g_heads; \ + sector /= g_heads; \ + c = sector & 0xff; \ + s |= (sector >> 2) & 0xc0; \ +} while (0) + if (dos_compatible_flag && (start / (g_sectors * g_heads) > 1023)) + start = g_heads * g_sectors * 1024 - 1; + SET_HEAD_SECT_CYL(p->head, p->sector, p->cyl, start); + if (dos_compatible_flag && (stop / (g_sectors * g_heads) > 1023)) + stop = g_heads * g_sectors * 1024 - 1; + SET_HEAD_SECT_CYL(p->end_head, p->end_sector, p->end_cyl, stop); +#undef SET_HEAD_SECT_CYL +} + +// add_partition() helper +static void +set_partition(int i, int doext, sector_t start, sector_t stop, int sysid) +{ + struct dos_partition *p; + sector_t offset; + + if (doext) { + p = ptes[i].ext_pointer; + offset = extended_offset; + } else { + p = ptes[i].part_table; + offset = ptes[i].offset_from_dev_start; + } + p->boot_ind = 0; + p->sys_ind = sysid; + set_start_sect(p, start - offset); + set_nr_sects(p, stop - start + 1); + set_hsc_start_end(p, start, stop); + ptes[i].changed = 1; +} static void add_partition(int n, int sys) { @@ -2259,7 +2308,7 @@ add_partition(int n, int sys) struct dos_partition *p = ptes[n].part_table; struct dos_partition *q = ptes[ext_index].part_table; sector_t limit, temp; - sector_t start, stop = 0; + sector_t start, stop; sector_t first[g_partitions], last[g_partitions]; if (p && p->sys_ind) { @@ -2268,7 +2317,7 @@ add_partition(int n, int sys) } fill_bounds(first, last); if (n < 4) { - start = sector_offset; + start = offset_after_MBR_and_ext; if (DISPLAY_IN_CYL_UNITS || !total_number_of_sectors) limit = (sector_t) g_heads * g_sectors * g_cylinders - 1; else @@ -2279,7 +2328,7 @@ add_partition(int n, int sys) get_nr_sects(q) - 1; } } else { - start = extended_offset + sector_offset; + start = extended_offset + offset_after_MBR_and_ext; limit = get_start_sect(q) + get_nr_sects(q) - 1; } if (DISPLAY_IN_CYL_UNITS) @@ -2294,14 +2343,14 @@ add_partition(int n, int sys) int lastplusoff; if (start == ptes[i].offset_from_dev_start) - start += sector_offset; - lastplusoff = last[i] + ((n < 4) ? 0 : sector_offset); + start += offset_after_MBR_and_ext; + lastplusoff = last[i] + ((n < 4) ? 0 : offset_after_MBR_and_ext); if (start >= first[i] && start <= lastplusoff) start = lastplusoff + 1; } if (start > limit) break; - if (start >= temp+units_per_sector && num_read) { + if (start >= temp + units_per_sector && num_read) { printf("Sector %"SECT_FMT"u is already allocated\n", temp); temp = start; num_read = 0; @@ -2319,13 +2368,14 @@ add_partition(int n, int sys) num_read = 1; } } while (start != temp || !num_read); + if (n > 4) { /* NOT for fifth partition */ struct pte *pe = &ptes[n]; - pe->offset_from_dev_start = start - sector_offset; + pe->offset_from_dev_start = start - offset_after_MBR_and_ext; if (pe->offset_from_dev_start == extended_offset) { /* must be corrected */ pe->offset_from_dev_start++; - if (sector_offset == 1) + if (offset_after_MBR_and_ext == 1) start++; } } @@ -2354,14 +2404,14 @@ add_partition(int n, int sys) stop = read_int(cround(start), cround(limit), cround(limit), cround(start), mesg); if (DISPLAY_IN_CYL_UNITS) { stop = stop * units_per_sector - 1; - if (stop >limit) + if (stop > limit) stop = limit; } } - set_partition(n, 0, start, stop, sys); + set_partition(n, /* ext:*/ 0, start, stop, sys); if (n > 4) - set_partition(n - 1, 1, ptes[n].offset_from_dev_start, stop, EXTENDED); + set_partition(n - 1, /* ext:*/ 1, ptes[n].offset_from_dev_start, stop, EXTENDED); if (IS_EXTENDED(sys)) { struct pte *pe4 = &ptes[4]; @@ -2430,7 +2480,7 @@ new_partition(void) " p primary partition (1-4)\n" " %s\n", (extended_offset ? - "l logical (5 or over)" : "e extended")); + "l logical (5+)" : "e extended (1-4)")); while (1) { c = read_nonempty(line); c |= 0x20; /* lowercase */ @@ -2486,10 +2536,11 @@ write_table_and_exit(void) for (i = 0; i < 3; i++) if (ptes[i].changed) ptes[3].changed = 1; + // If needed, write MBR and extended partition sectors for (i = 3; i < g_partitions; i++) { struct pte *pe = &ptes[i]; if (pe->changed) { - write_part_table_flag(pe->sectorbuffer); + write_55AA_signature(pe->sectorbuffer); write_sector(pe->offset_from_dev_start, pe->sectorbuffer); } } @@ -2576,7 +2627,7 @@ menu(void) } else { puts("a\ttoggle a bootable flag"); puts("b\tedit bsd disklabel"); - puts("c\ttoggle the dos compatibility flag"); + printf("c\tturn o%s DOS sector gap flag\n", dos_compatible_flag ? "ff" : "n"); puts("d\tdelete a partition"); puts("l\tlist known partition types"); puts("n\tadd a new partition"); @@ -2681,9 +2732,9 @@ xselect(void) case 's': user_sectors = g_sectors = read_int(1, g_sectors, 63, 0, "Number of sectors"); if (dos_compatible_flag) { - sector_offset = g_sectors; - puts("Warning: setting sector offset for DOS " - "compatibility"); + offset_after_MBR_and_ext = g_sectors; + printf("Warning: setting sector offset %u for DOS " + "compatibility\n", g_sectors); } update_units(); break; diff --git a/util-linux/fdisk_gpt.c b/util-linux/fdisk_gpt.c index c50d0da1b..dcc52c7dc 100644 --- a/util-linux/fdisk_gpt.c +++ b/util-linux/fdisk_gpt.c @@ -153,7 +153,7 @@ check_gpt_label(void) /* LBA 0 contains the legacy MBR */ - if (!valid_part_table_flag(MBRbuffer) + if (!valid_55AA_signature(MBRbuffer) || first->sys_ind != LEGACY_GPT_TYPE ) { return 0; From vda.linux at googlemail.com Sun Sep 27 21:21:23 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sun, 27 Sep 2026 23:21:23 +0200 Subject: [git commit] fdisk: remove "devfs kludge" Message-ID: <20260928012234.1610D8E01D@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=25c50c4805c93578839ae020a4cbffaf900bb7d9 branch: https://git.busybox.net/busybox/log/?h=master function old new delta partname 184 136 -48 Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index f155239fb..3c310cad8 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -571,22 +571,13 @@ partname(const char *dev, int pno, int lth) w = strlen(dev); p = ""; - - if (isdigit(dev[w-1])) + if (isdigit(dev[w - 1])) p = "p"; - - /* devfs kludge - note: fdisk partition names are not supposed - to equal kernel names, so there is no reason to do this */ - if (strcmp(dev + w - 4, "disc") == 0) { - w -= 4; - p = "part"; - } - - wp = strlen(p); + wp = (p[0] != '\0'); //=strlen(p) if (lth) { snprintf(bufp, bufsiz, "%*.*s%s%-2u", - lth-wp-2, w, dev, p, pno); + lth - wp - 2, w, dev, p, pno); } else { snprintf(bufp, bufsiz, "%.*s%s%-2u", w, dev, p, pno); } From vda.linux at googlemail.com Mon Sep 28 01:23:26 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 28 Sep 2026 03:23:26 +0200 Subject: [git commit] fdisk: simplify new_partition() Message-ID: <20260928012234.31C1A98339@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=152c6227d0fcf9a2d1d0f4fbe41bbb951e4faec0 branch: https://git.busybox.net/busybox/log/?h=master function old new delta input_partition_number - 73 +73 fdisk_main 2547 2610 +63 get_partition 73 - -73 get_nonexisting_partition 100 - -100 .rodata 104879 104779 -100 ------------------------------------------------------------------------------ (add/remove: 1/2 grow/shrink: 1/1 up/down: 136/-273) Total: -137 bytes Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 104 ++++++++++++++++++++++++------------------------- util-linux/fdisk_osf.c | 2 +- 2 files changed, 51 insertions(+), 55 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 9c6692e23..331a20f39 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -257,7 +257,7 @@ static void update_units(void); static void change_units(void); static void reread_partition_table(int leave); static void delete_partition(int i); -static unsigned get_partition(int warn, unsigned max); +static unsigned input_partition_number(int warn, unsigned max); static void list_types(const char *const *sys); static sector_t read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char *mesg); #endif @@ -1421,11 +1421,12 @@ read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char * } static unsigned -get_partition(int warn, unsigned max) +input_partition_number(int warn, unsigned max) { struct pte *pe; unsigned i; + // low, default(none), high, base(?), msg i = read_int(1, 0, max, 0, "Partition number") - 1; pe = &ptes[i]; @@ -1454,24 +1455,23 @@ get_existing_partition(int warn, unsigned max) } } if (pno >= 0) { - printf("Selected partition %u\n", pno+1); + printf("Selected partition %u\n", pno + 1); return pno; } puts("No partition is defined yet!"); return pno; // -1 not_unique: - return get_partition(warn, max); + return input_partition_number(warn, max); } static int -get_nonexisting_partition(void) +find_free_primary_partition(void) { - const int max = 4; int pno = -1; unsigned i; - for (i = 0; i < max; i++) { + for (i = 0; i < 4; i++) { struct pte *pe = &ptes[i]; struct dos_partition *p = pe->part_table; @@ -1481,15 +1481,16 @@ get_nonexisting_partition(void) pno = i; } } - if (pno >= 0) { - printf("Selected partition %u\n", pno+1); +// Caller ensures cleared partition exists +// if (pno >= 0) { + printf("Selected partition %u\n", pno + 1); return pno; - } - puts("All primary partitions have been defined already!"); - return -1; +// } +// puts("All primary partitions have been defined already!"); +// return pno; // -1 not_unique: - return get_partition(/*warn*/ 0, max); + return input_partition_number(/*warn*/ 0, 4); } @@ -2447,7 +2448,8 @@ add_logical(void) static void new_partition(void) { - int i, free_primary = 0; + char c, line[80]; + int free_primary; if (warn_geometry()) return; @@ -2459,51 +2461,45 @@ new_partition(void) return; } - for (i = 0; i < 4; i++) - free_primary += !ptes[i].part_table->sys_ind; - - if (!free_primary && g_partitions >= MAXIMUM_PARTS) { - puts("The maximum number of partitions has been created"); - return; - } + for (free_primary = 0; free_primary < 4; free_primary++) + if (is_cleared_partition(ptes[free_primary].part_table)) + break; - if (!free_primary) { + if (free_primary == 4) { + // no free primary partition + if (g_partitions >= MAXIMUM_PARTS) { + puts("The maximum number of partitions has been created"); + return; + } if (extended_offset) add_logical(); else puts("You must delete some partition and add " "an extended partition first"); - } else { - char c, line[80]; - snprintf(line, sizeof(line), - "Partition type\n" - " p primary partition (1-4)\n" - " %s\n", - (extended_offset ? - "l logical (5+)" : "e extended (1-4)")); - while (1) { - c = read_nonempty(line); - c |= 0x20; /* lowercase */ - if (c == 'p') { - i = get_nonexisting_partition(); - if (i >= 0) - add_partition(i, LINUX_NATIVE); - return; - } - if (c == 'l' && extended_offset) { - add_logical(); - return; - } - if (c == 'e' && !extended_offset) { - i = get_nonexisting_partition(); - if (i >= 0) - add_partition(i, EXTENDED); - return; - } - printf("Invalid partition number " - "for type '%c'\n", c); - } + return; + } + + snprintf(line, sizeof(line), + "Partition type\n" + " p primary (1-4)\n" + " %s\n", + (extended_offset ? + "l logical (5+)" : "e extended (1-4)")); + c = read_nonempty(line); + c |= 0x20; /* lowercase */ + if (c == 'p' + || (c == 'e' && !extended_offset) + ) { + free_primary = find_free_primary_partition(); + //if (free_primary >= 0) // cannot fail, we know it exists + add_partition(free_primary, c == 'p' ? LINUX_NATIVE : EXTENDED); + return; + } + if (c == 'l' && extended_offset) { + add_logical(); + return; } + // Bad answer: go back to main menu } static void @@ -2684,7 +2680,7 @@ xselect(void) // break; case 'b': if (LABEL_IS_DOS) - move_begin(get_partition(0, g_partitions)); + move_begin(input_partition_number(0, g_partitions)); break; case 'c': user_cylinders = g_cylinders = @@ -2871,7 +2867,7 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv) switch (c) { case 'a': if (LABEL_IS_DOS) - toggle_active(get_partition(1, g_partitions)); + toggle_active(input_partition_number(1, g_partitions)); else goto unknown_cmd; break; diff --git a/util-linux/fdisk_osf.c b/util-linux/fdisk_osf.c index 28569a956..fca0c0d5a 100644 --- a/util-linux/fdisk_osf.c +++ b/util-linux/fdisk_osf.c @@ -911,7 +911,7 @@ xbsd_link_part(void) int k, i; struct dos_partition *p; - k = get_partition(1, g_partitions); + k = input_partition_number(1, g_partitions); if (!xbsd_check_new_partition(&i)) return; From vda.linux at googlemail.com Sun Sep 27 20:31:46 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sun, 27 Sep 2026 22:31:46 +0200 Subject: [git commit] fdisk: group menu functions together Message-ID: <20260928012233.E75D796FF8@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=dcb8ae589bb5731d67fd60886a014d9d028d35d3 branch: https://git.busybox.net/busybox/log/?h=master Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 389 ++++++++++++++++++++++++++--------------------------- 1 file changed, 191 insertions(+), 198 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index e8871567c..375ad65b9 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -799,67 +799,6 @@ get_partition_start_from_dev_start(const struct pte *pe) return pe->offset_from_dev_start + get_start_sect(pe->part_table); } -#if ENABLE_FEATURE_FDISK_WRITABLE -static void -menu(void) -{ - puts("Command Action"); - if (LABEL_IS_AIX) { - puts("o\tcreate a new empty DOS partition table"); - puts("q\tquit without saving changes"); - //deleted: puts("s\tcreate a new empty Sun disklabel"); /* sun */ - } else if (LABEL_IS_GPT) { - puts("o\tcreate a new empty DOS partition table"); - puts("p\tprint partition table"); - puts("q\tquit without saving changes"); - //deleted: puts("s\tcreate a new empty Sun disklabel"); /* sun */ - } else { - puts("a\ttoggle a bootable flag"); - puts("b\tedit bsd disklabel"); - puts("c\ttoggle the dos compatibility flag"); - puts("d\tdelete a partition"); - puts("l\tlist known partition types"); - puts("n\tadd a new partition"); - puts("o\tcreate a new empty DOS partition table"); - puts("p\tprint partition table"); - puts("q\tquit without saving changes"); - //deleted: puts("s\tcreate a new empty Sun disklabel"); /* sun */ - puts("t\tchange a partition's system id"); - puts("u\tchange display/entry units"); - puts("v\tverify partition table"); - puts("w\twrite table to disk and exit"); -#if ENABLE_FEATURE_FDISK_ADVANCED - puts("x\textra functionality (experts only)"); -#endif - } -} -#endif /* FEATURE_FDISK_WRITABLE */ - -#if ENABLE_FEATURE_FDISK_ADVANCED -static void -xmenu(void) -{ - puts("Command Action"); - puts("c\tchange number of cylinders"); - puts("h\tchange number of heads"); - puts("s\tchange number of sectors/track"); - if (LABEL_IS_AIX) { - puts("b\tmove beginning of data in a partition"); - puts("e\tprint extended partitions"); - } else { - puts("f\tfix partition order"); /* !aix */ - puts("b\tmove beginning of data in a partition"); - puts("e\tprint extended partitions"); - } - puts("d\tprint raw data in partition table"); - puts("p\tprint partition table"); - puts("v\tverify partition table"); - puts("w\twrite table to disk and exit"); - puts("q\tquit without saving changes"); - puts("r\treturn to main menu"); -} -#endif /* ADVANCED mode */ - #define get_sys_types() i386_sys_types static const char * @@ -1865,7 +1804,6 @@ fix_chain_of_logicals(void) ptes[j].changed = 1; } - static void fix_partition_table_order(void) { @@ -1903,7 +1841,7 @@ fix_partition_table_order(void) puts("Done"); } -#endif +#endif // ADVANCED static const char * chs_string11(unsigned cyl, unsigned head, unsigned sect) @@ -2087,6 +2025,134 @@ print_disklabel(int xtra) dos_print_disklabel(); } +static int +is_ide_cdrom_or_tape(const char *device) +{ + FILE *procf; + char buf[100]; + struct stat statbuf; + int is_ide = 0; + + /* No device was given explicitly, and we are trying some + likely things. But opening /dev/hdc may produce errors like + "hdc: tray open or drive not ready" + if it happens to be a CD-ROM drive. It even happens that + the process hangs on the attempt to read a music CD. + So try to be careful. This only works since 2.1.73. */ + + if (!is_prefixed_with(device, "/dev/hd")) + return 0; + + snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5); + procf = fopen_for_read(buf); + if (procf != NULL && fgets(buf, sizeof(buf), procf)) + is_ide = (is_prefixed_with(buf, "cdrom") || + is_prefixed_with(buf, "tape")); + else + /* Now when this proc file does not exist, skip the + device when it is read-only. */ + if (stat(device, &statbuf) == 0) + is_ide = ((statbuf.st_mode & 0222) == 0); + + if (procf) + fclose(procf); + return is_ide; +} + +static void +open_list_and_close(const char *device, int user_specified) +{ + int gb; + + disk_device = device; + if (setjmp(listingbuf)) + return; + if (!user_specified) + if (is_ide_cdrom_or_tape(device)) + return; + + /* Open disk_device, save file descriptor to dev_fd */ + errno = 0; + gb = get_boot(TRY_ONLY); + if (gb > 0) { /* I/O error */ + /* Ignore other errors, since we try IDE + and SCSI hard disks which may not be + installed on the system. */ + if (user_specified || errno == EACCES) + bb_perror_msg("can't open '%s'", device); + return; + } + + if (gb < 0) { /* no DOS signature */ + print_disk_name_and_sizes(); + print_disk_geometry(); + if (LABEL_IS_AIX) + goto ret; +#if ENABLE_FEATURE_OSF_LABEL + if (bsd_trydev(device) < 0) +#endif + printf("Disk %s doesn't contain a valid " + "partition table\n", device); + } else { + print_disklabel(0); +#if ENABLE_FEATURE_FDISK_WRITABLE + if (g_partitions > 4) { + delete_partition(ext_index); + } +#endif + } + ret: + close_dev_fd(); +} + +/* Is it a whole disk? The digit check is still useful + for Xen devices for example. */ +static int is_whole_disk(const char *disk) +{ + unsigned len; + int fd = open(disk, O_RDONLY); + + if (fd != -1) { + struct hd_geometry geometry; + int err = ioctl(fd, HDIO_GETGEO, &geometry); + close(fd); + if (!err) + return (geometry.start == 0); + } + + /* Treat "nameN" as a partition name, not whole disk */ + /* note: mmcblk0 should work from the geometry check above */ + len = strlen(disk); + if (len != 0 && isdigit(disk[len - 1])) + return 0; + + return 1; +} + +/* for fdisk -l: try all things in /proc/partitions + that look like a partition name (do not end in a digit) */ +static void +list_devs_in_proc_partititons(void) +{ + FILE *procpt; + char line[100], ptname[100], devname[120]; + int ma, mi, sz; + + procpt = fopen_or_warn("/proc/partitions", "r"); + + while (fgets(line, sizeof(line), procpt)) { + if (sscanf(line, " %u %u %u %[^\n ]", + &ma, &mi, &sz, ptname) != 4) + continue; + + sprintf(devname, "/dev/%s", ptname); + if (is_whole_disk(devname)) + open_list_and_close(devname, 0); + } + if (ENABLE_FEATURE_CLEAN_UP) + fclose(procpt); +} + #if ENABLE_FEATURE_FDISK_WRITABLE static void fill_bounds(sector_t *first, sector_t *last) @@ -2449,9 +2515,8 @@ write_table_and_exit(void) reread_partition_table(1); // 1: exit bb_unreachable(abort()); } -#endif /* FEATURE_FDISK_WRITABLE */ -#if ENABLE_FEATURE_FDISK_ADVANCED +# if ENABLE_FEATURE_FDISK_ADVANCED #define MAX_PER_LINE 16 static void print_buffer(uint8_t *pbuffer) @@ -2509,6 +2574,65 @@ move_begin(unsigned i) pe->changed = 1; } } +# endif /* ADVANCED mode */ + +static void +menu(void) +{ + puts("Command Action"); + if (LABEL_IS_AIX) { + puts("o\tcreate a new empty DOS partition table"); + puts("q\tquit without saving changes"); + //deleted: puts("s\tcreate a new empty Sun disklabel"); /* sun */ + } else if (LABEL_IS_GPT) { + puts("o\tcreate a new empty DOS partition table"); + puts("p\tprint partition table"); + puts("q\tquit without saving changes"); + //deleted: puts("s\tcreate a new empty Sun disklabel"); /* sun */ + } else { + puts("a\ttoggle a bootable flag"); + puts("b\tedit bsd disklabel"); + puts("c\ttoggle the dos compatibility flag"); + puts("d\tdelete a partition"); + puts("l\tlist known partition types"); + puts("n\tadd a new partition"); + puts("o\tcreate a new empty DOS partition table"); + puts("p\tprint partition table"); + puts("q\tquit without saving changes"); + //deleted: puts("s\tcreate a new empty Sun disklabel"); /* sun */ + puts("t\tchange a partition's system id"); + puts("u\tchange display/entry units"); + puts("v\tverify partition table"); + puts("w\twrite table to disk and exit"); +# if ENABLE_FEATURE_FDISK_ADVANCED + puts("x\textra functionality (experts only)"); +# endif + } +} + +# if ENABLE_FEATURE_FDISK_ADVANCED +static void +xmenu(void) +{ + puts("Command Action"); + puts("c\tchange number of cylinders"); + puts("h\tchange number of heads"); + puts("s\tchange number of sectors/track"); + if (LABEL_IS_AIX) { + puts("b\tmove beginning of data in a partition"); + puts("e\tprint extended partitions"); + } else { + puts("f\tfix partition order"); /* !aix */ + puts("b\tmove beginning of data in a partition"); + puts("e\tprint extended partitions"); + } + puts("d\tprint raw data in partition table"); + puts("p\tprint partition table"); + puts("v\tverify partition table"); + puts("w\twrite table to disk and exit"); + puts("q\tquit without saving changes"); + puts("r\treturn to main menu"); +} static void xselect(void) @@ -2594,145 +2718,14 @@ xselect(void) } } } -#endif /* ADVANCED mode */ - -static int -is_ide_cdrom_or_tape(const char *device) -{ - FILE *procf; - char buf[100]; - struct stat statbuf; - int is_ide = 0; - - /* No device was given explicitly, and we are trying some - likely things. But opening /dev/hdc may produce errors like - "hdc: tray open or drive not ready" - if it happens to be a CD-ROM drive. It even happens that - the process hangs on the attempt to read a music CD. - So try to be careful. This only works since 2.1.73. */ - - if (!is_prefixed_with(device, "/dev/hd")) - return 0; - - snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5); - procf = fopen_for_read(buf); - if (procf != NULL && fgets(buf, sizeof(buf), procf)) - is_ide = (is_prefixed_with(buf, "cdrom") || - is_prefixed_with(buf, "tape")); - else - /* Now when this proc file does not exist, skip the - device when it is read-only. */ - if (stat(device, &statbuf) == 0) - is_ide = ((statbuf.st_mode & 0222) == 0); - - if (procf) - fclose(procf); - return is_ide; -} - - -static void -open_list_and_close(const char *device, int user_specified) -{ - int gb; - - disk_device = device; - if (setjmp(listingbuf)) - return; - if (!user_specified) - if (is_ide_cdrom_or_tape(device)) - return; - - /* Open disk_device, save file descriptor to dev_fd */ - errno = 0; - gb = get_boot(TRY_ONLY); - if (gb > 0) { /* I/O error */ - /* Ignore other errors, since we try IDE - and SCSI hard disks which may not be - installed on the system. */ - if (user_specified || errno == EACCES) - bb_perror_msg("can't open '%s'", device); - return; - } - - if (gb < 0) { /* no DOS signature */ - print_disk_name_and_sizes(); - print_disk_geometry(); - if (LABEL_IS_AIX) - goto ret; -#if ENABLE_FEATURE_OSF_LABEL - if (bsd_trydev(device) < 0) -#endif - printf("Disk %s doesn't contain a valid " - "partition table\n", device); - } else { - print_disklabel(0); -#if ENABLE_FEATURE_FDISK_WRITABLE - if (g_partitions > 4) { - delete_partition(ext_index); - } -#endif - } - ret: - close_dev_fd(); -} +# endif /* ADVANCED mode */ -/* Is it a whole disk? The digit check is still useful - for Xen devices for example. */ -static int is_whole_disk(const char *disk) -{ - unsigned len; - int fd = open(disk, O_RDONLY); - - if (fd != -1) { - struct hd_geometry geometry; - int err = ioctl(fd, HDIO_GETGEO, &geometry); - close(fd); - if (!err) - return (geometry.start == 0); - } - - /* Treat "nameN" as a partition name, not whole disk */ - /* note: mmcblk0 should work from the geometry check above */ - len = strlen(disk); - if (len != 0 && isdigit(disk[len - 1])) - return 0; - - return 1; -} - -/* for fdisk -l: try all things in /proc/partitions - that look like a partition name (do not end in a digit) */ -static void -list_devs_in_proc_partititons(void) -{ - FILE *procpt; - char line[100], ptname[100], devname[120]; - int ma, mi, sz; - - procpt = fopen_or_warn("/proc/partitions", "r"); - - while (fgets(line, sizeof(line), procpt)) { - if (sscanf(line, " %u %u %u %[^\n ]", - &ma, &mi, &sz, ptname) != 4) - continue; - - sprintf(devname, "/dev/%s", ptname); - if (is_whole_disk(devname)) - open_list_and_close(devname, 0); - } -#if ENABLE_FEATURE_CLEAN_UP - fclose(procpt); -#endif -} - -#if ENABLE_FEATURE_FDISK_WRITABLE static void unknown_command(int c) { printf("%c: unknown command\n", c); } -#endif +#endif /* FEATURE_FDISK_WRITABLE */ int fdisk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int fdisk_main(int argc UNUSED_PARAM, char **argv) From vda.linux at googlemail.com Sun Sep 27 18:32:07 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sun, 27 Sep 2026 20:32:07 +0200 Subject: [git commit] fdisk: standardize on XYZ_print_disklabel() as the function for 'p' Message-ID: <20260928012233.DC8278D9DB@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=ef63f4dec200c75ce53f329f6a4b8811992f8535 branch: https://git.busybox.net/busybox/log/?h=master list_disk_name_and_sizes() shouldn't be used by gpt's print_disklabel(), just use it unconditionally for all partition types. function old new delta static.print_disklabel - 775 +775 x_dos_print_disklabel - 227 +227 print_disk_name_and_sizes - 80 +80 print_disk_geometry - 66 +66 open_list_and_close 419 424 +5 .rodata 104928 104913 -15 list_disk_geometry 71 - -71 list_disk_name_and_sizes 80 - -80 x_list_table 227 - -227 static.list_table 775 - -775 ------------------------------------------------------------------------------ (add/remove: 4/4 grow/shrink: 1/1 up/down: 1153/-1168) Total: -15 bytes Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 157 ++++++++++++++++++++++++++++--------------------- util-linux/fdisk_gpt.c | 3 +- util-linux/fdisk_osf.c | 2 +- 3 files changed, 92 insertions(+), 70 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 18ea4d370..e8871567c 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -271,7 +271,6 @@ static int get_boot(void); #endif static sector_t get_start_sect(const struct dos_partition *p); static sector_t get_nr_sects(const struct dos_partition *p); -static void list_disk_name_and_sizes(void); /* DOS partition types */ @@ -734,7 +733,7 @@ STATIC_OSF void if_osf_label_loop_in_menu_until_r(void); STATIC_OSF void xbsd_print_disklabel(int); #include "fdisk_osf.c" -STATIC_GPT void gpt_list_table(int xtra); +STATIC_GPT void gpt_print_disklabel(void); #include "fdisk_gpt.c" static inline_if_little_endian uint32_t @@ -811,7 +810,7 @@ menu(void) //deleted: puts("s\tcreate a new empty Sun disklabel"); /* sun */ } else if (LABEL_IS_GPT) { puts("o\tcreate a new empty DOS partition table"); - puts("p\tprint the partition table"); + puts("p\tprint partition table"); puts("q\tquit without saving changes"); //deleted: puts("s\tcreate a new empty Sun disklabel"); /* sun */ } else { @@ -822,12 +821,12 @@ menu(void) puts("l\tlist known partition types"); puts("n\tadd a new partition"); puts("o\tcreate a new empty DOS partition table"); - puts("p\tprint the partition table"); + puts("p\tprint partition table"); puts("q\tquit without saving changes"); //deleted: puts("s\tcreate a new empty Sun disklabel"); /* sun */ puts("t\tchange a partition's system id"); puts("u\tchange display/entry units"); - puts("v\tverify the partition table"); + puts("v\tverify partition table"); puts("w\twrite table to disk and exit"); #if ENABLE_FEATURE_FDISK_ADVANCED puts("x\textra functionality (experts only)"); @@ -846,15 +845,15 @@ xmenu(void) puts("s\tchange number of sectors/track"); if (LABEL_IS_AIX) { puts("b\tmove beginning of data in a partition"); - puts("e\tlist extended partitions"); + puts("e\tprint extended partitions"); } else { puts("f\tfix partition order"); /* !aix */ puts("b\tmove beginning of data in a partition"); - puts("e\tlist extended partitions"); + puts("e\tprint extended partitions"); } - puts("d\tprint the raw data in the partition table"); - puts("p\tprint the partition table"); - puts("v\tverify the partition table"); + puts("d\tprint raw data in partition table"); + puts("p\tprint partition table"); + puts("v\tverify partition table"); puts("w\twrite table to disk and exit"); puts("q\tquit without saving changes"); puts("r\treturn to main menu"); @@ -1765,37 +1764,6 @@ check_consistency(const struct dos_partition *p, int partition) } } -static void -list_disk_name_and_sizes(void) -{ - char numstr6[6]; - ullong total_bytes; - - 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", - g_cylinders, g_heads, g_sectors, - str_units(), - units_per_sector, sector_size, units_per_sector * sector_size - ); -} - /* * Check whether partition entries are ordered by their starting positions. * Return 0 if OK. Return i if partition i should have been earlier. @@ -1945,27 +1913,13 @@ chs_string11(unsigned cyl, unsigned head, unsigned sect) return buf; } -static void -list_table(int xtra) +static void dos_print_disklabel(void) { int i, w; - if (LABEL_IS_GPT) { - gpt_list_table(xtra); - return; - } - - list_disk_geometry(); - - if (LABEL_IS_OSF) { - xbsd_print_disklabel(xtra); - return; - } - - /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3, - * but if the device name ends in a digit, say /dev/foo1, - * then the partition is called /dev/foo1p3. - */ + // Heuristic: we list partition 3 of /dev/foo as /dev/foo3, + // but if the device name ends in a digit, say /dev/foo1, + // then the partition is called /dev/foo1p3. w = strlen(disk_device); if (w && isdigit(disk_device[w-1])) w++; @@ -2006,7 +1960,7 @@ list_table(int xtra) #define SFMT SECT_FMT // Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type - printf("%s%s %-11s"/**/" %-11s"/**/" %10"SFMT"u %10"SFMT"u %10"SFMT"u %s %2x %s\n", + printf("%s%s %-11s"/**/" %-11s"/**/" %10"SFMT"u %10"SFMT"u %10"SFMT"u" " %s %2x %s\n", partname(disk_device, i+1, w+2), boot4, chs_string11(p->cyl, p->head, p->sector), @@ -2033,7 +1987,7 @@ list_table(int xtra) #if ENABLE_FEATURE_FDISK_ADVANCED static void -x_list_table(int extend) +x_dos_print_disklabel(int extend) { const struct pte *pe; const struct dos_partition *p; @@ -2065,6 +2019,74 @@ x_list_table(int extend) } #endif +static void +print_disk_name_and_sizes(void) +{ + char numstr6[6]; + ullong total_bytes; + + 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 + ); + // util-linux 2.41.1 also says after the above: + //Disk model: (string from /sys/dev/block/MAJ:MIN/device/model) + //Units: sectors of 1 * 512 = 512 bytes + //Sector size (logical/physical): 512 bytes / 512 bytes + //I/O size (minimum/optimal): 512 bytes / 512 bytes + //Disklabel type: gpt + //Disk identifier: FEEDC0DE-BEEF-F00D-FEDC-F0CACC1A1234 + // or + //Disklabel type: dos + //Disk identifier: 0xdeafbead + // + // On a all-zeros image file, these lines reduce to: + //Units: sectors of 1 * 512 = 512 bytes + //Sector size (logical/physical): 512 bytes / 512 bytes + //I/O size (minimum/optimal): 512 bytes / 512 bytes + // (yes, with -l, not even "doesn't contain a valid partition table" shown!) +} + +static void +print_disk_geometry(void) +{ + printf( + "%u cylinders, %u heads, %u sectors/track\n" + "Units: %ss of %u * %u = %u bytes\n" + "\n", + g_cylinders, g_heads, g_sectors, + str_units(), + units_per_sector, sector_size, units_per_sector * sector_size + ); +} + +static void +print_disklabel(int xtra) +{ + // Print common header for all formats + print_disk_name_and_sizes(); + + if (LABEL_IS_GPT) { + gpt_print_disklabel(); + return; + } + // GPT does not talk about cylinders/heads/sectors, other formats do + print_disk_geometry(); + + if (LABEL_IS_OSF) { + xbsd_print_disklabel(xtra); + return; + } + + // So far AIX also goes here - FIXME? + dos_print_disklabel(); +} + #if ENABLE_FEATURE_FDISK_WRITABLE static void fill_bounds(sector_t *first, sector_t *last) @@ -2515,9 +2537,9 @@ xselect(void) case 'd': print_raw(); break; - case 'e': + case 'e': // "list extended partitions" if (LABEL_IS_DOS) - x_list_table(1); + x_dos_print_disklabel(1); break; case 'f': if (LABEL_IS_DOS) @@ -2539,7 +2561,7 @@ xselect(void) // if (LABEL_IS_SUN) sun_set_rspeed(); // break; case 'p': - x_list_table(0); + x_dos_print_disklabel(0); break; case 'q': if (ENABLE_FEATURE_CLEAN_UP) @@ -2634,7 +2656,8 @@ open_list_and_close(const char *device, int user_specified) } if (gb < 0) { /* no DOS signature */ - list_disk_geometry(); + print_disk_name_and_sizes(); + print_disk_geometry(); if (LABEL_IS_AIX) goto ret; #if ENABLE_FEATURE_OSF_LABEL @@ -2643,7 +2666,7 @@ open_list_and_close(const char *device, int user_specified) printf("Disk %s doesn't contain a valid " "partition table\n", device); } else { - list_table(0); + print_disklabel(0); #if ENABLE_FEATURE_FDISK_WRITABLE if (g_partitions > 4) { delete_partition(ext_index); @@ -2859,7 +2882,7 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv) dos_reset_primary_partition_info(); break; case 'p': - list_table(0); + print_disklabel(0); break; case 'q': if (ENABLE_FEATURE_CLEAN_UP) diff --git a/util-linux/fdisk_gpt.c b/util-linux/fdisk_gpt.c index ff22c566f..c50d0da1b 100644 --- a/util-linux/fdisk_gpt.c +++ b/util-linux/fdisk_gpt.c @@ -97,12 +97,11 @@ gpt_print_wide36(uint16_t *s) } static void -gpt_list_table(int xtra UNUSED_PARAM) +gpt_print_disklabel(void) { int i; char numstr6[6]; - 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, diff --git a/util-linux/fdisk_osf.c b/util-linux/fdisk_osf.c index fc1b5ec4f..28569a956 100644 --- a/util-linux/fdisk_osf.c +++ b/util-linux/fdisk_osf.c @@ -925,7 +925,7 @@ xbsd_link_part(void) #endif static void -if_osf_label_loop_forever_in_menu(void) +if_osf_label_loop_in_menu_until_r(void) { #if !defined(__alpha__) int t, ss; From vda.linux at googlemail.com Sun Sep 27 17:44:34 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sun, 27 Sep 2026 19:44:34 +0200 Subject: [git commit] fdisk: rename bsd_select() -> if_osf_label_loop_in_menu_until_r() Message-ID: <20260928012233.CEC0592011@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=6853c796be0cdd3fffc3fdf6bd8aa8bbdfa85ded branch: https://git.busybox.net/busybox/log/?h=master Much clearer to read the call site. While at it, shiffle fdisk_osf.c to reduce forward references. Shorten sprintf() in xbsd_get_part_index(). function old new delta if_osf_label_loop_in_menu_until_r - 1546 +1546 xbsd_get_part_index 69 67 -2 bsd_select 1546 - -1546 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 0/1 up/down: 1546/-1548) Total: -2 bytes Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 21 +-- util-linux/fdisk_osf.c | 401 +++++++++++++++++++++++-------------------------- 2 files changed, 197 insertions(+), 225 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 4b912f4b5..18ea4d370 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -730,7 +730,7 @@ write_sector(sector_t secno, const void *buf) #include "fdisk_aix.c" -STATIC_OSF void bsd_select(void); +STATIC_OSF void if_osf_label_loop_in_menu_until_r(void); STATIC_OSF void xbsd_print_disklabel(int); #include "fdisk_osf.c" @@ -2776,12 +2776,8 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv) user_heads = 0; if (user_sectors <= 0 || user_sectors >= 64) user_sectors = 0; - //if (opt & OPT_u) - // DISPLAY_IN_CYL_UNITS = 0; -#if ENABLE_FEATURE_FDISK_WRITABLE - if (opt & OPT_l) { -#endif + if (!ENABLE_FEATURE_FDISK_WRITABLE || (opt & OPT_l)) { if (*argv) { do { open_list_and_close(*argv, 1); @@ -2792,9 +2788,9 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv) list_devs_in_proc_partititons(); } return 0; -#if ENABLE_FEATURE_FDISK_WRITABLE } +#if ENABLE_FEATURE_FDISK_WRITABLE if (!argv[0] || argv[1]) bb_show_usage(); @@ -2809,13 +2805,12 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv) get_boot(OPEN_MAIN); if (LABEL_IS_OSF) { - /* OSF label, and no DOS label */ + // OSF label, and no DOS label printf("Detected an OSF/1 disklabel on %s, entering " "disklabel mode\n", disk_device); - bsd_select(); - /*Why do we do this? It seems to be counter-intuitive*/ - current_label_type = LABEL_DOS; - /* If we return we may want to make an empty DOS label? */ + if_osf_label_loop_in_menu_until_r(); + // ^^^ returned: it's not an OSF, or user selected 'return to main menu' + current_label_type = LABEL_DOS; // Why do we do this? It seems counter-intuitive } while (1) { @@ -2831,7 +2826,7 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv) break; case 'b': # if ENABLE_FEATURE_OSF_LABEL - bsd_select(); + if_osf_label_loop_in_menu_until_r(); # endif break; case 'c': diff --git a/util-linux/fdisk_osf.c b/util-linux/fdisk_osf.c index 609a61523..fc1b5ec4f 100644 --- a/util-linux/fdisk_osf.c +++ b/util-linux/fdisk_osf.c @@ -30,7 +30,14 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +/* + Changes: + 19990319 - Arnaldo Carvalho de Melo - i18n/nls + 20000101 - David Huggins-Daines - Better + support for OSF/1 disklabels on Alpha. + Also fixed unaligned accesses in alpha_bootblock_checksum() +*/ #if ENABLE_FEATURE_OSF_LABEL #ifndef BSD_DISKMAGIC @@ -159,7 +166,6 @@ static const char *const xbsd_dktypenames[] ALIGN_PTR = { 0 }; - /* * Filesystem type and version. * Used to interpret other filesystem-specific @@ -215,7 +221,6 @@ static const char *const xbsd_fstypes[] ALIGN_PTR = { NULL }; - /* * flags shared by various drives: */ @@ -226,31 +231,10 @@ static const char *const xbsd_fstypes[] ALIGN_PTR = { #define BSD_D_CHAIN 0x10 /* can do back-back transfers */ #define BSD_D_DOSPART 0x20 /* within MSDOS partition */ -/* - Changes: - 19990319 - Arnaldo Carvalho de Melo - i18n/nls - - 20000101 - David Huggins-Daines - Better - support for OSF/1 disklabels on Alpha. - Also fixed unaligned accesses in alpha_bootblock_checksum() -*/ - #define FREEBSD_PARTITION 0xa5 #define NETBSD_PARTITION 0xa9 -static void xbsd_delete_part(void); -static void xbsd_new_part(void); -static void xbsd_write_disklabel(void); -static int xbsd_create_disklabel(void); -static void xbsd_edit_disklabel(void); -static void xbsd_write_bootstrap(void); -static void xbsd_change_fstype(void); -static int xbsd_get_part_index(int max); static int xbsd_check_new_partition(int *i); -static void xbsd_list_types(void); -static uint16_t xbsd_dkcksum(struct xbsd_disklabel *lp); -static int xbsd_initlabel(struct dos_partition *p); -static int xbsd_readlabel(struct dos_partition *p); static int xbsd_writelabel(struct dos_partition *p); #if defined(__alpha__) @@ -273,7 +257,6 @@ static int xbsd_translate_fstype(int linux_type); static void xbsd_link_part(void); #endif - /* Group big globals data and allocate it in one go */ struct bsd_globals { /* We access this through a uint64_t * when checksumming */ @@ -287,36 +270,11 @@ static struct bsd_globals *bsd_globals_ptr; #define disklabelbuffer (bsd_globals_ptr->disklabelbuffer) #define xbsd_dlabel (bsd_globals_ptr->xbsd_dlabel) - /* Code */ #define bsd_cround(n) \ (DISPLAY_IN_CYL_UNITS ? ((n)/xbsd_dlabel.d_secpercyl) + 1 : (n)) -/* - * Test whether the whole disk has BSD disk label magic. - * - * Note: often reformatting with DOS-type label leaves the BSD magic, - * so this does not mean that there is a BSD disk label. - */ -static int -check_osf_label(void) -{ - if (xbsd_readlabel(NULL) == 0) - return 0; - return 1; -} - -static int -bsd_trydev(const char * dev) -{ - if (xbsd_readlabel(NULL) == 0) - return -1; - printf("\nBSD label for device: %s\n", dev); - xbsd_print_disklabel(0); - return 0; -} - static void bsd_menu(void) { @@ -338,6 +296,19 @@ bsd_menu(void) #endif } +static int +xbsd_get_part_index(int max) +{ + char prompt[sizeof("Partition (a-%c): ") + 16]; + char l; + + sprintf(prompt, "Partition (a-%c): ", 'a' + max - 1); + do + l = tolower(read_nonempty(prompt)); + while (l < 'a' || l > 'a' + max - 1); + return l - 'a'; +} + #if !defined(__alpha__) static int hidden(int type) @@ -356,95 +327,65 @@ is_bsd_partition_type(int type) #endif static void -bsd_select(void) +xbsd_initlabel(struct dos_partition *p) { -#if !defined(__alpha__) - int t, ss; - struct dos_partition *p; - - for (t = 0; t < 4; t++) { - p = get_part_table(t); - if (p && is_bsd_partition_type(p->sys_ind)) { - G.xbsd_part = p; - G.xbsd_part_index = t; - ss = get_start_sect(G.xbsd_part); - if (ss == 0) { - printf("Partition %s has invalid starting sector 0\n", - partname(disk_device, t+1, 0)); - return; - } - printf("Reading disklabel of %s at sector %u\n", - partname(disk_device, t+1, 0), ss + BSD_LABELSECTOR); - if (xbsd_readlabel(G.xbsd_part) == 0) { - if (xbsd_create_disklabel() == 0) - return; - break; - } - } - } + struct xbsd_disklabel *d = &xbsd_dlabel; + struct xbsd_partition *pp; - if (t == 4) { - printf("There is no *BSD partition on %s\n", disk_device); - return; - } + get_geometry(); + memset(d, 0, sizeof(struct xbsd_disklabel)); -#elif defined(__alpha__) + d->d_magic = BSD_DISKMAGIC; - if (xbsd_readlabel(NULL) == 0) - if (xbsd_create_disklabel() == 0) - exit_SUCCESS(); + if (is_prefixed_with(disk_device, "/dev/sd")) + d->d_type = BSD_DTYPE_SCSI; + else + d->d_type = BSD_DTYPE_ST506; +#if !defined(__alpha__) + d->d_flags = BSD_D_DOSPART; +#else + d->d_flags = 0; #endif + d->d_secsize = SECTOR_SIZE; /* bytes/sector */ + d->d_nsectors = g_sectors; /* sectors/track */ + d->d_ntracks = g_heads; /* tracks/cylinder (heads) */ + d->d_ncylinders = g_cylinders; + d->d_secpercyl = g_sectors * g_heads;/* sectors/cylinder */ + if (d->d_secpercyl == 0) + d->d_secpercyl = 1; /* avoid segfaults */ + d->d_secperunit = d->d_secpercyl * d->d_ncylinders; + + d->d_rpm = 3600; + d->d_interleave = 1; + d->d_trackskew = 0; + d->d_cylskew = 0; + d->d_headswitch = 0; + d->d_trkseek = 0; + + d->d_magic2 = BSD_DISKMAGIC; + d->d_bbsize = BSD_BBSIZE; + d->d_sbsize = BSD_SBSIZE; - while (1) { - bb_putchar('\n'); - switch (tolower(read_nonempty("BSD disklabel command (m for help): "))) { - case 'd': - xbsd_delete_part(); - break; - case 'e': - xbsd_edit_disklabel(); - break; - case 'i': - xbsd_write_bootstrap(); - break; - case 'l': - xbsd_list_types(); - break; - case 'n': - xbsd_new_part(); - break; - case 'p': - xbsd_print_disklabel(0); - break; - case 'q': - if (ENABLE_FEATURE_CLEAN_UP) - close_dev_fd(); - exit_SUCCESS(); - case 'r': - return; - case 's': - xbsd_print_disklabel(1); - break; - case 't': - xbsd_change_fstype(); - break; - case 'u': - change_units(); - break; - case 'w': - xbsd_write_disklabel(); - break; #if !defined(__alpha__) - case 'x': - xbsd_link_part(); - break; + d->d_npartitions = 4; + pp = &d->d_partitions[2]; /* Partition C should be NetBSD partition */ + + pp->p_offset = get_start_sect(p); + pp->p_size = get_nr_sects(p); + pp->p_fstype = BSD_FS_UNUSED; + pp = &d->d_partitions[3]; /* Partition D should be whole disk */ + + pp->p_offset = 0; + pp->p_size = d->d_secperunit; + pp->p_fstype = BSD_FS_UNUSED; +#else + d->d_npartitions = 3; + pp = &d->d_partitions[2]; /* Partition C should be the whole disk */ + pp->p_offset = 0; + pp->p_size = d->d_secperunit; + pp->p_fstype = BSD_FS_UNUSED; #endif - default: - bsd_menu(); - break; - } - } } static void @@ -623,18 +564,16 @@ xbsd_create_disklabel(void) while (1) { c = read_nonempty("Do you want to create a disklabel? (y/n) "); if ((c|0x20) == 'y') { - if (xbsd_initlabel( + xbsd_initlabel( #if defined(__alpha__) || defined(__powerpc__) || defined(__hppa__) || \ defined(__s390__) || defined(__s390x__) NULL #else G.xbsd_part #endif - ) == 1) { - xbsd_print_disklabel(1); - return 1; - } - return 0; + ); + xbsd_print_disklabel(1); + return 1; } if ((c|0x20) == 'n') return 0; @@ -795,19 +734,6 @@ xbsd_change_fstype(void) xbsd_dlabel.d_partitions[i].p_fstype = read_hex(xbsd_fstypes); } -static int -xbsd_get_part_index(int max) -{ - char prompt[sizeof("Partition (a-%c): ") + 16]; - char l; - - snprintf(prompt, sizeof(prompt), "Partition (a-%c): ", 'a' + max - 1); - do - l = tolower(read_nonempty(prompt)); - while (l < 'a' || l > 'a' + max - 1); - return l - 'a'; -} - static int xbsd_check_new_partition(int *i) { @@ -857,70 +783,6 @@ xbsd_dkcksum(struct xbsd_disklabel *lp) return sum; } -static int -xbsd_initlabel(struct dos_partition *p) -{ - struct xbsd_disklabel *d = &xbsd_dlabel; - struct xbsd_partition *pp; - - get_geometry(); - memset(d, 0, sizeof(struct xbsd_disklabel)); - - d->d_magic = BSD_DISKMAGIC; - - if (is_prefixed_with(disk_device, "/dev/sd")) - d->d_type = BSD_DTYPE_SCSI; - else - d->d_type = BSD_DTYPE_ST506; - -#if !defined(__alpha__) - d->d_flags = BSD_D_DOSPART; -#else - d->d_flags = 0; -#endif - d->d_secsize = SECTOR_SIZE; /* bytes/sector */ - d->d_nsectors = g_sectors; /* sectors/track */ - d->d_ntracks = g_heads; /* tracks/cylinder (heads) */ - d->d_ncylinders = g_cylinders; - d->d_secpercyl = g_sectors * g_heads;/* sectors/cylinder */ - if (d->d_secpercyl == 0) - d->d_secpercyl = 1; /* avoid segfaults */ - d->d_secperunit = d->d_secpercyl * d->d_ncylinders; - - d->d_rpm = 3600; - d->d_interleave = 1; - d->d_trackskew = 0; - d->d_cylskew = 0; - d->d_headswitch = 0; - d->d_trkseek = 0; - - d->d_magic2 = BSD_DISKMAGIC; - d->d_bbsize = BSD_BBSIZE; - d->d_sbsize = BSD_SBSIZE; - -#if !defined(__alpha__) - d->d_npartitions = 4; - pp = &d->d_partitions[2]; /* Partition C should be NetBSD partition */ - - pp->p_offset = get_start_sect(p); - pp->p_size = get_nr_sects(p); - pp->p_fstype = BSD_FS_UNUSED; - pp = &d->d_partitions[3]; /* Partition D should be whole disk */ - - pp->p_offset = 0; - pp->p_size = d->d_secperunit; - pp->p_fstype = BSD_FS_UNUSED; -#else - d->d_npartitions = 3; - pp = &d->d_partitions[2]; /* Partition C should be the whole disk */ - pp->p_offset = 0; - pp->p_size = d->d_secperunit; - pp->p_fstype = BSD_FS_UNUSED; -#endif - - return 1; -} - /* * Read a xbsd_disklabel from sector 0 or from the starting sector of p. * If it has the right magic, return 1. @@ -1000,6 +862,29 @@ xbsd_writelabel(struct dos_partition *p) return 1; } +/* + * Test whether the whole disk has BSD disk label magic. + * + * Note: often reformatting with DOS-type label leaves the BSD magic, + * so this does not mean that there is a BSD disk label. + */ +static int +check_osf_label(void) +{ + if (xbsd_readlabel(NULL) == 0) + return 0; + return 1; +} + +static int +bsd_trydev(const char * dev) +{ + if (xbsd_readlabel(NULL) == 0) + return -1; + printf("\nBSD label for device: %s\n", dev); + xbsd_print_disklabel(0); + return 0; +} #if !defined(__alpha__) static int @@ -1039,6 +924,98 @@ xbsd_link_part(void) } #endif +static void +if_osf_label_loop_forever_in_menu(void) +{ +#if !defined(__alpha__) + int t, ss; + struct dos_partition *p; + + for (t = 0; t < 4; t++) { + p = get_part_table(t); + if (p && is_bsd_partition_type(p->sys_ind)) { + G.xbsd_part = p; + G.xbsd_part_index = t; + ss = get_start_sect(G.xbsd_part); + if (ss == 0) { + printf("Partition %s has invalid starting sector 0\n", + partname(disk_device, t+1, 0)); + return; + } + printf("Reading disklabel of %s at sector %u\n", + partname(disk_device, t+1, 0), ss + BSD_LABELSECTOR); + if (xbsd_readlabel(G.xbsd_part) == 0) { + if (xbsd_create_disklabel() == 0) + return; + break; + } + } + } + + if (t == 4) { + printf("There is no *BSD partition on %s\n", disk_device); + return; + } + +#elif defined(__alpha__) + + if (xbsd_readlabel(NULL) == 0) + if (xbsd_create_disklabel() == 0) + exit_SUCCESS(); + +#endif + + while (1) { + bb_putchar('\n'); + switch (tolower(read_nonempty("BSD disklabel command (m for help): "))) { + case 'd': + xbsd_delete_part(); + break; + case 'e': + xbsd_edit_disklabel(); + break; + case 'i': + xbsd_write_bootstrap(); + break; + case 'l': + xbsd_list_types(); + break; + case 'n': + xbsd_new_part(); + break; + case 'p': + xbsd_print_disklabel(0); + break; + case 'q': + if (ENABLE_FEATURE_CLEAN_UP) + close_dev_fd(); + exit_SUCCESS(); + case 'r': + return; + case 's': + xbsd_print_disklabel(1); + break; + case 't': + xbsd_change_fstype(); + break; + case 'u': + change_units(); + break; + case 'w': + xbsd_write_disklabel(); + break; +#if !defined(__alpha__) + case 'x': + xbsd_link_part(); + break; +#endif + default: + bsd_menu(); + break; + } + } +} + /* Undefine 'global' tricks */ #undef disklabelbuffer #undef xbsd_dlabel From vda.linux at googlemail.com Mon Sep 28 09:16:02 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 28 Sep 2026 11:16:02 +0200 Subject: [git commit] fdisk: DOS compat flag off by default - mimic util-linux 2.41.1 Message-ID: <20260928152446.0136C983C1@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=bfdf3560d64efc2b7819b629914195368bb4a7d2 branch: https://git.busybox.net/busybox/log/?h=master function old new delta fdisk_main 2610 2590 -20 Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 7ba5e7e92..0600e7817 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -404,8 +404,7 @@ struct globals { #if ENABLE_FEATURE_OSF_LABEL smallint possibly_osf_label; #endif - - smallint dos_compatible_flag; // = 1; + smallint dos_compatible_flag; #if ENABLE_FEATURE_OSF_LABEL # if !defined(__alpha__) struct dos_partition *xbsd_part; @@ -468,7 +467,8 @@ struct globals { offset_after_MBR_and_ext = 1; \ g_partitions = 4; \ units_per_sector = 1; \ - dos_compatible_flag = 1; \ + /* off by default in util-linux 2.41.1: */ \ + /* dos_compatible_flag = 1; */ \ } while (0) /* TODO: move to libbb? */ @@ -1519,13 +1519,8 @@ static void toggle_dos_compatibility_flag(void) { dos_compatible_flag = 1 - dos_compatible_flag; - if (dos_compatible_flag) { - offset_after_MBR_and_ext = g_sectors; - printf("DOS Compatibility flag is %sset (sector gap:%d)\n", "", offset_after_MBR_and_ext); - } else { - offset_after_MBR_and_ext = 1; - printf("DOS Compatibility flag is %sset (sector gap:%d)\n", "un", offset_after_MBR_and_ext); - } + offset_after_MBR_and_ext = dos_compatible_flag ? g_sectors : 1; + printf("DOS Compatibility flag is %sset (sector gap:%d)\n", "", offset_after_MBR_and_ext); } static void @@ -2264,13 +2259,14 @@ static void set_hsc_start_end(struct dos_partition *p, sector_t start, sector_t stop) { #define SET_HEAD_SECT_CYL(h, s, c, sector) do { \ - s = sector % g_sectors + 1; \ - sector /= g_sectors; \ - h = sector % g_heads; \ - sector /= g_heads; \ - c = sector & 0xff; \ - s |= (sector >> 2) & 0xc0; \ -} while (0) + s = sector % g_sectors + 1; \ + sector /= g_sectors; \ + h = sector % g_heads; \ + sector /= g_heads; \ + c = sector & 0xff; \ + s |= (sector >> 2) & 0xc0; \ + } while (0) + if (dos_compatible_flag && (start / (g_sectors * g_heads) > 1023)) start = g_heads * g_sectors * 1024 - 1; SET_HEAD_SECT_CYL(p->head, p->sector, p->cyl, start); From vda.linux at googlemail.com Mon Sep 28 02:04:56 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 28 Sep 2026 04:04:56 +0200 Subject: [git commit] fdisk: in add_partition(), only fully zeroed partition entry is free Message-ID: <20260928152445.ED5DF983C0@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=4c9b0e45dada0731c8caadda6653f80b14d55695 branch: https://git.busybox.net/busybox/log/?h=master function old new delta add_partition 1038 1041 +3 Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 331a20f39..7ba5e7e92 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -1466,7 +1466,7 @@ get_existing_partition(int warn, unsigned max) } static int -find_free_primary_partition(void) +choose_free_primary_partition(void) { int pno = -1; unsigned i; @@ -1521,10 +1521,10 @@ toggle_dos_compatibility_flag(void) dos_compatible_flag = 1 - dos_compatible_flag; if (dos_compatible_flag) { offset_after_MBR_and_ext = g_sectors; - printf("DOS Compatibility flag (%u sector gap) is %sset\n", offset_after_MBR_and_ext, ""); + printf("DOS Compatibility flag is %sset (sector gap:%d)\n", "", offset_after_MBR_and_ext); } else { offset_after_MBR_and_ext = 1; - printf("DOS Compatibility flag (%u sector gap) is %sset\n", offset_after_MBR_and_ext, "not "); + printf("DOS Compatibility flag is %sset (sector gap:%d)\n", "un", offset_after_MBR_and_ext); } } @@ -2306,17 +2306,19 @@ add_partition(int n, int sys) { char mesg[64]; int i, num_read; - struct dos_partition *p = ptes[n].part_table; - struct dos_partition *q = ptes[ext_index].part_table; + struct dos_partition *p; + struct dos_partition *main_ext; sector_t limit, temp; sector_t start, stop; sector_t first[g_partitions], last[g_partitions]; - if (p && p->sys_ind) { + p = ptes[n].part_table; + if (p && !is_cleared_partition(p)) { printf("Partition %u is already defined, delete it before re-adding\n", n + 1); return; } fill_bounds(first, last); + main_ext = ptes[ext_index].part_table; if (n < 4) { start = offset_after_MBR_and_ext; if (DISPLAY_IN_CYL_UNITS || !total_number_of_sectors) @@ -2325,12 +2327,11 @@ add_partition(int n, int sys) limit = total_number_of_sectors - 1; if (extended_offset) { first[ext_index] = extended_offset; - last[ext_index] = get_start_sect(q) + - get_nr_sects(q) - 1; + last[ext_index] = extended_offset + get_nr_sects(main_ext) - 1; } } else { start = extended_offset + offset_after_MBR_and_ext; - limit = get_start_sect(q) + get_nr_sects(q) - 1; + limit = extended_offset + get_nr_sects(main_ext) - 1; } if (DISPLAY_IN_CYL_UNITS) for (i = 0; i < g_partitions; i++) @@ -2490,7 +2491,7 @@ new_partition(void) if (c == 'p' || (c == 'e' && !extended_offset) ) { - free_primary = find_free_primary_partition(); + free_primary = choose_free_primary_partition(); //if (free_primary >= 0) // cannot fail, we know it exists add_partition(free_primary, c == 'p' ? LINUX_NATIVE : EXTENDED); return; From vda.linux at googlemail.com Mon Sep 28 10:22:10 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 28 Sep 2026 12:22:10 +0200 Subject: [git commit] fdisk: more readable code in read_extended_chain() Message-ID: <20260928152446.14BFC983C3@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=928b0384cfbff3eed655f87704361331035c895c branch: https://git.busybox.net/busybox/log/?h=master function old new delta get_boot 1426 1423 -3 Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index ca273e6d9..eac748580 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -1040,13 +1040,15 @@ read_extended_chain(int ext) remove: for (i = 4; i < g_partitions; i++) { struct pte *pe = &ptes[i]; - - if (get_nr_sects(pe->part_table) == 0 - && (g_partitions > 5 || ptes[4].part_table->sys_ind) - ) { - printf("Omitting empty partition (%u)\n", i + 1); - delete_partition(i); - goto remove; // numbering changed + if (get_nr_sects(pe->part_table) == 0) { + if (g_partitions == 5 && is_cleared_partition(pe->part_table)) { + // empty extended partition + // (need to keep it for its sector buffer?) + } else { + printf("Omitting empty partition (%u)\n", i + 1); + delete_partition(i); + goto remove; // numbering changed + } } } #endif From vda.linux at googlemail.com Mon Sep 28 09:44:00 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 28 Sep 2026 11:44:00 +0200 Subject: [git commit] fdisk: consider partition empty only when it's all-zeros, not merely type 0 Message-ID: <20260928152446.0A0A6983C2@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=ccf1e30fa2979b09aef31e05491db8f8c7881fe6 branch: https://git.busybox.net/busybox/log/?h=master Our own message in the source says that Linux does not consider type 0 as "empty". (Linux thinks that size of 0 sectors means "empty"). function old new delta verify 726 767 +41 wrong_p_order 86 119 +33 get_geometry 437 460 +23 get_boot 1412 1426 +14 fill_bounds 115 124 +9 x_dos_print_disklabel 227 232 +5 delete_partition 334 337 +3 add_logical 106 109 +3 fdisk_main 2590 2576 -14 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 8/1 up/down: 131/-14) Total: 117 bytes Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 0600e7817..ca273e6d9 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -1003,7 +1003,7 @@ read_extended_chain(int ext) " %u\n", "link", g_partitions + 1); else pe->ext_pointer = p; - } else if (p->sys_ind != 0) { + } else if (!is_cleared_partition(p)) { if (pe->part_table) printf("Warning: extra %s " "pointer in ext.partition chain" @@ -1036,17 +1036,17 @@ read_extended_chain(int ext) } #if ENABLE_FEATURE_FDISK_WRITABLE - /* remove empty links */ + // Remove empty data partitions in extended chain remove: for (i = 4; i < g_partitions; i++) { struct pte *pe = &ptes[i]; - if (!get_nr_sects(pe->part_table) + if (get_nr_sects(pe->part_table) == 0 && (g_partitions > 5 || ptes[4].part_table->sys_ind) ) { - printf("Omitting empty partition (%u)\n", i+1); + printf("Omitting empty partition (%u)\n", i + 1); delete_partition(i); - goto remove; /* numbering changed */ + goto remove; // numbering changed } } #endif @@ -1114,7 +1114,7 @@ get_partition_table_geometry(void) hh = ss = 0; for (i = 0; i < 4; i++) { p = pt_offset(bufp, i); - if (p->sys_ind != 0) { + if (!is_cleared_partition(p)) { h = p->end_head + 1; s = (p->end_sector & 077); if (first) { @@ -1432,7 +1432,7 @@ input_partition_number(int warn, unsigned max) if (warn) { if (pe->part_table->sys_ind == 0) { - printf("Warning: partition %u has type 0\n", i+1); + printf("Warning: partition %u has type 0\n", i + 1); } } return i; @@ -1548,7 +1548,7 @@ delete_partition(int i) return; } - if (!q->sys_ind && i > 4) { + if (is_cleared_partition(q) && i > 4) { /* the last one in the chain - just delete */ --g_partitions; --i; @@ -1602,7 +1602,7 @@ change_sysid(void) /* if changing types T to 0 is allowed, then the reverse change must be allowed, too */ - if (sys == 0 && !get_nr_sects(p)) { + if (sys == 0 && get_nr_sects(p) == 0) { printf("Partition %u does not exist yet\n", i + 1); return; } @@ -1718,15 +1718,13 @@ wrong_p_order(int *prev) } pe = &ptes[i]; p = pe->part_table; - if (p->sys_ind) { + if (!is_cleared_partition(p)) { p_start_pos = get_partition_start_from_dev_start(pe); - if (last_p_start_pos > p_start_pos) { if (prev) *prev = last_i; return i; } - last_p_start_pos = p_start_pos; last_i = i; } @@ -1945,7 +1943,7 @@ x_dos_print_disklabel(int extend) get_nr_sects(p), p->sys_ind ); - if (p->sys_ind) + if (!is_cleared_partition(p)) check_consistency(p, i); } } @@ -2150,7 +2148,7 @@ fill_bounds(sector_t *first, sector_t *last) for (i = 0; i < g_partitions; pe++,i++) { p = pe->part_table; - if (!p->sys_ind || IS_EXTENDED(p->sys_ind)) { + if (is_cleared_partition(p) || IS_EXTENDED(p->sys_ind)) { first[i] = 0xffffffff; last[i] = 0; } else { @@ -2201,7 +2199,7 @@ verify(void) struct pte *pe = &ptes[i]; p = pe->part_table; - if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) { + if (!is_cleared_partition(p) && !IS_EXTENDED(p->sys_ind)) { check_consistency(p, i); if (get_partition_start_from_dev_start(pe) < first[i]) printf("Warning: bad start-of-data in " @@ -2231,7 +2229,7 @@ verify(void) for (i = 4; i < g_partitions; i++) { total++; p = ptes[i].part_table; - if (!p->sys_ind) { + if (is_cleared_partition(p)) { if (i != 4 || i + 1 < g_partitions) printf("Warning: partition %u " "is empty\n", i + 1); @@ -2429,7 +2427,7 @@ add_partition(int n, int sys) static void add_logical(void) { - if (g_partitions > 5 || ptes[4].part_table->sys_ind) { + if (g_partitions > 5 || !is_cleared_partition(ptes[4].part_table)) { struct pte *pe = &ptes[g_partitions]; pe->sectorbuffer = xzalloc(sector_size); @@ -2585,11 +2583,11 @@ move_begin(unsigned i) if (warn_geometry()) return; nr_sects = get_nr_sects(p); - if (!p->sys_ind || !nr_sects || IS_EXTENDED(p->sys_ind)) { + if (nr_sects == 0 || IS_EXTENDED(p->sys_ind)) { printf("Partition %u has no data area\n", i + 1); return; } - first = get_partition_start_from_dev_start(pe); /* == pe->offset_from_dev_start + get_start_sect(p) */ + first = get_partition_start_from_dev_start(pe); // pe->offset_from_dev_start + get_start_sect(p) new = read_int(0 /*was:first*/, first, first + nr_sects - 1, first, "New beginning of data"); if (new != first) { sector_t new_relative = new - pe->offset_from_dev_start; From vda.linux at googlemail.com Mon Sep 28 15:24:03 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 28 Sep 2026 17:24:03 +0200 Subject: [git commit] fdisk: add a test of adding a 1-sector primary partition Message-ID: <20260928152446.1F72C983C4@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=c1289d58d5ef391fb2605edc002e8ef6d0a1d2f3 branch: https://git.busybox.net/busybox/log/?h=master function old new delta packed_usage 36323 36390 +67 read_int 450 457 +7 read_line_input 2664 2669 +5 get_boot 1426 1423 -3 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/1 up/down: 79/-3) Total: 76 bytes Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 3 +- testsuite/fdisk.tests | 43 ++++++++++++++++++++++++ testsuite/fdisk_8k_parts1_5-11_ext14.raw | Bin 0 -> 8192 bytes testsuite/fdisk_8k_parts1_5-11_ext15.raw | Bin 0 -> 8192 bytes testsuite/fdisk_8k_parts1_5-11_ext15.txt | 26 ++++++++++++++ util-linux/fdisk.c | 56 ++++++++++++++++++++++++------- 6 files changed, 114 insertions(+), 14 deletions(-) diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 1ca21e924..2016fc7f5 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -2502,8 +2502,8 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman * (example: interactive python ^Z-backgrounded, * tty is still in "raw mode"). */ - parse_and_put_prompt(prompt); fflush_all(); + parse_and_put_prompt(prompt); if (fgets(command, maxsize, stdin) == NULL) len = -1; /* EOF or error */ else @@ -2548,6 +2548,7 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman #endif #define command command_must_not_be_used + fflush_all(); tcsetattr_stdin_TCSANOW(&new_settings); #if 0 diff --git a/testsuite/fdisk.tests b/testsuite/fdisk.tests new file mode 100755 index 000000000..c5a2387b1 --- /dev/null +++ b/testsuite/fdisk.tests @@ -0,0 +1,43 @@ +#!/bin/sh + +# Copyright 2026 by Denys Vlasenko +# Licensed under GPLv2, see file LICENSE in this source tree. + +. ./testing.sh + +# testing "description" "command" "result" "infile" "stdin" + +# We require -u for this test to disable use of incorrectly guessed +# sectors-per-track (15, should be 16). +# utili-linux 2.41.1 does not - did they change the default? +testing 'fdisk -u fdisk_8k_parts1_5-11_ext14.raw add primary' \ + 'fdisk -u fdisk_8k_parts1_5-11_ext14.raw 2>&1' \ + "\ +Changes will remain in memory only, until you decide to write them. +Be careful before using the write command. + +Command (m for help): Partition type + p primary (1-4) + l logical (5+) +Partition number (1-4): +Command (m for help): Command (m for help): Disk fdisk_8k_parts1_5-11_ext14.raw: 8192, 8192 bytes, 16 sectors +1 cylinders, 1 heads, 15 sectors/track +Units: sectors of 1 * 512 = 512 bytes + +Device Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type +fdisk_8k_parts1_5-11_ext14.raw1 0,0,2 0,0,15 1 14 14 7168 5 Extended +fdisk_8k_parts1_5-11_ext14.raw2 1,0,1 1,0,1 15 15 1 512 83 Linux +fdisk_8k_parts1_5-11_ext14.raw5 0,0,3 0,0,3 2 2 1 512 83 Linux +fdisk_8k_parts1_5-11_ext14.raw6 0,0,5 0,0,5 4 4 1 512 83 Linux +fdisk_8k_parts1_5-11_ext14.raw7 0,0,7 0,0,7 6 6 1 512 83 Linux +fdisk_8k_parts1_5-11_ext14.raw8 0,0,9 0,0,9 8 8 1 512 83 Linux +fdisk_8k_parts1_5-11_ext14.raw9 0,0,11 0,0,11 10 10 1 512 83 Linux +fdisk_8k_parts1_5-11_ext14.raw10 0,0,13 0,0,13 12 12 1 512 83 Linux +fdisk_8k_parts1_5-11_ext14.raw11 0,0,15 0,0,15 14 14 1 512 83 Linux + +Command (m for help): " \ + '' \ + 'n''\n''p''\n''2''\n''\n''p''\n' +# n(create),p(primary),2,(agree to default start sector [this could be suppressed, since there is ony one choice:15]),p(print) + +exit $FAILCOUNT diff --git a/testsuite/fdisk_8k_parts1_5-11_ext14.raw b/testsuite/fdisk_8k_parts1_5-11_ext14.raw new file mode 100644 index 000000000..f339409d2 Binary files /dev/null and b/testsuite/fdisk_8k_parts1_5-11_ext14.raw differ diff --git a/testsuite/fdisk_8k_parts1_5-11_ext15.raw b/testsuite/fdisk_8k_parts1_5-11_ext15.raw new file mode 100644 index 000000000..bba52cd89 Binary files /dev/null and b/testsuite/fdisk_8k_parts1_5-11_ext15.raw differ diff --git a/testsuite/fdisk_8k_parts1_5-11_ext15.txt b/testsuite/fdisk_8k_parts1_5-11_ext15.txt new file mode 100644 index 000000000..a0384fb45 --- /dev/null +++ b/testsuite/fdisk_8k_parts1_5-11_ext15.txt @@ -0,0 +1,26 @@ +This is an example disk image created via: + dd bs=1K count=8 IMAGE + fdisk IMAGE +then creating extended partition on the whole disk, +then creating one-sector-sized logical partitions, +up to partition 11. +Every logical partition was filled by data in QEMU via + yes "part 05" >/dev/sda5 +and similar commands. + +This image, if mounted by linux, will have extended partition +visible as 1024-byte /dev/sda1 partition, which also +includes /dev/sda5 first (and only) sector - +kernel does not check for overlap! + +The last link sector of extended partition chain, one just before +partition 11, has only data pointer, no "next ext" pointer. +There is an unused sector at the end. It is meant to be this +"next ext" pointer if further logical partitions were to be created +(and if the disk would have space for them). + +It is possible to use the last sector for a primary partition. +The extended partition needs to resized ('e','1') to 14 sectors: +'7168' in util-linux 2.41.1 works. In fact, you can resize it +down to 1 sector (0 does not work, kernel will ignore it). +Then you can create a primary partition in sector 15. diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index eac748580..4e4c0d027 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -81,6 +81,7 @@ //usage:#define fdisk_full_usage "\n\n" //usage: IF_FEATURE_FDISK_WRITABLE("Change")IF_NOT_FEATURE_FDISK_WRITABLE("Show")" partition table\n" //usage: "\n -u Start and End are in sectors (instead of cylinders)" +//usage: "\n Also disable rounding of sizes to cylinders" //usage: "\n -l Show partition table for each DISK and exit" //usage: IF_FEATURE_FDISK_BLKSIZE( //usage: "\n -s Show size in kb for each DISK and exit" @@ -108,6 +109,12 @@ # define BLKGETSIZE64 _IOR(0x12,114,size_t) #endif +#if 0 +# define dbg(...) bb_error_msg(__VA_ARGS__) +#else +# define dbg(...) ((void)0) +#endif + /* Get device geometry in this struct: */ #define HDIO_GETGEO 0x0301 struct hd_geometry { @@ -1118,7 +1125,7 @@ get_partition_table_geometry(void) p = pt_offset(bufp, i); if (!is_cleared_partition(p)) { h = p->end_head + 1; - s = (p->end_sector & 077); + s = (p->end_sector & 0x3f); if (first) { hh = h; ss = s; @@ -1318,14 +1325,18 @@ static sector_t read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char *mesg) { sector_t value; - int default_ok = 1; - const char *fmt = "%s (%u-%u, default %u): "; + int default_ok; + const char *fmt; + + if (low == high) // no need to ask + return low; + default_ok = 1; + fmt = "%s (%u-%u, default %u): "; if (dflt < low || dflt > high) { fmt = "%s (%u-%u): "; default_ok = 0; } - while (1) { int use_default = default_ok; @@ -2201,6 +2212,7 @@ verify(void) struct pte *pe = &ptes[i]; p = pe->part_table; +//TODO: warn about EXTENDED partitions with nr_sect==0? Kernel will ignore these (requires at least 1) if (!is_cleared_partition(p) && !IS_EXTENDED(p->sys_ind)) { check_consistency(p, i); if (get_partition_start_from_dev_start(pe) < first[i]) @@ -2317,17 +2329,22 @@ add_partition(int n, int sys) main_ext = ptes[ext_index].part_table; if (n < 4) { start = offset_after_MBR_and_ext; - if (DISPLAY_IN_CYL_UNITS || !total_number_of_sectors) + if (DISPLAY_IN_CYL_UNITS || !total_number_of_sectors) { limit = (sector_t) g_heads * g_sectors * g_cylinders - 1; - else + dbg("%d: limit:%d", __LINE__, limit); + } else { limit = total_number_of_sectors - 1; + dbg("%d: limit:%d", __LINE__, limit); + } if (extended_offset) { first[ext_index] = extended_offset; last[ext_index] = extended_offset + get_nr_sects(main_ext) - 1; } + dbg("%d: start:%d limit:%d", __LINE__, start, limit); } else { start = extended_offset + offset_after_MBR_and_ext; limit = extended_offset + get_nr_sects(main_ext) - 1; + dbg("%d: start:%d limit:%d", __LINE__, start, limit); } if (DISPLAY_IN_CYL_UNITS) for (i = 0; i < g_partitions; i++) @@ -2338,13 +2355,17 @@ add_partition(int n, int sys) do { temp = start; for (i = 0; i < g_partitions; i++) { - int lastplusoff; + sector_t lastplusoff; - if (start == ptes[i].offset_from_dev_start) + if (start == ptes[i].offset_from_dev_start) { start += offset_after_MBR_and_ext; + dbg("%d: start:%d", __LINE__, start); + } lastplusoff = last[i] + ((n < 4) ? 0 : offset_after_MBR_and_ext); - if (start >= first[i] && start <= lastplusoff) + if (start >= first[i] && start <= lastplusoff) { start = lastplusoff + 1; + dbg("%d: start:%d", __LINE__, start); + } } if (start > limit) break; @@ -2360,8 +2381,11 @@ add_partition(int n, int sys) start = read_int(cround(saved_start), cround(saved_start), cround(limit), 0, mesg); if (DISPLAY_IN_CYL_UNITS) { start = (start - 1) * units_per_sector; - if (start < saved_start) + dbg("%d: start:%d", __LINE__, start); + if (start < saved_start) { start = saved_start; + dbg("%d: start:%d", __LINE__, start); + } } num_read = 1; } @@ -2373,18 +2397,24 @@ add_partition(int n, int sys) pe->offset_from_dev_start = start - offset_after_MBR_and_ext; if (pe->offset_from_dev_start == extended_offset) { /* must be corrected */ pe->offset_from_dev_start++; - if (offset_after_MBR_and_ext == 1) + if (offset_after_MBR_and_ext == 1) { start++; + dbg("%d: ++start:%d", __LINE__, start); + } } } for (i = 0; i < g_partitions; i++) { struct pte *pe = &ptes[i]; - if (start < pe->offset_from_dev_start && limit >= pe->offset_from_dev_start) + if (start < pe->offset_from_dev_start && limit >= pe->offset_from_dev_start) { limit = pe->offset_from_dev_start - 1; - if (start < first[i] && limit >= first[i]) + dbg("%d: limit:%d", __LINE__, limit); + } + if (start < first[i] && limit >= first[i]) { limit = first[i] - 1; + dbg("%d: limit:%d", __LINE__, limit); + } } if (start > limit) { puts("No free sectors available"); From vda.linux at googlemail.com Mon Sep 28 16:07:32 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 28 Sep 2026 18:07:32 +0200 Subject: [git commit] fdisk: util-linux compat wrt meaning of -u Message-ID: <20260928220026.C7B349842D@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=e8cb0a1d14c22c8d95b71e97f6f61347d62b7bc0 branch: https://git.busybox.net/busybox/log/?h=master function old new delta menu 261 282 +21 .rodata 104779 104787 +8 packed_usage 36390 36379 -11 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 29/-11) Total: 18 bytes Signed-off-by: Denys Vlasenko --- testsuite/fdisk.tests | 7 +++---- util-linux/fdisk.c | 14 +++++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/testsuite/fdisk.tests b/testsuite/fdisk.tests index c5a2387b1..de5009c9a 100755 --- a/testsuite/fdisk.tests +++ b/testsuite/fdisk.tests @@ -7,11 +7,10 @@ # testing "description" "command" "result" "infile" "stdin" -# We require -u for this test to disable use of incorrectly guessed +# With -u (use cyinder units) this wouldn't work: incorrectly guessed # sectors-per-track (15, should be 16). -# utili-linux 2.41.1 does not - did they change the default? -testing 'fdisk -u fdisk_8k_parts1_5-11_ext14.raw add primary' \ - 'fdisk -u fdisk_8k_parts1_5-11_ext14.raw 2>&1' \ +testing 'fdisk fdisk_8k_parts1_5-11_ext14.raw add primary' \ + 'fdisk fdisk_8k_parts1_5-11_ext14.raw 2>&1' \ "\ Changes will remain in memory only, until you decide to write them. Be careful before using the write command. diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 4e4c0d027..b3ed1b99c 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -80,8 +80,8 @@ //usage: "[-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SSZ] [-t PARTTYPE] DISK" //usage:#define fdisk_full_usage "\n\n" //usage: IF_FEATURE_FDISK_WRITABLE("Change")IF_NOT_FEATURE_FDISK_WRITABLE("Show")" partition table\n" -//usage: "\n -u Start and End are in sectors (instead of cylinders)" -//usage: "\n Also disable rounding of sizes to cylinders" +//usage: "\n -u Start and End are in cylinders (instead of sectors)" +//usage: "\n Also enable rounding of sizes to cylinders" //usage: "\n -l Show partition table for each DISK and exit" //usage: IF_FEATURE_FDISK_BLKSIZE( //usage: "\n -s Show size in kb for each DISK and exit" @@ -183,7 +183,12 @@ enum { }; #define USER_SET_SECTOR_SIZE (option_mask32 & OPT_b) #define NOWARN_OPT_ls (!ENABLE_FEATURE_FDISK_WRITABLE || (option_mask32 & (OPT_l|OPT_s))) -#define DISPLAY_IN_CYL_UNITS (!(option_mask32 & OPT_u)) +// The meaning of -u was inverted in its meaning in util-linux by: +// commit 0b1f769f281ac2feb21b8a52af3fe999922c8833 +// Date: Tue Jun 15 13:13:05 2010 +0200 +// fdisk: disable DOS mode and cylinders by default +// Now it also takes an optional parameter, "cylinder[s]"/"sector[s]" (we don't do that): +#define DISPLAY_IN_CYL_UNITS (option_mask32 & OPT_u) #define TOGGLE_DISPLAY_IN_CYL_UNITS (option_mask32 ^= OPT_u) #define SUPPORT_DISKLABELS (0 \ @@ -1506,7 +1511,6 @@ choose_free_primary_partition(void) return input_partition_number(/*warn*/ 0, 4); } - static void change_units(void) { @@ -2659,7 +2663,7 @@ menu(void) puts("q\tquit without saving changes"); //deleted: puts("s\tcreate a new empty Sun disklabel"); /* sun */ puts("t\tchange a partition's system id"); - puts("u\tchange display/entry units"); + printf("u\tchange display/entry units to %ss\n", DISPLAY_IN_CYL_UNITS ? "sector" : "cylinder"); puts("v\tverify partition table"); puts("w\twrite table to disk and exit"); # if ENABLE_FEATURE_FDISK_ADVANCED From vda.linux at googlemail.com Mon Sep 28 22:00:42 2026 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 29 Sep 2026 00:00:42 +0200 Subject: [git commit] swapon: with no arguments, show /proc/swaps (mimic util-linux 2.41.1) Message-ID: <20260928220026.D32979842E@busybox.osuosl.org> commit: https://git.busybox.net/busybox/commit/?id=e6302b2711d851bfb17eaf9316514c9d0a806cc7 branch: https://git.busybox.net/busybox/log/?h=master function old new delta swap_on_off_main 419 449 +30 Signed-off-by: Denys Vlasenko --- util-linux/swaponoff.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c index e2ff4b5cc..5cda7b19d 100644 --- a/util-linux/swaponoff.c +++ b/util-linux/swaponoff.c @@ -323,6 +323,11 @@ int swap_on_off_main(int argc UNUSED_PARAM, char **argv) ret = do_all_in_proc_swaps(); ret |= do_em_all_in_fstab(); } else if (!*argv) { + if (!do_swapoff) { + /* bare "swapon" shows current swap usage */ + *--argv = (char*)"/proc/swaps"; + return bb_cat(argv); + } /* if not -a we need at least one arg */ bb_show_usage(); }