summaryrefslogtreecommitdiff
path: root/libc/src/__support/File/linux/file.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libc/src/__support/File/linux/file.cpp')
-rw-r--r--libc/src/__support/File/linux/file.cpp58
1 files changed, 56 insertions, 2 deletions
diff --git a/libc/src/__support/File/linux/file.cpp b/libc/src/__support/File/linux/file.cpp
index b84da64cbe63..00ff93846c6b 100644
--- a/libc/src/__support/File/linux/file.cpp
+++ b/libc/src/__support/File/linux/file.cpp
@@ -8,10 +8,10 @@
#include "file.h"
-#include "src/__support/File/file.h"
-
#include "src/__support/CPP/new.h"
+#include "src/__support/File/file.h"
#include "src/__support/File/linux/lseekImpl.h"
+#include "src/__support/OSUtil/fcntl.h"
#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/errno/libc_errno.h" // For error macros
@@ -119,6 +119,60 @@ ErrorOr<File *> openfile(const char *path, const char *mode) {
return file;
}
+ErrorOr<LinuxFile *> create_file_from_fd(int fd, const char *mode) {
+ using ModeFlags = File::ModeFlags;
+ ModeFlags modeflags = File::mode_flags(mode);
+ if (modeflags == 0) {
+ return Error(EINVAL);
+ }
+
+ int fd_flags = internal::fcntl(fd, F_GETFL);
+ if (fd_flags == -1) {
+ return Error(EBADF);
+ }
+
+ using OpenMode = File::OpenMode;
+ if (((fd_flags & O_ACCMODE) == O_RDONLY &&
+ !(modeflags & static_cast<ModeFlags>(OpenMode::READ))) ||
+ ((fd_flags & O_ACCMODE) == O_WRONLY &&
+ !(modeflags & static_cast<ModeFlags>(OpenMode::WRITE)))) {
+ return Error(EINVAL);
+ }
+
+ bool do_seek = false;
+ if ((modeflags & static_cast<ModeFlags>(OpenMode::APPEND)) &&
+ !(fd_flags & O_APPEND)) {
+ do_seek = true;
+ if (internal::fcntl(fd, F_SETFL,
+ reinterpret_cast<void *>(fd_flags | O_APPEND)) == -1) {
+ return Error(EBADF);
+ }
+ }
+
+ uint8_t *buffer;
+ {
+ AllocChecker ac;
+ buffer = new (ac) uint8_t[File::DEFAULT_BUFFER_SIZE];
+ if (!ac) {
+ return Error(ENOMEM);
+ }
+ }
+ AllocChecker ac;
+ auto *file = new (ac)
+ LinuxFile(fd, buffer, File::DEFAULT_BUFFER_SIZE, _IOFBF, true, modeflags);
+ if (!ac) {
+ return Error(ENOMEM);
+ }
+ if (do_seek) {
+ auto result = file->seek(0, SEEK_END);
+ if (!result.has_value()) {
+ free(file);
+ return Error(result.error());
+ }
+ }
+ return file;
+}
+
int get_fileno(File *f) {
auto *lf = reinterpret_cast<LinuxFile *>(f);
return lf->get_fd();