1 //===--- Linux specialization of the File data structure ------------------===// 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 "src/__support/OSUtil/syscall.h" // For internal syscall function. 12 13 #include <errno.h> 14 #include <fcntl.h> // For mode_t and other flags to the open syscall 15 #include <stdio.h> 16 #include <stdlib.h> // For malloc 17 #include <sys/syscall.h> // For syscall numbers 18 19 namespace __llvm_libc { 20 21 namespace { 22 23 size_t write_func(File *, const void *, size_t); 24 size_t read_func(File *, void *, size_t); 25 int seek_func(File *, long, int); 26 int close_func(File *); 27 int flush_func(File *); 28 29 } // anonymous namespace 30 31 class LinuxFile : public File { 32 int fd; 33 34 public: 35 constexpr LinuxFile(int file_descriptor, void *buffer, size_t buffer_size, 36 int buffer_mode, bool owned, File::ModeFlags modeflags) 37 : File(&write_func, &read_func, &seek_func, &close_func, flush_func, 38 buffer, buffer_size, buffer_mode, owned, modeflags), 39 fd(file_descriptor) {} 40 41 static void init(LinuxFile *f, int file_descriptor, void *buffer, 42 size_t buffer_size, int buffer_mode, bool owned, 43 File::ModeFlags modeflags) { 44 File::init(f, &write_func, &read_func, &seek_func, &close_func, &flush_func, 45 buffer, buffer_size, buffer_mode, owned, modeflags); 46 f->fd = file_descriptor; 47 } 48 49 int get_fd() const { return fd; } 50 }; 51 52 namespace { 53 54 size_t write_func(File *f, const void *data, size_t size) { 55 auto *lf = reinterpret_cast<LinuxFile *>(f); 56 long ret = __llvm_libc::syscall(SYS_write, lf->get_fd(), data, size); 57 if (ret < 0) { 58 errno = -ret; 59 return 0; 60 } 61 return ret; 62 } 63 64 size_t read_func(File *f, void *buf, size_t size) { 65 auto *lf = reinterpret_cast<LinuxFile *>(f); 66 long ret = __llvm_libc::syscall(SYS_read, lf->get_fd(), buf, size); 67 if (ret < 0) { 68 errno = -ret; 69 return 0; 70 } 71 return ret; 72 } 73 74 int seek_func(File *f, long offset, int whence) { 75 auto *lf = reinterpret_cast<LinuxFile *>(f); 76 #ifdef SYS_lseek 77 long ret = __llvm_libc::syscall(SYS_lseek, lf->get_fd(), offset, whence); 78 #elif defined(SYS__llseek) 79 long result; 80 long ret = __llvm_libc::syscall(SYS__lseek, lf->get_fd(), offset >> 32, 81 offset, &result, whence); 82 #else 83 #error "lseek and _llseek syscalls not available to perform a seek operation." 84 #endif 85 86 if (ret < 0) { 87 errno = -ret; 88 return -1; 89 } 90 return 0; 91 } 92 93 int close_func(File *f) { 94 auto *lf = reinterpret_cast<LinuxFile *>(f); 95 long ret = __llvm_libc::syscall(SYS_close, lf->get_fd()); 96 if (ret < 0) { 97 errno = -ret; 98 return -1; 99 } 100 return 0; 101 } 102 103 int flush_func(File *f) { 104 auto *lf = reinterpret_cast<LinuxFile *>(f); 105 long ret = __llvm_libc::syscall(SYS_fsync, lf->get_fd()); 106 if (ret < 0) { 107 errno = -ret; 108 return -1; 109 } 110 return 0; 111 } 112 113 } // anonymous namespace 114 115 File *openfile(const char *path, const char *mode) { 116 using ModeFlags = File::ModeFlags; 117 auto modeflags = File::mode_flags(mode); 118 if (modeflags == 0) { 119 errno = EINVAL; 120 return nullptr; 121 } 122 long open_flags = 0; 123 if (modeflags & ModeFlags(File::OpenMode::APPEND)) { 124 open_flags = O_CREAT | O_APPEND; 125 if (modeflags & ModeFlags(File::OpenMode::PLUS)) 126 open_flags |= O_RDWR; 127 else 128 open_flags |= O_WRONLY; 129 } else if (modeflags & ModeFlags(File::OpenMode::WRITE)) { 130 open_flags = O_CREAT | O_TRUNC; 131 if (modeflags & ModeFlags(File::OpenMode::PLUS)) 132 open_flags |= O_RDWR; 133 else 134 open_flags |= O_WRONLY; 135 } else { 136 if (modeflags & ModeFlags(File::OpenMode::PLUS)) 137 open_flags |= O_RDWR; 138 else 139 open_flags |= O_RDONLY; 140 } 141 142 // File created will have 0666 permissions. 143 constexpr long OPEN_MODE = 144 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; 145 146 #ifdef SYS_open 147 int fd = __llvm_libc::syscall(SYS_open, path, open_flags, OPEN_MODE); 148 #elif defined(SYS_openat) 149 int fd = 150 __llvm_libc::syscall(SYS_openat, AT_FDCWD, path, open_flags, OPEN_MODE); 151 #else 152 #error "SYS_open and SYS_openat syscalls not available to perform a file open." 153 #endif 154 155 if (fd < 0) { 156 errno = -fd; 157 return nullptr; 158 } 159 160 void *buffer = malloc(File::DEFAULT_BUFFER_SIZE); 161 auto *file = reinterpret_cast<LinuxFile *>(malloc(sizeof(LinuxFile))); 162 LinuxFile::init(file, fd, buffer, File::DEFAULT_BUFFER_SIZE, _IOFBF, true, 163 modeflags); 164 return file; 165 } 166 167 constexpr size_t STDOUT_BUFFER_SIZE = 1024; 168 char stdout_buffer[STDOUT_BUFFER_SIZE]; 169 static LinuxFile StdOut(1, stdout_buffer, STDOUT_BUFFER_SIZE, _IOLBF, false, 170 File::ModeFlags(File::OpenMode::APPEND)); 171 File *stdout = &StdOut; 172 173 constexpr size_t STDERR_BUFFER_SIZE = 0; 174 static LinuxFile StdErr(2, nullptr, STDERR_BUFFER_SIZE, _IONBF, false, 175 File::ModeFlags(File::OpenMode::APPEND)); 176 File *stderr = &StdErr; 177 178 } // namespace __llvm_libc 179