Skip to content

Commit 5522dcc

Browse files
committed
Implement faccessat
1 parent 80beefa commit 5522dcc

File tree

10 files changed

+272
-0
lines changed

10 files changed

+272
-0
lines changed

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ set(TARGET_LIBC_ENTRYPOINTS
325325
libc.src.unistd.dup2
326326
libc.src.unistd.dup3
327327
libc.src.unistd.execve
328+
libc.src.unistd.faccessat
328329
libc.src.unistd.fchdir
329330
libc.src.unistd.fpathconf
330331
libc.src.unistd.fsync

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ set(TARGET_LIBC_ENTRYPOINTS
331331
libc.src.unistd.dup2
332332
libc.src.unistd.dup3
333333
libc.src.unistd.execve
334+
libc.src.unistd.faccessat
334335
libc.src.unistd.fchdir
335336
libc.src.unistd.fpathconf
336337
libc.src.unistd.fsync

libc/include/llvm-libc-macros/linux/fcntl-macros.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@
6161
// Allow empty relative pathname.
6262
#define AT_EMPTY_PATH 0x1000
6363

64+
// Perform access checks using the effective user and group IDs.
65+
#define AT_EACCESS
66+
6467
// Values of SYS_fcntl commands.
6568
#define F_DUPFD 0
6669
#define F_GETFD 1

libc/include/unistd.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ functions:
9696
- type: const char *
9797
- type: __exec_argv_t
9898
- type: __exec_envp_t
99+
- name: faccessat
100+
standards:
101+
- POSIX
102+
return_type: int
103+
arguments:
104+
- type: int
105+
- type: const char *
106+
- type: int
107+
- type: int
99108
- name: fchdir
100109
standards:
101110
- POSIX

libc/src/unistd/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ add_entrypoint_object(
5555
.${LIBC_TARGET_OS}.dup3
5656
)
5757

58+
add_entrypoint_object(
59+
faccessat
60+
ALIAS
61+
DEPENDS
62+
.${LIBC_TARGET_OS}.faccessat
63+
)
64+
5865
add_entrypoint_object(
5966
fchdir
6067
ALIAS

libc/src/unistd/faccessat.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation header for faccessat ------------------------*- C++ -*-===//
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_UNISTD_FACCESSAT_H
10+
#define LLVM_LIBC_SRC_UNISTD_FACCESSAT_H
11+
12+
13+
#include "src/__support/macros/config.h"
14+
15+
namespace LIBC_NAMESPACE_DECL {
16+
17+
int faccessat(int fd, const char *path, int amode, int flag);
18+
19+
} // namespace LIBC_NAMESPACE_DECL
20+
21+
#endif // LLVM_LIBC_SRC_UNISTD_FACCESSAT_H

libc/src/unistd/linux/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,20 @@ add_entrypoint_object(
8080
libc.src.errno.errno
8181
)
8282

83+
add_entrypoint_object(
84+
faccessat
85+
SRCS
86+
faccessat.cpp
87+
HDRS
88+
../faccessat.h
89+
DEPENDS
90+
libc.hdr.fcntl_macros
91+
libc.include.unistd
92+
libc.include.sys_syscall
93+
libc.src.__support.OSUtil.osutil
94+
libc.src.errno.errno
95+
)
96+
8397
add_entrypoint_object(
8498
fchdir
8599
SRCS
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===-- Linux implementation of faccessat ------------------------------------===//
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/unistd/faccessat.h"
10+
11+
#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
12+
#include "src/__support/common.h"
13+
14+
#include "hdr/fcntl_macros.h"
15+
#include "src/__support/libc_errno.h"
16+
#include "src/__support/macros/config.h"
17+
#include <sys/syscall.h> // For syscall numbers.
18+
19+
namespace LIBC_NAMESPACE_DECL {
20+
21+
LLVM_LIBC_FUNCTION(int, faccessat, (int fd, const char *path, int amode, int flag)) {
22+
#ifdef SYS_faccessat2
23+
int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_faccessat2, fd, path, amode, flag);
24+
#else
25+
#error "faccessat2 syscall is not available."
26+
#endif
27+
28+
if (ret < 0) {
29+
libc_errno = -ret;
30+
return -1;
31+
}
32+
return 0;
33+
}
34+
35+
} // namespace LIBC_NAMESPACE_DECL

libc/test/src/unistd/CMakeLists.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,28 @@ add_libc_unittest(
9393
libc.test.UnitTest.ErrnoSetterMatcher
9494
)
9595

96+
add_libc_unittest(
97+
faccessat_test
98+
SUITE
99+
libc_unistd_unittests
100+
SRCS
101+
faccessat_test.cpp
102+
DEPENDS
103+
libc.include.unistd
104+
libc.src.__support.CPP.string
105+
libc.src.sys.stat.mkdir
106+
libc.src.errno.errno
107+
libc.src.fcntl.open
108+
libc.src.unistd.faccessat
109+
libc.src.unistd.close
110+
libc.src.unistd.symlink
111+
libc.src.unistd.unlink
112+
libc.src.unistd.unlinkat
113+
libc.src.unistd.rmdir
114+
libc.test.UnitTest.ErrnoCheckingTest
115+
libc.test.UnitTest.ErrnoSetterMatcher
116+
)
117+
96118
add_libc_unittest(
97119
fchdir_test
98120
SUITE
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
//===-- Unittests for faccessat -------------------------------------------===//
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/fcntl/open.h"
10+
#include "src/__support/CPP/string.h"
11+
#include "src/sys/stat/mkdir.h"
12+
#include "src/unistd/faccessat.h"
13+
#include "src/unistd/close.h"
14+
#include "src/unistd/unlink.h"
15+
#include "src/unistd/unlinkat.h"
16+
#include "src/unistd/symlink.h"
17+
#include "src/unistd/rmdir.h"
18+
#include "src/unistd/symlink.h"
19+
#include "test/UnitTest/ErrnoCheckingTest.h"
20+
#include "test/UnitTest/ErrnoSetterMatcher.h"
21+
#include "test/UnitTest/Test.h"
22+
23+
#include <fcntl.h> // For AT_FDCWD, AT_EACCESS, AT_SYMLINK_NOFOLLOW
24+
#include <sys/stat.h> // For S_IRWXU, S_IXUSR, etc.
25+
#include <unistd.h> // For F_OK, R_OK, W_OK, X_OK
26+
27+
28+
namespace {
29+
30+
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
31+
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
32+
33+
34+
class LlvmLibcFaccessatTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {
35+
public:
36+
void create_test_dir(const char* dirname, int& dir_fd) {
37+
auto TEST_DIR = libc_make_test_file_path(dirname);
38+
ASSERT_THAT(LIBC_NAMESPACE::mkdir(TEST_DIR, S_IRWXU), Succeeds(0));
39+
40+
dir_fd = LIBC_NAMESPACE::open(TEST_DIR, O_RDONLY | O_DIRECTORY);
41+
ASSERT_ERRNO_SUCCESS();
42+
ASSERT_GT(dir_fd, 0);
43+
}
44+
45+
void cleanup_test_dir(const char* dirname, int dir_fd) {
46+
auto TEST_DIR = libc_make_test_file_path(dirname);
47+
ASSERT_THAT(LIBC_NAMESPACE::close(dir_fd), Succeeds(0));
48+
ASSERT_THAT(LIBC_NAMESPACE::rmdir(TEST_DIR), Succeeds(0));
49+
}
50+
};
51+
52+
53+
TEST_F(LlvmLibcFaccessatTest, CreateAndTest_AT_FDCWD) {
54+
// Test access checks on a file with AT_FDCWD, equivalent to access().
55+
constexpr const char *FILENAME = "faccessat_basic3.test";
56+
auto TEST_FILE = libc_make_test_file_path(FILENAME);
57+
58+
// Create file with full permissions
59+
int fd = LIBC_NAMESPACE::open(TEST_FILE, O_WRONLY | O_CREAT, S_IRWXU);
60+
ASSERT_ERRNO_SUCCESS();
61+
ASSERT_GT(fd, 0);
62+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
63+
64+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(AT_FDCWD, TEST_FILE, F_OK, 0), Succeeds(0));
65+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(AT_FDCWD, TEST_FILE, X_OK | W_OK | R_OK, 0),
66+
Succeeds(0));
67+
ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
68+
69+
// Create file with execute-only permission
70+
fd = LIBC_NAMESPACE::open(TEST_FILE, O_WRONLY | O_CREAT, S_IXUSR);
71+
ASSERT_ERRNO_SUCCESS();
72+
ASSERT_GT(fd, 0);
73+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
74+
75+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(AT_FDCWD, TEST_FILE, F_OK, 0), Succeeds(0));
76+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(AT_FDCWD, TEST_FILE, X_OK, 0), Succeeds(0));
77+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(AT_FDCWD, TEST_FILE, R_OK, 0), Fails(EACCES));
78+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(AT_FDCWD, TEST_FILE, W_OK, 0), Fails(EACCES));
79+
ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
80+
}
81+
82+
TEST_F(LlvmLibcFaccessatTest, RelativePathWithDirFD) {
83+
LIBC_NAMESPACE::cpp::string DIRNAME ="faccessat_dir3";
84+
LIBC_NAMESPACE::cpp::string FILENAME = "relative_file3.txt";
85+
86+
int dir_fd;
87+
create_test_dir(DIRNAME.data(), dir_fd);
88+
89+
auto FULL_FILE_PATH = libc_make_test_file_path((DIRNAME + "/" + FILENAME).data());
90+
int fd = LIBC_NAMESPACE::open(FULL_FILE_PATH, O_WRONLY | O_CREAT, S_IRWXU);
91+
ASSERT_ERRNO_SUCCESS();
92+
ASSERT_GT(fd, 0);
93+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
94+
95+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(dir_fd, FILENAME.data(), R_OK | W_OK, 0), Succeeds(0));
96+
97+
ASSERT_THAT(LIBC_NAMESPACE::unlinkat(dir_fd, FILENAME.data(), 0), Succeeds(0));
98+
cleanup_test_dir(DIRNAME.data(), dir_fd);
99+
}
100+
101+
TEST_F(LlvmLibcFaccessatTest, SymlinkNoFollow) {
102+
constexpr const char *TARGET = "faccessat_target2";
103+
constexpr const char *SYMLINK = "faccessat_link2";
104+
auto TEST_TARGET = libc_make_test_file_path(TARGET);
105+
auto TEST_SYMLINK = libc_make_test_file_path(SYMLINK);
106+
107+
int fd = LIBC_NAMESPACE::open(TEST_TARGET, O_WRONLY | O_CREAT, 0);
108+
ASSERT_ERRNO_SUCCESS();
109+
ASSERT_GT(fd, 0);
110+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
111+
112+
ASSERT_THAT(LIBC_NAMESPACE::symlink(TARGET, TEST_SYMLINK), Succeeds(0));
113+
114+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(AT_FDCWD, TEST_SYMLINK, R_OK, 0), Fails(EACCES));
115+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(AT_FDCWD, TEST_SYMLINK, F_OK, AT_SYMLINK_NOFOLLOW), Succeeds(0));
116+
117+
ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_SYMLINK), Succeeds(0));
118+
ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_TARGET), Succeeds(0));
119+
}
120+
121+
TEST_F(LlvmLibcFaccessatTest, AtEaccess) {
122+
// With AT_EACCESS, faccessat checks permissions using the effective user ID,
123+
// but the effective and real user ID will be the same here and changing that is not feasible in a test,
124+
// so this is just a basic sanity check.
125+
constexpr const char *FILENAME = "faccessat_eaccess.test";
126+
auto TEST_FILE = libc_make_test_file_path(FILENAME);
127+
128+
int fd = LIBC_NAMESPACE::open(TEST_FILE, O_WRONLY | O_CREAT, S_IRWXU);
129+
ASSERT_ERRNO_SUCCESS();
130+
ASSERT_GT(fd, 0);
131+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
132+
133+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(AT_FDCWD, TEST_FILE, X_OK | W_OK | R_OK, AT_EACCESS),
134+
Succeeds(0));
135+
136+
ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
137+
}
138+
139+
TEST_F(LlvmLibcFaccessatTest, AtEmptyPath) {
140+
constexpr const char *FILENAME = "faccessat_atemptypath.test";
141+
auto TEST_FILE = libc_make_test_file_path(FILENAME);
142+
143+
int fd = LIBC_NAMESPACE::open(TEST_FILE, O_WRONLY | O_CREAT, S_IRWXU);
144+
ASSERT_ERRNO_SUCCESS();
145+
ASSERT_GT(fd, 0);
146+
147+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(fd, "", F_OK, AT_EMPTY_PATH), Succeeds(0));
148+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(fd, "", X_OK | W_OK | R_OK, AT_EMPTY_PATH),
149+
Succeeds(0));
150+
151+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
152+
ASSERT_THAT(LIBC_NAMESPACE::unlink(TEST_FILE), Succeeds(0));
153+
154+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(AT_FDCWD, "", F_OK, AT_EMPTY_PATH), Succeeds(0));
155+
ASSERT_THAT(LIBC_NAMESPACE::faccessat(AT_FDCWD, "", X_OK | W_OK | R_OK, AT_EMPTY_PATH),
156+
Succeeds(0));
157+
}
158+
159+
} // namespace

0 commit comments

Comments
 (0)