@photostructure/fs-metadata 0.4.0 → 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 (179) hide show
  1. package/C++_REVIEW_TODO.md +291 -0
  2. package/CHANGELOG.md +29 -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 -41
  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/error.ts +3 -3
  101. package/src/fs.ts +1 -1
  102. package/src/glob.ts +2 -2
  103. package/src/hidden.ts +6 -6
  104. package/src/index.ts +19 -23
  105. package/src/linux/blkid_cache.cpp +15 -12
  106. package/src/linux/dev_disk.ts +2 -2
  107. package/src/linux/gio_mount_points.cpp +7 -7
  108. package/src/linux/gio_utils.cpp +19 -8
  109. package/src/linux/gio_volume_metadata.cpp +15 -15
  110. package/src/linux/mount_points.ts +9 -9
  111. package/src/linux/mtab.ts +7 -7
  112. package/src/linux/volume_metadata.cpp +6 -1
  113. package/src/object.ts +1 -1
  114. package/src/options.ts +3 -3
  115. package/src/path.ts +2 -2
  116. package/src/remote_info.ts +5 -5
  117. package/src/system_volume.ts +8 -8
  118. package/src/test-utils/assert.ts +2 -2
  119. package/src/test-utils/debuglog-child.ts +1 -3
  120. package/src/test-utils/debuglog-enabled-child.ts +10 -0
  121. package/src/test-utils/hidden-tests.ts +1 -1
  122. package/src/test-utils/platform.ts +3 -3
  123. package/src/types/native_bindings.ts +3 -3
  124. package/src/types/volume_metadata.ts +2 -2
  125. package/src/unc.ts +2 -2
  126. package/src/uuid.ts +1 -1
  127. package/src/volume_health_status.ts +6 -6
  128. package/src/volume_metadata.ts +20 -23
  129. package/src/volume_mount_points.ts +12 -17
  130. package/src/windows/drive_status.h +30 -13
  131. package/src/windows/hidden.cpp +12 -0
  132. package/src/windows/volume_metadata.cpp +17 -7
  133. package/tsup.config.ts +8 -2
  134. package/dist/index.cjs +0 -1439
  135. package/dist/index.cjs.map +0 -1
  136. package/dist/index.mjs +0 -1396
  137. package/dist/index.mjs.map +0 -1
  138. package/dist/types/array.d.ts +0 -25
  139. package/dist/types/async.d.ts +0 -42
  140. package/dist/types/debuglog.d.ts +0 -3
  141. package/dist/types/defer.d.ts +0 -10
  142. package/dist/types/dirname.d.ts +0 -1
  143. package/dist/types/error.d.ts +0 -17
  144. package/dist/types/fs.d.ts +0 -22
  145. package/dist/types/glob.d.ts +0 -17
  146. package/dist/types/hidden.d.ts +0 -29
  147. package/dist/types/index.d.ts +0 -91
  148. package/dist/types/linux/dev_disk.d.ts +0 -13
  149. package/dist/types/linux/mount_points.d.ts +0 -6
  150. package/dist/types/linux/mtab.d.ts +0 -47
  151. package/dist/types/mount_point.d.ts +0 -2
  152. package/dist/types/number.d.ts +0 -3
  153. package/dist/types/object.d.ts +0 -18
  154. package/dist/types/options.d.ts +0 -33
  155. package/dist/types/path.d.ts +0 -17
  156. package/dist/types/platform.d.ts +0 -4
  157. package/dist/types/random.d.ts +0 -12
  158. package/dist/types/remote_info.d.ts +0 -6
  159. package/dist/types/stack_path.d.ts +0 -2
  160. package/dist/types/string.d.ts +0 -37
  161. package/dist/types/string_enum.d.ts +0 -19
  162. package/dist/types/system_volume.d.ts +0 -14
  163. package/dist/types/types/hidden_metadata.d.ts +0 -32
  164. package/dist/types/types/mount_point.d.ts +0 -46
  165. package/dist/types/types/native_bindings.d.ts +0 -51
  166. package/dist/types/types/options.d.ts +0 -47
  167. package/dist/types/types/remote_info.d.ts +0 -33
  168. package/dist/types/types/volume_metadata.d.ts +0 -46
  169. package/dist/types/unc.d.ts +0 -11
  170. package/dist/types/units.d.ts +0 -38
  171. package/dist/types/uuid.d.ts +0 -16
  172. package/dist/types/volume_health_status.d.ts +0 -24
  173. package/dist/types/volume_metadata.d.ts +0 -8
  174. package/dist/types/volume_mount_points.d.ts +0 -6
  175. package/jest.config.base.cjs +0 -63
  176. package/prebuilds/darwin-arm64/@photostructure+fs-metadata.glibc.node +0 -0
  177. package/prebuilds/linux-arm64/@photostructure+fs-metadata.musl.node +0 -0
  178. package/prebuilds/linux-x64/@photostructure+fs-metadata.musl.node +0 -0
  179. package/prebuilds/win32-x64/@photostructure+fs-metadata.glibc.node +0 -0
@@ -2,6 +2,7 @@
2
2
 
3
3
  #pragma once
4
4
  #include "../common/debug_log.h"
5
+ #include <atomic>
5
6
  #include <memory>
6
7
  #include <string>
7
8
  #include <vector>
@@ -35,15 +36,15 @@ inline std::string DriveStatusToString(DriveStatus status) {
35
36
  class IOOperation {
36
37
  private:
37
38
  HANDLE completionEvent;
38
- DriveStatus result;
39
+ std::atomic<DriveStatus> result;
39
40
  std::string path;
40
41
  DWORD threadId;
41
- volatile bool shouldTerminate;
42
+ std::atomic<bool> shouldTerminate;
42
43
  HANDLE threadHandle; // Store thread handle as member
43
44
 
44
45
  public:
45
46
  IOOperation()
46
- : result(DriveStatus::Unknown), threadId(0), shouldTerminate(false),
47
+ : result{DriveStatus::Unknown}, threadId(0), shouldTerminate{false},
47
48
  threadHandle(NULL) {
48
49
  completionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
49
50
  }
@@ -59,12 +60,20 @@ public:
59
60
  if (threadHandle) {
60
61
  DEBUG_LOG("[IOOperation] Cleaning up thread %lu", threadId);
61
62
  // Signal termination
62
- shouldTerminate = true;
63
-
64
- // Give thread chance to exit gracefully
65
- if (WaitForSingleObject(threadHandle, 100) != WAIT_OBJECT_0) {
66
- DEBUG_LOG("[IOOperation] Force terminating thread %lu", threadId);
67
- TerminateThread(threadHandle, 1);
63
+ shouldTerminate.store(true, std::memory_order_release);
64
+
65
+ // Wake up thread if it's waiting on something
66
+ SetEvent(completionEvent);
67
+
68
+ // Give thread chance to exit gracefully (increased timeout)
69
+ DWORD waitResult = WaitForSingleObject(threadHandle, 1000);
70
+ if (waitResult != WAIT_OBJECT_0) {
71
+ DEBUG_LOG("[IOOperation] WARNING: Thread %lu did not exit gracefully "
72
+ "after 1000ms",
73
+ threadId);
74
+ // NEVER use TerminateThread - it's extremely dangerous
75
+ // Instead, log the issue and abandon the thread
76
+ // The thread will eventually exit when the process terminates
68
77
  }
69
78
 
70
79
  CloseHandle(threadHandle);
@@ -81,15 +90,23 @@ public:
81
90
 
82
91
  DEBUG_LOG("[WorkerThread] Starting search on path: %s", searchPath.c_str());
83
92
 
93
+ // Check if we should terminate before starting work
94
+ if (self->shouldTerminate.load(std::memory_order_acquire)) {
95
+ DEBUG_LOG("[WorkerThread] Thread %lu terminating before work",
96
+ self->threadId);
97
+ return 0;
98
+ }
99
+
84
100
  findHandle = FindFirstFileExA(
85
101
  searchPath.c_str(), FindExInfoBasic, &findData, FindExSearchNameMatch,
86
102
  NULL, FIND_FIRST_EX_LARGE_FETCH | FIND_FIRST_EX_ON_DISK_ENTRIES_ONLY);
87
103
 
88
104
  if (findHandle == INVALID_HANDLE_VALUE) {
89
- self->result = self->MapErrorToDriveStatus(GetLastError());
105
+ self->result.store(self->MapErrorToDriveStatus(GetLastError()),
106
+ std::memory_order_release);
90
107
  DEBUG_LOG("[WorkerThread] Search failed with error: %lu", GetLastError());
91
108
  } else {
92
- self->result = DriveStatus::Healthy;
109
+ self->result.store(DriveStatus::Healthy, std::memory_order_release);
93
110
  FindClose(findHandle);
94
111
  DEBUG_LOG("[WorkerThread] Search completed successfully");
95
112
  }
@@ -109,7 +126,7 @@ public:
109
126
 
110
127
  path = checkPath;
111
128
  ResetEvent(completionEvent);
112
- shouldTerminate = false;
129
+ shouldTerminate.store(false, std::memory_order_release);
113
130
 
114
131
  threadHandle = CreateThread(NULL, 0, WorkerThread, this, 0, &threadId);
115
132
  if (!threadHandle) {
@@ -131,7 +148,7 @@ public:
131
148
  CloseHandle(threadHandle);
132
149
  DEBUG_LOG("[CheckDriveStatus] CloseHandle %lu completed", threadId);
133
150
  threadHandle = NULL;
134
- return result;
151
+ return result.load(std::memory_order_acquire);
135
152
  }
136
153
 
137
154
  private:
@@ -70,6 +70,12 @@ public:
70
70
 
71
71
  void Execute() override {
72
72
  try {
73
+ // Add path validation to prevent directory traversal
74
+ if (path.find("..") != std::string::npos) {
75
+ throw FSException("Invalid path containing '..'",
76
+ ERROR_INVALID_PARAMETER);
77
+ }
78
+
73
79
  auto wpath = PathConverter::ToWString(path);
74
80
  FileAttributeHandler handler(wpath);
75
81
  result = handler.isHidden();
@@ -98,6 +104,12 @@ public:
98
104
 
99
105
  void Execute() override {
100
106
  try {
107
+ // Add path validation to prevent directory traversal
108
+ if (path.find("..") != std::string::npos) {
109
+ throw FSException("Invalid path containing '..'",
110
+ ERROR_INVALID_PARAMETER);
111
+ }
112
+
101
113
  auto wpath = PathConverter::ToWString(path);
102
114
  FileAttributeHandler handler(wpath);
103
115
  handler.setHidden(value);
@@ -24,11 +24,20 @@ class WNetConnection {
24
24
 
25
25
  public:
26
26
  explicit WNetConnection(const std::string &path)
27
- : drivePath(path.substr(0, 2)), bufferSize(BUFFER_SIZE),
28
- buffer(std::make_unique<char[]>(BUFFER_SIZE)) {
27
+ : drivePath(path.substr(0, 2)), bufferSize(MAX_PATH) {
28
+
29
+ // Allocate initial buffer
30
+ buffer = std::make_unique<char[]>(bufferSize);
29
31
 
30
32
  DWORD result =
31
33
  WNetGetConnectionA(drivePath.c_str(), buffer.get(), &bufferSize);
34
+
35
+ if (result == ERROR_MORE_DATA) {
36
+ // bufferSize now contains the required size
37
+ buffer = std::make_unique<char[]>(bufferSize);
38
+ result = WNetGetConnectionA(drivePath.c_str(), buffer.get(), &bufferSize);
39
+ }
40
+
32
41
  isValid = (result == NO_ERROR);
33
42
  }
34
43
 
@@ -70,8 +79,9 @@ inline std::string GetVolumeGUID(const std::string &mountPoint) {
70
79
 
71
80
  // RAII wrapper for volume information
72
81
  class VolumeInfo {
73
- char volumeName[BUFFER_SIZE];
74
- char fstype[BUFFER_SIZE];
82
+ static constexpr DWORD VOLUME_NAME_SIZE = MAX_PATH + 1; // 261 characters
83
+ char volumeName[VOLUME_NAME_SIZE];
84
+ char fstype[VOLUME_NAME_SIZE];
75
85
  DWORD serialNumber;
76
86
  DWORD maxComponentLen;
77
87
  DWORD fsFlags;
@@ -79,9 +89,9 @@ class VolumeInfo {
79
89
 
80
90
  public:
81
91
  explicit VolumeInfo(const std::string &mountPoint) {
82
- valid = GetVolumeInformationA(mountPoint.c_str(), volumeName, BUFFER_SIZE,
83
- &serialNumber, &maxComponentLen, &fsFlags,
84
- fstype, BUFFER_SIZE);
92
+ valid = GetVolumeInformationA(
93
+ mountPoint.c_str(), volumeName, VOLUME_NAME_SIZE, &serialNumber,
94
+ &maxComponentLen, &fsFlags, fstype, VOLUME_NAME_SIZE);
85
95
 
86
96
  if (!valid && GetLastError() != ERROR_NOT_READY) {
87
97
  throw FSException("GetVolumeInformation", GetLastError());
package/tsup.config.ts CHANGED
@@ -1,9 +1,15 @@
1
1
  import { defineConfig } from "tsup";
2
2
 
3
3
  export default defineConfig({
4
+ entry: ["src/index.ts"],
5
+ format: ["cjs", "esm"],
6
+ dts: true, // Generate .d.ts files automatically
7
+ clean: true, // Clean dist before each build
8
+ sourcemap: true,
4
9
  outExtension: ({ format }) => ({
5
10
  js: format === "cjs" ? ".cjs" : ".mjs", // Use .cjs for CommonJS and .mjs for ESM
6
11
  }),
7
- shims: true,
8
- sourcemap: true,
12
+ shims: true, // Inject CJS shims (__dirname, __filename) in ESM output
13
+ target: "es2022", // Align with TypeScript target
14
+ tsconfig: "tsconfig.build.json", // Use a single tsconfig for building
9
15
  });