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