diff options
author | Karel Zak <kzak@redhat.com> | 2024-04-18 09:08:05 +0200 |
---|---|---|
committer | Karel Zak <kzak@redhat.com> | 2024-04-18 09:08:05 +0200 |
commit | 532865fce9c521aea09e43b948a42985fbb3249a (patch) | |
tree | 3082407904125a874388d5b1b786c3e30a5a7c9a | |
parent | f5d3a3e4b138896ed97690680735c5e44559ee00 (diff) | |
parent | 402039303605045642607984bfc5121a759306b4 (diff) | |
download | util-linux-532865fce9c521aea09e43b948a42985fbb3249a.tar.gz |
Merge branch 'PR/fdisk-trim' of github.com:karelzak/util-linux-work
* 'PR/fdisk-trim' of github.com:karelzak/util-linux-work:
blkdiscard: (man) add note about fdisk
fdisk: add 'T' command to discard sectors
libfdisk: add fdisk_ask_menu()
fdisk: improve list_freespace()
-rw-r--r-- | disk-utils/fdisk-list.c | 130 | ||||
-rw-r--r-- | disk-utils/fdisk-list.h | 4 | ||||
-rw-r--r-- | disk-utils/fdisk-menu.c | 5 | ||||
-rw-r--r-- | disk-utils/fdisk.c | 142 | ||||
-rw-r--r-- | disk-utils/fdisk.h | 2 | ||||
-rw-r--r-- | libfdisk/docs/libfdisk-sections.txt | 1 | ||||
-rw-r--r-- | libfdisk/src/ask.c | 46 | ||||
-rw-r--r-- | libfdisk/src/libfdisk.h.in | 3 | ||||
-rw-r--r-- | libfdisk/src/libfdisk.sym | 4 | ||||
-rw-r--r-- | sys-utils/blkdiscard.8.adoc | 2 |
10 files changed, 305 insertions, 34 deletions
diff --git a/disk-utils/fdisk-list.c b/disk-utils/fdisk-list.c index 26ae1bd1eb..fe2fe3e647 100644 --- a/disk-utils/fdisk-list.c +++ b/disk-utils/fdisk-list.c @@ -229,32 +229,45 @@ done: fdisk_free_iter(itr); } -void list_freespace(struct fdisk_context *cxt) +/* + * List freespace areas and if @tb0 not NULL then returns the table. The + * @best0 returns number of the "best" area (may be used as default in some + * dialog). + * + * Returns: <0 on error, else number of free areas + */ +int list_freespace_get_table(struct fdisk_context *cxt, + struct fdisk_table **tb0, + size_t *best0) { struct fdisk_table *tb = NULL; - struct fdisk_partition *pa = NULL; + struct fdisk_partition *pa = NULL, *best = NULL; struct fdisk_iter *itr = NULL; struct libscols_table *out = NULL; const char *bold = NULL; size_t i; - uintmax_t sumsize = 0, bytes = 0; + uintmax_t sumsize = 0, bytes = 0, nbest = 0; char *strsz; + int rc = 0, ct = 0; static const char *colnames[] = { N_("Start"), N_("End"), N_("Sectors"), N_("Size") }; static const int colids[] = { FDISK_FIELD_START, FDISK_FIELD_END, FDISK_FIELD_SECTORS, FDISK_FIELD_SIZE }; - if (fdisk_get_freespaces(cxt, &tb)) + rc = fdisk_get_freespaces(cxt, &tb); + if (rc) goto done; itr = fdisk_new_iter(FDISK_ITER_FORWARD); if (!itr) { fdisk_warn(cxt, _("failed to allocate iterator")); + rc = -ENOMEM; goto done; } out = scols_new_table(); if (!out) { fdisk_warn(cxt, _("failed to allocate output table")); + rc = -ENOMEM; goto done; } @@ -264,67 +277,120 @@ void list_freespace(struct fdisk_context *cxt) } for (i = 0; i < ARRAY_SIZE(colnames); i++) { - struct libscols_column *co = scols_table_new_column(out, _(colnames[i]), 5, SCOLS_FL_RIGHT); + struct libscols_column *co; - if (!co) + if (tb0 && i == 0) { + co = scols_table_new_column(out, "#", 5, SCOLS_FL_RIGHT); + if (!co) { + rc = -ENOMEM; + goto done; + } + } + + co = scols_table_new_column(out, _(colnames[i]), 5, SCOLS_FL_RIGHT); + if (!co) { + rc = -ENOMEM; goto done; + } if (bold) scols_cell_set_color(scols_column_get_header(co), bold); } /* fill-in output table */ while (fdisk_table_next_partition(tb, itr, &pa) == 0) { + int col; struct libscols_line *ln = scols_table_new_line(out, NULL); - char *data; if (!ln) { fdisk_warn(cxt, _("failed to allocate output line")); goto done; } - for (i = 0; i < ARRAY_SIZE(colids); i++) { + for (col = 0, i = 0; i < ARRAY_SIZE(colnames); col++, i++) { + char *data = NULL; + + if (tb0 && i == 0) { + xasprintf(&data, "%d", ct + 1); + + if (scols_line_refer_data(ln, i, data)) { + fdisk_warn(cxt, _("failed to add output data")); + rc = -ENOMEM; + goto done; + } + col++; + } + if (fdisk_partition_to_string(pa, cxt, colids[i], &data)) continue; - if (scols_line_refer_data(ln, i, data)) { + if (scols_line_refer_data(ln, col, data)) { fdisk_warn(cxt, _("failed to add output data")); + rc = -ENOMEM; goto done; } } - if (fdisk_partition_has_size(pa)) - sumsize += fdisk_partition_get_size(pa); - } + if (fdisk_partition_has_size(pa)) { + uintmax_t sz = fdisk_partition_get_size(pa);; - bytes = sumsize * fdisk_get_sector_size(cxt); - strsz = size_to_human_string(SIZE_DECIMAL_2DIGITS - | SIZE_SUFFIX_SPACE - | SIZE_SUFFIX_3LETTER, bytes); + sumsize += sz; - color_scheme_enable("header", UL_COLOR_BOLD); - fdisk_info(cxt, _("Unpartitioned space %s: %s, %ju bytes, %ju sectors"), - fdisk_get_devname(cxt), strsz, - bytes, sumsize); - color_disable(); - free(strsz); - - fdisk_info(cxt, _("Units: %s of %d * %ld = %ld bytes"), - fdisk_get_unit(cxt, FDISK_PLURAL), - fdisk_get_units_per_sector(cxt), - fdisk_get_sector_size(cxt), - fdisk_get_units_per_sector(cxt) * fdisk_get_sector_size(cxt)); + if (best0 && + (best == NULL || fdisk_partition_get_size(best) < sz)) { + nbest = ct; + best = pa; + } + } + ct++; + } - fdisk_info(cxt, _("Sector size (logical/physical): %lu bytes / %lu bytes"), - fdisk_get_sector_size(cxt), - fdisk_get_physector_size(cxt)); + if (tb0 == NULL) { + bytes = sumsize * fdisk_get_sector_size(cxt); + strsz = size_to_human_string(SIZE_DECIMAL_2DIGITS + | SIZE_SUFFIX_SPACE + | SIZE_SUFFIX_3LETTER, bytes); + + color_scheme_enable("header", UL_COLOR_BOLD); + fdisk_info(cxt, _("Unpartitioned space %s: %s, %ju bytes, %ju sectors"), + fdisk_get_devname(cxt), strsz, + bytes, sumsize); + color_disable(); + free(strsz); + + fdisk_info(cxt, _("Units: %s of %d * %ld = %ld bytes"), + fdisk_get_unit(cxt, FDISK_PLURAL), + fdisk_get_units_per_sector(cxt), + fdisk_get_sector_size(cxt), + fdisk_get_units_per_sector(cxt) * fdisk_get_sector_size(cxt)); + + fdisk_info(cxt, _("Sector size (logical/physical): %lu bytes / %lu bytes"), + fdisk_get_sector_size(cxt), + fdisk_get_physector_size(cxt)); + } /* print */ if (!scols_table_is_empty(out)) { fdisk_info(cxt, "%s", ""); /* line break */ scols_print_table(out); } + + rc = 0; done: scols_unref_table(out); - fdisk_unref_table(tb); fdisk_free_iter(itr); + + if (tb0) + *tb0 = tb; + else + fdisk_unref_table(tb); + + if (best0) + *best0 = nbest; + + return rc < 0 ? rc : ct; +} + +void list_freespace(struct fdisk_context *cxt) +{ + list_freespace_get_table(cxt, NULL, NULL); } char *next_proc_partition(FILE **f) diff --git a/disk-utils/fdisk-list.h b/disk-utils/fdisk-list.h index a30cd6a515..ccd982e593 100644 --- a/disk-utils/fdisk-list.h +++ b/disk-utils/fdisk-list.h @@ -15,6 +15,10 @@ extern void list_disklabel(struct fdisk_context *cxt); extern void list_disk_identifier(struct fdisk_context *cxt); extern void list_disk_geometry(struct fdisk_context *cxt); extern void list_freespace(struct fdisk_context *cxt); +extern int list_freespace_get_table( + struct fdisk_context *cxt, + struct fdisk_table **tb0, + size_t *best0); extern char *next_proc_partition(FILE **f); extern int print_device_pt(struct fdisk_context *cxt, char *device, int warnme, int verify, int separator); diff --git a/disk-utils/fdisk-menu.c b/disk-utils/fdisk-menu.c index 4c6a918934..717554b0e3 100644 --- a/disk-utils/fdisk-menu.c +++ b/disk-utils/fdisk-menu.c @@ -112,6 +112,7 @@ static const struct menu menu_generic = { MENU_BENT_E('v', N_("verify the partition table"), FDISK_DISKLABEL_BSD), MENU_ENT ('i', N_("print information about a partition")), MENU_ENT ('e', N_("resize a partition")), + MENU_ENT ('T', N_("discard (trim) sectors")), MENU_XENT('d', N_("print the raw data of the first sector from the device")), MENU_XENT('D', N_("print the raw data of the disklabel from the device")), @@ -714,6 +715,10 @@ static int generic_menu_cb(struct fdisk_context **cxt0, fdisk_unref_context(cxt); } break; + case 'T': + /* discard (trim) */ + discard_sectors(cxt); + break; } return rc; diff --git a/disk-utils/fdisk.c b/disk-utils/fdisk.c index c75a7a63c8..f75ed2445a 100644 --- a/disk-utils/fdisk.c +++ b/disk-utils/fdisk.c @@ -55,6 +55,15 @@ # include <linux/blkpg.h> #endif +#ifdef __linux__ +# ifdef HAVE_LINUX_FS_H +# include <linux/fs.h> +# endif +# ifndef BLKDISCARD +# define BLKDISCARD _IO(0x12,119) +# endif +#endif + int pwipemode = WIPEMODE_AUTO; int device_is_used; int is_interactive; @@ -201,8 +210,12 @@ static int ask_menu(struct fdisk_context *cxt, struct fdisk_ask *ask, size_t i = 0; /* print menu items */ - while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0) - fprintf(stdout, " %c %s (%s)\n", key, name, desc); + while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0) { + if (desc) + fprintf(stdout, " %c %s (%s)\n", key, name, desc); + else + fprintf(stdout, " %c %s\n", key, name); + } /* ask for key */ snprintf(prompt, sizeof(prompt), _("Select (default %c): "), dft); @@ -771,6 +784,131 @@ void change_partition_type(struct fdisk_context *cxt) fdisk_unref_parttype(t); } +#ifdef BLKDISCARD + +static int do_discard(struct fdisk_context *cxt, struct fdisk_partition *pa) +{ + char buf[512]; + unsigned long ss; + uint64_t range[2]; + int yes = 0; + + ss = fdisk_get_sector_size(cxt); + + range[0] = (uint64_t) fdisk_partition_get_start(pa); + range[1] = (uint64_t) fdisk_partition_get_size(pa); + + snprintf(buf, sizeof(buf), _("All data in the region (%"PRIu64 + "-%"PRIu64") will be lost! Continue?"), + range[0], range[0] + range[1] - 1); + + range[0] *= (uint64_t) ss; + range[1] *= (uint64_t) ss; + + fdisk_ask_yesno(cxt, buf, &yes); + if (!yes) + return 1; + + errno = 0; + if (ioctl(fdisk_get_devfd(cxt), BLKDISCARD, &range)) { + fdisk_warn(cxt, _("BLKDISCARD ioctl failed")); + return -errno; + } + return 0; +} + +static void discard_partition(struct fdisk_context *cxt) +{ + struct fdisk_partition *pa = NULL; + size_t n = 0; + + fdisk_info(cxt, _("\nThe partition sectors will be immediately discarded.\n" + "You can exit this dialog by pressing CTRL+C.\n")); + + if (fdisk_ask_partnum(cxt, &n, FALSE)) + goto done; + if (fdisk_get_partition(cxt, n, &pa)) { + fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), n + 1); + goto done; + } + + if (!fdisk_partition_has_size(pa) || !fdisk_partition_has_start(pa)) { + fdisk_warnx(cxt, _("Partition %zu has unspeficied range"), n + 1); + goto done; + } + + if (do_discard(cxt, pa) == 0) + fdisk_info(cxt, _("Discarded sectors on partition %zu."), n + 1); +done: + fdisk_unref_partition(pa); +} + +static void discard_freespace(struct fdisk_context *cxt) +{ + struct fdisk_partition *pa = NULL; + struct fdisk_table *tb = NULL; + size_t best = 0; + uintmax_t n = 0; + int ct; + + ct = list_freespace_get_table(cxt, &tb, &best); + if (ct <= 0) { + fdisk_info(cxt, _("No free space.")); + goto done; + } + fdisk_info(cxt, _("\nThe unused sectors will be immediately discarded.\n" + "You can exit this dialog by pressing CTRL+C.\n")); + + if (fdisk_ask_number(cxt, 1, best + 1, (uintmax_t) ct, + _("Free space number"), &n) != 0) + goto done; + + pa = fdisk_table_get_partition(tb, n - 1); + if (!pa) + goto done; + + if (!fdisk_partition_has_size(pa) || !fdisk_partition_has_start(pa)) { + fdisk_warnx(cxt, _("Free space %"PRIu64 "has unspeficied range"), n); + goto done; + } + + if (do_discard(cxt, pa) == 0) + fdisk_info(cxt, _("Discarded sectors on free space.")); +done: + fdisk_unref_table(tb); +} + +void discard_sectors(struct fdisk_context *cxt) +{ + int c; + + if (fdisk_is_readonly(cxt)) { + fdisk_warnx(cxt, _("Discarding sectors is not possible in read-only mode.")); + return; + } + + if (fdisk_ask_menu(cxt, _("Type of area to be discarded"), + &c, 'p', _("partition sectors"), 'p', + _("free space sectros"), 'f', NULL) != 0) + return; + + switch (c) { + case 'p': + discard_partition(cxt); + break; + case 'f': + discard_freespace(cxt); + break; + } +} + +#else /* !BLKDISCARD */ +void discard_sectors(struct fdisk_context *cxt) +{ + fdisk_warnx(cxt, _("Discard unsupported on your system.")); +} +#endif /* BLKDISCARD */ + int print_partition_info(struct fdisk_context *cxt) { struct fdisk_partition *pa = NULL; diff --git a/disk-utils/fdisk.h b/disk-utils/fdisk.h index d4578e0724..72d36531f8 100644 --- a/disk-utils/fdisk.h +++ b/disk-utils/fdisk.h @@ -65,4 +65,6 @@ extern void follow_wipe_mode(struct fdisk_context *cxt); extern void resize_partition(struct fdisk_context *cxt); +extern void discard_sectors(struct fdisk_context *cxt); + #endif /* UTIL_LINUX_FDISK_H */ diff --git a/libfdisk/docs/libfdisk-sections.txt b/libfdisk/docs/libfdisk-sections.txt index efc138571d..b58c880997 100644 --- a/libfdisk/docs/libfdisk-sections.txt +++ b/libfdisk/docs/libfdisk-sections.txt @@ -25,6 +25,7 @@ fdisk_ask fdisk_is_ask fdisk_ask_get_query fdisk_ask_get_type +fdisk_ask_menu fdisk_ask_menu_get_default fdisk_ask_menu_get_item fdisk_ask_menu_get_nitems diff --git a/libfdisk/src/ask.c b/libfdisk/src/ask.c index 299f65b434..4ce7dc0b4e 100644 --- a/libfdisk/src/ask.c +++ b/libfdisk/src/ask.c @@ -2,6 +2,8 @@ #include "strutils.h" #include "fdiskP.h" +#include <stdarg.h> + /** * SECTION: ask * @title: Ask @@ -875,6 +877,50 @@ int fdisk_ask_menu_add_item(struct fdisk_ask *ask, int key, return 0; } +/** + * fdisk_ask_menu: + * @cxt: fdisk context + * @query: query to ask (menu title) + * @result: returns selected key + * @dflt: default key + * @...: list of char *name and int key pairs + * + * Displays a menu with the given query and returns the result of the menu selection. + * + * Returns: <0 on error, 0 on success + * + * Since: 2.41 + * + */ +int fdisk_ask_menu(struct fdisk_context *cxt, char *query, int *result, int dflt, ...) +{ + struct fdisk_ask *ask; + va_list ap; + char *name; + int rc; + + if (!query || !result) + return -EINVAL; + + ask = fdisk_new_ask(); + if (!ask) + return -ENOMEM; + + fdisk_ask_set_type(ask, FDISK_ASKTYPE_MENU); + fdisk_ask_set_query(ask, query); + fdisk_ask_menu_set_default(ask, dflt); + + va_start(ap, dflt); + + while ((name = va_arg(ap, char *))) + fdisk_ask_menu_add_item(ask, va_arg(ap, int), name, NULL); + + rc = fdisk_do_ask(cxt, ask); + if (~rc) + fdisk_ask_menu_get_result(ask, result); + fdisk_unref_ask(ask); + return rc; +} /* * print-like diff --git a/libfdisk/src/libfdisk.h.in b/libfdisk/src/libfdisk.h.in index 9c20f44beb..be3b22e7a3 100644 --- a/libfdisk/src/libfdisk.h.in +++ b/libfdisk/src/libfdisk.h.in @@ -872,6 +872,9 @@ int fdisk_ask_yesno(struct fdisk_context *cxt, int *result); int fdisk_ask_yesno_get_result(struct fdisk_ask *ask); int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, int result); + +int fdisk_ask_menu(struct fdisk_context *cxt, char *query, int *result, int dflt, ...); + int fdisk_ask_menu_get_default(struct fdisk_ask *ask); int fdisk_ask_menu_set_result(struct fdisk_ask *ask, int key); int fdisk_ask_menu_get_result(struct fdisk_ask *ask, int *key); diff --git a/libfdisk/src/libfdisk.sym b/libfdisk/src/libfdisk.sym index bb69e93c41..43eb0f251a 100644 --- a/libfdisk/src/libfdisk.sym +++ b/libfdisk/src/libfdisk.sym @@ -324,3 +324,7 @@ FDISK_2.38 { FDISK_2.40 { fdisk_partition_get_max_size; } FDISK_2.38; + +FDISK_2_41 { + fdisk_ask_menu; +} FDISK_2.40; diff --git a/sys-utils/blkdiscard.8.adoc b/sys-utils/blkdiscard.8.adoc index 578f300f73..cf661acd9c 100644 --- a/sys-utils/blkdiscard.8.adoc +++ b/sys-utils/blkdiscard.8.adoc @@ -24,6 +24,8 @@ The _device_ argument is the pathname of the block device. *WARNING: All data in the discarded region on the device will be lost!* +Since util-linux v2.41, fdisk has the ability to discard sectors on both partitions and unpartitioned areas using the 'T' command. + == OPTIONS The _offset_ and _length_ arguments may be followed by the multiplicative suffixes KiB (=1024), MiB (=1024*1024), and so on for GiB, TiB, PiB, EiB, ZiB and YiB (the "iB" is optional, e.g., "K" has the same meaning as "KiB") or the suffixes KB (=1000), MB (=1000*1000), and so on for GB, TB, PB, EB, ZB and YB. |