1614595dcSXin LI /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni *
46b88e76bSSheldon Hearn * Copyright (c) 2000 Sheldon Hearn <sheldonh@FreeBSD.org>.
56b88e76bSSheldon Hearn * All rights reserved.
6edeb84a1SSheldon Hearn *
75ee2c357SKa Ho Ng * Copyright (c) 2021 The FreeBSD Foundation
85ee2c357SKa Ho Ng *
95ee2c357SKa Ho Ng * Portions of this software were developed by Ka Ho Ng <khng@FreeBSD.org>
105ee2c357SKa Ho Ng * under sponsorship from the FreeBSD Foundation.
115ee2c357SKa 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
35edeb84a1SSheldon Hearn #include <sys/stat.h>
36edeb84a1SSheldon Hearn
37edeb84a1SSheldon Hearn #include <ctype.h>
38edeb84a1SSheldon Hearn #include <err.h>
39edeb84a1SSheldon Hearn #include <errno.h>
40edeb84a1SSheldon Hearn #include <fcntl.h>
41edeb84a1SSheldon Hearn #include <stdio.h>
42edeb84a1SSheldon Hearn #include <stdlib.h>
43edeb84a1SSheldon Hearn #include <unistd.h>
44edeb84a1SSheldon Hearn
4532bdc2b6SMaxim Sobolev #include <libutil.h>
4632bdc2b6SMaxim Sobolev
473f330d7dSWarner Losh static void usage(void);
48edeb84a1SSheldon Hearn
49edeb84a1SSheldon Hearn int
main(int argc,char ** argv)50edeb84a1SSheldon Hearn main(int argc, char **argv)
51edeb84a1SSheldon Hearn {
52edeb84a1SSheldon Hearn struct stat sb;
53edeb84a1SSheldon Hearn mode_t omode;
545ee2c357SKa Ho Ng off_t oflow, rsize, sz, tsize, round, off, len;
55fc96358cSJilles Tjoelker uint64_t usz;
565ee2c357SKa Ho Ng int ch, error, fd, oflags, r;
575ee2c357SKa Ho Ng int do_dealloc;
585ee2c357SKa Ho Ng int do_truncate;
59dde27dbcSEmmanuel Vadot int no_create;
60dde27dbcSEmmanuel Vadot int do_relative;
6110173270SEmmanuel Vadot int do_round;
62dde27dbcSEmmanuel Vadot int do_refer;
63dde27dbcSEmmanuel Vadot int got_size;
64edeb84a1SSheldon Hearn char *fname, *rname;
655ee2c357SKa Ho Ng struct spacectl_range sr;
66edeb84a1SSheldon Hearn
67d59e8ae8SColin Percival fd = -1;
685ee2c357SKa Ho Ng rsize = tsize = sz = off = 0;
695ee2c357SKa Ho Ng len = -1;
705ee2c357SKa Ho Ng do_dealloc = no_create = do_relative = do_round = do_refer =
715ee2c357SKa Ho Ng got_size = 0;
725ee2c357SKa Ho Ng do_truncate = 1;
735ee2c357SKa Ho Ng error = r = 0;
74b86e825aSSheldon Hearn rname = NULL;
755ee2c357SKa Ho Ng while ((ch = getopt(argc, argv, "cdr:s:o:l:")) != -1)
76edeb84a1SSheldon Hearn switch (ch) {
77edeb84a1SSheldon Hearn case 'c':
78b86e825aSSheldon Hearn no_create = 1;
79edeb84a1SSheldon Hearn break;
805ee2c357SKa Ho Ng case 'd':
815ee2c357SKa Ho Ng do_dealloc = 1;
825ee2c357SKa Ho Ng do_truncate = 0;
835ee2c357SKa Ho Ng break;
84edeb84a1SSheldon Hearn case 'r':
85b86e825aSSheldon Hearn do_refer = 1;
86edeb84a1SSheldon Hearn rname = optarg;
87edeb84a1SSheldon Hearn break;
88edeb84a1SSheldon Hearn case 's':
8910173270SEmmanuel Vadot if (*optarg == '+' || *optarg == '-') {
9010173270SEmmanuel Vadot do_relative = 1;
9110173270SEmmanuel Vadot } else if (*optarg == '%' || *optarg == '/') {
9210173270SEmmanuel Vadot do_round = 1;
9310173270SEmmanuel Vadot }
9410173270SEmmanuel Vadot if (expand_number(do_relative || do_round ?
9510173270SEmmanuel Vadot optarg + 1 : optarg,
96fc96358cSJilles Tjoelker &usz) == -1 || (off_t)usz < 0)
97edeb84a1SSheldon Hearn errx(EXIT_FAILURE,
98edeb84a1SSheldon Hearn "invalid size argument `%s'", optarg);
99fc96358cSJilles Tjoelker
10010173270SEmmanuel Vadot sz = (*optarg == '-' || *optarg == '/') ?
10110173270SEmmanuel Vadot -(off_t)usz : (off_t)usz;
102b86e825aSSheldon Hearn got_size = 1;
103edeb84a1SSheldon Hearn break;
1045ee2c357SKa Ho Ng case 'o':
1055ee2c357SKa Ho Ng if (expand_number(optarg, &usz) == -1 ||
1065ee2c357SKa Ho Ng (off_t)usz < 0)
1075ee2c357SKa Ho Ng errx(EXIT_FAILURE,
1085ee2c357SKa Ho Ng "invalid offset argument `%s'", optarg);
1095ee2c357SKa Ho Ng
1105ee2c357SKa Ho Ng off = usz;
1115ee2c357SKa Ho Ng break;
1125ee2c357SKa Ho Ng case 'l':
1135ee2c357SKa Ho Ng if (expand_number(optarg, &usz) == -1 ||
1145ee2c357SKa Ho Ng (off_t)usz <= 0)
1155ee2c357SKa Ho Ng errx(EXIT_FAILURE,
1165ee2c357SKa Ho Ng "invalid length argument `%s'", optarg);
1175ee2c357SKa Ho Ng
1185ee2c357SKa Ho Ng len = usz;
1195ee2c357SKa Ho Ng break;
120edeb84a1SSheldon Hearn default:
121edeb84a1SSheldon Hearn usage();
122edeb84a1SSheldon Hearn /* NOTREACHED */
123edeb84a1SSheldon Hearn }
124edeb84a1SSheldon Hearn
125edeb84a1SSheldon Hearn argv += optind;
126edeb84a1SSheldon Hearn argc -= optind;
127edeb84a1SSheldon Hearn
128edeb84a1SSheldon Hearn /*
1295ee2c357SKa Ho Ng * Exactly one of do_refer, got_size or do_dealloc must be specified.
1305ee2c357SKa Ho Ng * Since do_relative implies got_size, do_relative, do_refer and
1315ee2c357SKa Ho Ng * do_dealloc are also mutually exclusive. If do_dealloc is specified,
1325ee2c357SKa Ho Ng * the length argument must be set. See usage() for allowed
1335ee2c357SKa Ho Ng * invocations.
134edeb84a1SSheldon Hearn */
1355ee2c357SKa Ho Ng if (argc < 1 || do_refer + got_size + do_dealloc != 1 ||
1365ee2c357SKa Ho Ng (do_dealloc == 1 && len == -1))
137edeb84a1SSheldon Hearn usage();
1385ee2c357SKa Ho Ng if (do_refer == 1) {
139edeb84a1SSheldon Hearn if (stat(rname, &sb) == -1)
140edeb84a1SSheldon Hearn err(EXIT_FAILURE, "%s", rname);
141edeb84a1SSheldon Hearn tsize = sb.st_size;
1425ee2c357SKa Ho Ng } else if (do_relative == 1 || do_round == 1)
143edeb84a1SSheldon Hearn rsize = sz;
1445ee2c357SKa Ho Ng else if (do_dealloc == 0)
145edeb84a1SSheldon Hearn tsize = sz;
146edeb84a1SSheldon Hearn
147edeb84a1SSheldon Hearn if (no_create)
148edeb84a1SSheldon Hearn oflags = O_WRONLY;
149edeb84a1SSheldon Hearn else
150edeb84a1SSheldon Hearn oflags = O_WRONLY | O_CREAT;
151edeb84a1SSheldon Hearn omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
152edeb84a1SSheldon Hearn
153edeb84a1SSheldon Hearn while ((fname = *argv++) != NULL) {
154a39846b5SMaxim Konovalov if (fd != -1)
155a39846b5SMaxim Konovalov close(fd);
156edeb84a1SSheldon Hearn if ((fd = open(fname, oflags, omode)) == -1) {
157edeb84a1SSheldon Hearn if (errno != ENOENT) {
158edeb84a1SSheldon Hearn warn("%s", fname);
159edeb84a1SSheldon Hearn error++;
160edeb84a1SSheldon Hearn }
161edeb84a1SSheldon Hearn continue;
162edeb84a1SSheldon Hearn }
1635ee2c357SKa Ho Ng if (do_relative == 1) {
164edeb84a1SSheldon Hearn if (fstat(fd, &sb) == -1) {
165edeb84a1SSheldon Hearn warn("%s", fname);
166edeb84a1SSheldon Hearn error++;
167edeb84a1SSheldon Hearn continue;
168edeb84a1SSheldon Hearn }
169edeb84a1SSheldon Hearn oflow = sb.st_size + rsize;
170edeb84a1SSheldon Hearn if (oflow < (sb.st_size + rsize)) {
171edeb84a1SSheldon Hearn errno = EFBIG;
172edeb84a1SSheldon Hearn warn("%s", fname);
173edeb84a1SSheldon Hearn error++;
174edeb84a1SSheldon Hearn continue;
175edeb84a1SSheldon Hearn }
176edeb84a1SSheldon Hearn tsize = oflow;
177edeb84a1SSheldon Hearn }
1785ee2c357SKa Ho Ng if (do_round == 1) {
17910173270SEmmanuel Vadot if (fstat(fd, &sb) == -1) {
18010173270SEmmanuel Vadot warn("%s", fname);
18110173270SEmmanuel Vadot error++;
18210173270SEmmanuel Vadot continue;
18310173270SEmmanuel Vadot }
18410173270SEmmanuel Vadot sz = rsize;
18510173270SEmmanuel Vadot if (sz < 0)
18610173270SEmmanuel Vadot sz = -sz;
18710173270SEmmanuel Vadot if (sb.st_size % sz) {
18810173270SEmmanuel Vadot round = sb.st_size / sz;
18910173270SEmmanuel Vadot if (round != sz && rsize < 0)
19010173270SEmmanuel Vadot round--;
19110173270SEmmanuel Vadot else if (rsize > 0)
19210173270SEmmanuel Vadot round++;
19310173270SEmmanuel Vadot tsize = (round < 0 ? 0 : round) * sz;
19410173270SEmmanuel Vadot } else
19510173270SEmmanuel Vadot tsize = sb.st_size;
19610173270SEmmanuel Vadot }
197edeb84a1SSheldon Hearn if (tsize < 0)
198edeb84a1SSheldon Hearn tsize = 0;
199edeb84a1SSheldon Hearn
2005ee2c357SKa Ho Ng if (do_dealloc == 1) {
2015ee2c357SKa Ho Ng sr.r_offset = off;
2025ee2c357SKa Ho Ng sr.r_len = len;
2035ee2c357SKa Ho Ng r = fspacectl(fd, SPACECTL_DEALLOC, &sr, 0, &sr);
2045ee2c357SKa Ho Ng }
2055ee2c357SKa Ho Ng if (do_truncate == 1)
2065ee2c357SKa Ho Ng r = ftruncate(fd, tsize);
2075ee2c357SKa Ho Ng if (r == -1) {
208edeb84a1SSheldon Hearn warn("%s", fname);
209edeb84a1SSheldon Hearn error++;
210edeb84a1SSheldon Hearn }
211edeb84a1SSheldon Hearn }
212a39846b5SMaxim Konovalov if (fd != -1)
213a39846b5SMaxim Konovalov close(fd);
214edeb84a1SSheldon Hearn
21589c0d2b1SKa Ho Ng return (error ? EXIT_FAILURE : EXIT_SUCCESS);
216edeb84a1SSheldon Hearn }
217edeb84a1SSheldon Hearn
218edeb84a1SSheldon Hearn static void
usage(void)219edeb84a1SSheldon Hearn usage(void)
220edeb84a1SSheldon Hearn {
2215ee2c357SKa Ho Ng fprintf(stderr, "%s\n%s\n%s\n",
22210173270SEmmanuel Vadot "usage: truncate [-c] -s [+|-|%|/]size[K|k|M|m|G|g|T|t] file ...",
2235ee2c357SKa Ho Ng " truncate [-c] -r rfile file ...",
2245ee2c357SKa 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 ...");
225edeb84a1SSheldon Hearn exit(EXIT_FAILURE);
226edeb84a1SSheldon Hearn }
227