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