/*** 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 #include "FS.hpp" #include "FSPlatformDevices.hpp" #include #include #include #include //major, minor #include 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> gCachedStat; static AuMutex gMutex; bool SpecialsInDirectory(const AuROString &string, AuList &dirs); static AuOptional 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 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 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 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 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 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 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 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 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 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; 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 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 {}; } } }