AuroraRuntime/Source/IO/FS/FSPlatformDevices.Linux.cpp

1636 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 AuROString &path)
{
AuString ret;
ret.resize(4096);
auto count = readlink(AuString(path).c_str(), ret.data(), ret.size());
if (count <= 0)
{
return {};
}
else
{
ret.resize(count);
}
if (ret.size() && 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 AuROString &path, struct stat *pstat)
{
AU_LOCK_GUARD(gMutex);
try
{
// TODO: use roxtl het lookup instead of allocating AuString
AuString copy(path);
auto itr = gCachedStat.find(copy);
if (itr == gCachedStat.end())
{
int err = ::stat(copy.c_str(), pstat);
if (-1 == err)
{
gCachedStat[copy] = {};
errno = 0;
}
else
{
gCachedStat[copy] = *pstat;
}
return err;
}
else
{
auto &that = itr->second;
if (that)
{
*pstat = that.Value();
return 0;
}
else
{
return -1;
}
}
}
catch (...)
{
return -1;
}
}
static bool BlockExists(const AuROString &path)
{
struct stat s;
int err = CachedStat(path, &s);
if (-1 == err)
{
return false;
}
return S_ISBLK(s.st_mode);
}
static AuString _DevFromPath(const AuROString &path)
{
auto pos = path.find_last_of('/');
if (pos == AuString::npos)
{
return {};
}
auto dev = AuString("/dev/") + AuString(path.substr(pos + 1));
if (!BlockExists(dev))
{
return {};
}
return dev;
}
static AuString GetDevDevPathFromSysDev(const AuROString &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 AuROString &path)
{
struct stat s;
if (path.empty())
{
return {};
}
auto err = CachedStat(path, &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 AuROString &path)
{
if (auto optLink = ReadLink(path))
{
return ReadIDPair(optLink.Value());
}
else
{
return ReadIDPair(path);
}
}
static AuString GetSysDevFromPathLogical(const AuROString &path)
{
struct stat s;
auto pathEx = NormalizePathRet(path);
if (pathEx.empty())
{
return {};
}
auto err = CachedStat(pathEx, &s);
if (err == -1)
{
return {};
}
return AuString(fmt::format("/sys/dev/block/{}:{}", major(s.st_dev), minor(s.st_dev)));
}
static AuString GetSysDevFromDev(const AuROString &path)
{
struct stat s;
if (path.empty())
{
return {};
}
auto err = CachedStat(path, &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 AuROString &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 AuROString &path)
{
auto shortLogical = GetSysDevFromPathLogical(path);
if (shortLogical.empty())
{
return {};
}
return MagicPathRd(shortLogical);
}
// isnt posshit a nice family of os?
static AuString GetSysDevFromPathPhysical(const AuROString &path)
{
if (AuStartsWith(path, "/dev/"))
{
AuUInt uCount {};
for (AU_ITERATE_N(i, path.size()))
{
if (path[i] == '/')
{
uCount++;
}
}
if (uCount == 2)
{
auto guess = AuString("/sys/block/") + AuString(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 AuString(path);
}
else
{
return MagicPath(path);
}
return {};
}
static AuString GetDevParent(const AuROString &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 AuROString &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 AuROString &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 AuROString &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 AuROString &root)
{
try
{
return GetDeviceFromPath(root);
}
catch (...)
{
SysPushErrorCatch();
return {};
}
}
AUKN_SYM AuUInt32 GetLogicalSectorSizeFromPath(const AuROString &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 AuROString &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 AuROString &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 AuROString &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 AuROString &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 AuROString &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 AuROString &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 {};
}
}
}