master ohmu/fastfetch-openbsd / src / detection / disk / disk_bsd.c
ohmu  ·  2026-10-15
  1#include "disk.h"
  2#include "common/debug.h"
  3#include "common/mallocHelper.h"
  4#include "common/strutil.h"
  5
  6#include <errno.h>
  7#include <stdalign.h>
  8#include <sys/mount.h>
  9#include <sys/stat.h>
 10
 11#ifdef __NetBSD__
 12    #include <sys/types.h>
 13    #include <sys/statvfs.h>
 14    #define statfs statvfs
 15    #define f_flags f_flag
 16    #define f_bsize f_frsize
 17#endif
 18
 19#ifdef __FreeBSD__
 20    #if __has_include(<libgeom.h>)
 21        #include <libgeom.h>
 22
 23static const char* detectFsLabel(struct statfs* fs, FFDisk* disk) {
 24    if (!ffStrStartsWith(fs->f_mntfromname, "/dev/")) {
 25        return "Only block devices are supported";
 26    }
 27
 28    // Detect volume label in geom tree
 29    static struct gmesh geomTree;
 30    static struct gclass* cLabels;
 31    if (!cLabels) {
 32        if (geomTree.lg_ident) {
 33            FF_DEBUG("Previous geom_gettree() failed");
 34            return "Previous geom_gettree() failed";
 35        }
 36
 37        if (geom_gettree(&geomTree) < 0) {
 38            FF_DEBUG("geom_gettree() failed");
 39            geomTree.lg_ident = (void*) (intptr_t) -1;
 40            return "geom_gettree() failed";
 41        }
 42
 43        for (cLabels = geomTree.lg_class.lh_first; cLabels && !ffStrEquals(cLabels->lg_name, "LABEL"); cLabels = cLabels->lg_class.le_next);
 44        if (!cLabels) {
 45            FF_DEBUG("Class LABEL is not found");
 46            return "Class LABEL is not found";
 47        }
 48    }
 49
 50    for (struct ggeom* label = cLabels->lg_geom.lh_first; label; label = label->lg_geom.le_next) {
 51        struct gprovider* provider = label->lg_provider.lh_first;
 52        if (!provider || !ffStrEquals(label->lg_name, fs->f_mntfromname + strlen("/dev/"))) {
 53            continue;
 54        }
 55        const char* str = strchr(provider->lg_name, '/');
 56        ffStrbufSetS(&disk->name, str ? str + 1 : provider->lg_name);
 57    }
 58
 59    return nullptr;
 60}
 61    #else
 62static const char* detectFsLabel([[maybe_unused]] struct statfs* fs, [[maybe_unused]] FFDisk* disk) {
 63    return "Fastfetch was compiled without libgeom support";
 64}
 65    #endif
 66
 67static void detectFsInfo(struct statfs* fs, FFDisk* disk) {
 68    if (ffStrbufEqualS(&disk->filesystem, "zfs")) {
 69        disk->type = !ffStrbufStartsWithS(&disk->mountFrom, "zroot/") || ffStrbufStartsWithS(&disk->mountFrom, "zroot/ROOT/")
 70            ? FF_DISK_VOLUME_TYPE_REGULAR_BIT
 71            : FF_DISK_VOLUME_TYPE_SUBVOLUME_BIT;
 72    } else if (fs->f_flags & MNT_IGNORE) {
 73        disk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT;
 74    } else if (!(fs->f_flags & MNT_LOCAL)) {
 75        disk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT;
 76    } else {
 77        disk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT;
 78    }
 79
 80    detectFsLabel(fs, disk);
 81}
 82#elif __APPLE__
 83    #include "common/apple/cf_helpers.h"
 84
 85    #include <sys/attr.h>
 86    #include <unistd.h>
 87
 88    #ifndef MAC_OS_X_VERSION_10_15
 89        #define MNT_REMOVABLE 0x00000200
 90    #endif
 91
 92struct [[gnu::packed]] CmnAttrBuf {
 93    uint32_t length;
 94    attrreference_t nameRef;
 95    char nameSpace[NAME_MAX * 3 + 1];
 96};
 97
 98void detectFsInfo(struct statfs* fs, FFDisk* disk) {
 99    if (fs->f_flags & MNT_DONTBROWSE) {
100        disk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT;
101    } else if (fs->f_flags & MNT_REMOVABLE || !(fs->f_flags & MNT_LOCAL)) {
102        disk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT;
103    } else {
104        disk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT;
105    }
106
107    alignas(4) struct CmnAttrBuf attrBuf;
108    if (getattrlist(disk->mountpoint.chars, &(struct attrlist) {
109                                                .bitmapcount = ATTR_BIT_MAP_COUNT,
110                                                .commonattr = ATTR_CMN_NAME,
111                                            },
112            &attrBuf,
113            sizeof(attrBuf),
114            0) == 0) {
115        ffStrbufInitNS(&disk->name, attrBuf.nameRef.attr_length - 1 /* excluding '\0' */, attrBuf.nameSpace);
116    }
117}
118#else
119static void detectFsInfo(struct statfs* fs, FFDisk* disk) {
120    #ifdef MNT_IGNORE
121    if (fs->f_flags & MNT_IGNORE) {
122        disk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT;
123    } else
124    #endif
125        if (!(fs->f_flags & MNT_LOCAL)) {
126        disk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT;
127    } else {
128        disk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT;
129    }
130}
131#endif
132
133const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks) {
134#ifndef __NetBSD__
135    int size = getfsstat(nullptr, 0, MNT_WAIT);
136    if (size <= 0) {
137        FF_DEBUG("getfsstat(nullptr, 0, MNT_WAIT) failed: %s", strerror(errno));
138        return "getfsstat(nullptr, 0, MNT_WAIT) failed";
139    }
140#else
141    int size = getvfsstat(nullptr, 0, ST_WAIT);
142    if (size <= 0) {
143        FF_DEBUG("getvfsstat(nullptr, 0, ST_WAIT) failed: %s", strerror(errno));
144        return "getvfsstat(nullptr, 0, ST_WAIT) failed";
145    }
146#endif
147
148    FF_AUTO_FREE struct statfs* buf = malloc(sizeof(*buf) * (unsigned) size);
149#ifndef __NetBSD__
150    if (getfsstat(buf, (int) (sizeof(*buf) * (unsigned) size), MNT_NOWAIT) <= 0) {
151        FF_DEBUG("getfsstat(buf, size, MNT_NOWAIT) failed: %s", strerror(errno));
152        return "getfsstat(buf, size, MNT_NOWAIT) failed";
153    }
154#else
155    if (getvfsstat(buf, sizeof(*buf) * (unsigned) size, ST_NOWAIT) <= 0) {
156        FF_DEBUG("getvfsstat(buf, size, ST_NOWAIT) failed: %s", strerror(errno));
157        return "getvfsstat(buf, size, ST_NOWAIT) failed";
158    }
159#endif
160
161    for (struct statfs* fs = buf; fs < buf + size; ++fs) {
162        if (__builtin_expect(options->folders.length > 0, 0)) {
163            if (!ffStrbufSeparatedContainS(&options->folders, fs->f_mntonname, FF_DISK_FOLDER_SEPARATOR)) {
164                continue;
165            }
166        } else if (!ffStrEquals(fs->f_mntonname, "/") && !ffStrStartsWith(fs->f_mntfromname, "/dev/") && !ffStrEquals(fs->f_fstypename, "zfs") && !ffStrEquals(fs->f_fstypename, "fusefs.sshfs")) {
167            continue;
168        }
169
170        if (options->hideFolders.length && ffDiskMatchesFolderPatterns(&options->hideFolders, fs->f_mntonname, FF_DISK_FOLDER_SEPARATOR)) {
171            continue;
172        }
173
174        if (options->hideFS.length && ffStrbufSeparatedContainS(&options->hideFS, fs->f_fstypename, ':')) {
175            continue;
176        }
177
178#ifdef __FreeBSD__
179        // f_bavail and f_ffree are signed on FreeBSD...
180        if (fs->f_bavail < 0) {
181            fs->f_bavail = 0;
182        }
183        if (fs->f_ffree < 0) {
184            fs->f_ffree = 0;
185        }
186#endif
187
188        FFDisk* disk = FF_LIST_ADD(FFDisk, *disks);
189
190        disk->bytesTotal = (uint64_t) fs->f_blocks * (uint64_t) fs->f_bsize;
191        disk->bytesFree = (uint64_t) (options->calcType == FF_DISK_CALC_TYPE_FREE ? fs->f_bfree : fs->f_bavail) * (uint64_t) fs->f_bsize;
192
193        disk->filesTotal = (uint32_t) fs->f_files;
194        disk->filesUsed = (uint32_t) fs->f_files - (uint32_t) fs->f_ffree;
195
196        ffStrbufInitS(&disk->mountFrom, fs->f_mntfromname);
197        ffStrbufInitS(&disk->mountpoint, fs->f_mntonname);
198        ffStrbufInitS(&disk->filesystem, fs->f_fstypename);
199        ffStrbufInit(&disk->name);
200        disk->type = 0;
201        disk->createTime = 0;
202
203        detectFsInfo(fs, disk);
204
205        if (fs->f_flags & MNT_RDONLY) {
206            disk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT;
207        }
208
209#ifdef __OpenBSD__
210    #define st_birthtimespec __st_birthtim
211#endif
212#ifndef __DragonFly__
213        struct stat st;
214        if (stat(fs->f_mntonname, &st) == 0 && st.st_birthtimespec.tv_sec > 0) {
215            disk->createTime = (uint64_t) (((uint64_t) st.st_birthtimespec.tv_sec * 1000) + ((uint64_t) st.st_birthtimespec.tv_nsec / 1000000));
216        }
217#endif
218    }
219
220    return nullptr;
221}