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. */
26 #include <sys/types.h>
31 #include "stat-size.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
},
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. */
69 parse_len (char const *str
, off_t
*size
)
73 e
= xstrtoimax (str
, NULL
, 10, &tmp_size
, "EgGkKmMPtTYZ0");
75 && !(OFF_T_MIN
<= tmp_size
&& tmp_size
<= OFF_T_MAX
))
85 errno
= (e
== LONGINT_OVERFLOW
? EOVERFLOW
: 0);
92 if (status
!= EXIT_SUCCESS
)
96 printf (_("Usage: %s OPTION... FILE...\n"), program_name
);
98 Shrink or extend the size of each FILE to the specified size\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\
107 emit_mandatory_arg_note ();
110 -c, --no-create do not create any files\n\
113 -o, --io-blocks treat SIZE as number of IO blocks instead of bytes\n\
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
);
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 ();
130 /* return true on success, false on error. */
132 do_ftruncate (int fd
, char const *fname
, off_t ssize
, off_t rsize
,
138 if ((block_mode
|| (rel_mode
&& rsize
< 0)) && fstat (fd
, &sb
) != 0)
140 error (0, errno
, _("cannot fstat %s"), quote (fname
));
145 off_t
const blksize
= ST_BLKSIZE (sb
);
146 if (ssize
< OFF_T_MIN
/ blksize
|| ssize
> OFF_T_MAX
/ blksize
)
149 _("overflow in %" PRIdMAX
150 " * %" PRIdMAX
" byte blocks for file %s"),
151 (intmax_t) ssize
, (intmax_t) blksize
,
166 if (usable_st_size (&sb
))
168 file_size
= sb
.st_size
;
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"),
180 file_size
= lseek (fd
, 0, SEEK_END
);
183 error (0, errno
, _("cannot get the size of %s"),
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"),
213 if (ssize
> OFF_T_MAX
- (off_t
)fsize
)
215 error (0, 0, _("overflow extending size of file %s"),
219 nsize
= fsize
+ ssize
;
227 if (ftruncate (fd
, nsize
) == -1) /* note updates mtime & ctime */
230 _("failed to truncate %s at %" PRIdMAX
" bytes"), quote (fname
),
239 main (int argc
, char **argv
)
241 bool got_size
= false;
243 off_t size
IF_LINT ( = 0);
245 rel_mode_t rel_mode
= rm_abs
;
246 int c
, fd
= -1, oflags
;
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)
274 /* skip any whitespace */
275 while (isspace (to_uchar (*optarg
)))
296 /* skip any whitespace */
297 while (isspace (to_uchar (*optarg
)))
299 if (*optarg
== '+' || *optarg
== '-')
303 error (0, 0, _("multiple relative modifiers specified"));
304 /* Note other combinations are flagged as invalid numbers */
305 usage (EXIT_FAILURE
);
309 if (parse_len (optarg
, &size
) == -1)
310 error (EXIT_FAILURE
, errno
, _("invalid number %s"),
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"));
318 case_GETOPT_HELP_CHAR
;
320 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
323 usage (EXIT_FAILURE
);
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 */
354 error (0, 0, _("missing file operand"));
355 usage (EXIT_FAILURE
);
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
;
368 int ref_fd
= open (ref_file
, O_RDONLY
);
371 off_t file_end
= lseek (ref_fd
, 0, SEEK_END
);
372 int saved_errno
= errno
;
373 close (ref_fd
); /* ignore failure */
375 file_size
= file_end
;
378 /* restore, in case close clobbered it. */
384 error (EXIT_FAILURE
, errno
, _("cannot get the size of %s"),
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"),
414 errors
|= !do_ftruncate (fd
, fname
, size
, rsize
, rel_mode
);
417 error (0, errno
, _("failed to close %s"), quote (fname
));
423 return errors
? EXIT_FAILURE
: EXIT_SUCCESS
;