/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: FileStream.Unix.cpp Date: 2021-6-12 Author: Reece ***/ #define _FILE_OFFSET_BITS 64 #define _LARGEFILE64_SOURCE #include #include "FS.hpp" #include "FileStream.Generic.hpp" #include #include #include #if !defined(_AURUNTIME_GENERICFILESTREAM) namespace Aurora::IO::FS { static const AuUInt64 kFileCopyBlock = 0x4000; // 16KiB static bool PosixLseek63(int fd, AuUInt64 offset, int whence, AuUInt64 *pOffset) { if (offset > std::numeric_limits::max()) { SysPushErrorIO("int overflow exploit?"); return false; } #if defined(AURORA_IS_LINUX_DERIVED) auto ret = lseek64(fd, offset, whence); #elif defined(AURORA_IS_64BIT) static_assert(std::numeric_limits::max() >= std::numeric_limits::max(), "unsupported posix os"); auto ret = lseek(fd, offset, whence); #else #error accient 32-bit posix operating systems require 64-bit file awareness #endif if (ret < 0) { SysPushErrorIO("PosixLseek63 IO Error: %i", ret); return false; } if (pOffset) { *pOffset = ret; } return true; } static AuUInt64 PosixGetOffset(int fd) { AuUInt64 ret; if (!PosixLseek63(fd, 0, SEEK_SET, &ret)) { return 0; } return ret; } static bool PosixSetOffset(int fd, AuUInt64 offset) { return PosixLseek63(fd, offset, SEEK_SET, nullptr); } static AuUInt64 PosixGetLength(int fd) { AuUInt64 ret {}, old {}; bool status {}; if (!PosixLseek63(fd, 0, SEEK_SET, &old)) { return 0; } status = PosixLseek63(fd, 0, SEEK_END, &ret); status &= PosixLseek63(fd, old, SEEK_SET, nullptr); return status ? ret : 0; } static bool PosixRead(int fd, void *buf, AuUInt32 count, AuUInt32 *pRead) { auto ret = read(fd, buf, count); if (ret < 0) { SysPushErrorIO("PosixRead IO Error: %i", ret); return false; } if (pRead) { *pRead = ret; } return true; } static bool PosixWrite(int fd, const void *buf, AuUInt32 count, AuUInt32 *pWritten) { auto ret = write(fd, buf, count); if (ret < 0) { SysPushErrorIO("PosixWrite IO Error: %i", ret); return false; } if (pWritten) { *pWritten = ret; } return true; } PosixFileStream::~PosixFileStream() { Close(); } bool PosixFileStream::Init(int handle, const AuString &path) { AuCtorCode_t code; this->handle_ = handle; this->path_ = AuTryConstruct(code, path); if (!code) { return false; } return true; } AuUInt64 PosixFileStream::GetOffset() { AU_LOCK_GUARD(this->spinlock_); if (this->handle_ == -1) { SysPushErrorUninitialized(); return 0; } return PosixGetOffset(this->handle_); } bool PosixFileStream::SetOffset(AuUInt64 offset) { AU_LOCK_GUARD(this->spinlock_); if (this->handle_ == -1) { SysPushErrorUninitialized(); return 0; } return PosixSetOffset(this->handle_, offset); } AuUInt64 PosixFileStream::GetLength() { AU_LOCK_GUARD(this->spinlock_); if (this->handle_ == -1) { SysPushErrorUninitialized(); return 0; } return PosixGetLength(this->handle_); } bool PosixFileStream::Read(const Memory::MemoryViewStreamWrite ¶meters) { AU_LOCK_GUARD(this->spinlock_); if (this->handle_ == -1) { SysPushErrorUninitialized(); return 0; } auto length = parameters.length; AuUInt offset {0}; while (length) { AuUInt32 read; int blockSize = AuMin(kFileCopyBlock, length); if (!PosixRead(handle_, &reinterpret_cast(parameters.ptr)[offset], blockSize, &read)) { SysPushErrorNested("File Error: {}", path_); break; } if (read == 0) { break; } offset += read; length -= read; } if (!offset) { return false; } parameters.outVariable = offset; return true; } bool PosixFileStream::Write(const Memory::MemoryViewStreamRead ¶meters) { AU_LOCK_GUARD(this->spinlock_); if (this->handle_ == -1) { SysPushErrorUninitialized(); return 0; } AuUInt length = parameters.length; AuUInt offset {0}; while (length) { AuUInt32 written; int blockSize = AuMin(AuUInt(kFileCopyBlock), length); if (!PosixWrite(this->handle_, &reinterpret_cast(parameters.ptr)[offset], blockSize, &written)) { SysPushErrorNested("File Error: {}", path_); return false; } if (written != blockSize) { SysPushErrorNested("File Error: {}", path_); break; } offset += written; length -= written; } if (!offset) { return false; } parameters.outVariable = offset; return true; } void PosixFileStream::Close() { int handle; if ((handle = std::exchange(this->handle_, -1)) != -1) { ::close(handle); } if (AuExchange(this->bMadeTemporary, false)) { ::unlink(this->path_.c_str()); } } void PosixFileStream::Flush() { ::fsync(this->handle_); } void PosixFileStream::WriteEoS() { ::ftruncate(this->handle_, GetOffset()); } void PosixFileStream::MakeTemporary() { this->bMadeTemporary = true; } static IFileStream *OpenNew(const AuString &path, EFileOpenMode openMode, EFileAdvisoryLockLevel lock) { auto pathex = NormalizePathRet(path); if (openMode != EFileOpenMode::eRead) { CreateDirectories(pathex, true); } auto fileHandle = open(pathex.c_str(), openMode == EFileOpenMode::eRead ? O_RDONLY : O_RDWR); if (fileHandle < 0) { SysPushErrorIO("Couldn't open file: {}", path); return nullptr; } auto stream = _new PosixFileStream(); if (!stream) { ::close(fileHandle); SysPushErrorMem("{}", path); return nullptr; } if (!stream->Init(fileHandle, pathex)) { delete stream; SysPushErrorGeneric("{}", path); return nullptr; } return stream; } AUKN_SYM void OpenRelease(IFileStream *that) { AuSafeDelete(that); } AUKN_SYM IFileStream *OpenReadNew(const AuString &path, EFileAdvisoryLockLevel level) { return OpenNew(path, EFileOpenMode::eRead, level); } AUKN_SYM void OpenReadRelease(IFileStream * that) { AuSafeDelete(that); } AUKN_SYM IFileStream *OpenWriteNew(const AuString &path, EFileAdvisoryLockLevel level) { return OpenNew(path, EFileOpenMode::eWrite, level); } AUKN_SYM void OpenWriteRelease(IFileStream *that) { AuSafeDelete(that); } } #endif