* lib/utimens.c (futimens): Use futimesat if available.
[gnulib.git] / lib / utimens.c
1 /* Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
2
3    This program is free software; you can redistribute it and/or modify it
4    under the terms of the GNU General Public License as published by the
5    Free Software Foundation; either version 2, or (at your option) any
6    later version.
7
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software Foundation,
15    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
16
17 /* Written by Paul Eggert.  */
18
19 /* derived from a function in touch.c */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include "utimens.h"
26
27 #include <errno.h>
28 #include <fcntl.h>
29
30 #if HAVE_UTIME_H
31 # include <utime.h>
32 #endif
33
34 /* Some systems (even some that do have <utime.h>) don't declare this
35    structure anywhere.  */
36 #ifndef HAVE_STRUCT_UTIMBUF
37 struct utimbuf
38 {
39   long actime;
40   long modtime;
41 };
42 #endif
43
44 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
45 # define __attribute__(x)
46 #endif
47
48 #ifndef ATTRIBUTE_UNUSED
49 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
50 #endif
51
52 /* Set the access and modification time stamps of FD (a.k.a. FILE) to be
53    TIMESPEC[0] and TIMESPEC[1], respectively.
54    FD must be either negative -- in which case it is ignored --
55    or a file descriptor that is open on FILE.
56    If TIMESPEC is null, set the time stamps to the current time.  */
57
58 int
59 futimens (int fd ATTRIBUTE_UNUSED,
60           char const *file, struct timespec const timespec[2])
61 {
62   /* There's currently no interface to set file timestamps with
63      nanosecond resolution, so do the best we can, discarding any
64      fractional part of the timestamp.  */
65 #if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES
66   struct timeval timeval[2];
67   struct timeval const *t;
68   if (timespec)
69     {
70       timeval[0].tv_sec = timespec[0].tv_sec;
71       timeval[0].tv_usec = timespec[0].tv_nsec / 1000;
72       timeval[1].tv_sec = timespec[1].tv_sec;
73       timeval[1].tv_usec = timespec[1].tv_nsec / 1000;
74       t = timeval;
75     }
76   else
77     t = NULL;
78
79 # if HAVE_FUTIMESAT
80   return fd < 0 ? futimesat (AT_FDCWD, file, t) : futimesat (fd, NULL, t);
81 # else
82 #  if HAVE_FUTIMES
83   if (0 <= fd)
84     {
85       if (futimes (fd, t) == 0)
86         return 0;
87
88       /* On GNU/Linux without the futimes syscall and without /proc
89          mounted, glibc futimes fails with errno == ENOENT.  Fall back
90          on utimes if we get a weird error number like that.  */
91       switch (errno)
92         {
93         case EACCES:
94         case EIO:
95         case EPERM:
96         case EROFS:
97           return -1;
98         }
99     }
100 #  endif
101   return utimes (file, t);
102 # endif
103
104 #else
105
106   struct utimbuf utimbuf;
107   struct utimbuf const *t;
108   if (timespec)
109     {
110       utimbuf.actime = timespec[0].tv_sec;
111       utimbuf.modtime = timespec[1].tv_sec;
112       t = &utimbuf;
113     }
114   else
115     t = NULL;
116   return utime (file, t);
117
118 #endif
119 }
120
121 /* Set the access and modification time stamps of FILE to be
122    TIMESPEC[0] and TIMESPEC[1], respectively.  */
123 int
124 utimens (char const *file, struct timespec const timespec[2])
125 {
126   return futimens (-1, file, timespec);
127 }