xref: /freebsd/usr.bin/truncate/truncate.c (revision 5ee2c35751ef5d131222423bf3e25073f997c337)
1614595dcSXin LI /*-
21de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
31de7b4b8SPedro F. Giffuni  *
46b88e76bSSheldon Hearn  * Copyright (c) 2000 Sheldon Hearn <sheldonh@FreeBSD.org>.
56b88e76bSSheldon Hearn  * All rights reserved.
6edeb84a1SSheldon Hearn  *
7*5ee2c357SKa Ho Ng  * Copyright (c) 2021 The FreeBSD Foundation
8*5ee2c357SKa Ho Ng  *
9*5ee2c357SKa Ho Ng  * Portions of this software were developed by Ka Ho Ng <khng@FreeBSD.org>
10*5ee2c357SKa Ho Ng  * under sponsorship from the FreeBSD Foundation.
11*5ee2c357SKa Ho Ng  *
12edeb84a1SSheldon Hearn  * Redistribution and use in source and binary forms, with or without
13edeb84a1SSheldon Hearn  * modification, are permitted provided that the following conditions
14edeb84a1SSheldon Hearn  * are met:
15edeb84a1SSheldon Hearn  * 1. Redistributions of source code must retain the above copyright
16edeb84a1SSheldon Hearn  *    notice, this list of conditions and the following disclaimer.
17edeb84a1SSheldon Hearn  * 2. Redistributions in binary form must reproduce the above copyright
18edeb84a1SSheldon Hearn  *    notice, this list of conditions and the following disclaimer in the
19edeb84a1SSheldon Hearn  *    documentation and/or other materials provided with the distribution.
20edeb84a1SSheldon Hearn  *
21edeb84a1SSheldon Hearn  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22edeb84a1SSheldon Hearn  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23edeb84a1SSheldon Hearn  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24edeb84a1SSheldon Hearn  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25edeb84a1SSheldon Hearn  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26edeb84a1SSheldon Hearn  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27edeb84a1SSheldon Hearn  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28edeb84a1SSheldon Hearn  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29edeb84a1SSheldon Hearn  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30edeb84a1SSheldon Hearn  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31edeb84a1SSheldon Hearn  * SUCH DAMAGE.
32edeb84a1SSheldon Hearn  *
33edeb84a1SSheldon Hearn  */
34edeb84a1SSheldon Hearn 
356b88e76bSSheldon Hearn static const char rcsid[] =
366b88e76bSSheldon Hearn     "$FreeBSD$";
376b88e76bSSheldon Hearn 
38edeb84a1SSheldon Hearn #include <sys/stat.h>
39edeb84a1SSheldon Hearn 
40edeb84a1SSheldon Hearn #include <ctype.h>
41edeb84a1SSheldon Hearn #include <err.h>
42edeb84a1SSheldon Hearn #include <errno.h>
43edeb84a1SSheldon Hearn #include <fcntl.h>
44edeb84a1SSheldon Hearn #include <stdio.h>
45edeb84a1SSheldon Hearn #include <stdlib.h>
46edeb84a1SSheldon Hearn #include <unistd.h>
47edeb84a1SSheldon Hearn 
4832bdc2b6SMaxim Sobolev #include <libutil.h>
4932bdc2b6SMaxim Sobolev 
503f330d7dSWarner Losh static void	usage(void);
51edeb84a1SSheldon Hearn 
52edeb84a1SSheldon Hearn int
53edeb84a1SSheldon Hearn main(int argc, char **argv)
54edeb84a1SSheldon Hearn {
55edeb84a1SSheldon Hearn 	struct stat sb;
56edeb84a1SSheldon Hearn 	mode_t omode;
57*5ee2c357SKa Ho Ng 	off_t oflow, rsize, sz, tsize, round, off, len;
58fc96358cSJilles Tjoelker 	uint64_t usz;
59*5ee2c357SKa Ho Ng 	int ch, error, fd, oflags, r;
60*5ee2c357SKa Ho Ng 	int do_dealloc;
61*5ee2c357SKa Ho Ng 	int do_truncate;
62dde27dbcSEmmanuel Vadot 	int no_create;
63dde27dbcSEmmanuel Vadot 	int do_relative;
6410173270SEmmanuel Vadot 	int do_round;
65dde27dbcSEmmanuel Vadot 	int do_refer;
66dde27dbcSEmmanuel Vadot 	int got_size;
67edeb84a1SSheldon Hearn 	char *fname, *rname;
68*5ee2c357SKa Ho Ng 	struct spacectl_range sr;
69edeb84a1SSheldon Hearn 
70d59e8ae8SColin Percival 	fd = -1;
71*5ee2c357SKa Ho Ng 	rsize = tsize = sz = off = 0;
72*5ee2c357SKa Ho Ng 	len = -1;
73*5ee2c357SKa Ho Ng 	do_dealloc = no_create = do_relative = do_round = do_refer =
74*5ee2c357SKa Ho Ng 	    got_size = 0;
75*5ee2c357SKa Ho Ng 	do_truncate = 1;
76*5ee2c357SKa Ho Ng 	error = r = 0;
77b86e825aSSheldon Hearn 	rname = NULL;
78*5ee2c357SKa Ho Ng 	while ((ch = getopt(argc, argv, "cdr:s:o:l:")) != -1)
79edeb84a1SSheldon Hearn 		switch (ch) {
80edeb84a1SSheldon Hearn 		case 'c':
81b86e825aSSheldon Hearn 			no_create = 1;
82edeb84a1SSheldon Hearn 			break;
83*5ee2c357SKa Ho Ng 		case 'd':
84*5ee2c357SKa Ho Ng 			do_dealloc = 1;
85*5ee2c357SKa Ho Ng 			do_truncate = 0;
86*5ee2c357SKa Ho Ng 			break;
87edeb84a1SSheldon Hearn 		case 'r':
88b86e825aSSheldon Hearn 			do_refer = 1;
89edeb84a1SSheldon Hearn 			rname = optarg;
90edeb84a1SSheldon Hearn 			break;
91edeb84a1SSheldon Hearn 		case 's':
9210173270SEmmanuel Vadot 			if (*optarg == '+' || *optarg == '-') {
9310173270SEmmanuel Vadot 				do_relative = 1;
9410173270SEmmanuel Vadot 			} else if (*optarg == '%' || *optarg == '/') {
9510173270SEmmanuel Vadot 				do_round = 1;
9610173270SEmmanuel Vadot 			}
9710173270SEmmanuel Vadot 			if (expand_number(do_relative || do_round ?
9810173270SEmmanuel Vadot 			    optarg + 1 : optarg,
99fc96358cSJilles Tjoelker 			    &usz) == -1 || (off_t)usz < 0)
100edeb84a1SSheldon Hearn 				errx(EXIT_FAILURE,
101edeb84a1SSheldon Hearn 				    "invalid size argument `%s'", optarg);
102fc96358cSJilles Tjoelker 
10310173270SEmmanuel Vadot 			sz = (*optarg == '-' || *optarg == '/') ?
10410173270SEmmanuel Vadot 				-(off_t)usz : (off_t)usz;
105b86e825aSSheldon Hearn 			got_size = 1;
106edeb84a1SSheldon Hearn 			break;
107*5ee2c357SKa Ho Ng 		case 'o':
108*5ee2c357SKa Ho Ng 			if (expand_number(optarg, &usz) == -1 ||
109*5ee2c357SKa Ho Ng 			    (off_t)usz < 0)
110*5ee2c357SKa Ho Ng 				errx(EXIT_FAILURE,
111*5ee2c357SKa Ho Ng 				    "invalid offset argument `%s'", optarg);
112*5ee2c357SKa Ho Ng 
113*5ee2c357SKa Ho Ng 			off = usz;
114*5ee2c357SKa Ho Ng 			break;
115*5ee2c357SKa Ho Ng 		case 'l':
116*5ee2c357SKa Ho Ng 			if (expand_number(optarg, &usz) == -1 ||
117*5ee2c357SKa Ho Ng 			    (off_t)usz <= 0)
118*5ee2c357SKa Ho Ng 				errx(EXIT_FAILURE,
119*5ee2c357SKa Ho Ng 				    "invalid length argument `%s'", optarg);
120*5ee2c357SKa Ho Ng 
121*5ee2c357SKa Ho Ng 			len = usz;
122*5ee2c357SKa Ho Ng 			break;
123edeb84a1SSheldon Hearn 		default:
124edeb84a1SSheldon Hearn 			usage();
125edeb84a1SSheldon Hearn 			/* NOTREACHED */
126edeb84a1SSheldon Hearn 		}
127edeb84a1SSheldon Hearn 
128edeb84a1SSheldon Hearn 	argv += optind;
129edeb84a1SSheldon Hearn 	argc -= optind;
130edeb84a1SSheldon Hearn 
131edeb84a1SSheldon Hearn 	/*
132*5ee2c357SKa Ho Ng 	 * Exactly one of do_refer, got_size or do_dealloc must be specified.
133*5ee2c357SKa Ho Ng 	 * Since do_relative implies got_size, do_relative, do_refer and
134*5ee2c357SKa Ho Ng 	 * do_dealloc are also mutually exclusive.  If do_dealloc is specified,
135*5ee2c357SKa Ho Ng 	 * the length argument must be set.  See usage() for allowed
136*5ee2c357SKa Ho Ng 	 * invocations.
137edeb84a1SSheldon Hearn 	 */
138*5ee2c357SKa Ho Ng 	if (argc < 1 || do_refer + got_size + do_dealloc != 1 ||
139*5ee2c357SKa Ho Ng 	    (do_dealloc == 1 && len == -1))
140edeb84a1SSheldon Hearn 		usage();
141*5ee2c357SKa Ho Ng 	if (do_refer == 1) {
142edeb84a1SSheldon Hearn 		if (stat(rname, &sb) == -1)
143edeb84a1SSheldon Hearn 			err(EXIT_FAILURE, "%s", rname);
144edeb84a1SSheldon Hearn 		tsize = sb.st_size;
145*5ee2c357SKa Ho Ng 	} else if (do_relative == 1 || do_round == 1)
146edeb84a1SSheldon Hearn 		rsize = sz;
147*5ee2c357SKa Ho Ng 	else if (do_dealloc == 0)
148edeb84a1SSheldon Hearn 		tsize = sz;
149edeb84a1SSheldon Hearn 
150edeb84a1SSheldon Hearn 	if (no_create)
151edeb84a1SSheldon Hearn 		oflags = O_WRONLY;
152edeb84a1SSheldon Hearn 	else
153edeb84a1SSheldon Hearn 		oflags = O_WRONLY | O_CREAT;
154edeb84a1SSheldon Hearn 	omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
155edeb84a1SSheldon Hearn 
156edeb84a1SSheldon Hearn 	while ((fname = *argv++) != NULL) {
157a39846b5SMaxim Konovalov 		if (fd != -1)
158a39846b5SMaxim Konovalov 			close(fd);
159edeb84a1SSheldon Hearn 		if ((fd = open(fname, oflags, omode)) == -1) {
160edeb84a1SSheldon Hearn 			if (errno != ENOENT) {
161edeb84a1SSheldon Hearn 				warn("%s", fname);
162edeb84a1SSheldon Hearn 				error++;
163edeb84a1SSheldon Hearn 			}
164edeb84a1SSheldon Hearn 			continue;
165edeb84a1SSheldon Hearn 		}
166*5ee2c357SKa Ho Ng 		if (do_relative == 1) {
167edeb84a1SSheldon Hearn 			if (fstat(fd, &sb) == -1) {
168edeb84a1SSheldon Hearn 				warn("%s", fname);
169edeb84a1SSheldon Hearn 				error++;
170edeb84a1SSheldon Hearn 				continue;
171edeb84a1SSheldon Hearn 			}
172edeb84a1SSheldon Hearn 			oflow = sb.st_size + rsize;
173edeb84a1SSheldon Hearn 			if (oflow < (sb.st_size + rsize)) {
174edeb84a1SSheldon Hearn 				errno = EFBIG;
175edeb84a1SSheldon Hearn 				warn("%s", fname);
176edeb84a1SSheldon Hearn 				error++;
177edeb84a1SSheldon Hearn 				continue;
178edeb84a1SSheldon Hearn 			}
179edeb84a1SSheldon Hearn 			tsize = oflow;
180edeb84a1SSheldon Hearn 		}
181*5ee2c357SKa Ho Ng 		if (do_round == 1) {
18210173270SEmmanuel Vadot 			if (fstat(fd, &sb) == -1) {
18310173270SEmmanuel Vadot 				warn("%s", fname);
18410173270SEmmanuel Vadot 				error++;
18510173270SEmmanuel Vadot 				continue;
18610173270SEmmanuel Vadot 			}
18710173270SEmmanuel Vadot 			sz = rsize;
18810173270SEmmanuel Vadot 			if (sz < 0)
18910173270SEmmanuel Vadot 				sz = -sz;
19010173270SEmmanuel Vadot 			if (sb.st_size % sz) {
19110173270SEmmanuel Vadot 				round = sb.st_size / sz;
19210173270SEmmanuel Vadot 				if (round != sz && rsize < 0)
19310173270SEmmanuel Vadot 					round--;
19410173270SEmmanuel Vadot 				else if (rsize > 0)
19510173270SEmmanuel Vadot 					round++;
19610173270SEmmanuel Vadot 				tsize = (round < 0 ? 0 : round) * sz;
19710173270SEmmanuel Vadot 			} else
19810173270SEmmanuel Vadot 				tsize = sb.st_size;
19910173270SEmmanuel Vadot 		}
200edeb84a1SSheldon Hearn 		if (tsize < 0)
201edeb84a1SSheldon Hearn 			tsize = 0;
202edeb84a1SSheldon Hearn 
203*5ee2c357SKa Ho Ng 		if (do_dealloc == 1) {
204*5ee2c357SKa Ho Ng 			sr.r_offset = off;
205*5ee2c357SKa Ho Ng 			sr.r_len = len;
206*5ee2c357SKa Ho Ng 			r = fspacectl(fd, SPACECTL_DEALLOC, &sr, 0, &sr);
207*5ee2c357SKa Ho Ng 		}
208*5ee2c357SKa Ho Ng 		if (do_truncate == 1)
209*5ee2c357SKa Ho Ng 			r = ftruncate(fd, tsize);
210*5ee2c357SKa Ho Ng 		if (r == -1) {
211edeb84a1SSheldon Hearn 			warn("%s", fname);
212edeb84a1SSheldon Hearn 			error++;
213edeb84a1SSheldon Hearn 		}
214edeb84a1SSheldon Hearn 	}
215a39846b5SMaxim Konovalov 	if (fd != -1)
216a39846b5SMaxim Konovalov 		close(fd);
217edeb84a1SSheldon Hearn 
21889c0d2b1SKa Ho Ng 	return (error ? EXIT_FAILURE : EXIT_SUCCESS);
219edeb84a1SSheldon Hearn }
220edeb84a1SSheldon Hearn 
221edeb84a1SSheldon Hearn static void
222edeb84a1SSheldon Hearn usage(void)
223edeb84a1SSheldon Hearn {
224*5ee2c357SKa Ho Ng 	fprintf(stderr, "%s\n%s\n%s\n",
22510173270SEmmanuel Vadot 	    "usage: truncate [-c] -s [+|-|%|/]size[K|k|M|m|G|g|T|t] file ...",
226*5ee2c357SKa Ho Ng 	    "       truncate [-c] -r rfile file ...",
227*5ee2c357SKa Ho Ng 	    "       truncate [-c] -d [-o offset[K|k|M|m|G|g|T|t]] -l length[K|k|M|m|G|g|T|t] file ...");
228edeb84a1SSheldon Hearn 	exit(EXIT_FAILURE);
229edeb84a1SSheldon Hearn }
230