Skip to content

Commit d33ae41

Browse files
authored
[libc] Implemented utimes (Issue #133953) (#134167)
This pull request implements the `utimes` command in libc ([Issue #133953](#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)
1 parent bd197ca commit d33ae41

File tree

10 files changed

+245
-1
lines changed

10 files changed

+245
-1
lines changed

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,9 @@ set(TARGET_LIBC_ENTRYPOINTS
288288
libc.src.sys.statvfs.fstatvfs
289289
libc.src.sys.statvfs.statvfs
290290

291+
# sys/utimes.h entrypoints
292+
libc.src.sys.time.utimes
293+
291294
# sys/utsname.h entrypoints
292295
libc.src.sys.utsname.uname
293296

libc/include/sys/time.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,10 @@ macros: []
55
types:
66
- type_name: struct_timeval
77
enums: []
8-
functions: []
98
objects: []
9+
functions:
10+
- name: utimes
11+
return_type: int
12+
arguments:
13+
- type: const char*
14+
- type: const struct timeval*

libc/src/sys/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_subdirectory(socket)
88
add_subdirectory(sendfile)
99
add_subdirectory(stat)
1010
add_subdirectory(statvfs)
11+
add_subdirectory(time)
1112
add_subdirectory(utsname)
1213
add_subdirectory(wait)
1314
add_subdirectory(prctl)

libc/src/sys/time/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
2+
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
3+
endif()
4+
5+
add_entrypoint_object(
6+
utimes
7+
ALIAS
8+
DEPENDS
9+
.${LIBC_TARGET_OS}.utimes
10+
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
add_entrypoint_object(
2+
utimes
3+
SRCS
4+
utimes.cpp
5+
HDRS
6+
../utimes.h
7+
DEPENDS
8+
libc.hdr.types.struct_timeval
9+
libc.hdr.fcntl_macros
10+
libc.src.__support.OSUtil.osutil
11+
libc.include.sys_stat
12+
libc.include.sys_syscall
13+
libc.include.fcntl
14+
libc.src.__support.OSUtil.osutil
15+
libc.src.errno.errno
16+
)

libc/src/sys/time/linux/utimes.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//===-- Linux implementation of utimes ------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/sys/time/utimes.h"
10+
11+
#include "hdr/fcntl_macros.h"
12+
#include "hdr/types/struct_timeval.h"
13+
14+
#include "src/__support/OSUtil/syscall.h"
15+
#include "src/__support/common.h"
16+
17+
#include "src/errno/libc_errno.h"
18+
19+
#include <sys/syscall.h>
20+
21+
namespace LIBC_NAMESPACE_DECL {
22+
23+
LLVM_LIBC_FUNCTION(int, utimes,
24+
(const char *path, const struct timeval times[2])) {
25+
int ret;
26+
27+
#ifdef SYS_utimes
28+
// No need to define a timespec struct, use the syscall directly.
29+
ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_utimes, path, times);
30+
#elif defined(SYS_utimensat)
31+
// the utimensat syscall requires a timespec struct, not timeval.
32+
struct timespec ts[2];
33+
struct timespec *ts_ptr = nullptr; // default value if times is NULL
34+
35+
// convert the microsec values in timeval struct times
36+
// to nanosecond values in timespec struct ts
37+
if (times != NULL) {
38+
39+
// ensure consistent values
40+
if ((times[0].tv_usec < 0 || times[1].tv_usec < 0) ||
41+
(times[0].tv_usec >= 1000000 || times[1].tv_usec >= 1000000)) {
42+
libc_errno = EINVAL;
43+
return -1;
44+
}
45+
46+
// set seconds in ts
47+
ts[0].tv_sec = times[0].tv_sec;
48+
ts[1].tv_sec = times[1].tv_sec;
49+
50+
// convert u-seconds to nanoseconds
51+
ts[0].tv_nsec = times[0].tv_usec * 1000;
52+
ts[1].tv_nsec = times[1].tv_usec * 1000;
53+
54+
ts_ptr = ts;
55+
}
56+
57+
// If times was NULL, ts_ptr remains NULL, which utimensat interprets
58+
// as setting times to the current time.
59+
60+
// utimensat syscall.
61+
// flags=0 means don't follow symlinks (like utimes)
62+
ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_utimensat, AT_FDCWD, path, ts_ptr,
63+
0);
64+
65+
#else
66+
#error "utimensat and utimes syscalls not available."
67+
#endif // SYS_utimensat
68+
69+
if (ret < 0) {
70+
libc_errno = -ret;
71+
return -1;
72+
}
73+
74+
return 0;
75+
}
76+
} // namespace LIBC_NAMESPACE_DECL

libc/src/sys/time/utimes.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation header for utimes ----------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_SYS_TIME_UTIMES_H
10+
#define LLVM_LIBC_SRC_SYS_TIME_UTIMES_H
11+
12+
#include "hdr/types/struct_timeval.h"
13+
#include "src/__support/macros/config.h"
14+
15+
namespace LIBC_NAMESPACE_DECL {
16+
17+
int utimes(const char *path, const struct timeval times[2]);
18+
19+
} // namespace LIBC_NAMESPACE_DECL
20+
21+
#endif // LLVM_LIBC_SRC_SYS_TIME_UTIMES_H

libc/test/src/sys/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ add_subdirectory(prctl)
1212
add_subdirectory(auxv)
1313
add_subdirectory(epoll)
1414
add_subdirectory(uio)
15+
add_subdirectory(time)

libc/test/src/sys/time/CMakeLists.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
add_custom_target(libc_sys_time_unittests)
2+
3+
add_libc_unittest(
4+
utimes_test
5+
SUITE
6+
libc_sys_time_unittests
7+
SRCS
8+
utimes_test.cpp
9+
DEPENDS
10+
libc.hdr.fcntl_macros
11+
libc.src.errno.errno
12+
libc.src.fcntl.open
13+
libc.src.sys.time.utimes
14+
libc.src.unistd.close
15+
libc.src.unistd.read
16+
libc.src.unistd.write
17+
libc.src.stdio.remove
18+
libc.src.sys.stat.stat
19+
)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//===-- Unittests for utimes ----------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "hdr/fcntl_macros.h"
10+
#include "hdr/types/struct_timeval.h"
11+
#include "src/errno/libc_errno.h"
12+
#include "src/fcntl/open.h"
13+
#include "src/stdio/remove.h"
14+
#include "src/sys/stat/stat.h"
15+
#include "src/sys/time/utimes.h"
16+
#include "src/unistd/close.h"
17+
#include "test/UnitTest/ErrnoSetterMatcher.h"
18+
#include "test/UnitTest/Test.h"
19+
20+
constexpr const char *FILE_PATH = "utimes.test";
21+
22+
// SUCCESS: Takes a file and successfully updates
23+
// its last access and modified times.
24+
TEST(LlvmLibcUtimesTest, ChangeTimesSpecific) {
25+
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
26+
27+
auto TEST_FILE = libc_make_test_file_path(FILE_PATH);
28+
int fd = LIBC_NAMESPACE::open(TEST_FILE, O_WRONLY | O_CREAT);
29+
ASSERT_GT(fd, 0);
30+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
31+
32+
// make a dummy timeval struct
33+
struct timeval times[2];
34+
times[0].tv_sec = 54321;
35+
times[0].tv_usec = 12345;
36+
times[1].tv_sec = 43210;
37+
times[1].tv_usec = 23456;
38+
39+
// ensure utimes succeeds
40+
ASSERT_THAT(LIBC_NAMESPACE::utimes(FILE_PATH, times), Succeeds(0));
41+
42+
// verify the times values against stat of the TEST_FILE
43+
struct stat statbuf;
44+
ASSERT_EQ(LIBC_NAMESPACE::stat(FILE_PATH, &statbuf), 0);
45+
46+
// seconds
47+
ASSERT_EQ(statbuf.st_atim.tv_sec, times[0].tv_sec);
48+
ASSERT_EQ(statbuf.st_mtim.tv_sec, times[1].tv_sec);
49+
50+
// microseconds
51+
ASSERT_EQ(statbuf.st_atim.tv_nsec,
52+
static_cast<long>(times[0].tv_usec * 1000));
53+
ASSERT_EQ(statbuf.st_mtim.tv_nsec,
54+
static_cast<long>(times[1].tv_usec * 1000));
55+
56+
ASSERT_THAT(LIBC_NAMESPACE::remove(TEST_FILE), Succeeds(0));
57+
}
58+
59+
// FAILURE: Invalid values in the timeval struct
60+
// to check that utimes rejects it.
61+
TEST(LlvmLibcUtimesTest, InvalidMicroseconds) {
62+
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
63+
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
64+
65+
auto TEST_FILE = libc_make_test_file_path(FILE_PATH);
66+
int fd = LIBC_NAMESPACE::open(TEST_FILE, O_WRONLY | O_CREAT);
67+
ASSERT_GT(fd, 0);
68+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
69+
70+
// make a dummy timeval struct
71+
// populated with bad usec values
72+
struct timeval times[2];
73+
times[0].tv_sec = 54321;
74+
times[0].tv_usec = 4567;
75+
times[1].tv_sec = 43210;
76+
times[1].tv_usec = 1000000; // invalid
77+
78+
// ensure utimes fails
79+
ASSERT_THAT(LIBC_NAMESPACE::utimes(FILE_PATH, times), Fails(EINVAL));
80+
81+
// check for failure on
82+
// the other possible bad values
83+
84+
times[0].tv_sec = 54321;
85+
times[0].tv_usec = -4567; // invalid
86+
times[1].tv_sec = 43210;
87+
times[1].tv_usec = 1000;
88+
89+
// ensure utimes fails once more
90+
ASSERT_THAT(LIBC_NAMESPACE::utimes(FILE_PATH, times), Fails(EINVAL));
91+
ASSERT_THAT(LIBC_NAMESPACE::remove(TEST_FILE), Succeeds(0));
92+
}

0 commit comments

Comments
 (0)