@@ -1076,7 +1076,7 @@ static int local_utimensat(FsContext *s, V9fsPath *fs_path,
goto out;
}
- ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW);
+ ret = utimensat_nofollow(dirfd, name, buf);
close_preserve_errno(dirfd);
out:
g_free(dirpath);
@@ -62,3 +62,99 @@ int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
close_preserve_errno(fd);
return ret;
}
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+static int update_times_from_stat(int fd, struct timespec times[2],
+ int update0, int update1)
+{
+ struct stat buf;
+ int ret = fstat(fd, &buf);
+ if (ret == -1) {
+ return ret;
+ }
+ if (update0) {
+ times[0] = buf.st_atimespec;
+ }
+ if (update1) {
+ times[1] = buf.st_mtimespec;
+ }
+ return 0;
+}
+
+int utimensat_nofollow(int dirfd, const char *filename,
+ const struct timespec times_in[2])
+{
+ int ret, fd;
+ int special0, special1;
+ struct timeval futimes_buf[2];
+ struct timespec times[2];
+ memcpy(times, times_in, 2 * sizeof(struct timespec));
+
+/* Check whether we have an SDK version that defines utimensat */
+#if defined(__MAC_10_13)
+# if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_13
+# define UTIMENSAT_AVAILABLE 1
+# elif __has_builtin(__builtin_available)
+# define UTIMENSAT_AVAILABLE __builtin_available(macos 10.13, *)
+# else
+# define UTIMENSAT_AVAILABLE 0
+# endif
+ if (UTIMENSAT_AVAILABLE) {
+ return utimensat(dirfd, filename, times, AT_SYMLINK_NOFOLLOW);
+ }
+#endif
+
+ /* utimensat not available. Use futimes. */
+ fd = openat_file(dirfd, filename, O_PATH_9P_UTIL | O_NOFOLLOW, 0);
+ if (fd == -1) {
+ return -1;
+ }
+
+ special0 = times[0].tv_nsec == UTIME_OMIT;
+ special1 = times[1].tv_nsec == UTIME_OMIT;
+ if (special0 || special1) {
+ /* If both are set, nothing to do */
+ if (special0 && special1) {
+ ret = 0;
+ goto done;
+ }
+
+ ret = update_times_from_stat(fd, times, special0, special1);
+ if (ret < 0) {
+ goto done;
+ }
+ }
+
+ special0 = times[0].tv_nsec == UTIME_NOW;
+ special1 = times[1].tv_nsec == UTIME_NOW;
+ if (special0 || special1) {
+ ret = futimes(fd, NULL);
+ if (ret < 0) {
+ goto done;
+ }
+
+ /* If both are set, we are done */
+ if (special0 && special1) {
+ ret = 0;
+ goto done;
+ }
+
+ ret = update_times_from_stat(fd, times, special0, special1);
+ if (ret < 0) {
+ goto done;
+ }
+ }
+
+ futimes_buf[0].tv_sec = times[0].tv_sec;
+ futimes_buf[0].tv_usec = times[0].tv_nsec / 1000;
+ futimes_buf[1].tv_sec = times[1].tv_sec;
+ futimes_buf[1].tv_usec = times[1].tv_nsec / 1000;
+ ret = futimes(fd, futimes_buf);
+
+done:
+ close_preserve_errno(fd);
+ return ret;
+}
@@ -62,3 +62,9 @@ int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
g_free(proc_path);
return ret;
}
+
+int utimensat_nofollow(int dirfd, const char *filename,
+ const struct timespec times[2])
+{
+ return utimensat(dirfd, filename, times, AT_SYMLINK_NOFOLLOW);
+}
@@ -36,6 +36,12 @@ static inline int qemu_lsetxattr(const char *path, const char *name,
#define qemu_lsetxattr lsetxattr
#endif
+/* Compatibility with old SDK Versions for Darwin */
+#if defined(CONFIG_DARWIN) && !defined(UTIME_NOW)
+#define UTIME_NOW -1
+#define UTIME_OMIT -2
+#endif
+
static inline void close_preserve_errno(int fd)
{
int serrno = errno;
@@ -96,5 +102,7 @@ ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
char *list, size_t size);
ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
const char *name);
+int utimensat_nofollow(int dirfd, const char *filename,
+ const struct timespec times[2]);
#endif
@@ -27,6 +27,7 @@
#include "virtio-9p.h"
#include "fsdev/qemu-fsdev.h"
#include "9p-xattr.h"
+#include "9p-util.h"
#include "coth.h"
#include "trace.h"
#include "migration/blocker.h"