diff options
author | Eugene Grosbein <eugen@FreeBSD.org> | 2018-11-29 16:08:16 +0000 |
---|---|---|
committer | Eugene Grosbein <eugen@FreeBSD.org> | 2018-11-29 16:08:16 +0000 |
commit | fcd7ccb28cc7edcaf834a2a0035a2dca4c973e27 (patch) | |
tree | 6db5111a777d2f27726f12de31d54b672ac5d6a6 /usr.sbin/trim | |
parent | 98af779d05a5f48e22da95954e550d78a317136e (diff) | |
download | src-fcd7ccb28cc7edcaf834a2a0035a2dca4c973e27.tar.gz src-fcd7ccb28cc7edcaf834a2a0035a2dca4c973e27.zip |
Notes
Diffstat (limited to 'usr.sbin/trim')
-rw-r--r-- | usr.sbin/trim/Makefile | 6 | ||||
-rw-r--r-- | usr.sbin/trim/trim.1 | 166 | ||||
-rw-r--r-- | usr.sbin/trim/trim.c | 212 |
3 files changed, 384 insertions, 0 deletions
diff --git a/usr.sbin/trim/Makefile b/usr.sbin/trim/Makefile new file mode 100644 index 000000000000..ad18cf051c34 --- /dev/null +++ b/usr.sbin/trim/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +PROG= trim +LIBADD= util + +.include <bsd.prog.mk> diff --git a/usr.sbin/trim/trim.1 b/usr.sbin/trim/trim.1 new file mode 100644 index 000000000000..eac5e7dd7e46 --- /dev/null +++ b/usr.sbin/trim/trim.1 @@ -0,0 +1,166 @@ +.\" +.\" Copyright (c) 2018 Eugene Grosbein <eugen@FreeBSD.org>. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd November 29, 2018 +.Dt TRIM 1 +.Os +.Sh NAME +.Nm trim +.Nd erase device blocks that have no needed contents +.Sh SYNOPSIS +.Nm +.Op Fl Nfqv +.Fl [ [lo] Xo +.Bk -words +.Sm off +.Ar offset +.Op Cm K | k | M | m | G | g | T | t ] +.Sm on +.Xc +.Ek +.Bk -words +.Op Fl r Ar rfile +.Ek +.Ar device ... +.Sh DESCRIPTION +The +.Nm +utility erases specified region of the device. +It is only relevant for flash based storage devices that use wear-leveling +algorithms. +.Sy All erased data is lost. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl N +Do not actually erase anything but show what it would do (dry run). +Implies +.Fl v . +This is the default. Overrides +.Fl f . +.It Fl f +Perform the operation. Overrides +.Fl N . +.It Fl l Xo +.Sm off +.Ar offset +.Op Cm K | k | M | m | G | g | T | t +.Sm on +.Xc +.It Fl o Xo +.Sm off +.Ar offset +.Op Cm K | k | M | m | G | g | T | t +.Sm on +.Xc +Specify the length +.Fl l +of the region to trim or its offset +.Fl o +from the beginning of the device. +.Sy The whole device is erased by default +unless one or both of these options are presented. +.Pp +The argument may be suffixed with one of +.Cm K , +.Cm M , +.Cm G +or +.Cm T +(either upper or lower case) to indicate a multiple of +Kilobytes, Megabytes, Gigabytes or Terabytes +respectively. +.It Fl q +Do not output anything except of possible error messages (quiet mode). +Overrides +.Fl v . +.It Fl r Ar rfile +Uses the length of given +.Ar rfile +as length of the region to erase. +.Sy The whole device is erased by default. +.It Fl v +Show offset and length of actual region being erased, in bytes. +.El +.Pp +Later options override previous ones. +.Pp +Note that actual success of the operation depends of underlying +device driver such as +.Xr ada 4 , +.Xr da 4 +and others. +Refer to corresponding manual pages for detail on possible caveats +in low level support for ATA TRIM or SCSI UNMAP commands. +.Sh EXIT STATUS +.Ex -std +If the final erase operation fails for an argument, the +.Nm +utility returns exit code 1. +It can also return one of the exit codes defined in +.Xr sysexits 3 , +as follows: +.Bl -tag -width ".Dv EX_UNAVAILABLE" +.It Dv EX_USAGE +The specified offset or length of the region is incorrect. +.It Dv EX_OSERR +There is no enough memory to proceed. +.It Dv EX_NOINPUT +The specified +.Ar rfile +cannot be opened (perhaps, it does not exist). +.It Dv EX_IOERR +The specified +.Ar rfile +cannot be examined for its size due to some system input/output error. +.It Dv EX_DATAERR +The specified +.Ar rfile +is not regular file, directory nor special device, so its size +cannot be examined. +.It Dv EX_UNAVAILABLE +The specified +.Ar rfile +is special device file not supporting DIOCGMEDIASIZE +.Xr ioctl 2 +(probably not a disk), so its size cannot be examined. +.El +.Sh SEE ALSO +.Xr ada 4 , +.Xr da 4 , +.Xr ioctl 2 , +.Xr sysexits 3 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 12.0 . +.Sh AUTHORS +The +.Nm +utility was written by +.An Eugene Grosbein Aq Mt eugen@FreeBSD.org . diff --git a/usr.sbin/trim/trim.c b/usr.sbin/trim/trim.c new file mode 100644 index 000000000000..7f9810affaea --- /dev/null +++ b/usr.sbin/trim/trim.c @@ -0,0 +1,212 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Eugene Grosbein <eugen@FreeBSD.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/disk.h> +#include <sys/ioctl.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <libutil.h> +#include <limits.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <sysexits.h> +#include <unistd.h> + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +static int trim(char *path, off_t offset, off_t length, int dryrun, int verbose); +static off_t getsize(char *path); +static void usage(char *name) __dead2; + +int +main(int argc, char **argv) +{ + off_t offset, length; + uint64_t usz; + int ch, dryrun, error, verbose; + char *fname, *name; + + error = 0; + length = offset = 0; + name = argv[0]; + dryrun = verbose = 1; + + while ((ch = getopt(argc, argv, "Nfl:o:qr:v")) != -1) + switch (ch) { + case 'N': + dryrun = 1; + verbose = 1; + break; + case 'f': + dryrun = 0; + break; + case 'l': + case 'o': + if (expand_number(optarg, &usz) == -1 || + (off_t)usz < 0 || + (usz == 0 && ch == 'l')) + errx(EX_USAGE, + "invalid %s of the region: `%s'", + ch == 'o' ? "offset" : "length", + optarg); + if (ch == 'o') + offset = (off_t)usz; + else + length = (off_t)usz; + break; + case 'q': + verbose = 0; + break; + case 'r': + if ((length = getsize(optarg)) == 0) + errx(EX_USAGE, + "invalid zero length reference file" + " for the region: `%s'", optarg); + break; + case 'v': + verbose = 1; + break; + default: + usage(name); + /* NOTREACHED */ + } + + argv += optind; + argc -= optind; + + if (argc < 1) + usage(name); + + while ((fname = *argv++) != NULL) + if (trim(fname, offset, length, dryrun, verbose) < 0) + error++; + + return (error ? EXIT_FAILURE : EXIT_SUCCESS); +} + +static off_t +getsize(char *path) +{ + struct stat sb; + char *tstr; + off_t mediasize; + int fd; + + if ((fd = open(path, O_RDONLY | O_DIRECT)) < 0) { + if (errno == ENOENT && path[0] != '/') { + if (asprintf(&tstr, "%s%s", _PATH_DEV, path) < 0) + errx(EX_OSERR, "no memory"); + fd = open(tstr, O_RDONLY | O_DIRECT); + free(tstr); + } + } + + if (fd < 0) + err(EX_NOINPUT, "`%s'", path); + + if (fstat(fd, &sb) < 0) + err(EX_IOERR, "`%s'", path); + + if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) { + close(fd); + return (sb.st_size); + } + + if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode)) + errx(EX_DATAERR, + "invalid type of the file " + "(not regular, directory nor special device): `%s'", + path); + + if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) + errx(EX_UNAVAILABLE, + "ioctl(DIOCGMEDIASIZE) failed, probably not a disk: " + "`%s'", path); + close(fd); + + return (mediasize); +} + +static int +trim(char *path, off_t offset, off_t length, int dryrun, int verbose) +{ + off_t arg[2]; + char *tstr; + int error, fd; + + if (length == 0) + length = getsize(path); + + if (verbose) + printf("trim `%s' offset %ju length %ju\n", + path, (uintmax_t)offset, (uintmax_t)length); + + if (dryrun) { + printf("dry run: add -f to actually perform the operation\n"); + return (0); + } + + if ((fd = open(path, O_WRONLY | O_DIRECT)) < 0) { + if (errno == ENOENT && path[0] != '/') { + if (asprintf(&tstr, "%s%s", _PATH_DEV, path) < 0) + errx(EX_OSERR, "no memory"); + fd = open(tstr, O_WRONLY | O_DIRECT); + free(tstr); + } + } + + if (fd < 0) + err(EX_NOINPUT, "`%s'", path); + + arg[0] = offset; + arg[1] = length; + + error = ioctl(fd, DIOCGDELETE, arg); + if (error < 0) + warn("ioctl(DIOCGDELETE) failed for `%s'", path); + + close(fd); + return (error); +} + +static void +usage(char *name) +{ + (void)fprintf(stderr, + "usage: %s [-[lo] offset[K|k|M|m|G|g|T|t]] [-r rfile] [-Nfqv] device ...\n", + name); + exit(EX_USAGE); +} |