dd: synchronize output after write errors
[coreutils.git] / src / truncate.c
blob918dc305fab5e638ac3ddfa66c2e57d7e53faac0
1 /* truncate -- truncate or extend the length of files.
2 Copyright (C) 2008-2022 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Pádraig Brady
19 This is backwards compatible with the FreeBSD utility, but is more
20 flexible wrt the size specifications and the use of long options,
21 to better fit the "GNU" environment. */
23 #include <config.h> /* sets _FILE_OFFSET_BITS=64 etc. */
24 #include <stdio.h>
25 #include <getopt.h>
26 #include <sys/types.h>
28 #include "system.h"
29 #include "die.h"
30 #include "error.h"
31 #include "quote.h"
32 #include "stat-size.h"
33 #include "xdectoint.h"
35 /* The official name of this program (e.g., no 'g' prefix). */
36 #define PROGRAM_NAME "truncate"
38 #define AUTHORS proper_name ("Padraig Brady")
40 /* (-c) If true, don't create if not already there */
41 static bool no_create;
43 /* (-o) If true, --size refers to blocks not bytes */
44 static bool block_mode;
46 /* (-r) Reference file to use size from */
47 static char const *ref_file;
49 static struct option const longopts[] =
51 {"no-create", no_argument, NULL, 'c'},
52 {"io-blocks", no_argument, NULL, 'o'},
53 {"reference", required_argument, NULL, 'r'},
54 {"size", required_argument, NULL, 's'},
55 {GETOPT_HELP_OPTION_DECL},
56 {GETOPT_VERSION_OPTION_DECL},
57 {NULL, 0, NULL, 0}
60 typedef enum
61 { rm_abs = 0, rm_rel, rm_min, rm_max, rm_rdn, rm_rup } rel_mode_t;
63 void
64 usage (int status)
66 if (status != EXIT_SUCCESS)
67 emit_try_help ();
68 else
70 printf (_("Usage: %s OPTION... FILE...\n"), program_name);
71 fputs (_("\
72 Shrink or extend the size of each FILE to the specified size\n\
73 \n\
74 A FILE argument that does not exist is created.\n\
75 \n\
76 If a FILE is larger than the specified size, the extra data is lost.\n\
77 If a FILE is shorter, it is extended and the sparse extended part (hole)\n\
78 reads as zero bytes.\n\
79 "), stdout);
81 emit_mandatory_arg_note ();
83 fputs (_("\
84 -c, --no-create do not create any files\n\
85 "), stdout);
86 fputs (_("\
87 -o, --io-blocks treat SIZE as number of IO blocks instead of bytes\n\
88 "), stdout);
89 fputs (_("\
90 -r, --reference=RFILE base size on RFILE\n\
91 -s, --size=SIZE set or adjust the file size by SIZE bytes\n"), stdout);
92 fputs (HELP_OPTION_DESCRIPTION, stdout);
93 fputs (VERSION_OPTION_DESCRIPTION, stdout);
94 emit_size_note ();
95 fputs (_("\n\
96 SIZE may also be prefixed by one of the following modifying characters:\n\
97 '+' extend by, '-' reduce by, '<' at most, '>' at least,\n\
98 '/' round down to multiple of, '%' round up to multiple of.\n"), stdout);
99 emit_ancillary_info (PROGRAM_NAME);
101 exit (status);
104 /* return true on success, false on error. */
105 static bool
106 do_ftruncate (int fd, char const *fname, off_t ssize, off_t rsize,
107 rel_mode_t rel_mode)
109 struct stat sb;
110 off_t nsize;
112 if ((block_mode || (rel_mode && rsize < 0)) && fstat (fd, &sb) != 0)
114 error (0, errno, _("cannot fstat %s"), quoteaf (fname));
115 return false;
117 if (block_mode)
119 ptrdiff_t blksize = ST_BLKSIZE (sb);
120 intmax_t ssize0 = ssize;
121 if (INT_MULTIPLY_WRAPV (ssize, blksize, &ssize))
123 error (0, 0,
124 _("overflow in %" PRIdMAX
125 " * %" PRIdPTR " byte blocks for file %s"),
126 ssize0, blksize, quoteaf (fname));
127 return false;
130 if (rel_mode)
132 off_t fsize;
134 if (0 <= rsize)
135 fsize = rsize;
136 else
138 if (usable_st_size (&sb))
140 fsize = sb.st_size;
141 if (fsize < 0)
143 /* Sanity check. Overflow is the only reason I can think
144 this would ever go negative. */
145 error (0, 0, _("%s has unusable, apparently negative size"),
146 quoteaf (fname));
147 return false;
150 else
152 fsize = lseek (fd, 0, SEEK_END);
153 if (fsize < 0)
155 error (0, errno, _("cannot get the size of %s"),
156 quoteaf (fname));
157 return false;
162 if (rel_mode == rm_min)
163 nsize = MAX (fsize, ssize);
164 else if (rel_mode == rm_max)
165 nsize = MIN (fsize, ssize);
166 else if (rel_mode == rm_rdn)
167 /* 0..ssize-1 -> 0 */
168 nsize = fsize - fsize % ssize;
169 else
171 if (rel_mode == rm_rup)
173 /* 1..ssize -> ssize */
174 off_t r = fsize % ssize;
175 ssize = r == 0 ? 0 : ssize - r;
177 if (INT_ADD_WRAPV (fsize, ssize, &nsize))
179 error (0, 0, _("overflow extending size of file %s"),
180 quoteaf (fname));
181 return false;
185 else
186 nsize = ssize;
187 if (nsize < 0)
188 nsize = 0;
190 if (ftruncate (fd, nsize) == -1) /* note updates mtime & ctime */
192 error (0, errno,
193 _("failed to truncate %s at %" PRIdMAX " bytes"), quoteaf (fname),
194 (intmax_t) nsize);
195 return false;
198 return true;
202 main (int argc, char **argv)
204 bool got_size = false;
205 bool errors = false;
206 off_t size IF_LINT ( = 0);
207 off_t rsize = -1;
208 rel_mode_t rel_mode = rm_abs;
209 int c, fd = -1, oflags;
210 char const *fname;
212 initialize_main (&argc, &argv);
213 set_program_name (argv[0]);
214 setlocale (LC_ALL, "");
215 bindtextdomain (PACKAGE, LOCALEDIR);
216 textdomain (PACKAGE);
218 atexit (close_stdout);
220 while ((c = getopt_long (argc, argv, "cor:s:", longopts, NULL)) != -1)
222 switch (c)
224 case 'c':
225 no_create = true;
226 break;
228 case 'o':
229 block_mode = true;
230 break;
232 case 'r':
233 ref_file = optarg;
234 break;
236 case 's':
237 /* skip any whitespace */
238 while (isspace (to_uchar (*optarg)))
239 optarg++;
240 switch (*optarg)
242 case '<':
243 rel_mode = rm_max;
244 optarg++;
245 break;
246 case '>':
247 rel_mode = rm_min;
248 optarg++;
249 break;
250 case '/':
251 rel_mode = rm_rdn;
252 optarg++;
253 break;
254 case '%':
255 rel_mode = rm_rup;
256 optarg++;
257 break;
259 /* skip any whitespace */
260 while (isspace (to_uchar (*optarg)))
261 optarg++;
262 if (*optarg == '+' || *optarg == '-')
264 if (rel_mode)
266 error (0, 0, _("multiple relative modifiers specified"));
267 /* Note other combinations are flagged as invalid numbers */
268 usage (EXIT_FAILURE);
270 rel_mode = rm_rel;
272 /* Support dd BLOCK size suffixes + lowercase g,t,m for bsd compat.
273 Note we don't support dd's b=512, c=1, w=2 or 21x512MiB formats. */
274 size = xdectoimax (optarg, OFF_T_MIN, OFF_T_MAX, "EgGkKmMPtTYZ0",
275 _("Invalid number"), 0);
276 /* Rounding to multiple of 0 is nonsensical */
277 if ((rel_mode == rm_rup || rel_mode == rm_rdn) && size == 0)
278 die (EXIT_FAILURE, 0, _("division by zero"));
279 got_size = true;
280 break;
282 case_GETOPT_HELP_CHAR;
284 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
286 default:
287 usage (EXIT_FAILURE);
291 argv += optind;
292 argc -= optind;
294 /* must specify either size or reference file */
295 if (!ref_file && !got_size)
297 error (0, 0, _("you must specify either %s or %s"),
298 quote_n (0, "--size"), quote_n (1, "--reference"));
299 usage (EXIT_FAILURE);
301 /* must specify a relative size with a reference file */
302 if (ref_file && got_size && !rel_mode)
304 error (0, 0, _("you must specify a relative %s with %s"),
305 quote_n (0, "--size"), quote_n (1, "--reference"));
306 usage (EXIT_FAILURE);
308 /* block_mode without size is not valid */
309 if (block_mode && !got_size)
311 error (0, 0, _("%s was specified but %s was not"),
312 quote_n (0, "--io-blocks"), quote_n (1, "--size"));
313 usage (EXIT_FAILURE);
315 /* must specify at least 1 file */
316 if (argc < 1)
318 error (0, 0, _("missing file operand"));
319 usage (EXIT_FAILURE);
322 if (ref_file)
324 struct stat sb;
325 off_t file_size = -1;
326 if (stat (ref_file, &sb) != 0)
327 die (EXIT_FAILURE, errno, _("cannot stat %s"), quoteaf (ref_file));
328 if (usable_st_size (&sb))
329 file_size = sb.st_size;
330 else
332 int ref_fd = open (ref_file, O_RDONLY);
333 if (0 <= ref_fd)
335 off_t file_end = lseek (ref_fd, 0, SEEK_END);
336 int saved_errno = errno;
337 close (ref_fd); /* ignore failure */
338 if (0 <= file_end)
339 file_size = file_end;
340 else
342 /* restore, in case close clobbered it. */
343 errno = saved_errno;
347 if (file_size < 0)
348 die (EXIT_FAILURE, errno, _("cannot get the size of %s"),
349 quoteaf (ref_file));
350 if (!got_size)
351 size = file_size;
352 else
353 rsize = file_size;
356 oflags = O_WRONLY | (no_create ? 0 : O_CREAT) | O_NONBLOCK;
358 while ((fname = *argv++) != NULL)
360 if ((fd = open (fname, oflags, MODE_RW_UGO)) == -1)
362 /* 'truncate -s0 -c no-such-file' shouldn't gen error
363 'truncate -s0 no-such-dir/file' should gen ENOENT error
364 'truncate -s0 no-such-dir/' should gen EISDIR error
365 'truncate -s0 .' should gen EISDIR error */
366 if (!(no_create && errno == ENOENT))
368 error (0, errno, _("cannot open %s for writing"),
369 quoteaf (fname));
370 errors = true;
372 continue;
376 if (fd != -1)
378 errors |= !do_ftruncate (fd, fname, size, rsize, rel_mode);
379 if (close (fd) != 0)
381 error (0, errno, _("failed to close %s"), quoteaf (fname));
382 errors = true;
387 return errors ? EXIT_FAILURE : EXIT_SUCCESS;