blob: ed37b42aedf6c92c848e9f0b5314a368671a0747 [file] [log] [blame]
//===-- Linux implementation of utimes ------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "src/sys/time/utimes.h"
#include "hdr/fcntl_macros.h"
#include "hdr/types/struct_timespec.h"
#include "hdr/types/struct_timeval.h"
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
#include <sys/syscall.h>
namespace LIBC_NAMESPACE_DECL {
#ifdef SYS_utimes
constexpr auto UTIMES_SYSCALL_ID = SYS_utimes;
#elif defined(SYS_utimensat)
constexpr auto UTIMES_SYSCALL_ID = SYS_utimensat;
#elif defined(SYS_utimensat_time64)
constexpr auto UTIMES_SYSCALL_ID = SYS_utimensat_time64;
#else
#error "utimes, utimensat, utimensat_time64, syscalls not available."
#endif
LLVM_LIBC_FUNCTION(int, utimes,
(const char *path, const struct timeval times[2])) {
int ret;
#ifdef SYS_utimes
// No need to define a timespec struct, use the syscall directly.
ret = LIBC_NAMESPACE::syscall_impl<int>(UTIMES_SYSCALL_ID, path, times);
#elif defined(SYS_utimensat) || defined(SYS_utimensat_time64)
// the utimensat syscall requires a timespec struct, not timeval.
struct timespec ts[2];
struct timespec *ts_ptr = nullptr; // default value if times is nullptr
// convert the microsec values in timeval struct times
// to nanosecond values in timespec struct ts
if (times != nullptr) {
// ensure consistent values
if ((times[0].tv_usec < 0 || times[1].tv_usec < 0) ||
(times[0].tv_usec >= 1000000 || times[1].tv_usec >= 1000000)) {
libc_errno = EINVAL;
return -1;
}
// set seconds in ts
ts[0].tv_sec = times[0].tv_sec;
ts[1].tv_sec = times[1].tv_sec;
// convert u-seconds to nanoseconds
ts[0].tv_nsec = times[0].tv_usec * 1000;
ts[1].tv_nsec = times[1].tv_usec * 1000;
ts_ptr = ts;
}
// If times was nullptr, ts_ptr remains nullptr, which utimensat interprets
// as setting times to the current time.
// utimensat syscall.
// flags=0 means don't follow symlinks (like utimes)
ret = LIBC_NAMESPACE::syscall_impl<int>(UTIMES_SYSCALL_ID, AT_FDCWD, path,
ts_ptr, 0);
#endif // SYS_utimensat
if (ret < 0) {
libc_errno = -ret;
return -1;
}
return 0;
}
} // namespace LIBC_NAMESPACE_DECL