xref: /aosp_15_r20/external/llvm-libc/src/__support/File/linux/file.cpp (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
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/macros/config.h"
19 #include "src/errno/libc_errno.h" // For error macros
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 
25 namespace LIBC_NAMESPACE_DECL {
26 
linux_file_write(File * f,const void * data,size_t size)27 FileIOResult 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 
linux_file_read(File * f,void * buf,size_t size)37 FileIOResult 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 
linux_file_seek(File * f,off_t offset,int whence)47 ErrorOr<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 
linux_file_close(File * f)55 int 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 
openfile(const char * path,const char * mode)65 ErrorOr<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 
create_file_from_fd(int fd,const char * mode)124 ErrorOr<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   int fd_flags = internal::fcntl(fd, F_GETFL);
132   if (fd_flags == -1) {
133     return Error(EBADF);
134   }
135 
136   using OpenMode = File::OpenMode;
137   if (((fd_flags & O_ACCMODE) == O_RDONLY &&
138        !(modeflags & static_cast<ModeFlags>(OpenMode::READ))) ||
139       ((fd_flags & O_ACCMODE) == O_WRONLY &&
140        !(modeflags & static_cast<ModeFlags>(OpenMode::WRITE)))) {
141     return Error(EINVAL);
142   }
143 
144   bool do_seek = false;
145   if ((modeflags & static_cast<ModeFlags>(OpenMode::APPEND)) &&
146       !(fd_flags & O_APPEND)) {
147     do_seek = true;
148     if (internal::fcntl(fd, F_SETFL,
149                         reinterpret_cast<void *>(fd_flags | O_APPEND)) == -1) {
150       return Error(EBADF);
151     }
152   }
153 
154   uint8_t *buffer;
155   {
156     AllocChecker ac;
157     buffer = new (ac) uint8_t[File::DEFAULT_BUFFER_SIZE];
158     if (!ac) {
159       return Error(ENOMEM);
160     }
161   }
162   AllocChecker ac;
163   auto *file = new (ac)
164       LinuxFile(fd, buffer, File::DEFAULT_BUFFER_SIZE, _IOFBF, true, modeflags);
165   if (!ac) {
166     return Error(ENOMEM);
167   }
168   if (do_seek) {
169     auto result = file->seek(0, SEEK_END);
170     if (!result.has_value()) {
171       free(file);
172       return Error(result.error());
173     }
174   }
175   return file;
176 }
177 
get_fileno(File * f)178 int get_fileno(File *f) {
179   auto *lf = reinterpret_cast<LinuxFile *>(f);
180   return lf->get_fd();
181 }
182 
183 } // namespace LIBC_NAMESPACE_DECL
184