1//===--- Implementation of the Linux specialization of File ---------------===//
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 "hdr/stdio_macros.h"
12#include "hdr/types/off_t.h"
13#include "src/__support/CPP/new.h"
14#include "src/__support/File/file.h"
15#include "src/__support/File/linux/lseekImpl.h"
16#include "src/__support/OSUtil/fcntl.h"
17#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
18#include "src/__support/libc_errno.h" // For error macros
19#include "src/__support/macros/config.h"
20
21#include "hdr/fcntl_macros.h" // For mode_t and other flags to the open syscall
22#include <sys/stat.h> // For S_IS*, S_IF*, and S_IR* flags.
23#include <sys/syscall.h> // For syscall numbers
24
25namespace LIBC_NAMESPACE_DECL {
26
27FileIOResult linux_file_write(File *f, const void *data, size_t size) {
28 auto *lf = reinterpret_cast<LinuxFile *>(f);
29 int ret =
30 LIBC_NAMESPACE::syscall_impl<int>(SYS_write, lf->get_fd(), data, size);
31 if (ret < 0) {
32 return {0, -ret};
33 }
34 return ret;
35}
36
37FileIOResult linux_file_read(File *f, void *buf, size_t size) {
38 auto *lf = reinterpret_cast<LinuxFile *>(f);
39 int ret =
40 LIBC_NAMESPACE::syscall_impl<int>(SYS_read, lf->get_fd(), buf, size);
41 if (ret < 0) {
42 return {0, -ret};
43 }
44 return ret;
45}
46
47ErrorOr<off_t> linux_file_seek(File *f, off_t offset, int whence) {
48 auto *lf = reinterpret_cast<LinuxFile *>(f);
49 auto result = internal::lseekimpl(lf->get_fd(), offset, whence);
50 if (!result.has_value())
51 return result.error();
52 return result.value();
53}
54
55int linux_file_close(File *f) {
56 auto *lf = reinterpret_cast<LinuxFile *>(f);
57 int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_close, lf->get_fd());
58 if (ret < 0) {
59 return -ret;
60 }
61 delete lf;
62 return 0;
63}
64
65ErrorOr<File *> openfile(const char *path, const char *mode) {
66 using ModeFlags = File::ModeFlags;
67 auto modeflags = File::mode_flags(mode);
68 if (modeflags == 0) {
69 // return {nullptr, EINVAL};
70 return Error(EINVAL);
71 }
72 long open_flags = 0;
73 if (modeflags & ModeFlags(File::OpenMode::APPEND)) {
74 open_flags = O_CREAT | O_APPEND;
75 if (modeflags & ModeFlags(File::OpenMode::PLUS))
76 open_flags |= O_RDWR;
77 else
78 open_flags |= O_WRONLY;
79 } else if (modeflags & ModeFlags(File::OpenMode::WRITE)) {
80 open_flags = O_CREAT | O_TRUNC;
81 if (modeflags & ModeFlags(File::OpenMode::PLUS))
82 open_flags |= O_RDWR;
83 else
84 open_flags |= O_WRONLY;
85 } else {
86 if (modeflags & ModeFlags(File::OpenMode::PLUS))
87 open_flags |= O_RDWR;
88 else
89 open_flags |= O_RDONLY;
90 }
91
92 // File created will have 0666 permissions.
93 constexpr long OPEN_MODE =
94 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
95
96#ifdef SYS_open
97 int fd =
98 LIBC_NAMESPACE::syscall_impl<int>(SYS_open, path, open_flags, OPEN_MODE);
99#elif defined(SYS_openat)
100 int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_openat, AT_FDCWD, path,
101 open_flags, OPEN_MODE);
102#else
103#error "open and openat syscalls not available."
104#endif
105
106 if (fd < 0)
107 return Error(-fd);
108
109 uint8_t *buffer;
110 {
111 AllocChecker ac;
112 buffer = new (ac) uint8_t[File::DEFAULT_BUFFER_SIZE];
113 if (!ac)
114 return Error(ENOMEM);
115 }
116 AllocChecker ac;
117 auto *file = new (ac)
118 LinuxFile(fd, buffer, File::DEFAULT_BUFFER_SIZE, _IOFBF, true, modeflags);
119 if (!ac)
120 return Error(ENOMEM);
121 return file;
122}
123
124ErrorOr<LinuxFile *> create_file_from_fd(int fd, const char *mode) {
125 using ModeFlags = File::ModeFlags;
126 ModeFlags modeflags = File::mode_flags(mode);
127 if (modeflags == 0) {
128 return Error(EINVAL);
129 }
130
131 auto result = internal::fcntl(fd, F_GETFL);
132 if (!result.has_value()) {
133 return Error(EBADF);
134 }
135 int fd_flags = result.value();
136
137 using OpenMode = File::OpenMode;
138 if (((fd_flags & O_ACCMODE) == O_RDONLY &&
139 !(modeflags & static_cast<ModeFlags>(OpenMode::READ))) ||
140 ((fd_flags & O_ACCMODE) == O_WRONLY &&
141 !(modeflags & static_cast<ModeFlags>(OpenMode::WRITE)))) {
142 return Error(EINVAL);
143 }
144
145 bool do_seek = false;
146 if ((modeflags & static_cast<ModeFlags>(OpenMode::APPEND)) &&
147 !(fd_flags & O_APPEND)) {
148 do_seek = true;
149 if (!internal::fcntl(fd, F_SETFL,
150 reinterpret_cast<void *>(fd_flags | O_APPEND))
151 .has_value()) {
152 return Error(EBADF);
153 }
154 }
155
156 uint8_t *buffer;
157 {
158 AllocChecker ac;
159 buffer = new (ac) uint8_t[File::DEFAULT_BUFFER_SIZE];
160 if (!ac) {
161 return Error(ENOMEM);
162 }
163 }
164 AllocChecker ac;
165 auto *file = new (ac)
166 LinuxFile(fd, buffer, File::DEFAULT_BUFFER_SIZE, _IOFBF, true, modeflags);
167 if (!ac) {
168 return Error(ENOMEM);
169 }
170 if (do_seek) {
171 auto result = file->seek(0, SEEK_END);
172 if (!result.has_value()) {
173 free(file);
174 return Error(result.error());
175 }
176 }
177 return file;
178}
179
180int get_fileno(File *f) {
181 auto *lf = reinterpret_cast<LinuxFile *>(f);
182 return lf->get_fd();
183}
184
185} // namespace LIBC_NAMESPACE_DECL
186

source code of libc/src/__support/File/linux/file.cpp