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