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