2022-09-23 19:45:23 +08:00
|
|
|
#include "disk.h"
|
|
|
|
|
|
2023-02-01 15:56:25 +01:00
|
|
|
#include "util/stringUtils.h"
|
|
|
|
|
|
2022-12-29 23:19:44 +01:00
|
|
|
#include <limits.h>
|
2022-10-17 13:19:48 +02:00
|
|
|
#include <ctype.h>
|
2023-01-20 20:25:22 +01:00
|
|
|
#include <dirent.h>
|
|
|
|
|
#include <sys/stat.h>
|
2022-09-23 19:45:23 +08:00
|
|
|
#include <sys/statvfs.h>
|
|
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
#ifdef __USE_LARGEFILE64
|
|
|
|
|
#define stat stat64
|
|
|
|
|
#define statvfs statvfs64
|
|
|
|
|
#define dirent dirent64
|
|
|
|
|
#define readdir readdir64
|
|
|
|
|
#endif
|
|
|
|
|
|
2023-02-09 11:20:37 +01:00
|
|
|
static bool isPhysicalDevice(FFstrbuf* device)
|
2022-12-29 22:51:23 +01:00
|
|
|
{
|
2023-01-20 20:25:22 +01:00
|
|
|
//DrvFs is a filesystem plugin to WSL that was designed to support interop between WSL and the Windows filesystem.
|
2023-02-09 11:20:37 +01:00
|
|
|
if(ffStrbufEqualS(device, "drvfs"))
|
2023-01-20 20:25:22 +01:00
|
|
|
return true;
|
|
|
|
|
|
2023-02-01 15:56:25 +01:00
|
|
|
//ZFS root pool. The format is rpool/<POOL_NAME>/<VOLUME_NAME>/<SUBVOLUME_NAME>
|
2023-02-09 11:20:37 +01:00
|
|
|
if(ffStrbufStartsWithS(device, "rpool/"))
|
2023-02-01 15:56:25 +01:00
|
|
|
return true;
|
|
|
|
|
|
2023-06-08 21:48:32 +08:00
|
|
|
struct stat deviceStat;
|
|
|
|
|
if(stat(device->chars, &deviceStat) != 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//Ignore all devices that are not block devices
|
|
|
|
|
if(!S_ISBLK(deviceStat.st_mode))
|
|
|
|
|
return false;
|
|
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
//Pseudo filesystems don't have a device in /dev
|
2023-02-09 11:20:37 +01:00
|
|
|
if(!ffStrbufStartsWithS(device, "/dev/"))
|
2023-01-20 20:25:22 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if(
|
2023-02-09 11:20:37 +01:00
|
|
|
ffStrbufStartsWithS(device, "/dev/loop") || //Ignore loop devices
|
|
|
|
|
ffStrbufStartsWithS(device, "/dev/ram") || //Ignore ram devices
|
|
|
|
|
ffStrbufStartsWithS(device, "/dev/fd") //Ignore fd devices
|
2023-01-20 20:25:22 +01:00
|
|
|
) return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void appendNextEntry(FFstrbuf* buffer, char** source)
|
|
|
|
|
{
|
|
|
|
|
//Read the current entry into the buffer
|
|
|
|
|
while(**source != '\0' && !isspace(**source))
|
2022-12-29 22:51:23 +01:00
|
|
|
{
|
2022-12-29 23:19:44 +01:00
|
|
|
//After a backslash the next 3 characters are octal ascii codes
|
2023-01-20 20:25:22 +01:00
|
|
|
if(**source == '\\' && strnlen(*source, 4) == 4)
|
2022-12-29 22:58:29 +01:00
|
|
|
{
|
2022-12-29 23:19:44 +01:00
|
|
|
char octal[4] = {0};
|
2023-01-20 20:25:22 +01:00
|
|
|
strncpy(octal, *source + 1, 3);
|
2022-12-29 22:58:29 +01:00
|
|
|
|
2022-12-29 23:19:44 +01:00
|
|
|
long value = strtol(octal, NULL, 8); //Returns 0 on error, so no need to check endptr
|
|
|
|
|
if(value > 0 && value < CHAR_MAX)
|
|
|
|
|
{
|
2023-01-20 20:25:22 +01:00
|
|
|
ffStrbufAppendC(buffer, (char) value);
|
|
|
|
|
*source += 4;
|
2022-12-29 23:19:44 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2022-12-29 22:51:23 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
ffStrbufAppendC(buffer, **source);
|
|
|
|
|
++*source;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Skip whitespace
|
|
|
|
|
while(isspace(**source))
|
|
|
|
|
++*source;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void detectNameFromPath(FFDisk* disk, const struct stat* deviceStat, FFstrbuf* basePath)
|
|
|
|
|
{
|
|
|
|
|
DIR* dir = opendir(basePath->chars);
|
|
|
|
|
if(dir == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
uint32_t basePathLength = basePath->length;
|
|
|
|
|
|
|
|
|
|
struct dirent* entry;
|
|
|
|
|
while((entry = readdir(dir)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
if(entry->d_name[0] == '.')
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
ffStrbufAppendS(basePath, entry->d_name);
|
|
|
|
|
|
|
|
|
|
struct stat entryStat;
|
|
|
|
|
bool ret = stat(basePath->chars, &entryStat) == 0;
|
|
|
|
|
|
|
|
|
|
ffStrbufSubstrBefore(basePath, basePathLength);
|
|
|
|
|
|
|
|
|
|
if(!ret || deviceStat->st_ino != entryStat.st_ino)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
ffStrbufAppendS(&disk->name, entry->d_name);
|
|
|
|
|
break;
|
2022-12-29 22:51:23 +01:00
|
|
|
}
|
2023-01-20 20:25:22 +01:00
|
|
|
|
|
|
|
|
closedir(dir);
|
2022-12-29 22:51:23 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
static void detectName(FFDisk* disk, const FFstrbuf* device)
|
|
|
|
|
{
|
|
|
|
|
struct stat deviceStat;
|
|
|
|
|
if(stat(device->chars, &deviceStat) != 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2023-04-15 15:29:54 +08:00
|
|
|
FF_STRBUF_AUTO_DESTROY basePath = ffStrbufCreate();
|
2023-01-20 20:25:22 +01:00
|
|
|
|
|
|
|
|
//Try partlabel first
|
|
|
|
|
ffStrbufSetS(&basePath, "/dev/disk/by-partlabel/");
|
|
|
|
|
detectNameFromPath(disk, &deviceStat, &basePath);
|
|
|
|
|
|
|
|
|
|
//Try label second
|
|
|
|
|
if(disk->name.length == 0)
|
|
|
|
|
{
|
|
|
|
|
ffStrbufSetS(&basePath, "/dev/disk/by-label/");
|
|
|
|
|
detectNameFromPath(disk, &deviceStat, &basePath);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-21 14:57:30 +01:00
|
|
|
//Use the mountpoint as a last resort
|
|
|
|
|
if(disk->name.length == 0)
|
|
|
|
|
ffStrbufAppend(&disk->name, &disk->mountpoint);
|
2023-01-20 20:25:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef __ANDROID__
|
|
|
|
|
|
2023-01-21 14:43:02 +01:00
|
|
|
static void detectType(FF_MAYBE_UNUSED const FFlist* devices, FFDisk* currentDisk, FF_MAYBE_UNUSED const char* options)
|
2023-01-20 20:25:22 +01:00
|
|
|
{
|
2023-01-21 14:42:19 +01:00
|
|
|
if(ffStrbufEqualS(¤tDisk->mountpoint, "/") || ffStrbufEqualS(¤tDisk->mountpoint, "/storage/emulated"))
|
2023-06-08 21:34:56 +08:00
|
|
|
currentDisk->type = FF_DISK_TYPE_REGULAR_BIT;
|
2023-01-21 14:04:17 +08:00
|
|
|
else if(ffStrbufStartsWithS(¤tDisk->mountpoint, "/mnt/media_rw/"))
|
2023-06-08 21:34:56 +08:00
|
|
|
currentDisk->type = FF_DISK_TYPE_EXTERNAL_BIT;
|
2023-01-20 20:25:22 +01:00
|
|
|
else
|
2023-06-08 21:34:56 +08:00
|
|
|
currentDisk->type = FF_DISK_TYPE_HIDDEN_BIT;
|
2023-01-20 20:25:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
2023-01-21 14:42:19 +01:00
|
|
|
static bool isSubvolume(const FFlist* devices)
|
2023-01-16 11:52:54 +01:00
|
|
|
{
|
2023-01-21 14:42:19 +01:00
|
|
|
const FFstrbuf* currentDevie = ffListGet(devices, devices->length - 1);
|
|
|
|
|
|
2023-02-01 15:56:25 +01:00
|
|
|
//Filter all disks which device was already found. This catches BTRFS subvolumes.
|
2023-01-21 14:42:19 +01:00
|
|
|
for(uint32_t i = 0; i < devices->length - 1; i++)
|
2023-01-20 13:28:25 +01:00
|
|
|
{
|
2023-01-21 14:42:19 +01:00
|
|
|
const FFstrbuf* otherDevice = ffListGet(devices, i);
|
2023-01-16 11:52:54 +01:00
|
|
|
|
2023-01-21 14:42:19 +01:00
|
|
|
if(ffStrbufEqual(currentDevie, otherDevice))
|
2023-01-20 13:28:25 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
2023-01-16 11:52:54 +01:00
|
|
|
|
2023-02-01 15:56:25 +01:00
|
|
|
//ZFS subvolumes: rpool/<POOL_NAME>/<VOLUME_NAME>/<SUBVOLUME_NAME>.
|
|
|
|
|
//Test if the third slash is present.
|
|
|
|
|
if(strncmp(currentDevie->chars, "rpool/", 6) == 0 && ffStrHasNChars(currentDevie->chars, '/', 3))
|
|
|
|
|
return true;
|
|
|
|
|
|
2023-01-20 13:28:25 +01:00
|
|
|
return false;
|
2023-01-16 11:52:54 +01:00
|
|
|
}
|
2023-01-20 20:25:22 +01:00
|
|
|
|
2023-01-21 14:42:19 +01:00
|
|
|
static void detectType(const FFlist* devices, FFDisk* currentDisk, const char* options)
|
2023-01-20 20:25:22 +01:00
|
|
|
{
|
2023-01-21 14:42:19 +01:00
|
|
|
if(isSubvolume(devices))
|
2023-06-08 21:34:56 +08:00
|
|
|
currentDisk->type = FF_DISK_TYPE_SUBVOLUME_BIT;
|
2023-01-20 20:25:22 +01:00
|
|
|
else if(strstr(options, "nosuid") != NULL || strstr(options, "nodev") != NULL)
|
2023-06-08 21:34:56 +08:00
|
|
|
currentDisk->type = FF_DISK_TYPE_EXTERNAL_BIT;
|
2023-01-20 20:25:22 +01:00
|
|
|
else if(ffStrbufStartsWithS(¤tDisk->mountpoint, "/boot") || ffStrbufStartsWithS(¤tDisk->mountpoint, "/efi"))
|
2023-06-08 21:34:56 +08:00
|
|
|
currentDisk->type = FF_DISK_TYPE_HIDDEN_BIT;
|
2023-01-20 20:25:22 +01:00
|
|
|
else
|
2023-06-08 21:34:56 +08:00
|
|
|
currentDisk->type = FF_DISK_TYPE_REGULAR_BIT;
|
2023-01-20 20:25:22 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-16 11:52:54 +01:00
|
|
|
#endif
|
|
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
static void detectStats(FFDisk* disk)
|
|
|
|
|
{
|
|
|
|
|
struct statvfs fs;
|
|
|
|
|
if(statvfs(disk->mountpoint.chars, &fs) != 0)
|
|
|
|
|
memset(&fs, 0, sizeof(struct statvfs)); //Set all values to 0, so our values get initialized to 0 too
|
|
|
|
|
|
|
|
|
|
disk->bytesTotal = fs.f_blocks * fs.f_frsize;
|
|
|
|
|
disk->bytesUsed = disk->bytesTotal - (fs.f_bavail * fs.f_frsize);
|
|
|
|
|
|
|
|
|
|
disk->filesTotal = (uint32_t) fs.f_files;
|
|
|
|
|
disk->filesUsed = (uint32_t) (disk->filesTotal - fs.f_ffree);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-17 13:19:48 +02:00
|
|
|
void ffDetectDisksImpl(FFDiskResult* disks)
|
2022-09-23 19:45:23 +08:00
|
|
|
{
|
2022-10-17 13:19:48 +02:00
|
|
|
FILE* mountsFile = fopen("/proc/mounts", "r");
|
|
|
|
|
if(mountsFile == NULL)
|
|
|
|
|
{
|
|
|
|
|
ffStrbufAppendS(&disks->error, "fopen(\"/proc/mounts\", \"r\") == NULL");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-08 21:34:56 +08:00
|
|
|
FF_LIST_AUTO_DESTROY devices;
|
2023-01-21 14:42:19 +01:00
|
|
|
ffListInit(&devices, sizeof(FFstrbuf));
|
2023-01-20 20:25:22 +01:00
|
|
|
|
2022-10-17 13:19:48 +02:00
|
|
|
char* line = NULL;
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
|
|
|
while(getline(&line, &len, mountsFile) != EOF)
|
|
|
|
|
{
|
|
|
|
|
//Format of the file: "<device> <mountpoint> <filesystem> <options> ..." (Same as fstab)
|
|
|
|
|
char* currentPos = line;
|
|
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
//detect device
|
2023-01-21 14:42:19 +01:00
|
|
|
FFstrbuf* device = ffListAdd(&devices);
|
|
|
|
|
ffStrbufInit(device);
|
|
|
|
|
appendNextEntry(device, ¤tPos);
|
2022-10-17 13:19:48 +02:00
|
|
|
|
2023-02-09 11:20:37 +01:00
|
|
|
if(!isPhysicalDevice(device))
|
|
|
|
|
{
|
|
|
|
|
ffStrbufDestroy(device);
|
|
|
|
|
devices.length--;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//We have a valid device, add it to the list
|
|
|
|
|
FFDisk* disk = ffListAdd(&disks->disks);
|
|
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
//detect mountpoint
|
|
|
|
|
ffStrbufInit(&disk->mountpoint);
|
|
|
|
|
appendNextEntry(&disk->mountpoint, ¤tPos);
|
2022-10-17 13:19:48 +02:00
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
//detect filesystem
|
|
|
|
|
ffStrbufInit(&disk->filesystem);
|
|
|
|
|
appendNextEntry(&disk->filesystem, ¤tPos);
|
2022-10-17 13:19:48 +02:00
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
//detect name
|
|
|
|
|
ffStrbufInit(&disk->name);
|
2023-01-21 14:42:19 +01:00
|
|
|
detectName(disk, device);
|
2022-10-17 13:19:48 +02:00
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
//detect type
|
2023-01-21 14:42:19 +01:00
|
|
|
detectType(&devices, disk, currentPos);
|
2022-10-17 13:19:48 +02:00
|
|
|
|
|
|
|
|
//Detects stats
|
2023-01-20 20:25:22 +01:00
|
|
|
detectStats(disk);
|
2022-10-17 13:19:48 +02:00
|
|
|
}
|
2022-09-23 19:45:23 +08:00
|
|
|
|
2022-10-17 13:19:48 +02:00
|
|
|
if(line != NULL)
|
|
|
|
|
free(line);
|
2022-09-23 19:45:23 +08:00
|
|
|
|
2023-01-21 14:42:19 +01:00
|
|
|
FF_LIST_FOR_EACH(FFstrbuf, device, devices)
|
|
|
|
|
ffStrbufDestroy(device);
|
2023-01-20 20:25:22 +01:00
|
|
|
|
2022-10-17 13:19:48 +02:00
|
|
|
fclose(mountsFile);
|
2022-09-23 19:45:23 +08:00
|
|
|
}
|