Back to index

glibc  2.9
futimes.c
Go to the documentation of this file.
00001 /* futimes -- change access and modification times of open file.  Linux version.
00002    Copyright (C) 2002,2003,2005,2006,2007 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <errno.h>
00021 #include <sysdep.h>
00022 #include <string.h>
00023 #include <time.h>
00024 #include <utime.h>
00025 #include <sys/time.h>
00026 #include <stdio-common/_itoa.h>
00027 #include <fcntl.h>
00028 
00029 #include <kernel-features.h>
00030 
00031 
00032 #if defined __NR_utimensat && !defined __ASSUME_UTIMENSAT
00033 static int miss_utimensat;
00034 #endif
00035 
00036 /* Change the access time of the file associated with FD to TVP[0] and
00037    the modification time of FILE to TVP[1].
00038 
00039    Starting with 2.6.22 the Linux kernel has the utimensat syscall which
00040    can be used to implement futimes.  Earlier kernels have no futimes()
00041    syscall so we use the /proc filesystem.  */
00042 int
00043 __futimes (int fd, const struct timeval tvp[2])
00044 {
00045   /* The utimensat system call expects timespec not timeval.  */
00046   struct timespec ts[2];
00047   if (tvp != NULL)
00048     {
00049       if (tvp[0].tv_usec < 0 || tvp[0].tv_usec >= 1000000
00050           || tvp[1].tv_usec < 0 || tvp[1].tv_usec >= 1000000)
00051        {
00052          __set_errno (EINVAL);
00053          return -1;
00054        }
00055 
00056       TIMEVAL_TO_TIMESPEC (&tvp[0], &ts[0]);
00057       TIMEVAL_TO_TIMESPEC (&tvp[1], &ts[1]);
00058     }
00059 
00060 #ifdef __ASSUME_UTIMENSAT
00061   return INLINE_SYSCALL (utimensat, 4, fd, NULL, tvp ? &ts : NULL, 0);
00062 #else
00063   int result;
00064 # ifdef __NR_utimensat
00065   if (!__builtin_expect (miss_utimensat, 0))
00066     {
00067       result = INLINE_SYSCALL (utimensat, 4, fd, NULL, tvp ? &ts : NULL, 0);
00068       if (__builtin_expect (result, 0) != -1 || errno != ENOSYS)
00069        return result;
00070 
00071       miss_utimensat = 1;
00072     }
00073 # endif
00074 
00075   static const char selffd[] = "/proc/self/fd/";
00076   char fname[sizeof (selffd) + 3 * sizeof (int)];
00077   fname[sizeof (fname) - 1] = '\0';
00078   char *cp = _itoa_word ((unsigned int) fd, fname + sizeof (fname) - 1, 10, 0);
00079   cp = memcpy (cp - sizeof (selffd) + 1, selffd, sizeof (selffd) - 1);
00080 
00081 # ifdef __NR_utimes
00082   result = INLINE_SYSCALL (utimes, 2, cp, tvp);
00083 #  ifndef __ASSUME_UTIMES
00084   if (result == -1 && errno == ENOSYS)
00085 #  endif
00086 # endif
00087     {
00088       /* The utimes() syscall does not exist or is not available in the
00089         used kernel.  Use utime().  For this we have to convert to the
00090         data format utime() expects.  */
00091 # ifndef __ASSUME_UTIMES
00092       struct utimbuf buf;
00093       struct utimbuf *times;
00094 
00095       if (tvp != NULL)
00096        {
00097          times = &buf;
00098          buf.actime = tvp[0].tv_sec + (tvp[0].tv_usec + 500000) / 1000000;
00099          buf.modtime = tvp[1].tv_sec + (tvp[1].tv_usec + 500000) / 1000000;
00100        }
00101       else
00102        times = NULL;
00103 
00104       result = INLINE_SYSCALL (utime, 2, cp, times);
00105 # endif
00106     }
00107 
00108   if (result == -1)
00109     /* Check for errors that result from failing to find /proc.
00110        This means we can't do futimes at all, so return ENOSYS
00111        rather than some confusing error.  */
00112     switch (errno)
00113       {
00114       case EACCES:
00115        if (tvp == NULL)  /* Could be a path problem or a file problem.  */
00116          break;
00117        /*FALLTHROUGH*/
00118       case ELOOP:
00119       case ENAMETOOLONG:
00120       case ENOTDIR:
00121        __set_errno (ENOSYS);
00122        break;
00123 
00124       case ENOENT:
00125        /* Validate the file descriptor by letting fcntl set errno to
00126           EBADF if it's bogus.  Otherwise it's a /proc issue.  */
00127 # if !defined __NR_fcntl && defined __NR_fcntl64
00128 #  define __NR_fcntl __NR_fcntl64
00129 # endif
00130        if (INLINE_SYSCALL (fcntl, 3, fd, F_GETFD, 0) != -1)
00131          __set_errno (ENOSYS);
00132        break;
00133       }
00134 
00135   return result;
00136 #endif
00137 }
00138 weak_alias (__futimes, futimes)