@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.
- package/LICENSE +20 -0
- package/README.md +72 -0
- package/ReactNativeSmb.podspec +59 -0
- package/cpp/ios/OnLoad.mm +24 -0
- package/cpp/shared/HybridSMB.cpp +324 -0
- package/cpp/shared/HybridSMB.hpp +102 -0
- package/cpp/shared/ReactNativeSmb.hpp +428 -0
- package/cpp/shared/connection/ContextRequest.hpp +27 -0
- package/cpp/shared/connection/ContextRequestQueue.cpp +85 -0
- package/cpp/shared/connection/ContextRequestQueue.hpp +28 -0
- package/cpp/shared/connection/MetadataContextLease.hpp +33 -0
- package/cpp/shared/connection/PoolContextHandle.cpp +35 -0
- package/cpp/shared/connection/PoolContextHandle.hpp +44 -0
- package/cpp/shared/connection/PoolSlot.cpp +73 -0
- package/cpp/shared/connection/PoolSlot.hpp +46 -0
- package/cpp/shared/connection/PoolTypes.hpp +41 -0
- package/cpp/shared/connection/SmbConnection.cpp +385 -0
- package/cpp/shared/connection/SmbConnection.hpp +93 -0
- package/cpp/shared/connection/SmbConnectionPool.cpp +316 -0
- package/cpp/shared/connection/SmbConnectionPool.hpp +84 -0
- package/cpp/shared/core/CancellationToken.hpp +22 -0
- package/cpp/shared/core/OperatorContext.hpp +24 -0
- package/cpp/shared/core/SmbEnums.hpp +126 -0
- package/cpp/shared/core/SmbTask.cpp +131 -0
- package/cpp/shared/core/SmbTask.hpp +52 -0
- package/cpp/shared/core/SmbTaskCore.cpp +222 -0
- package/cpp/shared/core/SmbTaskCore.hpp +107 -0
- package/cpp/shared/core/SmbTypes.hpp +35 -0
- package/cpp/shared/core/TaskObserverHub.cpp +166 -0
- package/cpp/shared/core/TaskObserverHub.hpp +66 -0
- package/cpp/shared/core/TaskSnapshotCodec.cpp +32 -0
- package/cpp/shared/core/TaskSnapshotCodec.hpp +41 -0
- package/cpp/shared/io/SmbCopyTree.hpp +114 -0
- package/cpp/shared/io/SmbDirScan.hpp +80 -0
- package/cpp/shared/io/SmbFileIO.cpp +1355 -0
- package/cpp/shared/io/SmbFileIO.hpp +45 -0
- package/cpp/shared/io/SmbPathUtil.hpp +75 -0
- package/cpp/shared/io/SmbSecurity.cpp +308 -0
- package/cpp/shared/io/SmbSecurity.hpp +32 -0
- package/cpp/shared/io/SmbTreeOps.hpp +112 -0
- package/cpp/shared/operators/Operator.hpp +35 -0
- package/cpp/shared/operators/OperatorBase.cpp +57 -0
- package/cpp/shared/operators/OperatorBase.hpp +57 -0
- package/cpp/shared/operators/connection/ConnectOperator.cpp +19 -0
- package/cpp/shared/operators/connection/ConnectOperator.hpp +22 -0
- package/cpp/shared/operators/connection/ConnectShareOperator.cpp +19 -0
- package/cpp/shared/operators/connection/ConnectShareOperator.hpp +21 -0
- package/cpp/shared/operators/connection/DisconnectOperator.cpp +12 -0
- package/cpp/shared/operators/connection/DisconnectOperator.hpp +15 -0
- package/cpp/shared/operators/connection/InitializeOperator.cpp +12 -0
- package/cpp/shared/operators/connection/InitializeOperator.hpp +22 -0
- package/cpp/shared/operators/connection/ListSharesOperator.cpp +13 -0
- package/cpp/shared/operators/connection/ListSharesOperator.hpp +18 -0
- package/cpp/shared/operators/listing/GetPathInfoOperator.cpp +43 -0
- package/cpp/shared/operators/listing/GetPathInfoOperator.hpp +21 -0
- package/cpp/shared/operators/listing/GetSecurityDescriptorOperator.cpp +202 -0
- package/cpp/shared/operators/listing/GetSecurityDescriptorOperator.hpp +22 -0
- package/cpp/shared/operators/listing/ListDirectoryOperator.cpp +20 -0
- package/cpp/shared/operators/listing/ListDirectoryOperator.hpp +24 -0
- package/cpp/shared/operators/mutate/CopyItemOperator.cpp +19 -0
- package/cpp/shared/operators/mutate/CopyItemOperator.hpp +25 -0
- package/cpp/shared/operators/mutate/CreateDirectoryOperator.cpp +28 -0
- package/cpp/shared/operators/mutate/CreateDirectoryOperator.hpp +20 -0
- package/cpp/shared/operators/mutate/DeleteItemOperator.cpp +96 -0
- package/cpp/shared/operators/mutate/DeleteItemOperator.hpp +23 -0
- package/cpp/shared/operators/mutate/DuplicateItemOperator.cpp +48 -0
- package/cpp/shared/operators/mutate/DuplicateItemOperator.hpp +23 -0
- package/cpp/shared/operators/mutate/MoveItemOperator.cpp +32 -0
- package/cpp/shared/operators/mutate/MoveItemOperator.hpp +21 -0
- package/cpp/shared/operators/mutate/RenameItemOperator.cpp +37 -0
- package/cpp/shared/operators/mutate/RenameItemOperator.hpp +21 -0
- package/cpp/shared/operators/transfer/DownloadFileOperator.cpp +31 -0
- package/cpp/shared/operators/transfer/DownloadFileOperator.hpp +21 -0
- package/cpp/shared/operators/transfer/UploadFileOperator.cpp +27 -0
- package/cpp/shared/operators/transfer/UploadFileOperator.hpp +21 -0
- package/cpp/shared/util/SmbErrorMapper.hpp +229 -0
- package/cpp/shared/util/SmbLog.hpp +20 -0
- package/docs/API.md +105 -0
- package/docs/ARCHITECTURE.md +70 -0
- package/docs/TASK_MODEL.md +149 -0
- package/ios/include/smb2/libsmb2-dcerpc-lsa.h +172 -0
- package/ios/include/smb2/libsmb2-dcerpc-srvsvc.h +180 -0
- package/ios/include/smb2/libsmb2-dcerpc.h +155 -0
- package/ios/include/smb2/libsmb2-raw.h +497 -0
- package/ios/include/smb2/libsmb2.h +1384 -0
- package/ios/include/smb2/smb2-errors.h +549 -0
- package/ios/include/smb2/smb2.h +1251 -0
- package/ios/libs/device/libsmb2-device.a +0 -0
- package/ios/libs/libsmb2.xcframework/Info.plist +48 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2-dcerpc-lsa.h +172 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2-dcerpc-srvsvc.h +180 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2-dcerpc.h +155 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2-raw.h +497 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2.h +1384 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/smb2-errors.h +549 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/smb2.h +1251 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/libsmb2.a +0 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2-dcerpc-lsa.h +172 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2-dcerpc-srvsvc.h +180 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2-dcerpc.h +155 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2-raw.h +497 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2.h +1384 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/smb2-errors.h +549 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/smb2.h +1251 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/libsmb2.a +0 -0
- package/ios/libs/simulator/libsmb2-simulator.a +0 -0
- package/lib/ReactNativeSmb.d.ts +200 -0
- package/lib/ReactNativeSmb.js +1 -0
- package/lib/bridge/decode.d.ts +4 -0
- package/lib/bridge/decode.js +92 -0
- package/lib/core/SMB.d.ts +3 -0
- package/lib/core/SMB.js +4 -0
- package/lib/core/SmbClient.d.ts +35 -0
- package/lib/core/SmbClient.js +99 -0
- package/lib/core/SmbTask.d.ts +41 -0
- package/lib/core/SmbTask.js +74 -0
- package/lib/core/TransferStore.d.ts +22 -0
- package/lib/core/TransferStore.js +88 -0
- package/lib/core/ids.d.ts +1 -0
- package/lib/core/ids.js +3 -0
- package/lib/core/taskResult.d.ts +3 -0
- package/lib/core/taskResult.js +52 -0
- package/lib/hooks/useSubscribe.d.ts +20 -0
- package/lib/hooks/useSubscribe.js +36 -0
- package/lib/hooks/useTransfers.d.ts +7 -0
- package/lib/hooks/useTransfers.js +29 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +7 -0
- package/lib/labels.d.ts +6 -0
- package/lib/labels.js +71 -0
- package/lib/types.d.ts +141 -0
- package/lib/types.js +59 -0
- package/lib/utils.d.ts +79 -0
- package/lib/utils.js +153 -0
- package/package.json +63 -0
- package/scripts/build-libsmb2.sh +143 -0
- package/scripts/create-xcframework.sh +84 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#include "PoolSlot.hpp"
|
|
2
|
+
|
|
3
|
+
namespace react_native_smb {
|
|
4
|
+
|
|
5
|
+
PoolSlot::PoolSlot(size_t index) : index_(index), conn_(std::make_unique<SmbConnectionManager>()) {}
|
|
6
|
+
|
|
7
|
+
bool PoolSlot::accepts(AcquireMode mode) const {
|
|
8
|
+
if (index_ == 0) return mode == AcquireMode::Interactive;
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
PoolSlotInfo PoolSlot::snapshot(size_t poolSize) const {
|
|
13
|
+
PoolSlotInfo info;
|
|
14
|
+
info.poolSize = poolSize;
|
|
15
|
+
info.index = index_;
|
|
16
|
+
info.interactiveOnly = isInteractiveOnly();
|
|
17
|
+
info.state = state();
|
|
18
|
+
info.isConnected = conn_ && conn_->isConnected();
|
|
19
|
+
info.shareName = conn_ ? conn_->getShareName() : std::string();
|
|
20
|
+
info.taskId = activeTaskId_;
|
|
21
|
+
info.kind = isIdle() ? SmbOperatorKind::Initialize : activeKind_;
|
|
22
|
+
return info;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
void PoolSlot::assign(uint64_t requestId, const std::string& taskId, SmbOperatorKind kind) {
|
|
26
|
+
assignedRequestId_ = requestId;
|
|
27
|
+
activeTaskId_ = taskId;
|
|
28
|
+
activeKind_ = kind;
|
|
29
|
+
state_ = SlotState::Assigned;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
void PoolSlot::release() {
|
|
33
|
+
assignedRequestId_ = 0;
|
|
34
|
+
activeTaskId_.clear();
|
|
35
|
+
activeKind_ = SmbOperatorKind::Initialize;
|
|
36
|
+
state_ = SlotState::Idle;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
void PoolSlot::markActivating() { state_ = SlotState::Activating; }
|
|
40
|
+
|
|
41
|
+
void PoolSlot::activate(const PoolConnectParams& params, const std::string& taskId) {
|
|
42
|
+
state_ = SlotState::Activating;
|
|
43
|
+
if (!params.serverUrl.empty() && params.credentials) {
|
|
44
|
+
conn_->initialize(params.serverUrl, *params.credentials, taskId);
|
|
45
|
+
}
|
|
46
|
+
if (!params.shareName.empty()) {
|
|
47
|
+
conn_->connectShare(params.shareName, taskId);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
void PoolSlot::ensureShare(const std::string& share, const std::string& taskId) {
|
|
52
|
+
if (share.empty()) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (conn_->getShareName() != share) {
|
|
56
|
+
conn_->connectShare(share, taskId);
|
|
57
|
+
} else if (!conn_->isConnected()) {
|
|
58
|
+
conn_->checkAndConnect(taskId);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
void PoolSlot::resetBroken() {
|
|
63
|
+
release();
|
|
64
|
+
if (conn_) {
|
|
65
|
+
try {
|
|
66
|
+
conn_->disconnect();
|
|
67
|
+
} catch (...) {
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
conn_ = std::make_unique<SmbConnectionManager>();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
} // namespace react_native_smb
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cstddef>
|
|
4
|
+
#include <cstdint>
|
|
5
|
+
#include <memory>
|
|
6
|
+
#include <string>
|
|
7
|
+
|
|
8
|
+
#include "PoolTypes.hpp"
|
|
9
|
+
#include "SmbConnection.hpp"
|
|
10
|
+
|
|
11
|
+
namespace react_native_smb {
|
|
12
|
+
|
|
13
|
+
class PoolSlot {
|
|
14
|
+
public:
|
|
15
|
+
explicit PoolSlot(size_t index);
|
|
16
|
+
|
|
17
|
+
size_t index() const { return index_; }
|
|
18
|
+
SlotState state() const { return state_; }
|
|
19
|
+
|
|
20
|
+
bool isInteractiveOnly() const { return index_ == 0; }
|
|
21
|
+
bool isIdle() const { return state() == SlotState::Idle; }
|
|
22
|
+
bool accepts(AcquireMode mode) const;
|
|
23
|
+
|
|
24
|
+
SmbConnectionManager* manager() { return conn_.get(); }
|
|
25
|
+
const SmbConnectionManager* manager() const { return conn_.get(); }
|
|
26
|
+
smb2_context* rawCtx() const { return conn_ ? conn_->rawCtx() : nullptr; }
|
|
27
|
+
|
|
28
|
+
PoolSlotInfo snapshot(size_t poolSize) const;
|
|
29
|
+
|
|
30
|
+
void assign(uint64_t requestId, const std::string& taskId, SmbOperatorKind kind);
|
|
31
|
+
void release();
|
|
32
|
+
void markActivating();
|
|
33
|
+
void activate(const PoolConnectParams& params, const std::string& taskId);
|
|
34
|
+
void ensureShare(const std::string& share, const std::string& taskId);
|
|
35
|
+
void resetBroken();
|
|
36
|
+
|
|
37
|
+
private:
|
|
38
|
+
size_t index_{0};
|
|
39
|
+
SlotState state_{SlotState::Idle};
|
|
40
|
+
std::unique_ptr<SmbConnectionManager> conn_;
|
|
41
|
+
std::string activeTaskId_;
|
|
42
|
+
SmbOperatorKind activeKind_{SmbOperatorKind::Initialize};
|
|
43
|
+
uint64_t assignedRequestId_{0};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
} // namespace react_native_smb
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cstddef>
|
|
4
|
+
#include <memory>
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
#include "../core/SmbEnums.hpp"
|
|
8
|
+
|
|
9
|
+
namespace react_native_smb {
|
|
10
|
+
|
|
11
|
+
struct SmbCredentials;
|
|
12
|
+
|
|
13
|
+
enum class AcquireMode {
|
|
14
|
+
Interactive,
|
|
15
|
+
Metadata,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
enum class SlotState {
|
|
19
|
+
Idle,
|
|
20
|
+
Assigned,
|
|
21
|
+
Activating,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
struct PoolConnectParams {
|
|
25
|
+
std::string serverUrl;
|
|
26
|
+
std::string shareName;
|
|
27
|
+
std::shared_ptr<SmbCredentials> credentials;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
struct PoolSlotInfo {
|
|
31
|
+
size_t poolSize{0};
|
|
32
|
+
size_t index{0};
|
|
33
|
+
bool interactiveOnly{false};
|
|
34
|
+
SlotState state{SlotState::Idle};
|
|
35
|
+
bool isConnected{false};
|
|
36
|
+
std::string shareName;
|
|
37
|
+
std::string taskId;
|
|
38
|
+
SmbOperatorKind kind{SmbOperatorKind::Initialize};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
} // namespace react_native_smb
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
#include "SmbConnection.hpp" // sibling
|
|
2
|
+
|
|
3
|
+
#include <poll.h>
|
|
4
|
+
#include <smb2/libsmb2-dcerpc-srvsvc.h>
|
|
5
|
+
#include <smb2/libsmb2-raw.h>
|
|
6
|
+
|
|
7
|
+
#include <cerrno>
|
|
8
|
+
#include <stdexcept>
|
|
9
|
+
|
|
10
|
+
namespace react_native_smb {
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
constexpr int kShareEnumPollTimeoutMs = 5000;
|
|
14
|
+
constexpr int kShareEnumMaxIdlePolls = 12;
|
|
15
|
+
|
|
16
|
+
[[noreturn]] void throwError(const std::string& /*taskId*/, const std::string& message, int /*code*/) {
|
|
17
|
+
throw std::runtime_error(message);
|
|
18
|
+
}
|
|
19
|
+
} // namespace
|
|
20
|
+
|
|
21
|
+
ScopedContextLock::ScopedContextLock(SmbConnectionManager& manager, const std::string& taskId) : lock_(manager.connectionMutex_), context_(nullptr) {
|
|
22
|
+
manager.checkAndConnect(taskId);
|
|
23
|
+
context_ = manager.context_.get();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
SmbConnectionManager::SmbConnectionManager() {
|
|
27
|
+
context_ = nullptr;
|
|
28
|
+
currentUrl_ = "";
|
|
29
|
+
serverName_ = "";
|
|
30
|
+
isInitialized_ = false;
|
|
31
|
+
isConnected_ = false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
SmbConnectionManager::~SmbConnectionManager() {
|
|
35
|
+
std::lock_guard<std::recursive_timed_mutex> lock(connectionMutex_);
|
|
36
|
+
if (isConnected_ && context_) {
|
|
37
|
+
smb2_disconnect_share(context_.get());
|
|
38
|
+
isConnected_ = false;
|
|
39
|
+
currentShareName_.clear();
|
|
40
|
+
}
|
|
41
|
+
context_.reset();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
void SmbConnectionManager::initialize(const std::string& url, const SmbCredentials& credentials, const std::string& taskId) {
|
|
45
|
+
try {
|
|
46
|
+
std::lock_guard<std::recursive_timed_mutex> lock(connectionMutex_);
|
|
47
|
+
|
|
48
|
+
initializeContext();
|
|
49
|
+
|
|
50
|
+
credentials_ = std::make_shared<SmbCredentials>(credentials);
|
|
51
|
+
|
|
52
|
+
smb2_set_user(context_.get(), credentials.username.c_str());
|
|
53
|
+
smb2_set_password(context_.get(), credentials.password.c_str());
|
|
54
|
+
|
|
55
|
+
if (url.substr(0, 6) == "smb://") {
|
|
56
|
+
std::string remaining = url.substr(6);
|
|
57
|
+
size_t slashPos = remaining.find('/');
|
|
58
|
+
if (slashPos != std::string::npos) {
|
|
59
|
+
serverName_ = remaining.substr(0, slashPos);
|
|
60
|
+
} else {
|
|
61
|
+
serverName_ = remaining;
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
std::string msg = "Invalid SMB URL format. Expected: smb://server";
|
|
65
|
+
throwError(taskId, msg, static_cast<int>(SmbErrorCode::InvalidArgument));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
currentUrl_ = "smb://" + serverName_;
|
|
69
|
+
isInitialized_ = true;
|
|
70
|
+
} catch (const std::exception& e) {
|
|
71
|
+
throwError(taskId, e.what(), static_cast<int>(SmbErrorCode::Unknown));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
void SmbConnectionManager::connect(const std::string& url, const SmbCredentials& credentials, const std::string& taskId) {
|
|
76
|
+
try {
|
|
77
|
+
std::lock_guard<std::recursive_timed_mutex> lock(connectionMutex_);
|
|
78
|
+
|
|
79
|
+
if (isConnected_) {
|
|
80
|
+
smb2_disconnect_share(context_.get());
|
|
81
|
+
isConnected_ = false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
initializeContext();
|
|
85
|
+
|
|
86
|
+
credentials_ = std::make_shared<SmbCredentials>(credentials);
|
|
87
|
+
|
|
88
|
+
smb2_set_user(context_.get(), credentials.username.c_str());
|
|
89
|
+
smb2_set_password(context_.get(), credentials.password.c_str());
|
|
90
|
+
|
|
91
|
+
std::string serverName;
|
|
92
|
+
std::string shareName;
|
|
93
|
+
|
|
94
|
+
if (url.substr(0, 6) == "smb://") {
|
|
95
|
+
std::string remaining = url.substr(6);
|
|
96
|
+
size_t slashPos = remaining.find('/');
|
|
97
|
+
if (slashPos != std::string::npos) {
|
|
98
|
+
serverName = remaining.substr(0, slashPos);
|
|
99
|
+
shareName = remaining.substr(slashPos + 1);
|
|
100
|
+
} else {
|
|
101
|
+
std::string msg = "Invalid URL: format must be smb://server/share. Received: " + url;
|
|
102
|
+
throwError(taskId, msg, static_cast<int>(SmbErrorCode::InvalidArgument));
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
std::string msg = "Invalid URL: format must be smb://server/share. Received: " + url;
|
|
106
|
+
throwError(taskId, msg, static_cast<int>(SmbErrorCode::InvalidArgument));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const int connectResult = smb2_connect_share(context_.get(), serverName.c_str(), shareName.c_str(), credentials.username.c_str());
|
|
110
|
+
if (connectResult < 0) {
|
|
111
|
+
std::string libError = smb2_get_error(context_.get());
|
|
112
|
+
std::string msg = "SMB Connection Failed: Could not connect to share '" + shareName + "'. Error: " + libError;
|
|
113
|
+
throwError(taskId, msg, SmbErrorMapper::fromErrnoResult(connectResult));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
isConnected_ = true;
|
|
117
|
+
isInitialized_ = true;
|
|
118
|
+
serverName_ = serverName;
|
|
119
|
+
currentShareName_ = shareName;
|
|
120
|
+
currentUrl_ = url;
|
|
121
|
+
} catch (const std::exception& e) {
|
|
122
|
+
throwError(taskId, e.what(), static_cast<int>(SmbErrorCode::Unknown));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
void SmbConnectionManager::connectShare(const std::string& share, const std::string& taskId) {
|
|
127
|
+
try {
|
|
128
|
+
std::lock_guard<std::recursive_timed_mutex> lock(connectionMutex_);
|
|
129
|
+
|
|
130
|
+
if (!isInitialized_) {
|
|
131
|
+
std::string msg = "Connection not initialized. Call initialize() first.";
|
|
132
|
+
throwError(taskId, msg, static_cast<int>(SmbErrorCode::NotConnected));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (isConnected_ && currentShareName_ == share) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (isConnected_) {
|
|
140
|
+
smb2_disconnect_share(context_.get());
|
|
141
|
+
isConnected_ = false;
|
|
142
|
+
currentShareName_.clear();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const int connectResult = smb2_connect_share(context_.get(), serverName_.c_str(), share.c_str(), credentials_->username.c_str());
|
|
146
|
+
if (connectResult < 0) {
|
|
147
|
+
std::string msg = "Failed to connect to SMB share '" + share + "': " + std::string(smb2_get_error(context_.get()));
|
|
148
|
+
throwError(taskId, msg, SmbErrorMapper::fromErrnoResult(connectResult));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
isConnected_ = true;
|
|
152
|
+
currentShareName_ = share;
|
|
153
|
+
|
|
154
|
+
currentUrl_ = "smb://" + serverName_ + "/" + share;
|
|
155
|
+
} catch (const std::exception& e) {
|
|
156
|
+
throwError(taskId, e.what(), static_cast<int>(SmbErrorCode::Unknown));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
void SmbConnectionManager::disconnect(const std::string& taskId) {
|
|
161
|
+
try {
|
|
162
|
+
std::lock_guard<std::recursive_timed_mutex> lock(connectionMutex_);
|
|
163
|
+
if (isConnected_ && context_) {
|
|
164
|
+
smb2_disconnect_share(context_.get());
|
|
165
|
+
isConnected_ = false;
|
|
166
|
+
}
|
|
167
|
+
context_.reset();
|
|
168
|
+
isInitialized_ = false;
|
|
169
|
+
credentials_.reset();
|
|
170
|
+
serverName_.clear();
|
|
171
|
+
currentShareName_.clear();
|
|
172
|
+
currentUrl_.clear();
|
|
173
|
+
} catch (const std::exception& e) {
|
|
174
|
+
throwError(taskId, e.what(), static_cast<int>(SmbErrorCode::Unknown));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
std::vector<SmbShareList> SmbConnectionManager::listShares(const std::string& taskId) {
|
|
179
|
+
std::lock_guard<std::recursive_timed_mutex> lock(connectionMutex_);
|
|
180
|
+
|
|
181
|
+
if (!isInitialized_) {
|
|
182
|
+
throwError(taskId, "Share List Failed: SMB context not initialized. Please call initialize() first.", static_cast<int>(SmbErrorCode::NotConnected));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (serverName_.empty()) {
|
|
186
|
+
throwError(taskId, "Share List Failed: No server name found. You must connect to a server before listing shares.", static_cast<int>(SmbErrorCode::NotConnected));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
std::vector<SmbShareList> shares;
|
|
190
|
+
|
|
191
|
+
auto ipcContext = std::shared_ptr<smb2_context>(smb2_init_context(), [](smb2_context* ctx) {
|
|
192
|
+
if (ctx) {
|
|
193
|
+
smb2_destroy_context(ctx);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
if (!ipcContext) {
|
|
198
|
+
throwError(taskId, "Share List Failed: Could not create temporary SMB context. System might be low on memory.", static_cast<int>(SmbErrorCode::Io));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (credentials_) {
|
|
202
|
+
smb2_set_user(ipcContext.get(), credentials_->username.c_str());
|
|
203
|
+
smb2_set_password(ipcContext.get(), credentials_->password.c_str());
|
|
204
|
+
} else {
|
|
205
|
+
throwError(taskId, "No credentials available for share enumeration", static_cast<int>(SmbErrorCode::NotConnected));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const int ipcConnectResult = smb2_connect_share(ipcContext.get(), serverName_.c_str(), "IPC$", nullptr);
|
|
209
|
+
if (ipcConnectResult < 0) {
|
|
210
|
+
throwError(taskId, "Failed to connect to IPC$ share: " + std::string(smb2_get_error(ipcContext.get())), SmbErrorMapper::fromErrnoResult(ipcConnectResult));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
struct ShareEnumContext {
|
|
214
|
+
std::vector<SmbShareList>* shares;
|
|
215
|
+
std::string error;
|
|
216
|
+
int errorCode;
|
|
217
|
+
bool finished;
|
|
218
|
+
} enumContext = {&shares, "", static_cast<int>(SmbErrorCode::Unknown), false};
|
|
219
|
+
|
|
220
|
+
auto shareEnumCallback = [](struct smb2_context* smb2, int status, void* command_data, void* private_data) {
|
|
221
|
+
ShareEnumContext* ctx = static_cast<ShareEnumContext*>(private_data);
|
|
222
|
+
ctx->finished = true;
|
|
223
|
+
|
|
224
|
+
if (status != 0) {
|
|
225
|
+
ctx->error = "Failed to enumerate shares: " + std::string(smb2_get_error(smb2));
|
|
226
|
+
ctx->errorCode = SmbErrorMapper::fromErrnoResult(status);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
auto rep = static_cast<struct srvsvc_NetrShareEnum_rep*>(command_data);
|
|
231
|
+
if (!rep) {
|
|
232
|
+
ctx->error = "No share enumeration data received";
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
for (uint32_t i = 0; i < rep->ses.ShareInfo.Level1.EntriesRead; i++) {
|
|
237
|
+
auto& shareInfo = rep->ses.ShareInfo.Level1.Buffer->share_info_1[i];
|
|
238
|
+
|
|
239
|
+
SmbShareList share;
|
|
240
|
+
share.name = shareInfo.netname.utf8 ? shareInfo.netname.utf8 : "";
|
|
241
|
+
share.comment = shareInfo.remark.utf8 ? shareInfo.remark.utf8 : "";
|
|
242
|
+
|
|
243
|
+
bool isHidden = (shareInfo.type & SHARE_TYPE_HIDDEN) != 0;
|
|
244
|
+
bool isSystem = (shareInfo.type & 3) == SHARE_TYPE_IPC;
|
|
245
|
+
|
|
246
|
+
if (!isHidden && !isSystem && !share.name.empty()) {
|
|
247
|
+
ctx->shares->push_back(share);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
smb2_free_data(smb2, rep);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const int enumStartResult = smb2_share_enum_async(ipcContext.get(), SHARE_INFO_1, shareEnumCallback, &enumContext);
|
|
255
|
+
if (enumStartResult != 0) {
|
|
256
|
+
throwError(taskId, "Failed to start share enumeration: " + std::string(smb2_get_error(ipcContext.get())), SmbErrorMapper::fromErrnoResult(enumStartResult));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
struct pollfd pfd;
|
|
260
|
+
int idlePolls = 0;
|
|
261
|
+
while (!enumContext.finished) {
|
|
262
|
+
pfd.fd = smb2_get_fd(ipcContext.get());
|
|
263
|
+
pfd.events = smb2_which_events(ipcContext.get());
|
|
264
|
+
|
|
265
|
+
if (poll(&pfd, 1, kShareEnumPollTimeoutMs) < 0) {
|
|
266
|
+
throwError(taskId, "Poll failed during share enumeration", SmbErrorMapper::fromErrno(errno));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (pfd.revents == 0) {
|
|
270
|
+
if (++idlePolls >= kShareEnumMaxIdlePolls) {
|
|
271
|
+
throwError(taskId, "Share enumeration timed out", static_cast<int>(SmbErrorCode::TimedOut));
|
|
272
|
+
}
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
idlePolls = 0;
|
|
276
|
+
|
|
277
|
+
const int serviceResult = smb2_service(ipcContext.get(), pfd.revents);
|
|
278
|
+
if (serviceResult < 0) {
|
|
279
|
+
throwError(taskId, "SMB2 service failed: " + std::string(smb2_get_error(ipcContext.get())), SmbErrorMapper::fromErrnoResult(serviceResult));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (!enumContext.error.empty()) {
|
|
284
|
+
throwError(taskId, enumContext.error, enumContext.errorCode);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
smb2_disconnect_share(ipcContext.get());
|
|
288
|
+
|
|
289
|
+
return shares;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
void SmbConnectionManager::checkAndInitialize(const std::string& taskId) {
|
|
293
|
+
try {
|
|
294
|
+
std::lock_guard<std::recursive_timed_mutex> lock(connectionMutex_);
|
|
295
|
+
|
|
296
|
+
if (isInitialized_ && context_) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (!credentials_) {
|
|
301
|
+
std::string msg = "SMB Reconnect Error: Missing saved credentials. Please call initialize() or connect() first.";
|
|
302
|
+
throwError(taskId, msg, static_cast<int>(SmbErrorCode::NotConnected));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (serverName_.empty()) {
|
|
306
|
+
if (!currentUrl_.empty() && currentUrl_.rfind("smb://", 0) == 0) {
|
|
307
|
+
std::string remaining = currentUrl_.substr(6);
|
|
308
|
+
size_t slashPos = remaining.find('/');
|
|
309
|
+
serverName_ = (slashPos == std::string::npos) ? remaining : remaining.substr(0, slashPos);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (serverName_.empty()) {
|
|
314
|
+
std::string msg = "SMB Reconnect Error: Missing saved server name. Please call initialize() or connect() first.";
|
|
315
|
+
throwError(taskId, msg, static_cast<int>(SmbErrorCode::NotConnected));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
initializeContext();
|
|
319
|
+
|
|
320
|
+
smb2_set_user(context_.get(), credentials_->username.c_str());
|
|
321
|
+
smb2_set_password(context_.get(), credentials_->password.c_str());
|
|
322
|
+
|
|
323
|
+
if (currentUrl_.empty()) {
|
|
324
|
+
currentUrl_ = "smb://" + serverName_;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
isInitialized_ = true;
|
|
328
|
+
} catch (const std::exception& e) {
|
|
329
|
+
throwError(taskId, e.what(), static_cast<int>(SmbErrorCode::Unknown));
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
void SmbConnectionManager::checkAndConnect(const std::string& taskId) {
|
|
334
|
+
try {
|
|
335
|
+
std::lock_guard<std::recursive_timed_mutex> lock(connectionMutex_);
|
|
336
|
+
|
|
337
|
+
if (isConnected_ && context_) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
checkAndInitialize(taskId);
|
|
342
|
+
|
|
343
|
+
if (currentShareName_.empty() && currentUrl_.rfind("smb://", 0) == 0) {
|
|
344
|
+
std::string remaining = currentUrl_.substr(6);
|
|
345
|
+
size_t slashPos = remaining.find('/');
|
|
346
|
+
if (slashPos != std::string::npos && slashPos + 1 < remaining.size()) {
|
|
347
|
+
currentShareName_ = remaining.substr(slashPos + 1);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (currentShareName_.empty()) {
|
|
352
|
+
std::string msg = "SMB Reconnect Error: Missing saved share name. Please call connect() or connectShare() first.";
|
|
353
|
+
throwError(taskId, msg, static_cast<int>(SmbErrorCode::NotConnected));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const int connectResult = smb2_connect_share(context_.get(), serverName_.c_str(), currentShareName_.c_str(), credentials_->username.c_str());
|
|
357
|
+
if (connectResult < 0) {
|
|
358
|
+
std::string msg = "SMB Reconnect Failed: Could not connect to share '" + currentShareName_ + "'. Error: " + std::string(smb2_get_error(context_.get()));
|
|
359
|
+
throwError(taskId, msg, SmbErrorMapper::fromErrnoResult(connectResult));
|
|
360
|
+
}
|
|
361
|
+
isConnected_ = true;
|
|
362
|
+
currentUrl_ = "smb://" + serverName_ + "/" + currentShareName_;
|
|
363
|
+
|
|
364
|
+
} catch (const std::exception& e) {
|
|
365
|
+
throwError(taskId, e.what(), static_cast<int>(SmbErrorCode::Unknown));
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
void SmbConnectionManager::initializeContext() {
|
|
370
|
+
if (context_) {
|
|
371
|
+
context_.reset();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
auto* raw_context = smb2_init_context();
|
|
375
|
+
if (!raw_context) {
|
|
376
|
+
throw std::runtime_error("Fatal Error: Failed to initialize libsmb2 context. System may be out of memory.");
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
context_ = std::shared_ptr<smb2_context>(raw_context, [](smb2_context* ctx) {
|
|
380
|
+
if (ctx) {
|
|
381
|
+
smb2_destroy_context(ctx);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
} // namespace react_native_smb
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "../ReactNativeSmb.hpp"
|
|
4
|
+
#include "../util/SmbErrorMapper.hpp"
|
|
5
|
+
|
|
6
|
+
#include <cstdint>
|
|
7
|
+
#include <cstddef>
|
|
8
|
+
#include <ctime>
|
|
9
|
+
#include <smb2/smb2.h>
|
|
10
|
+
#include <smb2/libsmb2.h>
|
|
11
|
+
|
|
12
|
+
#include <future>
|
|
13
|
+
#include <memory>
|
|
14
|
+
#include <mutex>
|
|
15
|
+
#include <string>
|
|
16
|
+
#include <type_traits>
|
|
17
|
+
#include <utility>
|
|
18
|
+
|
|
19
|
+
namespace react_native_smb {
|
|
20
|
+
class SmbConnectionManager;
|
|
21
|
+
|
|
22
|
+
// RAII lock over connectionMutex_; auto-reconnects on construction.
|
|
23
|
+
class ScopedContextLock {
|
|
24
|
+
private:
|
|
25
|
+
std::unique_lock<std::recursive_timed_mutex> lock_;
|
|
26
|
+
smb2_context* context_;
|
|
27
|
+
|
|
28
|
+
public:
|
|
29
|
+
ScopedContextLock(SmbConnectionManager& manager, const std::string& taskId = "");
|
|
30
|
+
|
|
31
|
+
smb2_context* get() const { return context_; }
|
|
32
|
+
|
|
33
|
+
ScopedContextLock(const ScopedContextLock&) = delete;
|
|
34
|
+
ScopedContextLock& operator=(const ScopedContextLock&) = delete;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Per-manager recursive_timed_mutex serialises all smb2_* calls on one context.
|
|
38
|
+
// submitSync() acquires the mutex and runs the callable on the calling thread.
|
|
39
|
+
// No background poll thread — a second poller on the same fd corrupts libsmb2.
|
|
40
|
+
class SmbConnectionManager {
|
|
41
|
+
friend class ScopedContextLock;
|
|
42
|
+
|
|
43
|
+
private:
|
|
44
|
+
std::shared_ptr<smb2_context> context_;
|
|
45
|
+
std::shared_ptr<SmbCredentials> credentials_;
|
|
46
|
+
std::recursive_timed_mutex connectionMutex_;
|
|
47
|
+
std::string currentUrl_;
|
|
48
|
+
std::string serverName_;
|
|
49
|
+
std::string currentShareName_;
|
|
50
|
+
bool isInitialized_ = false;
|
|
51
|
+
bool isConnected_ = false;
|
|
52
|
+
|
|
53
|
+
public:
|
|
54
|
+
SmbConnectionManager();
|
|
55
|
+
~SmbConnectionManager();
|
|
56
|
+
|
|
57
|
+
void initialize(const std::string& url, const SmbCredentials& credentials, const std::string& taskId = "");
|
|
58
|
+
void connect(const std::string& url, const SmbCredentials& credentials, const std::string& taskId = "");
|
|
59
|
+
void connectShare(const std::string& share, const std::string& taskId = "");
|
|
60
|
+
void disconnect(const std::string& taskId = "");
|
|
61
|
+
|
|
62
|
+
std::vector<SmbShareList> listShares(const std::string& taskId = "");
|
|
63
|
+
|
|
64
|
+
void checkAndInitialize(const std::string& taskId = "");
|
|
65
|
+
void checkAndConnect(const std::string& taskId = "");
|
|
66
|
+
|
|
67
|
+
bool isConnected() const { return isConnected_; }
|
|
68
|
+
bool isInitialized() const { return isInitialized_; }
|
|
69
|
+
const std::string& getCurrentUrl() const { return currentUrl_; }
|
|
70
|
+
const std::string& getServerName() const { return serverName_; }
|
|
71
|
+
const std::string& getShareName() const { return currentShareName_; }
|
|
72
|
+
|
|
73
|
+
std::shared_ptr<smb2_context> getContext() { return context_; }
|
|
74
|
+
const std::shared_ptr<SmbCredentials>& getCredentials() const { return credentials_; }
|
|
75
|
+
|
|
76
|
+
std::recursive_timed_mutex& getConnectionMutex() { return connectionMutex_; }
|
|
77
|
+
|
|
78
|
+
smb2_context* rawCtx() const { return context_.get(); }
|
|
79
|
+
|
|
80
|
+
template <typename Fn, typename R = std::invoke_result_t<Fn, smb2_context*>>
|
|
81
|
+
R submitSync(Fn&& fn) {
|
|
82
|
+
std::lock_guard<std::recursive_timed_mutex> lock(connectionMutex_);
|
|
83
|
+
if constexpr (std::is_void_v<R>) {
|
|
84
|
+
fn(context_.get());
|
|
85
|
+
} else {
|
|
86
|
+
return fn(context_.get());
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private:
|
|
91
|
+
void initializeContext();
|
|
92
|
+
};
|
|
93
|
+
} // namespace react_native_smb
|