Home | History | Annotate | Line # | Download | only in blkdiscard
      1  1.4  andvar /*	$NetBSD: blkdiscard.c,v 1.4 2024/02/08 20:51:24 andvar Exp $	*/
      2  1.1     mrg 
      3  1.1     mrg /*
      4  1.3     mrg  * Copyright (c) 2019, 2020, 2022, 2024 Matthew R. Green
      5  1.1     mrg  * All rights reserved.
      6  1.1     mrg  *
      7  1.1     mrg  * Redistribution and use in source and binary forms, with or without
      8  1.1     mrg  * modification, are permitted provided that the following conditions
      9  1.1     mrg  * are met:
     10  1.1     mrg  * 1. Redistributions of source code must retain the above copyright
     11  1.1     mrg  *    notice, this list of conditions and the following disclaimer.
     12  1.1     mrg  * 2. Redistributions in binary form must reproduce the above copyright
     13  1.1     mrg  *    notice, this list of conditions and the following disclaimer in the
     14  1.1     mrg  *    documentation and/or other materials provided with the distribution.
     15  1.1     mrg  * 3. The name of the author may not be used to endorse or promote products
     16  1.1     mrg  *    derived from this software without specific prior written permission.
     17  1.1     mrg  *
     18  1.1     mrg  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     19  1.1     mrg  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     20  1.1     mrg  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     21  1.1     mrg  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     22  1.1     mrg  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     23  1.1     mrg  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     24  1.1     mrg  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     25  1.1     mrg  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     26  1.1     mrg  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     27  1.1     mrg  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     28  1.1     mrg  * SUCH DAMAGE.
     29  1.1     mrg  */
     30  1.1     mrg 
     31  1.1     mrg /* from: $eterna: fdiscard-stuff.c,v 1.3 2020/12/25 23:40:19 mrg Exp $	*/
     32  1.1     mrg 
     33  1.1     mrg /* fdiscard(2) front-end -- TRIM support. */
     34  1.1     mrg 
     35  1.1     mrg #include <sys/param.h>
     36  1.1     mrg #include <sys/stat.h>
     37  1.1     mrg #include <sys/disk.h>
     38  1.1     mrg #include <sys/disklabel.h>
     39  1.1     mrg #include <sys/ioctl.h>
     40  1.1     mrg 
     41  1.1     mrg #include <fcntl.h>
     42  1.1     mrg #include <stdio.h>
     43  1.1     mrg #include <stdlib.h>
     44  1.1     mrg #include <string.h>
     45  1.1     mrg #include <unistd.h>
     46  1.1     mrg #include <err.h>
     47  1.1     mrg #include <errno.h>
     48  1.1     mrg #include <stdbool.h>
     49  1.1     mrg 
     50  1.1     mrg static bool secure = false;
     51  1.1     mrg static bool zeroout = false;
     52  1.1     mrg static char *zeros = NULL;
     53  1.2     mrg static unsigned ttywidth = 80;
     54  1.1     mrg 
     55  1.3     mrg #define FDISCARD_VERSION	20240113u
     56  1.1     mrg 
     57  1.1     mrg static void __dead
     58  1.1     mrg usage(const char* msg)
     59  1.1     mrg {
     60  1.1     mrg 	FILE *out = stdout;
     61  1.1     mrg 	int rv = 0;
     62  1.1     mrg 
     63  1.1     mrg 	if (msg) {
     64  1.1     mrg 		out = stderr;
     65  1.1     mrg 		rv = 1;
     66  1.1     mrg 		fprintf(out, "%s", msg);
     67  1.1     mrg 	}
     68  1.1     mrg 	fprintf(out, "Usage: blkdiscard [-hnRsVvz] [-l length] "
     69  1.1     mrg 		     "[-m chunk_bytes]\n"
     70  1.1     mrg 		     "                  [-o first_byte] <file>\n");
     71  1.1     mrg 	exit(rv);
     72  1.1     mrg }
     73  1.1     mrg 
     74  1.1     mrg static void __dead
     75  1.1     mrg version(void)
     76  1.1     mrg {
     77  1.1     mrg 
     78  1.1     mrg 	printf("NetBSD blkdiscard %u\n", FDISCARD_VERSION);
     79  1.1     mrg 	exit(0);
     80  1.1     mrg }
     81  1.1     mrg 
     82  1.1     mrg static void
     83  1.1     mrg write_one(int fd, off_t discard_size, off_t first_byte)
     84  1.1     mrg {
     85  1.1     mrg 
     86  1.1     mrg 	if (secure)
     87  1.1     mrg 		/* not yet */;
     88  1.1     mrg 	else if (zeroout) {
     89  1.1     mrg 		if (pwrite(fd, zeros, discard_size, first_byte) != discard_size)
     90  1.1     mrg 			err(1, "pwrite");
     91  1.1     mrg 	} else if (fdiscard(fd, first_byte, discard_size) != 0)
     92  1.1     mrg 		err(1, "fdiscard");
     93  1.1     mrg }
     94  1.1     mrg 
     95  1.1     mrg int
     96  1.1     mrg main(int argc, char *argv[])
     97  1.1     mrg {
     98  1.1     mrg 	off_t first_byte = 0, end_offset = 0;
     99  1.1     mrg 	/* doing a terabyte at a time leads to ATA timeout issues */
    100  1.1     mrg 	off_t max_per_call = 32 * 1024 * 1024;
    101  1.1     mrg 	off_t discard_size;
    102  1.1     mrg 	off_t size = 0;
    103  1.1     mrg 	off_t length = 0;
    104  1.1     mrg 	int64_t val;
    105  1.1     mrg 	int i;
    106  1.1     mrg 	bool verbose = false;
    107  1.3     mrg 	struct stat sb;
    108  1.1     mrg 
    109  1.1     mrg 	/* Default /sbin/blkdiscard to be "run" */
    110  1.1     mrg 	bool norun = strcmp(getprogname(), "blkdiscard") != 0;
    111  1.1     mrg 
    112  1.1     mrg 	while ((i = getopt(argc, argv, "f:hl:m:no:p:RsvVz")) != -1) {
    113  1.1     mrg 		switch (i) {
    114  1.1     mrg 		case 'l':
    115  1.1     mrg 			if (dehumanize_number(optarg, &val) == -1 || val < 0)
    116  1.1     mrg 				usage("bad -s\n");
    117  1.1     mrg 			length = val;
    118  1.1     mrg 			break;
    119  1.1     mrg 		case 'm':
    120  1.1     mrg 		case 'p':
    121  1.1     mrg 			if (dehumanize_number(optarg, &val) == -1 || val < 0)
    122  1.1     mrg 				usage("bad -m\n");
    123  1.1     mrg 			max_per_call = val;
    124  1.1     mrg 			break;
    125  1.1     mrg 		case 'f':
    126  1.1     mrg 		case 'o':
    127  1.1     mrg 			if (dehumanize_number(optarg, &val) == -1 || val < 0)
    128  1.1     mrg 				usage("bad -f\n");
    129  1.1     mrg 			first_byte = val;
    130  1.1     mrg 			break;
    131  1.1     mrg 		case 'n':
    132  1.1     mrg 			norun = true;
    133  1.1     mrg 			break;
    134  1.1     mrg 		case 'R':
    135  1.1     mrg 			norun = false;
    136  1.1     mrg 			break;
    137  1.1     mrg 		case 's':
    138  1.1     mrg 			secure = true;
    139  1.1     mrg 			break;
    140  1.1     mrg 		case 'V':
    141  1.1     mrg 			version();
    142  1.1     mrg 		case 'v':
    143  1.1     mrg 			verbose = true;
    144  1.1     mrg 			break;
    145  1.1     mrg 		case 'z':
    146  1.1     mrg 			zeroout = true;
    147  1.1     mrg 			break;
    148  1.1     mrg 		case 'h':
    149  1.1     mrg 			usage(NULL);
    150  1.1     mrg 		default:
    151  1.1     mrg 			usage("bad options\n");
    152  1.1     mrg 		}
    153  1.1     mrg 	}
    154  1.1     mrg 	argc -= optind;
    155  1.1     mrg 	argv += optind;
    156  1.1     mrg 
    157  1.1     mrg 	if (secure)
    158  1.4  andvar 		usage("blkdiscard: secure erase not yet implemented\n");
    159  1.1     mrg 	if (zeroout) {
    160  1.1     mrg 		zeros = calloc(1, max_per_call);
    161  1.1     mrg 		if (!zeros)
    162  1.1     mrg 			errx(1, "Unable to allocate %lld bytes for zeros",
    163  1.1     mrg 			    (long long)max_per_call);
    164  1.1     mrg 	}
    165  1.1     mrg 
    166  1.1     mrg 	if (length)
    167  1.1     mrg 		end_offset = first_byte + length;
    168  1.1     mrg 
    169  1.1     mrg 	if (argc != 1)
    170  1.1     mrg 		usage("not one arg left\n");
    171  1.1     mrg 
    172  1.1     mrg 	char *name = argv[0];
    173  1.1     mrg 	int fd = open(name, O_RDWR);
    174  1.1     mrg 	if (fd < 0)
    175  1.1     mrg 		err(1, "open on %s", name);
    176  1.1     mrg 
    177  1.1     mrg 	if (size == 0) {
    178  1.1     mrg 		struct dkwedge_info dkw;
    179  1.3     mrg 
    180  1.1     mrg 		if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
    181  1.1     mrg 			size = dkw.dkw_size * DEV_BSIZE;
    182  1.1     mrg 			if (verbose)
    183  1.1     mrg 				printf("%s: wedge size is %lld\n", name,
    184  1.1     mrg 				    (long long)size);
    185  1.1     mrg 		}
    186  1.1     mrg 	}
    187  1.1     mrg 
    188  1.3     mrg 	if (size == 0 && fstat(fd, &sb) != -1) {
    189  1.1     mrg 		struct disklabel dl;
    190  1.3     mrg 
    191  1.1     mrg 		if (ioctl(fd, DIOCGDINFO, &dl) != -1) {
    192  1.3     mrg 			unsigned part = DISKPART(sb.st_rdev);
    193  1.1     mrg 
    194  1.1     mrg 			size = (uint64_t)dl.d_partitions[part].p_size *
    195  1.1     mrg 			    dl.d_secsize;
    196  1.1     mrg 			if (verbose)
    197  1.3     mrg 				printf("%s: partition %u disklabel size is"
    198  1.3     mrg 				       " %lld\n", name, part, (long long)size);
    199  1.1     mrg 		}
    200  1.1     mrg 
    201  1.3     mrg 		if (size == 0) {
    202  1.1     mrg 			size = sb.st_size;
    203  1.1     mrg 			if (verbose)
    204  1.1     mrg 				printf("%s: stat size is %lld\n", name,
    205  1.1     mrg 				    (long long)size);
    206  1.1     mrg 		}
    207  1.1     mrg 	}
    208  1.1     mrg 
    209  1.1     mrg 	if (size == 0) {
    210  1.1     mrg 		fprintf(stderr, "unable to determine size.\n");
    211  1.1     mrg 		exit(1);
    212  1.1     mrg 	}
    213  1.1     mrg 
    214  1.1     mrg 	size -= first_byte;
    215  1.1     mrg 
    216  1.1     mrg 	if (end_offset) {
    217  1.1     mrg 		if (end_offset > size) {
    218  1.1     mrg 			fprintf(stderr, "end_offset %lld > size %lld\n",
    219  1.1     mrg 			    (long long)end_offset, (long long)size);
    220  1.1     mrg 			usage("");
    221  1.1     mrg 		}
    222  1.1     mrg 		size = end_offset;
    223  1.1     mrg 	}
    224  1.1     mrg 
    225  1.2     mrg 	if (verbose) {
    226  1.2     mrg 		struct winsize winsize;
    227  1.2     mrg 
    228  1.1     mrg 		printf("%sgoing to %s on %s from byte %lld for "
    229  1.1     mrg 		       "%lld bytes, %lld at a time\n",
    230  1.1     mrg 		       norun ? "not " : "",
    231  1.1     mrg 		       secure ? "secure erase" :
    232  1.1     mrg 		       zeroout ? "zero out" : "fdiscard(2)",
    233  1.1     mrg 		       name, (long long)first_byte, (long long)size,
    234  1.1     mrg 		       (long long)max_per_call);
    235  1.1     mrg 
    236  1.2     mrg 		if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1 &&
    237  1.2     mrg 		    winsize.ws_col > 1)
    238  1.2     mrg 			ttywidth = winsize.ws_col - 1;
    239  1.2     mrg 	}
    240  1.2     mrg 
    241  1.2     mrg 	unsigned loop = 0;
    242  1.1     mrg 	while (size > 0) {
    243  1.1     mrg 		if (size > max_per_call)
    244  1.1     mrg 			discard_size = max_per_call;
    245  1.1     mrg 		else
    246  1.1     mrg 			discard_size = size;
    247  1.1     mrg 
    248  1.1     mrg 		if (!norun)
    249  1.1     mrg 			write_one(fd, discard_size, first_byte);
    250  1.1     mrg 
    251  1.1     mrg 		first_byte += discard_size;
    252  1.1     mrg 		size -= discard_size;
    253  1.1     mrg 		if (verbose) {
    254  1.1     mrg 			printf(".");
    255  1.1     mrg 			fflush(stdout);
    256  1.2     mrg 			if (++loop >= ttywidth) {
    257  1.1     mrg 				loop = 0;
    258  1.1     mrg 				printf("\n");
    259  1.1     mrg 			}
    260  1.1     mrg 		}
    261  1.1     mrg 	}
    262  1.1     mrg 	if (loop != 0)
    263  1.1     mrg 		printf("\n");
    264  1.1     mrg 
    265  1.1     mrg 	if (close(fd) != 0)
    266  1.1     mrg 		warnx("close failed: %s", strerror(errno));
    267  1.1     mrg 
    268  1.1     mrg 	return 0;
    269  1.1     mrg }
    270