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>
44 #include "closestream.h"
45 #include "monotonic.h"
48 # define BLKDISCARD _IO(0x12,119)
52 # define BLKSECDISCARD _IO(0x12,125)
56 # define BLKZEROOUT _IO(0x12,127)
60 ACT_DISCARD
= 0, /* default */
65 static void print_stats(int act
, char *path
, uint64_t stats
[])
69 printf(_("%s: Zero-filled %" PRIu64
" bytes from the offset %" PRIu64
"\n"), \
70 path
, stats
[1], stats
[0]);
74 printf(_("%s: Discarded %" PRIu64
" bytes from the offset %" PRIu64
"\n"), \
75 path
, stats
[1], stats
[0]);
80 static void __attribute__((__noreturn__
)) usage(void)
83 fputs(USAGE_HEADER
, out
);
85 _(" %s [options] <device>\n"), program_invocation_short_name
);
87 fputs(USAGE_SEPARATOR
, out
);
88 fputs(_("Discard the content of sectors on a device.\n"), out
);
90 fputs(USAGE_OPTIONS
, out
);
91 fputs(_(" -f, --force disable all checking\n"), out
);
92 fputs(_(" -o, --offset <num> offset in bytes to discard from\n"), out
);
93 fputs(_(" -l, --length <num> length of bytes to discard from the offset\n"), out
);
94 fputs(_(" -p, --step <num> size of the discard iterations within the offset\n"), out
);
95 fputs(_(" -s, --secure perform secure discard\n"), out
);
96 fputs(_(" -z, --zeroout zero-fill rather than discard\n"), out
);
97 fputs(_(" -v, --verbose print aligned length and offset\n"), out
);
99 fputs(USAGE_SEPARATOR
, out
);
100 printf(USAGE_HELP_OPTIONS(21));
102 printf(USAGE_MAN_TAIL("blkdiscard(8)"));
107 int main(int argc
, char **argv
)
110 int c
, fd
, verbose
= 0, secsize
, force
= 0;
111 uint64_t end
, blksize
, step
, range
[2], stats
[2];
113 struct timeval now
, last
;
114 int act
= ACT_DISCARD
;
116 static const struct option longopts
[] = {
117 { "help", no_argument
, NULL
, 'h' },
118 { "version", no_argument
, NULL
, 'V' },
119 { "offset", required_argument
, NULL
, 'o' },
120 { "force", no_argument
, NULL
, 'f' },
121 { "length", required_argument
, NULL
, 'l' },
122 { "step", required_argument
, NULL
, 'p' },
123 { "secure", no_argument
, NULL
, 's' },
124 { "verbose", no_argument
, NULL
, 'v' },
125 { "zeroout", no_argument
, NULL
, 'z' },
129 setlocale(LC_ALL
, "");
130 bindtextdomain(PACKAGE
, LOCALEDIR
);
132 close_stdout_atexit();
135 range
[1] = ULLONG_MAX
;
138 while ((c
= getopt_long(argc
, argv
, "hfVsvo:l:p:z", longopts
, NULL
)) != -1) {
144 range
[1] = strtosize_or_err(optarg
,
145 _("failed to parse length"));
148 range
[0] = strtosize_or_err(optarg
,
149 _("failed to parse offset"));
152 step
= strtosize_or_err(optarg
,
153 _("failed to parse step"));
168 print_version(EXIT_SUCCESS
);
170 errtryhelp(EXIT_FAILURE
);
175 errx(EXIT_FAILURE
, _("no device specified"));
177 path
= argv
[optind
++];
179 if (optind
!= argc
) {
180 warnx(_("unexpected number of arguments"));
181 errtryhelp(EXIT_FAILURE
);
184 fd
= open(path
, O_WRONLY
| (force
? 0 : O_EXCL
));
186 err(EXIT_FAILURE
, _("cannot open %s"), path
);
188 if (fstat(fd
, &sb
) == -1)
189 err(EXIT_FAILURE
, _("stat of %s failed"), path
);
190 if (!S_ISBLK(sb
.st_mode
))
191 errx(EXIT_FAILURE
, _("%s: not a block device"), path
);
193 if (ioctl(fd
, BLKGETSIZE64
, &blksize
))
194 err(EXIT_FAILURE
, _("%s: BLKGETSIZE64 ioctl failed"), path
);
195 if (ioctl(fd
, BLKSSZGET
, &secsize
))
196 err(EXIT_FAILURE
, _("%s: BLKSSZGET ioctl failed"), path
);
198 /* check offset alignment to the sector size */
199 if (range
[0] % secsize
)
200 errx(EXIT_FAILURE
, _("%s: offset %" PRIu64
" is not aligned "
201 "to sector size %i"), path
, range
[0], secsize
);
203 /* is the range end behind the end of the device ?*/
204 if (range
[0] > blksize
)
205 errx(EXIT_FAILURE
, _("%s: offset is greater than device size"), path
);
206 end
= range
[0] + range
[1];
207 if (end
< range
[0] || end
> blksize
)
210 range
[1] = (step
> 0) ? step
: end
- range
[0];
212 /* check length alignment to the sector size */
213 if (range
[1] % secsize
)
214 errx(EXIT_FAILURE
, _("%s: length %" PRIu64
" is not aligned "
215 "to sector size %i"), path
, range
[1], secsize
);
217 stats
[0] = range
[0], stats
[1] = 0;
218 gettime_monotonic(&last
);
220 for (/* nothing */; range
[0] < end
; range
[0] += range
[1]) {
221 if (range
[0] + range
[1] > end
)
222 range
[1] = end
- range
[0];
226 if (ioctl(fd
, BLKZEROOUT
, &range
))
227 err(EXIT_FAILURE
, _("%s: BLKZEROOUT ioctl failed"), path
);
230 if (ioctl(fd
, BLKSECDISCARD
, &range
))
231 err(EXIT_FAILURE
, _("%s: BLKSECDISCARD ioctl failed"), path
);
234 if (ioctl(fd
, BLKDISCARD
, &range
))
235 err(EXIT_FAILURE
, _("%s: BLKDISCARD ioctl failed"), path
);
239 stats
[1] += range
[1];
241 /* reporting progress at most once per second */
242 if (verbose
&& step
) {
243 gettime_monotonic(&now
);
244 if (now
.tv_sec
> last
.tv_sec
&&
245 (now
.tv_usec
>= last
.tv_usec
|| now
.tv_sec
> last
.tv_sec
+ 1)) {
246 print_stats(act
, path
, stats
);
247 stats
[0] += stats
[1], stats
[1] = 0;
253 if (verbose
&& stats
[1])
254 print_stats(act
, path
, stats
);