186 lines · cpp
1//===--- Implementation of the Linux specialization of File ---------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "file.h"10 11#include "hdr/stdio_macros.h"12#include "hdr/types/off_t.h"13#include "src/__support/CPP/new.h"14#include "src/__support/File/file.h"15#include "src/__support/File/linux/lseekImpl.h"16#include "src/__support/OSUtil/fcntl.h"17#include "src/__support/OSUtil/syscall.h" // For internal syscall function.18#include "src/__support/libc_errno.h" // For error macros19#include "src/__support/macros/config.h"20 21#include "hdr/fcntl_macros.h" // For mode_t and other flags to the open syscall22#include <sys/stat.h> // For S_IS*, S_IF*, and S_IR* flags.23#include <sys/syscall.h> // For syscall numbers24 25namespace LIBC_NAMESPACE_DECL {26 27FileIOResult linux_file_write(File *f, const void *data, size_t size) {28 auto *lf = reinterpret_cast<LinuxFile *>(f);29 int ret =30 LIBC_NAMESPACE::syscall_impl<int>(SYS_write, lf->get_fd(), data, size);31 if (ret < 0) {32 return {0, -ret};33 }34 return ret;35}36 37FileIOResult linux_file_read(File *f, void *buf, size_t size) {38 auto *lf = reinterpret_cast<LinuxFile *>(f);39 int ret =40 LIBC_NAMESPACE::syscall_impl<int>(SYS_read, lf->get_fd(), buf, size);41 if (ret < 0) {42 return {0, -ret};43 }44 return ret;45}46 47ErrorOr<off_t> linux_file_seek(File *f, off_t offset, int whence) {48 auto *lf = reinterpret_cast<LinuxFile *>(f);49 auto result = internal::lseekimpl(lf->get_fd(), offset, whence);50 if (!result.has_value())51 return result.error();52 return result.value();53}54 55int linux_file_close(File *f) {56 auto *lf = reinterpret_cast<LinuxFile *>(f);57 int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_close, lf->get_fd());58 if (ret < 0) {59 return -ret;60 }61 delete lf;62 return 0;63}64 65ErrorOr<File *> openfile(const char *path, const char *mode) {66 using ModeFlags = File::ModeFlags;67 auto modeflags = File::mode_flags(mode);68 if (modeflags == 0) {69 // return {nullptr, EINVAL};70 return Error(EINVAL);71 }72 long open_flags = 0;73 if (modeflags & ModeFlags(File::OpenMode::APPEND)) {74 open_flags = O_CREAT | O_APPEND;75 if (modeflags & ModeFlags(File::OpenMode::PLUS))76 open_flags |= O_RDWR;77 else78 open_flags |= O_WRONLY;79 } else if (modeflags & ModeFlags(File::OpenMode::WRITE)) {80 open_flags = O_CREAT | O_TRUNC;81 if (modeflags & ModeFlags(File::OpenMode::PLUS))82 open_flags |= O_RDWR;83 else84 open_flags |= O_WRONLY;85 } else {86 if (modeflags & ModeFlags(File::OpenMode::PLUS))87 open_flags |= O_RDWR;88 else89 open_flags |= O_RDONLY;90 }91 92 // File created will have 0666 permissions.93 constexpr long OPEN_MODE =94 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;95 96#ifdef SYS_open97 int fd =98 LIBC_NAMESPACE::syscall_impl<int>(SYS_open, path, open_flags, OPEN_MODE);99#elif defined(SYS_openat)100 int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_openat, AT_FDCWD, path,101 open_flags, OPEN_MODE);102#else103#error "open and openat syscalls not available."104#endif105 106 if (fd < 0)107 return Error(-fd);108 109 uint8_t *buffer;110 {111 AllocChecker ac;112 buffer = new (ac) uint8_t[File::DEFAULT_BUFFER_SIZE];113 if (!ac)114 return Error(ENOMEM);115 }116 AllocChecker ac;117 auto *file = new (ac)118 LinuxFile(fd, buffer, File::DEFAULT_BUFFER_SIZE, _IOFBF, true, modeflags);119 if (!ac)120 return Error(ENOMEM);121 return file;122}123 124ErrorOr<LinuxFile *> create_file_from_fd(int fd, const char *mode) {125 using ModeFlags = File::ModeFlags;126 ModeFlags modeflags = File::mode_flags(mode);127 if (modeflags == 0) {128 return Error(EINVAL);129 }130 131 auto result = internal::fcntl(fd, F_GETFL);132 if (!result.has_value()) {133 return Error(EBADF);134 }135 int fd_flags = result.value();136 137 using OpenMode = File::OpenMode;138 if (((fd_flags & O_ACCMODE) == O_RDONLY &&139 !(modeflags & static_cast<ModeFlags>(OpenMode::READ))) ||140 ((fd_flags & O_ACCMODE) == O_WRONLY &&141 !(modeflags & static_cast<ModeFlags>(OpenMode::WRITE)))) {142 return Error(EINVAL);143 }144 145 bool do_seek = false;146 if ((modeflags & static_cast<ModeFlags>(OpenMode::APPEND)) &&147 !(fd_flags & O_APPEND)) {148 do_seek = true;149 if (!internal::fcntl(fd, F_SETFL,150 reinterpret_cast<void *>(fd_flags | O_APPEND))151 .has_value()) {152 return Error(EBADF);153 }154 }155 156 uint8_t *buffer;157 {158 AllocChecker ac;159 buffer = new (ac) uint8_t[File::DEFAULT_BUFFER_SIZE];160 if (!ac) {161 return Error(ENOMEM);162 }163 }164 AllocChecker ac;165 auto *file = new (ac)166 LinuxFile(fd, buffer, File::DEFAULT_BUFFER_SIZE, _IOFBF, true, modeflags);167 if (!ac) {168 return Error(ENOMEM);169 }170 if (do_seek) {171 auto result = file->seek(0, SEEK_END);172 if (!result.has_value()) {173 free(file);174 return Error(result.error());175 }176 }177 return file;178}179 180int get_fileno(File *f) {181 auto *lf = reinterpret_cast<LinuxFile *>(f);182 return lf->get_fd();183}184 185} // namespace LIBC_NAMESPACE_DECL186