@photostructure/fs-metadata 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -200
- package/binding.gyp +6 -3
- package/package.json +27 -24
- package/prebuilds/darwin-arm64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+fs-metadata.musl.node +0 -0
- package/prebuilds/linux-x64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/linux-x64/@photostructure+fs-metadata.musl.node +0 -0
- package/prebuilds/win32-x64/@photostructure+fs-metadata.glibc.node +0 -0
- package/src/binding.cpp +59 -29
- package/src/common/debug_log.h +64 -0
- package/src/common/hidden.h +12 -0
- package/src/common/metadata_worker.h +1 -44
- package/src/common/volume_metadata.h +98 -8
- package/src/common/volume_mount_points.h +42 -2
- package/src/darwin/fs_meta.h +0 -11
- package/src/darwin/hidden.cpp +139 -0
- package/src/darwin/hidden.h +35 -0
- package/src/darwin/volume_metadata.cpp +105 -40
- package/src/darwin/volume_mount_points.cpp +88 -29
- package/src/linux/blkid_cache.cpp +9 -0
- package/src/linux/blkid_cache.h +1 -0
- package/src/linux/gio_mount_points.cpp +81 -0
- package/src/linux/{gio_worker.h → gio_mount_points.h} +7 -2
- package/src/linux/gio_utils.cpp +29 -115
- package/src/linux/gio_utils.h +69 -9
- package/src/linux/gio_volume_metadata.cpp +90 -0
- package/src/linux/gio_volume_metadata.h +20 -0
- package/src/linux/volume_metadata.cpp +31 -16
- package/src/windows/drive_status.h +227 -0
- package/src/windows/error_utils.h +27 -6
- package/src/windows/fs_meta.h +3 -33
- package/src/windows/hidden.cpp +160 -0
- package/src/windows/hidden.h +3 -0
- package/src/windows/string.h +48 -0
- package/src/windows/system_volume.h +63 -0
- package/src/windows/volume_metadata.cpp +171 -138
- package/src/windows/volume_mount_points.cpp +102 -26
- package/src/common/mount_point.h +0 -13
- package/src/linux/gio_worker.cpp +0 -87
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/darwin/volume_metadata.cpp
|
|
2
2
|
|
|
3
|
+
#include "../common/debug_log.h"
|
|
3
4
|
#include "./fs_meta.h"
|
|
4
5
|
|
|
5
6
|
#include <CoreFoundation/CoreFoundation.h>
|
|
@@ -86,37 +87,44 @@ public:
|
|
|
86
87
|
|
|
87
88
|
class GetVolumeMetadataWorker : public MetadataWorkerBase {
|
|
88
89
|
public:
|
|
89
|
-
GetVolumeMetadataWorker(const std::string &
|
|
90
|
+
GetVolumeMetadataWorker(const std::string &mountPoint,
|
|
91
|
+
const VolumeMetadataOptions &options,
|
|
90
92
|
const Napi::Promise::Deferred &deferred)
|
|
91
|
-
: MetadataWorkerBase(
|
|
92
|
-
|
|
93
|
-
if (path.empty()) {
|
|
94
|
-
throw FSException("Mount point path cannot be empty");
|
|
95
|
-
}
|
|
96
|
-
}
|
|
93
|
+
: MetadataWorkerBase(mountPoint, deferred), options_(options) {}
|
|
97
94
|
|
|
98
95
|
void Execute() override {
|
|
96
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] Executing for mount point: %s",
|
|
97
|
+
mountPoint.c_str());
|
|
99
98
|
try {
|
|
100
99
|
if (!GetBasicVolumeInfo()) {
|
|
101
100
|
return;
|
|
102
101
|
}
|
|
103
102
|
GetDiskArbitrationInfo();
|
|
104
103
|
} catch (const std::exception &e) {
|
|
104
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] Exception: %s", e.what());
|
|
105
105
|
SetError(e.what());
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
private:
|
|
110
|
+
VolumeMetadataOptions options_;
|
|
111
|
+
|
|
110
112
|
bool GetBasicVolumeInfo() {
|
|
113
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] Getting basic volume info for: %s",
|
|
114
|
+
mountPoint.c_str());
|
|
111
115
|
struct statvfs vfs;
|
|
112
116
|
struct statfs fs;
|
|
113
117
|
|
|
114
118
|
if (statvfs(mountPoint.c_str(), &vfs) != 0) {
|
|
119
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] statvfs failed: %s (%d)",
|
|
120
|
+
strerror(errno), errno);
|
|
115
121
|
SetError(CreateErrorMessage("statvfs", errno));
|
|
116
122
|
return false;
|
|
117
123
|
}
|
|
118
124
|
|
|
119
125
|
if (statfs(mountPoint.c_str(), &fs) != 0) {
|
|
126
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] statfs failed: %s (%d)",
|
|
127
|
+
strerror(errno), errno);
|
|
120
128
|
SetError(CreateErrorMessage("statfs", errno));
|
|
121
129
|
return false;
|
|
122
130
|
}
|
|
@@ -138,53 +146,96 @@ private:
|
|
|
138
146
|
metadata.available = static_cast<double>(blockSize * availBlocks);
|
|
139
147
|
metadata.used = static_cast<double>(blockSize * (totalBlocks - freeBlocks));
|
|
140
148
|
|
|
141
|
-
metadata.
|
|
149
|
+
metadata.fstype = fs.f_fstypename;
|
|
142
150
|
metadata.mountFrom = fs.f_mntfromname;
|
|
143
151
|
metadata.mountName = fs.f_mntonname;
|
|
144
152
|
metadata.status = "ready";
|
|
145
153
|
|
|
154
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] Volume info - size: %f, available: "
|
|
155
|
+
"%f, used: %f",
|
|
156
|
+
metadata.size, metadata.available, metadata.used);
|
|
146
157
|
return true;
|
|
147
158
|
}
|
|
148
159
|
|
|
149
160
|
void GetDiskArbitrationInfo() {
|
|
161
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] Getting Disk Arbitration info for: %s",
|
|
162
|
+
mountPoint.c_str());
|
|
163
|
+
|
|
164
|
+
// Check if this is a network filesystem
|
|
165
|
+
if (metadata.fstype == "smbfs" || metadata.fstype == "nfs" ||
|
|
166
|
+
metadata.fstype == "afpfs" || metadata.fstype == "webdav") {
|
|
167
|
+
// For network filesystems, we consider them healthy even without DA info
|
|
168
|
+
metadata.remote = true;
|
|
169
|
+
metadata.status = "healthy";
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
150
173
|
CFReleaser<DASessionRef> session(DASessionCreate(kCFAllocatorDefault));
|
|
151
174
|
if (!session.isValid()) {
|
|
175
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] Failed to create DA session");
|
|
152
176
|
metadata.status = "partial";
|
|
177
|
+
metadata.error = "Failed to create DA session";
|
|
153
178
|
return;
|
|
154
179
|
}
|
|
155
180
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
181
|
+
try {
|
|
182
|
+
// RAII cleanup for RunLoop scheduling
|
|
183
|
+
struct RunLoopCleaner {
|
|
184
|
+
DASessionRef session;
|
|
185
|
+
explicit RunLoopCleaner(DASessionRef s) : session(s) {}
|
|
186
|
+
~RunLoopCleaner() {
|
|
187
|
+
DASessionUnscheduleFromRunLoop(session, CFRunLoopGetCurrent(),
|
|
188
|
+
kCFRunLoopDefaultMode);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Schedule session with RunLoop
|
|
193
|
+
DASessionScheduleWithRunLoop(session.get(), CFRunLoopGetCurrent(),
|
|
194
|
+
kCFRunLoopDefaultMode);
|
|
195
|
+
|
|
196
|
+
auto scopeGuard = std::make_unique<RunLoopCleaner>(session.get());
|
|
197
|
+
|
|
198
|
+
CFReleaser<DADiskRef> disk(DADiskCreateFromBSDName(
|
|
199
|
+
kCFAllocatorDefault, session.get(), metadata.mountFrom.c_str()));
|
|
200
|
+
|
|
201
|
+
if (!disk.isValid()) {
|
|
202
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] Failed to create disk reference");
|
|
203
|
+
metadata.status = "partial";
|
|
204
|
+
metadata.error = "Failed to create disk reference";
|
|
205
|
+
return;
|
|
167
206
|
}
|
|
168
|
-
} runLoopCleaner(session.get());
|
|
169
207
|
|
|
170
|
-
|
|
171
|
-
|
|
208
|
+
CFReleaser<CFDictionaryRef> description(
|
|
209
|
+
DADiskCopyDescription(disk.get()));
|
|
210
|
+
if (!description.isValid()) {
|
|
211
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] Failed to get disk description");
|
|
212
|
+
metadata.status = "partial";
|
|
213
|
+
metadata.error = "Failed to get disk description";
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
172
216
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
217
|
+
ProcessDiskDescription(description.get());
|
|
218
|
+
|
|
219
|
+
// Only set ready if we got this far without errors
|
|
220
|
+
if (metadata.status != "partial") {
|
|
221
|
+
metadata.status = "healthy";
|
|
222
|
+
}
|
|
223
|
+
} catch (const std::exception &e) {
|
|
224
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] Exception: %s", e.what());
|
|
225
|
+
metadata.status = "error";
|
|
226
|
+
metadata.error = e.what();
|
|
176
227
|
}
|
|
228
|
+
}
|
|
177
229
|
|
|
178
|
-
|
|
179
|
-
|
|
230
|
+
void ProcessDiskDescription(CFDictionaryRef description) {
|
|
231
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] Processing disk description");
|
|
232
|
+
if (!description) {
|
|
233
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] Invalid disk description");
|
|
180
234
|
metadata.status = "partial";
|
|
235
|
+
metadata.error = "Invalid disk description";
|
|
181
236
|
return;
|
|
182
237
|
}
|
|
183
238
|
|
|
184
|
-
ProcessDiskDescription(description.get());
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
void ProcessDiskDescription(CFDictionaryRef description) {
|
|
188
239
|
// Get volume name/label
|
|
189
240
|
if (CFStringRef volumeName = (CFStringRef)CFDictionaryGetValue(
|
|
190
241
|
description, kDADiskDescriptionVolumeNameKey)) {
|
|
@@ -205,29 +256,43 @@ private:
|
|
|
205
256
|
}
|
|
206
257
|
|
|
207
258
|
void ProcessNetworkVolume(CFDictionaryRef description) {
|
|
259
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] Processing network volume");
|
|
208
260
|
CFBooleanRef isNetworkVolume = (CFBooleanRef)CFDictionaryGetValue(
|
|
209
261
|
description, kDADiskDescriptionVolumeNetworkKey);
|
|
210
|
-
|
|
211
262
|
metadata.remote = CFBooleanGetValue(isNetworkVolume);
|
|
263
|
+
|
|
212
264
|
CFURLRef url = (CFURLRef)CFDictionaryGetValue(
|
|
213
265
|
description, kDADiskDescriptionVolumePathKey);
|
|
214
266
|
if (!url) {
|
|
267
|
+
metadata.error = "Invalid URL";
|
|
215
268
|
return;
|
|
216
269
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (!urlString) {
|
|
270
|
+
CFReleaser<CFStringRef> urlString(
|
|
271
|
+
CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle));
|
|
272
|
+
if (!urlString.isValid()) {
|
|
273
|
+
metadata.error = std::string("Invalid URL string: ") +
|
|
274
|
+
CFStringToString(urlString.get());
|
|
220
275
|
return;
|
|
221
276
|
}
|
|
222
|
-
|
|
277
|
+
|
|
278
|
+
DEBUG_LOG("[GetVolumeMetadataWorker] URL path: %s",
|
|
279
|
+
CFStringToString(urlString.get()).c_str());
|
|
280
|
+
metadata.uri = CFStringToString(urlString.get());
|
|
223
281
|
}
|
|
224
282
|
};
|
|
225
283
|
|
|
226
|
-
Napi::Value GetVolumeMetadata(const Napi::
|
|
227
|
-
|
|
228
|
-
|
|
284
|
+
Napi::Value GetVolumeMetadata(const Napi::CallbackInfo &info) {
|
|
285
|
+
auto env = info.Env();
|
|
286
|
+
DEBUG_LOG("[GetVolumeMetadata] called");
|
|
287
|
+
|
|
288
|
+
VolumeMetadataOptions options;
|
|
289
|
+
if (info.Length() > 0 && info[0].IsObject()) {
|
|
290
|
+
options = VolumeMetadataOptions::FromObject(info[0].As<Napi::Object>());
|
|
291
|
+
}
|
|
292
|
+
|
|
229
293
|
auto deferred = Napi::Promise::Deferred::New(env);
|
|
230
|
-
auto *worker =
|
|
294
|
+
auto *worker =
|
|
295
|
+
new GetVolumeMetadataWorker(options.mountPoint, options, deferred);
|
|
231
296
|
worker->Queue();
|
|
232
297
|
return deferred.Promise();
|
|
233
298
|
}
|
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
// src/darwin/volume_mount_points.cpp
|
|
2
|
-
|
|
3
|
-
#include "
|
|
2
|
+
#include "../common/volume_mount_points.h"
|
|
3
|
+
#include "../common/debug_log.h"
|
|
4
|
+
#include "fs_meta.h"
|
|
5
|
+
#include <chrono>
|
|
6
|
+
#include <future>
|
|
4
7
|
#include <sys/mount.h>
|
|
5
|
-
#include <
|
|
6
|
-
#include <vector>
|
|
8
|
+
#include <unistd.h>
|
|
7
9
|
|
|
8
10
|
namespace FSMeta {
|
|
9
11
|
|
|
10
12
|
class GetVolumeMountPointsWorker : public Napi::AsyncWorker {
|
|
13
|
+
private:
|
|
14
|
+
Napi::Promise::Deferred deferred_;
|
|
15
|
+
std::vector<MountPoint> mountPoints_;
|
|
16
|
+
uint32_t timeoutMs_;
|
|
17
|
+
|
|
11
18
|
public:
|
|
12
|
-
GetVolumeMountPointsWorker(const Napi::Promise::Deferred &deferred
|
|
13
|
-
|
|
19
|
+
GetVolumeMountPointsWorker(const Napi::Promise::Deferred &deferred,
|
|
20
|
+
uint32_t timeoutMs = 5000)
|
|
21
|
+
: Napi::AsyncWorker(deferred.Env()), deferred_(deferred),
|
|
22
|
+
timeoutMs_(timeoutMs) {}
|
|
14
23
|
|
|
15
24
|
void Execute() override {
|
|
25
|
+
DEBUG_LOG("[GetVolumeMountPointsWorker] Executing");
|
|
16
26
|
try {
|
|
27
|
+
// Get mount list - this is fast
|
|
17
28
|
struct statfs *mntbufp;
|
|
18
29
|
int count = getmntinfo(&mntbufp, MNT_WAIT);
|
|
19
30
|
|
|
@@ -22,42 +33,90 @@ public:
|
|
|
22
33
|
}
|
|
23
34
|
|
|
24
35
|
for (int i = 0; i < count; i++) {
|
|
25
|
-
MountPoint
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
36
|
+
MountPoint mp;
|
|
37
|
+
mp.mountPoint = mntbufp[i].f_mntonname;
|
|
38
|
+
mp.fstype = mntbufp[i].f_fstypename;
|
|
39
|
+
mp.error = ""; // Initialize error field
|
|
40
|
+
|
|
41
|
+
DEBUG_LOG("[GetVolumeMountPointsWorker] Checking mount point: %s",
|
|
42
|
+
mp.mountPoint.c_str());
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
// Use shared_future to allow multiple gets
|
|
46
|
+
std::shared_future<bool> future =
|
|
47
|
+
std::async(std::launch::async, [path = mp.mountPoint]() {
|
|
48
|
+
return access(path.c_str(), R_OK) == 0;
|
|
49
|
+
}).share();
|
|
50
|
+
|
|
51
|
+
auto status = future.wait_for(std::chrono::milliseconds(timeoutMs_));
|
|
52
|
+
|
|
53
|
+
if (status == std::future_status::timeout) {
|
|
54
|
+
mp.status = "disconnected";
|
|
55
|
+
mp.error = "Access check timed out";
|
|
56
|
+
DEBUG_LOG(
|
|
57
|
+
"[GetVolumeMountPointsWorker] Access check timed out for: %s",
|
|
58
|
+
mp.mountPoint.c_str());
|
|
59
|
+
} else if (status == std::future_status::ready) {
|
|
60
|
+
try {
|
|
61
|
+
bool isAccessible = future.get();
|
|
62
|
+
mp.status = isAccessible ? "healthy" : "inaccessible";
|
|
63
|
+
if (!isAccessible) {
|
|
64
|
+
mp.error = "Path is not accessible";
|
|
65
|
+
}
|
|
66
|
+
DEBUG_LOG("[GetVolumeMountPointsWorker] Access check %s for: %s",
|
|
67
|
+
isAccessible ? "succeeded" : "failed",
|
|
68
|
+
mp.mountPoint.c_str());
|
|
69
|
+
} catch (const std::exception &e) {
|
|
70
|
+
mp.status = "error";
|
|
71
|
+
mp.error = std::string("Access check failed: ") + e.what();
|
|
72
|
+
DEBUG_LOG("[GetVolumeMountPointsWorker] Exception: %s", e.what());
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
mp.status = "error";
|
|
76
|
+
mp.error = "Unexpected future status";
|
|
77
|
+
DEBUG_LOG(
|
|
78
|
+
"[GetVolumeMountPointsWorker] Unexpected future status for: %s",
|
|
79
|
+
mp.mountPoint.c_str());
|
|
80
|
+
}
|
|
81
|
+
} catch (const std::exception &e) {
|
|
82
|
+
mp.status = "error";
|
|
83
|
+
mp.error = std::string("Mount point check failed: ") + e.what();
|
|
84
|
+
DEBUG_LOG("[GetVolumeMountPointsWorker] Exception: %s", e.what());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
mountPoints_.push_back(std::move(mp));
|
|
29
88
|
}
|
|
30
89
|
} catch (const std::exception &e) {
|
|
31
|
-
SetError(e.what());
|
|
90
|
+
SetError(std::string("Failed to process mount points: ") + e.what());
|
|
91
|
+
DEBUG_LOG("[GetVolumeMountPointsWorker] Exception: %s", e.what());
|
|
32
92
|
}
|
|
33
93
|
}
|
|
34
94
|
|
|
35
95
|
void OnOK() override {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
point.Set("fstype", mountPoints[i].fstype);
|
|
43
|
-
result.Set(i, point);
|
|
96
|
+
DEBUG_LOG("[GetVolumeMountPointsWorker] OnOK");
|
|
97
|
+
auto env = Env();
|
|
98
|
+
auto result = Napi::Array::New(env, mountPoints_.size());
|
|
99
|
+
|
|
100
|
+
for (size_t i = 0; i < mountPoints_.size(); i++) {
|
|
101
|
+
result[i] = mountPoints_[i].ToObject(env);
|
|
44
102
|
}
|
|
45
103
|
|
|
46
104
|
deferred_.Resolve(result);
|
|
47
105
|
}
|
|
48
|
-
|
|
49
|
-
void OnError(const Napi::Error &error) override {
|
|
50
|
-
deferred_.Reject(error.Value());
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
private:
|
|
54
|
-
std::vector<MountPoint> mountPoints;
|
|
55
|
-
Napi::Promise::Deferred deferred_;
|
|
56
106
|
};
|
|
57
107
|
|
|
58
|
-
Napi::
|
|
108
|
+
Napi::Promise GetVolumeMountPoints(const Napi::CallbackInfo &info) {
|
|
109
|
+
auto env = info.Env();
|
|
110
|
+
DEBUG_LOG("[GetVolumeMountPoints] called");
|
|
111
|
+
|
|
59
112
|
auto deferred = Napi::Promise::Deferred::New(env);
|
|
60
|
-
|
|
113
|
+
|
|
114
|
+
MountPointOptions options;
|
|
115
|
+
if (info.Length() > 0 && info[0].IsObject()) {
|
|
116
|
+
options = MountPointOptions::FromObject(info[0].As<Napi::Object>());
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
auto *worker = new GetVolumeMountPointsWorker(deferred, options.timeoutMs);
|
|
61
120
|
worker->Queue();
|
|
62
121
|
return deferred.Promise();
|
|
63
122
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// src/linux/blkid_cache.cpp
|
|
2
|
+
|
|
2
3
|
#include "blkid_cache.h"
|
|
4
|
+
#include "../common/debug_log.h"
|
|
3
5
|
#include <stdexcept>
|
|
4
6
|
|
|
5
7
|
namespace FSMeta {
|
|
@@ -10,19 +12,25 @@ std::mutex BlkidCache::mutex_;
|
|
|
10
12
|
// Constructor: Initializes the blkid cache with proper error handling
|
|
11
13
|
BlkidCache::BlkidCache() : cache_(nullptr) {
|
|
12
14
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
15
|
+
DEBUG_LOG("[BlkidCache] initializing cache");
|
|
13
16
|
if (blkid_get_cache(&cache_, nullptr) != 0) {
|
|
17
|
+
DEBUG_LOG("[BlkidCache] failed to initialize cache");
|
|
14
18
|
throw std::runtime_error("Failed to initialize blkid cache");
|
|
15
19
|
}
|
|
20
|
+
DEBUG_LOG("[BlkidCache] cache initialized successfully");
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
// Destructor: Safely releases the blkid cache resource
|
|
19
24
|
BlkidCache::~BlkidCache() {
|
|
20
25
|
if (cache_) {
|
|
21
26
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
27
|
+
DEBUG_LOG("[BlkidCache] releasing cache");
|
|
22
28
|
try {
|
|
23
29
|
blkid_put_cache(cache_);
|
|
24
30
|
cache_ = nullptr; // Avoid double-release
|
|
31
|
+
DEBUG_LOG("[BlkidCache] cache released successfully");
|
|
25
32
|
} catch (const std::exception &e) {
|
|
33
|
+
DEBUG_LOG("[BlkidCache] error releasing cache: %s", e.what());
|
|
26
34
|
// Optional: Log error during cache cleanup
|
|
27
35
|
// std::cerr << "Error while releasing blkid cache: " << e.what()
|
|
28
36
|
// << std::endl;
|
|
@@ -33,6 +41,7 @@ BlkidCache::~BlkidCache() {
|
|
|
33
41
|
// Accessor for blkid cache
|
|
34
42
|
blkid_cache BlkidCache::get() {
|
|
35
43
|
if (!cache_) {
|
|
44
|
+
DEBUG_LOG("[BlkidCache] attempted to access uninitialized cache");
|
|
36
45
|
throw std::runtime_error(
|
|
37
46
|
"blkid cache is uninitialized or has been released");
|
|
38
47
|
}
|
package/src/linux/blkid_cache.h
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/linux/gio_mount_points.cpp
|
|
2
|
+
#ifdef ENABLE_GIO
|
|
3
|
+
|
|
4
|
+
#include "gio_mount_points.h"
|
|
5
|
+
#include "../common/debug_log.h"
|
|
6
|
+
#include "gio_utils.h"
|
|
7
|
+
#include <gio/gio.h>
|
|
8
|
+
#include <memory>
|
|
9
|
+
#include <stdexcept>
|
|
10
|
+
|
|
11
|
+
namespace FSMeta {
|
|
12
|
+
namespace gio {
|
|
13
|
+
|
|
14
|
+
GioMountPointsWorker::GioMountPointsWorker(
|
|
15
|
+
const Napi::Promise::Deferred &deferred)
|
|
16
|
+
: Napi::AsyncWorker(deferred.Env()), deferred_(deferred) {}
|
|
17
|
+
|
|
18
|
+
GioMountPointsWorker::~GioMountPointsWorker() { mountPoints.clear(); }
|
|
19
|
+
|
|
20
|
+
void GioMountPointsWorker::Execute() {
|
|
21
|
+
try {
|
|
22
|
+
DEBUG_LOG("[GioMountPoints] processing mounts");
|
|
23
|
+
|
|
24
|
+
MountIterator::forEachMount([this](GMount *mount, GFile *root) {
|
|
25
|
+
GCharPtr path(g_file_get_path(root));
|
|
26
|
+
if (path) {
|
|
27
|
+
GFileInfoPtr info(g_file_query_filesystem_info(
|
|
28
|
+
root, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, nullptr, nullptr));
|
|
29
|
+
if (info) {
|
|
30
|
+
const char *fs_type_str = g_file_info_get_attribute_string(
|
|
31
|
+
info.get(), G_FILE_ATTRIBUTE_FILESYSTEM_TYPE);
|
|
32
|
+
if (fs_type_str) {
|
|
33
|
+
GCharPtr fs_type(g_strdup(fs_type_str));
|
|
34
|
+
DEBUG_LOG("[GioMountPoints] found {mountPoint: %s, fsType: %s}",
|
|
35
|
+
path.get(), fs_type.get());
|
|
36
|
+
MountPoint point{};
|
|
37
|
+
point.mountPoint = path.get();
|
|
38
|
+
point.fstype = fs_type.get();
|
|
39
|
+
mountPoints.push_back(point);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return true; // Continue iteration
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
DEBUG_LOG("[GioMountPoints] found %zu mount points", mountPoints.size());
|
|
47
|
+
} catch (const std::exception &e) {
|
|
48
|
+
DEBUG_LOG("[GioMountPoints] error: %s", e.what());
|
|
49
|
+
SetError(e.what());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
void GioMountPointsWorker::OnOK() {
|
|
54
|
+
Napi::HandleScope scope(Env());
|
|
55
|
+
Napi::Array result = Napi::Array::New(Env());
|
|
56
|
+
|
|
57
|
+
for (size_t i = 0; i < mountPoints.size(); i++) {
|
|
58
|
+
Napi::Object point = Napi::Object::New(Env());
|
|
59
|
+
point.Set("mountPoint", mountPoints[i].mountPoint);
|
|
60
|
+
point.Set("fstype", mountPoints[i].fstype);
|
|
61
|
+
result.Set(i, point);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
deferred_.Resolve(result);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
void GioMountPointsWorker::OnError(const Napi::Error &error) {
|
|
68
|
+
deferred_.Reject(error.Value());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Napi::Value GetMountPoints(Napi::Env env) {
|
|
72
|
+
auto deferred = Napi::Promise::Deferred::New(env);
|
|
73
|
+
auto *worker = new GioMountPointsWorker(deferred);
|
|
74
|
+
worker->Queue();
|
|
75
|
+
return deferred.Promise();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
} // namespace gio
|
|
79
|
+
} // namespace FSMeta
|
|
80
|
+
|
|
81
|
+
#endif // ENABLE_GIO
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// src/linux/
|
|
1
|
+
// src/linux/gio_mount_points.h
|
|
2
2
|
|
|
3
3
|
#pragma once
|
|
4
4
|
|
|
5
5
|
#ifdef ENABLE_GIO
|
|
6
6
|
|
|
7
|
-
#include "../common/
|
|
7
|
+
#include "../common/volume_mount_points.h"
|
|
8
8
|
#include <napi.h>
|
|
9
9
|
#include <string>
|
|
10
10
|
#include <vector>
|
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
namespace FSMeta {
|
|
13
13
|
namespace gio {
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Get mount points asynchronously using GIO
|
|
17
|
+
*/
|
|
18
|
+
Napi::Value GetMountPoints(Napi::Env env);
|
|
19
|
+
|
|
15
20
|
class GioMountPointsWorker : public Napi::AsyncWorker {
|
|
16
21
|
public:
|
|
17
22
|
explicit GioMountPointsWorker(const Napi::Promise::Deferred &deferred);
|