summaryrefslogtreecommitdiffstats
path: root/src/detection/btrfs
diff options
context:
space:
mode:
authorsumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
committersumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
commit76424950e373d3b04ac3dd13019151bfba3e8423 (patch)
tree4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/detection/btrfs
Add the files
Diffstat (limited to 'src/detection/btrfs')
-rw-r--r--src/detection/btrfs/btrfs.h28
-rw-r--r--src/detection/btrfs/btrfs_linux.c178
-rw-r--r--src/detection/btrfs/btrfs_nosupport.c5
3 files changed, 211 insertions, 0 deletions
diff --git a/src/detection/btrfs/btrfs.h b/src/detection/btrfs/btrfs.h
new file mode 100644
index 0000000..08ea4e9
--- /dev/null
+++ b/src/detection/btrfs/btrfs.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "fastfetch.h"
+#include "modules/btrfs/option.h"
+
+typedef struct FFBtrfsDiskUsage {
+ uint64_t total;
+ uint64_t used;
+ const char* type;
+ const char* profile; // single / dup / raidx
+ uint8_t copies;
+} FFBtrfsDiskUsage;
+
+typedef struct FFBtrfsResult {
+ FFstrbuf name;
+ FFstrbuf uuid;
+ FFstrbuf devices;
+ FFstrbuf features;
+ uint32_t generation;
+ uint32_t nodeSize;
+ uint32_t sectorSize;
+ uint64_t totalSize;
+ uint64_t globalReservationUsed;
+ uint64_t globalReservationTotal;
+ FFBtrfsDiskUsage allocation[3];
+} FFBtrfsResult;
+
+const char* ffDetectBtrfs(FFlist* result /* list of FFBtrfsResult */);
diff --git a/src/detection/btrfs/btrfs_linux.c b/src/detection/btrfs/btrfs_linux.c
new file mode 100644
index 0000000..41914d7
--- /dev/null
+++ b/src/detection/btrfs/btrfs_linux.c
@@ -0,0 +1,178 @@
+#include "btrfs.h"
+
+#include "common/io.h"
+#include <fcntl.h>
+
+enum { uuidLen = (uint32_t) __builtin_strlen("00000000-0000-0000-0000-000000000000") };
+
+static const char* enumerateDevices(FFBtrfsResult* item, int dfd, FFstrbuf* buffer) {
+ int subfd = openat(dfd, "devices", O_RDONLY | O_CLOEXEC | O_DIRECTORY);
+ if (subfd < 0) {
+ return "openat(\"/sys/fs/btrfs/UUID/devices\") == -1";
+ }
+
+ FF_AUTO_CLOSE_DIR DIR* dirp = fdopendir(subfd);
+ if (dirp == NULL) {
+ close(subfd);
+ return "fdopendir(\"/sys/fs/btrfs/UUID/devices\") == NULL";
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dirp)) != NULL) {
+ if (entry->d_name[0] == '.') {
+ continue;
+ }
+
+ if (item->devices.length) {
+ ffStrbufAppendC(&item->devices, ',');
+ }
+ ffStrbufAppendS(&item->devices, entry->d_name);
+
+ char path[sizeof(entry->d_name) + sizeof("/size") + 1];
+ snprintf(path, ARRAY_SIZE(path), "%s/size", entry->d_name);
+
+ if (ffReadFileBufferRelative(subfd, path, buffer)) {
+ item->totalSize += ffStrbufToUInt(buffer, 0) * 512;
+ }
+ }
+
+ return NULL;
+}
+
+static const char* enumerateFeatures(FFBtrfsResult* item, int dfd) {
+ int subfd = openat(dfd, "features", O_RDONLY | O_CLOEXEC | O_DIRECTORY);
+ if (subfd < 0) {
+ return "openat(\"/sys/fs/btrfs/UUID/features\") == -1";
+ }
+
+ FF_AUTO_CLOSE_DIR DIR* dirp = fdopendir(subfd);
+ if (dirp == NULL) {
+ return "fdopendir(\"/sys/fs/btrfs/UUID/features\") == NULL";
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dirp)) != NULL) {
+ if (entry->d_name[0] == '.') {
+ continue;
+ }
+ if (item->features.length) {
+ ffStrbufAppendC(&item->features, ',');
+ }
+ ffStrbufAppendS(&item->features, entry->d_name);
+ }
+
+ return NULL;
+}
+
+static const char* detectAllocation(FFBtrfsResult* item, int dfd, FFstrbuf* buffer) {
+ FF_AUTO_CLOSE_FD int subfd = openat(dfd, "allocation", O_RDONLY | O_CLOEXEC | O_PATH | O_DIRECTORY);
+ if (subfd < 0) {
+ return "openat(\"/sys/fs/btrfs/UUID/allocation\") == -1";
+ }
+
+ if (ffReadFileBufferRelative(subfd, "global_rsv_size", buffer)) {
+ item->globalReservationTotal = ffStrbufToUInt(buffer, 0);
+ } else {
+ return "ffReadFileBuffer(\"/sys/fs/btrfs/UUID/allocation/global_rsv_size\") == NULL";
+ }
+
+ if (ffReadFileBufferRelative(subfd, "global_rsv_reserved", buffer)) {
+ item->globalReservationUsed = ffStrbufToUInt(buffer, 0);
+ }
+ item->globalReservationUsed = item->globalReservationTotal - item->globalReservationUsed;
+
+#define FF_BTRFS_DETECT_PROFILE(_index, _type, _profile, _copies) \
+ else if (faccessat(subfd, _type "/" _profile "/", F_OK, 0) == 0) { \
+ item->allocation[_index].profile = _profile; \
+ item->allocation[_index].copies = _copies; \
+ }
+
+#define FF_BTRFS_DETECT_TYPE(_index, _type) \
+ do { \
+ item->allocation[_index].type = _type; \
+ if (ffReadFileBufferRelative(subfd, _type "/total_bytes", buffer)) \
+ item->allocation[_index].total = ffStrbufToUInt(buffer, 0); \
+ \
+ if (ffReadFileBufferRelative(subfd, _type "/bytes_used", buffer)) \
+ item->allocation[_index].used = ffStrbufToUInt(buffer, 0); \
+ \
+ if (false) {} \
+ FF_BTRFS_DETECT_PROFILE(_index, _type, "single", 1) \
+ FF_BTRFS_DETECT_PROFILE(_index, _type, "dup", 2) \
+ FF_BTRFS_DETECT_PROFILE(_index, _type, "raid0", 1) \
+ FF_BTRFS_DETECT_PROFILE(_index, _type, "raid1", 2) \
+ FF_BTRFS_DETECT_PROFILE(_index, _type, "raid10", 2) \
+ FF_BTRFS_DETECT_PROFILE(_index, _type, "raid1c3", 3) \
+ FF_BTRFS_DETECT_PROFILE(_index, _type, "raid1c4", 4) \
+ FF_BTRFS_DETECT_PROFILE(_index, _type, "raid5", 1) /* (n-1)/n */ \
+ FF_BTRFS_DETECT_PROFILE(_index, _type, "raid6", 1) /* (n-2)/n */ \
+ else { \
+ item->allocation[_index].profile = "unknown"; \
+ item->allocation[_index].copies = 1; \
+ } \
+ } while (0)
+
+ FF_BTRFS_DETECT_TYPE(0, "data");
+ FF_BTRFS_DETECT_TYPE(1, "metadata");
+ FF_BTRFS_DETECT_TYPE(2, "system");
+
+#undef FF_BTRFS_DETECT_TYPE
+
+ return NULL;
+}
+
+const char* ffDetectBtrfs(FFlist* result) {
+ FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/fs/btrfs/");
+ if (dirp == NULL) {
+ return "opendir(\"/sys/fs/btrfs\") == NULL";
+ }
+
+ FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
+
+ struct dirent* entry;
+ while ((entry = readdir(dirp)) != NULL) {
+ if (entry->d_name[0] == '.') {
+ continue;
+ }
+ if (strlen(entry->d_name) != uuidLen) {
+ continue;
+ }
+
+ FFBtrfsResult* item = FF_LIST_ADD(FFBtrfsResult, *result);
+ (*item) = (FFBtrfsResult) {
+ .uuid = ffStrbufCreateNS(uuidLen, entry->d_name),
+ .name = ffStrbufCreate(),
+ .devices = ffStrbufCreate(),
+ .features = ffStrbufCreate(),
+ };
+
+ FF_AUTO_CLOSE_FD int dfd = openat(dirfd(dirp), entry->d_name, O_RDONLY | O_CLOEXEC | O_PATH | O_DIRECTORY);
+ if (dfd < 0) {
+ continue;
+ }
+
+ if (ffAppendFileBufferRelative(dfd, "label", &item->name)) {
+ ffStrbufTrimRightSpace(&item->name);
+ }
+
+ enumerateDevices(item, dfd, &buffer);
+
+ enumerateFeatures(item, dfd);
+
+ if (ffReadFileBufferRelative(dfd, "generation", &buffer)) {
+ item->generation = (uint32_t) ffStrbufToUInt(&buffer, 0);
+ }
+
+ if (ffReadFileBufferRelative(dfd, "nodesize", &buffer)) {
+ item->nodeSize = (uint32_t) ffStrbufToUInt(&buffer, 0);
+ }
+
+ if (ffReadFileBufferRelative(dfd, "sectorsize", &buffer)) {
+ item->sectorSize = (uint32_t) ffStrbufToUInt(&buffer, 0);
+ }
+
+ detectAllocation(item, dfd, &buffer);
+ }
+
+ return NULL;
+}
diff --git a/src/detection/btrfs/btrfs_nosupport.c b/src/detection/btrfs/btrfs_nosupport.c
new file mode 100644
index 0000000..b51d2c5
--- /dev/null
+++ b/src/detection/btrfs/btrfs_nosupport.c
@@ -0,0 +1,5 @@
+#include "btrfs.h"
+
+const char* ffDetectBtrfs(FF_A_UNUSED FFlist* result) {
+ return "Not supported on this platform";
+}