1 /* $NetBSD: blkdiscard.c,v 1.4 2024/02/08 20:51:24 andvar Exp $ */ 2 3 /* 4 * Copyright (c) 2019, 2020, 2022, 2024 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 <stdbool.h> 49 50 static bool secure = false; 51 static bool zeroout = false; 52 static char *zeros = NULL; 53 static unsigned ttywidth = 80; 54 55 #define FDISCARD_VERSION 20240113u 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 struct stat sb; 108 109 /* Default /sbin/blkdiscard to be "run" */ 110 bool norun = strcmp(getprogname(), "blkdiscard") != 0; 111 112 while ((i = getopt(argc, argv, "f:hl:m:no:p:RsvVz")) != -1) { 113 switch (i) { 114 case 'l': 115 if (dehumanize_number(optarg, &val) == -1 || val < 0) 116 usage("bad -s\n"); 117 length = val; 118 break; 119 case 'm': 120 case 'p': 121 if (dehumanize_number(optarg, &val) == -1 || val < 0) 122 usage("bad -m\n"); 123 max_per_call = val; 124 break; 125 case 'f': 126 case 'o': 127 if (dehumanize_number(optarg, &val) == -1 || val < 0) 128 usage("bad -f\n"); 129 first_byte = val; 130 break; 131 case 'n': 132 norun = true; 133 break; 134 case 'R': 135 norun = false; 136 break; 137 case 's': 138 secure = true; 139 break; 140 case 'V': 141 version(); 142 case 'v': 143 verbose = true; 144 break; 145 case 'z': 146 zeroout = true; 147 break; 148 case 'h': 149 usage(NULL); 150 default: 151 usage("bad options\n"); 152 } 153 } 154 argc -= optind; 155 argv += optind; 156 157 if (secure) 158 usage("blkdiscard: secure erase not yet implemented\n"); 159 if (zeroout) { 160 zeros = calloc(1, max_per_call); 161 if (!zeros) 162 errx(1, "Unable to allocate %lld bytes for zeros", 163 (long long)max_per_call); 164 } 165 166 if (length) 167 end_offset = first_byte + length; 168 169 if (argc != 1) 170 usage("not one arg left\n"); 171 172 char *name = argv[0]; 173 int fd = open(name, O_RDWR); 174 if (fd < 0) 175 err(1, "open on %s", name); 176 177 if (size == 0) { 178 struct dkwedge_info dkw; 179 180 if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) { 181 size = dkw.dkw_size * DEV_BSIZE; 182 if (verbose) 183 printf("%s: wedge size is %lld\n", name, 184 (long long)size); 185 } 186 } 187 188 if (size == 0 && fstat(fd, &sb) != -1) { 189 struct disklabel dl; 190 191 if (ioctl(fd, DIOCGDINFO, &dl) != -1) { 192 unsigned part = DISKPART(sb.st_rdev); 193 194 size = (uint64_t)dl.d_partitions[part].p_size * 195 dl.d_secsize; 196 if (verbose) 197 printf("%s: partition %u disklabel size is" 198 " %lld\n", name, part, (long long)size); 199 } 200 201 if (size == 0) { 202 size = sb.st_size; 203 if (verbose) 204 printf("%s: stat size is %lld\n", name, 205 (long long)size); 206 } 207 } 208 209 if (size == 0) { 210 fprintf(stderr, "unable to determine size.\n"); 211 exit(1); 212 } 213 214 size -= first_byte; 215 216 if (end_offset) { 217 if (end_offset > size) { 218 fprintf(stderr, "end_offset %lld > size %lld\n", 219 (long long)end_offset, (long long)size); 220 usage(""); 221 } 222 size = end_offset; 223 } 224 225 if (verbose) { 226 struct winsize winsize; 227 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 if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1 && 237 winsize.ws_col > 1) 238 ttywidth = winsize.ws_col - 1; 239 } 240 241 unsigned loop = 0; 242 while (size > 0) { 243 if (size > max_per_call) 244 discard_size = max_per_call; 245 else 246 discard_size = size; 247 248 if (!norun) 249 write_one(fd, discard_size, first_byte); 250 251 first_byte += discard_size; 252 size -= discard_size; 253 if (verbose) { 254 printf("."); 255 fflush(stdout); 256 if (++loop >= ttywidth) { 257 loop = 0; 258 printf("\n"); 259 } 260 } 261 } 262 if (loop != 0) 263 printf("\n"); 264 265 if (close(fd) != 0) 266 warnx("close failed: %s", strerror(errno)); 267 268 return 0; 269 } 270