truncate: handle the case in which off_t != intmax_t
[platform/upstream/coreutils.git] / src / truncate.c
1 /* truncate -- truncate or extend the length of files.
2    Copyright (C) 2008 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 static 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       fputs (_("\n\
128 SIZE is a number which may be followed by one of the following suffixes:\n\
129 KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
130 "), stdout);
131       fputs (_("\n\
132 SIZE may also be prefixed by one of the following modifying characters:\n\
133 `+' extend by, `-' reduce by, `<' at most, `>' at least,\n\
134 `/' round down to multiple of, `%' round up to multiple of.\n"), stdout);
135       fputs (_("\
136 \n\
137 Note that the -r and -s options are mutually exclusive.\n\
138 "), stdout);
139       emit_bug_reporting_address ();
140     }
141   exit (status);
142 }
143
144 /* return 1 on error, 0 on success */
145 static int
146 do_ftruncate (int fd, char const *fname, off_t ssize, rel_mode_t rel_mode)
147 {
148   struct stat sb;
149   off_t nsize;
150
151   if ((block_mode || rel_mode) && fstat (fd, &sb) != 0)
152     {
153       error (0, errno, _("cannot fstat %s"), quote (fname));
154       return 1;
155     }
156   if (block_mode)
157     {
158       size_t const blksize = ST_BLKSIZE (sb);
159       if (ssize < OFF_T_MIN / blksize || ssize > OFF_T_MAX / blksize)
160         {
161           error (0, 0,
162                  _("overflow in %" PRIdMAX
163                    " * %zu byte blocks for file %s"), ssize, blksize,
164                  quote (fname));
165           return 1;
166         }
167       ssize *= blksize;
168     }
169   if (rel_mode)
170     {
171       uintmax_t const fsize = sb.st_size;
172
173       if (sb.st_size < 0)
174         {
175           /* Complain only for a regular file, a directory,
176              or a shared memory object, as POSIX 1003.1-2004 specifies
177              ftruncate's behavior only for these file types.  */
178           if (S_ISREG (sb.st_mode) || S_ISDIR (sb.st_mode)
179               || S_TYPEISSHM (&sb))
180             {
181               /* overflow is the only reason I can think
182                  this would ever go negative for the above types */
183               error (0, 0, _("%s has unusable, apparently negative size"),
184                      quote (fname));
185               return 1;
186             }
187           return 0;
188         }
189
190       if (rel_mode == rm_min)
191         nsize = MAX (fsize, ssize);
192       else if (rel_mode == rm_max)
193         nsize = MIN (fsize, ssize);
194       else if (rel_mode == rm_rdn)
195         /* 0..ssize-1 -> 0 */
196         nsize = (fsize / ssize) * ssize;
197       else if (rel_mode == rm_rup)
198         /* 1..ssize -> ssize */
199         {
200           /* Here ssize>=1 && fsize>=0 */
201           uintmax_t const overflow = ((fsize + ssize - 1) / ssize) * ssize;
202           if (overflow > OFF_T_MAX)
203             {
204               error (0, 0, _("overflow rounding up size of file %s"),
205                      quote (fname));
206               return 1;
207             }
208           nsize = overflow;
209         }
210       else
211         {
212           if (ssize > OFF_T_MAX - (off_t)fsize)
213             {
214               error (0, 0, _("overflow extending size of file %s"),
215                      quote (fname));
216               return 1;
217             }
218           nsize = fsize + ssize;
219         }
220     }
221   else
222     nsize = ssize;
223   if (nsize < 0)
224     nsize = 0;
225
226   if (ftruncate (fd, nsize) == -1)      /* note updates mtime & ctime */
227     {
228       /* Complain only when ftruncate fails on a regular file, a
229          directory, or a shared memory object, as POSIX 1003.1-2004
230          specifies ftruncate's behavior only for these file types.
231          For example, do not complain when Linux 2.4 ftruncate
232          fails on /dev/fd0.  */
233       int const ftruncate_errno = errno;
234       if (fstat (fd, &sb) != 0)
235         {
236           error (0, errno, _("cannot fstat %s"), quote (fname));
237           return 1;
238         }
239       else if (S_ISREG (sb.st_mode) || S_ISDIR (sb.st_mode)
240                || S_TYPEISSHM (&sb))
241         {
242           error (0, ftruncate_errno,
243                  _("truncating %s at %" PRIdMAX " bytes"), quote (fname),
244                  nsize);
245           return 1;
246         }
247       return 0;
248     }
249
250   return 0;
251 }
252
253 int
254 main (int argc, char **argv)
255 {
256   bool got_size = false;
257   off_t size IF_LINT (= 0);
258   rel_mode_t rel_mode = rm_abs;
259   mode_t omode;
260   int c, errors = 0, fd = -1, oflags;
261   char const *fname;
262
263   initialize_main (&argc, &argv);
264   set_program_name (argv[0]);
265   setlocale (LC_ALL, "");
266   bindtextdomain (PACKAGE, LOCALEDIR);
267   textdomain (PACKAGE);
268
269   atexit (close_stdout);
270
271   while ((c = getopt_long (argc, argv, "cor:s:", longopts, NULL)) != -1)
272     {
273       switch (c)
274         {
275         case 'c':
276           no_create = true;
277           break;
278
279         case 'o':
280           block_mode = true;
281           break;
282
283         case 'r':
284           ref_file = optarg;
285           break;
286
287         case 's':
288           switch (*optarg)
289             {
290             case '<':
291               rel_mode = rm_max;
292               optarg++;
293               break;
294             case '>':
295               rel_mode = rm_min;
296               optarg++;
297               break;
298             case '/':
299               rel_mode = rm_rdn;
300               optarg++;
301               break;
302             case '%':
303               rel_mode = rm_rup;
304               optarg++;
305               break;
306             }
307           if (*optarg == '+' || *optarg == '-')
308             {
309               if (rel_mode)
310                 {
311                   error (0, 0, _("multiple relative modifiers specified"));
312                   /* Note other combinations are flagged as invalid numbers */
313                   usage (EXIT_FAILURE);
314                 }
315               rel_mode = rm_rel;
316             }
317           if (parse_len (optarg, &size) == -1)
318             error (EXIT_FAILURE, errno, _("invalid number %s"),
319                    quote (optarg));
320           /* Rounding to multiple of 0 is nonsensical */
321           if ((rel_mode == rm_rup || rel_mode == rm_rdn) && size == 0)
322             error (EXIT_FAILURE, 0, _("division by zero"));
323           got_size = true;
324           break;
325
326         case_GETOPT_HELP_CHAR;
327
328         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
329
330         default:
331           usage (EXIT_FAILURE);
332         }
333     }
334
335   argv += optind;
336   argc -= optind;
337
338   /* must specify either size or reference file */
339   if ((ref_file && got_size) || (!ref_file && !got_size))
340     {
341       error (0, 0, _("you must specify one of %s or %s"),
342              quote_n (0, "--size"), quote_n (1, "--reference"));
343       usage (EXIT_FAILURE);
344     }
345   /* block_mode without size is not valid */
346   if (block_mode && !got_size)
347     {
348       error (0, 0, _("%s was specified but %s was not"),
349              quote_n (0, "--io-blocks"), quote_n (1, "--size"));
350       usage (EXIT_FAILURE);
351     }
352   /* must specify at least 1 file */
353   if (argc < 1)
354     {
355       error (0, 0, _("missing file operand"));
356       usage (EXIT_FAILURE);
357     }
358
359   if (ref_file)
360     {
361       struct stat sb;
362       if (stat (ref_file, &sb) != 0)
363         error (EXIT_FAILURE, errno, _("cannot stat %s"), quote (ref_file));
364       size = sb.st_size;
365     }
366
367   oflags = O_WRONLY | (no_create ? 0 : O_CREAT) | O_NONBLOCK;
368   omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
369
370   while ((fname = *argv++) != NULL)
371     {
372       if ((fd = open (fname, oflags, omode)) == -1)
373         {
374           /* `truncate -s0 -c no-such-file`  shouldn't gen error
375              `truncate -s0 no-such-dir/file` should gen ENOENT error
376              `truncate -s0 no-such-dir/` should gen EISDIR error
377              `truncate -s0 .` should gen EISDIR error */
378           if (!(no_create && errno == ENOENT))
379             {
380               int const open_errno = errno;
381               struct stat sb;
382               if (stat (fname, &sb) == 0)
383                 {
384                   /* Complain only for a regular file, a directory,
385                      or a shared memory object, as POSIX 1003.1-2004 specifies
386                      ftruncate's behavior only for these file types.  */
387                   if (!S_ISREG (sb.st_mode) && !S_ISDIR (sb.st_mode)
388                       && !S_TYPEISSHM (&sb))
389                     continue;
390                 }
391               error (0, open_errno, _("cannot open %s for writing"),
392                      quote (fname));
393               errors++;
394             }
395           continue;
396         }
397
398
399       if (fd != -1)
400         {
401           errors += do_ftruncate (fd, fname, size, rel_mode);
402           if (close (fd) != 0)
403             {
404               error (0, errno, _("closing %s"), quote (fname));
405               errors++;
406             }
407         }
408     }
409
410   return errors ? EXIT_FAILURE : EXIT_SUCCESS;
411 }
412
413 /*
414  * Local variables:
415  *  indent-tabs-mode: nil
416  * End:
417  */