[*] (Mostly Linux) Harden FS apis

This commit is contained in:
Reece Wilson 2023-09-16 01:13:41 +01:00 committed by Jamie Reece Wilson
parent 9bfd895fc4
commit 9a294cf955
3 changed files with 304 additions and 73 deletions

View File

@ -87,6 +87,12 @@ namespace Aurora::IO::FS
AUKN_SYM AuSPtr<IReadDir> ReadDir(const AuString &string) AUKN_SYM AuSPtr<IReadDir> ReadDir(const AuString &string)
{ {
if (string.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return {};
}
auto pObj = AuMakeShared<ReadDirStructure>(); auto pObj = AuMakeShared<ReadDirStructure>();
if (!pObj) if (!pObj)
{ {
@ -143,12 +149,6 @@ namespace Aurora::IO::FS
AUKN_SYM bool DirsInDirectory(const AuString &string, AuList<AuString> &dirs) AUKN_SYM bool DirsInDirectory(const AuString &string, AuList<AuString> &dirs)
{ {
if (string.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
auto itr = ReadDir(string); auto itr = ReadDir(string);
if (!itr) if (!itr)
{ {
@ -346,14 +346,33 @@ namespace Aurora::IO::FS
return false; return false;
} }
DWORD dwAttrib = ::GetFileAttributesW(translatedPath.c_str());
if (dwAttrib == INVALID_FILE_ATTRIBUTES)
{
SysPushErrorIO("Couldn't open file ({}) for removal", path);
return false;
}
if (dwAttrib & FILE_ATTRIBUTE_READONLY)
{
SysPushErrorIO("Couldn't open file ({}) for removal (read-only)", path);
return false;
}
if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
{
if (::RemoveDirectoryW(translatedPath.c_str()))
{
return true;
}
}
else
{
if (::DeleteFileW(translatedPath.c_str())) if (::DeleteFileW(translatedPath.c_str()))
{ {
return true; return true;
} }
if (::RemoveDirectoryW(translatedPath.c_str()))
{
return true;
} }
return false; return false;
@ -369,12 +388,28 @@ namespace Aurora::IO::FS
{ {
try try
{ {
auto srcPathNormalized = NormalizePathRet(src); if (src.empty() ||
auto destPathNormalized = NormalizePathRet(dest); dest.empty())
CreateDirectories(destPathNormalized, true); {
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
return ::MoveFileW(Locale::ConvertFromUTF8(srcPathNormalized).c_str(), auto pathSrcExpanded = Locale::ConvertFromUTF8(NormalizePathRet(src));
Locale::ConvertFromUTF8(destPathNormalized).c_str()); auto pathExpandedA = NormalizePathRet(dest);
auto pathExpanded = Locale::ConvertFromUTF8(pathExpandedA);
if (pathSrcExpanded.empty() ||
pathExpanded.empty())
{
SysPushErrorMemory();
return false;
}
CreateDirectories(pathExpandedA, true);
return ::MoveFileW(pathSrcExpanded.c_str(),
pathExpanded.c_str());
} }
catch (...) catch (...)
{ {
@ -386,12 +421,28 @@ namespace Aurora::IO::FS
{ {
try try
{ {
auto srcPathNormalized = NormalizePathRet(src); if (src.empty() ||
auto destPathNormalized = NormalizePathRet(dest); dest.empty())
CreateDirectories(destPathNormalized, true); {
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
return ::CopyFileW(Locale::ConvertFromUTF8(srcPathNormalized).c_str(), auto pathSrcExpanded = Locale::ConvertFromUTF8(NormalizePathRet(src));
Locale::ConvertFromUTF8(destPathNormalized).c_str(), true); auto pathExpandedA = NormalizePathRet(dest);
auto pathExpanded = Locale::ConvertFromUTF8(pathExpandedA);
if (pathSrcExpanded.empty() ||
pathExpanded.empty())
{
SysPushErrorMemory();
return false;
}
CreateDirectories(pathExpandedA, true);
return ::CopyFileW(pathSrcExpanded.c_str(),
pathExpanded.c_str(), true);
} }
catch (...) catch (...)
{ {
@ -406,7 +457,20 @@ namespace Aurora::IO::FS
stat = {}; stat = {};
if (!::GetFileAttributesExW(Locale::ConvertFromUTF8(NormalizePathRet(path)).c_str(), GetFileExInfoStandard, &data)) if (path.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
auto translatedPath = Locale::ConvertFromUTF8(NormalizePathRet(path));
if (translatedPath.empty())
{
SysPushErrorMemory();
return false;
}
if (!::GetFileAttributesExW(translatedPath.c_str(), GetFileExInfoStandard, &data))
{ {
stat.bExists = false; stat.bExists = false;
return false; return false;

View File

@ -126,6 +126,12 @@ namespace Aurora::IO::FS
static AuSPtr<IReadDir> ReadDirEx(const AuString &string, bool bFast) static AuSPtr<IReadDir> ReadDirEx(const AuString &string, bool bFast)
{ {
if (string.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return {};
}
auto pObj = AuMakeShared<ReadDirStructure>(); auto pObj = AuMakeShared<ReadDirStructure>();
if (!pObj) if (!pObj)
{ {
@ -216,6 +222,12 @@ namespace Aurora::IO::FS
{ {
AuMemoryViewWrite writeView; AuMemoryViewWrite writeView;
if (path.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
bool bIsStupidFD = bool bIsStupidFD =
AuStartsWith(path, "/proc/") || AuStartsWith(path, "/proc/") ||
AuStartsWith(path, "/sys/") || AuStartsWith(path, "/sys/") ||
@ -281,41 +293,124 @@ namespace Aurora::IO::FS
AUKN_SYM bool FileExists(const AuString &path) AUKN_SYM bool FileExists(const AuString &path)
{ {
return UnixExists(NormalizePathRet(path), false); 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);
} }
AUKN_SYM bool DirExists(const AuString &path) AUKN_SYM bool DirExists(const AuString &path)
{ {
return UnixExists(NormalizePathRet(path), true); 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);
} }
AUKN_SYM bool DirMk(const AuString &path) AUKN_SYM bool DirMk(const AuString &path)
{ {
return CreateDirectories(NormalizePathRet(path), false); 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);
} }
AUKN_SYM bool Remove(const AuString &path) AUKN_SYM bool Remove(const AuString &path)
{ {
return remove(NormalizePathRet(path).c_str()) != -1; 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 remove(pathExpanded.c_str()) != -1;
} }
AUKN_SYM bool Relink(const AuString &src, const AuString &dest) AUKN_SYM bool Relink(const AuString &src, const AuString &dest)
{ {
auto normalizedDestPath = NormalizePathRet(dest); if (src.empty() ||
CreateDirectories(normalizedDestPath, true); dest.empty())
return ::rename(NormalizePathRet(src).c_str(), normalizedDestPath.c_str()) != -1; {
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;
}
return ::rename(pathSrcExpanded.c_str(), pathExpanded.c_str()) != -1;
} }
#if defined(AURORA_IS_LINUX_DERIVED) #if defined(AURORA_IS_LINUX_DERIVED)
AUKN_SYM bool Copy(const AuString &src, const AuString &dest) AUKN_SYM bool Copy(const AuString &src, const AuString &dest)
{ {
auto normalizedSrcPath = NormalizePathRet(src); if (src.empty() ||
auto normalizedDestPath = NormalizePathRet(dest); 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);
int input, output; int input, output;
if ((input = ::open(normalizedSrcPath.c_str(), O_RDONLY | O_CLOEXEC)) == -1) if ((input = ::open(pathSrcExpanded.c_str(), O_RDONLY | O_CLOEXEC)) == -1)
{ {
return false; return false;
} }
@ -324,12 +419,14 @@ namespace Aurora::IO::FS
if (::fstat(input, &fileinfo) != 0) if (::fstat(input, &fileinfo) != 0)
{ {
::close(input); ::close(input);
SysPushErrorIO("fstat failed");
return false; return false;
} }
if ((output = ::creat(normalizedDestPath.c_str(), fileinfo.st_mode)) == -1) if ((output = ::creat(pathExpanded.c_str(), fileinfo.st_mode)) == -1)
{ {
::close(input); ::close(input);
SysPushErrorIO("creat failed");
return false; return false;
} }
@ -338,21 +435,35 @@ namespace Aurora::IO::FS
::close(input); ::close(input);
::close(output); ::close(output);
return result; return bytesCopied == fileinfo.st_size;
} }
#elif defined(AURORA_IS_BSD_DERIVED) #elif defined(AURORA_IS_BSD_DERIVED)
AUKN_SYM bool Copy(const AuString &src, const AuString &dest) AUKN_SYM bool Copy(const AuString &src, const AuString &dest)
{ {
auto normalizedSrcPath = NormalizePathRet(src); if (src.empty() ||
auto normalizedDestPath = NormalizePathRet(dest); dest.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
CreateDirectories(normalizedDestPath, true); auto pathSrcExpanded = NormalizePathRet(src);
auto pathExpanded = NormalizePathRet(dest);
if (pathSrcExpanded.empty() ||
pathExpanded.empty())
{
SysPushErrorMemory();
return false;
}
CreateDirectories(pathExpanded, true);
int input, output; int input, output;
if ((input = ::open(normalizedSrcPath.c_str(), O_RDONLY | O_CLOEXEC)) == -1) if ((input = ::open(pathSrcExpanded.c_str(), O_RDONLY | O_CLOEXEC)) == -1)
{ {
return false; return false;
} }
@ -361,12 +472,14 @@ namespace Aurora::IO::FS
if (::fstat(input, &fileinfo) != 0) if (::fstat(input, &fileinfo) != 0)
{ {
close(input) close(input)
SysPushErrorIO("fstat failed");
return false; return false;
} }
if ((output = ::creat(normalizedDestPath.c_str(), fileinfo.st_mode)) == -1) if ((output = ::creat(pathExpanded.c_str(), fileinfo.st_mode)) == -1)
{ {
close(input); close(input);
SysPushErrorIO("creat failed");
return false; return false;
} }
@ -390,7 +503,19 @@ namespace Aurora::IO::FS
AUKN_SYM bool StatFile(const AuString &pathRel, Stat &stat) AUKN_SYM bool StatFile(const AuString &pathRel, Stat &stat)
{ {
stat = {}; stat = {};
if (pathRel.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
auto path = NormalizePathRet(pathRel); auto path = NormalizePathRet(pathRel);
if (path.empty())
{
SysPushErrorMemory();
return false;
}
struct stat s; struct stat s;
auto err = ::stat(path.c_str(), &s); auto err = ::stat(path.c_str(), &s);

View File

@ -137,22 +137,29 @@ namespace Aurora::IO::FS
{ {
AU_LOCK_GUARD(this->spinlock_); AU_LOCK_GUARD(this->spinlock_);
auto iOptSafe = this->pHandle_->GetOSWriteHandleSafe(); int fd {};
if (!iOptSafe) if (auto pHandle = this->pHandle_)
{ {
iOptSafe = this->pHandle_->GetOSReadHandleSafe(); if (!pHandle->IsFile())
{
SysPushErrorIOResourceRejected();
return 0;
} }
if (!iOptSafe) if (auto opt = pHandle->GetOSHandleSafe())
{ {
return false; fd = opt.Value();
} }
else
auto fd = (int)iOptSafe.Value(); {
if (fd == -1) SysPushErrorInvalidFd();
return 0;
}
}
else
{ {
SysPushErrorUninitialized(); SysPushErrorUninitialized();
return false; return 0;
} }
return PosixGetOffset(fd); return PosixGetOffset(fd);
@ -162,19 +169,26 @@ namespace Aurora::IO::FS
{ {
AU_LOCK_GUARD(this->spinlock_); AU_LOCK_GUARD(this->spinlock_);
auto iOptSafe = this->pHandle_->GetOSWriteHandleSafe(); int fd {};
if (!iOptSafe) if (auto pHandle = this->pHandle_)
{ {
iOptSafe = this->pHandle_->GetOSReadHandleSafe(); if (!pHandle->IsFile())
{
SysPushErrorIOResourceRejected();
return 0;
} }
if (!iOptSafe) if (auto opt = pHandle->GetOSHandleSafe())
{ {
fd = opt.Value();
}
else
{
SysPushErrorInvalidFd();
return false; return false;
} }
}
auto fd = (int)iOptSafe.Value(); else
if (fd == -1)
{ {
SysPushErrorUninitialized(); SysPushErrorUninitialized();
return false; return false;
@ -187,17 +201,33 @@ namespace Aurora::IO::FS
{ {
AU_LOCK_GUARD(this->spinlock_); AU_LOCK_GUARD(this->spinlock_);
auto iOptSafe = this->pHandle_->GetOSReadHandleSafe(); int fd {};
if (!iOptSafe) if (auto pHandle = this->pHandle_)
{ {
return false; if (!pHandle->IsFile())
{
// Intentionally suppressed (for the moment)
// I don't want to fill error stacks up under paths that can handle char-devs by reading until EoS
// If we do, we could hit an always-throw on AuErrorStack-catch
// Some users are completely unaware they can access/test the underlying handle
// Returning zero will do
return 0;
} }
auto fd = (int)iOptSafe.Value(); if (auto opt = pHandle->GetOSHandleSafe())
if (fd == -1) {
fd = opt.Value();
}
else
{
SysPushErrorInvalidFd();
return 0;
}
}
else
{ {
SysPushErrorUninitialized(); SysPushErrorUninitialized();
return false; return 0;
} }
return PosixGetLength(fd); return PosixGetLength(fd);
@ -259,7 +289,7 @@ namespace Aurora::IO::FS
return 0; return 0;
} }
int fd = this->GetUnixHandle(); int fd = this->pHandle_->GetOSWriteHandleSafe().ValueOr((AuUInt)-1);
if (fd == -1) if (fd == -1)
{ {
SysPushErrorUninitialized(); SysPushErrorUninitialized();
@ -317,25 +347,37 @@ namespace Aurora::IO::FS
void PosixFileStream::Flush() void PosixFileStream::Flush()
{ {
int fd = this->GetUnixHandle(); if (!this->pHandle_)
if (fd == -1)
{ {
SysPushErrorUninitialized(); SysPushErrorUninitialized();
return; return;
} }
int fd = this->pHandle_->GetOSWriteHandleSafe().ValueOr((AuUInt)-1);
if (fd == -1)
{
SysPushErrorIOResourceRejected();
return;
}
::fsync(fd); ::fsync(fd);
} }
void PosixFileStream::WriteEoS() void PosixFileStream::WriteEoS()
{ {
int fd = this->GetUnixHandle(); if (!this->pHandle_)
if (fd == -1)
{ {
SysPushErrorUninitialized(); SysPushErrorUninitialized();
return; return;
} }
int fd = this->pHandle_->GetOSWriteHandleSafe().ValueOr((AuUInt)-1);
if (fd == -1)
{
SysPushErrorIOResourceRejected();
return;
}
::ftruncate(fd, GetOffset()); ::ftruncate(fd, GetOffset());
} }