[libc-commits] [libc] d33ae41 - [libc] Implemented utimes (Issue #133953) (#134167)
via libc-commits
libc-commits at lists.llvm.org
Thu Apr 3 16:19:16 PDT 2025
Author: Aditya Tejpaul
Date: 2025-04-03T16:19:12-07:00
New Revision: d33ae41c621dfbfb1eda5d469e2fb146ef49fbf9
URL: https://github.com/llvm/llvm-project/commit/d33ae41c621dfbfb1eda5d469e2fb146ef49fbf9
DIFF: https://github.com/llvm/llvm-project/commit/d33ae41c621dfbfb1eda5d469e2fb146ef49fbf9.diff
LOG: [libc] Implemented utimes (Issue #133953) (#134167)
This pull request implements the `utimes` command in libc ([Issue
#133953](https://github.com/llvm/llvm-project/issues/133953)).
- [x] Add the implementation of `utimes` in `/src/sys/time`.
- [x] Add tests for `utimes` in `/test/src/sys/time`.
- [x] Add `utimes` to
[entrypoints.txt](https://github.com/llvm/llvm-project/blob/main/libc/config/linux/x86_64/entrypoints.txt)
for at least x86_64 and whatever you're building on
- [x] Add `utimes` to
[include/sys/time.yaml](https://github.com/llvm/llvm-project/blob/main/libc/include/sys/time.yaml)
Added:
libc/src/sys/time/CMakeLists.txt
libc/src/sys/time/linux/CMakeLists.txt
libc/src/sys/time/linux/utimes.cpp
libc/src/sys/time/utimes.h
libc/test/src/sys/time/CMakeLists.txt
libc/test/src/sys/time/utimes_test.cpp
Modified:
libc/config/linux/x86_64/entrypoints.txt
libc/include/sys/time.yaml
libc/src/sys/CMakeLists.txt
libc/test/src/sys/CMakeLists.txt
Removed:
################################################################################
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index eccd222fa123e..1ac3a781d5279 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -288,6 +288,9 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.statvfs.fstatvfs
libc.src.sys.statvfs.statvfs
+ # sys/utimes.h entrypoints
+ libc.src.sys.time.utimes
+
# sys/utsname.h entrypoints
libc.src.sys.utsname.uname
diff --git a/libc/include/sys/time.yaml b/libc/include/sys/time.yaml
index ca497bbe92995..92ab9a467f33a 100644
--- a/libc/include/sys/time.yaml
+++ b/libc/include/sys/time.yaml
@@ -5,5 +5,10 @@ macros: []
types:
- type_name: struct_timeval
enums: []
-functions: []
objects: []
+functions:
+ - name: utimes
+ return_type: int
+ arguments:
+ - type: const char*
+ - type: const struct timeval*
diff --git a/libc/src/sys/CMakeLists.txt b/libc/src/sys/CMakeLists.txt
index bb177f11c6d62..9a73b80d35d2f 100644
--- a/libc/src/sys/CMakeLists.txt
+++ b/libc/src/sys/CMakeLists.txt
@@ -8,6 +8,7 @@ add_subdirectory(socket)
add_subdirectory(sendfile)
add_subdirectory(stat)
add_subdirectory(statvfs)
+add_subdirectory(time)
add_subdirectory(utsname)
add_subdirectory(wait)
add_subdirectory(prctl)
diff --git a/libc/src/sys/time/CMakeLists.txt b/libc/src/sys/time/CMakeLists.txt
new file mode 100644
index 0000000000000..f599cddaaeeb3
--- /dev/null
+++ b/libc/src/sys/time/CMakeLists.txt
@@ -0,0 +1,10 @@
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+endif()
+
+add_entrypoint_object(
+ utimes
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.utimes
+)
diff --git a/libc/src/sys/time/linux/CMakeLists.txt b/libc/src/sys/time/linux/CMakeLists.txt
new file mode 100644
index 0000000000000..506001e5c9fd2
--- /dev/null
+++ b/libc/src/sys/time/linux/CMakeLists.txt
@@ -0,0 +1,16 @@
+add_entrypoint_object(
+ utimes
+ SRCS
+ utimes.cpp
+ HDRS
+ ../utimes.h
+ DEPENDS
+ libc.hdr.types.struct_timeval
+ libc.hdr.fcntl_macros
+ libc.src.__support.OSUtil.osutil
+ libc.include.sys_stat
+ libc.include.sys_syscall
+ libc.include.fcntl
+ libc.src.__support.OSUtil.osutil
+ libc.src.errno.errno
+)
diff --git a/libc/src/sys/time/linux/utimes.cpp b/libc/src/sys/time/linux/utimes.cpp
new file mode 100644
index 0000000000000..1cc5a8344e91a
--- /dev/null
+++ b/libc/src/sys/time/linux/utimes.cpp
@@ -0,0 +1,76 @@
+//===-- 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_timeval.h"
+
+#include "src/__support/OSUtil/syscall.h"
+#include "src/__support/common.h"
+
+#include "src/errno/libc_errno.h"
+
+#include <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+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>(SYS_utimes, path, times);
+#elif defined(SYS_utimensat)
+ // the utimensat syscall requires a timespec struct, not timeval.
+ struct timespec ts[2];
+ struct timespec *ts_ptr = nullptr; // default value if times is NULL
+
+ // convert the microsec values in timeval struct times
+ // to nanosecond values in timespec struct ts
+ if (times != NULL) {
+
+ // 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 NULL, ts_ptr remains NULL, 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>(SYS_utimensat, AT_FDCWD, path, ts_ptr,
+ 0);
+
+#else
+#error "utimensat and utimes syscalls not available."
+#endif // SYS_utimensat
+
+ if (ret < 0) {
+ libc_errno = -ret;
+ return -1;
+ }
+
+ return 0;
+}
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/time/utimes.h b/libc/src/sys/time/utimes.h
new file mode 100644
index 0000000000000..6e19e412d69ac
--- /dev/null
+++ b/libc/src/sys/time/utimes.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_TIME_UTIMES_H
+#define LLVM_LIBC_SRC_SYS_TIME_UTIMES_H
+
+#include "hdr/types/struct_timeval.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int utimes(const char *path, const struct timeval times[2]);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_TIME_UTIMES_H
diff --git a/libc/test/src/sys/CMakeLists.txt b/libc/test/src/sys/CMakeLists.txt
index 9e9293aab628f..224cc7905ad31 100644
--- a/libc/test/src/sys/CMakeLists.txt
+++ b/libc/test/src/sys/CMakeLists.txt
@@ -12,3 +12,4 @@ add_subdirectory(prctl)
add_subdirectory(auxv)
add_subdirectory(epoll)
add_subdirectory(uio)
+add_subdirectory(time)
diff --git a/libc/test/src/sys/time/CMakeLists.txt b/libc/test/src/sys/time/CMakeLists.txt
new file mode 100644
index 0000000000000..c092d33e43d85
--- /dev/null
+++ b/libc/test/src/sys/time/CMakeLists.txt
@@ -0,0 +1,19 @@
+add_custom_target(libc_sys_time_unittests)
+
+add_libc_unittest(
+ utimes_test
+ SUITE
+ libc_sys_time_unittests
+ SRCS
+ utimes_test.cpp
+ DEPENDS
+ libc.hdr.fcntl_macros
+ libc.src.errno.errno
+ libc.src.fcntl.open
+ libc.src.sys.time.utimes
+ libc.src.unistd.close
+ libc.src.unistd.read
+ libc.src.unistd.write
+ libc.src.stdio.remove
+ libc.src.sys.stat.stat
+)
diff --git a/libc/test/src/sys/time/utimes_test.cpp b/libc/test/src/sys/time/utimes_test.cpp
new file mode 100644
index 0000000000000..b97befb8626e3
--- /dev/null
+++ b/libc/test/src/sys/time/utimes_test.cpp
@@ -0,0 +1,92 @@
+//===-- Unittests for 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 "hdr/fcntl_macros.h"
+#include "hdr/types/struct_timeval.h"
+#include "src/errno/libc_errno.h"
+#include "src/fcntl/open.h"
+#include "src/stdio/remove.h"
+#include "src/sys/stat/stat.h"
+#include "src/sys/time/utimes.h"
+#include "src/unistd/close.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+constexpr const char *FILE_PATH = "utimes.test";
+
+// SUCCESS: Takes a file and successfully updates
+// its last access and modified times.
+TEST(LlvmLibcUtimesTest, ChangeTimesSpecific) {
+ using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+
+ auto TEST_FILE = libc_make_test_file_path(FILE_PATH);
+ int fd = LIBC_NAMESPACE::open(TEST_FILE, O_WRONLY | O_CREAT);
+ ASSERT_GT(fd, 0);
+ ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+ // make a dummy timeval struct
+ struct timeval times[2];
+ times[0].tv_sec = 54321;
+ times[0].tv_usec = 12345;
+ times[1].tv_sec = 43210;
+ times[1].tv_usec = 23456;
+
+ // ensure utimes succeeds
+ ASSERT_THAT(LIBC_NAMESPACE::utimes(FILE_PATH, times), Succeeds(0));
+
+ // verify the times values against stat of the TEST_FILE
+ struct stat statbuf;
+ ASSERT_EQ(LIBC_NAMESPACE::stat(FILE_PATH, &statbuf), 0);
+
+ // seconds
+ ASSERT_EQ(statbuf.st_atim.tv_sec, times[0].tv_sec);
+ ASSERT_EQ(statbuf.st_mtim.tv_sec, times[1].tv_sec);
+
+ // microseconds
+ ASSERT_EQ(statbuf.st_atim.tv_nsec,
+ static_cast<long>(times[0].tv_usec * 1000));
+ ASSERT_EQ(statbuf.st_mtim.tv_nsec,
+ static_cast<long>(times[1].tv_usec * 1000));
+
+ ASSERT_THAT(LIBC_NAMESPACE::remove(TEST_FILE), Succeeds(0));
+}
+
+// FAILURE: Invalid values in the timeval struct
+// to check that utimes rejects it.
+TEST(LlvmLibcUtimesTest, InvalidMicroseconds) {
+ using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+ using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+
+ auto TEST_FILE = libc_make_test_file_path(FILE_PATH);
+ int fd = LIBC_NAMESPACE::open(TEST_FILE, O_WRONLY | O_CREAT);
+ ASSERT_GT(fd, 0);
+ ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
+
+ // make a dummy timeval struct
+ // populated with bad usec values
+ struct timeval times[2];
+ times[0].tv_sec = 54321;
+ times[0].tv_usec = 4567;
+ times[1].tv_sec = 43210;
+ times[1].tv_usec = 1000000; // invalid
+
+ // ensure utimes fails
+ ASSERT_THAT(LIBC_NAMESPACE::utimes(FILE_PATH, times), Fails(EINVAL));
+
+ // check for failure on
+ // the other possible bad values
+
+ times[0].tv_sec = 54321;
+ times[0].tv_usec = -4567; // invalid
+ times[1].tv_sec = 43210;
+ times[1].tv_usec = 1000;
+
+ // ensure utimes fails once more
+ ASSERT_THAT(LIBC_NAMESPACE::utimes(FILE_PATH, times), Fails(EINVAL));
+ ASSERT_THAT(LIBC_NAMESPACE::remove(TEST_FILE), Succeeds(0));
+}
More information about the libc-commits
mailing list