1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2019 Eugene Grosbein <eugen@FreeBSD.org>. 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #include <sys/disk.h> 31 #include <sys/ioctl.h> 32 #include <sys/stat.h> 33 34 #include <err.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <libutil.h> 38 #include <limits.h> 39 #include <paths.h> 40 #include <stdbool.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <sysexits.h> 44 #include <unistd.h> 45 46 #include <sys/cdefs.h> 47 __FBSDID("$FreeBSD$"); 48 49 static off_t getsize(const char *path); 50 static int opendev(const char *path, int flags); 51 static int trim(const char *path, off_t offset, off_t length, bool dryrun, bool verbose); 52 static void usage(const char *name); 53 54 int 55 main(int argc, char **argv) 56 { 57 off_t offset, length; 58 uint64_t usz; 59 int ch, error; 60 bool dryrun, verbose; 61 char *fname, *name; 62 63 error = 0; 64 length = offset = 0; 65 name = argv[0]; 66 dryrun = verbose = true; 67 68 while ((ch = getopt(argc, argv, "Nfl:o:qr:v")) != -1) 69 switch (ch) { 70 case 'N': 71 dryrun = true; 72 verbose = true; 73 break; 74 case 'f': 75 dryrun = false; 76 break; 77 case 'l': 78 case 'o': 79 if (expand_number(optarg, &usz) == -1 || 80 (off_t)usz < 0 || (usz == 0 && ch == 'l')) 81 errx(EX_USAGE, 82 "invalid %s of the region: %s", 83 ch == 'o' ? "offset" : "length", 84 optarg); 85 if (ch == 'o') 86 offset = (off_t)usz; 87 else 88 length = (off_t)usz; 89 break; 90 case 'q': 91 verbose = false; 92 break; 93 case 'r': 94 if ((length = getsize(optarg)) == 0) 95 errx(EX_USAGE, 96 "invalid zero length reference file" 97 " for the region: %s", optarg); 98 break; 99 case 'v': 100 verbose = true; 101 break; 102 default: 103 usage(name); 104 /* NOTREACHED */ 105 } 106 107 argv += optind; 108 argc -= optind; 109 110 if (argc < 1) 111 usage(name); 112 113 while ((fname = *argv++) != NULL) 114 if (trim(fname, offset, length, dryrun, verbose) < 0) 115 error++; 116 117 return (error ? EXIT_FAILURE : EXIT_SUCCESS); 118 } 119 120 static int 121 opendev(const char *path, int flags) 122 { 123 int fd; 124 char *tstr; 125 126 if ((fd = open(path, flags)) < 0) { 127 if (errno == ENOENT && path[0] != '/') { 128 if (asprintf(&tstr, "%s%s", _PATH_DEV, path) < 0) 129 errx(EX_OSERR, "no memory"); 130 fd = open(tstr, flags); 131 free(tstr); 132 } 133 } 134 135 if (fd < 0) 136 err(EX_NOINPUT, "open failed: %s", path); 137 138 return (fd); 139 } 140 141 static off_t 142 getsize(const char *path) 143 { 144 struct stat sb; 145 off_t mediasize; 146 int fd; 147 148 fd = opendev(path, O_RDONLY | O_DIRECT); 149 150 if (fstat(fd, &sb) < 0) 151 err(EX_IOERR, "fstat failed: %s", path); 152 153 if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) { 154 close(fd); 155 return (sb.st_size); 156 } 157 158 if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode)) 159 errx(EX_DATAERR, 160 "invalid type of the file " 161 "(not regular, directory nor special device): %s", 162 path); 163 164 if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) 165 err(EX_UNAVAILABLE, 166 "ioctl(DIOCGMEDIASIZE) failed, probably not a disk: " 167 "%s", path); 168 169 close(fd); 170 return (mediasize); 171 } 172 173 static int 174 trim(const char *path, off_t offset, off_t length, bool dryrun, bool verbose) 175 { 176 off_t arg[2]; 177 int error, fd; 178 179 if (length == 0) 180 length = getsize(path); 181 182 if (verbose) 183 printf("trim %s offset %ju length %ju\n", 184 path, (uintmax_t)offset, (uintmax_t)length); 185 186 if (dryrun) { 187 printf("dry run: add -f to actually perform the operation\n"); 188 return (0); 189 } 190 191 fd = opendev(path, O_WRONLY | O_DIRECT); 192 arg[0] = offset; 193 arg[1] = length; 194 195 error = ioctl(fd, DIOCGDELETE, arg); 196 if (error < 0) 197 warn("ioctl(DIOCGDELETE) failed: %s", path); 198 199 close(fd); 200 return (error); 201 } 202 203 static void 204 usage(const char *name) 205 { 206 (void)fprintf(stderr, 207 "usage: %s [-[lo] offset[K|k|M|m|G|g|T|t]] [-r rfile] [-Nfqv] device ...\n", 208 name); 209 exit(EX_USAGE); 210 } 211