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:
LinuxFile(int file_descriptor,void * buffer,size_t buffer_size,int buffer_mode,bool owned,File::ModeFlags modeflags)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
init(LinuxFile * f,int file_descriptor,void * buffer,size_t buffer_size,int buffer_mode,bool owned,File::ModeFlags modeflags)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
get_fd() const49 int get_fd() const { return fd; }
50 };
51
52 namespace {
53
write_func(File * f,const void * data,size_t size)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
read_func(File * f,void * buf,size_t size)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
seek_func(File * f,long offset,int whence)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
close_func(File * f)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
flush_func(File * f)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
openfile(const char * path,const char * mode)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