@photostructure/fs-metadata 0.0.1
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/LICENSE.txt +7 -0
- package/README.md +311 -0
- package/binding.gyp +111 -0
- package/package.json +121 -0
- 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/scripts/configure.mjs +36 -0
- package/src/binding.cpp +68 -0
- package/src/common/error_utils.h +19 -0
- package/src/common/metadata_worker.h +69 -0
- package/src/common/mount_point.h +13 -0
- package/src/common/volume_metadata.h +34 -0
- package/src/common/volume_mount_points.h +8 -0
- package/src/darwin/fs_meta.h +18 -0
- package/src/darwin/volume_metadata.cpp +235 -0
- package/src/darwin/volume_mount_points.cpp +65 -0
- package/src/linux/blkid_cache.cpp +42 -0
- package/src/linux/blkid_cache.h +27 -0
- package/src/linux/gio_utils.cpp +151 -0
- package/src/linux/gio_utils.h +27 -0
- package/src/linux/gio_worker.cpp +87 -0
- package/src/linux/gio_worker.h +32 -0
- package/src/linux/volume_metadata.cpp +92 -0
- package/src/windows/error_utils.h +22 -0
- package/src/windows/fs_meta.h +43 -0
- package/src/windows/volume_metadata.cpp +194 -0
- package/src/windows/volume_mount_points.cpp +62 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// src/common/metadata_worker.h
|
|
2
|
+
#pragma once
|
|
3
|
+
#include "./volume_metadata.h"
|
|
4
|
+
#include <napi.h>
|
|
5
|
+
|
|
6
|
+
namespace FSMeta {
|
|
7
|
+
|
|
8
|
+
class MetadataWorkerBase : public Napi::AsyncWorker {
|
|
9
|
+
protected:
|
|
10
|
+
std::string mountPoint;
|
|
11
|
+
VolumeMetadata metadata;
|
|
12
|
+
Napi::Promise::Deferred deferred_;
|
|
13
|
+
|
|
14
|
+
MetadataWorkerBase(const std::string &path,
|
|
15
|
+
const Napi::Promise::Deferred &deferred)
|
|
16
|
+
: Napi::AsyncWorker(deferred.Env()), mountPoint(path),
|
|
17
|
+
deferred_(deferred) {}
|
|
18
|
+
|
|
19
|
+
void OnError(const Napi::Error &error) override {
|
|
20
|
+
deferred_.Reject(error.Value());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Creates a JavaScript object from VolumeMetadata
|
|
24
|
+
Napi::Object CreateResultObject() {
|
|
25
|
+
auto env = Env();
|
|
26
|
+
auto result = Napi::Object::New(env);
|
|
27
|
+
|
|
28
|
+
// Match the current VolumeMetadata struct members
|
|
29
|
+
result.Set("label", metadata.label.empty()
|
|
30
|
+
? env.Null()
|
|
31
|
+
: Napi::String::New(env, metadata.label));
|
|
32
|
+
result.Set("fileSystem", metadata.fileSystem.empty()
|
|
33
|
+
? env.Null()
|
|
34
|
+
: Napi::String::New(env, metadata.fileSystem));
|
|
35
|
+
result.Set("size", Napi::Number::New(env, metadata.size));
|
|
36
|
+
result.Set("used", Napi::Number::New(env, metadata.used));
|
|
37
|
+
result.Set("available", Napi::Number::New(env, metadata.available));
|
|
38
|
+
result.Set("uuid", metadata.uuid.empty()
|
|
39
|
+
? env.Null()
|
|
40
|
+
: Napi::String::New(env, metadata.uuid));
|
|
41
|
+
result.Set("mountFrom", metadata.mountFrom.empty()
|
|
42
|
+
? env.Null()
|
|
43
|
+
: Napi::String::New(env, metadata.mountFrom));
|
|
44
|
+
result.Set("mountName", metadata.mountName.empty()
|
|
45
|
+
? env.Null()
|
|
46
|
+
: Napi::String::New(env, metadata.mountName));
|
|
47
|
+
result.Set("uri", metadata.uri.empty()
|
|
48
|
+
? env.Null()
|
|
49
|
+
: Napi::String::New(env, metadata.uri));
|
|
50
|
+
result.Set("status", Napi::String::New(env, metadata.status));
|
|
51
|
+
|
|
52
|
+
if (metadata.remote) {
|
|
53
|
+
result.Set("remote", Napi::Boolean::New(env, metadata.remote));
|
|
54
|
+
}
|
|
55
|
+
result.Set("remoteHost", metadata.remoteHost.empty()
|
|
56
|
+
? env.Null()
|
|
57
|
+
: Napi::String::New(env, metadata.remoteHost));
|
|
58
|
+
result.Set("remoteShare",
|
|
59
|
+
metadata.remoteShare.empty()
|
|
60
|
+
? env.Null()
|
|
61
|
+
: Napi::String::New(env, metadata.remoteShare));
|
|
62
|
+
|
|
63
|
+
return result;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
void OnOK() override { deferred_.Resolve(CreateResultObject()); }
|
|
67
|
+
}; // class MetadataWorkerBase
|
|
68
|
+
|
|
69
|
+
} // namespace FSMeta
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// src/common/volume_metadata.h
|
|
2
|
+
#pragma once
|
|
3
|
+
#include <napi.h>
|
|
4
|
+
#include <string>
|
|
5
|
+
|
|
6
|
+
namespace FSMeta {
|
|
7
|
+
|
|
8
|
+
// Volume metadata structure
|
|
9
|
+
struct VolumeMetadata {
|
|
10
|
+
std::string label;
|
|
11
|
+
std::string fileSystem;
|
|
12
|
+
double size;
|
|
13
|
+
double used;
|
|
14
|
+
double available;
|
|
15
|
+
std::string uuid;
|
|
16
|
+
std::string mountFrom;
|
|
17
|
+
std::string mountName;
|
|
18
|
+
std::string uri;
|
|
19
|
+
std::string status;
|
|
20
|
+
bool remote = false;
|
|
21
|
+
std::string remoteHost;
|
|
22
|
+
std::string remoteShare;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
struct VolumeMetadataOptions {
|
|
26
|
+
uint32_t timeoutMs = 5000;
|
|
27
|
+
std::string device;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
Napi::Value GetVolumeMetadata(const Napi::Env &env,
|
|
31
|
+
const std::string &mountPoint,
|
|
32
|
+
const Napi::Object &options);
|
|
33
|
+
|
|
34
|
+
} // namespace FSMeta
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/darwin/fs_meta.h
|
|
2
|
+
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include "../common/error_utils.h"
|
|
6
|
+
#include "../common/metadata_worker.h"
|
|
7
|
+
#include "../common/mount_point.h"
|
|
8
|
+
#include "../common/volume_mount_points.h"
|
|
9
|
+
|
|
10
|
+
namespace FSMeta {
|
|
11
|
+
|
|
12
|
+
// Forward declarations of the main interface functions
|
|
13
|
+
Napi::Value GetVolumeMountPoints(Napi::Env env);
|
|
14
|
+
Napi::Value GetVolumeMetadata(const Napi::Env &env,
|
|
15
|
+
const std::string &mountPoint,
|
|
16
|
+
const Napi::Object &options);
|
|
17
|
+
|
|
18
|
+
} // namespace FSMeta
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// src/darwin/volume_metadata.cpp
|
|
2
|
+
|
|
3
|
+
#include "./fs_meta.h"
|
|
4
|
+
|
|
5
|
+
#include <CoreFoundation/CoreFoundation.h>
|
|
6
|
+
#include <DiskArbitration/DiskArbitration.h>
|
|
7
|
+
#include <IOKit/IOBSD.h>
|
|
8
|
+
#include <IOKit/storage/IOMedia.h>
|
|
9
|
+
#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
|
|
10
|
+
#include <memory>
|
|
11
|
+
#include <string>
|
|
12
|
+
#include <sys/mount.h>
|
|
13
|
+
#include <sys/param.h>
|
|
14
|
+
#include <sys/statvfs.h>
|
|
15
|
+
|
|
16
|
+
namespace FSMeta {
|
|
17
|
+
|
|
18
|
+
// Helper function to convert CFString to std::string
|
|
19
|
+
static std::string CFStringToString(CFStringRef cfString) {
|
|
20
|
+
if (!cfString) {
|
|
21
|
+
return "";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
CFIndex length = CFStringGetLength(cfString);
|
|
25
|
+
CFIndex maxSize =
|
|
26
|
+
CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
|
|
27
|
+
std::string result(maxSize, '\0');
|
|
28
|
+
|
|
29
|
+
if (!CFStringGetCString(cfString, &result[0], maxSize,
|
|
30
|
+
kCFStringEncodingUTF8)) {
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
result.resize(strlen(result.c_str()));
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Improved CFReleaser with proper Core Foundation support
|
|
39
|
+
template <typename T> class CFReleaser {
|
|
40
|
+
private:
|
|
41
|
+
T ref_;
|
|
42
|
+
|
|
43
|
+
public:
|
|
44
|
+
explicit CFReleaser(T ref = nullptr) noexcept : ref_(ref) {}
|
|
45
|
+
|
|
46
|
+
// Delete copy operations
|
|
47
|
+
CFReleaser(const CFReleaser &) = delete;
|
|
48
|
+
CFReleaser &operator=(const CFReleaser &) = delete;
|
|
49
|
+
|
|
50
|
+
// Move operations
|
|
51
|
+
CFReleaser(CFReleaser &&other) noexcept : ref_(other.ref_) {
|
|
52
|
+
other.ref_ = nullptr;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
CFReleaser &operator=(CFReleaser &&other) noexcept {
|
|
56
|
+
if (this != &other) {
|
|
57
|
+
reset();
|
|
58
|
+
ref_ = other.ref_;
|
|
59
|
+
other.ref_ = nullptr;
|
|
60
|
+
}
|
|
61
|
+
return *this;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
~CFReleaser() { reset(); }
|
|
65
|
+
|
|
66
|
+
void reset(T ref = nullptr) {
|
|
67
|
+
if (ref_) {
|
|
68
|
+
CFRelease(ref_);
|
|
69
|
+
}
|
|
70
|
+
ref_ = ref;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Implicit conversion operator for Core Foundation APIs
|
|
74
|
+
operator T() const noexcept { return ref_; }
|
|
75
|
+
|
|
76
|
+
T get() const noexcept { return ref_; }
|
|
77
|
+
bool isValid() const noexcept { return ref_ != nullptr; }
|
|
78
|
+
|
|
79
|
+
// Release ownership
|
|
80
|
+
T release() noexcept {
|
|
81
|
+
T temp = ref_;
|
|
82
|
+
ref_ = nullptr;
|
|
83
|
+
return temp;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
class GetVolumeMetadataWorker : public MetadataWorkerBase {
|
|
88
|
+
public:
|
|
89
|
+
GetVolumeMetadataWorker(const std::string &path,
|
|
90
|
+
const Napi::Promise::Deferred &deferred)
|
|
91
|
+
: MetadataWorkerBase(path, deferred) {
|
|
92
|
+
|
|
93
|
+
if (path.empty()) {
|
|
94
|
+
throw FSException("Mount point path cannot be empty");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
void Execute() override {
|
|
99
|
+
try {
|
|
100
|
+
if (!GetBasicVolumeInfo()) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
GetDiskArbitrationInfo();
|
|
104
|
+
} catch (const std::exception &e) {
|
|
105
|
+
SetError(e.what());
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private:
|
|
110
|
+
bool GetBasicVolumeInfo() {
|
|
111
|
+
struct statvfs vfs;
|
|
112
|
+
struct statfs fs;
|
|
113
|
+
|
|
114
|
+
if (statvfs(mountPoint.c_str(), &vfs) != 0) {
|
|
115
|
+
SetError(CreateErrorMessage("statvfs", errno));
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (statfs(mountPoint.c_str(), &fs) != 0) {
|
|
120
|
+
SetError(CreateErrorMessage("statfs", errno));
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Calculate sizes using uint64_t to prevent overflow
|
|
125
|
+
const uint64_t blockSize = vfs.f_frsize ? vfs.f_frsize : vfs.f_bsize;
|
|
126
|
+
const uint64_t totalBlocks = vfs.f_blocks;
|
|
127
|
+
const uint64_t availBlocks = vfs.f_bavail;
|
|
128
|
+
const uint64_t freeBlocks = vfs.f_bfree;
|
|
129
|
+
|
|
130
|
+
// Check for overflow before multiplication
|
|
131
|
+
if (blockSize > 0 &&
|
|
132
|
+
totalBlocks > std::numeric_limits<uint64_t>::max() / blockSize) {
|
|
133
|
+
SetError("Volume size calculation would overflow");
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
metadata.size = static_cast<double>(blockSize * totalBlocks);
|
|
138
|
+
metadata.available = static_cast<double>(blockSize * availBlocks);
|
|
139
|
+
metadata.used = static_cast<double>(blockSize * (totalBlocks - freeBlocks));
|
|
140
|
+
|
|
141
|
+
metadata.fileSystem = fs.f_fstypename;
|
|
142
|
+
metadata.mountFrom = fs.f_mntfromname;
|
|
143
|
+
metadata.mountName = fs.f_mntonname;
|
|
144
|
+
metadata.status = "ready";
|
|
145
|
+
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
void GetDiskArbitrationInfo() {
|
|
150
|
+
CFReleaser<DASessionRef> session(DASessionCreate(kCFAllocatorDefault));
|
|
151
|
+
if (!session.isValid()) {
|
|
152
|
+
metadata.status = "partial";
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Schedule session with RunLoop
|
|
157
|
+
DASessionScheduleWithRunLoop(session.get(), CFRunLoopGetCurrent(),
|
|
158
|
+
kCFRunLoopDefaultMode);
|
|
159
|
+
|
|
160
|
+
// RAII cleanup for RunLoop scheduling
|
|
161
|
+
struct RunLoopCleaner {
|
|
162
|
+
DASessionRef session;
|
|
163
|
+
explicit RunLoopCleaner(DASessionRef s) : session(s) {}
|
|
164
|
+
~RunLoopCleaner() {
|
|
165
|
+
DASessionUnscheduleFromRunLoop(session, CFRunLoopGetCurrent(),
|
|
166
|
+
kCFRunLoopDefaultMode);
|
|
167
|
+
}
|
|
168
|
+
} runLoopCleaner(session.get());
|
|
169
|
+
|
|
170
|
+
CFReleaser<DADiskRef> disk(DADiskCreateFromBSDName(
|
|
171
|
+
kCFAllocatorDefault, session.get(), metadata.mountFrom.c_str()));
|
|
172
|
+
|
|
173
|
+
if (!disk.isValid()) {
|
|
174
|
+
metadata.status = "partial";
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
CFReleaser<CFDictionaryRef> description(DADiskCopyDescription(disk.get()));
|
|
179
|
+
if (!description.isValid()) {
|
|
180
|
+
metadata.status = "partial";
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
ProcessDiskDescription(description.get());
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
void ProcessDiskDescription(CFDictionaryRef description) {
|
|
188
|
+
// Get volume name/label
|
|
189
|
+
if (CFStringRef volumeName = (CFStringRef)CFDictionaryGetValue(
|
|
190
|
+
description, kDADiskDescriptionVolumeNameKey)) {
|
|
191
|
+
metadata.label = CFStringToString(volumeName);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Get UUID
|
|
195
|
+
if (CFUUIDRef uuid = (CFUUIDRef)CFDictionaryGetValue(
|
|
196
|
+
description, kDADiskDescriptionVolumeUUIDKey)) {
|
|
197
|
+
CFReleaser<CFStringRef> uuidString(
|
|
198
|
+
CFUUIDCreateString(kCFAllocatorDefault, uuid));
|
|
199
|
+
if (uuidString.isValid()) {
|
|
200
|
+
metadata.uuid = CFStringToString(uuidString.get());
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
ProcessNetworkVolume(description);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
void ProcessNetworkVolume(CFDictionaryRef description) {
|
|
208
|
+
CFBooleanRef isNetworkVolume = (CFBooleanRef)CFDictionaryGetValue(
|
|
209
|
+
description, kDADiskDescriptionVolumeNetworkKey);
|
|
210
|
+
|
|
211
|
+
metadata.remote = CFBooleanGetValue(isNetworkVolume);
|
|
212
|
+
CFURLRef url = (CFURLRef)CFDictionaryGetValue(
|
|
213
|
+
description, kDADiskDescriptionVolumePathKey);
|
|
214
|
+
if (!url) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
CFStringRef urlString = CFURLGetString(url);
|
|
219
|
+
if (!urlString) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
metadata.uri = CFStringToString(urlString);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
Napi::Value GetVolumeMetadata(const Napi::Env &env,
|
|
227
|
+
const std::string &mountPoint,
|
|
228
|
+
const Napi::Object &options) {
|
|
229
|
+
auto deferred = Napi::Promise::Deferred::New(env);
|
|
230
|
+
auto *worker = new GetVolumeMetadataWorker(mountPoint, deferred);
|
|
231
|
+
worker->Queue();
|
|
232
|
+
return deferred.Promise();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
} // namespace FSMeta
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// src/darwin/volume_mount_points.cpp
|
|
2
|
+
|
|
3
|
+
#include "./fs_meta.h"
|
|
4
|
+
#include <sys/mount.h>
|
|
5
|
+
#include <sys/param.h>
|
|
6
|
+
#include <vector>
|
|
7
|
+
|
|
8
|
+
namespace FSMeta {
|
|
9
|
+
|
|
10
|
+
class GetVolumeMountPointsWorker : public Napi::AsyncWorker {
|
|
11
|
+
public:
|
|
12
|
+
GetVolumeMountPointsWorker(const Napi::Promise::Deferred &deferred)
|
|
13
|
+
: Napi::AsyncWorker(deferred.Env()), deferred_(deferred) {}
|
|
14
|
+
|
|
15
|
+
void Execute() override {
|
|
16
|
+
try {
|
|
17
|
+
struct statfs *mntbufp;
|
|
18
|
+
int count = getmntinfo(&mntbufp, MNT_WAIT);
|
|
19
|
+
|
|
20
|
+
if (count <= 0) {
|
|
21
|
+
throw std::runtime_error("Failed to get mount information");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
for (int i = 0; i < count; i++) {
|
|
25
|
+
MountPoint point;
|
|
26
|
+
point.mountPoint = mntbufp[i].f_mntonname;
|
|
27
|
+
point.fstype = mntbufp[i].f_fstypename;
|
|
28
|
+
mountPoints.push_back(point);
|
|
29
|
+
}
|
|
30
|
+
} catch (const std::exception &e) {
|
|
31
|
+
SetError(e.what());
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
void OnOK() override {
|
|
36
|
+
Napi::HandleScope scope(Env());
|
|
37
|
+
Napi::Array result = Napi::Array::New(Env());
|
|
38
|
+
|
|
39
|
+
for (size_t i = 0; i < mountPoints.size(); i++) {
|
|
40
|
+
Napi::Object point = Napi::Object::New(Env());
|
|
41
|
+
point.Set("mountPoint", mountPoints[i].mountPoint);
|
|
42
|
+
point.Set("fstype", mountPoints[i].fstype);
|
|
43
|
+
result.Set(i, point);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
deferred_.Resolve(result);
|
|
47
|
+
}
|
|
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
|
+
};
|
|
57
|
+
|
|
58
|
+
Napi::Value GetVolumeMountPoints(Napi::Env env) {
|
|
59
|
+
auto deferred = Napi::Promise::Deferred::New(env);
|
|
60
|
+
auto *worker = new GetVolumeMountPointsWorker(deferred);
|
|
61
|
+
worker->Queue();
|
|
62
|
+
return deferred.Promise();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
} // namespace FSMeta
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/linux/blkid_cache.cpp
|
|
2
|
+
#include "blkid_cache.h"
|
|
3
|
+
#include <stdexcept>
|
|
4
|
+
|
|
5
|
+
namespace FSMeta {
|
|
6
|
+
|
|
7
|
+
// Define the static mutex
|
|
8
|
+
std::mutex BlkidCache::mutex_;
|
|
9
|
+
|
|
10
|
+
// Constructor: Initializes the blkid cache with proper error handling
|
|
11
|
+
BlkidCache::BlkidCache() : cache_(nullptr) {
|
|
12
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
13
|
+
if (blkid_get_cache(&cache_, nullptr) != 0) {
|
|
14
|
+
throw std::runtime_error("Failed to initialize blkid cache");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Destructor: Safely releases the blkid cache resource
|
|
19
|
+
BlkidCache::~BlkidCache() {
|
|
20
|
+
if (cache_) {
|
|
21
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
22
|
+
try {
|
|
23
|
+
blkid_put_cache(cache_);
|
|
24
|
+
cache_ = nullptr; // Avoid double-release
|
|
25
|
+
} catch (const std::exception &e) {
|
|
26
|
+
// Optional: Log error during cache cleanup
|
|
27
|
+
// std::cerr << "Error while releasing blkid cache: " << e.what()
|
|
28
|
+
// << std::endl;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Accessor for blkid cache
|
|
34
|
+
blkid_cache BlkidCache::get() {
|
|
35
|
+
if (!cache_) {
|
|
36
|
+
throw std::runtime_error(
|
|
37
|
+
"blkid cache is uninitialized or has been released");
|
|
38
|
+
}
|
|
39
|
+
return cache_;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
} // namespace FSMeta
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/linux/blkid_cache.h
|
|
2
|
+
#pragma once
|
|
3
|
+
#include <blkid/blkid.h>
|
|
4
|
+
#include <mutex>
|
|
5
|
+
|
|
6
|
+
namespace FSMeta {
|
|
7
|
+
|
|
8
|
+
// Thread-safe helper class for RAII handling of blkid cache
|
|
9
|
+
class BlkidCache {
|
|
10
|
+
private:
|
|
11
|
+
static std::mutex mutex_;
|
|
12
|
+
blkid_cache cache_;
|
|
13
|
+
|
|
14
|
+
public:
|
|
15
|
+
BlkidCache();
|
|
16
|
+
~BlkidCache();
|
|
17
|
+
|
|
18
|
+
// Returns the blkid cache pointer
|
|
19
|
+
blkid_cache get();
|
|
20
|
+
|
|
21
|
+
operator bool() const { return cache_ != nullptr; }
|
|
22
|
+
|
|
23
|
+
BlkidCache(const BlkidCache &) = delete;
|
|
24
|
+
BlkidCache &operator=(const BlkidCache &) = delete;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
} // namespace FSMeta
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#ifdef ENABLE_GIO
|
|
2
|
+
|
|
3
|
+
#include "gio_utils.h"
|
|
4
|
+
#include "gio_worker.h"
|
|
5
|
+
#include <gio/gio.h>
|
|
6
|
+
#include <iostream>
|
|
7
|
+
#include <memory>
|
|
8
|
+
#include <stdexcept>
|
|
9
|
+
|
|
10
|
+
namespace FSMeta {
|
|
11
|
+
namespace gio {
|
|
12
|
+
|
|
13
|
+
// Custom deleter for GObject types
|
|
14
|
+
struct GObjectDeleter {
|
|
15
|
+
void operator()(void *ptr) const {
|
|
16
|
+
if (ptr) {
|
|
17
|
+
g_object_unref(ptr);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Type aliases for common GObject smart pointers
|
|
23
|
+
using GVolumeMonitorPtr = std::unique_ptr<GVolumeMonitor, GObjectDeleter>;
|
|
24
|
+
using GObjectPtr = std::unique_ptr<GObject, GObjectDeleter>;
|
|
25
|
+
using GFilePtr = std::unique_ptr<GFile, GObjectDeleter>;
|
|
26
|
+
using GVolumePtr = std::unique_ptr<GVolume, GObjectDeleter>;
|
|
27
|
+
using GMountPtr = std::unique_ptr<GMount, GObjectDeleter>;
|
|
28
|
+
using GUriPtr = std::unique_ptr<GUri, GObjectDeleter>;
|
|
29
|
+
|
|
30
|
+
Napi::Value GetMountPoints(Napi::Env env) {
|
|
31
|
+
auto deferred = Napi::Promise::Deferred::New(env);
|
|
32
|
+
auto *worker = new GioMountPointsWorker(deferred);
|
|
33
|
+
worker->Queue();
|
|
34
|
+
return deferred.Promise();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void addMountMetadata(const std::string &mountPoint, VolumeMetadata &metadata) {
|
|
38
|
+
|
|
39
|
+
GVolumeMonitor *monitor = g_volume_monitor_get();
|
|
40
|
+
if (!monitor) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
std::unique_ptr<GVolumeMonitor, GObjectDeleter> monitor_guard(monitor);
|
|
45
|
+
GList *mounts = g_volume_monitor_get_mounts(monitor);
|
|
46
|
+
if (!mounts) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (GList *l = mounts; l != nullptr; l = l->next) {
|
|
51
|
+
GMount *mount = G_MOUNT(l->data);
|
|
52
|
+
if (!mount) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
GFile *root = g_mount_get_root(mount);
|
|
57
|
+
if (!root) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
char *path = g_file_get_path(root);
|
|
62
|
+
|
|
63
|
+
if (path && mountPoint == path) {
|
|
64
|
+
|
|
65
|
+
// Get volume name
|
|
66
|
+
GVolume *volume = g_mount_get_volume(mount);
|
|
67
|
+
if (volume) {
|
|
68
|
+
char *name = g_volume_get_name(volume);
|
|
69
|
+
if (name) {
|
|
70
|
+
metadata.label = name;
|
|
71
|
+
g_free(name);
|
|
72
|
+
}
|
|
73
|
+
g_object_unref(volume);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Get mount name (can include filesystem type info)
|
|
77
|
+
char *mount_name = g_mount_get_name(mount);
|
|
78
|
+
if (mount_name) {
|
|
79
|
+
g_free(mount_name);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Get location and URI
|
|
83
|
+
GFile *location = g_mount_get_default_location(mount);
|
|
84
|
+
if (location) {
|
|
85
|
+
|
|
86
|
+
// Try different URI methods
|
|
87
|
+
char *uri = g_file_get_uri(location);
|
|
88
|
+
char *parse_name = g_file_get_parse_name(location);
|
|
89
|
+
|
|
90
|
+
if (uri) {
|
|
91
|
+
metadata.uri = uri;
|
|
92
|
+
|
|
93
|
+
// Parse URI for remote details
|
|
94
|
+
GError *error = nullptr;
|
|
95
|
+
GUri *parsed_uri = g_uri_parse(uri, G_URI_FLAGS_NONE, &error);
|
|
96
|
+
if (parsed_uri) {
|
|
97
|
+
const char *scheme = g_uri_get_scheme(parsed_uri);
|
|
98
|
+
|
|
99
|
+
if (scheme && strcmp(scheme, "file") != 0) {
|
|
100
|
+
metadata.remote = true;
|
|
101
|
+
metadata.fileSystem = scheme;
|
|
102
|
+
|
|
103
|
+
const char *host = g_uri_get_host(parsed_uri);
|
|
104
|
+
const char *path = g_uri_get_path(parsed_uri);
|
|
105
|
+
|
|
106
|
+
if (host)
|
|
107
|
+
metadata.remoteHost = host;
|
|
108
|
+
if (path && path[0] == '/')
|
|
109
|
+
metadata.remoteShare = path + 1;
|
|
110
|
+
}
|
|
111
|
+
g_uri_unref(parsed_uri);
|
|
112
|
+
} else {
|
|
113
|
+
if (error) {
|
|
114
|
+
g_error_free(error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
g_free(uri);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (parse_name) {
|
|
121
|
+
g_free(parse_name);
|
|
122
|
+
}
|
|
123
|
+
g_object_unref(location);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Try additional methods to get filesystem info
|
|
127
|
+
if (metadata.fileSystem.empty()) {
|
|
128
|
+
GDrive *drive = g_mount_get_drive(mount);
|
|
129
|
+
if (drive) {
|
|
130
|
+
// Try to get unix device path
|
|
131
|
+
char *unix_device = g_drive_get_identifier(
|
|
132
|
+
drive, G_DRIVE_IDENTIFIER_KIND_UNIX_DEVICE);
|
|
133
|
+
if (unix_device) {
|
|
134
|
+
g_free(unix_device);
|
|
135
|
+
}
|
|
136
|
+
g_object_unref(drive);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
g_object_unref(root);
|
|
142
|
+
g_free(path);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
g_list_free_full(mounts, g_object_unref);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
} // namespace gio
|
|
149
|
+
} // namespace FSMeta
|
|
150
|
+
|
|
151
|
+
#endif // ENABLE_GIO
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/linux/gio_utils.h
|
|
2
|
+
#pragma once
|
|
3
|
+
|
|
4
|
+
#ifdef ENABLE_GIO
|
|
5
|
+
|
|
6
|
+
#include "../common/volume_metadata.h"
|
|
7
|
+
#include <napi.h>
|
|
8
|
+
#include <string>
|
|
9
|
+
#include <vector>
|
|
10
|
+
|
|
11
|
+
namespace FSMeta {
|
|
12
|
+
namespace gio {
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get mount points asynchronously using GIO
|
|
16
|
+
*/
|
|
17
|
+
Napi::Value GetMountPoints(Napi::Env env);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Add metadata from GIO to the volume metadata
|
|
21
|
+
*/
|
|
22
|
+
void addMountMetadata(const std::string &mountPoint, VolumeMetadata &metadata);
|
|
23
|
+
|
|
24
|
+
} // namespace gio
|
|
25
|
+
} // namespace FSMeta
|
|
26
|
+
|
|
27
|
+
#endif // ENABLE_GIO
|