1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 static const char rcsid[] = 36 "$FreeBSD$"; 37 38 #include <sys/stat.h> 39 40 #include <ctype.h> 41 #include <err.h> 42 #include <errno.h> 43 #include <fcntl.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <unistd.h> 47 48 #include <libutil.h> 49 50 static void usage(void); 51 52 int 53 main(int argc, char **argv) 54 { 55 struct stat sb; 56 mode_t omode; 57 off_t oflow, rsize, sz, tsize, round, off, len; 58 uint64_t usz; 59 int ch, error, fd, oflags, r; 60 int do_dealloc; 61 int do_truncate; 62 int no_create; 63 int do_relative; 64 int do_round; 65 int do_refer; 66 int got_size; 67 char *fname, *rname; 68 struct spacectl_range sr; 69 70 fd = -1; 71 rsize = tsize = sz = off = 0; 72 len = -1; 73 do_dealloc = no_create = do_relative = do_round = do_refer = 74 got_size = 0; 75 do_truncate = 1; 76 error = r = 0; 77 rname = NULL; 78 while ((ch = getopt(argc, argv, "cdr:s:o:l:")) != -1) 79 switch (ch) { 80 case 'c': 81 no_create = 1; 82 break; 83 case 'd': 84 do_dealloc = 1; 85 do_truncate = 0; 86 break; 87 case 'r': 88 do_refer = 1; 89 rname = optarg; 90 break; 91 case 's': 92 if (*optarg == '+' || *optarg == '-') { 93 do_relative = 1; 94 } else if (*optarg == '%' || *optarg == '/') { 95 do_round = 1; 96 } 97 if (expand_number(do_relative || do_round ? 98 optarg + 1 : optarg, 99 &usz) == -1 || (off_t)usz < 0) 100 errx(EXIT_FAILURE, 101 "invalid size argument `%s'", optarg); 102 103 sz = (*optarg == '-' || *optarg == '/') ? 104 -(off_t)usz : (off_t)usz; 105 got_size = 1; 106 break; 107 case 'o': 108 if (expand_number(optarg, &usz) == -1 || 109 (off_t)usz < 0) 110 errx(EXIT_FAILURE, 111 "invalid offset argument `%s'", optarg); 112 113 off = usz; 114 break; 115 case 'l': 116 if (expand_number(optarg, &usz) == -1 || 117 (off_t)usz <= 0) 118 errx(EXIT_FAILURE, 119 "invalid length argument `%s'", optarg); 120 121 len = usz; 122 break; 123 default: 124 usage(); 125 /* NOTREACHED */ 126 } 127 128 argv += optind; 129 argc -= optind; 130 131 /* 132 * Exactly one of do_refer, got_size or do_dealloc must be specified. 133 * Since do_relative implies got_size, do_relative, do_refer and 134 * do_dealloc are also mutually exclusive. If do_dealloc is specified, 135 * the length argument must be set. See usage() for allowed 136 * invocations. 137 */ 138 if (argc < 1 || do_refer + got_size + do_dealloc != 1 || 139 (do_dealloc == 1 && len == -1)) 140 usage(); 141 if (do_refer == 1) { 142 if (stat(rname, &sb) == -1) 143 err(EXIT_FAILURE, "%s", rname); 144 tsize = sb.st_size; 145 } else if (do_relative == 1 || do_round == 1) 146 rsize = sz; 147 else if (do_dealloc == 0) 148 tsize = sz; 149 150 if (no_create) 151 oflags = O_WRONLY; 152 else 153 oflags = O_WRONLY | O_CREAT; 154 omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; 155 156 while ((fname = *argv++) != NULL) { 157 if (fd != -1) 158 close(fd); 159 if ((fd = open(fname, oflags, omode)) == -1) { 160 if (errno != ENOENT) { 161 warn("%s", fname); 162 error++; 163 } 164 continue; 165 } 166 if (do_relative == 1) { 167 if (fstat(fd, &sb) == -1) { 168 warn("%s", fname); 169 error++; 170 continue; 171 } 172 oflow = sb.st_size + rsize; 173 if (oflow < (sb.st_size + rsize)) { 174 errno = EFBIG; 175 warn("%s", fname); 176 error++; 177 continue; 178 } 179 tsize = oflow; 180 } 181 if (do_round == 1) { 182 if (fstat(fd, &sb) == -1) { 183 warn("%s", fname); 184 error++; 185 continue; 186 } 187 sz = rsize; 188 if (sz < 0) 189 sz = -sz; 190 if (sb.st_size % sz) { 191 round = sb.st_size / sz; 192 if (round != sz && rsize < 0) 193 round--; 194 else if (rsize > 0) 195 round++; 196 tsize = (round < 0 ? 0 : round) * sz; 197 } else 198 tsize = sb.st_size; 199 } 200 if (tsize < 0) 201 tsize = 0; 202 203 if (do_dealloc == 1) { 204 sr.r_offset = off; 205 sr.r_len = len; 206 r = fspacectl(fd, SPACECTL_DEALLOC, &sr, 0, &sr); 207 } 208 if (do_truncate == 1) 209 r = ftruncate(fd, tsize); 210 if (r == -1) { 211 warn("%s", fname); 212 error++; 213 } 214 } 215 if (fd != -1) 216 close(fd); 217 218 return (error ? EXIT_FAILURE : EXIT_SUCCESS); 219 } 220 221 static void 222 usage(void) 223 { 224 fprintf(stderr, "%s\n%s\n%s\n", 225 "usage: truncate [-c] -s [+|-|%|/]size[K|k|M|m|G|g|T|t] file ...", 226 " truncate [-c] -r rfile file ...", 227 " truncate [-c] -d [-o offset[K|k|M|m|G|g|T|t]] -l length[K|k|M|m|G|g|T|t] file ..."); 228 exit(EXIT_FAILURE); 229 } 230