12b5691eeSEugene Grosbein /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 32b5691eeSEugene Grosbein * 42b5691eeSEugene Grosbein * Copyright (c) 2019 Eugene Grosbein <eugen@FreeBSD.org>. 5fa707ac7SEugene Grosbein * Contains code written by Alan Somers <asomers@FreeBSD.org>. 62b5691eeSEugene Grosbein * 72b5691eeSEugene Grosbein * Redistribution and use in source and binary forms, with or without 82b5691eeSEugene Grosbein * modification, are permitted provided that the following conditions 92b5691eeSEugene Grosbein * are met: 102b5691eeSEugene Grosbein * 1. Redistributions of source code must retain the above copyright 112b5691eeSEugene Grosbein * notice, this list of conditions and the following disclaimer. 122b5691eeSEugene Grosbein * 2. Redistributions in binary form must reproduce the above copyright 132b5691eeSEugene Grosbein * notice, this list of conditions and the following disclaimer in the 142b5691eeSEugene Grosbein * documentation and/or other materials provided with the distribution. 152b5691eeSEugene Grosbein * 162b5691eeSEugene Grosbein * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 172b5691eeSEugene Grosbein * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 182b5691eeSEugene Grosbein * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 192b5691eeSEugene Grosbein * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 202b5691eeSEugene Grosbein * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 212b5691eeSEugene Grosbein * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 222b5691eeSEugene Grosbein * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 232b5691eeSEugene Grosbein * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 242b5691eeSEugene Grosbein * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 252b5691eeSEugene Grosbein * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 262b5691eeSEugene Grosbein * SUCH DAMAGE. 272b5691eeSEugene Grosbein * 282b5691eeSEugene Grosbein */ 292b5691eeSEugene Grosbein 302b5691eeSEugene Grosbein #include <sys/disk.h> 312b5691eeSEugene Grosbein #include <sys/ioctl.h> 322b5691eeSEugene Grosbein #include <sys/stat.h> 332b5691eeSEugene Grosbein 342b5691eeSEugene Grosbein #include <err.h> 352b5691eeSEugene Grosbein #include <errno.h> 362b5691eeSEugene Grosbein #include <fcntl.h> 372b5691eeSEugene Grosbein #include <libutil.h> 382b5691eeSEugene Grosbein #include <limits.h> 392b5691eeSEugene Grosbein #include <paths.h> 402b5691eeSEugene Grosbein #include <stdbool.h> 412b5691eeSEugene Grosbein #include <stdio.h> 422b5691eeSEugene Grosbein #include <stdlib.h> 437f654915SEugene Grosbein #include <string.h> 442b5691eeSEugene Grosbein #include <sysexits.h> 452b5691eeSEugene Grosbein #include <unistd.h> 462b5691eeSEugene Grosbein 472b5691eeSEugene Grosbein #include <sys/cdefs.h> 48fa707ac7SEugene Grosbein static bool candelete(int fd); 492b5691eeSEugene Grosbein static off_t getsize(const char *path); 502b5691eeSEugene Grosbein static int opendev(const char *path, int flags); 512b5691eeSEugene Grosbein static int trim(const char *path, off_t offset, off_t length, bool dryrun, bool verbose); 522b5691eeSEugene Grosbein static void usage(const char *name); 532b5691eeSEugene Grosbein 542b5691eeSEugene Grosbein int 552b5691eeSEugene Grosbein main(int argc, char **argv) 562b5691eeSEugene Grosbein { 572b5691eeSEugene Grosbein off_t offset, length; 582b5691eeSEugene Grosbein uint64_t usz; 592b5691eeSEugene Grosbein int ch, error; 602b5691eeSEugene Grosbein bool dryrun, verbose; 612b5691eeSEugene Grosbein char *fname, *name; 622b5691eeSEugene Grosbein 632b5691eeSEugene Grosbein error = 0; 642b5691eeSEugene Grosbein length = offset = 0; 652b5691eeSEugene Grosbein name = argv[0]; 662b5691eeSEugene Grosbein dryrun = verbose = true; 672b5691eeSEugene Grosbein 682b5691eeSEugene Grosbein while ((ch = getopt(argc, argv, "Nfl:o:qr:v")) != -1) 692b5691eeSEugene Grosbein switch (ch) { 702b5691eeSEugene Grosbein case 'N': 712b5691eeSEugene Grosbein dryrun = true; 722b5691eeSEugene Grosbein verbose = true; 732b5691eeSEugene Grosbein break; 742b5691eeSEugene Grosbein case 'f': 752b5691eeSEugene Grosbein dryrun = false; 762b5691eeSEugene Grosbein break; 772b5691eeSEugene Grosbein case 'l': 782b5691eeSEugene Grosbein case 'o': 792b5691eeSEugene Grosbein if (expand_number(optarg, &usz) == -1 || 802b5691eeSEugene Grosbein (off_t)usz < 0 || (usz == 0 && ch == 'l')) 812b5691eeSEugene Grosbein errx(EX_USAGE, 822b5691eeSEugene Grosbein "invalid %s of the region: %s", 832b5691eeSEugene Grosbein ch == 'o' ? "offset" : "length", 842b5691eeSEugene Grosbein optarg); 852b5691eeSEugene Grosbein if (ch == 'o') 862b5691eeSEugene Grosbein offset = (off_t)usz; 872b5691eeSEugene Grosbein else 882b5691eeSEugene Grosbein length = (off_t)usz; 892b5691eeSEugene Grosbein break; 902b5691eeSEugene Grosbein case 'q': 912b5691eeSEugene Grosbein verbose = false; 922b5691eeSEugene Grosbein break; 932b5691eeSEugene Grosbein case 'r': 942b5691eeSEugene Grosbein if ((length = getsize(optarg)) == 0) 952b5691eeSEugene Grosbein errx(EX_USAGE, 962b5691eeSEugene Grosbein "invalid zero length reference file" 972b5691eeSEugene Grosbein " for the region: %s", optarg); 982b5691eeSEugene Grosbein break; 992b5691eeSEugene Grosbein case 'v': 1002b5691eeSEugene Grosbein verbose = true; 1012b5691eeSEugene Grosbein break; 1022b5691eeSEugene Grosbein default: 1032b5691eeSEugene Grosbein usage(name); 1042b5691eeSEugene Grosbein /* NOTREACHED */ 1052b5691eeSEugene Grosbein } 1062b5691eeSEugene Grosbein 1077f654915SEugene Grosbein /* 1087f654915SEugene Grosbein * Safety net: do not allow mistakes like 1097f654915SEugene Grosbein * 1107f654915SEugene Grosbein * trim -f /dev/da0 -r rfile 1117f654915SEugene Grosbein * 1127f654915SEugene Grosbein * This would trim whole device then error on non-existing file -r. 1137f654915SEugene Grosbein * Following check prevents this while allowing this form still: 1147f654915SEugene Grosbein * 1157f654915SEugene Grosbein * trim -f -- /dev/da0 -r rfile 1167f654915SEugene Grosbein */ 1177f654915SEugene Grosbein 1187f654915SEugene Grosbein if (strcmp(argv[optind-1], "--") != 0) { 1197f654915SEugene Grosbein for (ch = optind; ch < argc; ch++) 1207f654915SEugene Grosbein if (argv[ch][0] == '-') 1217f654915SEugene Grosbein usage(name); 1227f654915SEugene Grosbein } 1237f654915SEugene Grosbein 1242b5691eeSEugene Grosbein argv += optind; 1252b5691eeSEugene Grosbein argc -= optind; 1262b5691eeSEugene Grosbein 1272b5691eeSEugene Grosbein if (argc < 1) 1282b5691eeSEugene Grosbein usage(name); 1292b5691eeSEugene Grosbein 1302b5691eeSEugene Grosbein while ((fname = *argv++) != NULL) 1312b5691eeSEugene Grosbein if (trim(fname, offset, length, dryrun, verbose) < 0) 1322b5691eeSEugene Grosbein error++; 1332b5691eeSEugene Grosbein 1342b5691eeSEugene Grosbein return (error ? EXIT_FAILURE : EXIT_SUCCESS); 1352b5691eeSEugene Grosbein } 1362b5691eeSEugene Grosbein 137fa707ac7SEugene Grosbein static bool 138fa707ac7SEugene Grosbein candelete(int fd) 139fa707ac7SEugene Grosbein { 140fa707ac7SEugene Grosbein struct diocgattr_arg arg; 141fa707ac7SEugene Grosbein 142fa707ac7SEugene Grosbein strlcpy(arg.name, "GEOM::candelete", sizeof(arg.name)); 143fa707ac7SEugene Grosbein arg.len = sizeof(arg.value.i); 144fa707ac7SEugene Grosbein if (ioctl(fd, DIOCGATTR, &arg) == 0) 145fa707ac7SEugene Grosbein return (arg.value.i != 0); 146fa707ac7SEugene Grosbein else 147fa707ac7SEugene Grosbein return (false); 148fa707ac7SEugene Grosbein } 149fa707ac7SEugene Grosbein 1502b5691eeSEugene Grosbein static int 1512b5691eeSEugene Grosbein opendev(const char *path, int flags) 1522b5691eeSEugene Grosbein { 1532b5691eeSEugene Grosbein int fd; 1542b5691eeSEugene Grosbein char *tstr; 1552b5691eeSEugene Grosbein 1562b5691eeSEugene Grosbein if ((fd = open(path, flags)) < 0) { 1572b5691eeSEugene Grosbein if (errno == ENOENT && path[0] != '/') { 1582b5691eeSEugene Grosbein if (asprintf(&tstr, "%s%s", _PATH_DEV, path) < 0) 1592b5691eeSEugene Grosbein errx(EX_OSERR, "no memory"); 1602b5691eeSEugene Grosbein fd = open(tstr, flags); 1612b5691eeSEugene Grosbein free(tstr); 1622b5691eeSEugene Grosbein } 1632b5691eeSEugene Grosbein } 1642b5691eeSEugene Grosbein 1652b5691eeSEugene Grosbein if (fd < 0) 1662b5691eeSEugene Grosbein err(EX_NOINPUT, "open failed: %s", path); 1672b5691eeSEugene Grosbein 1682b5691eeSEugene Grosbein return (fd); 1692b5691eeSEugene Grosbein } 1702b5691eeSEugene Grosbein 1712b5691eeSEugene Grosbein static off_t 1722b5691eeSEugene Grosbein getsize(const char *path) 1732b5691eeSEugene Grosbein { 1742b5691eeSEugene Grosbein struct stat sb; 1752b5691eeSEugene Grosbein off_t mediasize; 1762b5691eeSEugene Grosbein int fd; 1772b5691eeSEugene Grosbein 1782b5691eeSEugene Grosbein fd = opendev(path, O_RDONLY | O_DIRECT); 1792b5691eeSEugene Grosbein 1802b5691eeSEugene Grosbein if (fstat(fd, &sb) < 0) 1812b5691eeSEugene Grosbein err(EX_IOERR, "fstat failed: %s", path); 1822b5691eeSEugene Grosbein 1832b5691eeSEugene Grosbein if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) { 1842b5691eeSEugene Grosbein close(fd); 1852b5691eeSEugene Grosbein return (sb.st_size); 1862b5691eeSEugene Grosbein } 1872b5691eeSEugene Grosbein 1882b5691eeSEugene Grosbein if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode)) 1892b5691eeSEugene Grosbein errx(EX_DATAERR, 1902b5691eeSEugene Grosbein "invalid type of the file " 1912b5691eeSEugene Grosbein "(not regular, directory nor special device): %s", 1922b5691eeSEugene Grosbein path); 1932b5691eeSEugene Grosbein 1942b5691eeSEugene Grosbein if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) 1952b5691eeSEugene Grosbein err(EX_UNAVAILABLE, 1962b5691eeSEugene Grosbein "ioctl(DIOCGMEDIASIZE) failed, probably not a disk: " 1972b5691eeSEugene Grosbein "%s", path); 1982b5691eeSEugene Grosbein 1992b5691eeSEugene Grosbein close(fd); 2002b5691eeSEugene Grosbein return (mediasize); 2012b5691eeSEugene Grosbein } 2022b5691eeSEugene Grosbein 2032b5691eeSEugene Grosbein static int 2042b5691eeSEugene Grosbein trim(const char *path, off_t offset, off_t length, bool dryrun, bool verbose) 2052b5691eeSEugene Grosbein { 2062b5691eeSEugene Grosbein off_t arg[2]; 2072b5691eeSEugene Grosbein int error, fd; 2082b5691eeSEugene Grosbein 2092b5691eeSEugene Grosbein if (length == 0) 2102b5691eeSEugene Grosbein length = getsize(path); 2112b5691eeSEugene Grosbein 2122b5691eeSEugene Grosbein if (verbose) 2132b5691eeSEugene Grosbein printf("trim %s offset %ju length %ju\n", 2142b5691eeSEugene Grosbein path, (uintmax_t)offset, (uintmax_t)length); 2152b5691eeSEugene Grosbein 2162b5691eeSEugene Grosbein if (dryrun) { 2172b5691eeSEugene Grosbein printf("dry run: add -f to actually perform the operation\n"); 2182b5691eeSEugene Grosbein return (0); 2192b5691eeSEugene Grosbein } 2202b5691eeSEugene Grosbein 2219d433cb8SAllan Jude fd = opendev(path, O_RDWR | O_DIRECT); 2222b5691eeSEugene Grosbein arg[0] = offset; 2232b5691eeSEugene Grosbein arg[1] = length; 2242b5691eeSEugene Grosbein 2252b5691eeSEugene Grosbein error = ioctl(fd, DIOCGDELETE, arg); 226fa707ac7SEugene Grosbein if (error < 0) { 227fa707ac7SEugene Grosbein if (errno == EOPNOTSUPP && verbose && !candelete(fd)) 228fa707ac7SEugene Grosbein warnx("%s: TRIM/UNMAP not supported by driver", path); 229fa707ac7SEugene Grosbein else 2302b5691eeSEugene Grosbein warn("ioctl(DIOCGDELETE) failed: %s", path); 231fa707ac7SEugene Grosbein } 2322b5691eeSEugene Grosbein close(fd); 2332b5691eeSEugene Grosbein return (error); 2342b5691eeSEugene Grosbein } 2352b5691eeSEugene Grosbein 2362b5691eeSEugene Grosbein static void 2372b5691eeSEugene Grosbein usage(const char *name) 2382b5691eeSEugene Grosbein { 2392b5691eeSEugene Grosbein (void)fprintf(stderr, 2402b5691eeSEugene Grosbein "usage: %s [-[lo] offset[K|k|M|m|G|g|T|t]] [-r rfile] [-Nfqv] device ...\n", 2412b5691eeSEugene Grosbein name); 2422b5691eeSEugene Grosbein exit(EX_USAGE); 2432b5691eeSEugene Grosbein } 244