summaryrefslogtreecommitdiff
path: root/lib/utimensat.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/utimensat.c')
-rw-r--r--lib/utimensat.c101
1 files changed, 79 insertions, 22 deletions
diff --git a/lib/utimensat.c b/lib/utimensat.c
index 2cea64f6982..9fdecd681f6 100644
--- a/lib/utimensat.c
+++ b/lib/utimensat.c
@@ -24,14 +24,40 @@
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
#include "stat-time.h"
#include "timespec.h"
#include "utimens.h"
-#if HAVE_UTIMENSAT
+#if HAVE_NEARLY_WORKING_UTIMENSAT
+/* Use the original utimensat(), but correct the trailing slash handling. */
+int
+rpl_utimensat (int fd, char const *file, struct timespec const times[2],
+ int flag)
# undef utimensat
+{
+ size_t len = strlen (file);
+ if (len && file[len - 1] == '/')
+ {
+ struct stat st;
+ if (fstatat (fd, file, &st, flag & AT_SYMLINK_NOFOLLOW) < 0)
+ return -1;
+ if (!S_ISDIR (st.st_mode))
+ {
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
+
+ return utimensat (fd, file, times, flag);
+}
+
+#else
+
+# if HAVE_UTIMENSAT
/* If we have a native utimensat, but are compiling this file, then
utimensat was defined to rpl_utimensat by our replacement
@@ -42,24 +68,25 @@
local_utimensat provides the fallback manipulation. */
static int local_utimensat (int, char const *, struct timespec const[2], int);
-# define AT_FUNC_NAME local_utimensat
+# define AT_FUNC_NAME local_utimensat
/* Like utimensat, but work around native bugs. */
int
rpl_utimensat (int fd, char const *file, struct timespec const times[2],
int flag)
+# undef utimensat
{
-# if defined __linux__ || defined __sun
+# if defined __linux__ || defined __sun
struct timespec ts[2];
-# endif
+# endif
/* See comments in utimens.c for details. */
static int utimensat_works_really; /* 0 = unknown, 1 = yes, -1 = no. */
if (0 <= utimensat_works_really)
{
int result;
-# if defined __linux__ || defined __sun
+# if defined __linux__ || defined __sun
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,
@@ -90,7 +117,7 @@ rpl_utimensat (int fd, char const *file, struct timespec const times[2],
ts[1] = times[1];
times = ts;
}
-# ifdef __hppa__
+# ifdef __hppa__
/* Linux kernel 2.6.22.19 on hppa does not reject invalid tv_nsec
values. */
else if (times
@@ -104,8 +131,36 @@ rpl_utimensat (int fd, char const *file, struct timespec const times[2],
errno = EINVAL;
return -1;
}
+# endif
+# endif
+# if defined __APPLE__ && defined __MACH__
+ /* macOS 10.13 does not reject invalid tv_nsec values either. */
+ if (times
+ && ((times[0].tv_nsec != UTIME_OMIT
+ && times[0].tv_nsec != UTIME_NOW
+ && ! (0 <= times[0].tv_nsec
+ && times[0].tv_nsec < TIMESPEC_HZ))
+ || (times[1].tv_nsec != UTIME_OMIT
+ && times[1].tv_nsec != UTIME_NOW
+ && ! (0 <= times[1].tv_nsec
+ && times[1].tv_nsec < TIMESPEC_HZ))))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ size_t len = strlen (file);
+ if (len > 0 && file[len - 1] == '/')
+ {
+ struct stat statbuf;
+ if (fstatat (fd, file, &statbuf, 0) < 0)
+ return -1;
+ if (!S_ISDIR (statbuf.st_mode))
+ {
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
# endif
-# endif
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
@@ -129,11 +184,11 @@ rpl_utimensat (int fd, char const *file, struct timespec const times[2],
return local_utimensat (fd, file, times, flag);
}
-#else /* !HAVE_UTIMENSAT */
+# else /* !HAVE_UTIMENSAT */
-# define AT_FUNC_NAME utimensat
+# define AT_FUNC_NAME utimensat
-#endif /* !HAVE_UTIMENSAT */
+# endif /* !HAVE_UTIMENSAT */
/* Set the access and modification timestamps of FILE to be
TIMESPEC[0] and TIMESPEC[1], respectively; relative to directory
@@ -146,15 +201,17 @@ rpl_utimensat (int fd, char const *file, struct timespec const times[2],
Return 0 on success, -1 (setting errno) on failure. */
/* AT_FUNC_NAME is now utimensat or local_utimensat. */
-#define AT_FUNC_F1 lutimens
-#define AT_FUNC_F2 utimens
-#define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW
-#define AT_FUNC_POST_FILE_PARAM_DECLS , struct timespec const ts[2], int flag
-#define AT_FUNC_POST_FILE_ARGS , ts
-#include "at-func.c"
-#undef AT_FUNC_NAME
-#undef AT_FUNC_F1
-#undef AT_FUNC_F2
-#undef AT_FUNC_USE_F1_COND
-#undef AT_FUNC_POST_FILE_PARAM_DECLS
-#undef AT_FUNC_POST_FILE_ARGS
+# define AT_FUNC_F1 lutimens
+# define AT_FUNC_F2 utimens
+# define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW
+# define AT_FUNC_POST_FILE_PARAM_DECLS , struct timespec const ts[2], int flag
+# define AT_FUNC_POST_FILE_ARGS , ts
+# include "at-func.c"
+# undef AT_FUNC_NAME
+# undef AT_FUNC_F1
+# undef AT_FUNC_F2
+# undef AT_FUNC_USE_F1_COND
+# undef AT_FUNC_POST_FILE_PARAM_DECLS
+# undef AT_FUNC_POST_FILE_ARGS
+
+#endif /* !HAVE_NEARLY_WORKING_UTIMENSAT */