@photostructure/fs-metadata 1.2.0 → 1.4.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/CHANGELOG.md +37 -0
- package/CONTRIBUTING.md +3 -3
- package/binding.gyp +0 -22
- package/dist/index.cjs +11 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -3
- package/dist/index.d.mts +15 -3
- package/dist/index.d.ts +15 -3
- package/dist/index.mjs +11 -40
- package/dist/index.mjs.map +1 -1
- package/doc/C++_REVIEW_TODO.md +3 -36
- package/doc/LINUX_API_REFERENCE.md +4 -147
- package/doc/SECURITY_AUDIT_2026.md +5 -0
- package/doc/gotchas.md +27 -0
- package/package.json +9 -10
- package/prebuilds/darwin-arm64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/darwin-x64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/linux-x64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/win32-arm64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/win32-x64/@photostructure+fs-metadata.glibc.node +0 -0
- package/scripts/clang-tidy.ts +1 -3
- package/scripts/prebuild-linux-glibc.sh +1 -5
- package/scripts/sanitizers-test.sh +0 -1
- package/src/binding.cpp +0 -15
- package/src/index.ts +6 -2
- package/src/linux/mount_points.ts +8 -42
- package/src/linux/volume_metadata.cpp +0 -16
- package/src/mount_point_for_path.ts +1 -1
- package/src/types/mount_point.ts +1 -1
- package/src/types/native_bindings.ts +0 -5
- package/src/types/options.ts +14 -0
- package/src/volume_metadata.ts +22 -11
- package/src/volume_mount_points.ts +1 -1
- package/scripts/setup-native.mjs +0 -39
- package/src/linux/gio_mount_points.cpp +0 -80
- package/src/linux/gio_mount_points.h +0 -37
- package/src/linux/gio_utils.cpp +0 -115
- package/src/linux/gio_utils.h +0 -69
- package/src/linux/gio_volume_metadata.cpp +0 -81
- package/src/linux/gio_volume_metadata.h +0 -20
package/src/volume_metadata.ts
CHANGED
|
@@ -95,7 +95,8 @@ async function _getVolumeMetadata(
|
|
|
95
95
|
}
|
|
96
96
|
} catch (err) {
|
|
97
97
|
debug("[getVolumeMetadata] failed to get mtab info: " + err);
|
|
98
|
-
//
|
|
98
|
+
// Mtab lookup can fail for transient mounts or race conditions.
|
|
99
|
+
// Ignore and continue with whatever the native call returns.
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
|
|
@@ -140,7 +141,7 @@ async function _getVolumeMetadata(
|
|
|
140
141
|
remote,
|
|
141
142
|
}) as VolumeMetadata;
|
|
142
143
|
|
|
143
|
-
// Backfill if blkid
|
|
144
|
+
// Backfill if blkid failed us:
|
|
144
145
|
if (isLinux && isNotBlank(device)) {
|
|
145
146
|
// Sometimes blkid doesn't have the UUID in cache. Try to get it from
|
|
146
147
|
// /dev/disk/by-uuid:
|
|
@@ -209,7 +210,7 @@ export async function getVolumeMetadataForPathImpl(
|
|
|
209
210
|
|
|
210
211
|
// Linux/Windows: stat().dev is reliable (no firmlinks). Find the mount point
|
|
211
212
|
// by comparing device IDs, using path prefix as a tiebreaker for bind mounts
|
|
212
|
-
// or
|
|
213
|
+
// or GVfs/FUSE mounts that share the same device id.
|
|
213
214
|
const mountPoint = await findMountPointByDeviceId(
|
|
214
215
|
resolved,
|
|
215
216
|
resolvedStat,
|
|
@@ -221,12 +222,18 @@ export async function getVolumeMetadataForPathImpl(
|
|
|
221
222
|
}
|
|
222
223
|
|
|
223
224
|
/**
|
|
224
|
-
* Find the mount point for a resolved path using device ID
|
|
225
|
+
* Find the mount point for a resolved path using device ID + path ancestry.
|
|
225
226
|
* Used on Linux and Windows where stat().dev is reliable (no firmlinks).
|
|
226
227
|
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
228
|
+
* Device ID filters out unrelated filesystems. Among same-device mount points,
|
|
229
|
+
* ancestor-path matches (mount point is a parent of `resolved`) are strongly
|
|
230
|
+
* preferred over device-only matches — GVfs/FUSE mounts on Linux can share
|
|
231
|
+
* the same device ID across unrelated volumes (e.g. multiple SMB shares
|
|
232
|
+
* under /run/user/.../gvfs/), so device ID alone is ambiguous. The longest
|
|
233
|
+
* ancestor wins.
|
|
234
|
+
*
|
|
235
|
+
* The device-only fallback (`deviceMatches`) exists for bind mounts where the
|
|
236
|
+
* canonical mount point may not be a path ancestor of the target.
|
|
230
237
|
*/
|
|
231
238
|
export async function findMountPointByDeviceId(
|
|
232
239
|
resolved: string,
|
|
@@ -235,10 +242,12 @@ export async function findMountPointByDeviceId(
|
|
|
235
242
|
nativeFn: NativeBindingsFn,
|
|
236
243
|
): Promise<string> {
|
|
237
244
|
const targetDev = resolvedStat.dev;
|
|
238
|
-
const mountPoints =
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
245
|
+
const mountPoints =
|
|
246
|
+
opts.mountPoints ??
|
|
247
|
+
(await getVolumeMountPointsImpl(
|
|
248
|
+
{ ...opts, includeSystemVolumes: true },
|
|
249
|
+
nativeFn,
|
|
250
|
+
));
|
|
242
251
|
|
|
243
252
|
const prefixMatches: string[] = [];
|
|
244
253
|
const deviceMatches: string[] = [];
|
|
@@ -259,6 +268,8 @@ export async function findMountPointByDeviceId(
|
|
|
259
268
|
}),
|
|
260
269
|
);
|
|
261
270
|
|
|
271
|
+
// Prefer ancestor matches — they're unambiguous. Fall back to device-only
|
|
272
|
+
// matches only when the mount point isn't an ancestor (e.g. bind mounts).
|
|
262
273
|
const candidates = prefixMatches.length > 0 ? prefixMatches : deviceMatches;
|
|
263
274
|
if (candidates.length === 0) {
|
|
264
275
|
throw new Error(
|
package/scripts/setup-native.mjs
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// scripts/setup-native.mjs
|
|
4
|
-
|
|
5
|
-
// This script sets up the config.gypi file to include GIO support when available.
|
|
6
|
-
// It should be run before building native modules with node-gyp.
|
|
7
|
-
|
|
8
|
-
import { execSync } from "node:child_process";
|
|
9
|
-
import { writeFileSync } from "node:fs";
|
|
10
|
-
import { platform } from "node:os";
|
|
11
|
-
import { argv } from "node:process";
|
|
12
|
-
import { pathToFileURL } from "node:url";
|
|
13
|
-
|
|
14
|
-
function hasGio() {
|
|
15
|
-
if (platform() !== "linux") return false;
|
|
16
|
-
try {
|
|
17
|
-
execSync("pkg-config --exists gio-2.0", { stdio: "ignore" });
|
|
18
|
-
return true;
|
|
19
|
-
} catch {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function configure() {
|
|
25
|
-
// Create a gyp config file that node-gyp will read
|
|
26
|
-
const config = {
|
|
27
|
-
variables: {
|
|
28
|
-
"enable_gio%": hasGio() ? "true" : "false",
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const payload = JSON.stringify(config, null, 2);
|
|
33
|
-
writeFileSync("config.gypi", payload);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// If the script is run directly, call the configure function
|
|
37
|
-
if (import.meta.url === pathToFileURL(argv[1]).href) {
|
|
38
|
-
configure();
|
|
39
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
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
|
-
// Use thread-safe g_unix_mounts_get() API
|
|
25
|
-
MountIterator::forEachMount([this](GUnixMountEntry *entry) {
|
|
26
|
-
// Get mount path and filesystem type from thread-safe Unix mount API
|
|
27
|
-
const char *mount_path = g_unix_mount_get_mount_path(entry);
|
|
28
|
-
const char *fs_type = g_unix_mount_get_fs_type(entry);
|
|
29
|
-
|
|
30
|
-
if (mount_path && fs_type) {
|
|
31
|
-
DEBUG_LOG("[GioMountPoints] found {mountPoint: %s, fsType: %s}",
|
|
32
|
-
mount_path, fs_type);
|
|
33
|
-
|
|
34
|
-
MountPoint point{};
|
|
35
|
-
point.mountPoint = mount_path;
|
|
36
|
-
point.fstype = fs_type;
|
|
37
|
-
mountPoints.push_back(point);
|
|
38
|
-
} else {
|
|
39
|
-
DEBUG_LOG("[GioMountPoints] skipping mount with null path or fstype");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return true; // Continue iteration
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
DEBUG_LOG("[GioMountPoints] found %zu mount points", mountPoints.size());
|
|
46
|
-
} catch (const std::exception &e) {
|
|
47
|
-
DEBUG_LOG("[GioMountPoints] error: %s", e.what());
|
|
48
|
-
SetError(e.what());
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
void GioMountPointsWorker::OnOK() {
|
|
53
|
-
const Napi::HandleScope scope(Env());
|
|
54
|
-
const Napi::Array result = Napi::Array::New(Env());
|
|
55
|
-
|
|
56
|
-
for (size_t i = 0; i < mountPoints.size(); i++) {
|
|
57
|
-
const Napi::Object point = Napi::Object::New(Env());
|
|
58
|
-
point.Set("mountPoint", mountPoints[i].mountPoint);
|
|
59
|
-
point.Set("fstype", mountPoints[i].fstype);
|
|
60
|
-
result.Set(i, point);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
deferred_.Resolve(result);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
void GioMountPointsWorker::OnError(const Napi::Error &error) {
|
|
67
|
-
deferred_.Reject(error.Value());
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
Napi::Value GetMountPoints(Napi::Env env) {
|
|
71
|
-
auto deferred = Napi::Promise::Deferred::New(env);
|
|
72
|
-
auto *worker = new GioMountPointsWorker(deferred);
|
|
73
|
-
worker->Queue();
|
|
74
|
-
return deferred.Promise();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
} // namespace gio
|
|
78
|
-
} // namespace FSMeta
|
|
79
|
-
|
|
80
|
-
#endif // ENABLE_GIO
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// src/linux/gio_mount_points.h
|
|
2
|
-
|
|
3
|
-
#pragma once
|
|
4
|
-
|
|
5
|
-
#ifdef ENABLE_GIO
|
|
6
|
-
|
|
7
|
-
#include "../common/volume_mount_points.h"
|
|
8
|
-
#include <napi.h>
|
|
9
|
-
#include <string>
|
|
10
|
-
#include <vector>
|
|
11
|
-
|
|
12
|
-
namespace FSMeta {
|
|
13
|
-
namespace gio {
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Get mount points asynchronously using GIO
|
|
17
|
-
*/
|
|
18
|
-
Napi::Value GetMountPoints(Napi::Env env);
|
|
19
|
-
|
|
20
|
-
class GioMountPointsWorker : public Napi::AsyncWorker {
|
|
21
|
-
public:
|
|
22
|
-
explicit GioMountPointsWorker(const Napi::Promise::Deferred &deferred);
|
|
23
|
-
~GioMountPointsWorker() override; // Add override specifier
|
|
24
|
-
|
|
25
|
-
void Execute() override;
|
|
26
|
-
void OnOK() override;
|
|
27
|
-
void OnError(const Napi::Error &error) override;
|
|
28
|
-
|
|
29
|
-
private:
|
|
30
|
-
std::vector<MountPoint> mountPoints;
|
|
31
|
-
Napi::Promise::Deferred deferred_;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
} // namespace gio
|
|
35
|
-
} // namespace FSMeta
|
|
36
|
-
|
|
37
|
-
#endif // ENABLE_GIO
|
package/src/linux/gio_utils.cpp
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
// src/linux/gio_utils.cpp
|
|
2
|
-
//
|
|
3
|
-
// Thread-Safe Mount Enumeration for Linux
|
|
4
|
-
//
|
|
5
|
-
// This implementation uses g_unix_mounts_get() as the primary, thread-safe path
|
|
6
|
-
// for enumerating mounts. GVolumeMonitor is optionally used for enrichment but
|
|
7
|
-
// is NOT required for correct operation.
|
|
8
|
-
//
|
|
9
|
-
// IMPORTANT THREAD SAFETY NOTES:
|
|
10
|
-
//
|
|
11
|
-
// According to GIO documentation
|
|
12
|
-
// (https://docs.gtk.org/gio/class.VolumeMonitor.html): "GVolumeMonitor is not
|
|
13
|
-
// thread-default-context aware and so should not be used other than from the
|
|
14
|
-
// main thread, with no thread-default-context active."
|
|
15
|
-
//
|
|
16
|
-
// However, g_unix_mounts_get() is explicitly thread-safe:
|
|
17
|
-
// - Uses getmntent_r() when available (reentrant)
|
|
18
|
-
// - Falls back to getmntent() with G_LOCK protection
|
|
19
|
-
// See: https://gitlab.gnome.org/GNOME/glib/-/blob/main/gio/gunixmounts.c
|
|
20
|
-
//
|
|
21
|
-
// This design:
|
|
22
|
-
// ✅ Primary path uses thread-safe g_unix_mounts_get()
|
|
23
|
-
// ✅ Optional GVolumeMonitor enhancement (best-effort, may be skipped)
|
|
24
|
-
// ✅ Fixes Finding #6 (thread safety violation)
|
|
25
|
-
// ✅ Fixes Finding #7 (double-free risk with g_list_free_full)
|
|
26
|
-
|
|
27
|
-
#ifdef ENABLE_GIO
|
|
28
|
-
|
|
29
|
-
#include "gio_utils.h"
|
|
30
|
-
#include "../common/debug_log.h"
|
|
31
|
-
#include <gio/gio.h>
|
|
32
|
-
#include <gio/gunixmounts.h>
|
|
33
|
-
#include <memory>
|
|
34
|
-
#include <stdexcept>
|
|
35
|
-
|
|
36
|
-
namespace FSMeta {
|
|
37
|
-
namespace gio {
|
|
38
|
-
|
|
39
|
-
void MountIterator::forEachMount(const MountCallback &callback) {
|
|
40
|
-
// PRIMARY PATH: Thread-safe Unix mount enumeration
|
|
41
|
-
// g_unix_mounts_get() is documented as thread-safe and can be called
|
|
42
|
-
// from worker threads without violating GIO threading requirements.
|
|
43
|
-
GList *unix_mounts = g_unix_mounts_get(nullptr);
|
|
44
|
-
|
|
45
|
-
if (!unix_mounts) {
|
|
46
|
-
DEBUG_LOG("[gio::MountIterator::forEachMount] no mounts found");
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
DEBUG_LOG("[gio::MountIterator::forEachMount] processing Unix mounts");
|
|
51
|
-
|
|
52
|
-
// Iterate over all Unix mounts
|
|
53
|
-
GList *current = unix_mounts;
|
|
54
|
-
bool should_continue = true;
|
|
55
|
-
|
|
56
|
-
while (current && should_continue) {
|
|
57
|
-
GUnixMountEntry *entry = static_cast<GUnixMountEntry *>(current->data);
|
|
58
|
-
|
|
59
|
-
if (!entry) {
|
|
60
|
-
DEBUG_LOG("[gio::MountIterator::forEachMount] Skipping null entry");
|
|
61
|
-
current = current->next;
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
// Get mount path from thread-safe Unix mount API
|
|
67
|
-
const char *mount_path = g_unix_mount_get_mount_path(entry);
|
|
68
|
-
if (!mount_path) {
|
|
69
|
-
DEBUG_LOG(
|
|
70
|
-
"[gio::MountIterator::forEachMount] Skipping mount with null path");
|
|
71
|
-
current = current->next;
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
DEBUG_LOG("[gio::MountIterator::forEachMount] processing mount: %s",
|
|
76
|
-
mount_path);
|
|
77
|
-
|
|
78
|
-
// Invoke callback with Unix mount entry
|
|
79
|
-
// The callback receives the entry and can extract data using
|
|
80
|
-
// g_unix_mount_get_* functions
|
|
81
|
-
should_continue = callback(entry);
|
|
82
|
-
|
|
83
|
-
} catch (const std::exception &e) {
|
|
84
|
-
DEBUG_LOG("[gio::MountIterator::forEachMount] Exception during mount "
|
|
85
|
-
"processing: %s",
|
|
86
|
-
e.what());
|
|
87
|
-
// Clean up and re-throw
|
|
88
|
-
g_list_free_full(unix_mounts,
|
|
89
|
-
reinterpret_cast<GDestroyNotify>(g_unix_mount_free));
|
|
90
|
-
throw;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
current = current->next;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Free list and all mount entries
|
|
97
|
-
// Each entry is freed with g_unix_mount_free() - no double-free risk
|
|
98
|
-
g_list_free_full(unix_mounts,
|
|
99
|
-
reinterpret_cast<GDestroyNotify>(g_unix_mount_free));
|
|
100
|
-
|
|
101
|
-
DEBUG_LOG("[gio::MountIterator::forEachMount] completed");
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// NOTE: tryGetMonitor() has been removed.
|
|
105
|
-
//
|
|
106
|
-
// GVolumeMonitor is NOT thread-safe and must only be used from the main thread.
|
|
107
|
-
// See: https://docs.gtk.org/gio/class.VolumeMonitor.html
|
|
108
|
-
//
|
|
109
|
-
// Since all our code runs on Napi::AsyncWorker threads, using GVolumeMonitor
|
|
110
|
-
// causes race conditions leading to GLib-GObject-CRITICAL errors.
|
|
111
|
-
|
|
112
|
-
} // namespace gio
|
|
113
|
-
} // namespace FSMeta
|
|
114
|
-
|
|
115
|
-
#endif // ENABLE_GIO
|
package/src/linux/gio_utils.h
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
// src/linux/gio_utils.h
|
|
2
|
-
#pragma once
|
|
3
|
-
|
|
4
|
-
#ifdef ENABLE_GIO
|
|
5
|
-
|
|
6
|
-
#include <gio/gio.h>
|
|
7
|
-
#include <gio/gunixmounts.h>
|
|
8
|
-
#include <napi.h>
|
|
9
|
-
#include <string>
|
|
10
|
-
#include <vector>
|
|
11
|
-
|
|
12
|
-
// Custom deleter for GObject types using g_object_unref
|
|
13
|
-
template <typename T> struct GObjectDeleter {
|
|
14
|
-
void operator()(T *ptr) const {
|
|
15
|
-
if (ptr) {
|
|
16
|
-
g_object_unref(ptr);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// Custom deleter for g_free (used for strings from GIO APIs like
|
|
22
|
-
// g_file_get_path)
|
|
23
|
-
struct GFreeDeleter {
|
|
24
|
-
void operator()(void *ptr) const {
|
|
25
|
-
if (ptr) {
|
|
26
|
-
g_free(ptr);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// Smart pointer aliases for RAII management of GIO resources
|
|
32
|
-
// These ensure proper cleanup even when exceptions occur
|
|
33
|
-
template <typename T> using GObjectPtr = std::unique_ptr<T, GObjectDeleter<T>>;
|
|
34
|
-
|
|
35
|
-
// Common GIO object types
|
|
36
|
-
using GFilePtr = GObjectPtr<GFile>;
|
|
37
|
-
using GMountPtr = GObjectPtr<GMount>;
|
|
38
|
-
using GVolumePtr = GObjectPtr<GVolume>;
|
|
39
|
-
using GVolumeMonitorPtr = GObjectPtr<GVolumeMonitor>;
|
|
40
|
-
using GFileInfoPtr = GObjectPtr<GFileInfo>;
|
|
41
|
-
|
|
42
|
-
// For strings allocated by GIO (g_file_get_path, g_file_get_uri, etc.)
|
|
43
|
-
using GCharPtr = std::unique_ptr<char, GFreeDeleter>;
|
|
44
|
-
|
|
45
|
-
namespace FSMeta {
|
|
46
|
-
namespace gio {
|
|
47
|
-
|
|
48
|
-
class MountIterator {
|
|
49
|
-
public:
|
|
50
|
-
// Callback type for mount processing
|
|
51
|
-
// Receives GUnixMountEntry which provides thread-safe access to mount data
|
|
52
|
-
// Return true to continue iteration, false to stop
|
|
53
|
-
using MountCallback = std::function<bool(GUnixMountEntry *)>;
|
|
54
|
-
|
|
55
|
-
// Static method to iterate over mounts using thread-safe g_unix_mounts_get()
|
|
56
|
-
// This is safe to call from worker threads
|
|
57
|
-
static void forEachMount(const MountCallback &callback);
|
|
58
|
-
|
|
59
|
-
// NOTE: tryGetMonitor() has been removed because GVolumeMonitor is NOT
|
|
60
|
-
// thread-safe. See: https://docs.gtk.org/gio/class.VolumeMonitor.html
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// Note: GioResource<T> has been removed in favor of GObjectPtr<T> above,
|
|
64
|
-
// which provides equivalent RAII semantics with std::unique_ptr.
|
|
65
|
-
|
|
66
|
-
} // namespace gio
|
|
67
|
-
} // namespace FSMeta
|
|
68
|
-
|
|
69
|
-
#endif // ENABLE_GIO
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// src/linux/gio_volume_metadata.cpp
|
|
2
|
-
|
|
3
|
-
#ifdef ENABLE_GIO
|
|
4
|
-
|
|
5
|
-
#include "gio_volume_metadata.h"
|
|
6
|
-
#include "../common/debug_log.h"
|
|
7
|
-
#include "gio_utils.h"
|
|
8
|
-
#include <gio/gio.h>
|
|
9
|
-
#include <memory>
|
|
10
|
-
#include <stdexcept>
|
|
11
|
-
|
|
12
|
-
namespace FSMeta {
|
|
13
|
-
namespace gio {
|
|
14
|
-
|
|
15
|
-
void addMountMetadata(const std::string &mountPoint, VolumeMetadata &metadata) {
|
|
16
|
-
DEBUG_LOG("[gio::addMountMetadata] getting mount metadata for %s",
|
|
17
|
-
mountPoint.c_str());
|
|
18
|
-
|
|
19
|
-
bool found = false;
|
|
20
|
-
|
|
21
|
-
// PRIMARY PATH: Thread-safe Unix mount API
|
|
22
|
-
MountIterator::forEachMount([&](GUnixMountEntry *entry) {
|
|
23
|
-
const char *mount_path = g_unix_mount_get_mount_path(entry);
|
|
24
|
-
if (!mount_path || mountPoint != mount_path) {
|
|
25
|
-
return true; // Continue iteration
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Found matching mount point
|
|
29
|
-
DEBUG_LOG("[gio::addMountMetadata] found matching mount point: %s",
|
|
30
|
-
mount_path);
|
|
31
|
-
found = true;
|
|
32
|
-
|
|
33
|
-
// Get basic metadata from thread-safe Unix mount API
|
|
34
|
-
if (metadata.fstype.empty()) {
|
|
35
|
-
const char *fs_type = g_unix_mount_get_fs_type(entry);
|
|
36
|
-
if (fs_type) {
|
|
37
|
-
DEBUG_LOG("[gio::addMountMetadata] {mountPoint: %s, fsType: %s}",
|
|
38
|
-
mount_path, fs_type);
|
|
39
|
-
metadata.fstype = fs_type;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (metadata.mountFrom.empty()) {
|
|
44
|
-
const char *device_path = g_unix_mount_get_device_path(entry);
|
|
45
|
-
if (device_path) {
|
|
46
|
-
DEBUG_LOG("[gio::addMountMetadata] {mountPoint: %s, mountFrom: %s}",
|
|
47
|
-
mount_path, device_path);
|
|
48
|
-
metadata.mountFrom = device_path;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// NOTE: GVolumeMonitor enrichment has been removed.
|
|
53
|
-
//
|
|
54
|
-
// According to GIO documentation:
|
|
55
|
-
// https://docs.gtk.org/gio/class.VolumeMonitor.html
|
|
56
|
-
// "GVolumeMonitor is not thread-default-context aware and so should not
|
|
57
|
-
// be used other than from the main thread, with no thread-default-context
|
|
58
|
-
// active."
|
|
59
|
-
//
|
|
60
|
-
// This function is called from Napi::AsyncWorker::Execute() which runs
|
|
61
|
-
// on a worker thread. Using GVolumeMonitor here causes race conditions
|
|
62
|
-
// leading to GLib-GObject-CRITICAL errors like:
|
|
63
|
-
// "g_object_ref: assertion '!object_already_finalized' failed"
|
|
64
|
-
//
|
|
65
|
-
// The basic metadata (fstype, mountFrom) from g_unix_mounts_get() is
|
|
66
|
-
// sufficient and thread-safe. Rich metadata (label, mountName, uri) can
|
|
67
|
-
// be obtained from blkid or other thread-safe sources.
|
|
68
|
-
|
|
69
|
-
return false; // Stop iteration, we found our mount
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
if (!found) {
|
|
73
|
-
DEBUG_LOG("[gio::addMountMetadata] mount point %s not found",
|
|
74
|
-
mountPoint.c_str());
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
} // namespace gio
|
|
79
|
-
} // namespace FSMeta
|
|
80
|
-
|
|
81
|
-
#endif // ENABLE_GIO
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// src/linux/gio_volume_metadata.h
|
|
2
|
-
|
|
3
|
-
#pragma once
|
|
4
|
-
|
|
5
|
-
#ifdef ENABLE_GIO
|
|
6
|
-
|
|
7
|
-
#include "../common/volume_metadata.h"
|
|
8
|
-
#include <string>
|
|
9
|
-
|
|
10
|
-
namespace FSMeta {
|
|
11
|
-
namespace gio {
|
|
12
|
-
/**
|
|
13
|
-
* Add metadata from GIO to the volume metadata
|
|
14
|
-
*/
|
|
15
|
-
void addMountMetadata(const std::string &mountPoint, VolumeMetadata &metadata);
|
|
16
|
-
|
|
17
|
-
} // namespace gio
|
|
18
|
-
} // namespace FSMeta
|
|
19
|
-
|
|
20
|
-
#endif // ENABLE_GIO
|