summaryrefslogtreecommitdiffstats
path: root/sys-utils/fstrim.c
diff options
context:
space:
mode:
authorKarel Zak2012-09-27 11:21:35 +0200
committerKarel Zak2012-09-27 11:38:31 +0200
commitc84ed54cefdcaa07f3d1f40a65434c9d4277da08 (patch)
tree6342fb9ea17063997df0164401f760e6082c8f9c /sys-utils/fstrim.c
parentlibmount: don't setup new loopdev on remount with regular mtab (diff)
downloadkernel-qcow2-util-linux-c84ed54cefdcaa07f3d1f40a65434c9d4277da08.tar.gz
kernel-qcow2-util-linux-c84ed54cefdcaa07f3d1f40a65434c9d4277da08.tar.xz
kernel-qcow2-util-linux-c84ed54cefdcaa07f3d1f40a65434c9d4277da08.zip
blkdiscard: new command
Add to fstrim(8) code to support new discard BLKDISCARD and BLKSECDISCARD ioctls for block devices. The new command is only symlink to fstrim(8) as the both utils share some code and the basic ideas. Based on patch from Lukas Czerner <lczerner@redhat.com>. Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'sys-utils/fstrim.c')
-rw-r--r--sys-utils/fstrim.c154
1 files changed, 119 insertions, 35 deletions
diff --git a/sys-utils/fstrim.c b/sys-utils/fstrim.c
index 332601df7..cfe2770d2 100644
--- a/sys-utils/fstrim.c
+++ b/sys-utils/fstrim.c
@@ -1,7 +1,7 @@
/*
* fstrim.c -- discard the part (or whole) of mounted filesystem.
*
- * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2010,2012 Red Hat, Inc. All rights reserved.
* Written by Lukas Czerner <lczerner@redhat.com>
* Karel Zak <kzak@redhat.com>
*
@@ -23,7 +23,6 @@
* online (mounted). You can specify range (start and length) to be
* discarded, or simply discard whole filesystem.
*/
-
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
@@ -41,6 +40,7 @@
#include "strutils.h"
#include "c.h"
#include "closestream.h"
+#include "blkdev.h"
#ifndef FITRIM
struct fstrim_range {
@@ -48,34 +48,107 @@ struct fstrim_range {
uint64_t len;
uint64_t minlen;
};
-#define FITRIM _IOWR('X', 121, struct fstrim_range)
+# define FITRIM _IOWR('X', 121, struct fstrim_range)
+#endif
+
+#ifndef BLKDISCARD
+# define BLKDISCARD _IO(0x12,119)
+#endif
+#ifndef BLKSECDISCARD
+# define BLKSECDISCARD _IO(0x12,125)
#endif
+static int is_blk = 0;
+
static void __attribute__((__noreturn__)) usage(FILE *out)
{
fputs(USAGE_HEADER, out);
- fprintf(out,
- _(" %s [options] <mount point>\n"), program_invocation_short_name);
- fputs(USAGE_OPTIONS, out);
+
+ if (is_blk) {
+ fprintf(out, _(" %s [options] <device>\n"), program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -s, --secure perform secure discard\n"), out);
+ } else {
+ fprintf(out, _(" %s [options] <mountpoint>\n"), program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -m, --minimum <num> minimum extent length to discard\n"), out);
+ }
fputs(_(" -o, --offset <num> offset in bytes to discard from\n"
" -l, --length <num> length of bytes to discard from the offset\n"
- " -m, --minimum <num> minimum extent length to discard\n"
" -v, --verbose print number of discarded bytes\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(USAGE_HELP, out);
fputs(USAGE_VERSION, out);
- fprintf(out, USAGE_MAN_TAIL("fstrim(8)"));
+ fprintf(out, USAGE_MAN_TAIL(is_blk ? "blkdiscard(8)" : "fstrim(8)"));
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
-int main(int argc, char **argv)
+static void do_fstrim(const char *path, uint64_t off, uint64_t len, uint64_t minlen)
{
- char *path;
- int c, fd, verbose = 0;
+ int fd;
+ struct stat sb;
struct fstrim_range range;
+
+ range.start = off;
+ range.len = len;
+ range.minlen = minlen;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), path);
+ if (fstat(fd, &sb) == -1)
+ err(EXIT_FAILURE, _("stat failed %s"), path);
+ if (!S_ISDIR(sb.st_mode))
+ errx(EXIT_FAILURE, _("%s: not a directory"), path);
+ if (ioctl(fd, FITRIM, &range))
+ err(EXIT_FAILURE, _("%s: FITRIM ioctl failed"), path);
+ close(fd);
+}
+
+static void do_blkdiscard(const char *path, uint64_t off, uint64_t len, int sec)
+{
+ int fd, secsize;
struct stat sb;
+ uint64_t blksize, range[2], end;
- static const struct option longopts[] = {
+ fd = open(path, O_WRONLY);
+ if (fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), path);
+ if (fstat(fd, &sb) == -1)
+ err(EXIT_FAILURE, _("stat failed %s"), path);
+ if (!S_ISBLK(sb.st_mode))
+ errx(EXIT_FAILURE, _("%s: not a block device"), path);
+ if (blkdev_get_size(fd, (unsigned long long *) &blksize) != 0)
+ err(EXIT_FAILURE, _("%s: failed to get device size"), path);
+ if (blkdev_get_sector_size(fd, &secsize) != 0)
+ err(EXIT_FAILURE, _("%s: failed to get sector size"), path);
+
+ /* align range to the sector size */
+ range[0] = (off + secsize - 1) & ~(secsize - 1);
+ range[1] = len & ~(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 (sec) {
+ 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);
+ }
+ close(fd);
+}
+
+int main(int argc, char **argv)
+{
+ char *path;
+ int c, verbose = 0, secure = 0;
+ uint64_t len = UINT64_MAX, off = 0, minlen = 0;
+
+ static const struct option fs_longopts[] = {
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'V' },
{ "offset", 1, 0, 'o' },
@@ -84,16 +157,32 @@ int main(int argc, char **argv)
{ "verbose", 0, 0, 'v' },
{ NULL, 0, 0, 0 }
};
+ static const struct option blk_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);
- memset(&range, 0, sizeof(range));
- range.len = ULLONG_MAX;
+ if (strcmp(program_invocation_short_name, "blkdiscard") == 0)
+ is_blk = 1;
+
+ do {
+ if (is_blk)
+ c = getopt_long(argc, argv, "hVo:l:sv", blk_longopts, NULL);
+ else
+ c = getopt_long(argc, argv, "hVo:l:m:v", fs_longopts, NULL);
+ if (c == -1)
+ break;
- while ((c = getopt_long(argc, argv, "hVo:l:m:v", longopts, NULL)) != -1) {
switch(c) {
case 'h':
usage(stdout);
@@ -102,17 +191,20 @@ int main(int argc, char **argv)
printf(UTIL_LINUX_VERSION);
return EXIT_SUCCESS;
case 'l':
- range.len = strtosize_or_err(optarg,
+ len = strtosize_or_err(optarg,
_("failed to parse length"));
break;
case 'o':
- range.start = strtosize_or_err(optarg,
+ off = strtosize_or_err(optarg,
_("failed to parse offset"));
break;
case 'm':
- range.minlen = strtosize_or_err(optarg,
+ minlen = strtosize_or_err(optarg,
_("failed to parse minimum extent length"));
break;
+ case 's':
+ secure = 1;
+ break;
case 'v':
verbose = 1;
break;
@@ -120,11 +212,11 @@ int main(int argc, char **argv)
usage(stderr);
break;
}
- }
+ } while (1);
if (optind == argc)
- errx(EXIT_FAILURE, _("no mountpoint specified."));
-
+ errx(EXIT_FAILURE, is_blk ? _("no device specified.") :
+ _("no mountpoint specified"));
path = argv[optind++];
if (optind != argc) {
@@ -132,22 +224,14 @@ int main(int argc, char **argv)
usage(stderr);
}
- if (stat(path, &sb) == -1)
- err(EXIT_FAILURE, _("stat failed %s"), path);
- if (!S_ISDIR(sb.st_mode))
- errx(EXIT_FAILURE, _("%s: not a directory"), path);
-
- fd = open(path, O_RDONLY);
- if (fd < 0)
- err(EXIT_FAILURE, _("cannot open %s"), path);
-
- if (ioctl(fd, FITRIM, &range))
- err(EXIT_FAILURE, _("%s: FITRIM ioctl failed"), path);
+ if (is_blk)
+ do_blkdiscard(path, off, len, secure);
+ else
+ do_fstrim(path, off, len, minlen);
if (verbose)
/* TRANSLATORS: The standard value here is a very large number. */
- printf(_("%s: %" PRIu64 " bytes were trimmed\n"),
- path, (uint64_t) range.len);
- close(fd);
+ printf(_("%s: %" PRIu64 " bytes were trimmed\n"), path, len);
+
return EXIT_SUCCESS;
}