[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