2021-06-27 21:25:29 +00:00
|
|
|
/***
|
|
|
|
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
|
|
|
|
File: FS.Unix.cpp
|
|
|
|
Date: 2021-6-12
|
|
|
|
Author: Reece
|
|
|
|
***/
|
2021-09-30 14:57:41 +00:00
|
|
|
#include <Source/RuntimeInternal.hpp>
|
2021-06-27 21:25:29 +00:00
|
|
|
#include "FS.hpp"
|
|
|
|
#include "FS.Generic.hpp"
|
2021-09-30 14:57:41 +00:00
|
|
|
#include <Source/Time/Time.hpp>
|
2021-06-27 21:25:29 +00:00
|
|
|
|
|
|
|
#if !defined(_AURUNTIME_GENERICFS)
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
2022-08-02 04:52:17 +00:00
|
|
|
#include <dirent.h>
|
2021-06-27 21:25:29 +00:00
|
|
|
#include <sys/stat.h>
|
2021-09-06 10:58:08 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
2021-06-27 21:25:29 +00:00
|
|
|
|
2021-10-02 16:07:33 +00:00
|
|
|
#if defined(AURORA_IS_LINUX_DERIVED)
|
|
|
|
#include <sys/sendfile.h>
|
|
|
|
#endif
|
|
|
|
|
2021-06-27 21:25:29 +00:00
|
|
|
namespace Aurora::IO::FS
|
|
|
|
{
|
2024-04-19 03:33:57 +00:00
|
|
|
bool _MkDir(const AuROString &path)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2024-05-07 13:41:16 +00:00
|
|
|
AuROString subdir;
|
2022-12-17 15:37:46 +00:00
|
|
|
|
|
|
|
if ((path.size() > 1) &&
|
|
|
|
((path[path.size() - 1] == '/') ||
|
|
|
|
(path[path.size() - 1] == '\\')))
|
|
|
|
{
|
2024-05-07 13:41:16 +00:00
|
|
|
subdir = AuROString(path).substr(0, path.size() - 1);
|
2022-12-17 15:37:46 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
subdir = path;
|
|
|
|
}
|
|
|
|
|
|
|
|
GoUpToSeparator(subdir, subdir);
|
|
|
|
|
|
|
|
mode_t mode { 0775 };
|
|
|
|
|
|
|
|
struct stat s;
|
2024-05-07 13:41:16 +00:00
|
|
|
if (::stat(AuString(subdir).c_str(), &s) != -1)
|
2022-12-17 15:37:46 +00:00
|
|
|
{
|
|
|
|
mode = s.st_mode;
|
|
|
|
}
|
|
|
|
|
2024-05-07 13:41:16 +00:00
|
|
|
return ::mkdir(AuString(path).c_str(), mode) == 0;
|
2021-06-27 21:25:29 +00:00
|
|
|
}
|
|
|
|
|
2022-08-02 04:52:17 +00:00
|
|
|
struct ReadDirStructure : IReadDir
|
|
|
|
{
|
|
|
|
DIR *pDIR {};
|
|
|
|
struct dirent *pDE {};
|
|
|
|
bool bFirstTick { true };
|
|
|
|
bool bDead { false };
|
|
|
|
StatEx stat;
|
|
|
|
AuString sPath;
|
|
|
|
bool bFast {};
|
2024-03-19 18:08:54 +00:00
|
|
|
AuUInt32 uErrorCount {};
|
2022-08-02 04:52:17 +00:00
|
|
|
|
|
|
|
~ReadDirStructure()
|
|
|
|
{
|
|
|
|
if (this->pDIR)
|
|
|
|
{
|
|
|
|
::closedir(this->pDIR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-19 18:08:54 +00:00
|
|
|
virtual AuUInt32 GetErrorCount() override
|
|
|
|
{
|
|
|
|
return this->uErrorCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual AuList<AuString> GetErrorPaths() override
|
|
|
|
{
|
|
|
|
if (this->uErrorCount)
|
|
|
|
{
|
|
|
|
return { this->sPath };
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-02 04:52:17 +00:00
|
|
|
virtual StatEx *Next() override
|
|
|
|
{
|
2024-03-19 18:08:54 +00:00
|
|
|
AU_DEBUG_MEMCRUNCH;
|
|
|
|
|
2022-08-02 04:52:17 +00:00
|
|
|
bool bTryAgain {};
|
|
|
|
|
|
|
|
if (this->bDead)
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
2024-03-19 18:08:54 +00:00
|
|
|
errno = 0;
|
|
|
|
|
2022-08-02 04:52:17 +00:00
|
|
|
if (!(this->pDE = ::readdir(this->pDIR)))
|
|
|
|
{
|
|
|
|
this->bDead = true;
|
2024-03-19 18:08:54 +00:00
|
|
|
if (errno)
|
|
|
|
{
|
|
|
|
this->uErrorCount++;
|
|
|
|
}
|
2022-08-02 04:52:17 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->pDE->d_name == AuString(".") ||
|
|
|
|
this->pDE->d_name == AuString(".."))
|
|
|
|
{
|
|
|
|
bTryAgain = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-05-07 13:41:16 +00:00
|
|
|
stat.bExists = true;
|
2022-08-02 04:52:17 +00:00
|
|
|
|
|
|
|
stat.fileName = this->pDE->d_name;
|
|
|
|
|
|
|
|
if (stat.fileName.empty())
|
|
|
|
{
|
|
|
|
bTryAgain = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-07-04 17:50:33 +00:00
|
|
|
AuString name = stat.fileName;
|
|
|
|
AuReplaceAll(name, "\\", "\\\\");
|
|
|
|
stat.path = this->sPath + name;
|
2022-08-02 04:52:17 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
static AuSPtr<IReadDir> ReadDirEx(const AuROString &string, bool bFast)
|
2022-08-02 04:52:17 +00:00
|
|
|
{
|
2023-09-16 00:13:41 +00:00
|
|
|
if (string.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2022-08-02 04:52:17 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
AUKN_SYM AuSPtr<IReadDir> ReadDir(const AuROString &string)
|
2022-08-02 04:52:17 +00:00
|
|
|
{
|
|
|
|
return ReadDirEx(string, false);
|
|
|
|
}
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
AUKN_SYM bool FilesInDirectory(const AuROString &string, AuList<AuString> &files)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2022-08-02 04:52:17 +00:00
|
|
|
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())
|
|
|
|
{
|
2022-08-21 02:53:50 +00:00
|
|
|
if (!stat->bExistsFile)
|
2022-08-02 04:52:17 +00:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!AuTryInsert(files, stat->fileName))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2021-06-27 21:25:29 +00:00
|
|
|
}
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
AUKN_SYM bool DirsInDirectory(const AuROString &string, AuList<AuString> &dirs)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2022-08-02 04:52:17 +00:00
|
|
|
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())
|
|
|
|
{
|
2022-08-21 02:53:50 +00:00
|
|
|
if (!stat->bExistsDirectory)
|
2022-08-02 04:52:17 +00:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!AuTryInsert(dirs, stat->fileName))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2021-06-27 21:25:29 +00:00
|
|
|
}
|
2023-02-04 19:43:01 +00:00
|
|
|
|
2024-07-04 17:50:33 +00:00
|
|
|
bool SpecialsInDirectory(const AuROString &string, AuList<AuString> &dirs)
|
|
|
|
{
|
|
|
|
auto itr = ReadDirEx(string, true);
|
|
|
|
if (!itr)
|
|
|
|
{
|
|
|
|
AuLogDbg("failed to open special read dir");
|
|
|
|
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 0
|
|
|
|
if (!stat->bExistsSystemResource)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!AuTryInsert(dirs, stat->fileName))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
AUKN_SYM bool ReadFile(const AuROString &path, AuByteBuffer &buffer)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2022-09-19 05:42:39 +00:00
|
|
|
AuMemoryViewWrite writeView;
|
2022-04-06 01:24:38 +00:00
|
|
|
|
2023-09-16 00:13:41 +00:00
|
|
|
if (path.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-01-25 16:47:25 +00:00
|
|
|
bool bIsStupidFD =
|
|
|
|
AuStartsWith(path, "/proc/") ||
|
|
|
|
AuStartsWith(path, "/sys/") ||
|
|
|
|
AuStartsWith(path, "/dev/");
|
|
|
|
|
|
|
|
auto pFile = OpenReadUnique(path, bIsStupidFD ? EFileAdvisoryLockLevel::eNoSafety : EFileAdvisoryLockLevel::eBlockWrite);
|
|
|
|
SysCheckReturn(pFile, false);
|
2021-09-06 10:58:08 +00:00
|
|
|
|
2023-01-25 16:47:25 +00:00
|
|
|
bool bIsZero = buffer.readPtr == buffer.base;
|
|
|
|
auto qwLength = pFile->GetLength();
|
2022-04-06 01:24:38 +00:00
|
|
|
|
2022-04-20 23:34:08 +00:00
|
|
|
// 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.
|
2023-01-26 21:43:19 +00:00
|
|
|
if (qwLength == 0)
|
2022-04-06 01:24:38 +00:00
|
|
|
{
|
2023-01-25 16:47:25 +00:00
|
|
|
if (bIsStupidFD)
|
2022-04-06 01:24:38 +00:00
|
|
|
{
|
2023-01-25 16:47:25 +00:00
|
|
|
qwLength = 4096 * 10;
|
2022-04-06 01:24:38 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-25 16:47:25 +00:00
|
|
|
writeView = buffer.GetOrAllocateLinearWriteable(qwLength);
|
2022-09-19 05:42:39 +00:00
|
|
|
if (!writeView)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2022-09-19 05:42:39 +00:00
|
|
|
return {};
|
2021-06-27 21:25:29 +00:00
|
|
|
}
|
2021-10-02 16:07:33 +00:00
|
|
|
|
2023-01-25 16:47:25 +00:00
|
|
|
AuUInt uLength { qwLength };
|
|
|
|
if (!pFile->Read(Memory::MemoryViewStreamWrite { writeView, uLength }))
|
2021-10-02 16:07:33 +00:00
|
|
|
{
|
|
|
|
SysPushErrorIO();
|
2022-04-06 01:24:38 +00:00
|
|
|
return false;
|
2021-10-02 16:07:33 +00:00
|
|
|
}
|
2023-09-18 06:11:34 +00:00
|
|
|
|
2022-04-06 01:24:38 +00:00
|
|
|
// NOTE: File devices love to lie
|
|
|
|
// Do not entertain an arbitrarily large page length provided by non-regular fds
|
2021-10-02 16:07:33 +00:00
|
|
|
|
2023-01-25 16:47:25 +00:00
|
|
|
buffer.writePtr += uLength;
|
|
|
|
if (bIsZero)
|
2022-12-16 00:41:01 +00:00
|
|
|
{
|
2023-01-25 16:47:25 +00:00
|
|
|
AuTryDownsize(buffer, uLength);
|
2022-12-16 00:41:01 +00:00
|
|
|
}
|
|
|
|
|
2022-09-19 05:42:39 +00:00
|
|
|
return true;
|
2021-06-27 21:25:29 +00:00
|
|
|
}
|
|
|
|
|
2024-05-07 13:41:16 +00:00
|
|
|
static bool UnixExists(const AuString &path, bool dir)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
|
|
|
struct stat s;
|
2023-01-26 21:43:19 +00:00
|
|
|
int err = ::stat(path.c_str(), &s);
|
2021-06-27 21:25:29 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
AUKN_SYM bool FileExists(const AuROString &path)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2023-09-16 00:13:41 +00:00
|
|
|
if (path.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto pathExpanded = NormalizePathRet(path);
|
|
|
|
if (pathExpanded.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorMemory();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return UnixExists(pathExpanded, false);
|
2021-06-27 21:25:29 +00:00
|
|
|
}
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
AUKN_SYM bool DirExists(const AuROString &path)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2023-09-16 00:13:41 +00:00
|
|
|
if (path.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto pathExpanded = NormalizePathRet(path);
|
|
|
|
if (pathExpanded.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorMemory();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return UnixExists(pathExpanded, true);
|
2021-06-27 21:25:29 +00:00
|
|
|
}
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
AUKN_SYM bool DirMk(const AuROString &path)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2023-09-16 00:13:41 +00:00
|
|
|
if (path.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto pathExpanded = NormalizePathRet(path);
|
|
|
|
if (pathExpanded.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorMemory();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CreateDirectories(pathExpanded, false);
|
2021-06-27 21:25:29 +00:00
|
|
|
}
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
AUKN_SYM bool Remove(const AuROString &path)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2023-09-20 19:17:27 +00:00
|
|
|
struct stat s;
|
|
|
|
|
2023-09-16 00:13:41 +00:00
|
|
|
if (path.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto pathExpanded = NormalizePathRet(path);
|
|
|
|
if (pathExpanded.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorMemory();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-12-30 19:38:58 +00:00
|
|
|
Aurora::RuntimeWaitForSecondaryTick();
|
|
|
|
|
2023-09-20 19:17:27 +00:00
|
|
|
if (::stat(pathExpanded.c_str(), &s) == -1)
|
|
|
|
{
|
|
|
|
if (errno == ENOENT)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (S_ISDIR(s.st_mode))
|
|
|
|
{
|
|
|
|
if (::rmdir(pathExpanded.c_str()) == 0)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2024-03-13 10:08:38 +00:00
|
|
|
else if (errno == ENOTDIR)
|
|
|
|
{
|
2024-03-27 03:12:49 +00:00
|
|
|
if (PosixUnlink(pathExpanded.c_str()) == 0)
|
2024-03-13 10:08:38 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2023-09-20 19:17:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (S_ISREG(s.st_mode))
|
|
|
|
{
|
2024-03-27 03:12:49 +00:00
|
|
|
if (PosixUnlink(pathExpanded.c_str()) == 0)
|
2023-09-20 19:17:27 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (::remove(pathExpanded.c_str()) == 0)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2021-06-27 21:25:29 +00:00
|
|
|
}
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
AUKN_SYM bool Relink(const AuROString &src, const AuROString &dest)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2023-09-16 00:13:41 +00:00
|
|
|
if (src.empty() ||
|
|
|
|
dest.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-12-30 19:38:58 +00:00
|
|
|
Aurora::RuntimeWaitForSecondaryTick();
|
|
|
|
|
2023-09-16 00:13:41 +00:00
|
|
|
auto pathSrcExpanded = NormalizePathRet(src);
|
|
|
|
auto pathExpanded = NormalizePathRet(dest);
|
|
|
|
|
|
|
|
if (pathSrcExpanded.empty() ||
|
|
|
|
pathExpanded.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorMemory();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ::rename(pathSrcExpanded.c_str(), pathExpanded.c_str()) != -1;
|
2021-06-27 21:25:29 +00:00
|
|
|
}
|
|
|
|
|
2021-09-06 10:58:08 +00:00
|
|
|
#if defined(AURORA_IS_LINUX_DERIVED)
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
AUKN_SYM bool Copy(const AuROString &src, const AuROString &dest)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2023-09-16 00:13:41 +00:00
|
|
|
if (src.empty() ||
|
|
|
|
dest.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto pathSrcExpanded = NormalizePathRet(src);
|
|
|
|
auto pathExpanded = NormalizePathRet(dest);
|
|
|
|
|
|
|
|
if (pathSrcExpanded.empty() ||
|
|
|
|
pathExpanded.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorMemory();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CreateDirectories(pathExpanded, true);
|
2021-06-27 21:25:29 +00:00
|
|
|
|
|
|
|
int input, output;
|
|
|
|
|
2023-09-16 00:13:41 +00:00
|
|
|
if ((input = ::open(pathSrcExpanded.c_str(), O_RDONLY | O_CLOEXEC)) == -1)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct stat fileinfo = { 0 };
|
2023-02-04 19:43:01 +00:00
|
|
|
if (::fstat(input, &fileinfo) != 0)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2023-02-04 19:43:01 +00:00
|
|
|
::close(input);
|
2023-09-16 00:13:41 +00:00
|
|
|
SysPushErrorIO("fstat failed");
|
2021-06-27 21:25:29 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-09-16 00:13:41 +00:00
|
|
|
if ((output = ::creat(pathExpanded.c_str(), fileinfo.st_mode)) == -1)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2023-02-04 19:43:01 +00:00
|
|
|
::close(input);
|
2023-09-16 00:13:41 +00:00
|
|
|
SysPushErrorIO("creat failed");
|
2021-06-27 21:25:29 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
off_t bytesCopied = 0;
|
2023-02-04 19:43:01 +00:00
|
|
|
auto result = ::sendfile(output, input, &bytesCopied, fileinfo.st_size) != -1;
|
2021-06-27 21:25:29 +00:00
|
|
|
|
2023-02-04 19:43:01 +00:00
|
|
|
::close(input);
|
|
|
|
::close(output);
|
2023-09-16 00:13:41 +00:00
|
|
|
return bytesCopied == fileinfo.st_size;
|
2021-06-27 21:25:29 +00:00
|
|
|
}
|
2021-09-06 10:58:08 +00:00
|
|
|
|
|
|
|
#elif defined(AURORA_IS_BSD_DERIVED)
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
AUKN_SYM bool Copy(const AuROString &src, const AuROString &dest)
|
2021-09-06 10:58:08 +00:00
|
|
|
{
|
2023-09-16 00:13:41 +00:00
|
|
|
if (src.empty() ||
|
|
|
|
dest.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto pathSrcExpanded = NormalizePathRet(src);
|
|
|
|
auto pathExpanded = NormalizePathRet(dest);
|
|
|
|
|
|
|
|
if (pathSrcExpanded.empty() ||
|
|
|
|
pathExpanded.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorMemory();
|
|
|
|
return false;
|
|
|
|
}
|
2021-09-06 10:58:08 +00:00
|
|
|
|
2023-09-16 00:13:41 +00:00
|
|
|
CreateDirectories(pathExpanded, true);
|
2023-01-23 21:18:58 +00:00
|
|
|
|
2021-09-06 10:58:08 +00:00
|
|
|
int input, output;
|
|
|
|
|
2023-09-16 00:13:41 +00:00
|
|
|
if ((input = ::open(pathSrcExpanded.c_str(), O_RDONLY | O_CLOEXEC)) == -1)
|
2021-09-06 10:58:08 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct stat fileinfo = { 0 };
|
2022-12-17 15:37:46 +00:00
|
|
|
if (::fstat(input, &fileinfo) != 0)
|
2021-09-06 10:58:08 +00:00
|
|
|
{
|
|
|
|
close(input)
|
2023-09-16 00:13:41 +00:00
|
|
|
SysPushErrorIO("fstat failed");
|
2021-09-06 10:58:08 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-06-27 21:25:29 +00:00
|
|
|
|
2023-09-16 00:13:41 +00:00
|
|
|
if ((output = ::creat(pathExpanded.c_str(), fileinfo.st_mode)) == -1)
|
2021-09-06 10:58:08 +00:00
|
|
|
{
|
|
|
|
close(input);
|
2023-09-16 00:13:41 +00:00
|
|
|
SysPushErrorIO("creat failed");
|
2021-09-06 10:58:08 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-12-17 15:37:46 +00:00
|
|
|
auto result = ::fcopyfile(input, output, 0, COPYFILE_ALL) == 0;
|
2021-09-06 10:58:08 +00:00
|
|
|
|
2022-12-17 15:37:46 +00:00
|
|
|
::close(input);
|
|
|
|
::close(output);
|
2021-09-06 10:58:08 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
AUKN_SYM bool Copy(const AuROString &src, const AuROString &dest)
|
2021-09-06 10:58:08 +00:00
|
|
|
{
|
|
|
|
// TODO: not that i care
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2024-04-19 03:33:57 +00:00
|
|
|
AUKN_SYM bool StatFile(const AuROString &pathRel, Stat &stat)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
|
|
|
stat = {};
|
2023-09-16 00:13:41 +00:00
|
|
|
|
|
|
|
if (pathRel.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-06-27 21:25:29 +00:00
|
|
|
auto path = NormalizePathRet(pathRel);
|
2023-09-16 00:13:41 +00:00
|
|
|
if (path.empty())
|
|
|
|
{
|
|
|
|
SysPushErrorMemory();
|
|
|
|
return false;
|
|
|
|
}
|
2021-06-27 21:25:29 +00:00
|
|
|
|
|
|
|
struct stat s;
|
2021-10-02 16:07:33 +00:00
|
|
|
auto err = ::stat(path.c_str(), &s);
|
2021-06-27 21:25:29 +00:00
|
|
|
|
|
|
|
if (err == -1)
|
|
|
|
{
|
2021-11-07 22:32:54 +00:00
|
|
|
if (ENOENT != errno)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2023-02-04 19:43:01 +00:00
|
|
|
SysPushErrorIO("Critical IO error while stating file (errno: {}, path: {})", errno, path);
|
2021-06-27 21:25:29 +00:00
|
|
|
}
|
2024-07-04 17:50:33 +00:00
|
|
|
|
2021-06-27 21:25:29 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-08-21 02:53:50 +00:00
|
|
|
stat.bExistsFile = S_ISREG(s.st_mode);
|
|
|
|
stat.bExistsDirectory = S_ISDIR(s.st_mode);
|
2024-07-04 17:50:33 +00:00
|
|
|
stat.bExistsSystemResource = S_ISSOCK(s.st_mode) || S_ISBLK(s.st_mode) || S_ISCHR(s.st_mode);
|
2022-08-21 02:53:50 +00:00
|
|
|
stat.bExists = stat.bExistsFile || stat.bExistsDirectory || stat.bExistsSystemResource;
|
2021-06-27 21:25:29 +00:00
|
|
|
|
2024-07-04 17:50:33 +00:00
|
|
|
#if 0
|
2022-08-21 02:53:50 +00:00
|
|
|
if (!stat.bExists)
|
2021-06-27 21:25:29 +00:00
|
|
|
{
|
2023-02-04 19:43:01 +00:00
|
|
|
SysPushErrorIO("Missing attribute type in stat mode {} (of path {})", s.st_mode, path);
|
2021-06-27 21:25:29 +00:00
|
|
|
return false;
|
|
|
|
}
|
2024-07-04 17:50:33 +00:00
|
|
|
#else
|
|
|
|
stat.bExists = true;
|
|
|
|
#endif
|
2021-06-27 21:25:29 +00:00
|
|
|
|
2022-08-21 02:53:50 +00:00
|
|
|
stat.uSize = s.st_size;
|
2021-06-27 21:25:29 +00:00
|
|
|
|
2023-05-03 07:57:21 +00:00
|
|
|
|
|
|
|
#if defined(AURORA_IS_LINUX_DERIVED)
|
2023-08-15 08:00:48 +00:00
|
|
|
stat.createdNs = AuUInt64(s.st_ctim.tv_nsec) + AuMSToNS<AuUInt64>(Time::CTimeToMS(s.st_ctim.tv_sec));
|
|
|
|
stat.modifiedNs = AuUInt64(s.st_mtim.tv_nsec) + AuMSToNS<AuUInt64>(Time::CTimeToMS(s.st_mtim.tv_sec));
|
|
|
|
stat.accessedNs = AuUInt64(s.st_atim.tv_nsec) + AuMSToNS<AuUInt64>(Time::CTimeToMS(s.st_atim.tv_sec));
|
2023-05-03 07:57:21 +00:00
|
|
|
#else
|
|
|
|
stat.createdNs = AuMSToNS<AuUInt64>(Time::CTimeToMS(s.st_ctime));
|
|
|
|
stat.modifiedNs = AuMSToNS<AuUInt64>(Time::CTimeToMS(s.st_mtime));
|
|
|
|
stat.accessedNs = AuMSToNS<AuUInt64>(Time::CTimeToMS(s.st_atime));
|
|
|
|
#endif
|
2021-06-27 21:25:29 +00:00
|
|
|
|
|
|
|
err = lstat(path.c_str(), &s);
|
|
|
|
if (err != -1)
|
|
|
|
{
|
2022-08-21 02:53:50 +00:00
|
|
|
stat.bSymLink = S_ISLNK(s.st_mode);
|
2021-06-27 21:25:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2021-10-02 16:07:33 +00:00
|
|
|
#endif
|