AuroraRuntime/Source/IO/AuIOHandle.Unix.cpp

333 lines
8.0 KiB
C++

/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuIOHandle.Unix.cpp
Date: 2023-8-11
Author: Reece
***/
#include <RuntimeInternal.hpp>
#include "AuIOHandle.hpp"
#include "AuIOHandle.Unix.hpp"
#include "FS/FS.hpp"
#include "FS/FileAdvisory.Unix.hpp"
#include "FS/FileStream.Unix.hpp"
#include <Source/Grug/AuGrug.hpp>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#if defined(AURORA_COMPILER_CLANG)
// warning: enumeration values 'kEnumCount' and 'kEnumInvalid' not handled in switch [-Wswitch
#pragma clang diagnostic ignored "-Wswitch"
// Yea, I don't give a shit.
#endif
#if !defined(FD_CLOEXEC)
#define FD_CLOEXEC 0
#endif
namespace Aurora::IO
{
AuUInt64 DupHandle(AuUInt64 uOSHandle, bool bWriteAccess, bool bShareAccess)
{
int fd = ::dup(uOSHandle);
if (fd < 0)
{
return 0;
}
if (!SetFDShareAccess(uOSHandle, bShareAccess))
{
::close(fd);
return 0;
}
return AuUInt64(fd);
}
AuUInt64 AFileHandle::DupHandle(AuUInt64 uOSHandle, bool bWriteAccess)
{
return Aurora::IO::DupHandle(uOSHandle, bWriteAccess, false);
}
AuUInt64 AFileHandle::DupHandle(AuUInt64 uOSHandle, bool bWriteAccess, bool bShareAccess)
{
return Aurora::IO::DupHandle(uOSHandle, bWriteAccess, bShareAccess);
}
int SetFDShareAccess(AuUInt64 uOSHandle, bool bShareAccess)
{
int flags = ::fcntl(uOSHandle, F_GETFL, 0);
if (flags == -1)
{
return 0;
}
if (bShareAccess)
{
flags &= ~FD_CLOEXEC;
}
else
{
flags |= FD_CLOEXEC;
}
flags = ::fcntl(uOSHandle, F_SETFL, flags);
if (flags == -1)
{
return 0;
}
return 1;
}
void AFileHandle::CloseHandle(AuUInt64 uOSHandle)
{
int fd = (int)uOSHandle;
if (fd < 0)
{
return;
}
#if 0
::close(fd);
#else
Grug::CloseHandle(uOSHandle);
#endif
}
void AFileHandle::InitStdIn(bool bSharing)
{
this->uOSReadHandle = DupHandle(STDIN_FILENO, false, bSharing);
}
void AFileHandle::InitStdOut(bool bError, bool bSharing)
{
this->uOSReadHandle = this->uOSWriteHandle = DupHandle(bError ? STDERR_FILENO : STDOUT_FILENO, true, bSharing);
}
bool AFileHandle::InitFromPath(HandleCreate create)
{
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;
}
int flags {};
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 defined(O_EXCL)
flags = O_EXCL;
#else
if (AuFS::FileExists(pathex.c_str()))
{
SysPushErrorResourceExists("File {} already exists", create.path);
return false;
}
#endif
}
break;
}
};
flags |= create.eMode == FS::EFileOpenMode::eRead ? O_RDONLY : (O_RDWR | O_CREAT);
flags |= O_CLOEXEC;
flags |= this->bDirectIO ? O_DIRECT : 0;
iFileDescriptor = ::open(pathex.c_str(),
flags,
0664);
if (iFileDescriptor < 0)
{
if (create.bFailIfNonEmptyFile)
{
if (errno == EEXIST)
{
SysPushErrorResourceExists("File {} already exists", create.path);
return false;
}
}
if (errno != EISDIR &&
errno != ENOENT)
{
RuntimeWaitForSecondaryTick();
iFileDescriptor = ::open(pathex.c_str(),
flags,
0664);
}
if (iFileDescriptor < 0)
{
if (errno == EEXIST)
{
SysPushErrorResourceExists("File {} already exists", create.path);
return false;
}
if (errno == EISDIR)
{
flags &= ~(O_WRONLY | O_RDWR);
iFileDescriptor = ::open(pathex.c_str(),
flags,
0664);
}
if (iFileDescriptor < 0)
{
if (errno == ENFILE)
{
SysPushErrorIOResourceFailure("low resources");
return false;
}
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();
}
AUKN_SYM bool IsHandleTTY(AuUInt uHandle)
{
if ((AuSInt)uHandle < 0)
{
SysPushErrorArg();
return false;
}
return ::isatty((int)uHandle);
}
AUKN_SYM bool IsHandlePipe(AuUInt uHandle)
{
struct stat st;
if ((AuSInt)uHandle < 0)
{
SysPushErrorArg();
return false;
}
if (::fstat((int)uHandle, &st) != 0)
{
SysPushErrorIO("fstat failed");
return false;
}
return S_ISFIFO(st.st_mode);
}
AUKN_SYM bool IsHandleFile(AuUInt uHandle)
{
struct stat st;
if ((AuSInt)uHandle < 0)
{
SysPushErrorArg();
return false;
}
if (::fstat((int)uHandle, &st) != 0)
{
SysPushErrorIO("fstat failed");
return false;
}
return S_ISREG(st.st_mode);
}
}