1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2018 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 <stdio.h> 41 #include <stdlib.h> 42 #include <sysexits.h> 43 #include <unistd.h> 44 45 #ifndef lint 46 static const char rcsid[] = 47 "$FreeBSD$"; 48 #endif /* not lint */ 49 50 static int trim(char *path, off_t offset, off_t length, int dryrun, int verbose); 51 static off_t getsize(char *path); 52 static void usage(char *name) __dead2; 53 54 int 55 main(int argc, char **argv) 56 { 57 off_t offset, length; 58 uint64_t usz; 59 int ch, dryrun, error, verbose; 60 char *fname, *name; 61 62 error = 0; 63 length = offset = 0; 64 name = argv[0]; 65 dryrun = verbose = 1; 66 67 while ((ch = getopt(argc, argv, "Nfl:o:qr:v")) != -1) 68 switch (ch) { 69 case 'N': 70 dryrun = 1; 71 verbose = 1; 72 break; 73 case 'f': 74 dryrun = 0; 75 break; 76 case 'l': 77 case 'o': 78 if (expand_number(optarg, &usz) == -1 || 79 (off_t)usz < 0 || 80 (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 = 0; 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 = 1; 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 off_t 121 getsize(char *path) 122 { 123 struct stat sb; 124 char *tstr; 125 off_t mediasize; 126 int fd; 127 128 if ((fd = open(path, O_RDONLY | O_DIRECT)) < 0) { 129 if (errno == ENOENT && path[0] != '/') { 130 if (asprintf(&tstr, "%s%s", _PATH_DEV, path) < 0) 131 errx(EX_OSERR, "no memory"); 132 fd = open(tstr, O_RDONLY | O_DIRECT); 133 free(tstr); 134 } 135 } 136 137 if (fd < 0) 138 err(EX_NOINPUT, "`%s'", path); 139 140 if (fstat(fd, &sb) < 0) 141 err(EX_IOERR, "`%s'", path); 142 143 if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) { 144 close(fd); 145 return (sb.st_size); 146 } 147 148 if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode)) 149 errx(EX_DATAERR, 150 "invalid type of the file " 151 "(not regular, directory nor special device): `%s'", 152 path); 153 154 if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) 155 errx(EX_UNAVAILABLE, 156 "ioctl(DIOCGMEDIASIZE) failed, probably not a disk: " 157 "`%s'", path); 158 close(fd); 159 160 return (mediasize); 161 } 162 163 static int 164 trim(char *path, off_t offset, off_t length, int dryrun, int verbose) 165 { 166 off_t arg[2]; 167 char *tstr; 168 int error, fd; 169 170 if (length == 0) 171 length = getsize(path); 172 173 if (verbose) 174 printf("trim `%s' offset %ju length %ju\n", 175 path, (uintmax_t)offset, (uintmax_t)length); 176 177 if (dryrun) { 178 printf("dry run: add -f to actually perform the operation\n"); 179 return (0); 180 } 181 182 if ((fd = open(path, O_WRONLY | O_DIRECT)) < 0) { 183 if (errno == ENOENT && path[0] != '/') { 184 if (asprintf(&tstr, "%s%s", _PATH_DEV, path) < 0) 185 errx(EX_OSERR, "no memory"); 186 fd = open(tstr, O_WRONLY | O_DIRECT); 187 free(tstr); 188 } 189 } 190 191 if (fd < 0) 192 err(EX_NOINPUT, "`%s'", path); 193 194 arg[0] = offset; 195 arg[1] = length; 196 197 error = ioctl(fd, DIOCGDELETE, arg); 198 if (error < 0) 199 warn("ioctl(DIOCGDELETE) failed for `%s'", path); 200 201 close(fd); 202 return (error); 203 } 204 205 static void 206 usage(char *name) 207 { 208 (void)fprintf(stderr, 209 "usage: %s [-[lo] offset[K|k|M|m|G|g|T|t]] [-r rfile] [-Nfqv] device ...\n", 210 name); 211 exit(EX_USAGE); 212 } 213