Skip to content

Commit 6460fba

Browse files
committed
[libc] Add the implementation of the fdopen function
1 parent 718331f commit 6460fba

File tree

12 files changed

+230
-4
lines changed

12 files changed

+230
-4
lines changed

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ set(TARGET_LIBC_ENTRYPOINTS
201201
libc.src.stdio.snprintf
202202
libc.src.stdio.vsprintf
203203
libc.src.stdio.vsnprintf
204+
libc.src.stdio.fdopen
204205
#libc.src.stdio.sscanf
205206
#libc.src.stdio.scanf
206207
#libc.src.stdio.fscanf

libc/config/linux/riscv/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ set(TARGET_LIBC_ENTRYPOINTS
209209
libc.src.stdio.sscanf
210210
libc.src.stdio.scanf
211211
libc.src.stdio.fscanf
212+
libc.src.stdio.fdopen
212213

213214
# sys/mman.h entrypoints
214215
libc.src.sys.mman.madvise

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ set(TARGET_LIBC_ENTRYPOINTS
215215
libc.src.stdio.scanf
216216
libc.src.stdio.fscanf
217217
libc.src.stdio.fileno
218+
libc.src.stdio.fdopen
218219

219220
# sys/epoll.h entrypoints
220221
libc.src.sys.epoll.epoll_create

libc/spec/posix.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,11 @@ def POSIX : StandardSpec<"POSIX"> {
12931293
RetValSpec<IntType>,
12941294
[ArgSpec<FILEPtr>]
12951295
>,
1296+
FunctionSpec<
1297+
"fdopen",
1298+
RetValSpec<FILEPtr>,
1299+
[ArgSpec<IntType>, ArgSpec<ConstCharPtr>]
1300+
>,
12961301
]
12971302
>;
12981303

libc/src/fcntl/linux/fcntl.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ LLVM_LIBC_FUNCTION(int, fcntl, (int fd, int cmd, ...)) {
2929
va_end(varargs);
3030

3131
switch (cmd) {
32-
case F_SETLKW:
33-
return syscall_impl<int>(SYS_fcntl, fd, cmd, arg);
3432
case F_OFD_SETLKW: {
3533
struct flock *flk = reinterpret_cast<struct flock *>(arg);
3634
// convert the struct to a flock64
@@ -86,8 +84,15 @@ LLVM_LIBC_FUNCTION(int, fcntl, (int fd, int cmd, ...)) {
8684
return -1;
8785
}
8886
// The general case
89-
default:
90-
return syscall_impl<int>(SYS_fcntl, fd, cmd, reinterpret_cast<void *>(arg));
87+
default: {
88+
int retVal =
89+
syscall_impl<int>(SYS_fcntl, fd, cmd, reinterpret_cast<void *>(arg));
90+
if (retVal >= 0) {
91+
return retVal;
92+
}
93+
libc_errno = -retVal;
94+
return -1;
95+
}
9196
}
9297
}
9398
} // namespace LIBC_NAMESPACE

libc/src/stdio/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,13 @@ add_entrypoint_object(
263263
.${LIBC_TARGET_OS}.rename
264264
)
265265

266+
add_entrypoint_object(
267+
fdopen
268+
ALIAS
269+
DEPENDS
270+
.${LIBC_TARGET_OS}.fdopen
271+
)
272+
266273
# These entrypoints have multiple potential implementations.
267274
add_stdio_entrypoint_object(feof)
268275
add_stdio_entrypoint_object(feof_unlocked)

libc/src/stdio/fdopen.h

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

libc/src/stdio/linux/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,18 @@ add_entrypoint_object(
2424
libc.src.__support.OSUtil.osutil
2525
libc.src.errno.errno
2626
)
27+
28+
add_entrypoint_object(
29+
fdopen
30+
SRCS
31+
fdopen.cpp
32+
HDRS
33+
../fdopen.h
34+
DEPENDS
35+
libc.include.fcntl
36+
libc.include.stdio
37+
libc.src.errno.errno
38+
libc.src.fcntl.fcntl
39+
libc.src.stdio.fseek
40+
libc.src.__support.File.linux.file
41+
)

libc/src/stdio/linux/fdopen.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//===-- Implementation of fprintf -------------------------------*- 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+
#include "src/stdio/fdopen.h"
10+
11+
#include "include/llvm-libc-macros/generic-error-number-macros.h"
12+
#include "include/llvm-libc-macros/linux/fcntl-macros.h"
13+
#include "src/__support/File/linux/file.h"
14+
#include "src/errno/libc_errno.h"
15+
#include "src/fcntl/fcntl.h"
16+
#include "src/stdio/fseek.h"
17+
18+
#include <stdio.h>
19+
20+
namespace LIBC_NAMESPACE {
21+
22+
LLVM_LIBC_FUNCTION(::FILE *, fdopen, (int fd, const char *mode)) {
23+
using ModeFlags = File::ModeFlags;
24+
ModeFlags modeflags = File::mode_flags(mode);
25+
if (modeflags == 0) {
26+
libc_errno = EINVAL;
27+
return nullptr;
28+
}
29+
30+
int fd_flags = LIBC_NAMESPACE::fcntl(fd, F_GETFL);
31+
if (fd_flags == -1) {
32+
return nullptr;
33+
}
34+
35+
using OpenMode = File::OpenMode;
36+
if (((fd_flags & O_ACCMODE) == O_RDONLY &&
37+
!(modeflags & static_cast<ModeFlags>(OpenMode::READ))) ||
38+
((fd_flags & O_ACCMODE) == O_WRONLY &&
39+
!(modeflags & static_cast<ModeFlags>(OpenMode::WRITE)))) {
40+
libc_errno = EINVAL;
41+
return nullptr;
42+
}
43+
44+
bool do_seek = false;
45+
if ((modeflags & static_cast<ModeFlags>(OpenMode::APPEND)) &&
46+
!(fd_flags & O_APPEND)) {
47+
do_seek = true;
48+
if (LIBC_NAMESPACE::fcntl(fd, F_SETFL, fd_flags | O_APPEND) == -1) {
49+
libc_errno = EBADF;
50+
return nullptr;
51+
}
52+
}
53+
54+
uint8_t *buffer;
55+
{
56+
AllocChecker ac;
57+
buffer = new (ac) uint8_t[File::DEFAULT_BUFFER_SIZE];
58+
if (!ac) {
59+
libc_errno = ENOMEM;
60+
return nullptr;
61+
}
62+
}
63+
AllocChecker ac;
64+
auto *linux_file = new (ac)
65+
LinuxFile(fd, buffer, File::DEFAULT_BUFFER_SIZE, _IOFBF, true, modeflags);
66+
if (!ac) {
67+
libc_errno = ENOMEM;
68+
return nullptr;
69+
}
70+
auto *file = reinterpret_cast<::FILE *>(linux_file);
71+
if (do_seek && LIBC_NAMESPACE::fseek(file, 0, SEEK_END) != 0) {
72+
free(linux_file);
73+
return nullptr;
74+
}
75+
76+
return file;
77+
}
78+
79+
} // namespace LIBC_NAMESPACE

libc/test/src/fcntl/fcntl_test.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,13 @@ TEST(LlvmLibcFcntlTest, FcntlGetLkWrite) {
153153

154154
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
155155
}
156+
157+
TEST(LlvmLibcFcntlTest, UseAfterClose) {
158+
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
159+
constexpr const char *TEST_FILE_NAME = "testdata/fcntl_use_after_close.test";
160+
auto TEST_FILE = libc_make_test_file_path(TEST_FILE_NAME);
161+
int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
162+
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
163+
ASSERT_EQ(-1, LIBC_NAMESPACE::fcntl(fd, F_GETFL));
164+
ASSERT_ERRNO_EQ(EBADF);
165+
}

libc/test/src/stdio/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,20 @@ if(${LIBC_TARGET_OS} STREQUAL "linux")
369369
libc.src.unistd.close
370370
libc.test.UnitTest.ErrnoSetterMatcher
371371
)
372+
373+
add_libc_test(
374+
fdopen_test
375+
SUITE
376+
libc_stdio_unittests
377+
SRCS
378+
fdopen_test.cpp
379+
DEPENDS
380+
libc.src.fcntl.open
381+
libc.src.stdio.fclose
382+
libc.src.stdio.fdopen
383+
libc.src.unistd.close
384+
libc.test.UnitTest.ErrnoSetterMatcher
385+
)
372386
endif()
373387

374388
add_libc_test(

libc/test/src/stdio/fdopen_test.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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 "include/llvm-libc-macros/linux/fcntl-macros.h"
10+
#include "src/stdio/fdopen.h"
11+
12+
#include "src/errno/libc_errno.h"
13+
#include "src/fcntl/fcntl.h"
14+
#include "src/fcntl/open.h"
15+
#include "src/stdio/fclose.h"
16+
#include "src/unistd/close.h"
17+
#include "test/UnitTest/ErrnoSetterMatcher.h"
18+
#include "test/UnitTest/LibcTest.h"
19+
#include "test/UnitTest/Test.h"
20+
21+
#include <sys/stat.h> // For S_IRWXU
22+
23+
TEST(LlvmLibcStdioFdopenTest, InvalidInput) {
24+
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
25+
LIBC_NAMESPACE::libc_errno = 0;
26+
constexpr const char *TEST_FILE_NAME = "testdata/fdopen_invalid_inputs.test";
27+
auto TEST_FILE = libc_make_test_file_path(TEST_FILE_NAME);
28+
int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDONLY);
29+
auto *fp = LIBC_NAMESPACE::fdopen(fd, "m+");
30+
ASSERT_ERRNO_EQ(EINVAL);
31+
ASSERT_TRUE(nullptr == fp);
32+
LIBC_NAMESPACE::close(fd);
33+
LIBC_NAMESPACE::libc_errno = 0;
34+
fp = LIBC_NAMESPACE::fdopen(fd, "r");
35+
ASSERT_ERRNO_EQ(EBADF);
36+
ASSERT_TRUE(nullptr == fp);
37+
}
38+
39+
TEST(LlvmLibcStdioFdopenTest, InvalidMode) {
40+
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
41+
LIBC_NAMESPACE::libc_errno = 0;
42+
constexpr const char *TEST_FILE_NAME = "testdata/fdopen_invid_mode.test";
43+
auto TEST_FILE = libc_make_test_file_path(TEST_FILE_NAME);
44+
int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDONLY);
45+
ASSERT_ERRNO_SUCCESS();
46+
ASSERT_GT(fd, 0);
47+
auto *fp = LIBC_NAMESPACE::fdopen(fd, "r+");
48+
ASSERT_ERRNO_SUCCESS();
49+
ASSERT_TRUE(nullptr != fp);
50+
ASSERT_THAT(LIBC_NAMESPACE::fclose(fp), Succeeds(0));
51+
// If the mode argument is invalid, then `fdopen` returns a nullptr and sets
52+
// the `errno` to EINVAL. In this case the `mode` param can only be "r" or
53+
// "r+"
54+
int fd2 = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_TRUNC | O_RDONLY);
55+
ASSERT_ERRNO_SUCCESS();
56+
ASSERT_GT(fd2, 0);
57+
auto *fp2 = LIBC_NAMESPACE::fdopen(fd2, "w");
58+
ASSERT_ERRNO_EQ(EINVAL);
59+
ASSERT_TRUE(nullptr == fp2);
60+
}
61+
62+
TEST(LlvmLibcStdioFdopenTest, WriteRead) {
63+
64+
}
65+
66+
TEST(LlvmLibcStdioFdopenTest, Append) {}
67+
68+
TEST(LlvmLibcStdioFdopenTest, AppendPlus) {}

0 commit comments

Comments
 (0)