maint: improve error messages upon failed read, write, access, close
[platform/upstream/coreutils.git] / src / touch.c
1 /* touch -- change modification and access times of files
2    Copyright (C) 1987-2013 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 Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie,
18    and Randy Smith. */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <getopt.h>
23 #include <sys/types.h>
24 #include <assert.h>
25
26 #include "system.h"
27 #include "argmatch.h"
28 #include "error.h"
29 #include "fd-reopen.h"
30 #include "parse-datetime.h"
31 #include "posixtm.h"
32 #include "posixver.h"
33 #include "quote.h"
34 #include "stat-time.h"
35 #include "utimens.h"
36
37 /* The official name of this program (e.g., no 'g' prefix).  */
38 #define PROGRAM_NAME "touch"
39
40 #define AUTHORS \
41   proper_name ("Paul Rubin"), \
42   proper_name ("Arnold Robbins"), \
43   proper_name ("Jim Kingdon"), \
44   proper_name ("David MacKenzie"), \
45   proper_name ("Randy Smith")
46
47 /* Bitmasks for 'change_times'. */
48 #define CH_ATIME 1
49 #define CH_MTIME 2
50
51 /* Which timestamps to change. */
52 static int change_times;
53
54 /* (-c) If true, don't create if not already there.  */
55 static bool no_create;
56
57 /* (-r) If true, use times from a reference file.  */
58 static bool use_ref;
59
60 /* (-h) If true, change the times of an existing symlink, if possible.  */
61 static bool no_dereference;
62
63 /* If true, the only thing we have to do is change both the
64    modification and access time to the current time, so we don't
65    have to own the file, just be able to read and write it.
66    On some systems, we can do this if we own the file, even though
67    we have neither read nor write access to it.  */
68 static bool amtime_now;
69
70 /* New access and modification times to use when setting time.  */
71 static struct timespec newtime[2];
72
73 /* File to use for -r. */
74 static char *ref_file;
75
76 /* For long options that have no equivalent short option, use a
77    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
78 enum
79 {
80   TIME_OPTION = CHAR_MAX + 1
81 };
82
83 static struct option const longopts[] =
84 {
85   {"time", required_argument, NULL, TIME_OPTION},
86   {"no-create", no_argument, NULL, 'c'},
87   {"date", required_argument, NULL, 'd'},
88   {"reference", required_argument, NULL, 'r'},
89   {"no-dereference", no_argument, NULL, 'h'},
90   {GETOPT_HELP_OPTION_DECL},
91   {GETOPT_VERSION_OPTION_DECL},
92   {NULL, 0, NULL, 0}
93 };
94
95 /* Valid arguments to the '--time' option. */
96 static char const* const time_args[] =
97 {
98   "atime", "access", "use", "mtime", "modify", NULL
99 };
100
101 /* The bits in 'change_times' that those arguments set. */
102 static int const time_masks[] =
103 {
104   CH_ATIME, CH_ATIME, CH_ATIME, CH_MTIME, CH_MTIME
105 };
106
107 /* Store into *RESULT the result of interpreting FLEX_DATE as a date,
108    relative to NOW.  If NOW is null, use the current time.  */
109
110 static void
111 get_reldate (struct timespec *result,
112              char const *flex_date, struct timespec const *now)
113 {
114   if (! parse_datetime (result, flex_date, now))
115     error (EXIT_FAILURE, 0, _("invalid date format %s"), quote (flex_date));
116 }
117
118 /* Update the time of file FILE according to the options given.
119    Return true if successful.  */
120
121 static bool
122 touch (const char *file)
123 {
124   bool ok;
125   int fd = -1;
126   int open_errno = 0;
127   struct timespec const *t = newtime;
128
129   if (STREQ (file, "-"))
130     fd = STDOUT_FILENO;
131   else if (! (no_create || no_dereference))
132     {
133       /* Try to open FILE, creating it if necessary.  */
134       fd = fd_reopen (STDIN_FILENO, file,
135                       O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY, MODE_RW_UGO);
136
137       /* Don't save a copy of errno if it's EISDIR, since that would lead
138          touch to give a bogus diagnostic for e.g., 'touch /' (assuming
139          we don't own / or have write access to it).  On Solaris 5.6,
140          and probably other systems, it is EINVAL.  On SunOS4, it's EPERM.  */
141       if (fd == -1 && errno != EISDIR && errno != EINVAL && errno != EPERM)
142         open_errno = errno;
143     }
144
145   if (change_times != (CH_ATIME | CH_MTIME))
146     {
147       /* We're setting only one of the time values.  */
148       if (change_times == CH_MTIME)
149         newtime[0].tv_nsec = UTIME_OMIT;
150       else
151         {
152           assert (change_times == CH_ATIME);
153           newtime[1].tv_nsec = UTIME_OMIT;
154         }
155     }
156
157   if (amtime_now)
158     {
159       /* Pass NULL to futimens so it will not fail if we have
160          write access to the file, but don't own it.  */
161       t = NULL;
162     }
163
164   ok = (fdutimensat (fd, AT_FDCWD, (fd == STDOUT_FILENO ? NULL : file), t,
165                      (no_dereference && fd == -1) ? AT_SYMLINK_NOFOLLOW : 0)
166         == 0);
167
168   if (fd == STDIN_FILENO)
169     {
170       if (close (STDIN_FILENO) != 0)
171         {
172           error (0, errno, _("failed to close %s"), quote (file));
173           return false;
174         }
175     }
176   else if (fd == STDOUT_FILENO)
177     {
178       /* Do not diagnose "touch -c - >&-".  */
179       if (!ok && errno == EBADF && no_create)
180         return true;
181     }
182
183   if (!ok)
184     {
185       if (open_errno)
186         {
187           /* The wording of this diagnostic should cover at least two cases:
188              - the file does not exist, but the parent directory is unwritable
189              - the file exists, but it isn't writable
190              I think it's not worth trying to distinguish them.  */
191           error (0, open_errno, _("cannot touch %s"), quote (file));
192         }
193       else
194         {
195           if (no_create && errno == ENOENT)
196             return true;
197           error (0, errno, _("setting times of %s"), quote (file));
198         }
199       return false;
200     }
201
202   return true;
203 }
204
205 void
206 usage (int status)
207 {
208   if (status != EXIT_SUCCESS)
209     emit_try_help ();
210   else
211     {
212       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
213       fputs (_("\
214 Update the access and modification times of each FILE to the current time.\n\
215 \n\
216 A FILE argument that does not exist is created empty, unless -c or -h\n\
217 is supplied.\n\
218 \n\
219 A FILE argument string of - is handled specially and causes touch to\n\
220 change the times of the file associated with standard output.\n\
221 "), stdout);
222
223       emit_mandatory_arg_note ();
224
225       fputs (_("\
226   -a                     change only the access time\n\
227   -c, --no-create        do not create any files\n\
228   -d, --date=STRING      parse STRING and use it instead of current time\n\
229   -f                     (ignored)\n\
230 "), stdout);
231       fputs (_("\
232   -h, --no-dereference   affect each symbolic link instead of any referenced\n\
233                          file (useful only on systems that can change the\n\
234                          timestamps of a symlink)\n\
235   -m                     change only the modification time\n\
236 "), stdout);
237       fputs (_("\
238   -r, --reference=FILE   use this file's times instead of current time\n\
239   -t STAMP               use [[CC]YY]MMDDhhmm[.ss] instead of current time\n\
240       --time=WORD        change the specified time:\n\
241                            WORD is access, atime, or use: equivalent to -a\n\
242                            WORD is modify or mtime: equivalent to -m\n\
243 "), stdout);
244       fputs (HELP_OPTION_DESCRIPTION, stdout);
245       fputs (VERSION_OPTION_DESCRIPTION, stdout);
246       fputs (_("\
247 \n\
248 Note that the -d and -t options accept different time-date formats.\n\
249 "), stdout);
250       emit_ancillary_info ();
251     }
252   exit (status);
253 }
254
255 int
256 main (int argc, char **argv)
257 {
258   int c;
259   bool date_set = false;
260   bool ok = true;
261   char const *flex_date = NULL;
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   change_times = 0;
272   no_create = use_ref = false;
273
274   while ((c = getopt_long (argc, argv, "acd:fhmr:t:", longopts, NULL)) != -1)
275     {
276       switch (c)
277         {
278         case 'a':
279           change_times |= CH_ATIME;
280           break;
281
282         case 'c':
283           no_create = true;
284           break;
285
286         case 'd':
287           flex_date = optarg;
288           break;
289
290         case 'f':
291           break;
292
293         case 'h':
294           no_dereference = true;
295           break;
296
297         case 'm':
298           change_times |= CH_MTIME;
299           break;
300
301         case 'r':
302           use_ref = true;
303           ref_file = optarg;
304           break;
305
306         case 't':
307           if (! posixtime (&newtime[0].tv_sec, optarg,
308                            PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS))
309             error (EXIT_FAILURE, 0, _("invalid date format %s"),
310                    quote (optarg));
311           newtime[0].tv_nsec = 0;
312           newtime[1] = newtime[0];
313           date_set = true;
314           break;
315
316         case TIME_OPTION:       /* --time */
317           change_times |= XARGMATCH ("--time", optarg,
318                                      time_args, time_masks);
319           break;
320
321         case_GETOPT_HELP_CHAR;
322
323         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
324
325         default:
326           usage (EXIT_FAILURE);
327         }
328     }
329
330   if (change_times == 0)
331     change_times = CH_ATIME | CH_MTIME;
332
333   if (date_set && (use_ref || flex_date))
334     {
335       error (0, 0, _("cannot specify times from more than one source"));
336       usage (EXIT_FAILURE);
337     }
338
339   if (use_ref)
340     {
341       struct stat ref_stats;
342       /* Don't use (no_dereference?lstat:stat) (args), since stat
343          might be an object-like macro.  */
344       if (no_dereference ? lstat (ref_file, &ref_stats)
345           : stat (ref_file, &ref_stats))
346         error (EXIT_FAILURE, errno,
347                _("failed to get attributes of %s"), quote (ref_file));
348       newtime[0] = get_stat_atime (&ref_stats);
349       newtime[1] = get_stat_mtime (&ref_stats);
350       date_set = true;
351       if (flex_date)
352         {
353           if (change_times & CH_ATIME)
354             get_reldate (&newtime[0], flex_date, &newtime[0]);
355           if (change_times & CH_MTIME)
356             get_reldate (&newtime[1], flex_date, &newtime[1]);
357         }
358     }
359   else
360     {
361       if (flex_date)
362         {
363           struct timespec now;
364           gettime (&now);
365           get_reldate (&newtime[0], flex_date, &now);
366           newtime[1] = newtime[0];
367           date_set = true;
368
369           /* If neither -a nor -m is specified, treat "-d now" as if
370              it were absent; this lets "touch" succeed more often in
371              the presence of restrictive permissions.  */
372           if (change_times == (CH_ATIME | CH_MTIME)
373               && newtime[0].tv_sec == now.tv_sec
374               && newtime[0].tv_nsec == now.tv_nsec)
375             {
376               /* Check that it really was "-d now", and not a time
377                  stamp that just happens to be the current time.  */
378               struct timespec notnow, notnow1;
379               notnow.tv_sec = now.tv_sec ^ 1;
380               notnow.tv_nsec = now.tv_nsec;
381               get_reldate (&notnow1, flex_date, &notnow);
382               if (notnow1.tv_sec == notnow.tv_sec
383                   && notnow1.tv_nsec == notnow.tv_nsec)
384                 date_set = false;
385             }
386         }
387     }
388
389   /* The obsolete 'MMDDhhmm[YY]' form is valid IFF there are
390      two or more non-option arguments.  */
391   if (!date_set && 2 <= argc - optind && posix2_version () < 200112
392       && posixtime (&newtime[0].tv_sec, argv[optind],
393                     PDS_TRAILING_YEAR | PDS_PRE_2000))
394     {
395       newtime[0].tv_nsec = 0;
396       newtime[1] = newtime[0];
397       date_set = true;
398
399       if (! getenv ("POSIXLY_CORRECT"))
400         {
401           struct tm const *tm = localtime (&newtime[0].tv_sec);
402
403           /* Technically, it appears that even a deliberate attempt to cause
404              the above localtime to return NULL will always fail because our
405              posixtime implementation rejects all dates for which localtime
406              would fail.  However, skip the warning if it ever fails.  */
407           if (tm)
408             error (0, 0,
409                    _("warning: 'touch %s' is obsolete; use "
410                      "'touch -t %04ld%02d%02d%02d%02d.%02d'"),
411                    argv[optind],
412                    tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
413                    tm->tm_hour, tm->tm_min, tm->tm_sec);
414         }
415
416       optind++;
417     }
418
419   if (!date_set)
420     {
421       if (change_times == (CH_ATIME | CH_MTIME))
422         amtime_now = true;
423       else
424         newtime[1].tv_nsec = newtime[0].tv_nsec = UTIME_NOW;
425     }
426
427   if (optind == argc)
428     {
429       error (0, 0, _("missing file operand"));
430       usage (EXIT_FAILURE);
431     }
432
433   for (; optind < argc; ++optind)
434     ok &= touch (argv[optind]);
435
436   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
437 }