]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/blkdiscard.c
Merge branch 'getwc' of https://github.com/t-8ch/util-linux
[thirdparty/util-linux.git] / sys-utils / blkdiscard.c
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>
34 #include <time.h>
35
36 #include <sys/ioctl.h>
37 #include <sys/stat.h>
38 #include <sys/time.h>
39 #include <linux/fs.h>
40
41 #ifdef HAVE_LIBBLKID
42 # include <blkid.h>
43 #endif
44
45 #include "nls.h"
46 #include "strutils.h"
47 #include "c.h"
48 #include "closestream.h"
49 #include "monotonic.h"
50 #include "exitcodes.h"
51
52 #ifndef BLKDISCARD
53 # define BLKDISCARD _IO(0x12,119)
54 #endif
55
56 #ifndef BLKSECDISCARD
57 # define BLKSECDISCARD _IO(0x12,125)
58 #endif
59
60 #ifndef BLKZEROOUT
61 # define BLKZEROOUT _IO(0x12,127)
62 #endif
63
64 enum {
65 ACT_DISCARD = 0, /* default */
66 ACT_ZEROOUT,
67 ACT_SECURE
68 };
69
70 static int quiet;
71
72 static 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 }
86
87 static void __attribute__((__noreturn__)) usage(void)
88 {
89 FILE *out = stdout;
90 fputs(USAGE_HEADER, out);
91 fprintf(out,
92 _(" %s [options] <device>\n"), program_invocation_short_name);
93
94 fputs(USAGE_SEPARATOR, out);
95 fputs(_("Discard the content of sectors on a device.\n"), out);
96
97 fputs(USAGE_OPTIONS, out);
98 fputs(_(" -f, --force disable all checking\n"), out);
99 fputs(_(" -l, --length <num> length of bytes to discard from the offset\n"), out);
100 fputs(_(" -o, --offset <num> offset in bytes to discard from\n"), out);
101 fputs(_(" -p, --step <num> size of the discard iterations within the offset\n"), out);
102 fputs(_(" -q, --quiet suppress warning messages\n"), out);
103 fputs(_(" -s, --secure perform secure discard\n"), out);
104 fputs(_(" -v, --verbose print aligned length and offset\n"), out);
105 fputs(_(" -z, --zeroout zero-fill rather than discard\n"), out);
106
107 fputs(USAGE_SEPARATOR, out);
108 fprintf(out, USAGE_HELP_OPTIONS(21));
109
110 fputs(USAGE_ARGUMENTS, out);
111 fprintf(out, USAGE_ARG_SIZE(_("<num>")));
112
113 fprintf(out, USAGE_MAN_TAIL("blkdiscard(8)"));
114 exit(EXIT_SUCCESS);
115 }
116
117 #ifdef HAVE_LIBBLKID
118 /*
119 * Check existing signature on the open fd
120 * Returns 0 signature found
121 * 1 no signature
122 * <0 error
123 */
124 static 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
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);
148 }
149
150 out:
151 blkid_free_probe(pr);
152 return ret;
153 }
154 #endif /* HAVE_LIBBLKID */
155
156 static void __attribute__((__noreturn__)) err_on_ioctl(
157 const char *ioctlname, const char *path)
158 {
159 int exno = errno == EOPNOTSUPP ?
160 EXIT_NOTSUPP : EXIT_FAILURE;
161
162 err(exno, _("%s: %s ioctl failed"), ioctlname, path);
163 }
164
165 int main(int argc, char **argv)
166 {
167 char *path;
168 int c, fd, verbose = 0, secsize, force = 0;
169 uint64_t end, blksize, step, range[2], stats[2];
170 struct stat sb;
171 struct timeval now = { 0 }, last = { 0 };
172 int act = ACT_DISCARD;
173
174 static const struct option longopts[] = {
175 { "force", no_argument, NULL, 'f' },
176 { "help", no_argument, NULL, 'h' },
177 { "length", required_argument, NULL, 'l' },
178 { "offset", required_argument, NULL, 'o' },
179 { "quiet", no_argument, NULL, 'q' },
180 { "secure", no_argument, NULL, 's' },
181 { "step", required_argument, NULL, 'p' },
182 { "verbose", no_argument, NULL, 'v' },
183 { "version", no_argument, NULL, 'V' },
184 { "zeroout", no_argument, NULL, 'z' },
185 { NULL, 0, NULL, 0 }
186 };
187
188 setlocale(LC_ALL, "");
189 bindtextdomain(PACKAGE, LOCALEDIR);
190 textdomain(PACKAGE);
191 close_stdout_atexit();
192
193 range[0] = 0;
194 range[1] = ULLONG_MAX;
195 step = 0;
196
197 while ((c = getopt_long(argc, argv, "hfVsvo:l:p:qz", longopts, NULL)) != -1) {
198 switch(c) {
199 case 'f':
200 force = 1;
201 break;
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;
210 case 'p':
211 step = strtosize_or_err(optarg,
212 _("failed to parse step"));
213 break;
214 case 'q':
215 quiet = 1;
216 break;
217 case 's':
218 act = ACT_SECURE;
219 break;
220 case 'v':
221 verbose = 1;
222 break;
223 case 'z':
224 act = ACT_ZEROOUT;
225 break;
226
227 case 'h':
228 usage();
229 case 'V':
230 print_version(EXIT_SUCCESS);
231 default:
232 errtryhelp(EXIT_FAILURE);
233 }
234 }
235
236 if (optind == argc)
237 errx(EXIT_FAILURE, _("no device specified"));
238
239 path = argv[optind++];
240
241 if (optind != argc) {
242 warnx(_("unexpected number of arguments"));
243 errtryhelp(EXIT_FAILURE);
244 }
245
246 fd = open(path, O_RDWR | (force ? 0 : O_EXCL));
247 if (fd < 0)
248 err(EXIT_FAILURE, _("cannot open %s"), path);
249
250 if (fstat(fd, &sb) == -1)
251 err(EXIT_FAILURE, _("stat of %s failed"), path);
252 if (!S_ISBLK(sb.st_mode))
253 errx(EXIT_FAILURE, _("%s: not a block device"), path);
254
255 if (ioctl(fd, BLKGETSIZE64, &blksize))
256 err(EXIT_FAILURE, _("%s: BLKGETSIZE64 ioctl failed"), path);
257 if (ioctl(fd, BLKSSZGET, &secsize))
258 err(EXIT_FAILURE, _("%s: BLKSSZGET ioctl failed"), path);
259
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);
264
265 /* is the range end behind the end of the device ?*/
266 if (range[0] > blksize)
267 errx(EXIT_FAILURE, _("%s: offset is greater than device size"), path);
268 end = range[0] + range[1];
269 if (end < range[0] || end > blksize)
270 end = blksize;
271
272 range[1] = (step > 0) ? step : end - range[0];
273
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);
278 #ifdef HAVE_LIBBLKID
279 if (force) {
280 if (!quiet)
281 warnx(_("Operation forced, data will be lost!"));
282 } else {
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 }
302 }
303 #endif /* HAVE_LIBBLKID */
304
305 stats[0] = range[0], stats[1] = 0;
306 gettime_monotonic(&last);
307
308 for (/* nothing */; range[0] < end; range[0] += range[1]) {
309 if (range[0] + range[1] > end)
310 range[1] = end - range[0];
311
312 errno = 0;
313
314 switch (act) {
315 case ACT_ZEROOUT:
316 if (ioctl(fd, BLKZEROOUT, &range))
317 err_on_ioctl("BLKZEROOUT", path);
318 break;
319 case ACT_SECURE:
320 if (ioctl(fd, BLKSECDISCARD, &range))
321 err_on_ioctl("BLKSECDISCARD", path);
322 break;
323 case ACT_DISCARD:
324 if (ioctl(fd, BLKDISCARD, &range))
325 err_on_ioctl("BLKDISCARD", path);
326 break;
327 }
328
329 stats[1] += range[1];
330
331 /* reporting progress at most once per second */
332 if (verbose && step) {
333 gettime_monotonic(&now);
334 if (now.tv_sec > last.tv_sec &&
335 (now.tv_usec >= last.tv_usec || now.tv_sec > last.tv_sec + 1)) {
336 print_stats(act, path, stats);
337 stats[0] += stats[1], stats[1] = 0;
338 last = now;
339 }
340 }
341 }
342
343 if (verbose && stats[1])
344 print_stats(act, path, stats);
345
346 close(fd);
347 return EXIT_SUCCESS;
348 }