@cetapod/react-native-smb 1.0.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 (137) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +72 -0
  3. package/ReactNativeSmb.podspec +59 -0
  4. package/cpp/ios/OnLoad.mm +24 -0
  5. package/cpp/shared/HybridSMB.cpp +324 -0
  6. package/cpp/shared/HybridSMB.hpp +102 -0
  7. package/cpp/shared/ReactNativeSmb.hpp +428 -0
  8. package/cpp/shared/connection/ContextRequest.hpp +27 -0
  9. package/cpp/shared/connection/ContextRequestQueue.cpp +85 -0
  10. package/cpp/shared/connection/ContextRequestQueue.hpp +28 -0
  11. package/cpp/shared/connection/MetadataContextLease.hpp +33 -0
  12. package/cpp/shared/connection/PoolContextHandle.cpp +35 -0
  13. package/cpp/shared/connection/PoolContextHandle.hpp +44 -0
  14. package/cpp/shared/connection/PoolSlot.cpp +73 -0
  15. package/cpp/shared/connection/PoolSlot.hpp +46 -0
  16. package/cpp/shared/connection/PoolTypes.hpp +41 -0
  17. package/cpp/shared/connection/SmbConnection.cpp +385 -0
  18. package/cpp/shared/connection/SmbConnection.hpp +93 -0
  19. package/cpp/shared/connection/SmbConnectionPool.cpp +316 -0
  20. package/cpp/shared/connection/SmbConnectionPool.hpp +84 -0
  21. package/cpp/shared/core/CancellationToken.hpp +22 -0
  22. package/cpp/shared/core/OperatorContext.hpp +24 -0
  23. package/cpp/shared/core/SmbEnums.hpp +126 -0
  24. package/cpp/shared/core/SmbTask.cpp +131 -0
  25. package/cpp/shared/core/SmbTask.hpp +52 -0
  26. package/cpp/shared/core/SmbTaskCore.cpp +222 -0
  27. package/cpp/shared/core/SmbTaskCore.hpp +107 -0
  28. package/cpp/shared/core/SmbTypes.hpp +35 -0
  29. package/cpp/shared/core/TaskObserverHub.cpp +166 -0
  30. package/cpp/shared/core/TaskObserverHub.hpp +66 -0
  31. package/cpp/shared/core/TaskSnapshotCodec.cpp +32 -0
  32. package/cpp/shared/core/TaskSnapshotCodec.hpp +41 -0
  33. package/cpp/shared/io/SmbCopyTree.hpp +114 -0
  34. package/cpp/shared/io/SmbDirScan.hpp +80 -0
  35. package/cpp/shared/io/SmbFileIO.cpp +1355 -0
  36. package/cpp/shared/io/SmbFileIO.hpp +45 -0
  37. package/cpp/shared/io/SmbPathUtil.hpp +75 -0
  38. package/cpp/shared/io/SmbSecurity.cpp +308 -0
  39. package/cpp/shared/io/SmbSecurity.hpp +32 -0
  40. package/cpp/shared/io/SmbTreeOps.hpp +112 -0
  41. package/cpp/shared/operators/Operator.hpp +35 -0
  42. package/cpp/shared/operators/OperatorBase.cpp +57 -0
  43. package/cpp/shared/operators/OperatorBase.hpp +57 -0
  44. package/cpp/shared/operators/connection/ConnectOperator.cpp +19 -0
  45. package/cpp/shared/operators/connection/ConnectOperator.hpp +22 -0
  46. package/cpp/shared/operators/connection/ConnectShareOperator.cpp +19 -0
  47. package/cpp/shared/operators/connection/ConnectShareOperator.hpp +21 -0
  48. package/cpp/shared/operators/connection/DisconnectOperator.cpp +12 -0
  49. package/cpp/shared/operators/connection/DisconnectOperator.hpp +15 -0
  50. package/cpp/shared/operators/connection/InitializeOperator.cpp +12 -0
  51. package/cpp/shared/operators/connection/InitializeOperator.hpp +22 -0
  52. package/cpp/shared/operators/connection/ListSharesOperator.cpp +13 -0
  53. package/cpp/shared/operators/connection/ListSharesOperator.hpp +18 -0
  54. package/cpp/shared/operators/listing/GetPathInfoOperator.cpp +43 -0
  55. package/cpp/shared/operators/listing/GetPathInfoOperator.hpp +21 -0
  56. package/cpp/shared/operators/listing/GetSecurityDescriptorOperator.cpp +202 -0
  57. package/cpp/shared/operators/listing/GetSecurityDescriptorOperator.hpp +22 -0
  58. package/cpp/shared/operators/listing/ListDirectoryOperator.cpp +20 -0
  59. package/cpp/shared/operators/listing/ListDirectoryOperator.hpp +24 -0
  60. package/cpp/shared/operators/mutate/CopyItemOperator.cpp +19 -0
  61. package/cpp/shared/operators/mutate/CopyItemOperator.hpp +25 -0
  62. package/cpp/shared/operators/mutate/CreateDirectoryOperator.cpp +28 -0
  63. package/cpp/shared/operators/mutate/CreateDirectoryOperator.hpp +20 -0
  64. package/cpp/shared/operators/mutate/DeleteItemOperator.cpp +96 -0
  65. package/cpp/shared/operators/mutate/DeleteItemOperator.hpp +23 -0
  66. package/cpp/shared/operators/mutate/DuplicateItemOperator.cpp +48 -0
  67. package/cpp/shared/operators/mutate/DuplicateItemOperator.hpp +23 -0
  68. package/cpp/shared/operators/mutate/MoveItemOperator.cpp +32 -0
  69. package/cpp/shared/operators/mutate/MoveItemOperator.hpp +21 -0
  70. package/cpp/shared/operators/mutate/RenameItemOperator.cpp +37 -0
  71. package/cpp/shared/operators/mutate/RenameItemOperator.hpp +21 -0
  72. package/cpp/shared/operators/transfer/DownloadFileOperator.cpp +31 -0
  73. package/cpp/shared/operators/transfer/DownloadFileOperator.hpp +21 -0
  74. package/cpp/shared/operators/transfer/UploadFileOperator.cpp +27 -0
  75. package/cpp/shared/operators/transfer/UploadFileOperator.hpp +21 -0
  76. package/cpp/shared/util/SmbErrorMapper.hpp +229 -0
  77. package/cpp/shared/util/SmbLog.hpp +20 -0
  78. package/docs/API.md +105 -0
  79. package/docs/ARCHITECTURE.md +70 -0
  80. package/docs/TASK_MODEL.md +149 -0
  81. package/ios/include/smb2/libsmb2-dcerpc-lsa.h +172 -0
  82. package/ios/include/smb2/libsmb2-dcerpc-srvsvc.h +180 -0
  83. package/ios/include/smb2/libsmb2-dcerpc.h +155 -0
  84. package/ios/include/smb2/libsmb2-raw.h +497 -0
  85. package/ios/include/smb2/libsmb2.h +1384 -0
  86. package/ios/include/smb2/smb2-errors.h +549 -0
  87. package/ios/include/smb2/smb2.h +1251 -0
  88. package/ios/libs/device/libsmb2-device.a +0 -0
  89. package/ios/libs/libsmb2.xcframework/Info.plist +48 -0
  90. package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2-dcerpc-lsa.h +172 -0
  91. package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2-dcerpc-srvsvc.h +180 -0
  92. package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2-dcerpc.h +155 -0
  93. package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2-raw.h +497 -0
  94. package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2.h +1384 -0
  95. package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/smb2-errors.h +549 -0
  96. package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/smb2.h +1251 -0
  97. package/ios/libs/libsmb2.xcframework/ios-arm64/libsmb2.a +0 -0
  98. package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2-dcerpc-lsa.h +172 -0
  99. package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2-dcerpc-srvsvc.h +180 -0
  100. package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2-dcerpc.h +155 -0
  101. package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2-raw.h +497 -0
  102. package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2.h +1384 -0
  103. package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/smb2-errors.h +549 -0
  104. package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/smb2.h +1251 -0
  105. package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/libsmb2.a +0 -0
  106. package/ios/libs/simulator/libsmb2-simulator.a +0 -0
  107. package/lib/ReactNativeSmb.d.ts +200 -0
  108. package/lib/ReactNativeSmb.js +1 -0
  109. package/lib/bridge/decode.d.ts +4 -0
  110. package/lib/bridge/decode.js +92 -0
  111. package/lib/core/SMB.d.ts +3 -0
  112. package/lib/core/SMB.js +4 -0
  113. package/lib/core/SmbClient.d.ts +35 -0
  114. package/lib/core/SmbClient.js +99 -0
  115. package/lib/core/SmbTask.d.ts +41 -0
  116. package/lib/core/SmbTask.js +74 -0
  117. package/lib/core/TransferStore.d.ts +22 -0
  118. package/lib/core/TransferStore.js +88 -0
  119. package/lib/core/ids.d.ts +1 -0
  120. package/lib/core/ids.js +3 -0
  121. package/lib/core/taskResult.d.ts +3 -0
  122. package/lib/core/taskResult.js +52 -0
  123. package/lib/hooks/useSubscribe.d.ts +20 -0
  124. package/lib/hooks/useSubscribe.js +36 -0
  125. package/lib/hooks/useTransfers.d.ts +7 -0
  126. package/lib/hooks/useTransfers.js +29 -0
  127. package/lib/index.d.ts +7 -0
  128. package/lib/index.js +7 -0
  129. package/lib/labels.d.ts +6 -0
  130. package/lib/labels.js +71 -0
  131. package/lib/types.d.ts +141 -0
  132. package/lib/types.js +59 -0
  133. package/lib/utils.d.ts +79 -0
  134. package/lib/utils.js +153 -0
  135. package/package.json +63 -0
  136. package/scripts/build-libsmb2.sh +143 -0
  137. package/scripts/create-xcframework.sh +84 -0
@@ -0,0 +1,316 @@
1
+ #include "SmbConnectionPool.hpp"
2
+
3
+ #include <stdexcept>
4
+ #include <utility>
5
+
6
+ #include "../util/SmbLog.hpp"
7
+
8
+ namespace react_native_smb {
9
+
10
+ namespace {
11
+ const char* acquireModeName(AcquireMode mode) {
12
+ return mode == AcquireMode::Interactive ? "interactive" : "metadata";
13
+ }
14
+ } // namespace
15
+
16
+ SmbConnectionPool::SmbConnectionPool(size_t maxConnections) : maxConnections_(maxConnections) {
17
+ slots_.reserve(maxConnections);
18
+ slots_.emplace_back(0);
19
+ }
20
+
21
+ SmbConnectionPool::~SmbConnectionPool() {
22
+ for (auto& slot : slots_) {
23
+ if (slot.manager()) slot.manager()->disconnect();
24
+ }
25
+ }
26
+
27
+ PoolSlot& SmbConnectionPool::primarySlot() { return slots_[0]; }
28
+
29
+ const PoolSlot& SmbConnectionPool::primarySlot() const { return slots_[0]; }
30
+
31
+ bool SmbConnectionPool::slotNeedsActivate(const PoolSlot& slot) const {
32
+ const auto* mgr = slot.manager();
33
+ if (!mgr) return true;
34
+ return !mgr->isInitialized() && !serverUrl_.empty() && credentials_ != nullptr;
35
+ }
36
+
37
+ size_t SmbConnectionPool::findIdleSlot(AcquireMode mode) const {
38
+ if (mode == AcquireMode::Interactive) {
39
+ if (slots_[0].isIdle()) return 0;
40
+ for (size_t i = 1; i < slots_.size(); ++i) {
41
+ if (slots_[i].isIdle()) return i;
42
+ }
43
+ return SIZE_MAX;
44
+ }
45
+ for (size_t i = 1; i < slots_.size(); ++i) {
46
+ if (slots_[i].isIdle()) return i;
47
+ }
48
+ return SIZE_MAX;
49
+ }
50
+
51
+ size_t SmbConnectionPool::maybeGrowSlot() {
52
+ if (slots_.size() >= maxConnections_) return SIZE_MAX;
53
+ const size_t idx = slots_.size();
54
+ slots_.emplace_back(idx);
55
+ return idx;
56
+ }
57
+
58
+ void SmbConnectionPool::tryAssignNext() {
59
+ std::vector<PoolSlotInfo> snap;
60
+ bool assigned = false;
61
+ for (;;) {
62
+ ContextRequestPtr req;
63
+ size_t slotIdx = SIZE_MAX;
64
+ bool needsActivate = false;
65
+ PoolConnectParams params;
66
+ std::string taskId;
67
+
68
+ {
69
+ std::unique_lock<std::mutex> lock(mutex_);
70
+ queue_.removeCancelledFromHead();
71
+ req = queue_.takeNext();
72
+ if (!req) break;
73
+
74
+ slotIdx = findIdleSlot(req->mode);
75
+ if (slotIdx == SIZE_MAX && req->mode == AcquireMode::Metadata) slotIdx = maybeGrowSlot();
76
+ if (slotIdx == SIZE_MAX) {
77
+ queue_.requeueFront(std::move(req));
78
+ break;
79
+ }
80
+
81
+ PoolSlot& slot = slots_[slotIdx];
82
+ if (!slot.accepts(req->mode)) {
83
+ queue_.requeueFront(std::move(req));
84
+ break;
85
+ }
86
+
87
+ slot.markActivating();
88
+ params.serverUrl = serverUrl_;
89
+ params.shareName = shareName_;
90
+ params.credentials = credentials_;
91
+ needsActivate = slotNeedsActivate(slot);
92
+ taskId = req->taskId;
93
+
94
+ lock.unlock();
95
+
96
+ bool ok = true;
97
+ try {
98
+ if (needsActivate)
99
+ slot.activate(params, taskId);
100
+ else
101
+ slot.ensureShare(shareName_, taskId);
102
+ } catch (...) {
103
+ ok = false;
104
+ }
105
+
106
+ lock.lock();
107
+
108
+ if (!ok) {
109
+ SMB_LOG("Pool assign failed slot=%zu kind=%s taskId=%s", slotIdx, operationName(req->kind), req->taskId.c_str());
110
+ slot.resetBroken();
111
+ req->fulfilled.store(true, std::memory_order_release);
112
+ try {
113
+ req->promise.set_value(PoolContextHandle{});
114
+ } catch (...) {
115
+ }
116
+ assignCv_.notify_all();
117
+ break;
118
+ }
119
+
120
+ slot.assign(req->id, req->taskId, req->kind);
121
+ SMB_LOG("Pool assign slot=%zu kind=%s taskId=%s mode=%s", slotIdx, operationName(req->kind), req->taskId.c_str(),
122
+ acquireModeName(req->mode));
123
+ PoolContextHandle handle(this, slotIdx, slot.manager());
124
+ req->fulfilled.store(true, std::memory_order_release);
125
+ try {
126
+ req->promise.set_value(std::move(handle));
127
+ } catch (...) {
128
+ slot.release();
129
+ req->fulfilled.store(false, std::memory_order_release);
130
+ break;
131
+ }
132
+ assigned = true;
133
+ }
134
+ }
135
+ {
136
+ std::lock_guard<std::mutex> lock(mutex_);
137
+ if (assigned) {
138
+ snap.reserve(slots_.size());
139
+ for (const auto& slot : slots_) snap.push_back(slot.snapshot(slots_.size()));
140
+ }
141
+ assignCv_.notify_all();
142
+ }
143
+ if (assigned) {
144
+ for (auto& [id, fn] : observers_) {
145
+ try {
146
+ if (fn) fn(snap);
147
+ } catch (...) {
148
+ }
149
+ }
150
+ }
151
+ }
152
+
153
+ PoolContextHandle SmbConnectionPool::requestContext(AcquireMode mode, SmbOperatorKind kind, const std::string& taskId) {
154
+ ContextRequestPtr req;
155
+ std::future<PoolContextHandle> fut;
156
+ {
157
+ std::lock_guard<std::mutex> lock(mutex_);
158
+ req = queue_.enqueue(mode, kind, taskId);
159
+ fut = req->promise.get_future();
160
+ }
161
+ tryAssignNext();
162
+
163
+ std::unique_lock<std::mutex> lock(mutex_);
164
+ assignCv_.wait(lock, [&] {
165
+ return req->fulfilled.load(std::memory_order_acquire) || req->cancelled.load(std::memory_order_acquire);
166
+ });
167
+
168
+ if (req->cancelled.load(std::memory_order_acquire)) {
169
+ SMB_LOG("Pool requestContext cancelled kind=%s taskId=%s", operationName(kind), taskId.c_str());
170
+ throw std::runtime_error("context request cancelled");
171
+ }
172
+
173
+ PoolContextHandle handle = fut.get();
174
+ if (!handle.valid()) {
175
+ SMB_LOG("Pool requestContext failed kind=%s taskId=%s", operationName(kind), taskId.c_str());
176
+ throw std::runtime_error("context request failed");
177
+ }
178
+ return handle;
179
+ }
180
+
181
+ void SmbConnectionPool::releaseContext(const PoolContextHandle& handle) {
182
+ {
183
+ std::lock_guard<std::mutex> lock(mutex_);
184
+ if (handle.valid()) {
185
+ SMB_LOG("Pool release slot=%zu", handle.slotIndex());
186
+ slots_[handle.slotIndex()].release();
187
+ }
188
+ }
189
+ tryAssignNext();
190
+ }
191
+
192
+ void SmbConnectionPool::cancelRequestsForTask(const std::string& taskId) {
193
+ std::lock_guard<std::mutex> lock(mutex_);
194
+ queue_.cancelForTask(taskId);
195
+ assignCv_.notify_all();
196
+ }
197
+
198
+ size_t SmbConnectionPool::addObserver(const PoolObserver& observer) {
199
+ std::lock_guard<std::mutex> lock(mutex_);
200
+ const size_t id = nextObserverId_++;
201
+ observers_.emplace(id, observer);
202
+ return id;
203
+ }
204
+
205
+ void SmbConnectionPool::removeObserver(size_t id) {
206
+ std::lock_guard<std::mutex> lock(mutex_);
207
+ observers_.erase(id);
208
+ }
209
+
210
+ std::vector<PoolSlotInfo> SmbConnectionPool::getPoolStatus() const {
211
+ std::lock_guard<std::mutex> lock(mutex_);
212
+ std::vector<PoolSlotInfo> out;
213
+ out.reserve(slots_.size());
214
+ for (const auto& slot : slots_) out.push_back(slot.snapshot(slots_.size()));
215
+ return out;
216
+ }
217
+
218
+ void SmbConnectionPool::initialize(const std::string& url, const SmbCredentials& credentials, const std::string& taskId) {
219
+ std::lock_guard<std::mutex> lock(mutex_);
220
+ serverUrl_ = url;
221
+ credentials_ = std::make_shared<SmbCredentials>(credentials);
222
+ shareName_.clear();
223
+ primarySlot().manager()->initialize(url, credentials, taskId);
224
+ for (size_t i = 1; i < slots_.size(); ++i) {
225
+ if (slots_[i].isIdle() && slots_[i].manager()) slots_[i].manager()->disconnect();
226
+ }
227
+ }
228
+
229
+ void SmbConnectionPool::connect(const std::string& url, const SmbCredentials& credentials, const std::string& taskId) {
230
+ std::lock_guard<std::mutex> lock(mutex_);
231
+ serverUrl_ = url;
232
+ credentials_ = std::make_shared<SmbCredentials>(credentials);
233
+ shareName_.clear();
234
+ if (url.size() > 6 && url.substr(0, 6) == "smb://") {
235
+ std::string remaining = url.substr(6);
236
+ const size_t slash = remaining.find('/');
237
+ if (slash != std::string::npos) shareName_ = remaining.substr(slash + 1);
238
+ }
239
+ primarySlot().manager()->connect(url, credentials, taskId);
240
+ for (size_t i = 1; i < slots_.size(); ++i) {
241
+ if (slots_[i].isIdle() && slots_[i].manager()) slots_[i].manager()->disconnect();
242
+ }
243
+ }
244
+
245
+ void SmbConnectionPool::connectShare(const std::string& share, const std::string& taskId) {
246
+ std::lock_guard<std::mutex> lock(mutex_);
247
+ shareName_ = share;
248
+ primarySlot().manager()->connectShare(share, taskId);
249
+ for (size_t i = 1; i < slots_.size(); ++i) {
250
+ if (slots_[i].isIdle() && slots_[i].manager()) slots_[i].manager()->disconnect();
251
+ }
252
+ }
253
+
254
+ void SmbConnectionPool::disconnect(const std::string& taskId) {
255
+ std::lock_guard<std::mutex> lock(mutex_);
256
+ serverUrl_.clear();
257
+ shareName_.clear();
258
+ credentials_.reset();
259
+ for (auto& slot : slots_) {
260
+ if (slot.isIdle() && slot.manager()) slot.manager()->disconnect(taskId);
261
+ }
262
+ }
263
+
264
+ void SmbConnectionPool::resetPool() {
265
+ std::lock_guard<std::mutex> lock(mutex_);
266
+ for (auto& slot : slots_) {
267
+ if (slot.manager()) {
268
+ try {
269
+ slot.manager()->disconnect();
270
+ } catch (...) {
271
+ }
272
+ }
273
+ }
274
+ if (slots_.size() > 1) slots_.erase(slots_.begin() + 1, slots_.end());
275
+ slots_[0].resetBroken();
276
+ serverUrl_.clear();
277
+ shareName_.clear();
278
+ credentials_.reset();
279
+ }
280
+
281
+ std::vector<SmbShareList> SmbConnectionPool::listShares(const std::string& taskId) {
282
+ auto handle = requestContext(AcquireMode::Interactive, SmbOperatorKind::ListShares, taskId);
283
+ return handle.manager().listShares(taskId);
284
+ }
285
+
286
+ bool SmbConnectionPool::isConnected() const {
287
+ std::lock_guard<std::mutex> lock(mutex_);
288
+ return primarySlot().manager() && primarySlot().manager()->isConnected();
289
+ }
290
+
291
+ bool SmbConnectionPool::isInitialized() const {
292
+ std::lock_guard<std::mutex> lock(mutex_);
293
+ return primarySlot().manager() && primarySlot().manager()->isInitialized();
294
+ }
295
+
296
+ std::string SmbConnectionPool::getCurrentUrl() const {
297
+ std::lock_guard<std::mutex> lock(mutex_);
298
+ return primarySlot().manager() ? primarySlot().manager()->getCurrentUrl() : std::string();
299
+ }
300
+
301
+ std::string SmbConnectionPool::getServerName() const {
302
+ std::lock_guard<std::mutex> lock(mutex_);
303
+ return primarySlot().manager() ? primarySlot().manager()->getServerName() : std::string();
304
+ }
305
+
306
+ std::string SmbConnectionPool::getShareName() const {
307
+ std::lock_guard<std::mutex> lock(mutex_);
308
+ return shareName_;
309
+ }
310
+
311
+ std::shared_ptr<SmbCredentials> SmbConnectionPool::getCredentials() const {
312
+ std::lock_guard<std::mutex> lock(mutex_);
313
+ return credentials_;
314
+ }
315
+
316
+ } // namespace react_native_smb
@@ -0,0 +1,84 @@
1
+ #pragma once
2
+
3
+ #include <condition_variable>
4
+ #include <functional>
5
+ #include <map>
6
+ #include <memory>
7
+ #include <mutex>
8
+ #include <string>
9
+ #include <vector>
10
+
11
+ #include "../core/SmbEnums.hpp"
12
+ #include "ContextRequestQueue.hpp"
13
+ #include "PoolContextHandle.hpp"
14
+ #include "PoolSlot.hpp"
15
+ #include "PoolTypes.hpp"
16
+ #include "SmbConnection.hpp"
17
+
18
+ namespace react_native_smb {
19
+
20
+ /**
21
+ * Pool layout (maxConnections = N):
22
+ *
23
+ * slot[0] — Interactive-only
24
+ * slot[1..N-1] — General (any AcquireMode; Interactive may overflow here)
25
+ *
26
+ * Op::requestContext → Interactive-first FIFO queue → exclusive slot assignment
27
+ */
28
+ class SmbConnectionPool {
29
+ public:
30
+ using PoolObserver = std::function<void(const std::vector<PoolSlotInfo>&)>;
31
+
32
+ explicit SmbConnectionPool(size_t maxConnections = 4);
33
+ ~SmbConnectionPool();
34
+
35
+ // [connection control]
36
+ void initialize(const std::string& url, const SmbCredentials& credentials, const std::string& taskId = "");
37
+ void connect(const std::string& url, const SmbCredentials& credentials, const std::string& taskId = "");
38
+ void connectShare(const std::string& share, const std::string& taskId = "");
39
+ void disconnect(const std::string& taskId = "");
40
+ void resetPool();
41
+ std::vector<SmbShareList> listShares(const std::string& taskId = "");
42
+
43
+ // [context]
44
+ PoolContextHandle requestContext(AcquireMode mode, SmbOperatorKind kind, const std::string& taskId = "");
45
+ void releaseContext(const PoolContextHandle& handle);
46
+ void cancelRequestsForTask(const std::string& taskId);
47
+
48
+ // [getter]
49
+ std::string getCurrentUrl() const;
50
+ std::string getServerName() const;
51
+ std::string getShareName() const;
52
+ std::shared_ptr<SmbCredentials> getCredentials() const;
53
+ std::vector<PoolSlotInfo> getPoolStatus() const;
54
+
55
+ // [is…]
56
+ bool isConnected() const;
57
+ bool isInitialized() const;
58
+
59
+ // [observer]
60
+ size_t addObserver(const PoolObserver& observer);
61
+ void removeObserver(size_t id);
62
+
63
+ private:
64
+ void tryAssignNext();
65
+ size_t findIdleSlot(AcquireMode mode) const;
66
+ size_t maybeGrowSlot();
67
+ bool slotNeedsActivate(const PoolSlot& slot) const;
68
+ PoolSlot& primarySlot();
69
+ const PoolSlot& primarySlot() const;
70
+ mutable std::mutex mutex_;
71
+ std::condition_variable assignCv_;
72
+ std::vector<PoolSlot> slots_;
73
+ size_t maxConnections_{4};
74
+ ContextRequestQueue queue_;
75
+
76
+ std::string serverUrl_;
77
+ std::string shareName_;
78
+ std::shared_ptr<SmbCredentials> credentials_;
79
+
80
+ std::map<size_t, PoolObserver> observers_;
81
+ size_t nextObserverId_{1};
82
+ };
83
+
84
+ } // namespace react_native_smb
@@ -0,0 +1,22 @@
1
+ #pragma once
2
+
3
+ #include <atomic>
4
+ #include <memory>
5
+
6
+ namespace react_native_smb {
7
+
8
+ // A cheap, copyable cancellation flag shared by a task and all its operators.
9
+ // The task owns the root token; cancel() trips it; operators poll cancelled()
10
+ // inside their work loops. Copying shares the same underlying atomic<bool>.
11
+ class CancellationToken {
12
+ public:
13
+ CancellationToken() = default;
14
+
15
+ void cancel() { flag_->store(true, std::memory_order_relaxed); }
16
+ bool cancelled() const { return flag_->load(std::memory_order_relaxed); }
17
+
18
+ private:
19
+ std::shared_ptr<std::atomic<bool>> flag_ = std::make_shared<std::atomic<bool>>(false);
20
+ };
21
+
22
+ } // namespace react_native_smb
@@ -0,0 +1,24 @@
1
+ #pragma once
2
+
3
+ #include <cstddef>
4
+
5
+ #include "CancellationToken.hpp"
6
+
7
+ namespace react_native_smb {
8
+
9
+ class SmbTask;
10
+ class SmbConnectionPool;
11
+
12
+ // Everything an operator needs, injected by OperatorBase::start() before run().
13
+ // - task : back-pointer to report status/progress and spawn() more work
14
+ // - pool : connection pool; op calls requestContext() when it needs a slot
15
+ // - cancel : shared token the operator polls in its loop
16
+ // - opIndex : this operator's stable index for per-op aggregation
17
+ struct OperatorContext {
18
+ SmbTask* task{nullptr}; // non-owning
19
+ SmbConnectionPool* pool{nullptr}; // non-owning
20
+ CancellationToken cancel; // shared with the task
21
+ size_t opIndex{0};
22
+ };
23
+
24
+ } // namespace react_native_smb
@@ -0,0 +1,126 @@
1
+ #pragma once
2
+
3
+ namespace react_native_smb {
4
+
5
+ // One operator class per kind. The enum is the single source of truth for the
6
+ // human-readable operation name (operationName) and whether byte-progress is
7
+ // meaningful (isDeterminate).
8
+ enum class SmbOperatorKind {
9
+ // Connection Management
10
+ Initialize,
11
+ Connect,
12
+ ConnectShare,
13
+ Disconnect,
14
+ ListShares,
15
+ // Listing & Info
16
+ ListDirectory,
17
+ GetPathInfo,
18
+ GetSecurityDescriptor,
19
+ // File Transfers
20
+ DownloadFile,
21
+ UploadFile,
22
+ // Mutating File Operations
23
+ CreateDirectory,
24
+ DeleteItem,
25
+ MoveItem,
26
+ RenameItem,
27
+ CopyItem,
28
+ DuplicateItem,
29
+ };
30
+
31
+ // Task lifecycle status. Index order matters: statusName[] mirrors it.
32
+ enum class SmbTaskStatus {
33
+ Idle,
34
+ Pending,
35
+ Running,
36
+ Success,
37
+ Error,
38
+ Cancelled,
39
+ };
40
+
41
+ // Single source of truth: operation string is derived from the enum, never
42
+ // typed by hand. HybridSMB passes no literal operation string.
43
+ constexpr const char* operationName(SmbOperatorKind k) {
44
+ switch (k) {
45
+ case SmbOperatorKind::Initialize:
46
+ return "initialize";
47
+ case SmbOperatorKind::Connect:
48
+ return "connect";
49
+ case SmbOperatorKind::ConnectShare:
50
+ return "connectShare";
51
+ case SmbOperatorKind::Disconnect:
52
+ return "disconnect";
53
+ case SmbOperatorKind::ListShares:
54
+ return "listShares";
55
+ case SmbOperatorKind::ListDirectory:
56
+ return "listDirectory";
57
+ case SmbOperatorKind::GetPathInfo:
58
+ return "getPathInfo";
59
+ case SmbOperatorKind::GetSecurityDescriptor:
60
+ return "getSecurityDescriptor";
61
+ case SmbOperatorKind::DownloadFile:
62
+ return "downloadFile";
63
+ case SmbOperatorKind::UploadFile:
64
+ return "uploadFile";
65
+ case SmbOperatorKind::CreateDirectory:
66
+ return "createDirectory";
67
+ case SmbOperatorKind::DeleteItem:
68
+ return "deleteItem";
69
+ case SmbOperatorKind::MoveItem:
70
+ return "moveItem";
71
+ case SmbOperatorKind::RenameItem:
72
+ return "renameItem";
73
+ case SmbOperatorKind::CopyItem:
74
+ return "copyItem";
75
+ case SmbOperatorKind::DuplicateItem:
76
+ return "duplicateItem";
77
+ }
78
+ return "unknown";
79
+ }
80
+
81
+ // Transfers carry meaningful byte progress; metadata ops do not. The UI uses
82
+ // this to choose a determinate bar vs. an indeterminate spinner.
83
+ constexpr bool isDeterminate(SmbOperatorKind k) {
84
+ switch (k) {
85
+ case SmbOperatorKind::DownloadFile:
86
+ case SmbOperatorKind::UploadFile:
87
+ case SmbOperatorKind::CopyItem:
88
+ case SmbOperatorKind::DuplicateItem:
89
+ return true;
90
+ default:
91
+ return false;
92
+ }
93
+ }
94
+
95
+ constexpr bool isTransferKind(SmbOperatorKind k) {
96
+ switch (k) {
97
+ case SmbOperatorKind::DownloadFile:
98
+ case SmbOperatorKind::UploadFile:
99
+ case SmbOperatorKind::CopyItem:
100
+ case SmbOperatorKind::DuplicateItem:
101
+ return true;
102
+ default:
103
+ return false;
104
+ }
105
+ }
106
+
107
+ // Lowercase status name for the JS bridge. Index mirrors SmbTaskStatus.
108
+ constexpr const char* statusName(SmbTaskStatus s) {
109
+ switch (s) {
110
+ case SmbTaskStatus::Idle:
111
+ return "idle";
112
+ case SmbTaskStatus::Pending:
113
+ return "pending";
114
+ case SmbTaskStatus::Running:
115
+ return "running";
116
+ case SmbTaskStatus::Success:
117
+ return "success";
118
+ case SmbTaskStatus::Error:
119
+ return "error";
120
+ case SmbTaskStatus::Cancelled:
121
+ return "cancelled";
122
+ }
123
+ return "unknown";
124
+ }
125
+
126
+ } // namespace react_native_smb
@@ -0,0 +1,131 @@
1
+ #include "SmbTask.hpp"
2
+
3
+ #include <NitroModules/Promise.hpp>
4
+ #include <thread>
5
+
6
+ #include "../connection/SmbConnectionPool.hpp"
7
+ #include "../operators/OperatorBase.hpp"
8
+ #include "../util/SmbErrorMapper.hpp"
9
+ #include "TaskSnapshotCodec.hpp"
10
+ #include "../util/SmbLog.hpp"
11
+
12
+ namespace react_native_smb {
13
+
14
+ namespace jsi = facebook::jsi;
15
+ using namespace margelo::nitro;
16
+
17
+ SmbTask::SmbTask(std::string id, std::unique_ptr<OperatorBase> seedOp, SmbConnectionPool* pool)
18
+ : HybridObject("SmbTask"),
19
+ SmbTaskCore(std::move(id), seedOp->kind()),
20
+ pool_(pool) {
21
+ ops_.push_back(std::move(seedOp));
22
+ opProgress_.resize(1);
23
+ loadHybridMethods();
24
+ }
25
+
26
+ SmbTask::~SmbTask() = default;
27
+
28
+ void SmbTask::cancel() {
29
+ SMB_LOG("Task cancel id=%s", getId().c_str());
30
+ if (pool_) pool_->cancelRequestsForTask(getId());
31
+ SmbTaskCore::cancel();
32
+ }
33
+
34
+ void SmbTask::start() {
35
+ markRunning();
36
+ auto fut = completionFuture();
37
+ {
38
+ std::lock_guard<std::mutex> lk(stateMutex_);
39
+ pending_ = 1;
40
+ launchOp(0);
41
+ }
42
+ fut.wait();
43
+ }
44
+
45
+ void SmbTask::launchOp(size_t opIndex) {
46
+ auto self = std::static_pointer_cast<SmbTask>(shared());
47
+ std::thread([self, opIndex] {
48
+ if (self->cancel_.cancelled()) {
49
+ self->onOpFinished();
50
+ return;
51
+ }
52
+ OperatorBase* op = nullptr;
53
+ {
54
+ std::lock_guard<std::mutex> lk(self->stateMutex_);
55
+ if (opIndex < self->ops_.size()) op = self->ops_[opIndex].get();
56
+ }
57
+ if (!op) {
58
+ self->onOpFinished();
59
+ return;
60
+ }
61
+ try {
62
+ op->start(self.get(), self->pool_, self->cancel_, opIndex);
63
+ } catch (const std::exception& e) {
64
+ if (!self->cancel_.cancelled()) {
65
+ const int code = SmbErrorMapper::fromErrnoOrMessage(0, e.what());
66
+ self->settle(SmbTaskStatus::Error, e.what(), code);
67
+ }
68
+ } catch (...) {
69
+ if (!self->cancel_.cancelled()) {
70
+ self->settle(SmbTaskStatus::Error, "unknown error", static_cast<int>(SmbErrorCode::Unknown));
71
+ }
72
+ }
73
+ self->onOpFinished();
74
+ }).detach();
75
+ }
76
+
77
+ size_t SmbTask::appendOps(std::vector<std::unique_ptr<OperatorBase>>& more) {
78
+ const size_t first = ops_.size();
79
+ for (auto& op : more) {
80
+ ops_.push_back(std::move(op));
81
+ }
82
+ opProgress_.resize(ops_.size());
83
+ return first;
84
+ }
85
+
86
+ size_t SmbTask::spawn(std::vector<std::unique_ptr<OperatorBase>> moreOps) {
87
+ if (moreOps.empty()) return ops_.size();
88
+ const size_t count = moreOps.size();
89
+ size_t first;
90
+ {
91
+ std::lock_guard<std::mutex> lk(stateMutex_);
92
+ first = appendOps(moreOps);
93
+ pending_ += count;
94
+ for (size_t i = 0; i < count; ++i) launchOp(first + i);
95
+ }
96
+ return first;
97
+ }
98
+
99
+ std::unordered_map<std::string, std::string> SmbTask::get() {
100
+ return TaskSnapshotCodec::encode(getState());
101
+ }
102
+
103
+ jsi::Value SmbTask::getResultValueRaw(jsi::Runtime& runtime, const jsi::Value& /*thisValue*/, const jsi::Value* /*args*/,
104
+ size_t /*count*/) {
105
+ return SmbTaskCore::getResultValue(runtime);
106
+ }
107
+
108
+ std::string SmbTask::subscribeMap(const std::function<void(const std::unordered_map<std::string, std::string>&)>& listener) {
109
+ return SmbTaskCore::subscribe([listener](const SmbTaskState& s) {
110
+ if (listener) {
111
+ try {
112
+ listener(TaskSnapshotCodec::encode(s));
113
+ } catch (...) {
114
+ }
115
+ }
116
+ });
117
+ }
118
+
119
+ void SmbTask::loadHybridMethods() {
120
+ HybridObject::loadHybridMethods();
121
+ registerHybrids(this, [](Prototype& prototype) {
122
+ prototype.registerHybridMethod("id", &SmbTask::getId);
123
+ prototype.registerHybridMethod("cancel", &SmbTask::cancel);
124
+ prototype.registerHybridMethod("get", &SmbTask::get);
125
+ prototype.registerHybridMethod("subscribe", &SmbTask::subscribeMap);
126
+ prototype.registerHybridMethod("unsubscribe", &SmbTask::unsubscribe);
127
+ prototype.registerRawHybridMethod("getResultValue", 0, &SmbTask::getResultValueRaw);
128
+ });
129
+ }
130
+
131
+ } // namespace react_native_smb