aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarel Zak <kzak@redhat.com>2024-04-18 09:08:05 +0200
committerKarel Zak <kzak@redhat.com>2024-04-18 09:08:05 +0200
commit532865fce9c521aea09e43b948a42985fbb3249a (patch)
tree3082407904125a874388d5b1b786c3e30a5a7c9a
parentf5d3a3e4b138896ed97690680735c5e44559ee00 (diff)
parent402039303605045642607984bfc5121a759306b4 (diff)
downloadutil-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.c130
-rw-r--r--disk-utils/fdisk-list.h4
-rw-r--r--disk-utils/fdisk-menu.c5
-rw-r--r--disk-utils/fdisk.c142
-rw-r--r--disk-utils/fdisk.h2
-rw-r--r--libfdisk/docs/libfdisk-sections.txt1
-rw-r--r--libfdisk/src/ask.c46
-rw-r--r--libfdisk/src/libfdisk.h.in3
-rw-r--r--libfdisk/src/libfdisk.sym4
-rw-r--r--sys-utils/blkdiscard.8.adoc2
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.