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