X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Futimensat.c;h=caeeb5f03c07e1857c26eaf26ac3a4cd9a27eafa;hb=a425462f784164b73297d8992075fd9aebd65a94;hp=03e65bfae125b1fedcd1548e91e186fb782a6272;hpb=9a669cf64253a2b2149d7f7cc5e0664c1bc7dda9;p=gnulib.git diff --git a/lib/utimensat.c b/lib/utimensat.c index 03e65bfae..caeeb5f03 100644 --- a/lib/utimensat.c +++ b/lib/utimensat.c @@ -1,5 +1,5 @@ /* Set the access and modification time of a file relative to directory fd. - Copyright (C) 2009 Free Software Foundation, Inc. + Copyright (C) 2009-2012 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -48,29 +48,58 @@ int rpl_utimensat (int fd, char const *file, struct timespec const times[2], int flag) { +# ifdef __linux__ + struct timespec ts[2]; +# endif + /* See comments in utimens.c for details. */ static int utimensat_works_really; /* 0 = unknown, 1 = yes, -1 = no. */ - static int utimensat_ctime_really; /* 0 = unknown, 1 = yes, -1 = no. */ if (0 <= utimensat_works_really) { int result; - struct stat st1; - struct stat st2; - struct timespec ts[2]; - /* Linux kernel 2.6.32 has a bug where mtime of UTIME_OMIT fails - to change ctime. */ - if (utimensat_ctime_really <= 0 && times - && times[0].tv_nsec != UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) +# ifdef __linux__ + struct stat st; + /* As recently as Linux kernel 2.6.32 (Dec 2009), several file + systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT, + but work if both times are either explicitly specified or + UTIME_NOW. Work around it with a preparatory [l]stat prior + to calling utimensat; fortunately, there is not much timing + impact due to the extra syscall even on file systems where + UTIME_OMIT would have worked. FIXME: Simplify this in 2012, + when file system bugs are no longer common. */ + if (times && (times[0].tv_nsec == UTIME_OMIT + || times[1].tv_nsec == UTIME_OMIT)) { - if (fstatat (fd, file, &st1, flag)) + if (fstatat (fd, file, &st, flag)) return -1; - if (utimensat_ctime_really < 0) - { - ts[0] = times[0]; - ts[1] = get_stat_mtime (&st1); - times = ts; - } + if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) + return 0; + if (times[0].tv_nsec == UTIME_OMIT) + ts[0] = get_stat_atime (&st); + else + ts[0] = times[0]; + if (times[1].tv_nsec == UTIME_OMIT) + ts[1] = get_stat_mtime (&st); + else + ts[1] = times[1]; + times = ts; + } +# ifdef __hppa__ + /* Linux kernel 2.6.22.19 on hppa does not reject invalid tv_nsec + values. */ + else if (times + && ((times[0].tv_nsec != UTIME_NOW + && (times[0].tv_nsec < 0 + || times[0].tv_nsec >= 1000000000)) + || (times[1].tv_nsec != UTIME_NOW + && (times[1].tv_nsec < 0 + || times[1].tv_nsec >= 1000000000)))) + { + errno = EINVAL; + return -1; } +# endif +# endif /* __linux__ */ result = utimensat (fd, file, times, flag); /* Linux kernel 2.6.25 has a bug where it returns EINVAL for UTIME_NOW or UTIME_OMIT with non-zero tv_sec, which @@ -82,37 +111,6 @@ rpl_utimensat (int fd, char const *file, struct timespec const times[2], if (result == 0 || (errno != ENOSYS && errno != EINVAL)) { utimensat_works_really = 1; - if (result == 0 && utimensat_ctime_really == 0 && times - && times[0].tv_nsec != UTIME_OMIT - && times[1].tv_nsec == UTIME_OMIT) - { - /* Perform a followup [l]stat. See detect_ctime_bug in - utimens.c for more details. */ - struct timespec now; - if (fstatat (fd, file, &st2, flag)) - return -1; - if (st1.st_ctime != st2.st_ctime - || get_stat_ctime_ns (&st1) != get_stat_ctime_ns (&st2)) - { - utimensat_ctime_really = 1; - return result; - } - if (times[0].tv_nsec == UTIME_NOW) - now = get_stat_atime (&st2); - else - gettime (&now); - if (now.tv_sec < st2.st_ctime - || 2 < now.tv_sec - st2.st_ctime - || (get_stat_ctime_ns (&st2) - && now.tv_sec - st2.st_ctime < 2 - && (20000000 < (1000000000 * (now.tv_sec - st2.st_ctime) - + now.tv_nsec - - get_stat_ctime_ns (&st2))))) - utimensat_ctime_really = -1; - ts[0] = times[0]; - ts[1] = get_stat_mtime (&st2); - result = utimensat (fd, file, ts, flag); - } return result; } }