summaryrefslogtreecommitdiffstats
path: root/libc-top-half/musl/src/stat
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 13:54:38 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 13:54:38 +0000
commit8c1ab65c0f548d20b7f177bdb736daaf603340e1 (patch)
treedf55b7e75bf43f2bf500845b105afe3ac3a5157e /libc-top-half/musl/src/stat
parentInitial commit. (diff)
downloadwasi-libc-8c1ab65c0f548d20b7f177bdb736daaf603340e1.tar.xz
wasi-libc-8c1ab65c0f548d20b7f177bdb736daaf603340e1.zip
Adding upstream version 0.0~git20221206.8b7148f.upstream/0.0_git20221206.8b7148f
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libc-top-half/musl/src/stat')
-rw-r--r--libc-top-half/musl/src/stat/__xstat.c40
-rw-r--r--libc-top-half/musl/src/stat/chmod.c12
-rw-r--r--libc-top-half/musl/src/stat/fchmod.c19
-rw-r--r--libc-top-half/musl/src/stat/fchmodat.c38
-rw-r--r--libc-top-half/musl/src/stat/fstat.c15
-rw-r--r--libc-top-half/musl/src/stat/fstatat.c147
-rw-r--r--libc-top-half/musl/src/stat/futimens.c6
-rw-r--r--libc-top-half/musl/src/stat/futimesat.c31
-rw-r--r--libc-top-half/musl/src/stat/lchmod.c8
-rw-r--r--libc-top-half/musl/src/stat/lstat.c11
-rw-r--r--libc-top-half/musl/src/stat/mkdir.c12
-rw-r--r--libc-top-half/musl/src/stat/mkdirat.c7
-rw-r--r--libc-top-half/musl/src/stat/mkfifo.c6
-rw-r--r--libc-top-half/musl/src/stat/mkfifoat.c6
-rw-r--r--libc-top-half/musl/src/stat/mknod.c12
-rw-r--r--libc-top-half/musl/src/stat/mknodat.c7
-rw-r--r--libc-top-half/musl/src/stat/stat.c11
-rw-r--r--libc-top-half/musl/src/stat/statvfs.c63
-rw-r--r--libc-top-half/musl/src/stat/umask.c7
-rw-r--r--libc-top-half/musl/src/stat/utimensat.c60
20 files changed, 518 insertions, 0 deletions
diff --git a/libc-top-half/musl/src/stat/__xstat.c b/libc-top-half/musl/src/stat/__xstat.c
new file mode 100644
index 0000000..630936a
--- /dev/null
+++ b/libc-top-half/musl/src/stat/__xstat.c
@@ -0,0 +1,40 @@
+#include <sys/stat.h>
+
+#if !_REDIR_TIME64
+
+int __fxstat(int ver, int fd, struct stat *buf)
+{
+ return fstat(fd, buf);
+}
+
+int __fxstatat(int ver, int fd, const char *path, struct stat *buf, int flag)
+{
+ return fstatat(fd, path, buf, flag);
+}
+
+int __lxstat(int ver, const char *path, struct stat *buf)
+{
+ return lstat(path, buf);
+}
+
+int __xstat(int ver, const char *path, struct stat *buf)
+{
+ return stat(path, buf);
+}
+
+weak_alias(__fxstat, __fxstat64);
+weak_alias(__fxstatat, __fxstatat64);
+weak_alias(__lxstat, __lxstat64);
+weak_alias(__xstat, __xstat64);
+
+#endif
+
+int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev)
+{
+ return mknod(path, mode, *dev);
+}
+
+int __xmknodat(int ver, int fd, const char *path, mode_t mode, dev_t *dev)
+{
+ return mknodat(fd, path, mode, *dev);
+}
diff --git a/libc-top-half/musl/src/stat/chmod.c b/libc-top-half/musl/src/stat/chmod.c
new file mode 100644
index 0000000..d4f53c5
--- /dev/null
+++ b/libc-top-half/musl/src/stat/chmod.c
@@ -0,0 +1,12 @@
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "syscall.h"
+
+int chmod(const char *path, mode_t mode)
+{
+#ifdef SYS_chmod
+ return syscall(SYS_chmod, path, mode);
+#else
+ return syscall(SYS_fchmodat, AT_FDCWD, path, mode);
+#endif
+}
diff --git a/libc-top-half/musl/src/stat/fchmod.c b/libc-top-half/musl/src/stat/fchmod.c
new file mode 100644
index 0000000..7a503ee
--- /dev/null
+++ b/libc-top-half/musl/src/stat/fchmod.c
@@ -0,0 +1,19 @@
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "syscall.h"
+
+int fchmod(int fd, mode_t mode)
+{
+ int ret = __syscall(SYS_fchmod, fd, mode);
+ if (ret != -EBADF || __syscall(SYS_fcntl, fd, F_GETFD) < 0)
+ return __syscall_ret(ret);
+
+ char buf[15+3*sizeof(int)];
+ __procfdname(buf, fd);
+#ifdef SYS_chmod
+ return syscall(SYS_chmod, buf, mode);
+#else
+ return syscall(SYS_fchmodat, AT_FDCWD, buf, mode);
+#endif
+}
diff --git a/libc-top-half/musl/src/stat/fchmodat.c b/libc-top-half/musl/src/stat/fchmodat.c
new file mode 100644
index 0000000..4ee00b0
--- /dev/null
+++ b/libc-top-half/musl/src/stat/fchmodat.c
@@ -0,0 +1,38 @@
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "syscall.h"
+#include "kstat.h"
+
+int fchmodat(int fd, const char *path, mode_t mode, int flag)
+{
+ if (!flag) return syscall(SYS_fchmodat, fd, path, mode, flag);
+
+ if (flag != AT_SYMLINK_NOFOLLOW)
+ return __syscall_ret(-EINVAL);
+
+ struct kstat st;
+ int ret, fd2;
+ char proc[15+3*sizeof(int)];
+
+ if ((ret = __syscall(SYS_fstatat, fd, path, &st, flag)))
+ return __syscall_ret(ret);
+ if (S_ISLNK(st.st_mode))
+ return __syscall_ret(-EOPNOTSUPP);
+
+ if ((fd2 = __syscall(SYS_openat, fd, path, O_RDONLY|O_PATH|O_NOFOLLOW|O_NOCTTY|O_CLOEXEC)) < 0) {
+ if (fd2 == -ELOOP)
+ return __syscall_ret(-EOPNOTSUPP);
+ return __syscall_ret(fd2);
+ }
+
+ __procfdname(proc, fd2);
+ ret = __syscall(SYS_fstatat, AT_FDCWD, proc, &st, 0);
+ if (!ret) {
+ if (S_ISLNK(st.st_mode)) ret = -EOPNOTSUPP;
+ else ret = __syscall(SYS_fchmodat, AT_FDCWD, proc, mode);
+ }
+
+ __syscall(SYS_close, fd2);
+ return __syscall_ret(ret);
+}
diff --git a/libc-top-half/musl/src/stat/fstat.c b/libc-top-half/musl/src/stat/fstat.c
new file mode 100644
index 0000000..9bbb46d
--- /dev/null
+++ b/libc-top-half/musl/src/stat/fstat.c
@@ -0,0 +1,15 @@
+#define _BSD_SOURCE
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "syscall.h"
+
+int fstat(int fd, struct stat *st)
+{
+ if (fd<0) return __syscall_ret(-EBADF);
+ return fstatat(fd, "", st, AT_EMPTY_PATH);
+}
+
+#if !_REDIR_TIME64
+weak_alias(fstat, fstat64);
+#endif
diff --git a/libc-top-half/musl/src/stat/fstatat.c b/libc-top-half/musl/src/stat/fstatat.c
new file mode 100644
index 0000000..de165b5
--- /dev/null
+++ b/libc-top-half/musl/src/stat/fstatat.c
@@ -0,0 +1,147 @@
+#define _BSD_SOURCE
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/sysmacros.h>
+#include "syscall.h"
+#include "kstat.h"
+
+struct statx {
+ uint32_t stx_mask;
+ uint32_t stx_blksize;
+ uint64_t stx_attributes;
+ uint32_t stx_nlink;
+ uint32_t stx_uid;
+ uint32_t stx_gid;
+ uint16_t stx_mode;
+ uint16_t pad1;
+ uint64_t stx_ino;
+ uint64_t stx_size;
+ uint64_t stx_blocks;
+ uint64_t stx_attributes_mask;
+ struct {
+ int64_t tv_sec;
+ uint32_t tv_nsec;
+ int32_t pad;
+ } stx_atime, stx_btime, stx_ctime, stx_mtime;
+ uint32_t stx_rdev_major;
+ uint32_t stx_rdev_minor;
+ uint32_t stx_dev_major;
+ uint32_t stx_dev_minor;
+ uint64_t spare[14];
+};
+
+static int fstatat_statx(int fd, const char *restrict path, struct stat *restrict st, int flag)
+{
+ struct statx stx;
+
+ int ret = __syscall(SYS_statx, fd, path, flag, 0x7ff, &stx);
+ if (ret) return ret;
+
+ *st = (struct stat){
+ .st_dev = makedev(stx.stx_dev_major, stx.stx_dev_minor),
+ .st_ino = stx.stx_ino,
+ .st_mode = stx.stx_mode,
+ .st_nlink = stx.stx_nlink,
+ .st_uid = stx.stx_uid,
+ .st_gid = stx.stx_gid,
+ .st_rdev = makedev(stx.stx_rdev_major, stx.stx_rdev_minor),
+ .st_size = stx.stx_size,
+ .st_blksize = stx.stx_blksize,
+ .st_blocks = stx.stx_blocks,
+ .st_atim.tv_sec = stx.stx_atime.tv_sec,
+ .st_atim.tv_nsec = stx.stx_atime.tv_nsec,
+ .st_mtim.tv_sec = stx.stx_mtime.tv_sec,
+ .st_mtim.tv_nsec = stx.stx_mtime.tv_nsec,
+ .st_ctim.tv_sec = stx.stx_ctime.tv_sec,
+ .st_ctim.tv_nsec = stx.stx_ctime.tv_nsec,
+#if _REDIR_TIME64
+ .__st_atim32.tv_sec = stx.stx_atime.tv_sec,
+ .__st_atim32.tv_nsec = stx.stx_atime.tv_nsec,
+ .__st_mtim32.tv_sec = stx.stx_mtime.tv_sec,
+ .__st_mtim32.tv_nsec = stx.stx_mtime.tv_nsec,
+ .__st_ctim32.tv_sec = stx.stx_ctime.tv_sec,
+ .__st_ctim32.tv_nsec = stx.stx_ctime.tv_nsec,
+#endif
+ };
+ return 0;
+}
+
+static int fstatat_kstat(int fd, const char *restrict path, struct stat *restrict st, int flag)
+{
+ int ret;
+ struct kstat kst;
+
+ if (flag==AT_EMPTY_PATH && fd>=0 && !*path) {
+ ret = __syscall(SYS_fstat, fd, &kst);
+ if (ret==-EBADF && __syscall(SYS_fcntl, fd, F_GETFD)>=0) {
+ ret = __syscall(SYS_fstatat, fd, path, &kst, flag);
+ if (ret==-EINVAL) {
+ char buf[15+3*sizeof(int)];
+ __procfdname(buf, fd);
+#ifdef SYS_stat
+ ret = __syscall(SYS_stat, buf, &kst);
+#else
+ ret = __syscall(SYS_fstatat, AT_FDCWD, buf, &kst, 0);
+#endif
+ }
+ }
+ }
+#ifdef SYS_lstat
+ else if ((fd == AT_FDCWD || *path=='/') && flag==AT_SYMLINK_NOFOLLOW)
+ ret = __syscall(SYS_lstat, path, &kst);
+#endif
+#ifdef SYS_stat
+ else if ((fd == AT_FDCWD || *path=='/') && !flag)
+ ret = __syscall(SYS_stat, path, &kst);
+#endif
+ else ret = __syscall(SYS_fstatat, fd, path, &kst, flag);
+
+ if (ret) return ret;
+
+ *st = (struct stat){
+ .st_dev = kst.st_dev,
+ .st_ino = kst.st_ino,
+ .st_mode = kst.st_mode,
+ .st_nlink = kst.st_nlink,
+ .st_uid = kst.st_uid,
+ .st_gid = kst.st_gid,
+ .st_rdev = kst.st_rdev,
+ .st_size = kst.st_size,
+ .st_blksize = kst.st_blksize,
+ .st_blocks = kst.st_blocks,
+ .st_atim.tv_sec = kst.st_atime_sec,
+ .st_atim.tv_nsec = kst.st_atime_nsec,
+ .st_mtim.tv_sec = kst.st_mtime_sec,
+ .st_mtim.tv_nsec = kst.st_mtime_nsec,
+ .st_ctim.tv_sec = kst.st_ctime_sec,
+ .st_ctim.tv_nsec = kst.st_ctime_nsec,
+#if _REDIR_TIME64
+ .__st_atim32.tv_sec = kst.st_atime_sec,
+ .__st_atim32.tv_nsec = kst.st_atime_nsec,
+ .__st_mtim32.tv_sec = kst.st_mtime_sec,
+ .__st_mtim32.tv_nsec = kst.st_mtime_nsec,
+ .__st_ctim32.tv_sec = kst.st_ctime_sec,
+ .__st_ctim32.tv_nsec = kst.st_ctime_nsec,
+#endif
+ };
+
+ return 0;
+}
+
+int fstatat(int fd, const char *restrict path, struct stat *restrict st, int flag)
+{
+ int ret;
+ if (sizeof((struct kstat){0}.st_atime_sec) < sizeof(time_t)) {
+ ret = fstatat_statx(fd, path, st, flag);
+ if (ret!=-ENOSYS) return __syscall_ret(ret);
+ }
+ ret = fstatat_kstat(fd, path, st, flag);
+ return __syscall_ret(ret);
+}
+
+#if !_REDIR_TIME64
+weak_alias(fstatat, fstatat64);
+#endif
diff --git a/libc-top-half/musl/src/stat/futimens.c b/libc-top-half/musl/src/stat/futimens.c
new file mode 100644
index 0000000..360225b
--- /dev/null
+++ b/libc-top-half/musl/src/stat/futimens.c
@@ -0,0 +1,6 @@
+#include <sys/stat.h>
+
+int futimens(int fd, const struct timespec times[2])
+{
+ return utimensat(fd, 0, times, 0);
+}
diff --git a/libc-top-half/musl/src/stat/futimesat.c b/libc-top-half/musl/src/stat/futimesat.c
new file mode 100644
index 0000000..c29b2b7
--- /dev/null
+++ b/libc-top-half/musl/src/stat/futimesat.c
@@ -0,0 +1,31 @@
+#define _GNU_SOURCE
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <errno.h>
+#ifdef __wasilibc_unmodified_upstream // WASI has no syscall
+#include "syscall.h"
+#endif
+
+int __futimesat(int dirfd, const char *pathname, const struct timeval times[2])
+{
+ struct timespec ts[2];
+ if (times) {
+ int i;
+ for (i=0; i<2; i++) {
+#ifdef __wasilibc_unmodified_upstream // WASI has no syscall
+ if (times[i].tv_usec >= 1000000ULL)
+ return __syscall_ret(-EINVAL);
+#else
+ if (times[i].tv_usec >= 1000000ULL) {
+ errno = EINVAL;
+ return -1;
+ }
+#endif
+ ts[i].tv_sec = times[i].tv_sec;
+ ts[i].tv_nsec = times[i].tv_usec * 1000;
+ }
+ }
+ return utimensat(dirfd, pathname, times ? ts : 0, 0);
+}
+
+weak_alias(__futimesat, futimesat);
diff --git a/libc-top-half/musl/src/stat/lchmod.c b/libc-top-half/musl/src/stat/lchmod.c
new file mode 100644
index 0000000..f324ba7
--- /dev/null
+++ b/libc-top-half/musl/src/stat/lchmod.c
@@ -0,0 +1,8 @@
+#define _GNU_SOURCE
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int lchmod(const char *path, mode_t mode)
+{
+ return fchmodat(AT_FDCWD, path, mode, AT_SYMLINK_NOFOLLOW);
+}
diff --git a/libc-top-half/musl/src/stat/lstat.c b/libc-top-half/musl/src/stat/lstat.c
new file mode 100644
index 0000000..6fe004d
--- /dev/null
+++ b/libc-top-half/musl/src/stat/lstat.c
@@ -0,0 +1,11 @@
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int lstat(const char *restrict path, struct stat *restrict buf)
+{
+ return fstatat(AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW);
+}
+
+#if !_REDIR_TIME64
+weak_alias(lstat, lstat64);
+#endif
diff --git a/libc-top-half/musl/src/stat/mkdir.c b/libc-top-half/musl/src/stat/mkdir.c
new file mode 100644
index 0000000..32625b7
--- /dev/null
+++ b/libc-top-half/musl/src/stat/mkdir.c
@@ -0,0 +1,12 @@
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "syscall.h"
+
+int mkdir(const char *path, mode_t mode)
+{
+#ifdef SYS_mkdir
+ return syscall(SYS_mkdir, path, mode);
+#else
+ return syscall(SYS_mkdirat, AT_FDCWD, path, mode);
+#endif
+}
diff --git a/libc-top-half/musl/src/stat/mkdirat.c b/libc-top-half/musl/src/stat/mkdirat.c
new file mode 100644
index 0000000..b8bc252
--- /dev/null
+++ b/libc-top-half/musl/src/stat/mkdirat.c
@@ -0,0 +1,7 @@
+#include <sys/stat.h>
+#include "syscall.h"
+
+int mkdirat(int fd, const char *path, mode_t mode)
+{
+ return syscall(SYS_mkdirat, fd, path, mode);
+}
diff --git a/libc-top-half/musl/src/stat/mkfifo.c b/libc-top-half/musl/src/stat/mkfifo.c
new file mode 100644
index 0000000..60efcf7
--- /dev/null
+++ b/libc-top-half/musl/src/stat/mkfifo.c
@@ -0,0 +1,6 @@
+#include <sys/stat.h>
+
+int mkfifo(const char *path, mode_t mode)
+{
+ return mknod(path, mode | S_IFIFO, 0);
+}
diff --git a/libc-top-half/musl/src/stat/mkfifoat.c b/libc-top-half/musl/src/stat/mkfifoat.c
new file mode 100644
index 0000000..d3a1f97
--- /dev/null
+++ b/libc-top-half/musl/src/stat/mkfifoat.c
@@ -0,0 +1,6 @@
+#include <sys/stat.h>
+
+int mkfifoat(int fd, const char *path, mode_t mode)
+{
+ return mknodat(fd, path, mode | S_IFIFO, 0);
+}
diff --git a/libc-top-half/musl/src/stat/mknod.c b/libc-top-half/musl/src/stat/mknod.c
new file mode 100644
index 0000000..beebd84
--- /dev/null
+++ b/libc-top-half/musl/src/stat/mknod.c
@@ -0,0 +1,12 @@
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "syscall.h"
+
+int mknod(const char *path, mode_t mode, dev_t dev)
+{
+#ifdef SYS_mknod
+ return syscall(SYS_mknod, path, mode, dev);
+#else
+ return syscall(SYS_mknodat, AT_FDCWD, path, mode, dev);
+#endif
+}
diff --git a/libc-top-half/musl/src/stat/mknodat.c b/libc-top-half/musl/src/stat/mknodat.c
new file mode 100644
index 0000000..7c97c91
--- /dev/null
+++ b/libc-top-half/musl/src/stat/mknodat.c
@@ -0,0 +1,7 @@
+#include <sys/stat.h>
+#include "syscall.h"
+
+int mknodat(int fd, const char *path, mode_t mode, dev_t dev)
+{
+ return syscall(SYS_mknodat, fd, path, mode, dev);
+}
diff --git a/libc-top-half/musl/src/stat/stat.c b/libc-top-half/musl/src/stat/stat.c
new file mode 100644
index 0000000..ea70efc
--- /dev/null
+++ b/libc-top-half/musl/src/stat/stat.c
@@ -0,0 +1,11 @@
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int stat(const char *restrict path, struct stat *restrict buf)
+{
+ return fstatat(AT_FDCWD, path, buf, 0);
+}
+
+#if !_REDIR_TIME64
+weak_alias(stat, stat64);
+#endif
diff --git a/libc-top-half/musl/src/stat/statvfs.c b/libc-top-half/musl/src/stat/statvfs.c
new file mode 100644
index 0000000..f65d1b5
--- /dev/null
+++ b/libc-top-half/musl/src/stat/statvfs.c
@@ -0,0 +1,63 @@
+#include <sys/statvfs.h>
+#include <sys/statfs.h>
+#include "syscall.h"
+
+static int __statfs(const char *path, struct statfs *buf)
+{
+ *buf = (struct statfs){0};
+#ifdef SYS_statfs64
+ return syscall(SYS_statfs64, path, sizeof *buf, buf);
+#else
+ return syscall(SYS_statfs, path, buf);
+#endif
+}
+
+static int __fstatfs(int fd, struct statfs *buf)
+{
+ *buf = (struct statfs){0};
+#ifdef SYS_fstatfs64
+ return syscall(SYS_fstatfs64, fd, sizeof *buf, buf);
+#else
+ return syscall(SYS_fstatfs, fd, buf);
+#endif
+}
+
+weak_alias(__statfs, statfs);
+weak_alias(__fstatfs, fstatfs);
+
+static void fixup(struct statvfs *out, const struct statfs *in)
+{
+ *out = (struct statvfs){0};
+ out->f_bsize = in->f_bsize;
+ out->f_frsize = in->f_frsize ? in->f_frsize : in->f_bsize;
+ out->f_blocks = in->f_blocks;
+ out->f_bfree = in->f_bfree;
+ out->f_bavail = in->f_bavail;
+ out->f_files = in->f_files;
+ out->f_ffree = in->f_ffree;
+ out->f_favail = in->f_ffree;
+ out->f_fsid = in->f_fsid.__val[0];
+ out->f_flag = in->f_flags;
+ out->f_namemax = in->f_namelen;
+}
+
+int statvfs(const char *restrict path, struct statvfs *restrict buf)
+{
+ struct statfs kbuf;
+ if (__statfs(path, &kbuf)<0) return -1;
+ fixup(buf, &kbuf);
+ return 0;
+}
+
+int fstatvfs(int fd, struct statvfs *buf)
+{
+ struct statfs kbuf;
+ if (__fstatfs(fd, &kbuf)<0) return -1;
+ fixup(buf, &kbuf);
+ return 0;
+}
+
+weak_alias(statvfs, statvfs64);
+weak_alias(statfs, statfs64);
+weak_alias(fstatvfs, fstatvfs64);
+weak_alias(fstatfs, fstatfs64);
diff --git a/libc-top-half/musl/src/stat/umask.c b/libc-top-half/musl/src/stat/umask.c
new file mode 100644
index 0000000..5ee913e
--- /dev/null
+++ b/libc-top-half/musl/src/stat/umask.c
@@ -0,0 +1,7 @@
+#include <sys/stat.h>
+#include "syscall.h"
+
+mode_t umask(mode_t mode)
+{
+ return syscall(SYS_umask, mode);
+}
diff --git a/libc-top-half/musl/src/stat/utimensat.c b/libc-top-half/musl/src/stat/utimensat.c
new file mode 100644
index 0000000..730723a
--- /dev/null
+++ b/libc-top-half/musl/src/stat/utimensat.c
@@ -0,0 +1,60 @@
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "syscall.h"
+
+#define IS32BIT(x) !((x)+0x80000000ULL>>32)
+#define NS_SPECIAL(ns) ((ns)==UTIME_NOW || (ns)==UTIME_OMIT)
+
+int utimensat(int fd, const char *path, const struct timespec times[2], int flags)
+{
+ int r;
+ if (times && times[0].tv_nsec==UTIME_NOW && times[1].tv_nsec==UTIME_NOW)
+ times = 0;
+#ifdef SYS_utimensat_time64
+ r = -ENOSYS;
+ time_t s0=0, s1=0;
+ long ns0=0, ns1=0;
+ if (times) {
+ ns0 = times[0].tv_nsec;
+ ns1 = times[1].tv_nsec;
+ if (!NS_SPECIAL(ns0)) s0 = times[0].tv_sec;
+ if (!NS_SPECIAL(ns1)) s1 = times[1].tv_sec;
+ }
+ if (SYS_utimensat == SYS_utimensat_time64 || !IS32BIT(s0) || !IS32BIT(s1))
+ r = __syscall(SYS_utimensat_time64, fd, path, times ?
+ ((long long[]){s0, ns0, s1, ns1}) : 0, flags);
+ if (SYS_utimensat == SYS_utimensat_time64 || r!=-ENOSYS)
+ return __syscall_ret(r);
+ if (!IS32BIT(s0) || !IS32BIT(s1))
+ return __syscall_ret(-ENOTSUP);
+ r = __syscall(SYS_utimensat, fd, path,
+ times ? ((long[]){s0, ns0, s1, ns1}) : 0, flags);
+#else
+ r = __syscall(SYS_utimensat, fd, path, times, flags);
+#endif
+
+#ifdef SYS_futimesat
+ if (r != -ENOSYS || flags) return __syscall_ret(r);
+ long *tv=0, tmp[4];
+ if (times) {
+ int i;
+ tv = tmp;
+ for (i=0; i<2; i++) {
+ if (times[i].tv_nsec >= 1000000000ULL) {
+ if (NS_SPECIAL(times[i].tv_nsec))
+ return __syscall_ret(-ENOSYS);
+ return __syscall_ret(-EINVAL);
+ }
+ tmp[2*i+0] = times[i].tv_sec;
+ tmp[2*i+1] = times[i].tv_nsec / 1000;
+ }
+ }
+
+ r = __syscall(SYS_futimesat, fd, path, tv);
+ if (r != -ENOSYS || fd != AT_FDCWD) return __syscall_ret(r);
+ r = __syscall(SYS_utimes, path, tv);
+#endif
+ return __syscall_ret(r);
+}