tests: avoid false df failure with nfs and lofs
[coreutils.git] / src / truncate.c
blobd48c346e8ec4d9763654f18b2bae84908eff26b1
1 /* truncate -- truncate or extend the length of files.
2 Copyright (C) 2008-2013 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 <http://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 "error.h"
30 #include "quote.h"
31 #include "stat-size.h"
32 #include "xstrtol.h"
34 /* The official name of this program (e.g., no 'g' prefix). */
35 #define PROGRAM_NAME "truncate"
37 #define AUTHORS proper_name_utf8 ("Padraig Brady", "P\303\241draig Brady")
39 /* (-c) If true, don't create if not already there */
40 static bool no_create;
42 /* (-o) If true, --size refers to blocks not bytes */
43 static bool block_mode;
45 /* (-r) Reference file to use size from */
46 static char const *ref_file;
48 static struct option const longopts[] =
50 {"no-create", no_argument, NULL, 'c'},
51 {"io-blocks", no_argument, NULL, 'o'},
52 {"reference", required_argument, NULL, 'r'},
53 {"size", required_argument, NULL, 's'},
54 {GETOPT_HELP_OPTION_DECL},
55 {GETOPT_VERSION_OPTION_DECL},
56 {NULL, 0, NULL, 0}
59 typedef enum
60 { rm_abs = 0, rm_rel, rm_min, rm_max, rm_rdn, rm_rup } rel_mode_t;
62 /* Set size to the value of STR, interpreted as a decimal integer,
63 optionally multiplied by various values.
64 Return -1 on error, 0 on success.
66 This supports dd BLOCK size suffixes + lowercase g,t,m for bsd compat
67 Note we don't support dd's b=512, c=1, w=2 or 21x512MiB formats. */
68 static int
69 parse_len (char const *str, off_t *size)
71 enum strtol_error e;
72 intmax_t tmp_size;
73 e = xstrtoimax (str, NULL, 10, &tmp_size, "EgGkKmMPtTYZ0");
74 if (e == LONGINT_OK
75 && !(OFF_T_MIN <= tmp_size && tmp_size <= OFF_T_MAX))
76 e = LONGINT_OVERFLOW;
78 if (e == LONGINT_OK)
80 errno = 0;
81 *size = tmp_size;
82 return 0;
85 errno = (e == LONGINT_OVERFLOW ? EOVERFLOW : 0);
86 return -1;
89 void
90 usage (int status)
92 if (status != EXIT_SUCCESS)
93 emit_try_help ();
94 else
96 printf (_("Usage: %s OPTION... FILE...\n"), program_name);
97 fputs (_("\
98 Shrink or extend the size of each FILE to the specified size\n\
99 \n\
100 A FILE argument that does not exist is created.\n\
102 If a FILE is larger than the specified size, the extra data is lost.\n\
103 If a FILE is shorter, it is extended and the extended part (hole)\n\
104 reads as zero bytes.\n\
105 "), stdout);
107 emit_mandatory_arg_note ();
109 fputs (_("\
110 -c, --no-create do not create any files\n\
111 "), stdout);
112 fputs (_("\
113 -o, --io-blocks treat SIZE as number of IO blocks instead of bytes\n\
114 "), stdout);
115 fputs (_("\
116 -r, --reference=RFILE base size on RFILE\n\
117 -s, --size=SIZE set or adjust the file size by SIZE bytes\n"), stdout);
118 fputs (HELP_OPTION_DESCRIPTION, stdout);
119 fputs (VERSION_OPTION_DESCRIPTION, stdout);
120 emit_size_note ();
121 fputs (_("\n\
122 SIZE may also be prefixed by one of the following modifying characters:\n\
123 '+' extend by, '-' reduce by, '<' at most, '>' at least,\n\
124 '/' round down to multiple of, '%' round up to multiple of.\n"), stdout);
125 emit_ancillary_info ();
127 exit (status);
130 /* return true on success, false on error. */
131 static bool
132 do_ftruncate (int fd, char const *fname, off_t ssize, off_t rsize,
133 rel_mode_t rel_mode)
135 struct stat sb;
136 off_t nsize;
138 if ((block_mode || (rel_mode && rsize < 0)) && fstat (fd, &sb) != 0)
140 error (0, errno, _("cannot fstat %s"), quote (fname));
141 return false;
143 if (block_mode)
145 off_t const blksize = ST_BLKSIZE (sb);
146 if (ssize < OFF_T_MIN / blksize || ssize > OFF_T_MAX / blksize)
148 error (0, 0,
149 _("overflow in %" PRIdMAX
150 " * %" PRIdMAX " byte blocks for file %s"),
151 (intmax_t) ssize, (intmax_t) blksize,
152 quote (fname));
153 return false;
155 ssize *= blksize;
157 if (rel_mode)
159 uintmax_t fsize;
161 if (0 <= rsize)
162 fsize = rsize;
163 else
165 off_t file_size;
166 if (usable_st_size (&sb))
168 file_size = sb.st_size;
169 if (file_size < 0)
171 /* Sanity check. Overflow is the only reason I can think
172 this would ever go negative. */
173 error (0, 0, _("%s has unusable, apparently negative size"),
174 quote (fname));
175 return false;
178 else
180 file_size = lseek (fd, 0, SEEK_END);
181 if (file_size < 0)
183 error (0, errno, _("cannot get the size of %s"),
184 quote (fname));
185 return false;
188 fsize = file_size;
191 if (rel_mode == rm_min)
192 nsize = MAX (fsize, (uintmax_t) ssize);
193 else if (rel_mode == rm_max)
194 nsize = MIN (fsize, (uintmax_t) ssize);
195 else if (rel_mode == rm_rdn)
196 /* 0..ssize-1 -> 0 */
197 nsize = (fsize / ssize) * ssize;
198 else if (rel_mode == rm_rup)
199 /* 1..ssize -> ssize */
201 /* Here ssize>=1 && fsize>=0 */
202 uintmax_t const overflow = ((fsize + ssize - 1) / ssize) * ssize;
203 if (overflow > OFF_T_MAX)
205 error (0, 0, _("overflow rounding up size of file %s"),
206 quote (fname));
207 return false;
209 nsize = overflow;
211 else
213 if (ssize > OFF_T_MAX - (off_t)fsize)
215 error (0, 0, _("overflow extending size of file %s"),
216 quote (fname));
217 return false;
219 nsize = fsize + ssize;
222 else
223 nsize = ssize;
224 if (nsize < 0)
225 nsize = 0;
227 if (ftruncate (fd, nsize) == -1) /* note updates mtime & ctime */
229 error (0, errno,
230 _("failed to truncate %s at %" PRIdMAX " bytes"), quote (fname),
231 (intmax_t) nsize);
232 return false;
235 return true;
239 main (int argc, char **argv)
241 bool got_size = false;
242 bool errors = false;
243 off_t size IF_LINT ( = 0);
244 off_t rsize = -1;
245 rel_mode_t rel_mode = rm_abs;
246 int c, fd = -1, oflags;
247 char const *fname;
249 initialize_main (&argc, &argv);
250 set_program_name (argv[0]);
251 setlocale (LC_ALL, "");
252 bindtextdomain (PACKAGE, LOCALEDIR);
253 textdomain (PACKAGE);
255 atexit (close_stdout);
257 while ((c = getopt_long (argc, argv, "cor:s:", longopts, NULL)) != -1)
259 switch (c)
261 case 'c':
262 no_create = true;
263 break;
265 case 'o':
266 block_mode = true;
267 break;
269 case 'r':
270 ref_file = optarg;
271 break;
273 case 's':
274 /* skip any whitespace */
275 while (isspace (to_uchar (*optarg)))
276 optarg++;
277 switch (*optarg)
279 case '<':
280 rel_mode = rm_max;
281 optarg++;
282 break;
283 case '>':
284 rel_mode = rm_min;
285 optarg++;
286 break;
287 case '/':
288 rel_mode = rm_rdn;
289 optarg++;
290 break;
291 case '%':
292 rel_mode = rm_rup;
293 optarg++;
294 break;
296 /* skip any whitespace */
297 while (isspace (to_uchar (*optarg)))
298 optarg++;
299 if (*optarg == '+' || *optarg == '-')
301 if (rel_mode)
303 error (0, 0, _("multiple relative modifiers specified"));
304 /* Note other combinations are flagged as invalid numbers */
305 usage (EXIT_FAILURE);
307 rel_mode = rm_rel;
309 if (parse_len (optarg, &size) == -1)
310 error (EXIT_FAILURE, errno, _("invalid number %s"),
311 quote (optarg));
312 /* Rounding to multiple of 0 is nonsensical */
313 if ((rel_mode == rm_rup || rel_mode == rm_rdn) && size == 0)
314 error (EXIT_FAILURE, 0, _("division by zero"));
315 got_size = true;
316 break;
318 case_GETOPT_HELP_CHAR;
320 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
322 default:
323 usage (EXIT_FAILURE);
327 argv += optind;
328 argc -= optind;
330 /* must specify either size or reference file */
331 if (!ref_file && !got_size)
333 error (0, 0, _("you must specify either %s or %s"),
334 quote_n (0, "--size"), quote_n (1, "--reference"));
335 usage (EXIT_FAILURE);
337 /* must specify a relative size with a reference file */
338 if (ref_file && got_size && !rel_mode)
340 error (0, 0, _("you must specify a relative %s with %s"),
341 quote_n (0, "--size"), quote_n (1, "--reference"));
342 usage (EXIT_FAILURE);
344 /* block_mode without size is not valid */
345 if (block_mode && !got_size)
347 error (0, 0, _("%s was specified but %s was not"),
348 quote_n (0, "--io-blocks"), quote_n (1, "--size"));
349 usage (EXIT_FAILURE);
351 /* must specify at least 1 file */
352 if (argc < 1)
354 error (0, 0, _("missing file operand"));
355 usage (EXIT_FAILURE);
358 if (ref_file)
360 struct stat sb;
361 off_t file_size = -1;
362 if (stat (ref_file, &sb) != 0)
363 error (EXIT_FAILURE, errno, _("cannot stat %s"), quote (ref_file));
364 if (usable_st_size (&sb))
365 file_size = sb.st_size;
366 else
368 int ref_fd = open (ref_file, O_RDONLY);
369 if (0 <= ref_fd)
371 off_t file_end = lseek (ref_fd, 0, SEEK_END);
372 int saved_errno = errno;
373 close (ref_fd); /* ignore failure */
374 if (0 <= file_end)
375 file_size = file_end;
376 else
378 /* restore, in case close clobbered it. */
379 errno = saved_errno;
383 if (file_size < 0)
384 error (EXIT_FAILURE, errno, _("cannot get the size of %s"),
385 quote (ref_file));
386 if (!got_size)
387 size = file_size;
388 else
389 rsize = file_size;
392 oflags = O_WRONLY | (no_create ? 0 : O_CREAT) | O_NONBLOCK;
394 while ((fname = *argv++) != NULL)
396 if ((fd = open (fname, oflags, MODE_RW_UGO)) == -1)
398 /* 'truncate -s0 -c no-such-file' shouldn't gen error
399 'truncate -s0 no-such-dir/file' should gen ENOENT error
400 'truncate -s0 no-such-dir/' should gen EISDIR error
401 'truncate -s0 .' should gen EISDIR error */
402 if (!(no_create && errno == ENOENT))
404 error (0, errno, _("cannot open %s for writing"),
405 quote (fname));
406 errors = true;
408 continue;
412 if (fd != -1)
414 errors |= !do_ftruncate (fd, fname, size, rsize, rel_mode);
415 if (close (fd) != 0)
417 error (0, errno, _("failed to close %s"), quote (fname));
418 errors = true;
423 return errors ? EXIT_FAILURE : EXIT_SUCCESS;