xref: /llvm-project/libc/src/__support/File/linux/file.cpp (revision abc49cc19463970d5523d7d3332e4c1f83bc2ef7)
10f6ef9bcSSiva Chandra Reddy //===--- Implementation of the Linux specialization of File ---------------===//
21ef0bafcSJoseph Huber //
31ef0bafcSJoseph Huber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
41ef0bafcSJoseph Huber // See https://llvm.org/LICENSE.txt for license information.
51ef0bafcSJoseph Huber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61ef0bafcSJoseph Huber //
71ef0bafcSJoseph Huber //===----------------------------------------------------------------------===//
81ef0bafcSJoseph Huber 
90f6ef9bcSSiva Chandra Reddy #include "file.h"
100f6ef9bcSSiva Chandra Reddy 
115aed6d67SMichael Jones #include "hdr/stdio_macros.h"
125aed6d67SMichael Jones #include "hdr/types/off_t.h"
131ef0bafcSJoseph Huber #include "src/__support/CPP/new.h"
140b24b470SXu Zhang #include "src/__support/File/file.h"
158cd4ecfaSMikhail R. Gadelha #include "src/__support/File/linux/lseekImpl.h"
160b24b470SXu Zhang #include "src/__support/OSUtil/fcntl.h"
171ef0bafcSJoseph Huber #include "src/__support/OSUtil/syscall.h" // For internal syscall function.
185ff3ff33SPetr Hosek #include "src/__support/macros/config.h"
191ef0bafcSJoseph Huber #include "src/errno/libc_errno.h" // For error macros
201ef0bafcSJoseph Huber 
21*abc49cc1SJob Henandez Lara #include "hdr/fcntl_macros.h" // For mode_t and other flags to the open syscall
22eaa11526SNick Desaulniers #include <sys/stat.h>    // For S_IS*, S_IF*, and S_IR* flags.
231ef0bafcSJoseph Huber #include <sys/syscall.h> // For syscall numbers
241ef0bafcSJoseph Huber 
255ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL {
261ef0bafcSJoseph Huber 
270f6ef9bcSSiva Chandra Reddy FileIOResult linux_file_write(File *f, const void *data, size_t size) {
281ef0bafcSJoseph Huber   auto *lf = reinterpret_cast<LinuxFile *>(f);
29b6bc9d72SGuillaume Chatelet   int ret =
30b6bc9d72SGuillaume Chatelet       LIBC_NAMESPACE::syscall_impl<int>(SYS_write, lf->get_fd(), data, size);
311ef0bafcSJoseph Huber   if (ret < 0) {
321ef0bafcSJoseph Huber     return {0, -ret};
331ef0bafcSJoseph Huber   }
341ef0bafcSJoseph Huber   return ret;
351ef0bafcSJoseph Huber }
361ef0bafcSJoseph Huber 
370f6ef9bcSSiva Chandra Reddy FileIOResult linux_file_read(File *f, void *buf, size_t size) {
381ef0bafcSJoseph Huber   auto *lf = reinterpret_cast<LinuxFile *>(f);
39b6bc9d72SGuillaume Chatelet   int ret =
40b6bc9d72SGuillaume Chatelet       LIBC_NAMESPACE::syscall_impl<int>(SYS_read, lf->get_fd(), buf, size);
411ef0bafcSJoseph Huber   if (ret < 0) {
421ef0bafcSJoseph Huber     return {0, -ret};
431ef0bafcSJoseph Huber   }
441ef0bafcSJoseph Huber   return ret;
451ef0bafcSJoseph Huber }
461ef0bafcSJoseph Huber 
477776fba4SMikhail R. Gadelha ErrorOr<off_t> linux_file_seek(File *f, off_t offset, int whence) {
481ef0bafcSJoseph Huber   auto *lf = reinterpret_cast<LinuxFile *>(f);
498cd4ecfaSMikhail R. Gadelha   auto result = internal::lseekimpl(lf->get_fd(), offset, whence);
508cd4ecfaSMikhail R. Gadelha   if (!result.has_value())
518cd4ecfaSMikhail R. Gadelha     return result.error();
528cd4ecfaSMikhail R. Gadelha   return result.value();
531ef0bafcSJoseph Huber }
541ef0bafcSJoseph Huber 
550f6ef9bcSSiva Chandra Reddy int linux_file_close(File *f) {
561ef0bafcSJoseph Huber   auto *lf = reinterpret_cast<LinuxFile *>(f);
57b6bc9d72SGuillaume Chatelet   int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_close, lf->get_fd());
581ef0bafcSJoseph Huber   if (ret < 0) {
591ef0bafcSJoseph Huber     return -ret;
601ef0bafcSJoseph Huber   }
6175d70b73SSiva Chandra Reddy   delete lf;
621ef0bafcSJoseph Huber   return 0;
631ef0bafcSJoseph Huber }
641ef0bafcSJoseph Huber 
651ef0bafcSJoseph Huber ErrorOr<File *> openfile(const char *path, const char *mode) {
661ef0bafcSJoseph Huber   using ModeFlags = File::ModeFlags;
671ef0bafcSJoseph Huber   auto modeflags = File::mode_flags(mode);
681ef0bafcSJoseph Huber   if (modeflags == 0) {
691ef0bafcSJoseph Huber     // return {nullptr, EINVAL};
701ef0bafcSJoseph Huber     return Error(EINVAL);
711ef0bafcSJoseph Huber   }
721ef0bafcSJoseph Huber   long open_flags = 0;
731ef0bafcSJoseph Huber   if (modeflags & ModeFlags(File::OpenMode::APPEND)) {
741ef0bafcSJoseph Huber     open_flags = O_CREAT | O_APPEND;
751ef0bafcSJoseph Huber     if (modeflags & ModeFlags(File::OpenMode::PLUS))
761ef0bafcSJoseph Huber       open_flags |= O_RDWR;
771ef0bafcSJoseph Huber     else
781ef0bafcSJoseph Huber       open_flags |= O_WRONLY;
791ef0bafcSJoseph Huber   } else if (modeflags & ModeFlags(File::OpenMode::WRITE)) {
801ef0bafcSJoseph Huber     open_flags = O_CREAT | O_TRUNC;
811ef0bafcSJoseph Huber     if (modeflags & ModeFlags(File::OpenMode::PLUS))
821ef0bafcSJoseph Huber       open_flags |= O_RDWR;
831ef0bafcSJoseph Huber     else
841ef0bafcSJoseph Huber       open_flags |= O_WRONLY;
851ef0bafcSJoseph Huber   } else {
861ef0bafcSJoseph Huber     if (modeflags & ModeFlags(File::OpenMode::PLUS))
871ef0bafcSJoseph Huber       open_flags |= O_RDWR;
881ef0bafcSJoseph Huber     else
891ef0bafcSJoseph Huber       open_flags |= O_RDONLY;
901ef0bafcSJoseph Huber   }
911ef0bafcSJoseph Huber 
921ef0bafcSJoseph Huber   // File created will have 0666 permissions.
931ef0bafcSJoseph Huber   constexpr long OPEN_MODE =
941ef0bafcSJoseph Huber       S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
951ef0bafcSJoseph Huber 
961ef0bafcSJoseph Huber #ifdef SYS_open
97f0a3954eSMichael Jones   int fd =
98b6bc9d72SGuillaume Chatelet       LIBC_NAMESPACE::syscall_impl<int>(SYS_open, path, open_flags, OPEN_MODE);
991ef0bafcSJoseph Huber #elif defined(SYS_openat)
100b6bc9d72SGuillaume Chatelet   int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_openat, AT_FDCWD, path,
101f0a3954eSMichael Jones                                              open_flags, OPEN_MODE);
1021ef0bafcSJoseph Huber #else
1031ef0bafcSJoseph Huber #error "open and openat syscalls not available."
1041ef0bafcSJoseph Huber #endif
1051ef0bafcSJoseph Huber 
1061ef0bafcSJoseph Huber   if (fd < 0)
1071ef0bafcSJoseph Huber     return Error(-fd);
1081ef0bafcSJoseph Huber 
1091ef0bafcSJoseph Huber   uint8_t *buffer;
1101ef0bafcSJoseph Huber   {
1111ef0bafcSJoseph Huber     AllocChecker ac;
1121ef0bafcSJoseph Huber     buffer = new (ac) uint8_t[File::DEFAULT_BUFFER_SIZE];
1131ef0bafcSJoseph Huber     if (!ac)
1141ef0bafcSJoseph Huber       return Error(ENOMEM);
1151ef0bafcSJoseph Huber   }
1161ef0bafcSJoseph Huber   AllocChecker ac;
1171ef0bafcSJoseph Huber   auto *file = new (ac)
1181ef0bafcSJoseph Huber       LinuxFile(fd, buffer, File::DEFAULT_BUFFER_SIZE, _IOFBF, true, modeflags);
1191ef0bafcSJoseph Huber   if (!ac)
1201ef0bafcSJoseph Huber     return Error(ENOMEM);
1211ef0bafcSJoseph Huber   return file;
1221ef0bafcSJoseph Huber }
1231ef0bafcSJoseph Huber 
1240b24b470SXu Zhang ErrorOr<LinuxFile *> create_file_from_fd(int fd, const char *mode) {
1250b24b470SXu Zhang   using ModeFlags = File::ModeFlags;
1260b24b470SXu Zhang   ModeFlags modeflags = File::mode_flags(mode);
1270b24b470SXu Zhang   if (modeflags == 0) {
1280b24b470SXu Zhang     return Error(EINVAL);
1290b24b470SXu Zhang   }
1300b24b470SXu Zhang 
1310b24b470SXu Zhang   int fd_flags = internal::fcntl(fd, F_GETFL);
1320b24b470SXu Zhang   if (fd_flags == -1) {
1330b24b470SXu Zhang     return Error(EBADF);
1340b24b470SXu Zhang   }
1350b24b470SXu Zhang 
1360b24b470SXu Zhang   using OpenMode = File::OpenMode;
1370b24b470SXu Zhang   if (((fd_flags & O_ACCMODE) == O_RDONLY &&
1380b24b470SXu Zhang        !(modeflags & static_cast<ModeFlags>(OpenMode::READ))) ||
1390b24b470SXu Zhang       ((fd_flags & O_ACCMODE) == O_WRONLY &&
1400b24b470SXu Zhang        !(modeflags & static_cast<ModeFlags>(OpenMode::WRITE)))) {
1410b24b470SXu Zhang     return Error(EINVAL);
1420b24b470SXu Zhang   }
1430b24b470SXu Zhang 
1440b24b470SXu Zhang   bool do_seek = false;
1450b24b470SXu Zhang   if ((modeflags & static_cast<ModeFlags>(OpenMode::APPEND)) &&
1460b24b470SXu Zhang       !(fd_flags & O_APPEND)) {
1470b24b470SXu Zhang     do_seek = true;
1480b24b470SXu Zhang     if (internal::fcntl(fd, F_SETFL,
1490b24b470SXu Zhang                         reinterpret_cast<void *>(fd_flags | O_APPEND)) == -1) {
1500b24b470SXu Zhang       return Error(EBADF);
1510b24b470SXu Zhang     }
1520b24b470SXu Zhang   }
1530b24b470SXu Zhang 
1540b24b470SXu Zhang   uint8_t *buffer;
1550b24b470SXu Zhang   {
1560b24b470SXu Zhang     AllocChecker ac;
1570b24b470SXu Zhang     buffer = new (ac) uint8_t[File::DEFAULT_BUFFER_SIZE];
1580b24b470SXu Zhang     if (!ac) {
1590b24b470SXu Zhang       return Error(ENOMEM);
1600b24b470SXu Zhang     }
1610b24b470SXu Zhang   }
1620b24b470SXu Zhang   AllocChecker ac;
1630b24b470SXu Zhang   auto *file = new (ac)
1640b24b470SXu Zhang       LinuxFile(fd, buffer, File::DEFAULT_BUFFER_SIZE, _IOFBF, true, modeflags);
1650b24b470SXu Zhang   if (!ac) {
1660b24b470SXu Zhang     return Error(ENOMEM);
1670b24b470SXu Zhang   }
1680b24b470SXu Zhang   if (do_seek) {
1690b24b470SXu Zhang     auto result = file->seek(0, SEEK_END);
1700b24b470SXu Zhang     if (!result.has_value()) {
1710b24b470SXu Zhang       free(file);
1720b24b470SXu Zhang       return Error(result.error());
1730b24b470SXu Zhang     }
1740b24b470SXu Zhang   }
1750b24b470SXu Zhang   return file;
1760b24b470SXu Zhang }
1770b24b470SXu Zhang 
1781ef0bafcSJoseph Huber int get_fileno(File *f) {
1791ef0bafcSJoseph Huber   auto *lf = reinterpret_cast<LinuxFile *>(f);
1801ef0bafcSJoseph Huber   return lf->get_fd();
1811ef0bafcSJoseph Huber }
1821ef0bafcSJoseph Huber 
1835ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL
184