Skip to content

Commit fff05af

Browse files
committed
[libc] Implement fcntl() function
1 parent 0620a63 commit fff05af

File tree

9 files changed

+299
-0
lines changed

9 files changed

+299
-0
lines changed

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ set(TARGET_LIBC_ENTRYPOINTS
2222

2323
# fcntl.h entrypoints
2424
libc.src.fcntl.creat
25+
libc.src.fcntl.fcntl
2526
libc.src.fcntl.open
2627
libc.src.fcntl.openat
2728

libc/config/linux/riscv/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ set(TARGET_LIBC_ENTRYPOINTS
2222

2323
# fcntl.h entrypoints
2424
libc.src.fcntl.creat
25+
libc.src.fcntl.fcntl
2526
libc.src.fcntl.open
2627
libc.src.fcntl.openat
2728

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ set(TARGET_LIBC_ENTRYPOINTS
2222

2323
# fcntl.h entrypoints
2424
libc.src.fcntl.creat
25+
libc.src.fcntl.fcntl
2526
libc.src.fcntl.open
2627
libc.src.fcntl.openat
2728

libc/src/fcntl/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ add_entrypoint_object(
99
.${LIBC_TARGET_OS}.creat
1010
)
1111

12+
add_entrypoint_object(
13+
fcntl
14+
ALIAS
15+
DEPENDS
16+
.${LIBC_TARGET_OS}.fcntl
17+
)
18+
1219
add_entrypoint_object(
1320
open
1421
ALIAS

libc/src/fcntl/fcntl.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header of fcntl --------------------------*- 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_FCNTL_FCNTL_H
10+
#define LLVM_LIBC_SRC_FCNTL_FCNTL_H
11+
12+
#include <fcntl.h>
13+
14+
namespace LIBC_NAMESPACE {
15+
16+
int fcntl(int fd, int cmd, ...);
17+
18+
} // namespace LIBC_NAMESPACE
19+
20+
#endif // LLVM_LIBC_SRC_FCNTL_FCNTL_H

libc/src/fcntl/linux/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ add_entrypoint_object(
1010
libc.src.errno.errno
1111
)
1212

13+
add_entrypoint_object(
14+
fcntl
15+
SRCS
16+
fcntl.cpp
17+
HDRS
18+
../fcntl.h
19+
DEPENDS
20+
libc.include.fcntl
21+
libc.src.__support.OSUtil.osutil
22+
libc.src.errno.errno
23+
)
24+
1325
add_entrypoint_object(
1426
open
1527
SRCS

libc/src/fcntl/linux/fcntl.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//===-- Implementation of fcntl -------------------------------------------===//
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/fcntl.h"
10+
11+
#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
12+
#include "src/__support/common.h"
13+
#include "src/errno/libc_errno.h"
14+
15+
#include <stdarg.h>
16+
#include <sys/syscall.h> // For syscall numbers.
17+
18+
namespace {
19+
struct fowner_ex {
20+
int type;
21+
pid_t pid;
22+
};
23+
} // namespace
24+
25+
// The OFD file locks require special handling for LARGEFILES
26+
namespace LIBC_NAMESPACE {
27+
LLVM_LIBC_FUNCTION(int, fcntl, (int fd, int cmd, ...)) {
28+
void *arg;
29+
va_list varargs;
30+
va_start(varargs, cmd);
31+
arg = va_arg(varargs, void *);
32+
va_end(varargs);
33+
34+
switch (cmd) {
35+
case F_SETLKW:
36+
return syscall_impl<int>(SYS_fcntl, fd, cmd, arg);
37+
case F_OFD_SETLKW: {
38+
struct flock *flk = (struct flock *)arg;
39+
// convert the struct to a flock64
40+
struct flock64 flk64;
41+
flk64.l_type = flk->l_type;
42+
flk64.l_whence = flk->l_whence;
43+
flk64.l_start = flk->l_start;
44+
flk64.l_len = flk->l_len;
45+
flk64.l_pid = flk->l_pid;
46+
// create a syscall
47+
return syscall_impl<int>(SYS_fcntl, fd, cmd, &flk64);
48+
}
49+
case F_OFD_GETLK:
50+
case F_OFD_SETLK: {
51+
struct flock *flk = (struct flock *)arg;
52+
// convert the struct to a flock64
53+
struct flock64 flk64;
54+
flk64.l_type = flk->l_type;
55+
flk64.l_whence = flk->l_whence;
56+
flk64.l_start = flk->l_start;
57+
flk64.l_len = flk->l_len;
58+
flk64.l_pid = flk->l_pid;
59+
// create a syscall
60+
int retVal = syscall_impl<int>(SYS_fcntl, fd, cmd, &flk64);
61+
// On failure, return
62+
if (retVal == -1)
63+
return -1;
64+
// Check for overflow, i.e. the offsets are not the same when cast
65+
// to off_t from off64_t.
66+
if ((off_t)flk64.l_len != flk64.l_len ||
67+
(off_t)flk64.l_start != flk64.l_start) {
68+
libc_errno = EOVERFLOW;
69+
return -1;
70+
}
71+
// Now copy back into flk, in case flk64 got modified
72+
flk->l_type = flk64.l_type;
73+
flk->l_whence = flk64.l_whence;
74+
flk->l_start = flk64.l_start;
75+
flk->l_len = flk64.l_len;
76+
flk->l_pid = flk64.l_pid;
77+
return retVal;
78+
}
79+
// The general case
80+
default: {
81+
if (cmd == F_GETOWN) {
82+
struct fowner_ex fex;
83+
int retVal = syscall_impl<int>(SYS_fcntl, fd, F_GETOWN_EX, &fex);
84+
if (retVal == -EINVAL)
85+
return syscall_impl<int>(SYS_fcntl, fd, cmd, (void *)arg);
86+
if ((uint64_t)retVal <= -4096UL)
87+
return fex.type == F_OWNER_PGRP ? -fex.pid : fex.pid;
88+
89+
libc_errno = -retVal;
90+
return -1;
91+
}
92+
return syscall_impl<int>(SYS_fcntl, fd, cmd, (void *)arg);
93+
}
94+
}
95+
}
96+
} // namespace LIBC_NAMESPACE

libc/test/src/fcntl/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,21 @@ add_libc_unittest(
1717
libc.test.UnitTest.ErrnoSetterMatcher
1818
)
1919

20+
add_libc_unittest(
21+
fcntl_test
22+
SUITE
23+
libc_fcntl_unittests
24+
SRCS
25+
fcntl_test.cpp
26+
DEPENDS
27+
libc.include.fcntl
28+
libc.src.errno.errno
29+
libc.src.fcntl.fcntl
30+
libc.src.fcntl.open
31+
libc.src.unistd.close
32+
libc.test.UnitTest.ErrnoSetterMatcher
33+
)
34+
2035
add_libc_unittest(
2136
openat_test
2237
SUITE

libc/test/src/fcntl/fcntl_test.cpp

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//===-- Unittest for fcntl ------------------------------------------------===//
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/errno/libc_errno.h"
10+
#include "src/fcntl/fcntl.h"
11+
#include "src/fcntl/open.h"
12+
#include "src/unistd/close.h"
13+
#include "test/UnitTest/ErrnoSetterMatcher.h"
14+
#include "test/UnitTest/Test.h"
15+
16+
#include <sys/stat.h>
17+
18+
TEST(LlvmLibcFcntlTest, FcntlDupfd) {
19+
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
20+
constexpr const char *TEST_FILE = "testdata/fcntl.test";
21+
int fd2, fd3;
22+
int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC, S_IRWXU);
23+
ASSERT_ERRNO_SUCCESS();
24+
ASSERT_GT(fd, 0);
25+
26+
fd2 = LIBC_NAMESPACE::fcntl(fd, F_DUPFD, 0);
27+
ASSERT_ERRNO_SUCCESS();
28+
ASSERT_GT(fd2, 0);
29+
30+
fd3 = LIBC_NAMESPACE::fcntl(fd, F_DUPFD, 10);
31+
ASSERT_ERRNO_SUCCESS();
32+
ASSERT_GT(fd3, 0);
33+
34+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
35+
ASSERT_THAT(LIBC_NAMESPACE::close(fd2), Succeeds(0));
36+
ASSERT_THAT(LIBC_NAMESPACE::close(fd3), Succeeds(0));
37+
}
38+
39+
TEST(LlvmLibcFcntlTest, FcntlGetFl) {
40+
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
41+
constexpr const char *TEST_FILE = "testdata/fcntl.test";
42+
int retVal;
43+
int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC, S_IRWXU);
44+
ASSERT_ERRNO_SUCCESS();
45+
ASSERT_GT(fd, 0);
46+
47+
retVal = LIBC_NAMESPACE::fcntl(fd, F_GETFL);
48+
ASSERT_ERRNO_SUCCESS();
49+
ASSERT_GT(retVal, -1);
50+
51+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
52+
}
53+
54+
TEST(LlvmLibcFcntlTest, FcntlSetFl) {
55+
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
56+
constexpr const char *TEST_FILE = "testdata/fcntl.test";
57+
58+
int retVal;
59+
int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
60+
ASSERT_ERRNO_SUCCESS();
61+
ASSERT_GT(fd, 0);
62+
63+
retVal = LIBC_NAMESPACE::fcntl(fd, F_GETFL);
64+
ASSERT_ERRNO_SUCCESS();
65+
ASSERT_GT(retVal, -1);
66+
67+
int oldFlags = LIBC_NAMESPACE::fcntl(fd, F_GETFL, 0);
68+
ASSERT_ERRNO_SUCCESS();
69+
ASSERT_GT(oldFlags, 0);
70+
71+
// Add the APPEND flag;
72+
oldFlags |= O_APPEND;
73+
74+
retVal = LIBC_NAMESPACE::fcntl(fd, F_SETFL, oldFlags);
75+
ASSERT_ERRNO_SUCCESS();
76+
ASSERT_GT(retVal, -1);
77+
78+
// Remove the APPEND flag;
79+
oldFlags = -oldFlags & O_APPEND;
80+
81+
retVal = LIBC_NAMESPACE::fcntl(fd, F_SETFL, oldFlags);
82+
ASSERT_ERRNO_SUCCESS();
83+
ASSERT_GT(retVal, -1);
84+
85+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
86+
}
87+
88+
TEST(LlvmLibcFcntlTest, FcntlGetLkRead) {
89+
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
90+
constexpr const char *TEST_FILE = "testdata/fcntl.test";
91+
92+
struct flock flk, svflk;
93+
int retVal;
94+
int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDONLY);
95+
ASSERT_ERRNO_SUCCESS();
96+
ASSERT_GT(fd, 0);
97+
98+
flk.l_type = F_RDLCK;
99+
flk.l_start = 0;
100+
flk.l_whence = SEEK_SET;
101+
flk.l_len = 50;
102+
103+
// copy flk into svflk
104+
svflk = flk;
105+
106+
retVal = LIBC_NAMESPACE::fcntl(fd, F_GETLK, &svflk);
107+
ASSERT_ERRNO_SUCCESS();
108+
ASSERT_GT(retVal, -1);
109+
ASSERT_NE((int)flk.l_type, F_WRLCK); // File should not be write locked.
110+
111+
retVal = LIBC_NAMESPACE::fcntl(fd, F_SETLK, &svflk);
112+
ASSERT_ERRNO_SUCCESS();
113+
ASSERT_GT(retVal, -1);
114+
115+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
116+
}
117+
118+
TEST(LlvmLibcFcntlTest, FcntlGetLkWrite) {
119+
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
120+
constexpr const char *TEST_FILE = "testdata/fcntl.test";
121+
122+
struct flock flk, svflk;
123+
int retVal;
124+
int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
125+
ASSERT_ERRNO_SUCCESS();
126+
ASSERT_GT(fd, 0);
127+
128+
flk.l_type = F_WRLCK;
129+
flk.l_start = 0;
130+
flk.l_whence = SEEK_SET;
131+
flk.l_len = 0;
132+
133+
// copy flk into svflk
134+
svflk = flk;
135+
136+
retVal = LIBC_NAMESPACE::fcntl(fd, F_GETLK, &svflk);
137+
ASSERT_ERRNO_SUCCESS();
138+
ASSERT_GT(retVal, -1);
139+
ASSERT_NE((int)flk.l_type, F_RDLCK); // File should not be read locked.
140+
141+
retVal = LIBC_NAMESPACE::fcntl(fd, F_SETLK, &svflk);
142+
ASSERT_ERRNO_SUCCESS();
143+
ASSERT_GT(retVal, -1);
144+
145+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
146+
}

0 commit comments

Comments
 (0)