347 lines
8.5 KiB
C++
347 lines
8.5 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, bool bFlushOnClose, bool bWriteEoS)
|
|
{
|
|
int fd = (int)uOSHandle;
|
|
if (fd < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
if (bFlushOnClose)
|
|
{
|
|
::fsync(uOSHandle);
|
|
}
|
|
::close(fd);
|
|
#else
|
|
Grug::CloseHandle(uOSHandle, bFlushOnClose, bWriteEoS);
|
|
#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.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;
|
|
|
|
#if defined(O_DIRECT)
|
|
flags |= this->bDirectIO ? O_DIRECT : 0;
|
|
#else
|
|
// TBD
|
|
#endif
|
|
|
|
iFileDescriptor = PosixOpen(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 = PosixOpen(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 = PosixOpen(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))
|
|
{
|
|
RuntimeWaitForSecondaryTick();
|
|
|
|
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 (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->bShouldWriteEoS = create.bWriteEoSOnClose;
|
|
this->bFlushOnClose = create.bFlushOnClose;
|
|
|
|
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);
|
|
}
|
|
} |