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
main(int argc,char ** argv)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
candelete(int fd)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
opendev(const char * path,int flags)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
getsize(const char * path)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
trim(const char * path,off_t offset,off_t length,bool dryrun,bool verbose)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
usage(const char * name)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