2 * blkdiscard.c -- discard the part (or whole) of the block device.
4 * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
5 * Written by Lukas Czerner <lczerner@redhat.com>
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.
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.
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/>.
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.
36 #include <sys/ioctl.h>
48 #include "closestream.h"
49 #include "monotonic.h"
50 #include "exitcodes.h"
53 # define BLKDISCARD _IO(0x12,119)
57 # define BLKSECDISCARD _IO(0x12,125)
61 # define BLKZEROOUT _IO(0x12,127)
65 ACT_DISCARD
= 0, /* default */
72 static void print_stats(int act
, char *path
, uint64_t stats
[])
76 printf(_("%s: Zero-filled %" PRIu64
" bytes from the offset %" PRIu64
"\n"), \
77 path
, stats
[1], stats
[0]);
81 printf(_("%s: Discarded %" PRIu64
" bytes from the offset %" PRIu64
"\n"), \
82 path
, stats
[1], stats
[0]);
87 static void __attribute__((__noreturn__
)) usage(void)
90 fputs(USAGE_HEADER
, out
);
92 _(" %s [options] <device>\n"), program_invocation_short_name
);
94 fputs(USAGE_SEPARATOR
, out
);
95 fputs(_("Discard the content of sectors on a device.\n"), out
);
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
);
107 fputs(USAGE_SEPARATOR
, out
);
108 fprintf(out
, USAGE_HELP_OPTIONS(21));
110 fputs(USAGE_ARGUMENTS
, out
);
111 fprintf(out
, USAGE_ARG_SIZE(_("<num>")));
113 fprintf(out
, USAGE_MAN_TAIL("blkdiscard(8)"));
119 * Check existing signature on the open fd
120 * Returns 0 signature found
124 static int probe_device(int fd
, char *path
)
127 blkid_probe pr
= NULL
;
130 pr
= blkid_new_probe();
131 if (!pr
|| blkid_probe_set_device(pr
, fd
, 0, 0))
134 blkid_probe_enable_superblocks(pr
, TRUE
);
135 blkid_probe_enable_partitions(pr
, TRUE
);
137 ret
= blkid_do_fullprobe(pr
);
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
);
147 warnx("%s contains existing signature.", path
);
151 blkid_free_probe(pr
);
154 #endif /* HAVE_LIBBLKID */
156 static void __attribute__((__noreturn__
)) err_on_ioctl(
157 const char *ioctlname
, const char *path
)
159 int exno
= errno
== EOPNOTSUPP
?
160 EXIT_NOTSUPP
: EXIT_FAILURE
;
162 err(exno
, _("%s: %s ioctl failed"), ioctlname
, path
);
165 int main(int argc
, char **argv
)
168 int c
, fd
, verbose
= 0, secsize
, force
= 0;
169 uint64_t end
, blksize
, step
, range
[2], stats
[2];
171 struct timeval now
= { 0 }, last
= { 0 };
172 int act
= ACT_DISCARD
;
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' },
188 setlocale(LC_ALL
, "");
189 bindtextdomain(PACKAGE
, LOCALEDIR
);
191 close_stdout_atexit();
194 range
[1] = ULLONG_MAX
;
197 while ((c
= getopt_long(argc
, argv
, "hfVsvo:l:p:qz", longopts
, NULL
)) != -1) {
203 range
[1] = strtosize_or_err(optarg
,
204 _("failed to parse length"));
207 range
[0] = strtosize_or_err(optarg
,
208 _("failed to parse offset"));
211 step
= strtosize_or_err(optarg
,
212 _("failed to parse step"));
230 print_version(EXIT_SUCCESS
);
232 errtryhelp(EXIT_FAILURE
);
237 errx(EXIT_FAILURE
, _("no device specified"));
239 path
= argv
[optind
++];
241 if (optind
!= argc
) {
242 warnx(_("unexpected number of arguments"));
243 errtryhelp(EXIT_FAILURE
);
246 fd
= open(path
, O_RDWR
| (force
? 0 : O_EXCL
));
248 err(EXIT_FAILURE
, _("cannot open %s"), path
);
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
);
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
);
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
);
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
)
272 range
[1] = (step
> 0) ? step
: end
- range
[0];
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
);
281 warnx(_("Operation forced, data will be lost!"));
283 /* Check for existing signatures on the device */
284 switch(probe_device(fd
, path
)) {
285 case 0: /* signature detected */
287 * Only require force in interactive mode to avoid
288 * breaking existing scripts
290 if (isatty(STDIN_FILENO
)) {
292 _("This is destructive operation, data will " \
293 "be lost! Use the -f option to override."));
296 case 1: /* no signature */
299 err(EXIT_FAILURE
, _("failed to probe the device"));
303 #endif /* HAVE_LIBBLKID */
305 stats
[0] = range
[0], stats
[1] = 0;
306 gettime_monotonic(&last
);
308 for (/* nothing */; range
[0] < end
; range
[0] += range
[1]) {
309 if (range
[0] + range
[1] > end
)
310 range
[1] = end
- range
[0];
316 if (ioctl(fd
, BLKZEROOUT
, &range
))
317 err_on_ioctl("BLKZEROOUT", path
);
320 if (ioctl(fd
, BLKSECDISCARD
, &range
))
321 err_on_ioctl("BLKSECDISCARD", path
);
324 if (ioctl(fd
, BLKDISCARD
, &range
))
325 err_on_ioctl("BLKDISCARD", path
);
329 stats
[1] += range
[1];
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;
343 if (verbose
&& stats
[1])
344 print_stats(act
, path
, stats
);