From c6e6c1c4330bf7ca15bcf311c86b9bdcfa75d761 Mon Sep 17 00:00:00 2001 From: J Reece Wilson Date: Fri, 5 Jul 2024 10:39:24 +0100 Subject: [PATCH] [+] Added Linux FSPlatformDevices implementation --- Source/IO/FS/FSPlatformDevices.Linux.cpp | 1599 +++++++++++++++++++++- 1 file changed, 1572 insertions(+), 27 deletions(-) diff --git a/Source/IO/FS/FSPlatformDevices.Linux.cpp b/Source/IO/FS/FSPlatformDevices.Linux.cpp index 9ef8d464..ec492097 100644 --- a/Source/IO/FS/FSPlatformDevices.Linux.cpp +++ b/Source/IO/FS/FSPlatformDevices.Linux.cpp @@ -1,78 +1,1623 @@ /*** - Copyright (C) 2023 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved. + 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 ***/ -// TODO: recover work in progress work dated last year #include #include "FS.hpp" #include "FSPlatformDevices.hpp" +#include +#include +#include +#include //major, minor +#include namespace Aurora::IO::FS { - AUKN_SYM AuString GetRootFromPath(const AuString &path) + 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> gCachedStat; + static AuMutex gMutex; + + bool SpecialsInDirectory(const AuROString &string, AuList &dirs); + + static AuOptional 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 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 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 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 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 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 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 GetDeviceFromPath(const AuString &path) { - return {}; + 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 GetLogicalMountFromPath(const AuString &fileOrDirPath) { - return {}; + 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 GetDeviceFromRoot(const AuString &root) { - return {}; + try + { + return GetDeviceFromPath(root); + } + catch (...) + { + SysPushErrorCatch(); + return {}; + } } AUKN_SYM AuUInt32 GetLogicalSectorSizeFromPath(const AuString &path) { - return 0; - } + struct statvfs data; - AUKN_SYM LogicalUsedResponse GetLogicalUsedFromLogicalDevice(const AuString &logicalMountPath) - { - return {}; - } + auto pathEx = NormalizePathRet(path); + if (pathEx.empty()) + { + return {}; + } + + if ((statvfs(pathEx.c_str(), &data)) < 0) + { + SysPushErrorIO(); + return {}; + } - AUKN_SYM AuUInt64 GetDeviceSizeInBytes(const AuString &physicalDevicePath) - { - return 0; + return data.f_bsize; } AUKN_SYM AuUInt32 GetPhysicalSectorSizeFromPath(const AuString &path) { - return 0; + 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 GatherRelevantBlockDevices() + { + AuList 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 idPairPhys; + }; + + // step two: gather partitions and misc virtual file systems + static AuList GatherActualPartitions() + { + AuList 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 &devices) + { + AuList miscDevices; + AuList specialBlockDevices; + + auto blocks = GatherRelevantBlockDevices(); + auto partitions = GatherActualPartitions(); + + // step 3: create fs devices from non-paritions + + AuList 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> 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 &devices) + { + + } + + static void SetOtherMetadata(AuList &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 &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 &devices, + AuList> &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 &devices, + AuList> &lookup) + { + AuHashMap, AuString> labelMap; + AuList> labels; + AuList 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 &devices, + AuList> &lookup) + { + AuHashMap, AuString> labelMap; + AuList> labels; + AuList 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 &devices, + AuList> &lookup) + { + ScanForUUIDsLogical(devices, lookup); + ScanForUUIDsGPT(devices, lookup); + } + + static void ScanForLabels(AuList &devices, + AuList> &lookup) + { + AuHashMap, AuString> labelMap; + AuList> labels; + AuList 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 &devices) + { + } AuList SysGetFSDevices() { AuList devices; - - return devices; + AuList> 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 AuUInt32 GetPerformanceBufferSizeFromPath(const AuString &path) + AUKN_SYM AuString GetRootFromPath(const AuString &path) { - return 4096; - } + AuString ret; - AUKN_SYM LogicalUsedResponse GetLogicalUsedFromPath(const AuString &path) - { - auto rootPath = GetLogicalMountFromPath(path); - if (!rootPath) + 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 {}; } - - return GetLogicalUsedFromLogicalDevice(rootPath.GetResult()); } } \ No newline at end of file