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(_(" -o, --offset <num> offset in bytes to discard from\n"), out
);
92 fputs(_(" -l, --length <num> length of bytes to discard from the offset\n"), out
);
93 fputs(_(" -p, --step <num> size of the discard iterations within the offset\n"), out
);
94 fputs(_(" -s, --secure perform secure discard\n"), out
);
95 fputs(_(" -z, --zeroout zero-fill rather than discard\n"), out
);
96 fputs(_(" -v, --verbose print aligned length and offset\n"), out
);
98 fputs(USAGE_SEPARATOR
, out
);
99 printf(USAGE_HELP_OPTIONS(21));
101 printf(USAGE_MAN_TAIL("blkdiscard(8)"));
106 int main(int argc
, char **argv
)
109 int c
, fd
, verbose
= 0, secsize
;
110 uint64_t end
, blksize
, step
, range
[2], stats
[2];
112 struct timeval now
, last
;
113 int act
= ACT_DISCARD
;
115 static const struct option longopts
[] = {
116 { "help", no_argument
, NULL
, 'h' },
117 { "version", no_argument
, NULL
, 'V' },
118 { "offset", required_argument
, NULL
, 'o' },
119 { "length", required_argument
, NULL
, 'l' },
120 { "step", required_argument
, NULL
, 'p' },
121 { "secure", no_argument
, NULL
, 's' },
122 { "verbose", no_argument
, NULL
, 'v' },
123 { "zeroout", no_argument
, NULL
, 'z' },
127 setlocale(LC_ALL
, "");
128 bindtextdomain(PACKAGE
, LOCALEDIR
);
130 close_stdout_atexit();
133 range
[1] = ULLONG_MAX
;
136 while ((c
= getopt_long(argc
, argv
, "hVsvo:l:p:z", longopts
, NULL
)) != -1) {
139 range
[1] = strtosize_or_err(optarg
,
140 _("failed to parse length"));
143 range
[0] = strtosize_or_err(optarg
,
144 _("failed to parse offset"));
147 step
= strtosize_or_err(optarg
,
148 _("failed to parse step"));
163 print_version(EXIT_SUCCESS
);
165 errtryhelp(EXIT_FAILURE
);
170 errx(EXIT_FAILURE
, _("no device specified"));
172 path
= argv
[optind
++];
174 if (optind
!= argc
) {
175 warnx(_("unexpected number of arguments"));
176 errtryhelp(EXIT_FAILURE
);
179 fd
= open(path
, O_WRONLY
);
181 err(EXIT_FAILURE
, _("cannot open %s"), path
);
183 if (fstat(fd
, &sb
) == -1)
184 err(EXIT_FAILURE
, _("stat of %s failed"), path
);
185 if (!S_ISBLK(sb
.st_mode
))
186 errx(EXIT_FAILURE
, _("%s: not a block device"), path
);
188 if (ioctl(fd
, BLKGETSIZE64
, &blksize
))
189 err(EXIT_FAILURE
, _("%s: BLKGETSIZE64 ioctl failed"), path
);
190 if (ioctl(fd
, BLKSSZGET
, &secsize
))
191 err(EXIT_FAILURE
, _("%s: BLKSSZGET ioctl failed"), path
);
193 /* check offset alignment to the sector size */
194 if (range
[0] % secsize
)
195 errx(EXIT_FAILURE
, _("%s: offset %" PRIu64
" is not aligned "
196 "to sector size %i"), path
, range
[0], secsize
);
198 /* is the range end behind the end of the device ?*/
199 if (range
[0] > blksize
)
200 errx(EXIT_FAILURE
, _("%s: offset is greater than device size"), path
);
201 end
= range
[0] + range
[1];
202 if (end
< range
[0] || end
> blksize
)
205 range
[1] = (step
> 0) ? step
: end
- range
[0];
207 /* check length alignment to the sector size */
208 if (range
[1] % secsize
)
209 errx(EXIT_FAILURE
, _("%s: length %" PRIu64
" is not aligned "
210 "to sector size %i"), path
, range
[1], secsize
);
212 stats
[0] = range
[0], stats
[1] = 0;
213 gettime_monotonic(&last
);
215 for (/* nothing */; range
[0] < end
; range
[0] += range
[1]) {
216 if (range
[0] + range
[1] > end
)
217 range
[1] = end
- range
[0];
221 if (ioctl(fd
, BLKZEROOUT
, &range
))
222 err(EXIT_FAILURE
, _("%s: BLKZEROOUT ioctl failed"), path
);
225 if (ioctl(fd
, BLKSECDISCARD
, &range
))
226 err(EXIT_FAILURE
, _("%s: BLKSECDISCARD ioctl failed"), path
);
229 if (ioctl(fd
, BLKDISCARD
, &range
))
230 err(EXIT_FAILURE
, _("%s: BLKDISCARD ioctl failed"), path
);
234 stats
[1] += range
[1];
236 /* reporting progress at most once per second */
237 if (verbose
&& step
) {
238 gettime_monotonic(&now
);
239 if (now
.tv_sec
> last
.tv_sec
&&
240 (now
.tv_usec
>= last
.tv_usec
|| now
.tv_sec
> last
.tv_sec
+ 1)) {
241 print_stats(act
, path
, stats
);
242 stats
[0] += stats
[1], stats
[1] = 0;
248 if (verbose
&& stats
[1])
249 print_stats(act
, path
, stats
);