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