]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/blkdiscard.c
Make the ways of using output stream consistent in usage()
[thirdparty/util-linux.git] / sys-utils / blkdiscard.c
CommitLineData
d964b669
LC
1/*
2 * blkdiscard.c -- discard the part (or whole) of the block device.
3 *
4 * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
5 * Written by Lukas Czerner <lczerner@redhat.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * This program uses BLKDISCARD ioctl to discard part or the whole block
21 * device if the device supports it. You can specify range (start and
22 * length) to be discarded, or simply discard the whole device.
23 */
24
25
26#include <string.h>
27#include <unistd.h>
28#include <stdlib.h>
29#include <stdio.h>
30#include <stdint.h>
31#include <fcntl.h>
32#include <limits.h>
33#include <getopt.h>
4f55368c 34#include <time.h>
d964b669
LC
35
36#include <sys/ioctl.h>
37#include <sys/stat.h>
4f55368c 38#include <sys/time.h>
d964b669 39#include <linux/fs.h>
b6464f8d
KZ
40
41#ifdef HAVE_LIBBLKID
42# include <blkid.h>
43#endif
d964b669
LC
44
45#include "nls.h"
46#include "strutils.h"
47#include "c.h"
48#include "closestream.h"
784467ad 49#include "monotonic.h"
c3e29c68 50#include "exitcodes.h"
4cf6f545 51
d964b669 52#ifndef BLKDISCARD
7154cc89 53# define BLKDISCARD _IO(0x12,119)
d964b669
LC
54#endif
55
56#ifndef BLKSECDISCARD
7154cc89 57# define BLKSECDISCARD _IO(0x12,125)
d964b669
LC
58#endif
59
7154cc89
KZ
60#ifndef BLKZEROOUT
61# define BLKZEROOUT _IO(0x12,127)
62#endif
63
64enum {
65 ACT_DISCARD = 0, /* default */
66 ACT_ZEROOUT,
67 ACT_SECURE
68};
69
8edaa1e1
KZ
70static int quiet;
71
7154cc89
KZ
72static void print_stats(int act, char *path, uint64_t stats[])
73{
74 switch (act) {
75 case ACT_ZEROOUT:
76 printf(_("%s: Zero-filled %" PRIu64 " bytes from the offset %" PRIu64"\n"), \
77 path, stats[1], stats[0]);
78 break;
79 case ACT_SECURE:
80 case ACT_DISCARD:
81 printf(_("%s: Discarded %" PRIu64 " bytes from the offset %" PRIu64"\n"), \
82 path, stats[1], stats[0]);
83 break;
84 }
85}
c472a7e3 86
6e1eda6f 87static void __attribute__((__noreturn__)) usage(void)
d964b669 88{
6e1eda6f 89 FILE *out = stdout;
d964b669
LC
90 fputs(USAGE_HEADER, out);
91 fprintf(out,
92 _(" %s [options] <device>\n"), program_invocation_short_name);
451dbcfa
BS
93
94 fputs(USAGE_SEPARATOR, out);
95 fputs(_("Discard the content of sectors on a device.\n"), out);
96
d964b669 97 fputs(USAGE_OPTIONS, out);
34fed3ff 98 fputs(_(" -f, --force disable all checking\n"), out);
b31fd516 99 fputs(_(" -l, --length <num> length of bytes to discard from the offset\n"), out);
dde842e2 100 fputs(_(" -o, --offset <num> offset in bytes to discard from\n"), out);
b31fd516 101 fputs(_(" -p, --step <num> size of the discard iterations within the offset\n"), out);
8edaa1e1 102 fputs(_(" -q, --quiet suppress warning messages\n"), out);
b31fd516 103 fputs(_(" -s, --secure perform secure discard\n"), out);
b31fd516 104 fputs(_(" -v, --verbose print aligned length and offset\n"), out);
dde842e2 105 fputs(_(" -z, --zeroout zero-fill rather than discard\n"), out);
b31fd516 106
d964b669 107 fputs(USAGE_SEPARATOR, out);
bad4c729 108 fprintf(out, USAGE_HELP_OPTIONS(21));
b31fd516 109
f1970cc5 110 fputs(USAGE_ARGUMENTS, out);
bad4c729 111 fprintf(out, USAGE_ARG_SIZE(_("<num>")));
f1970cc5 112
bad4c729 113 fprintf(out, USAGE_MAN_TAIL("blkdiscard(8)"));
6e1eda6f 114 exit(EXIT_SUCCESS);
d964b669
LC
115}
116
b6464f8d 117#ifdef HAVE_LIBBLKID
0f23e4c1
LC
118/*
119 * Check existing signature on the open fd
120 * Returns 0 signature found
121 * 1 no signature
122 * <0 error
123 */
124static int probe_device(int fd, char *path)
125{
126 const char *type;
127 blkid_probe pr = NULL;
128 int ret = -1;
129
130 pr = blkid_new_probe();
131 if (!pr || blkid_probe_set_device(pr, fd, 0, 0))
132 return ret;
133
134 blkid_probe_enable_superblocks(pr, TRUE);
135 blkid_probe_enable_partitions(pr, TRUE);
136
137 ret = blkid_do_fullprobe(pr);
138 if (ret)
139 goto out;
140
8edaa1e1
KZ
141 if (!quiet) {
142 if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL))
143 warnx("%s contains existing file system (%s).",path ,type);
144 else if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL))
145 warnx("%s contains existing partition (%s).",path ,type);
146 else
147 warnx("%s contains existing signature.", path);
0f23e4c1
LC
148 }
149
150out:
151 blkid_free_probe(pr);
152 return ret;
153}
b6464f8d 154#endif /* HAVE_LIBBLKID */
7154cc89 155
4cf6f545
KZ
156static void __attribute__((__noreturn__)) err_on_ioctl(
157 const char *ioctlname, const char *path)
158{
159 int exno = errno == EOPNOTSUPP ?
c3e29c68 160 EXIT_NOTSUPP : EXIT_FAILURE;
4cf6f545
KZ
161
162 err(exno, _("%s: %s ioctl failed"), ioctlname, path);
163}
164
d964b669
LC
165int main(int argc, char **argv)
166{
167 char *path;
34fed3ff 168 int c, fd, verbose = 0, secsize, force = 0;
c472a7e3 169 uint64_t end, blksize, step, range[2], stats[2];
d964b669 170 struct stat sb;
5d58258d 171 struct timeval now = { 0 }, last = { 0 };
7154cc89 172 int act = ACT_DISCARD;
d964b669
LC
173
174 static const struct option longopts[] = {
34fed3ff 175 { "force", no_argument, NULL, 'f' },
dde842e2 176 { "help", no_argument, NULL, 'h' },
87918040 177 { "length", required_argument, NULL, 'l' },
dde842e2 178 { "offset", required_argument, NULL, 'o' },
8edaa1e1 179 { "quiet", no_argument, NULL, 'q' },
87918040 180 { "secure", no_argument, NULL, 's' },
dde842e2 181 { "step", required_argument, NULL, 'p' },
87918040 182 { "verbose", no_argument, NULL, 'v' },
dde842e2 183 { "version", no_argument, NULL, 'V' },
87918040
SK
184 { "zeroout", no_argument, NULL, 'z' },
185 { NULL, 0, NULL, 0 }
d964b669
LC
186 };
187
188 setlocale(LC_ALL, "");
189 bindtextdomain(PACKAGE, LOCALEDIR);
190 textdomain(PACKAGE);
2c308875 191 close_stdout_atexit();
d964b669
LC
192
193 range[0] = 0;
194 range[1] = ULLONG_MAX;
c472a7e3 195 step = 0;
d964b669 196
8edaa1e1 197 while ((c = getopt_long(argc, argv, "hfVsvo:l:p:qz", longopts, NULL)) != -1) {
d964b669 198 switch(c) {
34fed3ff
KZ
199 case 'f':
200 force = 1;
201 break;
d964b669
LC
202 case 'l':
203 range[1] = strtosize_or_err(optarg,
204 _("failed to parse length"));
205 break;
206 case 'o':
207 range[0] = strtosize_or_err(optarg,
208 _("failed to parse offset"));
209 break;
c472a7e3
FS
210 case 'p':
211 step = strtosize_or_err(optarg,
212 _("failed to parse step"));
213 break;
8edaa1e1
KZ
214 case 'q':
215 quiet = 1;
216 break;
d964b669 217 case 's':
7154cc89 218 act = ACT_SECURE;
d964b669
LC
219 break;
220 case 'v':
221 verbose = 1;
222 break;
7154cc89
KZ
223 case 'z':
224 act = ACT_ZEROOUT;
225 break;
2c308875
KZ
226
227 case 'h':
228 usage();
229 case 'V':
230 print_version(EXIT_SUCCESS);
d964b669 231 default:
677ec86c 232 errtryhelp(EXIT_FAILURE);
d964b669
LC
233 }
234 }
235
236 if (optind == argc)
4ce393f4 237 errx(EXIT_FAILURE, _("no device specified"));
d964b669
LC
238
239 path = argv[optind++];
240
241 if (optind != argc) {
242 warnx(_("unexpected number of arguments"));
6e1eda6f 243 errtryhelp(EXIT_FAILURE);
d964b669
LC
244 }
245
0f23e4c1 246 fd = open(path, O_RDWR | (force ? 0 : O_EXCL));
d964b669
LC
247 if (fd < 0)
248 err(EXIT_FAILURE, _("cannot open %s"), path);
249
77751922 250 if (fstat(fd, &sb) == -1)
fc14ceba 251 err(EXIT_FAILURE, _("stat of %s failed"), path);
77751922
KZ
252 if (!S_ISBLK(sb.st_mode))
253 errx(EXIT_FAILURE, _("%s: not a block device"), path);
254
d964b669
LC
255 if (ioctl(fd, BLKGETSIZE64, &blksize))
256 err(EXIT_FAILURE, _("%s: BLKGETSIZE64 ioctl failed"), path);
d964b669
LC
257 if (ioctl(fd, BLKSSZGET, &secsize))
258 err(EXIT_FAILURE, _("%s: BLKSSZGET ioctl failed"), path);
259
d7ce9acb
FS
260 /* check offset alignment to the sector size */
261 if (range[0] % secsize)
262 errx(EXIT_FAILURE, _("%s: offset %" PRIu64 " is not aligned "
263 "to sector size %i"), path, range[0], secsize);
d964b669
LC
264
265 /* is the range end behind the end of the device ?*/
303884a4 266 if (range[0] > blksize)
0c75b6a4 267 errx(EXIT_FAILURE, _("%s: offset is greater than device size"), path);
d964b669
LC
268 end = range[0] + range[1];
269 if (end < range[0] || end > blksize)
c472a7e3
FS
270 end = blksize;
271
272 range[1] = (step > 0) ? step : end - range[0];
d964b669 273
d7ce9acb
FS
274 /* check length alignment to the sector size */
275 if (range[1] % secsize)
276 errx(EXIT_FAILURE, _("%s: length %" PRIu64 " is not aligned "
277 "to sector size %i"), path, range[1], secsize);
b6464f8d 278#ifdef HAVE_LIBBLKID
8edaa1e1
KZ
279 if (force) {
280 if (!quiet)
281 warnx(_("Operation forced, data will be lost!"));
282 } else {
e3044419
KZ
283 /* Check for existing signatures on the device */
284 switch(probe_device(fd, path)) {
285 case 0: /* signature detected */
286 /*
287 * Only require force in interactive mode to avoid
288 * breaking existing scripts
289 */
290 if (isatty(STDIN_FILENO)) {
291 errx(EXIT_FAILURE,
292 _("This is destructive operation, data will " \
293 "be lost! Use the -f option to override."));
294 }
295 break;
296 case 1: /* no signature */
297 break;
298 default: /* error */
299 err(EXIT_FAILURE, _("failed to probe the device"));
300 break;
301 }
0f23e4c1 302 }
b6464f8d 303#endif /* HAVE_LIBBLKID */
0f23e4c1 304
c472a7e3 305 stats[0] = range[0], stats[1] = 0;
700031ad 306 gettime_monotonic(&last);
c472a7e3 307
ee24ab6f 308 for (/* nothing */; range[0] < end; range[0] += range[1]) {
c472a7e3
FS
309 if (range[0] + range[1] > end)
310 range[1] = end - range[0];
311
4cf6f545
KZ
312 errno = 0;
313
7154cc89
KZ
314 switch (act) {
315 case ACT_ZEROOUT:
316 if (ioctl(fd, BLKZEROOUT, &range))
4cf6f545 317 err_on_ioctl("BLKZEROOUT", path);
7154cc89
KZ
318 break;
319 case ACT_SECURE:
c472a7e3 320 if (ioctl(fd, BLKSECDISCARD, &range))
4cf6f545 321 err_on_ioctl("BLKSECDISCARD", path);
7154cc89
KZ
322 break;
323 case ACT_DISCARD:
c472a7e3 324 if (ioctl(fd, BLKDISCARD, &range))
4cf6f545 325 err_on_ioctl("BLKDISCARD", path);
7154cc89 326 break;
c472a7e3
FS
327 }
328
a3e91e26
RM
329 stats[1] += range[1];
330
0e765365 331 /* reporting progress at most once per second */
c472a7e3 332 if (verbose && step) {
700031ad 333 gettime_monotonic(&now);
0e765365
RM
334 if (now.tv_sec > last.tv_sec &&
335 (now.tv_usec >= last.tv_usec || now.tv_sec > last.tv_sec + 1)) {
7154cc89 336 print_stats(act, path, stats);
a3e91e26 337 stats[0] += stats[1], stats[1] = 0;
c472a7e3
FS
338 last = now;
339 }
340 }
d964b669
LC
341 }
342
eeae4488 343 if (verbose && stats[1])
7154cc89 344 print_stats(act, path, stats);
d964b669
LC
345
346 close(fd);
347 return EXIT_SUCCESS;
348}