AuroraRuntime/Source/IO/FS/FS.Unix.cpp
J Reece Wilson b6b4ef5dd1 [*] Linux: bDirectIO wasn't enabling O_DIRECT (dumb)
[*] Fixed build errors post refactor
[*] Don't create an epoll for a read-poll, reuse fd
[+] AuByteBuffer:Allocate(AuUInt length, AuUInt alignment)
[*] NT+Linux: Ensure IO buffers are aligned to AuHwInfo::GetPageSize()
2022-08-21 04:38:49 +01:00

417 lines
10 KiB
C++
Executable File

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: FS.Unix.cpp
Date: 2021-6-12
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "FS.hpp"
#include "FS.Generic.hpp"
#include <Source/Time/Time.hpp>
#if !defined(_AURUNTIME_GENERICFS)
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#if defined(AURORA_IS_LINUX_DERIVED)
#include <sys/sendfile.h>
#endif
namespace Aurora::IO::FS
{
bool _MkDir(const AuString &path)
{
return mkdir(path.c_str(), 0775) == 0;
}
struct ReadDirStructure : IReadDir
{
DIR *pDIR {};
struct dirent *pDE {};
bool bFirstTick { true };
bool bDead { false };
StatEx stat;
AuString sPath;
bool bFast {};
~ReadDirStructure()
{
if (this->pDIR)
{
::closedir(this->pDIR);
}
}
virtual StatEx *Next() override
{
bool bTryAgain {};
if (this->bDead)
{
return {};
}
do
{
if (!(this->pDE = ::readdir(this->pDIR)))
{
this->bDead = true;
return {};
}
if (this->pDE->d_name == AuString(".") ||
this->pDE->d_name == AuString(".."))
{
bTryAgain = true;
continue;
}
stat.bExists = true;
stat.fileName = this->pDE->d_name;
if (stat.fileName.empty())
{
bTryAgain = true;
continue;
}
stat.path = NormalizePathRet(this->sPath + stat.fileName);
if (this->bFast)
{
// nvm wont work. still need the is type dir/file flags
// return &stat;
}
if (!StatFile(stat.path.c_str(), stat))
{
bTryAgain = true;
}
}
while (AuExchange(bTryAgain, false));
return &stat;
}
};
static AuSPtr<IReadDir> ReadDirEx(const AuString &string, bool bFast)
{
auto pObj = AuMakeShared<ReadDirStructure>();
if (!pObj)
{
SysPushErrorMem();
return {};
}
pObj->bFast = bFast;
if (!AuIOFS::NormalizePath(pObj->sPath, string))
{
SysPushErrorMem();
return {};
}
if (!AuTryInsert(pObj->sPath, '/'))
{
SysPushErrorMem();
return {};
}
pObj->pDIR = ::opendir(pObj->sPath.c_str());
if (!pObj->pDIR)
{
SysPushErrorIO();
return {};
}
return pObj;
}
AUKN_SYM AuSPtr<IReadDir> ReadDir(const AuString &string)
{
return ReadDirEx(string, false);
}
AUKN_SYM bool FilesInDirectory(const AuString &string, AuList<AuString> &files)
{
auto itr = ReadDirEx(string, true);
if (!itr)
{
return false;
}
// SECURITY(): if next fails, its indistinguishable from end of file list, and will return true. it kinda sucks
while (auto stat = itr->Next())
{
if (!stat->bExistsFile)
{
continue;
}
if (!AuTryInsert(files, stat->fileName))
{
return false;
}
}
return true;
}
AUKN_SYM bool DirsInDirectory(const AuString &string, AuList<AuString> &dirs)
{
auto itr = ReadDirEx(string, true);
if (!itr)
{
return false;
}
// SECURITY(): if next fails, its indistinguishable from end of file list, and will return true. it kinda sucks
while (auto stat = itr->Next())
{
if (!stat->bExistsDirectory)
{
continue;
}
if (!AuTryInsert(dirs, stat->fileName))
{
return false;
}
}
return true;
}
AUKN_SYM bool WriteFile(const AuString &path, const Memory::MemoryViewRead &blob)
{
auto file = OpenWriteUnique(path);
SysCheckReturn(file, false);
AuUInt written = blob.length;
if (!file->Write(Memory::MemoryViewStreamRead(blob, written)))
{
SysPushErrorMem();
return false;
}
if (written != blob.length)
{
SysPushErrorIO();
return false;
}
return true;
}
AUKN_SYM bool ReadFile(const AuString &path, AuByteBuffer &buffer)
{
AuUInt read;
auto file = OpenReadUnique(path, EFileAdvisoryLockLevel::eNoSafety);
SysCheckReturn(file, false);
auto len = file->GetLength();
// NOTE: Linux filesystems are such a cluster fuck of unimplemented interfaces and half-assed drivers
// It's not unusual for these "files" to not support the required seek operations across NIX-like oses.
if (len == 0)
{
if (AuStartsWith(path, "/proc/") ||
AuStartsWith(path, "/sys/") ||
AuStartsWith(path, "/dev/"))
{
len = 4096 * 10;
}
else
{
buffer.clear();
return true;
}
}
if (!AuTryResize(buffer, len))
{
SysPushErrorMem();
return false;
}
if (!file->Read(Memory::MemoryViewStreamWrite {buffer.begin(), buffer.end(), read}))
{
SysPushErrorIO();
return false;
}
// NOTE: File devices love to lie
// Do not entertain an arbitrarily large page length provided by non-regular fds
return AuTryResize(buffer, read);
}
static bool UnixExists(const AuString &path, bool dir)
{
struct stat s;
int err = stat(path.c_str(), &s);
if (-1 == err)
{
SysAssert(ENOENT == errno, "General File IO Error, path {}", path);
return false;
}
return dir ? S_ISDIR(s.st_mode) : S_ISREG(s.st_mode);
}
AUKN_SYM bool FileExists(const AuString &path)
{
return UnixExists(NormalizePathRet(path), false);
}
AUKN_SYM bool DirExists(const AuString &path)
{
return UnixExists(NormalizePathRet(path), true);
}
AUKN_SYM bool DirMk(const AuString &path)
{
return CreateDirectories(NormalizePathRet(path), false);
}
AUKN_SYM bool Remove(const AuString &path)
{
return remove(NormalizePathRet(path).c_str()) != -1;
}
AUKN_SYM bool Relink(const AuString &src, const AuString &dest)
{
return rename(NormalizePathRet(src).c_str(), NormalizePathRet(dest).c_str()) != -1;
}
#if defined(AURORA_IS_LINUX_DERIVED)
AUKN_SYM bool Copy(const AuString &src, const AuString &dest)
{
auto normalizedSrcPath = NormalizePathRet(src);
auto normalizedDestPath = NormalizePathRet(dest);
int input, output;
if ((input = open(normalizedSrcPath.c_str(), O_RDONLY)) == -1)
{
return false;
}
struct stat fileinfo = { 0 };
if (fstat(input, &fileinfo) != 0)
{
close(input);
return false;
}
if ((output = creat(normalizedDestPath.c_str(), fileinfo.st_mode)) == -1)
{
close(input);
return false;
}
off_t bytesCopied = 0;
auto result = sendfile(output, input, &bytesCopied, fileinfo.st_size) != -1;
close(input);
close(output);
return result;
}
#elif defined(AURORA_IS_BSD_DERIVED)
AUKN_SYM bool Copy(const AuString &src, const AuString &dest)
{
auto normalizedSrcPath = NormalizePathRet(src);
auto normalizedDestPath = NormalizePathRet(dest);
int input, output;
if ((input = open(normalizedSrcPath.c_str(), O_RDONLY)) == -1)
{
return false;
}
struct stat fileinfo = { 0 };
if (fstat(input, &fileinfo) != 0)
{
close(input)
return false;
}
if ((output = creat(normalizedDestPath.c_str(), fileinfo.st_mode)) == -1)
{
close(input);
return false;
}
auto result = fcopyfile(input, output, 0, COPYFILE_ALL) == 0;
close(input);
close(output);
return result;
}
#else
AUKN_SYM bool Copy(const AuString &src, const AuString &dest)
{
// TODO: not that i care
return false;
}
#endif
AUKN_SYM bool StatFile(const AuString &pathRel, Stat &stat)
{
stat = {};
auto path = NormalizePathRet(pathRel);
struct stat s;
auto err = ::stat(path.c_str(), &s);
if (err == -1)
{
if (ENOENT != errno)
{
AuLogWarn("Critical IO error while stating file (errno: {}, path: {})", errno, path);
}
return false;
}
stat.bExistsFile = S_ISREG(s.st_mode);
stat.bExistsDirectory = S_ISDIR(s.st_mode);
stat.bExistsSystemResource = S_ISSOCK(s.st_mode);
stat.bExists = stat.bExistsFile || stat.bExistsDirectory || stat.bExistsSystemResource;
if (!stat.bExists)
{
AuLogWarn("Missing attribute type in stat mode {} (of path {})", s.st_mode, path);
return false;
}
stat.uSize = s.st_size;
stat.created = Time::CTimeToMS(s.st_ctime);
stat.modified = Time::CTimeToMS(s.st_mtime);
stat.accessed = Time::CTimeToMS(s.st_atime);
err = lstat(path.c_str(), &s);
if (err != -1)
{
stat.bSymLink = S_ISLNK(s.st_mode);
}
return true;
}
}
#endif