1623 lines
45 KiB
C++
1623 lines
45 KiB
C++
/***
|
|
Copyright (C) 2023-2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: FSPlatformDevices.Linux.cpp
|
|
Date: 2023-08-11
|
|
Date: 2024-02-XX
|
|
Date: 2024-07-03
|
|
Author: Reece
|
|
***/
|
|
#include <Source/RuntimeInternal.hpp>
|
|
#include "FS.hpp"
|
|
#include "FSPlatformDevices.hpp"
|
|
#include <sys/types.h>
|
|
#include <sys/statvfs.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/sysmacros.h> //major, minor
|
|
#include <unistd.h>
|
|
|
|
namespace Aurora::IO::FS
|
|
{
|
|
static const auto kMagicDeviceSystem = "Linux Kernel";
|
|
static const auto kMagicDeviceTemp = "Linux Temp";
|
|
static const auto kMagicDeviceRam = "Linux Ram Disk";
|
|
static const auto kMagicDeviceCFG = "Linux Configuration";
|
|
static const auto kMagicDeviceVFS = "Linux FUSE";
|
|
|
|
static AuHashMap<AuString, AuOptional<struct stat>> gCachedStat;
|
|
static AuMutex gMutex;
|
|
|
|
bool SpecialsInDirectory(const AuROString &string, AuList<AuString> &dirs);
|
|
|
|
static AuOptional<AuString> ReadLink(const AuString &path)
|
|
{
|
|
AuString ret;
|
|
ret.resize(4096);
|
|
|
|
auto count = readlink(path.c_str(), ret.data(), ret.size());
|
|
if (count <= 0)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (ret[0] != '/')
|
|
{
|
|
AuROString dir;
|
|
AuString path2 = path;
|
|
path2 = AuReplaceAll(path2, "\\", "\\\\");
|
|
if (GoUpToSeparator(dir, path2))
|
|
{
|
|
ret = fmt::format("{}/{}", dir, ret);
|
|
ret = AuReplaceAll(ret, "\\\\", "\\");
|
|
}
|
|
else
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void CachedStatReset()
|
|
{
|
|
AU_LOCK_GUARD(gMutex);
|
|
gCachedStat.clear();
|
|
}
|
|
|
|
// lazy hack to avoid expanding structures and to avoid thousands of additional system calls.
|
|
// it doesnt make that much of a difference. it's an additional MS saved for what amounts
|
|
// to a handful of in thread IPC and intraprocess memcpy. posix and derived systems are
|
|
// going to suck regardless. its not like there some magic database with a nice little
|
|
// pnp or wsock api that'll just dump all the related fields in a handful of API calls.
|
|
static int CachedStat(const AuString &path, struct stat *pstat)
|
|
{
|
|
AU_LOCK_GUARD(gMutex);
|
|
|
|
auto itr = gCachedStat.find(path);
|
|
if (itr == gCachedStat.end())
|
|
{
|
|
int err = ::stat(path.c_str(), pstat);
|
|
if (-1 == err)
|
|
{
|
|
gCachedStat[path] = {};
|
|
errno = 0;
|
|
}
|
|
else
|
|
{
|
|
gCachedStat[path] = *pstat;
|
|
}
|
|
return err;
|
|
}
|
|
else
|
|
{
|
|
auto &that = itr->second;
|
|
if (that)
|
|
{
|
|
*pstat = that.Value();
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool BlockExists(const AuString &path)
|
|
{
|
|
struct stat s;
|
|
int err = CachedStat(path.c_str(), &s);
|
|
if (-1 == err)
|
|
{
|
|
return false;
|
|
}
|
|
return S_ISBLK(s.st_mode);
|
|
}
|
|
|
|
static AuString _DevFromPath(const AuString &path)
|
|
{
|
|
auto pos = path.find_last_of('/');
|
|
if (pos == AuString::npos)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
auto dev = "/dev/" + path.substr(pos + 1);
|
|
if (!BlockExists(dev))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return dev;
|
|
}
|
|
|
|
static AuString GetDevDevPathFromSysDev(const AuString &path)
|
|
{
|
|
if (AuStartsWith(path, "/sys/block/"))
|
|
{
|
|
return _DevFromPath(path);
|
|
}
|
|
else if (AuStartsWith(path, "/sys/dev/block/"))
|
|
{
|
|
if (AuStringContains(path, ".."))
|
|
{
|
|
return _DevFromPath(path);
|
|
}
|
|
|
|
if (auto optLink = ReadLink(path))
|
|
{
|
|
return _DevFromPath(optLink.Value());
|
|
}
|
|
else
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
|
|
static AuPair<AuUInt32, AuUInt32> ReadIDPair(const AuString &path)
|
|
{
|
|
struct stat s;
|
|
if (path.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
auto err = CachedStat(path.c_str(), &s);
|
|
if (err == -1)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (!S_ISBLK(s.st_mode))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return AuMakePair(major(s.st_rdev), minor(s.st_rdev));
|
|
}
|
|
|
|
static AuPair<AuUInt32, AuUInt32> ReadIDPairSym(const AuString &path)
|
|
{
|
|
if (auto optLink = ReadLink(path))
|
|
{
|
|
return ReadIDPair(optLink.Value());
|
|
}
|
|
else
|
|
{
|
|
return ReadIDPair(path);
|
|
}
|
|
}
|
|
|
|
static AuString GetSysDevFromPathLogical(const AuString &path)
|
|
{
|
|
struct stat s;
|
|
auto pathEx = NormalizePathRet(path);
|
|
if (pathEx.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
auto err = CachedStat(pathEx.c_str(), &s);
|
|
if (err == -1)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return AuString(fmt::format("/sys/dev/block/{}:{}", major(s.st_dev), minor(s.st_dev)));
|
|
}
|
|
|
|
static AuString GetSysDevFromDev(const AuString &path)
|
|
{
|
|
struct stat s;
|
|
if (path.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
auto err = CachedStat(path.c_str(), &s);
|
|
if (err == -1)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (!S_ISBLK(s.st_mode))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return AuString(fmt::format("/sys/dev/block/{}:{}", major(s.st_rdev), minor(s.st_rdev)));
|
|
}
|
|
|
|
static AuString MagicPathRd(const AuString &shortLogical)
|
|
{
|
|
if (auto optLink = ReadLink(shortLogical))
|
|
{
|
|
// the symlink should end in <...>/device id/logical mount
|
|
// both of which are valid /dev/xyz and block devices
|
|
// maybe we could open /block/major:0, dunno
|
|
auto &longLogical = optLink.Value();
|
|
|
|
auto pos = longLogical.find_last_of('/');
|
|
if (pos == AuString::npos)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return longLogical.substr(0, pos);
|
|
}
|
|
else
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
|
|
static AuString MagicPath(const AuString &path)
|
|
{
|
|
auto shortLogical = GetSysDevFromPathLogical(path);
|
|
if (shortLogical.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return MagicPathRd(shortLogical);
|
|
}
|
|
|
|
// isnt posshit a nice family of os?
|
|
static AuString GetSysDevFromPathPhysical(const AuString &path)
|
|
{
|
|
if (AuStartsWith(path, "/dev/"))
|
|
{
|
|
AuUInt uCount {};
|
|
for (AU_ITERATE_N(i, path.size()))
|
|
{
|
|
if (path[i] == '/')
|
|
{
|
|
uCount++;
|
|
}
|
|
}
|
|
|
|
if (uCount == 2)
|
|
{
|
|
auto guess = "/sys/block/" + path.substr(5);
|
|
if (AuFS::DirExists(guess))
|
|
{
|
|
return guess;
|
|
}
|
|
else
|
|
{
|
|
return GetSysDevFromDev(path);
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
else if (AuStartsWith(path, "/sys/dev/block/") ||
|
|
AuStartsWith(path, "/sys/block/"))
|
|
{
|
|
// dunno. have fun
|
|
return path;
|
|
}
|
|
else
|
|
{
|
|
return MagicPath(path);
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
static AuString GetDevParent(const AuString &str)
|
|
{
|
|
auto b = GetSysDevFromDev(str);
|
|
auto slavesDir = fmt::format("{}/slaves", b);
|
|
if (AuFS::DirExists(slavesDir))
|
|
{
|
|
AuList<AuString> slaves;
|
|
DirsInDirectory(slavesDir, slaves);
|
|
|
|
if (slaves.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
auto devStr = AuString(fmt::format("/dev/{}", slaves[0]));
|
|
if (!BlockExists(devStr))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return devStr;
|
|
}
|
|
else
|
|
{
|
|
auto parent = MagicPathRd(b);
|
|
|
|
if (parent.empty())
|
|
{
|
|
parent = b;
|
|
}
|
|
|
|
auto devStr = _DevFromPath(parent);
|
|
if (!BlockExists(devStr))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return devStr;
|
|
}
|
|
}
|
|
|
|
static AuOptional<AuString> ReadPosixFile(const AuROString &path)
|
|
{
|
|
AuString file;
|
|
if (!AuFS::FileExists(path))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (!AuFS::ReadString(path, file))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (file.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (file[file.size() - 1] == '\n')
|
|
{
|
|
file.pop_back();
|
|
}
|
|
|
|
return file;
|
|
}
|
|
|
|
static AuOptional<AuUInt> ReadPosixFileWord(const AuROString &path)
|
|
{
|
|
if (auto file = ReadPosixFile(path))
|
|
{
|
|
if (auto val = AuParse::ParseUInt(file.Value()))
|
|
{
|
|
return val.value();
|
|
}
|
|
else
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
|
|
static LogicalOffsetResponse ReadPhysStatsFromPartition(const AuString &path)
|
|
{
|
|
LogicalOffsetResponse ret;
|
|
|
|
auto optStart = ReadPosixFileWord(fmt::format("{}/start", path));
|
|
if (!optStart)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
auto optSize = ReadPosixFileWord(fmt::format("{}/size", path));
|
|
if (!optSize)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
ret.uLogicalSize = AuUInt64(optSize.Value()) * AuUInt64(512);
|
|
ret.uLogicalOffset = AuUInt64(optStart.Value()) * AuUInt64(512);
|
|
return ret;
|
|
}
|
|
|
|
static bool ReadIsROFromPartition(const AuString &path)
|
|
{
|
|
if (auto optFile = ReadPosixFile(fmt::format("{}/ro", path)))
|
|
{
|
|
auto file = optFile.Value();
|
|
if (file.size())
|
|
{
|
|
return file[0] == '1';
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static AuString UnescapeShitString(const AuString &str)
|
|
{
|
|
AuString ret;
|
|
ret.reserve(str.size());
|
|
|
|
for (AU_ITERATE_N(i, str.size()))
|
|
{
|
|
auto c = str[i];
|
|
if (c == '\\' &&
|
|
i + 3 < str.size() &&
|
|
str[i + 1] == 'x')
|
|
{
|
|
AuUInt8 tempC;
|
|
char tempIn[2] { str[i + 2], str[i + 3] };
|
|
AuParse::HexToByte(tempIn, tempC);
|
|
ret.push_back((char)tempC);
|
|
|
|
i += 3;
|
|
}
|
|
else
|
|
{
|
|
ret.push_back(str[i]);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static char UnescapeOct(const char *pThat)
|
|
{
|
|
int octalNumber = AuParse::ParseUInt(AuROString(pThat, pThat + 3)).value();
|
|
int decimalNumber = 0, i = 0;
|
|
|
|
while (octalNumber != 0)
|
|
{
|
|
decimalNumber += (octalNumber % 10) * std::pow(8, i);
|
|
++i;
|
|
octalNumber /= 10;
|
|
}
|
|
|
|
i = 1;
|
|
return (char)decimalNumber;
|
|
}
|
|
|
|
// Yes, this family of poshix freetardation requires two different unescape functions.
|
|
static AuString UnescapeOtherShitString(const AuROString &str)
|
|
{
|
|
AuString ret;
|
|
ret.reserve(str.size());
|
|
|
|
for (AU_ITERATE_N(i, str.size()))
|
|
{
|
|
auto c = str[i];
|
|
if (c == '\\')
|
|
{
|
|
if (i + 3 >= str.size())
|
|
{
|
|
return ret;
|
|
}
|
|
ret.push_back(UnescapeOct(str.data() + i + 1));
|
|
i += 3;
|
|
}
|
|
else
|
|
{
|
|
ret.push_back(str[i]);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uuids::uuid UUIDFromPersistentString(const AuROString &toHash)
|
|
{
|
|
AuArray<AuUInt8, 16> hash;
|
|
AuHashing::MD4(toHash, hash);
|
|
hash[8] &= 0xBF;
|
|
hash[8] |= 0x80;
|
|
hash[6] &= 0x4F;
|
|
hash[6] |= 0x40;
|
|
return uuids::uuid(hash.begin(), hash.end());
|
|
}
|
|
|
|
AUKN_SYM AuResult<AuString> GetDeviceFromPath(const AuString &path)
|
|
{
|
|
try
|
|
{
|
|
auto pathEx = NormalizePathRet(path);
|
|
if (pathEx.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
auto pathDev = GetSysDevFromPathPhysical(pathEx);
|
|
if (pathDev.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
auto devPath = GetDevDevPathFromSysDev(pathDev);
|
|
if (devPath.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return devPath;
|
|
}
|
|
catch (...)
|
|
{
|
|
SysPushErrorCatch();
|
|
return {};
|
|
}
|
|
}
|
|
|
|
AUKN_SYM AuResult<AuString> GetLogicalMountFromPath(const AuString &fileOrDirPath)
|
|
{
|
|
try
|
|
{
|
|
auto pathDev = GetSysDevFromPathLogical(fileOrDirPath);
|
|
if (pathDev.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
auto devPath = GetDevDevPathFromSysDev(pathDev);
|
|
if (devPath.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return devPath;
|
|
}
|
|
catch (...)
|
|
{
|
|
SysPushErrorCatch();
|
|
return {};
|
|
}
|
|
}
|
|
|
|
AUKN_SYM AuResult<AuString> GetDeviceFromRoot(const AuString &root)
|
|
{
|
|
try
|
|
{
|
|
return GetDeviceFromPath(root);
|
|
}
|
|
catch (...)
|
|
{
|
|
SysPushErrorCatch();
|
|
return {};
|
|
}
|
|
}
|
|
|
|
AUKN_SYM AuUInt32 GetLogicalSectorSizeFromPath(const AuString &path)
|
|
{
|
|
struct statvfs data;
|
|
|
|
auto pathEx = NormalizePathRet(path);
|
|
if (pathEx.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if ((statvfs(pathEx.c_str(), &data)) < 0)
|
|
{
|
|
SysPushErrorIO();
|
|
return {};
|
|
}
|
|
|
|
return data.f_bsize;
|
|
}
|
|
|
|
AUKN_SYM AuUInt32 GetPhysicalSectorSizeFromPath(const AuString &path)
|
|
{
|
|
try
|
|
{
|
|
auto pathEx = NormalizePathRet(path);
|
|
if (pathEx.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
AuString mainPath = GetSysDevFromPathPhysical(pathEx);
|
|
if (mainPath.empty())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (auto val = ReadPosixFileWord(mainPath + "/queue/physical_block_size"))
|
|
{
|
|
return val.value();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
catch (...)
|
|
{
|
|
SysPushErrorCatch();
|
|
return {};
|
|
}
|
|
}
|
|
|
|
AUKN_SYM LogicalUsedResponse GetLogicalUsedFromLogicalDevice(const AuString &logicalMountPath)
|
|
{
|
|
try
|
|
{
|
|
auto devices = AuFS::GetFSDevices();
|
|
|
|
for (const auto &device : devices)
|
|
{
|
|
for (const auto &partition : device.partitions)
|
|
{
|
|
if (partition.logicalMount == logicalMountPath)
|
|
{
|
|
if (!device.uFSDeviceSizeInBytes)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (partition.filesystemMountPoints.size())
|
|
{
|
|
return GetLogicalUsedFromPath(partition.filesystemMountPoints[0]);
|
|
}
|
|
else
|
|
{
|
|
// eh
|
|
return GetLogicalUsedFromPath(logicalMountPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
catch (...)
|
|
{
|
|
SysPushErrorCatch();
|
|
return {};
|
|
}
|
|
}
|
|
|
|
AUKN_SYM AuUInt64 GetDeviceSizeInBytes(const AuString &physicalDevicePath)
|
|
{
|
|
try
|
|
{
|
|
if (AuStartsWith(physicalDevicePath, "/sys/block/") ||
|
|
AuStartsWith(physicalDevicePath, "/sys/dev/block/"))
|
|
{
|
|
auto optSize = ReadPosixFileWord(fmt::format("{}/size", physicalDevicePath));
|
|
if (!optSize)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return optSize.ValueOr(0) * 512;
|
|
}
|
|
else
|
|
{
|
|
auto path = GetSysDevFromPathPhysical(physicalDevicePath);
|
|
if (path.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
auto optSize = ReadPosixFileWord(fmt::format("{}/size", path));
|
|
if (!optSize)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return optSize.ValueOr(0) * 512;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
SysPushErrorCatch();
|
|
return {};
|
|
}
|
|
}
|
|
|
|
AUKN_SYM AuUInt32 GetPerformanceBufferSizeFromPath(const AuString &path)
|
|
{
|
|
try
|
|
{
|
|
auto pathEx = NormalizePathRet(path);
|
|
if (pathEx.empty())
|
|
{
|
|
return 4096;
|
|
}
|
|
|
|
auto pathDev = GetSysDevFromPathPhysical(pathEx);
|
|
if (path.empty())
|
|
{
|
|
return 4096;
|
|
}
|
|
|
|
auto optSize = ReadPosixFileWord(fmt::format("{}/optimal_io_size", pathDev));
|
|
if (!optSize)
|
|
{
|
|
return 4096;
|
|
}
|
|
|
|
return optSize.Value();
|
|
}
|
|
catch (...)
|
|
{
|
|
SysPushErrorCatch();
|
|
return {};
|
|
}
|
|
}
|
|
|
|
AUKN_SYM LogicalUsedResponse GetLogicalUsedFromPath(const AuString &path)
|
|
{
|
|
struct statvfs data;
|
|
|
|
auto pathEx = NormalizePathRet(path);
|
|
if (pathEx.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if ((statvfs(pathEx.c_str(), &data)) < 0)
|
|
{
|
|
SysPushErrorIO();
|
|
return {};
|
|
}
|
|
|
|
LogicalUsedResponse used;
|
|
used.uLogicalSize = AuUInt64(data.f_blocks) * AuUInt64(data.f_bsize);
|
|
//used.uLogicalUsed = used.uLogicalSize - (AuUInt64(data.f_bfree) * AuUInt64(data.f_bsize));
|
|
used.uLogicalUsed = used.uLogicalSize - (AuUInt64(data.f_bavail) * AuUInt64(data.f_bsize));
|
|
return used;
|
|
}
|
|
|
|
// dumbshit freetards have conflated partitions with block devices
|
|
// everything here is a block device.
|
|
// linux and posix ecosystems are such a god damn joke. year of linux when?
|
|
// step 1: gather names of block devices to inspect
|
|
static AuList<AuString> GatherRelevantBlockDevices()
|
|
{
|
|
AuList<AuString> ret;
|
|
AuString str;
|
|
if (!AuFS::ReadString("/proc/partitions", str))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
bool bFirstLine {};
|
|
AuParse::SplitNewlines(str, [&](const AuROString &str)
|
|
{
|
|
if (!AuExchange(bFirstLine, true))
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
AuString string(str);
|
|
AuReplaceAll(AuReplaceAll(string, "\t", " "), " ", " ");
|
|
auto vec = AuSplitString(string, " ");
|
|
if (vec.size() >= 4)
|
|
{
|
|
ret.push_back(AuString(vec[3]));
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
SysPushErrorCatch();
|
|
}
|
|
});
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct ParsedMount
|
|
{
|
|
AuString fsPath;
|
|
AuString logicalMountPoint;
|
|
AuString logicalMountPointTrashOS;
|
|
AuString filesystemType;
|
|
AuString muhPosixWayofLifeBrainrotICantBeFuckedToParse;
|
|
AuString physical;
|
|
AuPair<AuUInt32, AuUInt32> idPairPhys;
|
|
};
|
|
|
|
// step two: gather partitions and misc virtual file systems
|
|
static AuList<ParsedMount> GatherActualPartitions()
|
|
{
|
|
AuList<ParsedMount> ret;
|
|
AuString str;
|
|
if (!AuFS::ReadString("/proc/mounts", str))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
AuParse::SplitNewlines(str, [&](const AuROString &str)
|
|
{
|
|
try
|
|
{
|
|
ParsedMount temp;
|
|
AuString string(str);
|
|
AuReplaceAll(AuReplaceAll(string, "\t", " "), " ", " ");
|
|
auto vec = AuSplitString(string, " ");
|
|
if (vec.size() >= 4)
|
|
{
|
|
temp.logicalMountPoint = vec[0];
|
|
temp.fsPath = UnescapeOtherShitString(vec[1]);
|
|
temp.filesystemType = vec[2];
|
|
temp.muhPosixWayofLifeBrainrotICantBeFuckedToParse = vec[3];
|
|
|
|
if (auto optLink = ReadLink(temp.logicalMountPoint))
|
|
{
|
|
auto &link = optLink.Value();
|
|
temp.logicalMountPointTrashOS = NormalizePathRet(link);
|
|
if (temp.logicalMountPointTrashOS.empty())
|
|
{
|
|
temp.logicalMountPointTrashOS = temp.logicalMountPoint;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
temp.logicalMountPointTrashOS = temp.logicalMountPoint;
|
|
}
|
|
|
|
temp.physical = GetSysDevFromPathPhysical(temp.logicalMountPoint);
|
|
temp.idPairPhys = ReadIDPair(temp.physical);
|
|
}
|
|
|
|
// yes?
|
|
// torbalds shit os allows for multiple mountpoints on a single path
|
|
// we shouldn't have duplicate entries, under the name of "systemd-," for a path thats already mounted
|
|
if (temp.filesystemType == "autofs")
|
|
{
|
|
return;
|
|
}
|
|
|
|
ret.push_back(AuMove(temp));
|
|
}
|
|
catch (...)
|
|
{
|
|
SysPushErrorCatch();
|
|
}
|
|
});
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void ScanProcFS(AuList<FSDevice> &devices)
|
|
{
|
|
AuList<AuString> miscDevices;
|
|
AuList<AuString> specialBlockDevices;
|
|
|
|
auto blocks = GatherRelevantBlockDevices();
|
|
auto partitions = GatherActualPartitions();
|
|
|
|
// step 3: create fs devices from non-paritions
|
|
|
|
AuList<AuString> specialConsiderations;
|
|
for (const auto &block : blocks)
|
|
{
|
|
bool bBreak {};
|
|
|
|
auto rootId = ReadIDPair("/dev/" + block);
|
|
|
|
for (const auto &partition : partitions)
|
|
{
|
|
// TODO: go back to string check (maybe. tbd)
|
|
if (ReadIDPair(partition.logicalMountPoint) == rootId)
|
|
{
|
|
bBreak = true;
|
|
}
|
|
|
|
if (bBreak)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bBreak)
|
|
{
|
|
specialConsiderations.push_back(block);
|
|
continue;
|
|
}
|
|
|
|
{
|
|
FSDevice device;
|
|
device.type = EFSDeviceType::eDeviceUnknown;
|
|
device.bus = EFSBusType::eBusGeneric;
|
|
device.devicePath = "/dev/" + block;
|
|
|
|
if (auto read = ReadPosixFile(fmt::format("/sys/block/{}/device/model", block)))
|
|
{
|
|
device.productModel = read.Value();
|
|
}
|
|
|
|
devices.push_back(device);
|
|
}
|
|
}
|
|
|
|
// step 4: add partitionless devices
|
|
for (const auto &block : specialConsiderations)
|
|
{
|
|
auto a = "/dev/" + block;
|
|
auto c = GetDevParent(a);
|
|
if (c.empty())
|
|
{
|
|
c = a;
|
|
}
|
|
|
|
bool bFound {};
|
|
for (auto &dev : devices)
|
|
{
|
|
if (dev.devicePath == c)
|
|
{
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bFound)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
{
|
|
FSDevice device;
|
|
device.type = EFSDeviceType::eDeviceUnknown;
|
|
device.bus = EFSBusType::eBusGeneric;
|
|
device.devicePath = c;
|
|
devices.push_back(device);
|
|
}
|
|
}
|
|
|
|
// step 4: remove suprious unmounted devices
|
|
{
|
|
auto itr = devices.begin();
|
|
|
|
while (itr != devices.end())
|
|
{
|
|
AuString lastPart;
|
|
auto strItr = itr->devicePath.find_last_of('/');
|
|
if (strItr == AuString::npos)
|
|
{
|
|
itr++;
|
|
continue;
|
|
}
|
|
lastPart = itr->devicePath.substr(strItr);
|
|
|
|
auto b = GetSysDevFromPathPhysical(itr->devicePath);
|
|
|
|
FSDevice *pDev {};
|
|
for (auto &dev : devices)
|
|
{
|
|
if (itr->devicePath == dev.devicePath)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (GetDevParent(itr->devicePath) != dev.devicePath)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pDev = &dev;
|
|
break;
|
|
}
|
|
|
|
if (!pDev)
|
|
{
|
|
itr++;
|
|
continue;
|
|
}
|
|
|
|
FSLogicalPartition part;
|
|
part.devicePath = pDev->devicePath;
|
|
part.logicalMount = itr->devicePath;
|
|
pDev->partitions.push_back(part);
|
|
itr = devices.erase(itr);
|
|
}
|
|
}
|
|
|
|
// step 5: adds some known fs mountpoints
|
|
for (auto &dev : devices)
|
|
{
|
|
auto devId = ReadIDPair(dev.devicePath);
|
|
|
|
for (const auto &partition : partitions)
|
|
{
|
|
FSLogicalPartition part;
|
|
|
|
auto parentId = ReadIDPair(GetDevParent(partition.logicalMountPoint));
|
|
if (parentId != devId)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FSLogicalPartition *pPart {};
|
|
for (auto &devPartition : dev.partitions)
|
|
{
|
|
if (partition.logicalMountPoint.empty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (devPartition.logicalMount == partition.logicalMountPoint ||
|
|
devPartition.logicalMount == partition.logicalMountPointTrashOS)
|
|
{
|
|
pPart = &devPartition;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pPart)
|
|
{
|
|
pPart->filesystemMountPoints.push_back(partition.fsPath);
|
|
}
|
|
else
|
|
{
|
|
part.devicePath = dev.devicePath;
|
|
part.logicalMount = partition.logicalMountPoint;
|
|
part.filesystemMountPoints.push_back(partition.fsPath);
|
|
dev.partitions.push_back(part);
|
|
}
|
|
}
|
|
}
|
|
|
|
// step 6: add special mounts
|
|
{
|
|
AuHashMap<AuString, AuList<ParsedMount *>> specialDevices;
|
|
|
|
for (auto &partition : partitions)
|
|
{
|
|
bool bBreak { false };
|
|
|
|
for (const auto &device : devices)
|
|
{
|
|
for (const auto &devPartition : device.partitions)
|
|
{
|
|
if (devPartition.logicalMount == partition.logicalMountPoint)
|
|
{
|
|
bBreak = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bBreak)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
specialDevices[partition.logicalMountPoint].push_back(&partition);
|
|
}
|
|
|
|
for (auto &[linuxName, mounts] : specialDevices)
|
|
{
|
|
FSDevice device;
|
|
FSDevice *pDevice {};
|
|
device.type = EFSDeviceType::eDeviceUnknown;
|
|
device.bus = EFSBusType::eBusGeneric;
|
|
device.altProductDescription = "$intrin";
|
|
device.productModel = *AuSwInfo::GetPlatformInfo().kUserlandBrand;
|
|
if (linuxName.empty())
|
|
{
|
|
continue;
|
|
}
|
|
else if (linuxName == "tmpfs")
|
|
{
|
|
device.devicePath = kMagicDeviceTemp;
|
|
device.type = EFSDeviceType::eDeviceTempRamDisk;
|
|
}
|
|
else if (linuxName == "ramfs" ||
|
|
linuxName == "debugfs")
|
|
{
|
|
device.devicePath = kMagicDeviceRam;
|
|
device.type = EFSDeviceType::eDeviceRamDisk;
|
|
}
|
|
else if (linuxName == "fusectl" ||
|
|
linuxName == "tracefs" ||
|
|
linuxName == "configfs" ||
|
|
linuxName == "securityfs" ||
|
|
linuxName == "efivarfs" ||
|
|
linuxName == "binfmt_misc" ||
|
|
linuxName == "gvfsd-fuse")
|
|
{
|
|
device.devicePath = kMagicDeviceCFG;
|
|
device.type = EFSDeviceType::eDeviceKernelConfig;
|
|
}
|
|
else if (linuxName == "pstore" ||
|
|
linuxName == "dev" ||
|
|
linuxName == "sys" ||
|
|
linuxName == "proc" ||
|
|
linuxName == "devpts" ||
|
|
linuxName == "pstore" ||
|
|
linuxName == "pgroup" ||
|
|
linuxName == "bpf" ||
|
|
linuxName == "hugetlbfs" ||
|
|
linuxName == "cgroup2" ||
|
|
linuxName == "mqueue")
|
|
{
|
|
device.devicePath = kMagicDeviceSystem;
|
|
device.type = EFSDeviceType::eDeviceVirtualFileSystem;
|
|
}
|
|
else
|
|
{
|
|
device.devicePath = linuxName;
|
|
device.type = EFSDeviceType::eDeviceVirtualFileSystem;
|
|
}
|
|
|
|
for (auto &device2 : devices)
|
|
{
|
|
if (device.devicePath == device2.devicePath)
|
|
{
|
|
pDevice = &device2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
FSLogicalPartition part;
|
|
part.devicePath = device.devicePath;
|
|
part.name = linuxName;
|
|
|
|
if (pDevice)
|
|
{
|
|
bool bBreak { false };
|
|
|
|
for (auto &part2 : pDevice->partitions)
|
|
{
|
|
if (part2.name == part.name)
|
|
{
|
|
for (auto &pMount : mounts)
|
|
{
|
|
part2.filesystemMountPoints.push_back(pMount->fsPath);
|
|
}
|
|
bBreak = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bBreak)
|
|
{
|
|
for (auto &pMount : mounts)
|
|
{
|
|
part.filesystemMountPoints.push_back(pMount->fsPath);
|
|
}
|
|
|
|
pDevice->partitions.push_back(part);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (auto &pMount : mounts)
|
|
{
|
|
part.filesystemMountPoints.push_back(pMount->fsPath);
|
|
}
|
|
|
|
device.partitions.push_back(part);
|
|
devices.push_back(device);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ScanFstab(AuList<FSDevice> &devices)
|
|
{
|
|
|
|
}
|
|
|
|
static void SetOtherMetadata(AuList<FSDevice> &devices)
|
|
{
|
|
for (auto &dev : devices)
|
|
{
|
|
AuString mainPath = GetSysDevFromPathPhysical(dev.devicePath);
|
|
if (mainPath.empty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (dev.type == EFSDeviceType::eDeviceUnknown)
|
|
{
|
|
if (mainPath.size())
|
|
{
|
|
if (auto val = ReadPosixFileWord(mainPath + "/device/type"))
|
|
{
|
|
if (val.Value() == 5)
|
|
{
|
|
dev.type = EFSDeviceType::eDeviceCD;
|
|
}
|
|
else if (val.Value() == 0)
|
|
{
|
|
dev.type = EFSDeviceType::eDeviceDisk;
|
|
}
|
|
}
|
|
|
|
if (AuFS::DirExists(mainPath + "/device/scsi_generic"))
|
|
{
|
|
if (dev.type == EFSDeviceType::eDeviceUnknown)
|
|
{
|
|
if (AuFS::DirExists(mainPath + "/device/scsi_disk"))
|
|
{
|
|
dev.type = EFSDeviceType::eDeviceDisk;
|
|
}
|
|
else
|
|
{
|
|
dev.type = EFSDeviceType::eDeviceSCSI;
|
|
}
|
|
}
|
|
|
|
if (dev.bus == EFSBusType::eBusGeneric)
|
|
{
|
|
dev.bus = EFSBusType::eBusSCSI;
|
|
|
|
if (auto val = ReadPosixFile(mainPath + "/device/vendor"))
|
|
{
|
|
if (AuStartsWith(val.Value(), "ATA"))
|
|
{
|
|
dev.bus = EFSBusType::eBusSATA;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dev.bus == EFSBusType::eBusGeneric ||
|
|
dev.bus == EFSBusType::eBusSCSI ||
|
|
dev.bus == EFSBusType::eBusSATA)
|
|
{
|
|
if (auto val = ReadPosixFile(mainPath + "/device/uevent"))
|
|
{
|
|
if (AuStringContains(val.Value(), "usb"))
|
|
{
|
|
dev.type = EFSDeviceType::eDeviceUSBMass;
|
|
dev.bus = EFSBusType::eBusUSB;
|
|
}
|
|
|
|
if (AuStringContains(val.Value(), "NVME_TRTYPE"))
|
|
{
|
|
dev.type = EFSDeviceType::eDeviceDisk;
|
|
dev.bus = EFSBusType::eBusNVMePCIe;
|
|
}
|
|
}
|
|
}
|
|
|
|
dev.uFSDeviceSizeInBytes = GetDeviceSizeInBytes(dev.devicePath);
|
|
dev.uFSDevicePageSizeInBytes = GetPhysicalSectorSizeFromPath(dev.devicePath);
|
|
|
|
for (auto &part : dev.partitions)
|
|
{
|
|
if (dev.uFSDeviceSizeInBytes)
|
|
{
|
|
if (part.filesystemMountPoints.size())
|
|
{
|
|
part.space = GetLogicalUsedFromPath(part.filesystemMountPoints[0]);
|
|
}
|
|
else
|
|
{
|
|
part.space = GetLogicalUsedFromPath(part.logicalMount);
|
|
}
|
|
}
|
|
|
|
auto sysPath = GetSysDevFromDev(part.logicalMount);
|
|
if (sysPath.empty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
part.offset = ReadPhysStatsFromPartition(sysPath);
|
|
part.bIsReadOnly = ReadIsROFromPartition(sysPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SetOtherMetadataFixup(AuList<FSDevice> &devices)
|
|
{
|
|
for (auto &device : devices)
|
|
{
|
|
if (device.uuid.is_nil() &&
|
|
device.partitions.size() == 1 &&
|
|
!device.partitions[0].uuid.is_nil())
|
|
{
|
|
device.uuid = device.partitions[0].uuid;
|
|
}
|
|
else if (device.uuid.is_nil())
|
|
{
|
|
device.uuid = UUIDFromPersistentString(device.devicePath);
|
|
}
|
|
|
|
for (auto &partition : device.partitions)
|
|
{
|
|
if (partition.uuid.is_nil())
|
|
{
|
|
if (partition.logicalMount.size())
|
|
{
|
|
partition.uuid = UUIDFromPersistentString(partition.logicalMount);
|
|
}
|
|
else if (partition.name)
|
|
{
|
|
partition.uuid = UUIDFromPersistentString(partition.name.Value());
|
|
}
|
|
}
|
|
else if (!partition.name)
|
|
{
|
|
partition.name = partition.uuid.to_string();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void CalculateLookupIds(AuList<FSDevice> &devices,
|
|
AuList<AuPair<AuUInt32, AuUInt32>> &lookup)
|
|
{
|
|
for (auto &dev : devices)
|
|
{
|
|
lookup.push_back(ReadIDPair(dev.devicePath));
|
|
|
|
for (auto &part : dev.partitions)
|
|
{
|
|
lookup.push_back(ReadIDPair(part.logicalMount));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ScanForUUIDsLogical(AuList<FSDevice> &devices,
|
|
AuList<AuPair<AuUInt32, AuUInt32>> &lookup)
|
|
{
|
|
AuHashMap<AuPair<AuUInt32, AuUInt32>, AuString> labelMap;
|
|
AuList<AuPair<AuString, AuString>> labels;
|
|
AuList<AuString> a;
|
|
|
|
AuFS::SpecialsInDirectory("/dev/disk/by-partuuid", a);
|
|
for (auto &label : a)
|
|
{
|
|
AuString cpy = label;
|
|
label.insert(0, "/dev/disk/by-partuuid/");
|
|
labels.push_back(AuMakePair(label, cpy));
|
|
}
|
|
|
|
for (const auto &[dev, label] : labels)
|
|
{
|
|
auto id = ReadIDPairSym(dev);
|
|
if (id.first == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AuTryInsert(labelMap, id, label);
|
|
}
|
|
|
|
AuUInt32 uIndex {};
|
|
for (auto &dev : devices)
|
|
{
|
|
auto uCurIndex = uIndex++;
|
|
|
|
{
|
|
auto &idPair = lookup[uCurIndex];
|
|
auto itr = labelMap.find(idPair);
|
|
if (itr != labelMap.end())
|
|
{
|
|
if (auto uuid = uuids::uuid::from_string(itr->second))
|
|
{
|
|
dev.uuid = uuid.value();
|
|
}
|
|
else
|
|
{
|
|
dev.altProductDescription = itr->second;
|
|
dev.uuid = UUIDFromPersistentString(itr->second);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto &part : dev.partitions)
|
|
{
|
|
auto uCurIndex = uIndex++;
|
|
|
|
{
|
|
auto &idPair = lookup[uCurIndex];
|
|
auto itr = labelMap.find(idPair);
|
|
if (itr != labelMap.end())
|
|
{
|
|
if (auto uuid = uuids::uuid::from_string(itr->second))
|
|
{
|
|
part.uuid = uuid.value();
|
|
}
|
|
else
|
|
{
|
|
part.name = itr->second;
|
|
part.altID = itr->second;
|
|
part.uuid = UUIDFromPersistentString(itr->second);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ScanForUUIDsGPT(AuList<FSDevice> &devices,
|
|
AuList<AuPair<AuUInt32, AuUInt32>> &lookup)
|
|
{
|
|
AuHashMap<AuPair<AuUInt32, AuUInt32>, AuString> labelMap;
|
|
AuList<AuPair<AuString, AuString>> labels;
|
|
AuList<AuString> a;
|
|
|
|
AuFS::SpecialsInDirectory("/dev/disk/by-uuid", a);
|
|
for (auto &label : a)
|
|
{
|
|
AuString cpy = label;
|
|
label.insert(0, "/dev/disk/by-uuid/");
|
|
labels.push_back(AuMakePair(label, cpy));
|
|
}
|
|
a.clear();
|
|
|
|
for (const auto &[dev, label] : labels)
|
|
{
|
|
auto id = ReadIDPairSym(dev);
|
|
if (id.first == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AuTryInsert(labelMap, id, label);
|
|
}
|
|
|
|
AuUInt32 uIndex {};
|
|
for (auto &dev : devices)
|
|
{
|
|
auto uCurIndex = uIndex++;
|
|
|
|
for (auto &part : dev.partitions)
|
|
{
|
|
auto uCurIndex = uIndex++;
|
|
|
|
{
|
|
auto &idPair = lookup[uCurIndex];
|
|
auto itr = labelMap.find(idPair);
|
|
if (itr != labelMap.end())
|
|
{
|
|
if (auto uuid = uuids::uuid::from_string(itr->second))
|
|
{
|
|
part.uuid = uuid.value();
|
|
}
|
|
else
|
|
{
|
|
part.name = itr->second;
|
|
part.altID = itr->second;
|
|
if (part.uuid.is_nil())
|
|
{
|
|
part.uuid = UUIDFromPersistentString(itr->second);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ScanForUUIDs(AuList<FSDevice> &devices,
|
|
AuList<AuPair<AuUInt32, AuUInt32>> &lookup)
|
|
{
|
|
ScanForUUIDsLogical(devices, lookup);
|
|
ScanForUUIDsGPT(devices, lookup);
|
|
}
|
|
|
|
static void ScanForLabels(AuList<FSDevice> &devices,
|
|
AuList<AuPair<AuUInt32, AuUInt32>> &lookup)
|
|
{
|
|
AuHashMap<AuPair<AuUInt32, AuUInt32>, AuString> labelMap;
|
|
AuList<AuPair<AuString, AuString>> labels;
|
|
AuList<AuString> a;
|
|
AuFS::SpecialsInDirectory("/dev/disk/by-partlabel", a);
|
|
for (auto &label : a)
|
|
{
|
|
auto cpy = label;
|
|
label.insert(0, "/dev/disk/by-partlabel/");
|
|
labels.push_back(AuMakePair(label, cpy));
|
|
}
|
|
a.clear();
|
|
AuFS::SpecialsInDirectory("/dev/disk/by-label", a);
|
|
for (auto &label : a)
|
|
{
|
|
auto cpy = label;
|
|
label.insert(0, "/dev/disk/by-label/");
|
|
labels.push_back(AuMakePair(label, cpy));
|
|
}
|
|
a.clear();
|
|
|
|
for (const auto &[dev, label] : labels)
|
|
{
|
|
auto id = ReadIDPairSym(dev);
|
|
if (id.first == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AuTryInsert(labelMap, id, UnescapeShitString(label));
|
|
}
|
|
|
|
AuUInt32 uIndex {};
|
|
for (auto &dev : devices)
|
|
{
|
|
auto uCurIndex = uIndex++;
|
|
|
|
{
|
|
auto &idPair = lookup[uCurIndex];
|
|
auto itr = labelMap.find(idPair);
|
|
if (itr != labelMap.end())
|
|
{
|
|
dev.altLabel = itr->second;
|
|
}
|
|
}
|
|
|
|
for (auto &part : dev.partitions)
|
|
{
|
|
auto uCurIndex = uIndex++;
|
|
|
|
{
|
|
auto &idPair = lookup[uCurIndex];
|
|
auto itr = labelMap.find(idPair);
|
|
if (itr != labelMap.end())
|
|
{
|
|
part.name = itr->second;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ScanForDesc(AuList<FSDevice> &devices)
|
|
{
|
|
|
|
}
|
|
|
|
AuList<FSDevice> SysGetFSDevices()
|
|
{
|
|
AuList<FSDevice> devices;
|
|
AuList<AuPair<AuUInt32, AuUInt32>> lookup;
|
|
ScanProcFS(devices);
|
|
ScanFstab(devices);
|
|
CalculateLookupIds(devices, lookup);
|
|
SetOtherMetadata(devices);
|
|
ScanForUUIDs(devices, lookup);
|
|
ScanForLabels(devices, lookup);
|
|
ScanForDesc(devices);
|
|
SetOtherMetadataFixup(devices);
|
|
CachedStatReset();
|
|
return devices;
|
|
}
|
|
|
|
AUKN_SYM AuString GetRootFromPath(const AuString &path)
|
|
{
|
|
AuString ret;
|
|
|
|
try
|
|
{
|
|
auto devices = AuFS::GetFSDevices();
|
|
|
|
auto pathEx = NormalizePathRet(path);
|
|
if (pathEx.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
for (const auto &device : devices)
|
|
{
|
|
for (const auto &partition : device.partitions)
|
|
{
|
|
for (const auto &mp : partition.filesystemMountPoints)
|
|
{
|
|
if (ret.size() < mp.size() &&
|
|
AuStartsWith(pathEx, mp))
|
|
{
|
|
ret = mp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
catch (...)
|
|
{
|
|
SysPushErrorCatch();
|
|
return {};
|
|
}
|
|
}
|
|
} |