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-exception 6 // 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/macros/config.h" 19 #include "src/errno/libc_errno.h" // For error macros 20 21 #include "hdr/fcntl_macros.h" // For mode_t and other flags to the open syscall 22 #include <sys/stat.h> // For S_IS*, S_IF*, and S_IR* flags. 23 #include <sys/syscall.h> // For syscall numbers 24 25 namespace LIBC_NAMESPACE_DECL { 26 27 FileIOResult 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 37 FileIOResult 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 47 ErrorOr<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 55 int 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 65 ErrorOr<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 else 78 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 else 84 open_flags |= O_WRONLY; 85 } else { 86 if (modeflags & ModeFlags(File::OpenMode::PLUS)) 87 open_flags |= O_RDWR; 88 else 89 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_open 97 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 #else 103 #error "open and openat syscalls not available." 104 #endif 105 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 124 ErrorOr<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 int fd_flags = internal::fcntl(fd, F_GETFL); 132 if (fd_flags == -1) { 133 return Error(EBADF); 134 } 135 136 using OpenMode = File::OpenMode; 137 if (((fd_flags & O_ACCMODE) == O_RDONLY && 138 !(modeflags & static_cast<ModeFlags>(OpenMode::READ))) || 139 ((fd_flags & O_ACCMODE) == O_WRONLY && 140 !(modeflags & static_cast<ModeFlags>(OpenMode::WRITE)))) { 141 return Error(EINVAL); 142 } 143 144 bool do_seek = false; 145 if ((modeflags & static_cast<ModeFlags>(OpenMode::APPEND)) && 146 !(fd_flags & O_APPEND)) { 147 do_seek = true; 148 if (internal::fcntl(fd, F_SETFL, 149 reinterpret_cast<void *>(fd_flags | O_APPEND)) == -1) { 150 return Error(EBADF); 151 } 152 } 153 154 uint8_t *buffer; 155 { 156 AllocChecker ac; 157 buffer = new (ac) uint8_t[File::DEFAULT_BUFFER_SIZE]; 158 if (!ac) { 159 return Error(ENOMEM); 160 } 161 } 162 AllocChecker ac; 163 auto *file = new (ac) 164 LinuxFile(fd, buffer, File::DEFAULT_BUFFER_SIZE, _IOFBF, true, modeflags); 165 if (!ac) { 166 return Error(ENOMEM); 167 } 168 if (do_seek) { 169 auto result = file->seek(0, SEEK_END); 170 if (!result.has_value()) { 171 free(file); 172 return Error(result.error()); 173 } 174 } 175 return file; 176 } 177 178 int get_fileno(File *f) { 179 auto *lf = reinterpret_cast<LinuxFile *>(f); 180 return lf->get_fd(); 181 } 182 183 } // namespace LIBC_NAMESPACE_DECL 184