1*d3789543Sandvar /* $NetBSD: blkdiscard.c,v 1.4 2024/02/08 20:51:24 andvar Exp $ */
2de782e43Smrg
3de782e43Smrg /*
44bcb661eSmrg * Copyright (c) 2019, 2020, 2022, 2024 Matthew R. Green
5de782e43Smrg * All rights reserved.
6de782e43Smrg *
7de782e43Smrg * Redistribution and use in source and binary forms, with or without
8de782e43Smrg * modification, are permitted provided that the following conditions
9de782e43Smrg * are met:
10de782e43Smrg * 1. Redistributions of source code must retain the above copyright
11de782e43Smrg * notice, this list of conditions and the following disclaimer.
12de782e43Smrg * 2. Redistributions in binary form must reproduce the above copyright
13de782e43Smrg * notice, this list of conditions and the following disclaimer in the
14de782e43Smrg * documentation and/or other materials provided with the distribution.
15de782e43Smrg * 3. The name of the author may not be used to endorse or promote products
16de782e43Smrg * derived from this software without specific prior written permission.
17de782e43Smrg *
18de782e43Smrg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19de782e43Smrg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20de782e43Smrg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21de782e43Smrg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22de782e43Smrg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23de782e43Smrg * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24de782e43Smrg * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25de782e43Smrg * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26de782e43Smrg * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27de782e43Smrg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28de782e43Smrg * SUCH DAMAGE.
29de782e43Smrg */
30de782e43Smrg
31de782e43Smrg /* from: $eterna: fdiscard-stuff.c,v 1.3 2020/12/25 23:40:19 mrg Exp $ */
32de782e43Smrg
33de782e43Smrg /* fdiscard(2) front-end -- TRIM support. */
34de782e43Smrg
35de782e43Smrg #include <sys/param.h>
36de782e43Smrg #include <sys/stat.h>
37de782e43Smrg #include <sys/disk.h>
38de782e43Smrg #include <sys/disklabel.h>
39de782e43Smrg #include <sys/ioctl.h>
40de782e43Smrg
41de782e43Smrg #include <fcntl.h>
42de782e43Smrg #include <stdio.h>
43de782e43Smrg #include <stdlib.h>
44de782e43Smrg #include <string.h>
45de782e43Smrg #include <unistd.h>
46de782e43Smrg #include <err.h>
47de782e43Smrg #include <errno.h>
48de782e43Smrg #include <stdbool.h>
49de782e43Smrg
50de782e43Smrg static bool secure = false;
51de782e43Smrg static bool zeroout = false;
52de782e43Smrg static char *zeros = NULL;
532bdf8003Smrg static unsigned ttywidth = 80;
54de782e43Smrg
554bcb661eSmrg #define FDISCARD_VERSION 20240113u
56de782e43Smrg
57de782e43Smrg static void __dead
usage(const char * msg)58de782e43Smrg usage(const char* msg)
59de782e43Smrg {
60de782e43Smrg FILE *out = stdout;
61de782e43Smrg int rv = 0;
62de782e43Smrg
63de782e43Smrg if (msg) {
64de782e43Smrg out = stderr;
65de782e43Smrg rv = 1;
66de782e43Smrg fprintf(out, "%s", msg);
67de782e43Smrg }
68de782e43Smrg fprintf(out, "Usage: blkdiscard [-hnRsVvz] [-l length] "
69de782e43Smrg "[-m chunk_bytes]\n"
70de782e43Smrg " [-o first_byte] <file>\n");
71de782e43Smrg exit(rv);
72de782e43Smrg }
73de782e43Smrg
74de782e43Smrg static void __dead
version(void)75de782e43Smrg version(void)
76de782e43Smrg {
77de782e43Smrg
78de782e43Smrg printf("NetBSD blkdiscard %u\n", FDISCARD_VERSION);
79de782e43Smrg exit(0);
80de782e43Smrg }
81de782e43Smrg
82de782e43Smrg static void
write_one(int fd,off_t discard_size,off_t first_byte)83de782e43Smrg write_one(int fd, off_t discard_size, off_t first_byte)
84de782e43Smrg {
85de782e43Smrg
86de782e43Smrg if (secure)
87de782e43Smrg /* not yet */;
88de782e43Smrg else if (zeroout) {
89de782e43Smrg if (pwrite(fd, zeros, discard_size, first_byte) != discard_size)
90de782e43Smrg err(1, "pwrite");
91de782e43Smrg } else if (fdiscard(fd, first_byte, discard_size) != 0)
92de782e43Smrg err(1, "fdiscard");
93de782e43Smrg }
94de782e43Smrg
95de782e43Smrg int
main(int argc,char * argv[])96de782e43Smrg main(int argc, char *argv[])
97de782e43Smrg {
98de782e43Smrg off_t first_byte = 0, end_offset = 0;
99de782e43Smrg /* doing a terabyte at a time leads to ATA timeout issues */
100de782e43Smrg off_t max_per_call = 32 * 1024 * 1024;
101de782e43Smrg off_t discard_size;
102de782e43Smrg off_t size = 0;
103de782e43Smrg off_t length = 0;
104de782e43Smrg int64_t val;
105de782e43Smrg int i;
106de782e43Smrg bool verbose = false;
1074bcb661eSmrg struct stat sb;
108de782e43Smrg
109de782e43Smrg /* Default /sbin/blkdiscard to be "run" */
110de782e43Smrg bool norun = strcmp(getprogname(), "blkdiscard") != 0;
111de782e43Smrg
112de782e43Smrg while ((i = getopt(argc, argv, "f:hl:m:no:p:RsvVz")) != -1) {
113de782e43Smrg switch (i) {
114de782e43Smrg case 'l':
115de782e43Smrg if (dehumanize_number(optarg, &val) == -1 || val < 0)
116de782e43Smrg usage("bad -s\n");
117de782e43Smrg length = val;
118de782e43Smrg break;
119de782e43Smrg case 'm':
120de782e43Smrg case 'p':
121de782e43Smrg if (dehumanize_number(optarg, &val) == -1 || val < 0)
122de782e43Smrg usage("bad -m\n");
123de782e43Smrg max_per_call = val;
124de782e43Smrg break;
125de782e43Smrg case 'f':
126de782e43Smrg case 'o':
127de782e43Smrg if (dehumanize_number(optarg, &val) == -1 || val < 0)
128de782e43Smrg usage("bad -f\n");
129de782e43Smrg first_byte = val;
130de782e43Smrg break;
131de782e43Smrg case 'n':
132de782e43Smrg norun = true;
133de782e43Smrg break;
134de782e43Smrg case 'R':
135de782e43Smrg norun = false;
136de782e43Smrg break;
137de782e43Smrg case 's':
138de782e43Smrg secure = true;
139de782e43Smrg break;
140de782e43Smrg case 'V':
141de782e43Smrg version();
142de782e43Smrg case 'v':
143de782e43Smrg verbose = true;
144de782e43Smrg break;
145de782e43Smrg case 'z':
146de782e43Smrg zeroout = true;
147de782e43Smrg break;
148de782e43Smrg case 'h':
149de782e43Smrg usage(NULL);
150de782e43Smrg default:
151de782e43Smrg usage("bad options\n");
152de782e43Smrg }
153de782e43Smrg }
154de782e43Smrg argc -= optind;
155de782e43Smrg argv += optind;
156de782e43Smrg
157de782e43Smrg if (secure)
158*d3789543Sandvar usage("blkdiscard: secure erase not yet implemented\n");
159de782e43Smrg if (zeroout) {
160de782e43Smrg zeros = calloc(1, max_per_call);
161de782e43Smrg if (!zeros)
162de782e43Smrg errx(1, "Unable to allocate %lld bytes for zeros",
163de782e43Smrg (long long)max_per_call);
164de782e43Smrg }
165de782e43Smrg
166de782e43Smrg if (length)
167de782e43Smrg end_offset = first_byte + length;
168de782e43Smrg
169de782e43Smrg if (argc != 1)
170de782e43Smrg usage("not one arg left\n");
171de782e43Smrg
172de782e43Smrg char *name = argv[0];
173de782e43Smrg int fd = open(name, O_RDWR);
174de782e43Smrg if (fd < 0)
175de782e43Smrg err(1, "open on %s", name);
176de782e43Smrg
177de782e43Smrg if (size == 0) {
178de782e43Smrg struct dkwedge_info dkw;
1794bcb661eSmrg
180de782e43Smrg if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
181de782e43Smrg size = dkw.dkw_size * DEV_BSIZE;
182de782e43Smrg if (verbose)
183de782e43Smrg printf("%s: wedge size is %lld\n", name,
184de782e43Smrg (long long)size);
185de782e43Smrg }
186de782e43Smrg }
187de782e43Smrg
1884bcb661eSmrg if (size == 0 && fstat(fd, &sb) != -1) {
189de782e43Smrg struct disklabel dl;
1904bcb661eSmrg
191de782e43Smrg if (ioctl(fd, DIOCGDINFO, &dl) != -1) {
1924bcb661eSmrg unsigned part = DISKPART(sb.st_rdev);
193de782e43Smrg
194de782e43Smrg size = (uint64_t)dl.d_partitions[part].p_size *
195de782e43Smrg dl.d_secsize;
196de782e43Smrg if (verbose)
1974bcb661eSmrg printf("%s: partition %u disklabel size is"
1984bcb661eSmrg " %lld\n", name, part, (long long)size);
199de782e43Smrg }
200de782e43Smrg
201de782e43Smrg if (size == 0) {
202de782e43Smrg size = sb.st_size;
203de782e43Smrg if (verbose)
204de782e43Smrg printf("%s: stat size is %lld\n", name,
205de782e43Smrg (long long)size);
206de782e43Smrg }
207de782e43Smrg }
208de782e43Smrg
209de782e43Smrg if (size == 0) {
210de782e43Smrg fprintf(stderr, "unable to determine size.\n");
211de782e43Smrg exit(1);
212de782e43Smrg }
213de782e43Smrg
214de782e43Smrg size -= first_byte;
215de782e43Smrg
216de782e43Smrg if (end_offset) {
217de782e43Smrg if (end_offset > size) {
218de782e43Smrg fprintf(stderr, "end_offset %lld > size %lld\n",
219de782e43Smrg (long long)end_offset, (long long)size);
220de782e43Smrg usage("");
221de782e43Smrg }
222de782e43Smrg size = end_offset;
223de782e43Smrg }
224de782e43Smrg
2252bdf8003Smrg if (verbose) {
2262bdf8003Smrg struct winsize winsize;
2272bdf8003Smrg
228de782e43Smrg printf("%sgoing to %s on %s from byte %lld for "
229de782e43Smrg "%lld bytes, %lld at a time\n",
230de782e43Smrg norun ? "not " : "",
231de782e43Smrg secure ? "secure erase" :
232de782e43Smrg zeroout ? "zero out" : "fdiscard(2)",
233de782e43Smrg name, (long long)first_byte, (long long)size,
234de782e43Smrg (long long)max_per_call);
235de782e43Smrg
2362bdf8003Smrg if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1 &&
2372bdf8003Smrg winsize.ws_col > 1)
2382bdf8003Smrg ttywidth = winsize.ws_col - 1;
2392bdf8003Smrg }
2402bdf8003Smrg
2412bdf8003Smrg unsigned loop = 0;
242de782e43Smrg while (size > 0) {
243de782e43Smrg if (size > max_per_call)
244de782e43Smrg discard_size = max_per_call;
245de782e43Smrg else
246de782e43Smrg discard_size = size;
247de782e43Smrg
248de782e43Smrg if (!norun)
249de782e43Smrg write_one(fd, discard_size, first_byte);
250de782e43Smrg
251de782e43Smrg first_byte += discard_size;
252de782e43Smrg size -= discard_size;
253de782e43Smrg if (verbose) {
254de782e43Smrg printf(".");
255de782e43Smrg fflush(stdout);
2562bdf8003Smrg if (++loop >= ttywidth) {
257de782e43Smrg loop = 0;
258de782e43Smrg printf("\n");
259de782e43Smrg }
260de782e43Smrg }
261de782e43Smrg }
262de782e43Smrg if (loop != 0)
263de782e43Smrg printf("\n");
264de782e43Smrg
265de782e43Smrg if (close(fd) != 0)
266de782e43Smrg warnx("close failed: %s", strerror(errno));
267de782e43Smrg
268de782e43Smrg return 0;
269de782e43Smrg }
270