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