trim.c revision 343118
1343118Seugen/*- 2343118Seugen * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3343118Seugen * 4343118Seugen * Copyright (c) 2019 Eugene Grosbein <eugen@FreeBSD.org>. 5343118Seugen * All rights reserved. 6343118Seugen * 7343118Seugen * Redistribution and use in source and binary forms, with or without 8343118Seugen * modification, are permitted provided that the following conditions 9343118Seugen * are met: 10343118Seugen * 1. Redistributions of source code must retain the above copyright 11343118Seugen * notice, this list of conditions and the following disclaimer. 12343118Seugen * 2. Redistributions in binary form must reproduce the above copyright 13343118Seugen * notice, this list of conditions and the following disclaimer in the 14343118Seugen * documentation and/or other materials provided with the distribution. 15343118Seugen * 16343118Seugen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17343118Seugen * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18343118Seugen * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19343118Seugen * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20343118Seugen * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21343118Seugen * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22343118Seugen * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23343118Seugen * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24343118Seugen * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25343118Seugen * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26343118Seugen * SUCH DAMAGE. 27343118Seugen * 28343118Seugen */ 29343118Seugen 30343118Seugen#include <sys/disk.h> 31343118Seugen#include <sys/ioctl.h> 32343118Seugen#include <sys/stat.h> 33343118Seugen 34343118Seugen#include <err.h> 35343118Seugen#include <errno.h> 36343118Seugen#include <fcntl.h> 37343118Seugen#include <libutil.h> 38343118Seugen#include <limits.h> 39343118Seugen#include <paths.h> 40343118Seugen#include <stdbool.h> 41343118Seugen#include <stdio.h> 42343118Seugen#include <stdlib.h> 43343118Seugen#include <sysexits.h> 44343118Seugen#include <unistd.h> 45343118Seugen 46343118Seugen#include <sys/cdefs.h> 47343118Seugen__FBSDID("$FreeBSD: head/usr.sbin/trim/trim.c 343118 2019-01-17 18:07:59Z eugen $"); 48343118Seugen 49343118Seugenstatic off_t getsize(const char *path); 50343118Seugenstatic int opendev(const char *path, int flags); 51343118Seugenstatic int trim(const char *path, off_t offset, off_t length, bool dryrun, bool verbose); 52343118Seugenstatic void usage(const char *name); 53343118Seugen 54343118Seugenint 55343118Seugenmain(int argc, char **argv) 56343118Seugen{ 57343118Seugen off_t offset, length; 58343118Seugen uint64_t usz; 59343118Seugen int ch, error; 60343118Seugen bool dryrun, verbose; 61343118Seugen char *fname, *name; 62343118Seugen 63343118Seugen error = 0; 64343118Seugen length = offset = 0; 65343118Seugen name = argv[0]; 66343118Seugen dryrun = verbose = true; 67343118Seugen 68343118Seugen while ((ch = getopt(argc, argv, "Nfl:o:qr:v")) != -1) 69343118Seugen switch (ch) { 70343118Seugen case 'N': 71343118Seugen dryrun = true; 72343118Seugen verbose = true; 73343118Seugen break; 74343118Seugen case 'f': 75343118Seugen dryrun = false; 76343118Seugen break; 77343118Seugen case 'l': 78343118Seugen case 'o': 79343118Seugen if (expand_number(optarg, &usz) == -1 || 80343118Seugen (off_t)usz < 0 || (usz == 0 && ch == 'l')) 81343118Seugen errx(EX_USAGE, 82343118Seugen "invalid %s of the region: %s", 83343118Seugen ch == 'o' ? "offset" : "length", 84343118Seugen optarg); 85343118Seugen if (ch == 'o') 86343118Seugen offset = (off_t)usz; 87343118Seugen else 88343118Seugen length = (off_t)usz; 89343118Seugen break; 90343118Seugen case 'q': 91343118Seugen verbose = false; 92343118Seugen break; 93343118Seugen case 'r': 94343118Seugen if ((length = getsize(optarg)) == 0) 95343118Seugen errx(EX_USAGE, 96343118Seugen "invalid zero length reference file" 97343118Seugen " for the region: %s", optarg); 98343118Seugen break; 99343118Seugen case 'v': 100343118Seugen verbose = true; 101343118Seugen break; 102343118Seugen default: 103343118Seugen usage(name); 104343118Seugen /* NOTREACHED */ 105343118Seugen } 106343118Seugen 107343118Seugen argv += optind; 108343118Seugen argc -= optind; 109343118Seugen 110343118Seugen if (argc < 1) 111343118Seugen usage(name); 112343118Seugen 113343118Seugen while ((fname = *argv++) != NULL) 114343118Seugen if (trim(fname, offset, length, dryrun, verbose) < 0) 115343118Seugen error++; 116343118Seugen 117343118Seugen return (error ? EXIT_FAILURE : EXIT_SUCCESS); 118343118Seugen} 119343118Seugen 120343118Seugenstatic int 121343118Seugenopendev(const char *path, int flags) 122343118Seugen{ 123343118Seugen int fd; 124343118Seugen char *tstr; 125343118Seugen 126343118Seugen if ((fd = open(path, flags)) < 0) { 127343118Seugen if (errno == ENOENT && path[0] != '/') { 128343118Seugen if (asprintf(&tstr, "%s%s", _PATH_DEV, path) < 0) 129343118Seugen errx(EX_OSERR, "no memory"); 130343118Seugen fd = open(tstr, flags); 131343118Seugen free(tstr); 132343118Seugen } 133343118Seugen } 134343118Seugen 135343118Seugen if (fd < 0) 136343118Seugen err(EX_NOINPUT, "open failed: %s", path); 137343118Seugen 138343118Seugen return (fd); 139343118Seugen} 140343118Seugen 141343118Seugenstatic off_t 142343118Seugengetsize(const char *path) 143343118Seugen{ 144343118Seugen struct stat sb; 145343118Seugen off_t mediasize; 146343118Seugen int fd; 147343118Seugen 148343118Seugen fd = opendev(path, O_RDONLY | O_DIRECT); 149343118Seugen 150343118Seugen if (fstat(fd, &sb) < 0) 151343118Seugen err(EX_IOERR, "fstat failed: %s", path); 152343118Seugen 153343118Seugen if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) { 154343118Seugen close(fd); 155343118Seugen return (sb.st_size); 156343118Seugen } 157343118Seugen 158343118Seugen if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode)) 159343118Seugen errx(EX_DATAERR, 160343118Seugen "invalid type of the file " 161343118Seugen "(not regular, directory nor special device): %s", 162343118Seugen path); 163343118Seugen 164343118Seugen if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) 165343118Seugen err(EX_UNAVAILABLE, 166343118Seugen "ioctl(DIOCGMEDIASIZE) failed, probably not a disk: " 167343118Seugen "%s", path); 168343118Seugen 169343118Seugen close(fd); 170343118Seugen return (mediasize); 171343118Seugen} 172343118Seugen 173343118Seugenstatic int 174343118Seugentrim(const char *path, off_t offset, off_t length, bool dryrun, bool verbose) 175343118Seugen{ 176343118Seugen off_t arg[2]; 177343118Seugen int error, fd; 178343118Seugen 179343118Seugen if (length == 0) 180343118Seugen length = getsize(path); 181343118Seugen 182343118Seugen if (verbose) 183343118Seugen printf("trim %s offset %ju length %ju\n", 184343118Seugen path, (uintmax_t)offset, (uintmax_t)length); 185343118Seugen 186343118Seugen if (dryrun) { 187343118Seugen printf("dry run: add -f to actually perform the operation\n"); 188343118Seugen return (0); 189343118Seugen } 190343118Seugen 191343118Seugen fd = opendev(path, O_WRONLY | O_DIRECT); 192343118Seugen arg[0] = offset; 193343118Seugen arg[1] = length; 194343118Seugen 195343118Seugen error = ioctl(fd, DIOCGDELETE, arg); 196343118Seugen if (error < 0) 197343118Seugen warn("ioctl(DIOCGDELETE) failed: %s", path); 198343118Seugen 199343118Seugen close(fd); 200343118Seugen return (error); 201343118Seugen} 202343118Seugen 203343118Seugenstatic void 204343118Seugenusage(const char *name) 205343118Seugen{ 206343118Seugen (void)fprintf(stderr, 207343118Seugen "usage: %s [-[lo] offset[K|k|M|m|G|g|T|t]] [-r rfile] [-Nfqv] device ...\n", 208343118Seugen name); 209343118Seugen exit(EX_USAGE); 210343118Seugen} 211