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