1 /* truncate -- truncate or extend the length of files.
2 Copyright (C) 2008-2016 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"
32 #include "xdectoint.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
;
65 if (status
!= EXIT_SUCCESS
)
69 printf (_("Usage: %s OPTION... FILE...\n"), program_name
);
71 Shrink or extend the size of each FILE to the specified size\n\
73 A FILE argument that does not exist is created.\n\
75 If a FILE is larger than the specified size, the extra data is lost.\n\
76 If a FILE is shorter, it is extended and the extended part (hole)\n\
77 reads as zero bytes.\n\
80 emit_mandatory_arg_note ();
83 -c, --no-create do not create any files\n\
86 -o, --io-blocks treat SIZE as number of IO blocks instead of bytes\n\
89 -r, --reference=RFILE base size on RFILE\n\
90 -s, --size=SIZE set or adjust the file size by SIZE bytes\n"), stdout
);
91 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
92 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
95 SIZE may also be prefixed by one of the following modifying characters:\n\
96 '+' extend by, '-' reduce by, '<' at most, '>' at least,\n\
97 '/' round down to multiple of, '%' round up to multiple of.\n"), stdout
);
98 emit_ancillary_info (PROGRAM_NAME
);
103 /* return true on success, false on error. */
105 do_ftruncate (int fd
, char const *fname
, off_t ssize
, off_t rsize
,
111 if ((block_mode
|| (rel_mode
&& rsize
< 0)) && fstat (fd
, &sb
) != 0)
113 error (0, errno
, _("cannot fstat %s"), quoteaf (fname
));
118 off_t
const blksize
= ST_BLKSIZE (sb
);
119 if (ssize
< OFF_T_MIN
/ blksize
|| ssize
> OFF_T_MAX
/ blksize
)
122 _("overflow in %" PRIdMAX
123 " * %" PRIdMAX
" byte blocks for file %s"),
124 (intmax_t) ssize
, (intmax_t) blksize
,
139 if (usable_st_size (&sb
))
141 file_size
= sb
.st_size
;
144 /* Sanity check. Overflow is the only reason I can think
145 this would ever go negative. */
146 error (0, 0, _("%s has unusable, apparently negative size"),
153 file_size
= lseek (fd
, 0, SEEK_END
);
156 error (0, errno
, _("cannot get the size of %s"),
164 if (rel_mode
== rm_min
)
165 nsize
= MAX (fsize
, (uintmax_t) ssize
);
166 else if (rel_mode
== rm_max
)
167 nsize
= MIN (fsize
, (uintmax_t) ssize
);
168 else if (rel_mode
== rm_rdn
)
169 /* 0..ssize-1 -> 0 */
170 nsize
= (fsize
/ ssize
) * ssize
;
171 else if (rel_mode
== rm_rup
)
172 /* 1..ssize -> ssize */
174 /* Here ssize>=1 && fsize>=0 */
175 uintmax_t const overflow
= ((fsize
+ ssize
- 1) / ssize
) * ssize
;
176 if (overflow
> OFF_T_MAX
)
178 error (0, 0, _("overflow rounding up size of file %s"),
186 if (ssize
> OFF_T_MAX
- (off_t
)fsize
)
188 error (0, 0, _("overflow extending size of file %s"),
192 nsize
= fsize
+ ssize
;
200 if (ftruncate (fd
, nsize
) == -1) /* note updates mtime & ctime */
203 _("failed to truncate %s at %" PRIdMAX
" bytes"), quoteaf (fname
),
212 main (int argc
, char **argv
)
214 bool got_size
= false;
216 off_t size
IF_LINT ( = 0);
218 rel_mode_t rel_mode
= rm_abs
;
219 int c
, fd
= -1, oflags
;
222 initialize_main (&argc
, &argv
);
223 set_program_name (argv
[0]);
224 setlocale (LC_ALL
, "");
225 bindtextdomain (PACKAGE
, LOCALEDIR
);
226 textdomain (PACKAGE
);
228 atexit (close_stdout
);
230 while ((c
= getopt_long (argc
, argv
, "cor:s:", longopts
, NULL
)) != -1)
247 /* skip any whitespace */
248 while (isspace (to_uchar (*optarg
)))
269 /* skip any whitespace */
270 while (isspace (to_uchar (*optarg
)))
272 if (*optarg
== '+' || *optarg
== '-')
276 error (0, 0, _("multiple relative modifiers specified"));
277 /* Note other combinations are flagged as invalid numbers */
278 usage (EXIT_FAILURE
);
282 /* Support dd BLOCK size suffixes + lowercase g,t,m for bsd compat.
283 Note we don't support dd's b=512, c=1, w=2 or 21x512MiB formats. */
284 size
= xdectoimax (optarg
, OFF_T_MIN
, OFF_T_MAX
, "EgGkKmMPtTYZ0",
285 _("Invalid number"), 0);
286 /* Rounding to multiple of 0 is nonsensical */
287 if ((rel_mode
== rm_rup
|| rel_mode
== rm_rdn
) && size
== 0)
288 error (EXIT_FAILURE
, 0, _("division by zero"));
292 case_GETOPT_HELP_CHAR
;
294 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
297 usage (EXIT_FAILURE
);
304 /* must specify either size or reference file */
305 if (!ref_file
&& !got_size
)
307 error (0, 0, _("you must specify either %s or %s"),
308 quote_n (0, "--size"), quote_n (1, "--reference"));
309 usage (EXIT_FAILURE
);
311 /* must specify a relative size with a reference file */
312 if (ref_file
&& got_size
&& !rel_mode
)
314 error (0, 0, _("you must specify a relative %s with %s"),
315 quote_n (0, "--size"), quote_n (1, "--reference"));
316 usage (EXIT_FAILURE
);
318 /* block_mode without size is not valid */
319 if (block_mode
&& !got_size
)
321 error (0, 0, _("%s was specified but %s was not"),
322 quote_n (0, "--io-blocks"), quote_n (1, "--size"));
323 usage (EXIT_FAILURE
);
325 /* must specify at least 1 file */
328 error (0, 0, _("missing file operand"));
329 usage (EXIT_FAILURE
);
335 off_t file_size
= -1;
336 if (stat (ref_file
, &sb
) != 0)
337 error (EXIT_FAILURE
, errno
, _("cannot stat %s"), quoteaf (ref_file
));
338 if (usable_st_size (&sb
))
339 file_size
= sb
.st_size
;
342 int ref_fd
= open (ref_file
, O_RDONLY
);
345 off_t file_end
= lseek (ref_fd
, 0, SEEK_END
);
346 int saved_errno
= errno
;
347 close (ref_fd
); /* ignore failure */
349 file_size
= file_end
;
352 /* restore, in case close clobbered it. */
358 error (EXIT_FAILURE
, errno
, _("cannot get the size of %s"),
366 oflags
= O_WRONLY
| (no_create
? 0 : O_CREAT
) | O_NONBLOCK
;
368 while ((fname
= *argv
++) != NULL
)
370 if ((fd
= open (fname
, oflags
, MODE_RW_UGO
)) == -1)
372 /* 'truncate -s0 -c no-such-file' shouldn't gen error
373 'truncate -s0 no-such-dir/file' should gen ENOENT error
374 'truncate -s0 no-such-dir/' should gen EISDIR error
375 'truncate -s0 .' should gen EISDIR error */
376 if (!(no_create
&& errno
== ENOENT
))
378 error (0, errno
, _("cannot open %s for writing"),
388 errors
|= !do_ftruncate (fd
, fname
, size
, rsize
, rel_mode
);
391 error (0, errno
, _("failed to close %s"), quoteaf (fname
));
397 return errors
? EXIT_FAILURE
: EXIT_SUCCESS
;