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 // TODO: Use the appropriate buffering modes for the standard streams below
169 // the different buffering modes are available.
170 constexpr size_t STDOUT_BUFFER_SIZE = 1024;
171 char stdout_buffer[STDOUT_BUFFER_SIZE];
172 static LinuxFile StdOut(1, stdout_buffer, STDOUT_BUFFER_SIZE, 0, false,
173                         File::ModeFlags(File::OpenMode::APPEND));
174 File *stdout = &StdOut;
175 
176 constexpr size_t STDERR_BUFFER_SIZE = 1024;
177 char stderr_buffer[STDERR_BUFFER_SIZE];
178 static LinuxFile StdErr(2, stderr_buffer, STDERR_BUFFER_SIZE, 0, false,
179                         File::ModeFlags(File::OpenMode::APPEND));
180 File *stderr = &StdErr;
181 
182 } // namespace __llvm_libc
183