@photostructure/fs-metadata 0.3.3 → 0.5.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.
Files changed (183) hide show
  1. package/C++_REVIEW_TODO.md +291 -0
  2. package/CHANGELOG.md +33 -1
  3. package/CLAUDE.md +169 -0
  4. package/CONTRIBUTING.md +25 -0
  5. package/coverage/base.css +224 -0
  6. package/coverage/block-navigation.js +87 -0
  7. package/coverage/favicon.png +0 -0
  8. package/coverage/index.html +131 -0
  9. package/coverage/lcov-report/base.css +224 -0
  10. package/coverage/lcov-report/block-navigation.js +87 -0
  11. package/coverage/lcov-report/favicon.png +0 -0
  12. package/coverage/lcov-report/index.html +131 -0
  13. package/coverage/lcov-report/prettify.css +1 -0
  14. package/coverage/lcov-report/prettify.js +2 -0
  15. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  16. package/coverage/lcov-report/sorter.js +196 -0
  17. package/coverage/lcov-report/src/array.ts.html +217 -0
  18. package/coverage/lcov-report/src/async.ts.html +547 -0
  19. package/coverage/lcov-report/src/debuglog.ts.html +187 -0
  20. package/coverage/lcov-report/src/defer.ts.html +175 -0
  21. package/coverage/lcov-report/src/dirname.ts.html +124 -0
  22. package/coverage/lcov-report/src/error.ts.html +322 -0
  23. package/coverage/lcov-report/src/fs.ts.html +316 -0
  24. package/coverage/lcov-report/src/glob.ts.html +472 -0
  25. package/coverage/lcov-report/src/hidden.ts.html +724 -0
  26. package/coverage/lcov-report/src/index.html +521 -0
  27. package/coverage/lcov-report/src/index.ts.html +676 -0
  28. package/coverage/lcov-report/src/linux/dev_disk.ts.html +316 -0
  29. package/coverage/lcov-report/src/linux/index.html +146 -0
  30. package/coverage/lcov-report/src/linux/mount_points.ts.html +364 -0
  31. package/coverage/lcov-report/src/linux/mtab.ts.html +493 -0
  32. package/coverage/lcov-report/src/mount_point.ts.html +106 -0
  33. package/coverage/lcov-report/src/number.ts.html +148 -0
  34. package/coverage/lcov-report/src/object.ts.html +265 -0
  35. package/coverage/lcov-report/src/options.ts.html +475 -0
  36. package/coverage/lcov-report/src/path.ts.html +268 -0
  37. package/coverage/lcov-report/src/platform.ts.html +112 -0
  38. package/coverage/lcov-report/src/random.ts.html +205 -0
  39. package/coverage/lcov-report/src/remote_info.ts.html +553 -0
  40. package/coverage/lcov-report/src/stack_path.ts.html +298 -0
  41. package/coverage/lcov-report/src/string.ts.html +382 -0
  42. package/coverage/lcov-report/src/string_enum.ts.html +208 -0
  43. package/coverage/lcov-report/src/system_volume.ts.html +301 -0
  44. package/coverage/lcov-report/src/unc.ts.html +274 -0
  45. package/coverage/lcov-report/src/units.ts.html +274 -0
  46. package/coverage/lcov-report/src/uuid.ts.html +157 -0
  47. package/coverage/lcov-report/src/volume_health_status.ts.html +259 -0
  48. package/coverage/lcov-report/src/volume_metadata.ts.html +787 -0
  49. package/coverage/lcov-report/src/volume_mount_points.ts.html +388 -0
  50. package/coverage/lcov.info +3581 -0
  51. package/coverage/prettify.css +1 -0
  52. package/coverage/prettify.js +2 -0
  53. package/coverage/sort-arrow-sprite.png +0 -0
  54. package/coverage/sorter.js +196 -0
  55. package/coverage/src/array.ts.html +217 -0
  56. package/coverage/src/async.ts.html +547 -0
  57. package/coverage/src/debuglog.ts.html +187 -0
  58. package/coverage/src/defer.ts.html +175 -0
  59. package/coverage/src/dirname.ts.html +124 -0
  60. package/coverage/src/error.ts.html +322 -0
  61. package/coverage/src/fs.ts.html +316 -0
  62. package/coverage/src/glob.ts.html +472 -0
  63. package/coverage/src/hidden.ts.html +724 -0
  64. package/coverage/src/index.html +521 -0
  65. package/coverage/src/index.ts.html +676 -0
  66. package/coverage/src/linux/dev_disk.ts.html +316 -0
  67. package/coverage/src/linux/index.html +146 -0
  68. package/coverage/src/linux/mount_points.ts.html +364 -0
  69. package/coverage/src/linux/mtab.ts.html +493 -0
  70. package/coverage/src/mount_point.ts.html +106 -0
  71. package/coverage/src/number.ts.html +148 -0
  72. package/coverage/src/object.ts.html +265 -0
  73. package/coverage/src/options.ts.html +475 -0
  74. package/coverage/src/path.ts.html +268 -0
  75. package/coverage/src/platform.ts.html +112 -0
  76. package/coverage/src/random.ts.html +205 -0
  77. package/coverage/src/remote_info.ts.html +553 -0
  78. package/coverage/src/stack_path.ts.html +298 -0
  79. package/coverage/src/string.ts.html +382 -0
  80. package/coverage/src/string_enum.ts.html +208 -0
  81. package/coverage/src/system_volume.ts.html +301 -0
  82. package/coverage/src/unc.ts.html +274 -0
  83. package/coverage/src/units.ts.html +274 -0
  84. package/coverage/src/uuid.ts.html +157 -0
  85. package/coverage/src/volume_health_status.ts.html +259 -0
  86. package/coverage/src/volume_metadata.ts.html +787 -0
  87. package/coverage/src/volume_mount_points.ts.html +388 -0
  88. package/jest.config.cjs +67 -6
  89. package/package.json +51 -40
  90. package/prebuilds/linux-x64/@photostructure+fs-metadata.glibc.node +0 -0
  91. package/scripts/check-memory.mjs +243 -0
  92. package/scripts/clang-tidy.mjs +73 -0
  93. package/scripts/is-platform.mjs +12 -0
  94. package/scripts/post-build.mjs +21 -0
  95. package/scripts/run-asan.sh +92 -0
  96. package/scripts/valgrind-test.mjs +83 -0
  97. package/scripts/valgrind.sh +70 -0
  98. package/src/async.ts +3 -3
  99. package/src/binding.cpp +3 -3
  100. package/src/darwin/hidden.cpp +14 -0
  101. package/src/darwin/raii_utils.h +85 -0
  102. package/src/darwin/volume_metadata.cpp +35 -60
  103. package/src/darwin/volume_mount_points.cpp +31 -22
  104. package/src/error.ts +3 -3
  105. package/src/fs.ts +1 -1
  106. package/src/glob.ts +2 -2
  107. package/src/hidden.ts +6 -6
  108. package/src/index.ts +19 -23
  109. package/src/linux/blkid_cache.cpp +15 -12
  110. package/src/linux/dev_disk.ts +2 -2
  111. package/src/linux/gio_mount_points.cpp +7 -7
  112. package/src/linux/gio_utils.cpp +19 -8
  113. package/src/linux/gio_volume_metadata.cpp +15 -15
  114. package/src/linux/mount_points.ts +9 -9
  115. package/src/linux/mtab.ts +7 -7
  116. package/src/linux/volume_metadata.cpp +6 -1
  117. package/src/object.ts +9 -4
  118. package/src/options.ts +4 -4
  119. package/src/path.ts +4 -4
  120. package/src/remote_info.ts +5 -5
  121. package/src/system_volume.ts +8 -8
  122. package/src/test-utils/assert.ts +2 -2
  123. package/src/test-utils/debuglog-child.ts +1 -3
  124. package/src/test-utils/debuglog-enabled-child.ts +10 -0
  125. package/src/test-utils/hidden-tests.ts +1 -1
  126. package/src/test-utils/platform.ts +3 -3
  127. package/src/types/native_bindings.ts +3 -3
  128. package/src/types/volume_metadata.ts +2 -2
  129. package/src/unc.ts +2 -2
  130. package/src/uuid.ts +1 -1
  131. package/src/volume_health_status.ts +6 -6
  132. package/src/volume_metadata.ts +20 -23
  133. package/src/volume_mount_points.ts +12 -17
  134. package/src/windows/drive_status.h +30 -13
  135. package/src/windows/hidden.cpp +12 -0
  136. package/src/windows/volume_metadata.cpp +17 -7
  137. package/tsup.config.ts +8 -2
  138. package/dist/index.cjs +0 -1439
  139. package/dist/index.cjs.map +0 -1
  140. package/dist/index.mjs +0 -1396
  141. package/dist/index.mjs.map +0 -1
  142. package/dist/types/array.d.ts +0 -25
  143. package/dist/types/async.d.ts +0 -42
  144. package/dist/types/debuglog.d.ts +0 -3
  145. package/dist/types/defer.d.ts +0 -10
  146. package/dist/types/dirname.d.ts +0 -1
  147. package/dist/types/error.d.ts +0 -17
  148. package/dist/types/fs.d.ts +0 -22
  149. package/dist/types/glob.d.ts +0 -17
  150. package/dist/types/hidden.d.ts +0 -29
  151. package/dist/types/index.d.ts +0 -91
  152. package/dist/types/linux/dev_disk.d.ts +0 -13
  153. package/dist/types/linux/mount_points.d.ts +0 -6
  154. package/dist/types/linux/mtab.d.ts +0 -47
  155. package/dist/types/mount_point.d.ts +0 -2
  156. package/dist/types/number.d.ts +0 -3
  157. package/dist/types/object.d.ts +0 -13
  158. package/dist/types/options.d.ts +0 -33
  159. package/dist/types/path.d.ts +0 -17
  160. package/dist/types/platform.d.ts +0 -4
  161. package/dist/types/random.d.ts +0 -12
  162. package/dist/types/remote_info.d.ts +0 -6
  163. package/dist/types/stack_path.d.ts +0 -2
  164. package/dist/types/string.d.ts +0 -37
  165. package/dist/types/string_enum.d.ts +0 -19
  166. package/dist/types/system_volume.d.ts +0 -14
  167. package/dist/types/types/hidden_metadata.d.ts +0 -32
  168. package/dist/types/types/mount_point.d.ts +0 -46
  169. package/dist/types/types/native_bindings.d.ts +0 -51
  170. package/dist/types/types/options.d.ts +0 -47
  171. package/dist/types/types/remote_info.d.ts +0 -33
  172. package/dist/types/types/volume_metadata.d.ts +0 -46
  173. package/dist/types/unc.d.ts +0 -11
  174. package/dist/types/units.d.ts +0 -38
  175. package/dist/types/uuid.d.ts +0 -16
  176. package/dist/types/volume_health_status.d.ts +0 -24
  177. package/dist/types/volume_metadata.d.ts +0 -8
  178. package/dist/types/volume_mount_points.d.ts +0 -6
  179. package/jest.config.base.cjs +0 -63
  180. package/prebuilds/darwin-arm64/@photostructure+fs-metadata.glibc.node +0 -0
  181. package/prebuilds/linux-arm64/@photostructure+fs-metadata.musl.node +0 -0
  182. package/prebuilds/linux-x64/@photostructure+fs-metadata.musl.node +0 -0
  183. package/prebuilds/win32-x64/@photostructure+fs-metadata.glibc.node +0 -0
@@ -0,0 +1,85 @@
1
+ #pragma once
2
+
3
+ #include <CoreFoundation/CoreFoundation.h>
4
+ #include <sys/mount.h>
5
+
6
+ namespace FSMeta {
7
+
8
+ // Generic RAII wrapper for resources that need free()
9
+ template <typename T> class ResourceRAII {
10
+ private:
11
+ T *resource_;
12
+
13
+ public:
14
+ ResourceRAII() : resource_(nullptr) {}
15
+ ~ResourceRAII() {
16
+ if (resource_) {
17
+ free(resource_);
18
+ }
19
+ }
20
+
21
+ T **ptr() { return &resource_; }
22
+ T *get() { return resource_; }
23
+
24
+ // Add move operations for better resource management
25
+ ResourceRAII(ResourceRAII &&other) noexcept : resource_(other.resource_) {
26
+ other.resource_ = nullptr;
27
+ }
28
+
29
+ ResourceRAII &operator=(ResourceRAII &&other) noexcept {
30
+ if (this != &other) {
31
+ if (resource_)
32
+ free(resource_);
33
+ resource_ = other.resource_;
34
+ other.resource_ = nullptr;
35
+ }
36
+ return *this;
37
+ }
38
+
39
+ // Prevent copying
40
+ ResourceRAII(const ResourceRAII &) = delete;
41
+ ResourceRAII &operator=(const ResourceRAII &) = delete;
42
+ };
43
+
44
+ // Specialized for mount info
45
+ using MountBufferRAII = ResourceRAII<struct statfs>;
46
+
47
+ // CoreFoundation RAII wrapper
48
+ template <typename T> class CFReleaser {
49
+ private:
50
+ T ref_;
51
+
52
+ public:
53
+ explicit CFReleaser(T ref = nullptr) noexcept : ref_(ref) {}
54
+ ~CFReleaser() { reset(); }
55
+
56
+ void reset(T ref = nullptr) {
57
+ if (ref_) {
58
+ CFRelease(ref_);
59
+ }
60
+ ref_ = ref;
61
+ }
62
+
63
+ operator T() const noexcept { return ref_; }
64
+ T get() const noexcept { return ref_; }
65
+ bool isValid() const noexcept { return ref_ != nullptr; }
66
+
67
+ // Prevent copying
68
+ CFReleaser(const CFReleaser &) = delete;
69
+ CFReleaser &operator=(const CFReleaser &) = delete;
70
+
71
+ // Allow moving
72
+ CFReleaser(CFReleaser &&other) noexcept : ref_(other.ref_) {
73
+ other.ref_ = nullptr;
74
+ }
75
+ CFReleaser &operator=(CFReleaser &&other) noexcept {
76
+ if (this != &other) {
77
+ reset();
78
+ ref_ = other.ref_;
79
+ other.ref_ = nullptr;
80
+ }
81
+ return *this;
82
+ }
83
+ };
84
+
85
+ } // namespace FSMeta
@@ -2,6 +2,7 @@
2
2
 
3
3
  #include "../common/debug_log.h"
4
4
  #include "./fs_meta.h"
5
+ #include "./raii_utils.h"
5
6
 
6
7
  #include <CoreFoundation/CoreFoundation.h>
7
8
  #include <DiskArbitration/DiskArbitration.h>
@@ -36,55 +37,6 @@ static std::string CFStringToString(CFStringRef cfString) {
36
37
  return result;
37
38
  }
38
39
 
39
- // Improved CFReleaser with proper Core Foundation support
40
- template <typename T> class CFReleaser {
41
- private:
42
- T ref_;
43
-
44
- public:
45
- explicit CFReleaser(T ref = nullptr) noexcept : ref_(ref) {}
46
-
47
- // Delete copy operations
48
- CFReleaser(const CFReleaser &) = delete;
49
- CFReleaser &operator=(const CFReleaser &) = delete;
50
-
51
- // Move operations
52
- CFReleaser(CFReleaser &&other) noexcept : ref_(other.ref_) {
53
- other.ref_ = nullptr;
54
- }
55
-
56
- CFReleaser &operator=(CFReleaser &&other) noexcept {
57
- if (this != &other) {
58
- reset();
59
- ref_ = other.ref_;
60
- other.ref_ = nullptr;
61
- }
62
- return *this;
63
- }
64
-
65
- ~CFReleaser() { reset(); }
66
-
67
- void reset(T ref = nullptr) {
68
- if (ref_) {
69
- CFRelease(ref_);
70
- }
71
- ref_ = ref;
72
- }
73
-
74
- // Implicit conversion operator for Core Foundation APIs
75
- operator T() const noexcept { return ref_; }
76
-
77
- T get() const noexcept { return ref_; }
78
- bool isValid() const noexcept { return ref_ != nullptr; }
79
-
80
- // Release ownership
81
- T release() noexcept {
82
- T temp = ref_;
83
- ref_ = nullptr;
84
- return temp;
85
- }
86
- };
87
-
88
40
  class GetVolumeMetadataWorker : public MetadataWorkerBase {
89
41
  public:
90
42
  GetVolumeMetadataWorker(const std::string &mountPoint,
@@ -178,12 +130,12 @@ private:
178
130
  // Check if this is a network filesystem
179
131
  if (metadata.fstype == "smbfs" || metadata.fstype == "nfs" ||
180
132
  metadata.fstype == "afpfs" || metadata.fstype == "webdav") {
181
- // For network filesystems, we consider them healthy even without DA info
182
133
  metadata.remote = true;
183
134
  metadata.status = "healthy";
184
135
  return;
185
136
  }
186
137
 
138
+ // Create session on current thread
187
139
  CFReleaser<DASessionRef> session(DASessionCreate(kCFAllocatorDefault));
188
140
  if (!session.isValid()) {
189
141
  DEBUG_LOG("[GetVolumeMetadataWorker] Failed to create DA session");
@@ -193,21 +145,39 @@ private:
193
145
  }
194
146
 
195
147
  try {
196
- // RAII cleanup for RunLoop scheduling
148
+ // Get thread-local runloop or create new one if needed
149
+ CFRunLoopRef runLoop = CFRunLoopGetCurrent();
150
+ if (!runLoop) {
151
+ // If no runloop exists, create a new one for this thread
152
+ CFRunLoopRun();
153
+ runLoop = CFRunLoopGetCurrent();
154
+ if (!runLoop) {
155
+ throw std::runtime_error("Failed to create thread-local runloop");
156
+ }
157
+ }
158
+
159
+ // Schedule session with our runloop
160
+ DASessionScheduleWithRunLoop(session.get(), runLoop,
161
+ kCFRunLoopDefaultMode);
162
+
163
+ // Use RAII to ensure cleanup
197
164
  struct RunLoopCleaner {
198
165
  DASessionRef session;
199
- explicit RunLoopCleaner(DASessionRef s) : session(s) {}
166
+ CFRunLoopRef runLoop;
167
+ bool shouldStop;
168
+ RunLoopCleaner(DASessionRef s, CFRunLoopRef l, bool stop = false)
169
+ : session(s), runLoop(l), shouldStop(stop) {}
200
170
  ~RunLoopCleaner() {
201
- DASessionUnscheduleFromRunLoop(session, CFRunLoopGetCurrent(),
171
+ DASessionUnscheduleFromRunLoop(session, runLoop,
202
172
  kCFRunLoopDefaultMode);
173
+ if (shouldStop) {
174
+ CFRunLoopStop(runLoop);
175
+ }
203
176
  }
204
- };
177
+ } scopeGuard(session.get(), runLoop, !CFRunLoopGetCurrent());
205
178
 
206
- // Schedule session with RunLoop
207
- DASessionScheduleWithRunLoop(session.get(), CFRunLoopGetCurrent(),
208
- kCFRunLoopDefaultMode);
209
-
210
- auto scopeGuard = std::make_unique<RunLoopCleaner>(session.get());
179
+ // Run the run loop briefly to ensure DA is ready
180
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);
211
181
 
212
182
  CFReleaser<DADiskRef> disk(DADiskCreateFromBSDName(
213
183
  kCFAllocatorDefault, session.get(), metadata.mountFrom.c_str()));
@@ -228,12 +198,17 @@ private:
228
198
  return;
229
199
  }
230
200
 
201
+ // Ensure we have a complete description before continuing
202
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
203
+
204
+ // Process description synchronously since we're already on the right
205
+ // thread
231
206
  ProcessDiskDescription(description.get());
232
207
 
233
- // Only set ready if we got this far without errors
234
208
  if (metadata.status != "partial") {
235
209
  metadata.status = "healthy";
236
210
  }
211
+
237
212
  } catch (const std::exception &e) {
238
213
  DEBUG_LOG("[GetVolumeMetadataWorker] Exception: %s", e.what());
239
214
  metadata.status = "error";
@@ -1,7 +1,8 @@
1
1
  // src/darwin/volume_mount_points.cpp
2
2
  #include "../common/volume_mount_points.h"
3
3
  #include "../common/debug_log.h"
4
- #include "fs_meta.h"
4
+ #include "./fs_meta.h"
5
+ #include "./raii_utils.h"
5
6
  #include <chrono>
6
7
  #include <future>
7
8
  #include <sys/mount.h>
@@ -24,9 +25,12 @@ public:
24
25
  void Execute() override {
25
26
  DEBUG_LOG("[GetVolumeMountPointsWorker] Executing");
26
27
  try {
27
- // Get mount list - this is fast
28
- struct statfs *mntbufp;
29
- int count = getmntinfo(&mntbufp, MNT_WAIT);
28
+ MountBufferRAII mntbuf;
29
+ // Use MNT_NOWAIT for better performance - we'll verify accessibility
30
+ // separately and our error handling already covers mount state changes
31
+ // See https://github.com/swiftlang/swift-corelibs-foundation/issues/4649
32
+
33
+ int count = getmntinfo_r_np(mntbuf.ptr(), MNT_NOWAIT);
30
34
 
31
35
  if (count <= 0) {
32
36
  throw std::runtime_error("Failed to get mount information");
@@ -34,36 +38,39 @@ public:
34
38
 
35
39
  for (int i = 0; i < count; i++) {
36
40
  MountPoint mp;
37
- mp.mountPoint = mntbufp[i].f_mntonname;
38
- mp.fstype = mntbufp[i].f_fstypename;
41
+ mp.mountPoint = mntbuf.get()[i].f_mntonname;
42
+ mp.fstype = mntbuf.get()[i].f_fstypename;
39
43
  mp.error = ""; // Initialize error field
40
44
 
41
45
  DEBUG_LOG("[GetVolumeMountPointsWorker] Checking mount point: %s",
42
46
  mp.mountPoint.c_str());
43
47
 
44
48
  try {
45
- // Use shared_future to allow multiple gets
46
- std::shared_future<bool> future =
49
+ // Use RAII to manage future
50
+ auto future = std::make_shared<std::future<bool>>(
47
51
  std::async(std::launch::async, [path = mp.mountPoint]() {
48
- return access(path.c_str(), R_OK) == 0;
49
- }).share();
52
+ // Use faccessat for better security
53
+ return faccessat(AT_FDCWD, path.c_str(), R_OK, AT_EACCESS) == 0;
54
+ }));
50
55
 
51
- auto status = future.wait_for(std::chrono::milliseconds(timeoutMs_));
56
+ auto status = future->wait_for(std::chrono::milliseconds(timeoutMs_));
52
57
 
53
- if (status == std::future_status::timeout) {
58
+ switch (status) {
59
+ case std::future_status::timeout:
54
60
  mp.status = "disconnected";
55
61
  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) {
62
+ DEBUG_LOG("[GetVolumeMountPointsWorker] Access check timed out: %s",
63
+ mp.mountPoint.c_str());
64
+ break;
65
+
66
+ case std::future_status::ready:
60
67
  try {
61
- bool isAccessible = future.get();
68
+ bool isAccessible = future->get();
62
69
  mp.status = isAccessible ? "healthy" : "inaccessible";
63
70
  if (!isAccessible) {
64
71
  mp.error = "Path is not accessible";
65
72
  }
66
- DEBUG_LOG("[GetVolumeMountPointsWorker] Access check %s for: %s",
73
+ DEBUG_LOG("[GetVolumeMountPointsWorker] Access check %s: %s",
67
74
  isAccessible ? "succeeded" : "failed",
68
75
  mp.mountPoint.c_str());
69
76
  } catch (const std::exception &e) {
@@ -71,12 +78,14 @@ public:
71
78
  mp.error = std::string("Access check failed: ") + e.what();
72
79
  DEBUG_LOG("[GetVolumeMountPointsWorker] Exception: %s", e.what());
73
80
  }
74
- } else {
81
+ break;
82
+
83
+ default:
75
84
  mp.status = "error";
76
85
  mp.error = "Unexpected future status";
77
- DEBUG_LOG(
78
- "[GetVolumeMountPointsWorker] Unexpected future status for: %s",
79
- mp.mountPoint.c_str());
86
+ DEBUG_LOG("[GetVolumeMountPointsWorker] Unexpected status: %s",
87
+ mp.mountPoint.c_str());
88
+ break;
80
89
  }
81
90
  } catch (const std::exception &e) {
82
91
  mp.status = "error";
package/src/error.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  // src/error.ts
2
2
 
3
- import { isNumber } from "./number.js";
4
- import { compactValues, map, omit } from "./object.js";
5
- import { isBlank, isNotBlank } from "./string.js";
3
+ import { isNumber } from "./number";
4
+ import { compactValues, map, omit } from "./object";
5
+ import { isBlank, isNotBlank } from "./string";
6
6
 
7
7
  function toMessage(context: string, cause: unknown): string {
8
8
  const causeStr =
package/src/fs.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  import { type PathLike, type StatOptions, Stats, statSync } from "node:fs";
4
4
  import { opendir, stat } from "node:fs/promises";
5
5
  import { join, resolve } from "node:path";
6
- import { withTimeout } from "./async.js";
6
+ import { withTimeout } from "./async";
7
7
 
8
8
  /**
9
9
  * Wrapping node:fs/promises.stat() so we can mock it in tests.
package/src/glob.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/glob.ts
2
2
 
3
- import { isWindows } from "./platform.js";
4
- import { isNotBlank } from "./string.js";
3
+ import { isWindows } from "./platform";
4
+ import { isNotBlank } from "./string";
5
5
 
6
6
  const cache = new Map<string, RegExp>();
7
7
 
package/src/hidden.ts CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  import { rename } from "node:fs/promises";
4
4
  import { basename, dirname, join } from "node:path";
5
- import { WrappedError } from "./error.js";
6
- import { canStatAsync, statAsync } from "./fs.js";
7
- import { isRootDirectory, normalizePath } from "./path.js";
8
- import { isWindows } from "./platform.js";
9
- import type { HiddenMetadata } from "./types/hidden_metadata.js";
10
- import type { NativeBindingsFn } from "./types/native_bindings.js";
5
+ import { WrappedError } from "./error";
6
+ import { canStatAsync, statAsync } from "./fs";
7
+ import { isRootDirectory, normalizePath } from "./path";
8
+ import { isWindows } from "./platform";
9
+ import type { HiddenMetadata } from "./types/hidden_metadata";
10
+ import type { NativeBindingsFn } from "./types/native_bindings";
11
11
 
12
12
  const HiddenSupportByPlatform: Partial<
13
13
  Record<NodeJS.Platform, Pick<HiddenMetadata, "supported">>
package/src/index.ts CHANGED
@@ -1,17 +1,17 @@
1
1
  // src/index.ts
2
2
 
3
3
  import NodeGypBuild from "node-gyp-build";
4
- import { debug, debugLogContext, isDebugEnabled } from "./debuglog.js";
5
- import { defer } from "./defer.js";
6
- import { _dirname } from "./dirname.js";
7
- import { findAncestorDir } from "./fs.js";
8
- import type { HideMethod, SetHiddenResult } from "./hidden.js";
4
+ import { debug, debugLogContext, isDebugEnabled } from "./debuglog";
5
+ import { defer } from "./defer";
6
+ import { _dirname } from "./dirname";
7
+ import { findAncestorDir } from "./fs";
8
+ import type { HideMethod, SetHiddenResult } from "./hidden";
9
9
  import {
10
10
  getHiddenMetadataImpl,
11
11
  isHiddenImpl,
12
12
  isHiddenRecursiveImpl,
13
13
  setHiddenImpl,
14
- } from "./hidden.js";
14
+ } from "./hidden";
15
15
  import {
16
16
  IncludeSystemVolumesDefault,
17
17
  LinuxMountTablePathsDefault,
@@ -20,26 +20,22 @@ import {
20
20
  SystemFsTypesDefault,
21
21
  SystemPathPatternsDefault,
22
22
  TimeoutMsDefault,
23
- } from "./options.js";
24
- import type {
25
- StringEnum,
26
- StringEnumKeys,
27
- StringEnumType,
28
- } from "./string_enum.js";
29
- import type { SystemVolumeConfig } from "./system_volume.js";
30
- import type { HiddenMetadata } from "./types/hidden_metadata.js";
31
- import type { MountPoint } from "./types/mount_point.js";
32
- import { NativeBindings } from "./types/native_bindings.js";
33
- import type { Options } from "./types/options.js";
34
- import type { VolumeMetadata } from "./types/volume_metadata.js";
35
- import type { VolumeHealthStatus } from "./volume_health_status.js";
36
- import { VolumeHealthStatuses } from "./volume_health_status.js";
23
+ } from "./options";
24
+ import type { StringEnum, StringEnumKeys, StringEnumType } from "./string_enum";
25
+ import type { SystemVolumeConfig } from "./system_volume";
26
+ import type { HiddenMetadata } from "./types/hidden_metadata";
27
+ import type { MountPoint } from "./types/mount_point";
28
+ import { NativeBindings } from "./types/native_bindings";
29
+ import type { Options } from "./types/options";
30
+ import type { VolumeMetadata } from "./types/volume_metadata";
31
+ import type { VolumeHealthStatus } from "./volume_health_status";
32
+ import { VolumeHealthStatuses } from "./volume_health_status";
37
33
  import {
38
34
  getAllVolumeMetadataImpl,
39
35
  getVolumeMetadataImpl,
40
- } from "./volume_metadata.js";
41
- import type { GetVolumeMountPointOptions } from "./volume_mount_points.js";
42
- import { getVolumeMountPointsImpl } from "./volume_mount_points.js";
36
+ } from "./volume_metadata";
37
+ import type { GetVolumeMountPointOptions } from "./volume_mount_points";
38
+ import { getVolumeMountPointsImpl } from "./volume_mount_points";
43
39
 
44
40
  export type {
45
41
  GetVolumeMountPointOptions,
@@ -11,7 +11,7 @@ std::mutex BlkidCache::mutex_;
11
11
 
12
12
  // Constructor: Initializes the blkid cache with proper error handling
13
13
  BlkidCache::BlkidCache() : cache_(nullptr) {
14
- std::lock_guard<std::mutex> lock(mutex_);
14
+ const std::lock_guard<std::mutex> lock(mutex_);
15
15
  DEBUG_LOG("[BlkidCache] initializing cache");
16
16
  if (blkid_get_cache(&cache_, nullptr) != 0) {
17
17
  DEBUG_LOG("[BlkidCache] failed to initialize cache");
@@ -23,17 +23,20 @@ BlkidCache::BlkidCache() : cache_(nullptr) {
23
23
  // Destructor: Safely releases the blkid cache resource
24
24
  BlkidCache::~BlkidCache() {
25
25
  if (cache_) {
26
- std::lock_guard<std::mutex> lock(mutex_);
27
- DEBUG_LOG("[BlkidCache] releasing cache");
28
- try {
29
- blkid_put_cache(cache_);
30
- cache_ = nullptr; // Avoid double-release
31
- DEBUG_LOG("[BlkidCache] cache released successfully");
32
- } catch (const std::exception &e) {
33
- DEBUG_LOG("[BlkidCache] error releasing cache: %s", e.what());
34
- // Optional: Log error during cache cleanup
35
- // std::cerr << "Error while releasing blkid cache: " << e.what()
36
- // << std::endl;
26
+ const std::lock_guard<std::mutex> lock(mutex_);
27
+ if (cache_) { // Double-check after acquiring lock
28
+ DEBUG_LOG("[BlkidCache] releasing cache");
29
+ try {
30
+ blkid_put_cache(cache_);
31
+ cache_ = nullptr; // Avoid double-release
32
+ DEBUG_LOG("[BlkidCache] cache released successfully");
33
+ } catch (const std::exception &e) {
34
+ DEBUG_LOG("[BlkidCache] error releasing cache: %s", e.what());
35
+ cache_ = nullptr; // Ensure it's nulled even on error
36
+ // Optional: Log error during cache cleanup
37
+ // std::cerr << "Error while releasing blkid cache: " << e.what()
38
+ // << std::endl;
39
+ }
37
40
  }
38
41
  }
39
42
  }
@@ -3,8 +3,8 @@
3
3
  import { Dirent } from "node:fs";
4
4
  import { readdir, readlink } from "node:fs/promises";
5
5
  import { join, resolve } from "node:path";
6
- import { debug } from "../debuglog.js";
7
- import { decodeEscapeSequences } from "../string.js";
6
+ import { debug } from "../debuglog";
7
+ import { decodeEscapeSequences } from "../string";
8
8
 
9
9
  /**
10
10
  * Gets the UUID from symlinks for a given device path asynchronously
@@ -21,16 +21,16 @@ void GioMountPointsWorker::Execute() {
21
21
  try {
22
22
  DEBUG_LOG("[GioMountPoints] processing mounts");
23
23
 
24
- MountIterator::forEachMount([this](GMount *mount, GFile *root) {
25
- GCharPtr path(g_file_get_path(root));
24
+ MountIterator::forEachMount([this](GMount * /*mount*/, GFile *root) {
25
+ const GCharPtr path(g_file_get_path(root));
26
26
  if (path) {
27
- GFileInfoPtr info(g_file_query_filesystem_info(
27
+ const GFileInfoPtr info(g_file_query_filesystem_info(
28
28
  root, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, nullptr, nullptr));
29
29
  if (info) {
30
30
  const char *fs_type_str = g_file_info_get_attribute_string(
31
31
  info.get(), G_FILE_ATTRIBUTE_FILESYSTEM_TYPE);
32
32
  if (fs_type_str) {
33
- GCharPtr fs_type(g_strdup(fs_type_str));
33
+ const GCharPtr fs_type(g_strdup(fs_type_str));
34
34
  DEBUG_LOG("[GioMountPoints] found {mountPoint: %s, fsType: %s}",
35
35
  path.get(), fs_type.get());
36
36
  MountPoint point{};
@@ -51,11 +51,11 @@ void GioMountPointsWorker::Execute() {
51
51
  }
52
52
 
53
53
  void GioMountPointsWorker::OnOK() {
54
- Napi::HandleScope scope(Env());
55
- Napi::Array result = Napi::Array::New(Env());
54
+ const Napi::HandleScope scope(Env());
55
+ const Napi::Array result = Napi::Array::New(Env());
56
56
 
57
57
  for (size_t i = 0; i < mountPoints.size(); i++) {
58
- Napi::Object point = Napi::Object::New(Env());
58
+ const Napi::Object point = Napi::Object::New(Env());
59
59
  point.Set("mountPoint", mountPoints[i].mountPoint);
60
60
  point.Set("fstype", mountPoints[i].fstype);
61
61
  result.Set(i, point);
@@ -41,17 +41,28 @@ void MountIterator::forEachMount(const MountCallback &callback) {
41
41
  // Take an extra reference on the mount while we work with it
42
42
  g_object_ref(mount);
43
43
 
44
- GioResource<GFile> root(g_mount_get_root(mount));
45
- if (G_IS_FILE(root.get())) {
46
- bool continue_iteration = callback(mount, root.get());
47
- g_object_unref(mount);
44
+ try {
45
+ const GioResource<GFile> root(g_mount_get_root(mount));
46
+
47
+ // Check both for null and valid GFile
48
+ if (root.get() && G_IS_FILE(root.get())) {
49
+ const bool continue_iteration = callback(mount, root.get());
50
+ g_object_unref(mount);
48
51
 
49
- if (!continue_iteration) {
50
- break;
52
+ if (!continue_iteration) {
53
+ break;
54
+ }
55
+ } else {
56
+ DEBUG_LOG(
57
+ "[gio::MountIterator::forEachMount] Invalid root file object");
58
+ g_object_unref(mount);
51
59
  }
52
- } else {
53
- DEBUG_LOG("[gio::MountIterator::forEachMount] Invalid root file object");
60
+ } catch (const std::exception &e) {
61
+ DEBUG_LOG("[gio::MountIterator::forEachMount] Exception during mount "
62
+ "processing: %s",
63
+ e.what());
54
64
  g_object_unref(mount);
65
+ throw; // Re-throw to maintain current behavior
55
66
  }
56
67
  }
57
68
 
@@ -17,7 +17,7 @@ void addMountMetadata(const std::string &mountPoint, VolumeMetadata &metadata) {
17
17
  mountPoint.c_str());
18
18
 
19
19
  MountIterator::forEachMount([&](GMount *mount, GFile *root) {
20
- GCharPtr path(g_file_get_path(root));
20
+ const GCharPtr path(g_file_get_path(root));
21
21
  if (!path || mountPoint != path.get()) {
22
22
  return true; // Continue iteration
23
23
  }
@@ -27,25 +27,25 @@ void addMountMetadata(const std::string &mountPoint, VolumeMetadata &metadata) {
27
27
  path.get());
28
28
 
29
29
  // Get volume information
30
- GObjectPtr<GVolume> volume(g_mount_get_volume(mount));
31
- if (volume) {
32
- GCharPtr label(g_volume_get_name(volume.get()));
33
- if (label) {
34
- DEBUG_LOG("[gio::addMountMetadata] {mountPoint: %s, label: %s",
30
+ const GObjectPtr<GVolume> volume(g_mount_get_volume(mount));
31
+ if (volume && volume.get()) {
32
+ const GCharPtr label(g_volume_get_name(volume.get()));
33
+ if (label && label.get()) {
34
+ DEBUG_LOG("[gio::addMountMetadata] {mountPoint: %s, label: %s}",
35
35
  path.get(), label.get());
36
36
  metadata.label = label.get();
37
37
  }
38
38
  }
39
39
 
40
- GCharPtr mount_name(g_mount_get_name(mount));
40
+ const GCharPtr mount_name(g_mount_get_name(mount));
41
41
  if (mount_name) {
42
42
  metadata.mountName = mount_name.get();
43
43
  }
44
44
 
45
- GObjectPtr<GFile> location(g_mount_get_default_location(mount));
46
- if (location) {
47
- GCharPtr uri(g_file_get_uri(location.get()));
48
- if (uri) {
45
+ const GObjectPtr<GFile> location(g_mount_get_default_location(mount));
46
+ if (location && location.get()) {
47
+ const GCharPtr uri(g_file_get_uri(location.get()));
48
+ if (uri && uri.get()) {
49
49
  DEBUG_LOG("[gio::addMountMetadata] {mountPoint: %s, uri: %s}",
50
50
  path.get(), uri.get());
51
51
  metadata.uri = uri.get();
@@ -53,13 +53,13 @@ void addMountMetadata(const std::string &mountPoint, VolumeMetadata &metadata) {
53
53
  }
54
54
 
55
55
  if (metadata.fstype.empty()) {
56
- GFileInfoPtr info(g_file_query_filesystem_info(
56
+ const GFileInfoPtr info(g_file_query_filesystem_info(
57
57
  root, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, nullptr, nullptr));
58
58
  if (info) {
59
59
  const char *fs_type_str = g_file_info_get_attribute_string(
60
60
  info.get(), G_FILE_ATTRIBUTE_FILESYSTEM_TYPE);
61
61
  if (fs_type_str) {
62
- GCharPtr fs_type(g_strdup(fs_type_str));
62
+ const GCharPtr fs_type(g_strdup(fs_type_str));
63
63
  DEBUG_LOG("[gio::addMountMetadata] {mountPoint: %s, fsType: %s}",
64
64
  path.get(), fs_type.get());
65
65
  metadata.fstype = fs_type.get();
@@ -68,9 +68,9 @@ void addMountMetadata(const std::string &mountPoint, VolumeMetadata &metadata) {
68
68
  }
69
69
 
70
70
  if (metadata.mountFrom.empty()) {
71
- GObjectPtr<GDrive> drive(g_mount_get_drive(mount));
71
+ const GObjectPtr<GDrive> drive(g_mount_get_drive(mount));
72
72
  if (drive) {
73
- GCharPtr unix_device(g_drive_get_identifier(
73
+ const GCharPtr unix_device(g_drive_get_identifier(
74
74
  drive.get(), G_DRIVE_IDENTIFIER_KIND_UNIX_DEVICE));
75
75
  if (unix_device) {
76
76
  DEBUG_LOG("[gio::addMountMetadata] {mountPoint: %s, mountFrom: %s}",