xref: /netbsd-src/sbin/blkdiscard/blkdiscard.c (revision d3789543466c1fc9b2703e1746ab85d655beccb3)
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