From ca291153ff2c696696c1406aca6433aab6e412a1 Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
Date: Fri, 24 Jun 2016 13:36:32 +0200
Subject: [PATCH 79/84] blkdiscard: backport --zeroout support

The patch also includes upstream cleanups.

Upstream: http://github.com/karelzak/util-linux/commit/0e765365798c54d412e355798ad584a52035f228
Upstream: http://github.com/karelzak/util-linux/commit/a3e91e26467a0f644ee568bb0b3d481591834015
Upstream: http://github.com/karelzak/util-linux/commit/eeae448805c0eb2ef130a6ac301750706bb80420
Upstream: http://github.com/karelzak/util-linux/commit/7154cc892688f3c58cbbcdc2055f2635c1d0ef5b
Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1327886
Signed-off-by: Karel Zak <kzak@redhat.com>
---
 bash-completion/blkdiscard |   2 +-
 sys-utils/blkdiscard.8     |   7 +--
 sys-utils/blkdiscard.c     | 122 ++++++++++++++++++++++++++++++++++-----------
 3 files changed, 98 insertions(+), 33 deletions(-)

diff --git a/bash-completion/blkdiscard b/bash-completion/blkdiscard
index 310cdfb..fb3cb1e 100644
--- a/bash-completion/blkdiscard
+++ b/bash-completion/blkdiscard
@@ -15,7 +15,7 @@ _blkdiscard_module()
	esac
	case $cur in
		-*)
-			OPTS="--offset --length --secure --verbose --help --version"
+			OPTS="--offset --length --secure --zeroout --verbose --help --version"
			COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
			return 0
			;;
diff --git a/sys-utils/blkdiscard.8 b/sys-utils/blkdiscard.8
index 5e094d4..71180e9 100644
--- a/sys-utils/blkdiscard.8
+++ b/sys-utils/blkdiscard.8
@@ -1,15 +1,13 @@
-.\" -*- nroff -*-
 .TH BLKDISCARD 8 "July 2014" "util-linux" "System Administration"
 .SH NAME
 blkdiscard \- discard sectors on a device
 .SH SYNOPSIS
 .B blkdiscard
+[options]
 .RB [ \-o
 .IR offset ]
 .RB [ \-l
 .IR length ]
-.RB [ \-s ]
-.RB [ \-v ]
 .I device
 .SH DESCRIPTION
 .B blkdiscard
@@ -59,6 +57,9 @@ Perform a secure discard.  A secure discard is the same as a regular discard
 except that all copies of the discarded blocks that were possibly created by
 garbage collection must also be erased.  This requires support from the device.
 .TP
+.BR \-z , " \-\-zeroout"
+Zero-fill rather than discard.
+.TP
 .BR \-v , " \-\-verbose"
 Display the aligned values of
 .I offset
diff --git a/sys-utils/blkdiscard.c b/sys-utils/blkdiscard.c
index 92ca52a..0ba99ee 100644
--- a/sys-utils/blkdiscard.c
+++ b/sys-utils/blkdiscard.c
@@ -44,43 +44,95 @@
 #include "closestream.h"

 #ifndef BLKDISCARD
-#define BLKDISCARD	_IO(0x12,119)
+# define BLKDISCARD	_IO(0x12,119)
 #endif

 #ifndef BLKSECDISCARD
-#define BLKSECDISCARD	_IO(0x12,125)
+# define BLKSECDISCARD	_IO(0x12,125)
 #endif

-#define print_stats(path, stats) \
-	printf(_("%s: Discarded %" PRIu64 " bytes from the " \
-		 "offset %" PRIu64"\n"), path, stats[1], stats[0]);
+#ifndef BLKZEROOUT
+# define BLKZEROOUT	_IO(0x12,127)
+#endif
+
+enum {
+	ACT_DISCARD = 0,	/* default */
+	ACT_ZEROOUT,
+	ACT_SECURE
+};
+
+/* RHEL: backport from upstream lib/monotonic.c */
+static int gettime_monotonic(struct timeval *tv)
+{
+#ifdef CLOCK_MONOTONIC
+	/* Can slew only by ntp and adjtime */
+	int ret;
+	struct timespec ts;
+
+# ifdef CLOCK_MONOTONIC_RAW
+	/* Linux specific, can't slew */
+	if (!(ret = clock_gettime(CLOCK_MONOTONIC_RAW, &ts))) {
+# else
+	if (!(ret = clock_gettime(CLOCK_MONOTONIC, &ts))) {
+# endif
+		tv->tv_sec = ts.tv_sec;
+		tv->tv_usec = ts.tv_nsec / 1000;
+	}
+	return ret;
+#else
+	return gettimeofday(tv, NULL);
+#endif
+}
+
+static void print_stats(int act, char *path, uint64_t stats[])
+{
+	switch (act) {
+	case ACT_ZEROOUT:
+		printf(_("%s: Zero-filled %" PRIu64 " bytes from the offset %" PRIu64"\n"), \
+			path, stats[1], stats[0]);
+		break;
+	case ACT_SECURE:
+	case ACT_DISCARD:
+		printf(_("%s: Discarded %" PRIu64 " bytes from the offset %" PRIu64"\n"), \
+			path, stats[1], stats[0]);
+		break;
+	}
+}

 static void __attribute__((__noreturn__)) usage(FILE *out)
 {
	fputs(USAGE_HEADER, out);
	fprintf(out,
	      _(" %s [options] <device>\n"), program_invocation_short_name);
+
+	fputs(USAGE_SEPARATOR, out);
+	fputs(_("Discard the content of sectors on a device.\n"), out);
+
	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"
-		" -p, --step <num>    size of the discard iterations within the offset\n"
-		" -s, --secure        perform secure discard\n"
-		" -v, --verbose       print aligned length and offset\n"),
-		out);
+	fputs(_(" -o, --offset <num>  offset in bytes to discard from\n"), out);
+	fputs(_(" -l, --length <num>  length of bytes to discard from the offset\n"), out);
+	fputs(_(" -p, --step <num>    size of the discard iterations within the offset\n"), out);
+	fputs(_(" -s, --secure        perform secure discard\n"), out);
+	fputs(_(" -z, --zeroout       zero-fill rather than discard\n"), out);
+	fputs(_(" -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, secsize;
+	int c, fd, verbose = 0, secsize;
	uint64_t end, blksize, step, range[2], stats[2];
	struct stat sb;
-	struct timespec now, last;
+	struct timeval now, last;
+	int act = ACT_DISCARD;

	static const struct option longopts[] = {
	    { "help",      0, 0, 'h' },
@@ -90,6 +142,7 @@ int main(int argc, char **argv)
	    { "step",      1, 0, 'p' },
	    { "secure",    0, 0, 's' },
	    { "verbose",   0, 0, 'v' },
+	    { "zeroout",   0, 0, 'z' },
	    { NULL,        0, 0, 0 }
	};

@@ -102,7 +155,7 @@ int main(int argc, char **argv)
	range[1] = ULLONG_MAX;
	step = 0;

-	while ((c = getopt_long(argc, argv, "hVsvo:l:p:", longopts, NULL)) != -1) {
+	while ((c = getopt_long(argc, argv, "hVsvo:l:p:z", longopts, NULL)) != -1) {
		switch(c) {
		case 'h':
			usage(stdout);
@@ -123,11 +176,14 @@ int main(int argc, char **argv)
					_("failed to parse step"));
			break;
		case 's':
-			secure = 1;
+			act = ACT_SECURE;
			break;
		case 'v':
			verbose = 1;
			break;
+		case 'z':
+			act = ACT_ZEROOUT;
+			break;
		default:
			usage(stderr);
			break;
@@ -149,7 +205,7 @@ int main(int argc, char **argv)
		err(EXIT_FAILURE, _("cannot open %s"), path);

	if (fstat(fd, &sb) == -1)
-		err(EXIT_FAILURE, _("stat failed %s"), path);
+		err(EXIT_FAILURE, _("stat of %s failed"), path);
	if (!S_ISBLK(sb.st_mode))
		errx(EXIT_FAILURE, _("%s: not a block device"), path);

@@ -178,35 +234,43 @@ int main(int argc, char **argv)
			 "to sector size %i"), path, range[1], secsize);

	stats[0] = range[0], stats[1] = 0;
-	clock_gettime(CLOCK_MONOTONIC, &last);
+	gettime_monotonic(&last);

-	for (range[0] = range[0]; range[0] < end; range[0] += range[1]) {
+	for (/* nothing */; range[0] < end; range[0] += range[1]) {
		if (range[0] + range[1] > end)
			range[1] = end - range[0];

-		if (secure) {
+		switch (act) {
+		case ACT_ZEROOUT:
+			if (ioctl(fd, BLKZEROOUT, &range))
+				 err(EXIT_FAILURE, _("%s: BLKZEROOUT ioctl failed"), path);
+			break;
+		case ACT_SECURE:
			if (ioctl(fd, BLKSECDISCARD, &range))
				err(EXIT_FAILURE, _("%s: BLKSECDISCARD ioctl failed"), path);
-		} else {
+			break;
+		case ACT_DISCARD:
			if (ioctl(fd, BLKDISCARD, &range))
				err(EXIT_FAILURE, _("%s: BLKDISCARD ioctl failed"), path);
+			break;
		}

-		/* reporting progress */
+		stats[1] += range[1];
+
+		/* reporting progress at most once per second */
		if (verbose && step) {
-			clock_gettime(CLOCK_MONOTONIC, &now);
-			if (last.tv_sec < now.tv_sec) {
-				print_stats(path, stats);
-				stats[0] = range[0], stats[1] = 0;
+			gettime_monotonic(&now);
+			if (now.tv_sec > last.tv_sec &&
+			    (now.tv_usec >= last.tv_usec || now.tv_sec > last.tv_sec + 1)) {
+				print_stats(act, path, stats);
+				stats[0] += stats[1], stats[1] = 0;
				last = now;
			}
		}
-
-		stats[1] += range[1];
	}

-	if (verbose)
-		print_stats(path, stats);
+	if (verbose && stats[1])
+		print_stats(act, path, stats);

	close(fd);
	return EXIT_SUCCESS;
--
2.7.4
