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. */
26 #include <sys/types.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
},
61 { rm_abs
= 0, rm_rel
, rm_min
, rm_max
, rm_rdn
, rm_rup
} rel_mode_t
;
66 if (status
!= EXIT_SUCCESS
)
70 printf (_("Usage: %s OPTION... FILE...\n"), program_name
);
72 Shrink or extend the size of each FILE to the specified size\n\
74 A FILE argument that does not exist is created.\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\
81 emit_mandatory_arg_note ();
84 -c, --no-create do not create any files\n\
87 -o, --io-blocks treat SIZE as number of IO blocks instead of bytes\n\
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
);
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
);
104 /* return true on success, false on error. */
106 do_ftruncate (int fd
, char const *fname
, off_t ssize
, off_t rsize
,
112 if ((block_mode
|| (rel_mode
&& rsize
< 0)) && fstat (fd
, &sb
) != 0)
114 error (0, errno
, _("cannot fstat %s"), quoteaf (fname
));
119 ptrdiff_t blksize
= ST_BLKSIZE (sb
);
120 intmax_t ssize0
= ssize
;
121 if (INT_MULTIPLY_WRAPV (ssize
, blksize
, &ssize
))
124 _("overflow in %" PRIdMAX
125 " * %" PRIdPTR
" byte blocks for file %s"),
126 ssize0
, blksize
, quoteaf (fname
));
138 if (usable_st_size (&sb
))
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"),
152 fsize
= lseek (fd
, 0, SEEK_END
);
155 error (0, errno
, _("cannot get the size of %s"),
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
;
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"),
190 if (ftruncate (fd
, nsize
) == -1) /* note updates mtime & ctime */
193 _("failed to truncate %s at %" PRIdMAX
" bytes"), quoteaf (fname
),
202 main (int argc
, char **argv
)
204 bool got_size
= false;
206 off_t size
IF_LINT ( = 0);
208 rel_mode_t rel_mode
= rm_abs
;
209 int c
, fd
= -1, oflags
;
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)
237 /* skip any whitespace */
238 while (isspace (to_uchar (*optarg
)))
259 /* skip any whitespace */
260 while (isspace (to_uchar (*optarg
)))
262 if (*optarg
== '+' || *optarg
== '-')
266 error (0, 0, _("multiple relative modifiers specified"));
267 /* Note other combinations are flagged as invalid numbers */
268 usage (EXIT_FAILURE
);
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"));
282 case_GETOPT_HELP_CHAR
;
284 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
287 usage (EXIT_FAILURE
);
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 */
318 error (0, 0, _("missing file operand"));
319 usage (EXIT_FAILURE
);
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
;
332 int ref_fd
= open (ref_file
, O_RDONLY
);
335 off_t file_end
= lseek (ref_fd
, 0, SEEK_END
);
336 int saved_errno
= errno
;
337 close (ref_fd
); /* ignore failure */
339 file_size
= file_end
;
342 /* restore, in case close clobbered it. */
348 die (EXIT_FAILURE
, errno
, _("cannot get the size of %s"),
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"),
378 errors
|= !do_ftruncate (fd
, fname
, size
, rsize
, rel_mode
);
381 error (0, errno
, _("failed to close %s"), quoteaf (fname
));
387 return errors
? EXIT_FAILURE
: EXIT_SUCCESS
;