maint: update all FSF copyright year lists to include 2010
[platform/upstream/coreutils.git] / src / truncate.c
1 /* truncate -- truncate or extend the length of files.
2    Copyright (C) 2008-2010 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>.  */
16
17 /* Written by Pádraig Brady
18
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.
22
23    Note if !defined(HAVE_FTRUNCATE) then the --skip-ftruncate configure flag
24    was specified or we're in a mingw environment. In these cases gnulib
25    emulation will be used and GNULIB_FTRUNCATE is defined. Note if emulation
26    can't even be provided ftruncate() will return EIO.  */
27
28 #include <config.h>             /* sets _FILE_OFFSET_BITS=64 etc. */
29 #include <stdio.h>
30 #include <getopt.h>
31 #include <sys/types.h>
32
33 #include "system.h"
34 #include "error.h"
35 #include "posixver.h"
36 #include "quote.h"
37 #include "xstrtol.h"
38
39 /* The official name of this program (e.g., no `g' prefix).  */
40 #define PROGRAM_NAME "truncate"
41
42 #define AUTHORS proper_name_utf8 ("Padraig Brady", "P\303\241draig Brady")
43
44 /* (-c) If true, don't create if not already there */
45 static bool no_create;
46
47 /* (-o) If true, --size refers to blocks not bytes */
48 static bool block_mode;
49
50 /* (-r) Reference file to use size from */
51 static char const *ref_file;
52
53 static struct option const longopts[] =
54 {
55   {"no-create", no_argument, NULL, 'c'},
56   {"io-blocks", no_argument, NULL, 'o'},
57   {"reference", required_argument, NULL, 'r'},
58   {"size", required_argument, NULL, 's'},
59   {GETOPT_HELP_OPTION_DECL},
60   {GETOPT_VERSION_OPTION_DECL},
61   {NULL, 0, NULL, 0}
62 };
63
64 typedef enum
65 { rm_abs = 0, rm_rel, rm_min, rm_max, rm_rdn, rm_rup } rel_mode_t;
66
67 /* Set size to the value of STR, interpreted as a decimal integer,
68    optionally multiplied by various values.
69    Return -1 on error, 0 on success.
70
71    This supports dd BLOCK size suffixes + lowercase g,t,m for bsd compat
72    Note we don't support dd's b=512, c=1, w=2 or 21x512MiB formats.  */
73 static int
74 parse_len (char const *str, off_t *size)
75 {
76   enum strtol_error e;
77   intmax_t tmp_size;
78   e = xstrtoimax (str, NULL, 10, &tmp_size, "EgGkKmMPtTYZ0");
79   if (e == LONGINT_OK
80       && !(OFF_T_MIN <= tmp_size && tmp_size <= OFF_T_MAX))
81     e = LONGINT_OVERFLOW;
82
83   if (e == LONGINT_OK)
84     {
85       errno = 0;
86       *size = tmp_size;
87       return 0;
88     }
89
90   errno = (e == LONGINT_OVERFLOW ? EOVERFLOW : 0);
91   return -1;
92 }
93
94 void
95 usage (int status)
96 {
97   if (status != EXIT_SUCCESS)
98     fprintf (stderr, _("Try `%s --help' for more information.\n"),
99              program_name);
100   else
101     {
102       printf (_("Usage: %s OPTION... FILE...\n"), program_name);
103       fputs (_("\
104 Shrink or extend the size of each FILE to the specified size\n\
105 \n\
106 A FILE argument that does not exist is created.\n\
107 \n\
108 If a FILE is larger than the specified size, the extra data is lost.\n\
109 If a FILE is shorter, it is extended and the extended part (hole)\n\
110 reads as zero bytes.\n\
111 \n\
112 "), stdout);
113       fputs (_("\
114 Mandatory arguments to long options are mandatory for short options too.\n\
115 "), stdout);
116       fputs (_("\
117   -c, --no-create        do not create any files\n\
118 "), stdout);
119       fputs (_("\
120   -o, --io-blocks        Treat SIZE as number of IO blocks instead of bytes\n\
121 "), stdout);
122       fputs (_("\
123   -r, --reference=FILE   use this FILE's size\n\
124   -s, --size=SIZE        use this SIZE\n"), stdout);
125       fputs (HELP_OPTION_DESCRIPTION, stdout);
126       fputs (VERSION_OPTION_DESCRIPTION, stdout);
127       emit_size_note ();
128       fputs (_("\n\
129 SIZE may also be prefixed by one of the following modifying characters:\n\
130 `+' extend by, `-' reduce by, `<' at most, `>' at least,\n\
131 `/' round down to multiple of, `%' round up to multiple of.\n"), stdout);
132       fputs (_("\
133 \n\
134 Note that the -r and -s options are mutually exclusive.\n\
135 "), stdout);
136       emit_ancillary_info ();
137     }
138   exit (status);
139 }
140
141 /* return 1 on error, 0 on success */
142 static int
143 do_ftruncate (int fd, char const *fname, off_t ssize, rel_mode_t rel_mode)
144 {
145   struct stat sb;
146   off_t nsize;
147
148   if ((block_mode || rel_mode) && fstat (fd, &sb) != 0)
149     {
150       error (0, errno, _("cannot fstat %s"), quote (fname));
151       return 1;
152     }
153   if (block_mode)
154     {
155       off_t const blksize = ST_BLKSIZE (sb);
156       if (ssize < OFF_T_MIN / blksize || ssize > OFF_T_MAX / blksize)
157         {
158           error (0, 0,
159                  _("overflow in %" PRIdMAX
160                    " * %" PRIdMAX " byte blocks for file %s"),
161                  (intmax_t) ssize, (intmax_t) blksize,
162                  quote (fname));
163           return 1;
164         }
165       ssize *= blksize;
166     }
167   if (rel_mode)
168     {
169       uintmax_t const fsize = sb.st_size;
170
171       if (sb.st_size < 0)
172         {
173           /* Complain only for a regular file, a directory,
174              or a shared memory object, as POSIX 1003.1-2004 specifies
175              ftruncate's behavior only for these file types.  */
176           if (S_ISREG (sb.st_mode) || S_ISDIR (sb.st_mode)
177               || S_TYPEISSHM (&sb))
178             {
179               /* overflow is the only reason I can think
180                  this would ever go negative for the above types */
181               error (0, 0, _("%s has unusable, apparently negative size"),
182                      quote (fname));
183               return 1;
184             }
185           return 0;
186         }
187
188       if (rel_mode == rm_min)
189         nsize = MAX (fsize, (uintmax_t) ssize);
190       else if (rel_mode == rm_max)
191         nsize = MIN (fsize, (uintmax_t) ssize);
192       else if (rel_mode == rm_rdn)
193         /* 0..ssize-1 -> 0 */
194         nsize = (fsize / ssize) * ssize;
195       else if (rel_mode == rm_rup)
196         /* 1..ssize -> ssize */
197         {
198           /* Here ssize>=1 && fsize>=0 */
199           uintmax_t const overflow = ((fsize + ssize - 1) / ssize) * ssize;
200           if (overflow > OFF_T_MAX)
201             {
202               error (0, 0, _("overflow rounding up size of file %s"),
203                      quote (fname));
204               return 1;
205             }
206           nsize = overflow;
207         }
208       else
209         {
210           if (ssize > OFF_T_MAX - (off_t)fsize)
211             {
212               error (0, 0, _("overflow extending size of file %s"),
213                      quote (fname));
214               return 1;
215             }
216           nsize = fsize + ssize;
217         }
218     }
219   else
220     nsize = ssize;
221   if (nsize < 0)
222     nsize = 0;
223
224   if (ftruncate (fd, nsize) == -1)      /* note updates mtime & ctime */
225     {
226       /* Complain only when ftruncate fails on a regular file, a
227          directory, or a shared memory object, as POSIX 1003.1-2004
228          specifies ftruncate's behavior only for these file types.
229          For example, do not complain when Linux kernel 2.4 ftruncate
230          fails on /dev/fd0.  */
231       int const ftruncate_errno = errno;
232       if (fstat (fd, &sb) != 0)
233         {
234           error (0, errno, _("cannot fstat %s"), quote (fname));
235           return 1;
236         }
237       else if (S_ISREG (sb.st_mode) || S_ISDIR (sb.st_mode)
238                || S_TYPEISSHM (&sb))
239         {
240           error (0, ftruncate_errno,
241                  _("truncating %s at %" PRIdMAX " bytes"), quote (fname),
242                  (intmax_t) nsize);
243           return 1;
244         }
245       return 0;
246     }
247
248   return 0;
249 }
250
251 int
252 main (int argc, char **argv)
253 {
254   bool got_size = false;
255   off_t size IF_LINT (= 0);
256   rel_mode_t rel_mode = rm_abs;
257   mode_t omode;
258   int c, errors = 0, fd = -1, oflags;
259   char const *fname;
260
261   initialize_main (&argc, &argv);
262   set_program_name (argv[0]);
263   setlocale (LC_ALL, "");
264   bindtextdomain (PACKAGE, LOCALEDIR);
265   textdomain (PACKAGE);
266
267   atexit (close_stdout);
268
269   while ((c = getopt_long (argc, argv, "cor:s:", longopts, NULL)) != -1)
270     {
271       switch (c)
272         {
273         case 'c':
274           no_create = true;
275           break;
276
277         case 'o':
278           block_mode = true;
279           break;
280
281         case 'r':
282           ref_file = optarg;
283           break;
284
285         case 's':
286           /* skip any whitespace */
287           while (isspace (to_uchar (*optarg)))
288             optarg++;
289           switch (*optarg)
290             {
291             case '<':
292               rel_mode = rm_max;
293               optarg++;
294               break;
295             case '>':
296               rel_mode = rm_min;
297               optarg++;
298               break;
299             case '/':
300               rel_mode = rm_rdn;
301               optarg++;
302               break;
303             case '%':
304               rel_mode = rm_rup;
305               optarg++;
306               break;
307             }
308           /* skip any whitespace */
309           while (isspace (to_uchar (*optarg)))
310             optarg++;
311           if (*optarg == '+' || *optarg == '-')
312             {
313               if (rel_mode)
314                 {
315                   error (0, 0, _("multiple relative modifiers specified"));
316                   /* Note other combinations are flagged as invalid numbers */
317                   usage (EXIT_FAILURE);
318                 }
319               rel_mode = rm_rel;
320             }
321           if (parse_len (optarg, &size) == -1)
322             error (EXIT_FAILURE, errno, _("invalid number %s"),
323                    quote (optarg));
324           /* Rounding to multiple of 0 is nonsensical */
325           if ((rel_mode == rm_rup || rel_mode == rm_rdn) && size == 0)
326             error (EXIT_FAILURE, 0, _("division by zero"));
327           got_size = true;
328           break;
329
330         case_GETOPT_HELP_CHAR;
331
332         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
333
334         default:
335           usage (EXIT_FAILURE);
336         }
337     }
338
339   argv += optind;
340   argc -= optind;
341
342   /* must specify either size or reference file */
343   if ((ref_file && got_size) || (!ref_file && !got_size))
344     {
345       error (0, 0, _("you must specify one of %s or %s"),
346              quote_n (0, "--size"), quote_n (1, "--reference"));
347       usage (EXIT_FAILURE);
348     }
349   /* block_mode without size is not valid */
350   if (block_mode && !got_size)
351     {
352       error (0, 0, _("%s was specified but %s was not"),
353              quote_n (0, "--io-blocks"), quote_n (1, "--size"));
354       usage (EXIT_FAILURE);
355     }
356   /* must specify at least 1 file */
357   if (argc < 1)
358     {
359       error (0, 0, _("missing file operand"));
360       usage (EXIT_FAILURE);
361     }
362
363   if (ref_file)
364     {
365       struct stat sb;
366       if (stat (ref_file, &sb) != 0)
367         error (EXIT_FAILURE, errno, _("cannot stat %s"), quote (ref_file));
368       size = sb.st_size;
369     }
370
371   oflags = O_WRONLY | (no_create ? 0 : O_CREAT) | O_NONBLOCK;
372   omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
373
374   while ((fname = *argv++) != NULL)
375     {
376       if ((fd = open (fname, oflags, omode)) == -1)
377         {
378           /* `truncate -s0 -c no-such-file`  shouldn't gen error
379              `truncate -s0 no-such-dir/file` should gen ENOENT error
380              `truncate -s0 no-such-dir/` should gen EISDIR error
381              `truncate -s0 .` should gen EISDIR error */
382           if (!(no_create && errno == ENOENT))
383             {
384               int const open_errno = errno;
385               struct stat sb;
386               if (stat (fname, &sb) == 0)
387                 {
388                   /* Complain only for a regular file, a directory,
389                      or a shared memory object, as POSIX 1003.1-2004 specifies
390                      ftruncate's behavior only for these file types.  */
391                   if (!S_ISREG (sb.st_mode) && !S_ISDIR (sb.st_mode)
392                       && !S_TYPEISSHM (&sb))
393                     continue;
394                 }
395               error (0, open_errno, _("cannot open %s for writing"),
396                      quote (fname));
397               errors++;
398             }
399           continue;
400         }
401
402
403       if (fd != -1)
404         {
405           errors += do_ftruncate (fd, fname, size, rel_mode);
406           if (close (fd) != 0)
407             {
408               error (0, errno, _("closing %s"), quote (fname));
409               errors++;
410             }
411         }
412     }
413
414   return errors ? EXIT_FAILURE : EXIT_SUCCESS;
415 }