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}