]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
fdisk: add 'T' command to discard sectors
authorKarel Zak <kzak@redhat.com>
Tue, 16 Apr 2024 11:36:32 +0000 (13:36 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 17 Apr 2024 07:38:52 +0000 (09:38 +0200)
We currently have blkdiscard(8), but it can be difficult to use for
unpartitioned areas or for partitions when working with a whole-disk
device.

This commit adds BLKDISCARD support to fdisk, so that the user does
not have to specify any range, but instead follows the partition
table.

Addresses: https://github.com/util-linux/util-linux/issues/2804
Signed-off-by: Karel Zak <kzak@redhat.com>
disk-utils/fdisk-menu.c
disk-utils/fdisk.c
disk-utils/fdisk.h

index 4c6a918934964a4910c9d22e80b9366fff6e3cd6..717554b0e3478d49e03a436086d61dbffa59c21d 100644 (file)
@@ -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;
index c75a7a63c84bbeb71ab2c7c6e93147813d008086..f75ed2445a0ad8fc1765c4a76011ab7007565758 100644 (file)
 # 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;
index d4578e07249e6e8381beae14b9abc06a4fdc3d66..72d36531f8cd47b061b580e1f57bcf08efdb2a3e 100644 (file)
@@ -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 */