master ohmu/fastfetch-openbsd / src / detection / btrfs / btrfs_linux.c
ohmu  ·  2026-10-15
  1#include "btrfs.h"
  2
  3#include "common/debug.h"
  4#include "common/io.h"
  5#include <fcntl.h>
  6
  7enum { uuidLen = (uint32_t) __builtin_strlen("00000000-0000-0000-0000-000000000000") };
  8
  9static const char* enumerateDevices(FFBtrfsResult* item, int dfd, FFstrbuf* buffer) {
 10    int subfd = openat(dfd, "devices", O_RDONLY | O_CLOEXEC | O_DIRECTORY);
 11    if (subfd < 0) {
 12        FF_DEBUG("openat(\"devices\") failed: %s", strerror(errno));
 13        return "openat(\"/sys/fs/btrfs/UUID/devices\") == -1";
 14    }
 15
 16    FF_AUTO_CLOSE_DIR DIR* dirp = fdopendir(subfd);
 17    if (dirp == nullptr) {
 18        FF_DEBUG("fdopendir(\"devices\") failed: %s", strerror(errno));
 19        close(subfd);
 20        return "fdopendir(\"/sys/fs/btrfs/UUID/devices\") == nullptr";
 21    }
 22
 23    struct dirent* entry;
 24    while ((entry = readdir(dirp)) != nullptr) {
 25        if (entry->d_name[0] == '.') {
 26            continue;
 27        }
 28
 29        if (item->devices.length) {
 30            ffStrbufAppendC(&item->devices, ',');
 31        }
 32        ffStrbufAppendS(&item->devices, entry->d_name);
 33
 34        char path[sizeof(entry->d_name) + sizeof("/size") + 1];
 35        snprintf(path, ARRAY_SIZE(path), "%s/size", entry->d_name);
 36
 37        if (ffReadFileBufferRelative(subfd, path, buffer)) {
 38            item->totalSize += ffStrbufToUInt(buffer, 0) * 512;
 39        }
 40    }
 41
 42    return nullptr;
 43}
 44
 45static const char* enumerateFeatures(FFBtrfsResult* item, int dfd) {
 46    int subfd = openat(dfd, "features", O_RDONLY | O_CLOEXEC | O_DIRECTORY);
 47    if (subfd < 0) {
 48        FF_DEBUG("openat(\"features\") failed: %s", strerror(errno));
 49        return "openat(\"/sys/fs/btrfs/UUID/features\") == -1";
 50    }
 51
 52    FF_AUTO_CLOSE_DIR DIR* dirp = fdopendir(subfd);
 53    if (dirp == nullptr) {
 54        FF_DEBUG("fdopendir(\"features\") failed: %s", strerror(errno));
 55        return "fdopendir(\"/sys/fs/btrfs/UUID/features\") == nullptr";
 56    }
 57
 58    struct dirent* entry;
 59    while ((entry = readdir(dirp)) != nullptr) {
 60        if (entry->d_name[0] == '.') {
 61            continue;
 62        }
 63        if (item->features.length) {
 64            ffStrbufAppendC(&item->features, ',');
 65        }
 66        ffStrbufAppendS(&item->features, entry->d_name);
 67    }
 68
 69    return nullptr;
 70}
 71
 72static const char* detectAllocation(FFBtrfsResult* item, int dfd, FFstrbuf* buffer) {
 73    FF_AUTO_CLOSE_FD int subfd = openat(dfd, "allocation", O_RDONLY | O_CLOEXEC | O_PATH | O_DIRECTORY);
 74    if (subfd < 0) {
 75        FF_DEBUG("openat(\"allocation\") failed: %s", strerror(errno));
 76        return "openat(\"/sys/fs/btrfs/UUID/allocation\") == -1";
 77    }
 78
 79    if (ffReadFileBufferRelative(subfd, "global_rsv_size", buffer)) {
 80        item->globalReservationTotal = ffStrbufToUInt(buffer, 0);
 81    } else {
 82        FF_DEBUG("ffReadFileBuffer(\"global_rsv_size\") failed");
 83        return "ffReadFileBuffer(\"/sys/fs/btrfs/UUID/allocation/global_rsv_size\") == nullptr";
 84    }
 85
 86    if (ffReadFileBufferRelative(subfd, "global_rsv_reserved", buffer)) {
 87        // global_rsv_reserved is already the used part of the reservation, global_rsv_size its total
 88        item->globalReservationUsed = ffStrbufToUInt(buffer, 0);
 89    }
 90
 91#define FF_BTRFS_DETECT_PROFILE(_index, _type, _profile, _copies)      \
 92    else if (faccessat(subfd, _type "/" _profile "/", F_OK, 0) == 0) { \
 93        item->allocation[_index].profile = _profile;                   \
 94        item->allocation[_index].copies = _copies;                     \
 95    }
 96
 97#define FF_BTRFS_DETECT_TYPE(_index, _type)                                \
 98    do {                                                                   \
 99        item->allocation[_index].type = _type;                             \
100        if (ffReadFileBufferRelative(subfd, _type "/total_bytes", buffer)) \
101            item->allocation[_index].total = ffStrbufToUInt(buffer, 0);    \
102                                                                           \
103        if (ffReadFileBufferRelative(subfd, _type "/bytes_used", buffer))  \
104            item->allocation[_index].used = ffStrbufToUInt(buffer, 0);     \
105                                                                           \
106        if (false) {}                                                      \
107        FF_BTRFS_DETECT_PROFILE(_index, _type, "single", 1)                \
108        FF_BTRFS_DETECT_PROFILE(_index, _type, "dup", 2)                   \
109        FF_BTRFS_DETECT_PROFILE(_index, _type, "raid0", 1)                 \
110        FF_BTRFS_DETECT_PROFILE(_index, _type, "raid1", 2)                 \
111        FF_BTRFS_DETECT_PROFILE(_index, _type, "raid10", 2)                \
112        FF_BTRFS_DETECT_PROFILE(_index, _type, "raid1c3", 3)               \
113        FF_BTRFS_DETECT_PROFILE(_index, _type, "raid1c4", 4)               \
114        FF_BTRFS_DETECT_PROFILE(_index, _type, "raid5", 1) /* (n-1)/n */   \
115        FF_BTRFS_DETECT_PROFILE(_index, _type, "raid6", 1) /* (n-2)/n */   \
116        else {                                                             \
117            item->allocation[_index].profile = "unknown";                  \
118            item->allocation[_index].copies = 1;                           \
119        }                                                                  \
120    } while (0)
121
122    FF_BTRFS_DETECT_TYPE(0, "data");
123    FF_BTRFS_DETECT_TYPE(1, "metadata");
124    FF_BTRFS_DETECT_TYPE(2, "system");
125
126#undef FF_BTRFS_DETECT_TYPE
127
128    return nullptr;
129}
130
131const char* ffDetectBtrfs(FFlist* result) {
132    FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/fs/btrfs/");
133    if (dirp == nullptr) {
134        FF_DEBUG("opendir(\"/sys/fs/btrfs\") failed: %s", strerror(errno));
135        return "opendir(\"/sys/fs/btrfs\") == nullptr";
136    }
137
138    FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
139
140    struct dirent* entry;
141    while ((entry = readdir(dirp)) != nullptr) {
142        if (entry->d_name[0] == '.') {
143            continue;
144        }
145        if (strlen(entry->d_name) != uuidLen) {
146            continue;
147        }
148
149        FFBtrfsResult* item = FF_LIST_ADD(FFBtrfsResult, *result);
150        (*item) = (FFBtrfsResult) {
151            .uuid = ffStrbufCreateNS(uuidLen, entry->d_name),
152            .name = ffStrbufCreate(),
153            .devices = ffStrbufCreate(),
154            .features = ffStrbufCreate(),
155        };
156
157        FF_AUTO_CLOSE_FD int dfd = openat(dirfd(dirp), entry->d_name, O_RDONLY | O_CLOEXEC | O_PATH | O_DIRECTORY);
158        if (dfd < 0) {
159            continue;
160        }
161
162        if (ffAppendFileBufferRelative(dfd, "label", &item->name)) {
163            ffStrbufTrimRightSpace(&item->name);
164        }
165
166        enumerateDevices(item, dfd, &buffer);
167
168        enumerateFeatures(item, dfd);
169
170        if (ffReadFileBufferRelative(dfd, "generation", &buffer)) {
171            item->generation = (uint32_t) ffStrbufToUInt(&buffer, 0);
172        }
173
174        if (ffReadFileBufferRelative(dfd, "nodesize", &buffer)) {
175            item->nodeSize = (uint32_t) ffStrbufToUInt(&buffer, 0);
176        }
177
178        if (ffReadFileBufferRelative(dfd, "sectorsize", &buffer)) {
179            item->sectorSize = (uint32_t) ffStrbufToUInt(&buffer, 0);
180        }
181
182        detectAllocation(item, dfd, &buffer);
183    }
184
185    return nullptr;
186}