2024-06-17 19:32:54 +08:00
|
|
|
#include "disk.h"
|
2026-01-06 09:48:38 +08:00
|
|
|
#include "common/io.h"
|
|
|
|
|
#include "common/stringUtils.h"
|
2024-06-17 19:32:54 +08:00
|
|
|
|
|
|
|
|
#include <sys/mntent.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/statvfs.h>
|
|
|
|
|
#include <sys/mount.h>
|
|
|
|
|
#include <sys/mnttab.h>
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
static bool isPhysicalDevice(const struct mnttab* device) {
|
|
|
|
|
// Always show the root path
|
|
|
|
|
if (ffStrEquals(device->mnt_mountp, "/")) {
|
2024-06-17 19:32:54 +08:00
|
|
|
return true;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-17 19:32:54 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (ffStrEquals(device->mnt_special, "none")) {
|
2024-06-17 19:32:54 +08:00
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-17 19:32:54 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
// ZFS pool
|
|
|
|
|
if (ffStrEquals(device->mnt_fstype, "zfs")) {
|
2024-06-17 19:32:54 +08:00
|
|
|
return true;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-17 19:32:54 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
// Pseudo filesystems don't have a device in /dev
|
|
|
|
|
if (!ffStrStartsWith(device->mnt_special, "/dev/")) {
|
2024-06-17 19:32:54 +08:00
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-17 19:32:54 +08:00
|
|
|
|
|
|
|
|
struct stat deviceStat;
|
2026-03-29 09:28:49 +08:00
|
|
|
if (stat(device->mnt_special, &deviceStat) != 0) {
|
2024-06-17 19:32:54 +08:00
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-17 19:32:54 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
// Ignore all devices that are not block devices
|
|
|
|
|
if (!S_ISBLK(deviceStat.st_mode)) {
|
2024-06-17 19:32:54 +08:00
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-17 19:32:54 +08:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
static bool isSubvolume(const FFlist* disks, FFDisk* currentDisk) {
|
|
|
|
|
if (ffStrbufEqualS(¤tDisk->filesystem, "zfs")) {
|
|
|
|
|
// ZFS subvolumes
|
2024-06-17 19:32:54 +08:00
|
|
|
uint32_t index = ffStrbufFirstIndexC(¤tDisk->mountFrom, '/');
|
2026-03-29 09:28:49 +08:00
|
|
|
if (index == currentDisk->mountFrom.length) {
|
2024-06-17 19:32:54 +08:00
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-17 19:32:54 +08:00
|
|
|
|
|
|
|
|
FF_STRBUF_AUTO_DESTROY zpoolName = ffStrbufCreateNS(index, currentDisk->mountFrom.chars);
|
2026-03-29 09:28:49 +08:00
|
|
|
for (uint32_t i = 0; i < disks->length - 1; i++) {
|
2024-10-25 14:48:11 +08:00
|
|
|
const FFDisk* otherDevice = FF_LIST_GET(FFDisk, *disks, i);
|
2026-03-29 09:28:49 +08:00
|
|
|
if (ffStrbufEqualS(&otherDevice->filesystem, "zfs") && ffStrbufStartsWith(&otherDevice->mountFrom, &zpoolName)) {
|
2024-06-17 19:32:54 +08:00
|
|
|
return true;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-17 19:32:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
|
|
|
|
// Filter all disks which device was already found. This catches BTRFS subvolumes.
|
|
|
|
|
for (uint32_t i = 0; i < disks->length - 1; i++) {
|
2024-10-25 14:48:11 +08:00
|
|
|
const FFDisk* otherDevice = FF_LIST_GET(FFDisk, *disks, i);
|
2024-06-17 19:32:54 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (ffStrbufEqual(¤tDisk->mountFrom, &otherDevice->mountFrom)) {
|
2024-06-17 19:32:54 +08:00
|
|
|
return true;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-17 19:32:54 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
static void detectType(const FFlist* disks, FFDisk* currentDisk, struct mnttab* device) {
|
|
|
|
|
if (hasmntopt(device, MNTOPT_NOBROWSE)) {
|
2024-06-17 19:32:54 +08:00
|
|
|
currentDisk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT;
|
2026-03-29 09:28:49 +08:00
|
|
|
} else if (isSubvolume(disks, currentDisk)) {
|
2024-06-17 19:32:54 +08:00
|
|
|
currentDisk->type = FF_DISK_VOLUME_TYPE_SUBVOLUME_BIT;
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
2024-06-17 19:32:54 +08:00
|
|
|
currentDisk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
|
|
|
|
if (hasmntopt(device, MNTOPT_RO)) {
|
2024-10-15 23:08:10 +08:00
|
|
|
currentDisk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-17 19:32:54 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
static void detectStats(FFDisk* disk) {
|
2024-06-17 19:32:54 +08:00
|
|
|
struct statvfs fs;
|
2026-03-29 09:28:49 +08:00
|
|
|
if (statvfs(disk->mountpoint.chars, &fs) != 0) {
|
2024-10-18 16:15:48 +08:00
|
|
|
memset(&fs, 0, sizeof(fs));
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-17 19:32:54 +08:00
|
|
|
|
|
|
|
|
disk->bytesTotal = fs.f_blocks * fs.f_frsize;
|
|
|
|
|
disk->bytesFree = fs.f_bfree * fs.f_frsize;
|
|
|
|
|
disk->bytesAvailable = fs.f_bavail * fs.f_frsize;
|
|
|
|
|
disk->bytesUsed = 0; // To be filled in ./disk.c
|
|
|
|
|
|
|
|
|
|
disk->filesTotal = (uint32_t) fs.f_files;
|
|
|
|
|
disk->filesUsed = (uint32_t) (disk->filesTotal - fs.f_ffree);
|
|
|
|
|
|
|
|
|
|
ffStrbufSetS(&disk->name, fs.f_fstr);
|
2026-04-04 00:18:42 +08:00
|
|
|
ffStrbufTrimRightSpace(&disk->name);
|
2024-06-17 19:32:54 +08:00
|
|
|
|
|
|
|
|
disk->createTime = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks) {
|
2024-06-17 19:32:54 +08:00
|
|
|
FF_AUTO_CLOSE_FILE FILE* mountsFile = fopen(MNTTAB, "r");
|
2026-03-29 09:28:49 +08:00
|
|
|
if (mountsFile == NULL) {
|
2024-06-17 19:32:54 +08:00
|
|
|
return "fopen(\"" MNTTAB "\", \"r\") == NULL";
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-17 19:32:54 +08:00
|
|
|
|
|
|
|
|
struct mnttab device;
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
while (getmntent(mountsFile, &device) == 0) {
|
|
|
|
|
if (__builtin_expect(options->folders.length, 0)) {
|
|
|
|
|
if (!ffStrbufSeparatedContainS(&options->folders, device.mnt_mountp, FF_DISK_FOLDER_SEPARATOR)) {
|
2024-06-17 19:32:54 +08:00
|
|
|
continue;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
|
|
|
|
} else if (!isPhysicalDevice(&device)) {
|
2024-06-17 19:32:54 +08:00
|
|
|
continue;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2024-06-17 19:32:54 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (options->hideFolders.length && ffDiskMatchesFolderPatterns(&options->hideFolders, device.mnt_mountp, FF_DISK_FOLDER_SEPARATOR)) {
|
2025-11-06 13:39:30 +08:00
|
|
|
continue;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2025-11-06 13:39:30 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (options->hideFS.length && ffStrbufSeparatedContainS(&options->hideFS, device.mnt_fstype, ':')) {
|
2025-11-06 13:39:30 +08:00
|
|
|
continue;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2025-11-06 13:39:30 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
// We have a valid device, add it to the list
|
2024-06-17 19:32:54 +08:00
|
|
|
FFDisk* disk = ffListAdd(disks);
|
|
|
|
|
disk->type = FF_DISK_VOLUME_TYPE_NONE;
|
|
|
|
|
ffStrbufInitS(&disk->mountFrom, device.mnt_special);
|
|
|
|
|
ffStrbufInitS(&disk->mountpoint, device.mnt_mountp);
|
|
|
|
|
ffStrbufInitS(&disk->filesystem, device.mnt_fstype);
|
|
|
|
|
ffStrbufInit(&disk->name);
|
|
|
|
|
detectType(disks, disk, &device);
|
|
|
|
|
detectStats(disk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|