12b5691eeSEugene Grosbein /*- 22b5691eeSEugene Grosbein * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 32b5691eeSEugene Grosbein * 42b5691eeSEugene Grosbein * Copyright (c) 2019 Eugene Grosbein <eugen@FreeBSD.org>. 5*fa707ac7SEugene 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> 482b5691eeSEugene Grosbein __FBSDID("$FreeBSD$"); 492b5691eeSEugene Grosbein 50*fa707ac7SEugene Grosbein static bool candelete(int fd); 512b5691eeSEugene Grosbein static off_t getsize(const char *path); 522b5691eeSEugene Grosbein static int opendev(const char *path, int flags); 532b5691eeSEugene Grosbein static int trim(const char *path, off_t offset, off_t length, bool dryrun, bool verbose); 542b5691eeSEugene Grosbein static void usage(const char *name); 552b5691eeSEugene Grosbein 562b5691eeSEugene Grosbein int 572b5691eeSEugene Grosbein main(int argc, char **argv) 582b5691eeSEugene Grosbein { 592b5691eeSEugene Grosbein off_t offset, length; 602b5691eeSEugene Grosbein uint64_t usz; 612b5691eeSEugene Grosbein int ch, error; 622b5691eeSEugene Grosbein bool dryrun, verbose; 632b5691eeSEugene Grosbein char *fname, *name; 642b5691eeSEugene Grosbein 652b5691eeSEugene Grosbein error = 0; 662b5691eeSEugene Grosbein length = offset = 0; 672b5691eeSEugene Grosbein name = argv[0]; 682b5691eeSEugene Grosbein dryrun = verbose = true; 692b5691eeSEugene Grosbein 702b5691eeSEugene Grosbein while ((ch = getopt(argc, argv, "Nfl:o:qr:v")) != -1) 712b5691eeSEugene Grosbein switch (ch) { 722b5691eeSEugene Grosbein case 'N': 732b5691eeSEugene Grosbein dryrun = true; 742b5691eeSEugene Grosbein verbose = true; 752b5691eeSEugene Grosbein break; 762b5691eeSEugene Grosbein case 'f': 772b5691eeSEugene Grosbein dryrun = false; 782b5691eeSEugene Grosbein break; 792b5691eeSEugene Grosbein case 'l': 802b5691eeSEugene Grosbein case 'o': 812b5691eeSEugene Grosbein if (expand_number(optarg, &usz) == -1 || 822b5691eeSEugene Grosbein (off_t)usz < 0 || (usz == 0 && ch == 'l')) 832b5691eeSEugene Grosbein errx(EX_USAGE, 842b5691eeSEugene Grosbein "invalid %s of the region: %s", 852b5691eeSEugene Grosbein ch == 'o' ? "offset" : "length", 862b5691eeSEugene Grosbein optarg); 872b5691eeSEugene Grosbein if (ch == 'o') 882b5691eeSEugene Grosbein offset = (off_t)usz; 892b5691eeSEugene Grosbein else 902b5691eeSEugene Grosbein length = (off_t)usz; 912b5691eeSEugene Grosbein break; 922b5691eeSEugene Grosbein case 'q': 932b5691eeSEugene Grosbein verbose = false; 942b5691eeSEugene Grosbein break; 952b5691eeSEugene Grosbein case 'r': 962b5691eeSEugene Grosbein if ((length = getsize(optarg)) == 0) 972b5691eeSEugene Grosbein errx(EX_USAGE, 982b5691eeSEugene Grosbein "invalid zero length reference file" 992b5691eeSEugene Grosbein " for the region: %s", optarg); 1002b5691eeSEugene Grosbein break; 1012b5691eeSEugene Grosbein case 'v': 1022b5691eeSEugene Grosbein verbose = true; 1032b5691eeSEugene Grosbein break; 1042b5691eeSEugene Grosbein default: 1052b5691eeSEugene Grosbein usage(name); 1062b5691eeSEugene Grosbein /* NOTREACHED */ 1072b5691eeSEugene Grosbein } 1082b5691eeSEugene Grosbein 1097f654915SEugene Grosbein /* 1107f654915SEugene Grosbein * Safety net: do not allow mistakes like 1117f654915SEugene Grosbein * 1127f654915SEugene Grosbein * trim -f /dev/da0 -r rfile 1137f654915SEugene Grosbein * 1147f654915SEugene Grosbein * This would trim whole device then error on non-existing file -r. 1157f654915SEugene Grosbein * Following check prevents this while allowing this form still: 1167f654915SEugene Grosbein * 1177f654915SEugene Grosbein * trim -f -- /dev/da0 -r rfile 1187f654915SEugene Grosbein */ 1197f654915SEugene Grosbein 1207f654915SEugene Grosbein if (strcmp(argv[optind-1], "--") != 0) { 1217f654915SEugene Grosbein for (ch = optind; ch < argc; ch++) 1227f654915SEugene Grosbein if (argv[ch][0] == '-') 1237f654915SEugene Grosbein usage(name); 1247f654915SEugene Grosbein } 1257f654915SEugene Grosbein 1262b5691eeSEugene Grosbein argv += optind; 1272b5691eeSEugene Grosbein argc -= optind; 1282b5691eeSEugene Grosbein 1292b5691eeSEugene Grosbein if (argc < 1) 1302b5691eeSEugene Grosbein usage(name); 1312b5691eeSEugene Grosbein 1322b5691eeSEugene Grosbein while ((fname = *argv++) != NULL) 1332b5691eeSEugene Grosbein if (trim(fname, offset, length, dryrun, verbose) < 0) 1342b5691eeSEugene Grosbein error++; 1352b5691eeSEugene Grosbein 1362b5691eeSEugene Grosbein return (error ? EXIT_FAILURE : EXIT_SUCCESS); 1372b5691eeSEugene Grosbein } 1382b5691eeSEugene Grosbein 139*fa707ac7SEugene Grosbein static bool 140*fa707ac7SEugene Grosbein candelete(int fd) 141*fa707ac7SEugene Grosbein { 142*fa707ac7SEugene Grosbein struct diocgattr_arg arg; 143*fa707ac7SEugene Grosbein 144*fa707ac7SEugene Grosbein strlcpy(arg.name, "GEOM::candelete", sizeof(arg.name)); 145*fa707ac7SEugene Grosbein arg.len = sizeof(arg.value.i); 146*fa707ac7SEugene Grosbein if (ioctl(fd, DIOCGATTR, &arg) == 0) 147*fa707ac7SEugene Grosbein return (arg.value.i != 0); 148*fa707ac7SEugene Grosbein else 149*fa707ac7SEugene Grosbein return (false); 150*fa707ac7SEugene Grosbein } 151*fa707ac7SEugene Grosbein 1522b5691eeSEugene Grosbein static int 1532b5691eeSEugene Grosbein opendev(const char *path, int flags) 1542b5691eeSEugene Grosbein { 1552b5691eeSEugene Grosbein int fd; 1562b5691eeSEugene Grosbein char *tstr; 1572b5691eeSEugene Grosbein 1582b5691eeSEugene Grosbein if ((fd = open(path, flags)) < 0) { 1592b5691eeSEugene Grosbein if (errno == ENOENT && path[0] != '/') { 1602b5691eeSEugene Grosbein if (asprintf(&tstr, "%s%s", _PATH_DEV, path) < 0) 1612b5691eeSEugene Grosbein errx(EX_OSERR, "no memory"); 1622b5691eeSEugene Grosbein fd = open(tstr, flags); 1632b5691eeSEugene Grosbein free(tstr); 1642b5691eeSEugene Grosbein } 1652b5691eeSEugene Grosbein } 1662b5691eeSEugene Grosbein 1672b5691eeSEugene Grosbein if (fd < 0) 1682b5691eeSEugene Grosbein err(EX_NOINPUT, "open failed: %s", path); 1692b5691eeSEugene Grosbein 1702b5691eeSEugene Grosbein return (fd); 1712b5691eeSEugene Grosbein } 1722b5691eeSEugene Grosbein 1732b5691eeSEugene Grosbein static off_t 1742b5691eeSEugene Grosbein getsize(const char *path) 1752b5691eeSEugene Grosbein { 1762b5691eeSEugene Grosbein struct stat sb; 1772b5691eeSEugene Grosbein off_t mediasize; 1782b5691eeSEugene Grosbein int fd; 1792b5691eeSEugene Grosbein 1802b5691eeSEugene Grosbein fd = opendev(path, O_RDONLY | O_DIRECT); 1812b5691eeSEugene Grosbein 1822b5691eeSEugene Grosbein if (fstat(fd, &sb) < 0) 1832b5691eeSEugene Grosbein err(EX_IOERR, "fstat failed: %s", path); 1842b5691eeSEugene Grosbein 1852b5691eeSEugene Grosbein if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) { 1862b5691eeSEugene Grosbein close(fd); 1872b5691eeSEugene Grosbein return (sb.st_size); 1882b5691eeSEugene Grosbein } 1892b5691eeSEugene Grosbein 1902b5691eeSEugene Grosbein if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode)) 1912b5691eeSEugene Grosbein errx(EX_DATAERR, 1922b5691eeSEugene Grosbein "invalid type of the file " 1932b5691eeSEugene Grosbein "(not regular, directory nor special device): %s", 1942b5691eeSEugene Grosbein path); 1952b5691eeSEugene Grosbein 1962b5691eeSEugene Grosbein if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) 1972b5691eeSEugene Grosbein err(EX_UNAVAILABLE, 1982b5691eeSEugene Grosbein "ioctl(DIOCGMEDIASIZE) failed, probably not a disk: " 1992b5691eeSEugene Grosbein "%s", path); 2002b5691eeSEugene Grosbein 2012b5691eeSEugene Grosbein close(fd); 2022b5691eeSEugene Grosbein return (mediasize); 2032b5691eeSEugene Grosbein } 2042b5691eeSEugene Grosbein 2052b5691eeSEugene Grosbein static int 2062b5691eeSEugene Grosbein trim(const char *path, off_t offset, off_t length, bool dryrun, bool verbose) 2072b5691eeSEugene Grosbein { 2082b5691eeSEugene Grosbein off_t arg[2]; 2092b5691eeSEugene Grosbein int error, fd; 2102b5691eeSEugene Grosbein 2112b5691eeSEugene Grosbein if (length == 0) 2122b5691eeSEugene Grosbein length = getsize(path); 2132b5691eeSEugene Grosbein 2142b5691eeSEugene Grosbein if (verbose) 2152b5691eeSEugene Grosbein printf("trim %s offset %ju length %ju\n", 2162b5691eeSEugene Grosbein path, (uintmax_t)offset, (uintmax_t)length); 2172b5691eeSEugene Grosbein 2182b5691eeSEugene Grosbein if (dryrun) { 2192b5691eeSEugene Grosbein printf("dry run: add -f to actually perform the operation\n"); 2202b5691eeSEugene Grosbein return (0); 2212b5691eeSEugene Grosbein } 2222b5691eeSEugene Grosbein 2232b5691eeSEugene Grosbein fd = opendev(path, O_WRONLY | O_DIRECT); 2242b5691eeSEugene Grosbein arg[0] = offset; 2252b5691eeSEugene Grosbein arg[1] = length; 2262b5691eeSEugene Grosbein 2272b5691eeSEugene Grosbein error = ioctl(fd, DIOCGDELETE, arg); 228*fa707ac7SEugene Grosbein if (error < 0) { 229*fa707ac7SEugene Grosbein if (errno == EOPNOTSUPP && verbose && !candelete(fd)) 230*fa707ac7SEugene Grosbein warnx("%s: TRIM/UNMAP not supported by driver", path); 231*fa707ac7SEugene Grosbein else 2322b5691eeSEugene Grosbein warn("ioctl(DIOCGDELETE) failed: %s", path); 233*fa707ac7SEugene Grosbein } 2342b5691eeSEugene Grosbein close(fd); 2352b5691eeSEugene Grosbein return (error); 2362b5691eeSEugene Grosbein } 2372b5691eeSEugene Grosbein 2382b5691eeSEugene Grosbein static void 2392b5691eeSEugene Grosbein usage(const char *name) 2402b5691eeSEugene Grosbein { 2412b5691eeSEugene Grosbein (void)fprintf(stderr, 2422b5691eeSEugene Grosbein "usage: %s [-[lo] offset[K|k|M|m|G|g|T|t]] [-r rfile] [-Nfqv] device ...\n", 2432b5691eeSEugene Grosbein name); 2442b5691eeSEugene Grosbein exit(EX_USAGE); 2452b5691eeSEugene Grosbein } 246