Line data Source code
1 : /* Set file access and modification times.
2 :
3 : Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Free Software
4 : Foundation, Inc.
5 :
6 : This program is free software: you can redistribute it and/or modify it
7 : under the terms of the GNU General Public License as published by the
8 : Free Software Foundation; either version 3 of the License, or any
9 : later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 :
19 : /* Written by Paul Eggert. */
20 :
21 : /* derived from a function in touch.c */
22 :
23 : #include <config.h>
24 :
25 : #include "utimens.h"
26 :
27 : #include <errno.h>
28 : #include <fcntl.h>
29 : #include <sys/time.h>
30 : #include <unistd.h>
31 :
32 : #if HAVE_UTIME_H
33 : # include <utime.h>
34 : #endif
35 :
36 : /* Some systems (even some that do have <utime.h>) don't declare this
37 : structure anywhere. */
38 : #ifndef HAVE_STRUCT_UTIMBUF
39 : struct utimbuf
40 : {
41 : long actime;
42 : long modtime;
43 : };
44 : #endif
45 :
46 : /* Some systems don't have ENOSYS. */
47 : #ifndef ENOSYS
48 : # ifdef ENOTSUP
49 : # define ENOSYS ENOTSUP
50 : # else
51 : /* Some systems don't have ENOTSUP either. */
52 : # define ENOSYS EINVAL
53 : # endif
54 : #endif
55 :
56 : #ifndef __attribute__
57 : # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8)
58 : # define __attribute__(x)
59 : # endif
60 : #endif
61 :
62 : #ifndef ATTRIBUTE_UNUSED
63 : # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
64 : #endif
65 :
66 : /* Set the access and modification time stamps of FD (a.k.a. FILE) to be
67 : TIMESPEC[0] and TIMESPEC[1], respectively.
68 : FD must be either negative -- in which case it is ignored --
69 : or a file descriptor that is open on FILE.
70 : If FD is nonnegative, then FILE can be NULL, which means
71 : use just futimes (or equivalent) instead of utimes (or equivalent),
72 : and fail if on an old system without futimes (or equivalent).
73 : If TIMESPEC is null, set the time stamps to the current time.
74 : Return 0 on success, -1 (setting errno) on failure. */
75 :
76 : int
77 58 : gl_futimens (int fd ATTRIBUTE_UNUSED,
78 : char const *file, struct timespec const timespec[2])
79 : {
80 : /* Some Linux-based NFS clients are buggy, and mishandle time stamps
81 : of files in NFS file systems in some cases. We have no
82 : configure-time test for this, but please see
83 : <http://bugs.gentoo.org/show_bug.cgi?id=132673> for references to
84 : some of the problems with Linux 2.6.16. If this affects you,
85 : compile with -DHAVE_BUGGY_NFS_TIME_STAMPS; this is reported to
86 : help in some cases, albeit at a cost in performance. But you
87 : really should upgrade your kernel to a fixed version, since the
88 : problem affects many applications. */
89 :
90 : #if HAVE_BUGGY_NFS_TIME_STAMPS
91 : if (fd < 0)
92 : sync ();
93 : else
94 : fsync (fd);
95 : #endif
96 :
97 : /* There's currently no interface to set file timestamps with
98 : nanosecond resolution, so do the best we can, discarding any
99 : fractional part of the timestamp. */
100 : #if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES
101 : struct timeval timeval[2];
102 : struct timeval const *t;
103 58 : if (timespec)
104 : {
105 29 : timeval[0].tv_sec = timespec[0].tv_sec;
106 29 : timeval[0].tv_usec = timespec[0].tv_nsec / 1000;
107 29 : timeval[1].tv_sec = timespec[1].tv_sec;
108 29 : timeval[1].tv_usec = timespec[1].tv_nsec / 1000;
109 29 : t = timeval;
110 : }
111 : else
112 29 : t = NULL;
113 :
114 :
115 58 : if (fd < 0)
116 : {
117 : # if HAVE_FUTIMESAT
118 17 : return futimesat (AT_FDCWD, file, t);
119 : # endif
120 : }
121 : else
122 : {
123 : /* If futimesat or futimes fails here, don't try to speed things
124 : up by returning right away. glibc can incorrectly fail with
125 : errno == ENOENT if /proc isn't mounted. Also, Mandrake 10.0
126 : in high security mode doesn't allow ordinary users to read
127 : /proc/self, so glibc incorrectly fails with errno == EACCES.
128 : If errno == EIO, EPERM, or EROFS, it's probably safe to fail
129 : right away, but these cases are rare enough that they're not
130 : worth optimizing, and who knows what other messed-up systems
131 : are out there? So play it safe and fall back on the code
132 : below. */
133 : # if HAVE_FUTIMESAT
134 41 : if (futimesat (fd, NULL, t) == 0)
135 41 : return 0;
136 : # elif HAVE_FUTIMES
137 : if (futimes (fd, t) == 0)
138 : return 0;
139 : # endif
140 : }
141 : #endif
142 :
143 0 : if (!file)
144 : {
145 : #if ! (HAVE_FUTIMESAT || (HAVE_WORKING_UTIMES && HAVE_FUTIMES))
146 : errno = ENOSYS;
147 : #endif
148 :
149 : /* Prefer EBADF to ENOSYS if both error numbers apply. */
150 0 : if (errno == ENOSYS)
151 : {
152 0 : int fd2 = dup (fd);
153 0 : int dup_errno = errno;
154 0 : if (0 <= fd2)
155 0 : close (fd2);
156 0 : errno = (fd2 < 0 && dup_errno == EBADF ? EBADF : ENOSYS);
157 : }
158 :
159 0 : return -1;
160 : }
161 :
162 : #if HAVE_WORKING_UTIMES
163 0 : return utimes (file, t);
164 : #else
165 : {
166 : struct utimbuf utimbuf;
167 : struct utimbuf const *ut;
168 : if (timespec)
169 : {
170 : utimbuf.actime = timespec[0].tv_sec;
171 : utimbuf.modtime = timespec[1].tv_sec;
172 : ut = &utimbuf;
173 : }
174 : else
175 : ut = NULL;
176 :
177 : return utime (file, ut);
178 : }
179 : #endif
180 : }
181 :
182 : /* Set the access and modification time stamps of FILE to be
183 : TIMESPEC[0] and TIMESPEC[1], respectively. */
184 : int
185 3 : utimens (char const *file, struct timespec const timespec[2])
186 : {
187 3 : return gl_futimens (-1, file, timespec);
188 : }
|