/*** Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuIOHandle.Unix.cpp Date: 2023-8-11 Author: Reece ***/ #include #include "AuIOHandle.hpp" #include "AuIOHandle.Unix.hpp" #include "FS/FS.hpp" #include "FS/FileAdvisory.Unix.hpp" #include "FS/FileStream.Unix.hpp" #include #include #include namespace Aurora::IO { AuUInt64 AFileHandle::DupHandle(AuUInt64 uOSHandle, bool bWriteAccess) { int fd = dup(uOSHandle); if (fd < 0) { return 0; } return AuUInt64(fd); } void AFileHandle::CloseHandle(AuUInt64 uOSHandle) { int fd = (int)uOSHandle; if (fd < 0) { return; } ::close(fd); } struct UnixIOHandle final : AFileHandle { bool InitFromPath(HandleCreate create) override { int iFileDescriptor { -1 }; if (create.path.empty()) { SysPushErrorArg("Cannot open an IO handle to the provided empty path"); return false; } if (!FS::EFileOpenModeIsValid(create.eMode)) { SysPushErrorParam("Invalid open mode"); return false; } if (!FS::EFileAdvisoryLockLevelIsValid(create.eAdvisoryLevel)) { SysPushErrorParam("Invalid lock mode"); return false; } auto pathex = FS::NormalizePathRet(create.path); if (pathex.empty()) { SysPushErrorMemory(); return false; } if (create.bAsyncHandle || create.bDirectIOMode) { this->bDirectIO = true; } switch (create.eMode) { case FS::EFileOpenMode::eRead: { break; } case FS::EFileOpenMode::eReadWrite: case FS::EFileOpenMode::eWrite: { if (create.bAlwaysCreateDirTree) { FS::CreateDirectories(pathex, true); } if (create.bFailIfNonEmptyFile) { if (AuFS::FileExists(pathex.c_str())) { SysPushErrorResourceExists("File {} already exists", create.path); return false; } } break; } }; iFileDescriptor = ::open(pathex.c_str(), (create.eMode == FS::EFileOpenMode::eRead ? O_RDONLY : (O_RDWR | O_CREAT)) | O_CLOEXEC | (this->bDirectIO ? O_DIRECT : 0), 0664); if (iFileDescriptor < 0) { SysPushErrorIO("Couldn't open file: {} ({}) {}", path, pathex, errno); return false; } if (!FS::ApplyDumbAdvisoryLock(iFileDescriptor, create.eAdvisoryLevel)) { SysPushErrorIO("Couldn't open file: {}. File node (not section) is locked.", path); ::close(iFileDescriptor); return false; } if (create.bFailIfNonEmptyFile) { if (FS::PosixGetLength(iFileDescriptor)) { SysPushErrorResourceExists("File {} already exists", create.path); ::close(iFileDescriptor); return false; } } switch (create.eMode) { case FS::EFileOpenMode::eRead: { this->uOSReadHandle = AuUInt64(iFileDescriptor); break; } case FS::EFileOpenMode::eReadWrite: { this->uOSWriteHandle = AuUInt64(iFileDescriptor); this->uOSReadHandle = AuUInt64(iFileDescriptor); break; } case FS::EFileOpenMode::eWrite: { this->uOSWriteHandle = AuUInt64(iFileDescriptor); break; } }; this->bIsAsync = create.bAsyncHandle; this->path = create.path; return this->IsValid(); } bool IsFile() override; bool IsTTY() override; bool IsPipe() override; AuOptionalEx optIsFile {}; AuOptionalEx optIsPipe {}; AuOptionalEx optIsTTY {}; }; bool UnixIOHandle::IsFile() { bool bIsFile {}; struct stat st; int iFileDescriptor {}; if (auto file = this->optIsFile) { return file.value(); } if (auto optHandle = this->GetOSHandleSafe()) { iFileDescriptor = (int)optHandle.value(); } else { SysPushErrorUninitialized(); return false; } if (::fstat(iFileDescriptor, &st) != 0) { SysPushErrorIO("fstat failed"); return false; } bIsFile = S_ISREG(st.st_mode); this->optIsFile = bIsFile; return bIsFile; } bool UnixIOHandle::IsTTY() { bool bIsTTY {}; int iFileDescriptor {}; if (auto file = this->optIsTTY) { return file.value(); } if (auto optHandle = this->GetOSHandleSafe()) { iFileDescriptor = (int)optHandle.value(); } else { SysPushErrorUninitialized(); return false; } bIsTTY = ::isatty(iFileDescriptor); this->optIsTTY = bIsTTY; return bIsTTY; } bool UnixIOHandle::IsPipe() { bool bIsPipe {}; struct stat st; int iFileDescriptor {}; if (auto file = this->optIsPipe) { return file.value(); } if (auto optHandle = this->GetOSHandleSafe()) { iFileDescriptor = (int)optHandle.value(); } else { SysPushErrorUninitialized(); return false; } if (::fstat(iFileDescriptor, &st) != 0) { SysPushErrorIO("fstat failed"); return false; } bIsPipe = S_ISFIFO(st.st_mode); this->optIsPipe = bIsPipe; return bIsPipe; } AUKN_SYM IIOHandle *IOHandleNew() { return _new UnixIOHandle(); } AUKN_SYM void IOHandleRelease(IIOHandle *pIOHandle) { AuSafeDelete(pIOHandle); } AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, IOHandle, UnixIOHandle) }