12b5691eeSEugene Grosbein /*- 22b5691eeSEugene Grosbein * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 32b5691eeSEugene Grosbein * 42b5691eeSEugene Grosbein * Copyright (c) 2019 Eugene Grosbein <eugen@FreeBSD.org>. 52b5691eeSEugene Grosbein * All rights reserved. 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> 43*7f654915SEugene 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 502b5691eeSEugene Grosbein static off_t getsize(const char *path); 512b5691eeSEugene Grosbein static int opendev(const char *path, int flags); 522b5691eeSEugene Grosbein static int trim(const char *path, off_t offset, off_t length, bool dryrun, bool verbose); 532b5691eeSEugene Grosbein static void usage(const char *name); 542b5691eeSEugene Grosbein 552b5691eeSEugene Grosbein int 562b5691eeSEugene Grosbein main(int argc, char **argv) 572b5691eeSEugene Grosbein { 582b5691eeSEugene Grosbein off_t offset, length; 592b5691eeSEugene Grosbein uint64_t usz; 602b5691eeSEugene Grosbein int ch, error; 612b5691eeSEugene Grosbein bool dryrun, verbose; 622b5691eeSEugene Grosbein char *fname, *name; 632b5691eeSEugene Grosbein 642b5691eeSEugene Grosbein error = 0; 652b5691eeSEugene Grosbein length = offset = 0; 662b5691eeSEugene Grosbein name = argv[0]; 672b5691eeSEugene Grosbein dryrun = verbose = true; 682b5691eeSEugene Grosbein 692b5691eeSEugene Grosbein while ((ch = getopt(argc, argv, "Nfl:o:qr:v")) != -1) 702b5691eeSEugene Grosbein switch (ch) { 712b5691eeSEugene Grosbein case 'N': 722b5691eeSEugene Grosbein dryrun = true; 732b5691eeSEugene Grosbein verbose = true; 742b5691eeSEugene Grosbein break; 752b5691eeSEugene Grosbein case 'f': 762b5691eeSEugene Grosbein dryrun = false; 772b5691eeSEugene Grosbein break; 782b5691eeSEugene Grosbein case 'l': 792b5691eeSEugene Grosbein case 'o': 802b5691eeSEugene Grosbein if (expand_number(optarg, &usz) == -1 || 812b5691eeSEugene Grosbein (off_t)usz < 0 || (usz == 0 && ch == 'l')) 822b5691eeSEugene Grosbein errx(EX_USAGE, 832b5691eeSEugene Grosbein "invalid %s of the region: %s", 842b5691eeSEugene Grosbein ch == 'o' ? "offset" : "length", 852b5691eeSEugene Grosbein optarg); 862b5691eeSEugene Grosbein if (ch == 'o') 872b5691eeSEugene Grosbein offset = (off_t)usz; 882b5691eeSEugene Grosbein else 892b5691eeSEugene Grosbein length = (off_t)usz; 902b5691eeSEugene Grosbein break; 912b5691eeSEugene Grosbein case 'q': 922b5691eeSEugene Grosbein verbose = false; 932b5691eeSEugene Grosbein break; 942b5691eeSEugene Grosbein case 'r': 952b5691eeSEugene Grosbein if ((length = getsize(optarg)) == 0) 962b5691eeSEugene Grosbein errx(EX_USAGE, 972b5691eeSEugene Grosbein "invalid zero length reference file" 982b5691eeSEugene Grosbein " for the region: %s", optarg); 992b5691eeSEugene Grosbein break; 1002b5691eeSEugene Grosbein case 'v': 1012b5691eeSEugene Grosbein verbose = true; 1022b5691eeSEugene Grosbein break; 1032b5691eeSEugene Grosbein default: 1042b5691eeSEugene Grosbein usage(name); 1052b5691eeSEugene Grosbein /* NOTREACHED */ 1062b5691eeSEugene Grosbein } 1072b5691eeSEugene Grosbein 108*7f654915SEugene Grosbein /* 109*7f654915SEugene Grosbein * Safety net: do not allow mistakes like 110*7f654915SEugene Grosbein * 111*7f654915SEugene Grosbein * trim -f /dev/da0 -r rfile 112*7f654915SEugene Grosbein * 113*7f654915SEugene Grosbein * This would trim whole device then error on non-existing file -r. 114*7f654915SEugene Grosbein * Following check prevents this while allowing this form still: 115*7f654915SEugene Grosbein * 116*7f654915SEugene Grosbein * trim -f -- /dev/da0 -r rfile 117*7f654915SEugene Grosbein */ 118*7f654915SEugene Grosbein 119*7f654915SEugene Grosbein if (strcmp(argv[optind-1], "--") != 0) { 120*7f654915SEugene Grosbein for (ch = optind; ch < argc; ch++) 121*7f654915SEugene Grosbein if (argv[ch][0] == '-') 122*7f654915SEugene Grosbein usage(name); 123*7f654915SEugene Grosbein } 124*7f654915SEugene Grosbein 1252b5691eeSEugene Grosbein argv += optind; 1262b5691eeSEugene Grosbein argc -= optind; 1272b5691eeSEugene Grosbein 1282b5691eeSEugene Grosbein if (argc < 1) 1292b5691eeSEugene Grosbein usage(name); 1302b5691eeSEugene Grosbein 1312b5691eeSEugene Grosbein while ((fname = *argv++) != NULL) 1322b5691eeSEugene Grosbein if (trim(fname, offset, length, dryrun, verbose) < 0) 1332b5691eeSEugene Grosbein error++; 1342b5691eeSEugene Grosbein 1352b5691eeSEugene Grosbein return (error ? EXIT_FAILURE : EXIT_SUCCESS); 1362b5691eeSEugene Grosbein } 1372b5691eeSEugene Grosbein 1382b5691eeSEugene Grosbein static int 1392b5691eeSEugene Grosbein opendev(const char *path, int flags) 1402b5691eeSEugene Grosbein { 1412b5691eeSEugene Grosbein int fd; 1422b5691eeSEugene Grosbein char *tstr; 1432b5691eeSEugene Grosbein 1442b5691eeSEugene Grosbein if ((fd = open(path, flags)) < 0) { 1452b5691eeSEugene Grosbein if (errno == ENOENT && path[0] != '/') { 1462b5691eeSEugene Grosbein if (asprintf(&tstr, "%s%s", _PATH_DEV, path) < 0) 1472b5691eeSEugene Grosbein errx(EX_OSERR, "no memory"); 1482b5691eeSEugene Grosbein fd = open(tstr, flags); 1492b5691eeSEugene Grosbein free(tstr); 1502b5691eeSEugene Grosbein } 1512b5691eeSEugene Grosbein } 1522b5691eeSEugene Grosbein 1532b5691eeSEugene Grosbein if (fd < 0) 1542b5691eeSEugene Grosbein err(EX_NOINPUT, "open failed: %s", path); 1552b5691eeSEugene Grosbein 1562b5691eeSEugene Grosbein return (fd); 1572b5691eeSEugene Grosbein } 1582b5691eeSEugene Grosbein 1592b5691eeSEugene Grosbein static off_t 1602b5691eeSEugene Grosbein getsize(const char *path) 1612b5691eeSEugene Grosbein { 1622b5691eeSEugene Grosbein struct stat sb; 1632b5691eeSEugene Grosbein off_t mediasize; 1642b5691eeSEugene Grosbein int fd; 1652b5691eeSEugene Grosbein 1662b5691eeSEugene Grosbein fd = opendev(path, O_RDONLY | O_DIRECT); 1672b5691eeSEugene Grosbein 1682b5691eeSEugene Grosbein if (fstat(fd, &sb) < 0) 1692b5691eeSEugene Grosbein err(EX_IOERR, "fstat failed: %s", path); 1702b5691eeSEugene Grosbein 1712b5691eeSEugene Grosbein if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) { 1722b5691eeSEugene Grosbein close(fd); 1732b5691eeSEugene Grosbein return (sb.st_size); 1742b5691eeSEugene Grosbein } 1752b5691eeSEugene Grosbein 1762b5691eeSEugene Grosbein if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode)) 1772b5691eeSEugene Grosbein errx(EX_DATAERR, 1782b5691eeSEugene Grosbein "invalid type of the file " 1792b5691eeSEugene Grosbein "(not regular, directory nor special device): %s", 1802b5691eeSEugene Grosbein path); 1812b5691eeSEugene Grosbein 1822b5691eeSEugene Grosbein if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) 1832b5691eeSEugene Grosbein err(EX_UNAVAILABLE, 1842b5691eeSEugene Grosbein "ioctl(DIOCGMEDIASIZE) failed, probably not a disk: " 1852b5691eeSEugene Grosbein "%s", path); 1862b5691eeSEugene Grosbein 1872b5691eeSEugene Grosbein close(fd); 1882b5691eeSEugene Grosbein return (mediasize); 1892b5691eeSEugene Grosbein } 1902b5691eeSEugene Grosbein 1912b5691eeSEugene Grosbein static int 1922b5691eeSEugene Grosbein trim(const char *path, off_t offset, off_t length, bool dryrun, bool verbose) 1932b5691eeSEugene Grosbein { 1942b5691eeSEugene Grosbein off_t arg[2]; 1952b5691eeSEugene Grosbein int error, fd; 1962b5691eeSEugene Grosbein 1972b5691eeSEugene Grosbein if (length == 0) 1982b5691eeSEugene Grosbein length = getsize(path); 1992b5691eeSEugene Grosbein 2002b5691eeSEugene Grosbein if (verbose) 2012b5691eeSEugene Grosbein printf("trim %s offset %ju length %ju\n", 2022b5691eeSEugene Grosbein path, (uintmax_t)offset, (uintmax_t)length); 2032b5691eeSEugene Grosbein 2042b5691eeSEugene Grosbein if (dryrun) { 2052b5691eeSEugene Grosbein printf("dry run: add -f to actually perform the operation\n"); 2062b5691eeSEugene Grosbein return (0); 2072b5691eeSEugene Grosbein } 2082b5691eeSEugene Grosbein 2092b5691eeSEugene Grosbein fd = opendev(path, O_WRONLY | O_DIRECT); 2102b5691eeSEugene Grosbein arg[0] = offset; 2112b5691eeSEugene Grosbein arg[1] = length; 2122b5691eeSEugene Grosbein 2132b5691eeSEugene Grosbein error = ioctl(fd, DIOCGDELETE, arg); 2142b5691eeSEugene Grosbein if (error < 0) 2152b5691eeSEugene Grosbein warn("ioctl(DIOCGDELETE) failed: %s", path); 2162b5691eeSEugene Grosbein 2172b5691eeSEugene Grosbein close(fd); 2182b5691eeSEugene Grosbein return (error); 2192b5691eeSEugene Grosbein } 2202b5691eeSEugene Grosbein 2212b5691eeSEugene Grosbein static void 2222b5691eeSEugene Grosbein usage(const char *name) 2232b5691eeSEugene Grosbein { 2242b5691eeSEugene Grosbein (void)fprintf(stderr, 2252b5691eeSEugene Grosbein "usage: %s [-[lo] offset[K|k|M|m|G|g|T|t]] [-r rfile] [-Nfqv] device ...\n", 2262b5691eeSEugene Grosbein name); 2272b5691eeSEugene Grosbein exit(EX_USAGE); 2282b5691eeSEugene Grosbein } 229