2022-09-23 19:45:23 +08:00
|
|
|
#include "disk.h"
|
|
|
|
|
|
2023-09-18 09:45:21 +08:00
|
|
|
#include "common/io/io.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>
|
2024-04-09 18:47:25 +08:00
|
|
|
#include <mntent.h>
|
2023-01-20 20:25:22 +01:00
|
|
|
#include <sys/stat.h>
|
2022-09-23 19:45:23 +08:00
|
|
|
#include <sys/statvfs.h>
|
2023-09-17 20:08:46 +08:00
|
|
|
#include <sys/mount.h>
|
2022-09-23 19:45:23 +08:00
|
|
|
|
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-09-17 20:08:46 +08:00
|
|
|
static bool isPhysicalDevice(const struct mntent* device)
|
2022-12-29 22:51:23 +01:00
|
|
|
{
|
2023-11-18 15:33:23 +01:00
|
|
|
#ifndef __ANDROID__ //On Android, `/dev` is not accessible, so that the following checks always fail
|
2023-06-19 19:59:21 +08:00
|
|
|
|
2023-09-17 20:08:46 +08:00
|
|
|
//Always show the root path
|
|
|
|
|
if(ffStrEquals(device->mnt_dir, "/"))
|
2023-07-16 22:39:21 +08:00
|
|
|
return true;
|
|
|
|
|
|
2023-12-24 19:42:10 +08:00
|
|
|
if(ffStrEquals(device->mnt_fsname, "none"))
|
|
|
|
|
return false;
|
|
|
|
|
|
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-12-24 19:42:10 +08:00
|
|
|
if(ffStrEquals(device->mnt_type, "9p"))
|
2024-04-21 17:35:47 +08:00
|
|
|
return ffStrContains(device->mnt_opts, "aname=drvfs");
|
2023-01-20 20:25:22 +01:00
|
|
|
|
2024-02-23 09:29:39 +08:00
|
|
|
//ZFS pool
|
|
|
|
|
if(ffStrEquals(device->mnt_type, "zfs"))
|
2023-02-01 15:56:25 +01:00
|
|
|
return true;
|
|
|
|
|
|
2023-09-17 20:08:46 +08:00
|
|
|
//Pseudo filesystems don't have a device in /dev
|
|
|
|
|
if(!ffStrStartsWith(device->mnt_fsname, "/dev/"))
|
|
|
|
|
return false;
|
|
|
|
|
|
2024-02-23 09:29:39 +08:00
|
|
|
//#731
|
|
|
|
|
if(ffStrEquals(device->mnt_type, "bcachefs"))
|
|
|
|
|
return true;
|
|
|
|
|
|
2023-09-17 20:08:46 +08:00
|
|
|
if(
|
|
|
|
|
ffStrStartsWith(device->mnt_fsname + 5, "loop") || //Ignore loop devices
|
|
|
|
|
ffStrStartsWith(device->mnt_fsname + 5, "ram") || //Ignore ram devices
|
|
|
|
|
ffStrStartsWith(device->mnt_fsname + 5, "fd") //Ignore fd devices
|
|
|
|
|
) return false;
|
|
|
|
|
|
2023-06-08 21:48:32 +08:00
|
|
|
struct stat deviceStat;
|
2023-09-17 20:08:46 +08:00
|
|
|
if(stat(device->mnt_fsname, &deviceStat) != 0)
|
2023-06-08 21:48:32 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//Ignore all devices that are not block devices
|
|
|
|
|
if(!S_ISBLK(deviceStat.st_mode))
|
|
|
|
|
return false;
|
|
|
|
|
|
2023-09-18 23:03:14 +08:00
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
//Pseudo filesystems don't have a device in /dev
|
|
|
|
|
if(!ffStrStartsWith(device->mnt_fsname, "/dev/"))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if(
|
|
|
|
|
ffStrStartsWith(device->mnt_fsname + 5, "loop") || //Ignore loop devices
|
|
|
|
|
ffStrStartsWith(device->mnt_fsname + 5, "ram") || //Ignore ram devices
|
|
|
|
|
ffStrStartsWith(device->mnt_fsname + 5, "fd") //Ignore fd devices
|
|
|
|
|
) return false;
|
|
|
|
|
|
2024-07-15 22:55:52 +08:00
|
|
|
// https://source.android.com/docs/core/ota/apex?hl=zh-cn
|
2024-08-21 00:23:24 +08:00
|
|
|
if(ffStrStartsWith(device->mnt_dir, "/apex/"))
|
2024-07-15 22:55:52 +08:00
|
|
|
return false;
|
|
|
|
|
|
2023-06-19 19:59:21 +08:00
|
|
|
#endif // __ANDROID__
|
|
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void detectNameFromPath(FFDisk* disk, const struct stat* deviceStat, FFstrbuf* basePath)
|
|
|
|
|
{
|
2023-09-18 09:45:21 +08:00
|
|
|
FF_AUTO_CLOSE_DIR DIR* dir = opendir(basePath->chars);
|
2023-01-20 20:25:22 +01:00
|
|
|
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-09-18 00:14:05 +08:00
|
|
|
static void detectName(FFDisk* disk)
|
2023-01-20 20:25:22 +01:00
|
|
|
{
|
|
|
|
|
struct stat deviceStat;
|
2023-09-18 00:14:05 +08:00
|
|
|
if(stat(disk->mountFrom.chars, &deviceStat) != 0)
|
2023-01-20 20:25:22 +01:00
|
|
|
return;
|
|
|
|
|
|
2023-09-18 09:45:21 +08:00
|
|
|
FF_STRBUF_AUTO_DESTROY basePath = ffStrbufCreate();
|
|
|
|
|
|
2023-08-15 20:10:01 +08:00
|
|
|
//Try label first
|
|
|
|
|
ffStrbufSetS(&basePath, "/dev/disk/by-label/");
|
2023-01-20 20:25:22 +01:00
|
|
|
detectNameFromPath(disk, &deviceStat, &basePath);
|
|
|
|
|
|
|
|
|
|
if(disk->name.length == 0)
|
|
|
|
|
{
|
2023-08-15 20:10:01 +08:00
|
|
|
//Try partlabel second
|
|
|
|
|
ffStrbufSetS(&basePath, "/dev/disk/by-partlabel/");
|
2023-01-20 20:25:22 +01:00
|
|
|
detectNameFromPath(disk, &deviceStat, &basePath);
|
|
|
|
|
}
|
2023-08-15 20:59:29 +08:00
|
|
|
|
2023-09-17 20:08:46 +08:00
|
|
|
if (disk->name.length == 0) return;
|
|
|
|
|
|
2023-08-15 20:59:29 +08:00
|
|
|
// Basic\x20data\x20partition
|
|
|
|
|
for (uint32_t i = ffStrbufFirstIndexS(&disk->name, "\\x");
|
|
|
|
|
i != disk->name.length;
|
|
|
|
|
i = ffStrbufNextIndexS(&disk->name, i + 1, "\\x"))
|
|
|
|
|
{
|
|
|
|
|
uint32_t len = (uint32_t) strlen("\\x20");
|
|
|
|
|
if (disk->name.length >= len)
|
|
|
|
|
{
|
|
|
|
|
char bak = disk->name.chars[i + len];
|
|
|
|
|
disk->name.chars[i + len] = '\0';
|
|
|
|
|
disk->name.chars[i] = (char) strtoul(&disk->name.chars[i + 2], NULL, 16);
|
|
|
|
|
ffStrbufRemoveSubstr(&disk->name, i + 1, i + len);
|
|
|
|
|
disk->name.chars[i + 1] = bak;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-20 20:25:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef __ANDROID__
|
|
|
|
|
|
2024-10-17 14:29:27 +08:00
|
|
|
static void detectType(FF_MAYBE_UNUSED const FFlist* disks, FFDisk* currentDisk, FF_MAYBE_UNUSED struct mntent* device)
|
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-08-26 09:32:19 +08:00
|
|
|
currentDisk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT;
|
2023-01-21 14:04:17 +08:00
|
|
|
else if(ffStrbufStartsWithS(¤tDisk->mountpoint, "/mnt/media_rw/"))
|
2023-08-26 09:32:19 +08:00
|
|
|
currentDisk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT;
|
2023-01-20 20:25:22 +01:00
|
|
|
else
|
2023-08-26 09:32:19 +08:00
|
|
|
currentDisk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT;
|
2023-01-20 20:25:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
2023-09-17 20:08:46 +08:00
|
|
|
static bool isSubvolume(const FFlist* disks, FFDisk* currentDisk)
|
2023-01-16 11:52:54 +01:00
|
|
|
{
|
2023-09-17 20:08:46 +08:00
|
|
|
if(ffStrbufEqualS(¤tDisk->mountFrom, "drvfs")) // WSL Windows drives
|
2023-06-14 22:41:40 +08:00
|
|
|
return false;
|
2023-01-21 14:42:19 +01:00
|
|
|
|
2023-09-17 20:08:46 +08:00
|
|
|
if(ffStrbufEqualS(¤tDisk->filesystem, "zfs"))
|
|
|
|
|
{
|
|
|
|
|
//ZFS subvolumes
|
2023-09-17 22:28:03 +08:00
|
|
|
uint32_t index = ffStrbufFirstIndexC(¤tDisk->mountFrom, '/');
|
|
|
|
|
if (index == currentDisk->mountFrom.length)
|
2023-09-17 20:08:46 +08:00
|
|
|
return false;
|
|
|
|
|
|
2023-09-17 22:28:03 +08:00
|
|
|
FF_STRBUF_AUTO_DESTROY zpoolName = ffStrbufCreateNS(index, currentDisk->mountFrom.chars);
|
2023-09-17 20:08:46 +08:00
|
|
|
for(uint32_t i = 0; i < disks->length - 1; i++)
|
|
|
|
|
{
|
|
|
|
|
const FFDisk* otherDevice = ffListGet(disks, i);
|
|
|
|
|
if(ffStrbufEqualS(&otherDevice->filesystem, "zfs") && ffStrbufStartsWith(&otherDevice->mountFrom, &zpoolName))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-09-17 22:28:03 +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++)
|
|
|
|
|
{
|
|
|
|
|
const FFDisk* otherDevice = ffListGet(disks, i);
|
|
|
|
|
|
|
|
|
|
if(ffStrbufEqual(¤tDisk->mountFrom, &otherDevice->mountFrom))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-01 15:56:25 +01:00
|
|
|
|
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-10-09 16:23:42 +08:00
|
|
|
static bool isRemovable(FFDisk* currentDisk)
|
2023-09-18 09:45:21 +08:00
|
|
|
{
|
2023-12-26 18:58:05 +08:00
|
|
|
if (!ffStrbufStartsWithS(¤tDisk->mountFrom, "/dev/"))
|
2023-09-18 09:45:21 +08:00
|
|
|
return false;
|
|
|
|
|
|
2023-12-26 18:58:05 +08:00
|
|
|
char sysBlockPartition[64];
|
|
|
|
|
snprintf(sysBlockPartition, sizeof(sysBlockPartition), "/sys/class/block/%s", currentDisk->mountFrom.chars + strlen("/dev/"));
|
2023-09-18 09:45:21 +08:00
|
|
|
|
2023-12-26 18:58:05 +08:00
|
|
|
char sysBlockVolume[PATH_MAX]; // /sys/devices/pci0000:00/0000:00:14.0/usb4/4-3/4-3:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda1
|
|
|
|
|
if (realpath(sysBlockPartition, sysBlockVolume) == NULL)
|
|
|
|
|
return false;
|
|
|
|
|
strcpy(strrchr(sysBlockVolume, '/') + 1, "removable");
|
2023-09-18 09:45:21 +08:00
|
|
|
|
2023-12-26 18:58:05 +08:00
|
|
|
char removableChar = '0';
|
|
|
|
|
return ffReadFileData(sysBlockVolume, 1, &removableChar) > 0 && removableChar == '1';
|
2023-09-18 09:45:21 +08:00
|
|
|
}
|
|
|
|
|
|
2024-10-15 23:08:10 +08:00
|
|
|
static void detectType(const FFlist* disks, FFDisk* currentDisk, struct mntent* device)
|
2023-01-20 20:25:22 +01:00
|
|
|
{
|
2023-12-17 12:13:28 +08:00
|
|
|
if(ffStrbufStartsWithS(¤tDisk->mountpoint, "/boot") || ffStrbufStartsWithS(¤tDisk->mountpoint, "/efi"))
|
|
|
|
|
currentDisk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT;
|
2023-09-18 09:45:21 +08:00
|
|
|
else if(isSubvolume(disks, currentDisk))
|
2023-08-26 09:32:19 +08:00
|
|
|
currentDisk->type = FF_DISK_VOLUME_TYPE_SUBVOLUME_BIT;
|
2023-12-24 20:32:43 +08:00
|
|
|
else if(isRemovable(currentDisk))
|
|
|
|
|
currentDisk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT;
|
2023-01-20 20:25:22 +01:00
|
|
|
else
|
2023-08-26 09:32:19 +08:00
|
|
|
currentDisk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT;
|
2024-10-15 23:08:10 +08:00
|
|
|
if (hasmntopt(device, MNTOPT_RO))
|
|
|
|
|
currentDisk->type |= FF_DISK_VOLUME_TYPE_READONLY_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;
|
2023-08-26 10:36:00 +08:00
|
|
|
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
|
2023-01-20 20:25:22 +01:00
|
|
|
|
2024-08-21 00:23:24 +08:00
|
|
|
if (fs.f_files >= fs.f_ffree)
|
|
|
|
|
{
|
|
|
|
|
disk->filesTotal = (uint32_t) fs.f_files;
|
|
|
|
|
disk->filesUsed = (uint32_t) (disk->filesTotal - fs.f_ffree);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Windows filesystem in WSL
|
|
|
|
|
disk->filesTotal = disk->filesUsed = 0;
|
|
|
|
|
}
|
2023-08-26 12:50:45 +08:00
|
|
|
|
2024-04-09 18:47:25 +08:00
|
|
|
disk->createTime = 0;
|
|
|
|
|
#ifdef FF_HAVE_STATX
|
|
|
|
|
struct statx stx;
|
|
|
|
|
if (statx(0, disk->mountpoint.chars, 0, STATX_BTIME, &stx) == 0 && (stx.stx_mask & STATX_BTIME))
|
|
|
|
|
disk->createTime = (uint64_t)((stx.stx_btime.tv_sec * 1000) + (stx.stx_btime.tv_nsec / 1000000));
|
|
|
|
|
#endif
|
2024-10-17 14:29:27 +08:00
|
|
|
|
|
|
|
|
#ifdef __ANDROID__ // hasmntopt requires a higher Android API level
|
|
|
|
|
if(fs.f_flag & ST_RDONLY)
|
|
|
|
|
disk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT;
|
|
|
|
|
#endif
|
2023-01-20 20:25:22 +01:00
|
|
|
}
|
|
|
|
|
|
2024-04-30 15:12:49 +08:00
|
|
|
const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
|
2022-09-23 19:45:23 +08:00
|
|
|
{
|
2023-09-17 20:08:46 +08:00
|
|
|
FILE* mountsFile = setmntent("/proc/mounts", "r");
|
2022-10-17 13:19:48 +02:00
|
|
|
if(mountsFile == NULL)
|
2023-09-17 20:08:46 +08:00
|
|
|
return "setmntent(\"/proc/mounts\", \"r\") == NULL";
|
2022-10-17 13:19:48 +02:00
|
|
|
|
2023-09-17 20:08:46 +08:00
|
|
|
struct mntent* device;
|
2022-10-17 13:19:48 +02:00
|
|
|
|
2023-09-17 20:08:46 +08:00
|
|
|
while((device = getmntent(mountsFile)))
|
2022-10-17 13:19:48 +02:00
|
|
|
{
|
2024-04-30 15:12:49 +08:00
|
|
|
if (__builtin_expect(options->folders.length, 0))
|
|
|
|
|
{
|
|
|
|
|
if (!ffDiskMatchMountpoint(options, device->mnt_dir))
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else if(!isPhysicalDevice(device))
|
2023-02-09 11:20:37 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
//We have a valid device, add it to the list
|
2023-06-12 22:09:52 +08:00
|
|
|
FFDisk* disk = ffListAdd(disks);
|
2023-09-18 23:03:14 +08:00
|
|
|
disk->type = FF_DISK_VOLUME_TYPE_NONE;
|
2023-02-09 11:20:37 +01:00
|
|
|
|
2023-09-17 20:08:46 +08:00
|
|
|
//detect mountFrom
|
|
|
|
|
ffStrbufInitS(&disk->mountFrom, device->mnt_fsname);
|
|
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
//detect mountpoint
|
2023-09-17 20:08:46 +08:00
|
|
|
ffStrbufInitS(&disk->mountpoint, device->mnt_dir);
|
2022-10-17 13:19:48 +02:00
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
//detect filesystem
|
2023-09-17 20:08:46 +08:00
|
|
|
ffStrbufInitS(&disk->filesystem, device->mnt_type);
|
2022-10-17 13:19:48 +02:00
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
//detect name
|
|
|
|
|
ffStrbufInit(&disk->name);
|
2023-09-18 00:14:05 +08:00
|
|
|
detectName(disk); // Also detects external devices
|
2022-10-17 13:19:48 +02:00
|
|
|
|
2023-01-20 20:25:22 +01:00
|
|
|
//detect type
|
2024-10-15 23:08:10 +08:00
|
|
|
detectType(disks, disk, device);
|
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
|
|
|
|
2023-09-17 20:08:46 +08:00
|
|
|
endmntent(mountsFile);
|
2023-06-12 22:09:52 +08:00
|
|
|
|
|
|
|
|
return NULL;
|
2022-09-23 19:45:23 +08:00
|
|
|
}
|