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