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