1 /* truncate -- truncate or extend the length of files.
2 Copyright (C) 2008-2010 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>
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
)
93 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
97 printf (_("Usage: %s OPTION... FILE...\n"), program_name
);
99 Shrink or extend the size of each FILE to the specified size\n\
101 A FILE argument that does not exist is created.\n\
103 If a FILE is larger than the specified size, the extra data is lost.\n\
104 If a FILE is shorter, it is extended and the extended part (hole)\n\
105 reads as zero bytes.\n\
109 Mandatory arguments to long options are mandatory for short options too.\n\
112 -c, --no-create do not create any files\n\
115 -o, --io-blocks treat SIZE as number of IO blocks instead of bytes\n\
118 -r, --reference=FILE use this FILE's size\n\
119 -s, --size=SIZE use this SIZE\n"), stdout
);
120 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
121 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
124 SIZE may also be prefixed by one of the following modifying characters:\n\
125 `+' extend by, `-' reduce by, `<' at most, `>' at least,\n\
126 `/' round down to multiple of, `%' round up to multiple of.\n"), stdout
);
129 Note that the -r and -s options are mutually exclusive.\n\
131 emit_ancillary_info ();
136 /* return 1 on error, 0 on success */
138 do_ftruncate (int fd
, char const *fname
, off_t ssize
, rel_mode_t rel_mode
)
143 if ((block_mode
|| rel_mode
) && fstat (fd
, &sb
) != 0)
145 error (0, errno
, _("cannot fstat %s"), quote (fname
));
150 off_t
const blksize
= ST_BLKSIZE (sb
);
151 if (ssize
< OFF_T_MIN
/ blksize
|| ssize
> OFF_T_MAX
/ blksize
)
154 _("overflow in %" PRIdMAX
155 " * %" PRIdMAX
" byte blocks for file %s"),
156 (intmax_t) ssize
, (intmax_t) blksize
,
164 uintmax_t const fsize
= sb
.st_size
;
168 /* Complain only for a regular file, a directory,
169 or a shared memory object, as POSIX 1003.1-2004 specifies
170 ftruncate's behavior only for these file types. */
171 if (S_ISREG (sb
.st_mode
) || S_ISDIR (sb
.st_mode
)
172 || S_TYPEISSHM (&sb
))
174 /* overflow is the only reason I can think
175 this would ever go negative for the above types */
176 error (0, 0, _("%s has unusable, apparently negative size"),
183 if (rel_mode
== rm_min
)
184 nsize
= MAX (fsize
, (uintmax_t) ssize
);
185 else if (rel_mode
== rm_max
)
186 nsize
= MIN (fsize
, (uintmax_t) ssize
);
187 else if (rel_mode
== rm_rdn
)
188 /* 0..ssize-1 -> 0 */
189 nsize
= (fsize
/ ssize
) * ssize
;
190 else if (rel_mode
== rm_rup
)
191 /* 1..ssize -> ssize */
193 /* Here ssize>=1 && fsize>=0 */
194 uintmax_t const overflow
= ((fsize
+ ssize
- 1) / ssize
) * ssize
;
195 if (overflow
> OFF_T_MAX
)
197 error (0, 0, _("overflow rounding up size of file %s"),
205 if (ssize
> OFF_T_MAX
- (off_t
)fsize
)
207 error (0, 0, _("overflow extending size of file %s"),
211 nsize
= fsize
+ ssize
;
219 if (ftruncate (fd
, nsize
) == -1) /* note updates mtime & ctime */
221 /* Complain only when ftruncate fails on a regular file, a
222 directory, or a shared memory object, as POSIX 1003.1-2004
223 specifies ftruncate's behavior only for these file types.
224 For example, do not complain when Linux kernel 2.4 ftruncate
225 fails on /dev/fd0. */
226 int const ftruncate_errno
= errno
;
227 if (fstat (fd
, &sb
) != 0)
229 error (0, errno
, _("cannot fstat %s"), quote (fname
));
232 else if (S_ISREG (sb
.st_mode
) || S_ISDIR (sb
.st_mode
)
233 || S_TYPEISSHM (&sb
))
235 error (0, ftruncate_errno
,
236 _("truncating %s at %" PRIdMAX
" bytes"), quote (fname
),
247 main (int argc
, char **argv
)
249 bool got_size
= false;
250 off_t size
IF_LINT (= 0);
251 rel_mode_t rel_mode
= rm_abs
;
253 int c
, errors
= 0, fd
= -1, oflags
;
256 initialize_main (&argc
, &argv
);
257 set_program_name (argv
[0]);
258 setlocale (LC_ALL
, "");
259 bindtextdomain (PACKAGE
, LOCALEDIR
);
260 textdomain (PACKAGE
);
262 atexit (close_stdout
);
264 while ((c
= getopt_long (argc
, argv
, "cor:s:", longopts
, NULL
)) != -1)
281 /* skip any whitespace */
282 while (isspace (to_uchar (*optarg
)))
303 /* skip any whitespace */
304 while (isspace (to_uchar (*optarg
)))
306 if (*optarg
== '+' || *optarg
== '-')
310 error (0, 0, _("multiple relative modifiers specified"));
311 /* Note other combinations are flagged as invalid numbers */
312 usage (EXIT_FAILURE
);
316 if (parse_len (optarg
, &size
) == -1)
317 error (EXIT_FAILURE
, errno
, _("invalid number %s"),
319 /* Rounding to multiple of 0 is nonsensical */
320 if ((rel_mode
== rm_rup
|| rel_mode
== rm_rdn
) && size
== 0)
321 error (EXIT_FAILURE
, 0, _("division by zero"));
325 case_GETOPT_HELP_CHAR
;
327 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
330 usage (EXIT_FAILURE
);
337 /* must specify either size or reference file */
338 if ((ref_file
&& got_size
) || (!ref_file
&& !got_size
))
340 error (0, 0, _("you must specify one of %s or %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 if (stat (ref_file
, &sb
) != 0)
362 error (EXIT_FAILURE
, errno
, _("cannot stat %s"), quote (ref_file
));
366 oflags
= O_WRONLY
| (no_create
? 0 : O_CREAT
) | O_NONBLOCK
;
367 omode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
| S_IWOTH
;
369 while ((fname
= *argv
++) != NULL
)
371 if ((fd
= open (fname
, oflags
, omode
)) == -1)
373 /* `truncate -s0 -c no-such-file` shouldn't gen error
374 `truncate -s0 no-such-dir/file` should gen ENOENT error
375 `truncate -s0 no-such-dir/` should gen EISDIR error
376 `truncate -s0 .` should gen EISDIR error */
377 if (!(no_create
&& errno
== ENOENT
))
379 int const open_errno
= errno
;
381 if (stat (fname
, &sb
) == 0)
383 /* Complain only for a regular file, a directory,
384 or a shared memory object, as POSIX 1003.1-2004 specifies
385 ftruncate's behavior only for these file types. */
386 if (!S_ISREG (sb
.st_mode
) && !S_ISDIR (sb
.st_mode
)
387 && !S_TYPEISSHM (&sb
))
390 error (0, open_errno
, _("cannot open %s for writing"),
400 errors
+= do_ftruncate (fd
, fname
, size
, rel_mode
);
403 error (0, errno
, _("closing %s"), quote (fname
));
409 return errors
? EXIT_FAILURE
: EXIT_SUCCESS
;