summaryrefslogtreecommitdiffstats
path: root/sys-utils/blkdiscard.c
diff options
context:
space:
mode:
authorLukas Czerner2012-09-12 23:49:15 +0200
committerKarel Zak2012-09-28 01:12:09 +0200
commitd964b669c8d8675af1b7d7e1742ee8b68dc285ef (patch)
tree007921b4702a78e7a8da4b34e5b9234500928fcb /sys-utils/blkdiscard.c
parentRevert "blkdiscard: new command" (diff)
downloadkernel-qcow2-util-linux-d964b669c8d8675af1b7d7e1742ee8b68dc285ef.tar.gz
kernel-qcow2-util-linux-d964b669c8d8675af1b7d7e1742ee8b68dc285ef.tar.xz
kernel-qcow2-util-linux-d964b669c8d8675af1b7d7e1742ee8b68dc285ef.zip
blkdiscard: add new command
blkdiscard is used to discard device sectors. This is useful for solid-state drivers (SSDs) and thinly-provisioned storage. Unlike fstrim this command is used directly on the block device. blkkdiscard uses BLKDISCARD ioctl or BLKSECDISCARD ioctl for the secure discard. All data in the discarded region on the device will be lost! Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Diffstat (limited to 'sys-utils/blkdiscard.c')
-rw-r--r--sys-utils/blkdiscard.c173
1 files changed, 173 insertions, 0 deletions
diff --git a/sys-utils/blkdiscard.c b/sys-utils/blkdiscard.c
new file mode 100644
index 000000000..bdcd06e37
--- /dev/null
+++ b/sys-utils/blkdiscard.c
@@ -0,0 +1,173 @@
+/*
+ * blkdiscard.c -- discard the part (or whole) of the block device.
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Written by Lukas Czerner <lczerner@redhat.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This program uses BLKDISCARD ioctl to discard part or the whole block
+ * device if the device supports it. You can specify range (start and
+ * length) to be discarded, or simply discard the whole device.
+ */
+
+
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <getopt.h>
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <linux/fs.h>
+
+#include "nls.h"
+#include "strutils.h"
+#include "c.h"
+#include "closestream.h"
+
+#ifndef BLKDISCARD
+#define BLKDISCARD _IO(0x12,119)
+#endif
+
+#ifndef BLKSECDISCARD
+#define BLKSECDISCARD _IO(0x12,125)
+#endif
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %s [options] <device>\n"), program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -o, --offset <num> offset in bytes to discard from\n"
+ " -l, --length <num> length of bytes to discard from the offset\n"
+ " -s, --secure perform secure discard\n"
+ " -v, --verbose print aligned length and offset\n"),
+ out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("blkdiscard(8)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ char *path;
+ int c, fd, verbose = 0, secure = 0;
+ uint64_t end, blksize, secsize, range[2];
+ struct stat sb;
+
+ static const struct option longopts[] = {
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'V' },
+ { "offset", 1, 0, 'o' },
+ { "length", 1, 0, 'l' },
+ { "secure", 0, 0, 's' },
+ { "verbose", 0, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ range[0] = 0;
+ range[1] = ULLONG_MAX;
+
+ while ((c = getopt_long(argc, argv, "hVsvo:l:", longopts, NULL)) != -1) {
+ switch(c) {
+ case 'h':
+ usage(stdout);
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'l':
+ range[1] = strtosize_or_err(optarg,
+ _("failed to parse length"));
+ break;
+ case 'o':
+ range[0] = strtosize_or_err(optarg,
+ _("failed to parse offset"));
+ break;
+ case 's':
+ secure = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage(stderr);
+ break;
+ }
+ }
+
+ if (optind == argc)
+ errx(EXIT_FAILURE, _("no device specified."));
+
+ path = argv[optind++];
+
+ if (optind != argc) {
+ warnx(_("unexpected number of arguments"));
+ usage(stderr);
+ }
+
+ if (stat(path, &sb) == -1)
+ err(EXIT_FAILURE, _("stat failed %s"), path);
+ if (!S_ISBLK(sb.st_mode))
+ errx(EXIT_FAILURE, _("%s: not a block device"), path);
+
+ fd = open(path, O_WRONLY);
+ if (fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), path);
+
+ if (ioctl(fd, BLKGETSIZE64, &blksize))
+ err(EXIT_FAILURE, _("%s: BLKGETSIZE64 ioctl failed"), path);
+
+ if (ioctl(fd, BLKSSZGET, &secsize))
+ err(EXIT_FAILURE, _("%s: BLKSSZGET ioctl failed"), path);
+
+ /* align range to the sector size */
+ range[0] = (range[0] + secsize - 1) & ~(secsize - 1);
+ range[1] &= ~(secsize - 1);
+
+ /* is the range end behind the end of the device ?*/
+ end = range[0] + range[1];
+ if (end < range[0] || end > blksize)
+ range[1] = blksize - range[0];
+
+ if (secure) {
+ if (ioctl(fd, BLKSECDISCARD, &range))
+ err(EXIT_FAILURE, _("%s: BLKSECDISCARD ioctl failed"), path);
+ } else {
+ if (ioctl(fd, BLKDISCARD, &range))
+ err(EXIT_FAILURE, _("%s: BLKDISCARD ioctl failed"), path);
+ }
+
+ if (verbose)
+ /* TRANSLATORS: The standard value here is a very large number. */
+ printf(_("%s: Discarded %" PRIu64 " bytes from the "
+ "offset %" PRIu64"\n"), path,
+ (uint64_t) range[1], (uint64_t) range[0]);
+
+ close(fd);
+ return EXIT_SUCCESS;
+}