1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "connectdfs.h"
17
18 #include <cstring>
19 #include <dirent.h>
20 #include <fcntl.h>
21 #include <memory>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <tuple>
25 #include <unistd.h>
26
27 #include "common_func.h"
28 #include "filemgmt_libhilog.h"
29 #include "distributed_file_daemon_manager.h"
30
31 namespace OHOS {
32 namespace FileManagement {
33 namespace ModuleFileIO {
34 namespace fs = std::filesystem;
35
CreateConnectDfsCBCBInfo(napi_env & env)36 ConnectDfsCB *ConnectDfs::CreateConnectDfsCBCBInfo(napi_env &env)
37 {
38 HILOGI("CreateConnectDfsCBCBInfo called");
39 auto connectDfsCB = new(std::nothrow) ConnectDfsCB;
40 if (connectDfsCB == nullptr) {
41 HILOGE("CreateConnectDfsCBCBInfo failed, connectDfsCB == nullptr");
42 return nullptr;
43 }
44 connectDfsCB->cbBase.cbInfo.env = env;
45 connectDfsCB->cbBase.asyncWork = nullptr;
46 connectDfsCB->cbBase.deferred = nullptr;
47 connectDfsCB->callbackRef = nullptr;
48 HILOGI("CreateConnectDfsCBCBInfo end");
49 return connectDfsCB;
50 }
51
cbExec(napi_env env,void * data)52 void cbExec(napi_env env, void *data)
53 {
54 HILOGI("cbExec for connectDfs called");
55 auto connectDfsCB = static_cast<ConnectDfsCB *>(data);
56 sptr<NAPIDfsListener> dfsListeners(new (std::nothrow) NAPIDfsListener());
57 connectDfsCB->jsCallbackObject = dfsListeners;
58 if (connectDfsCB->jsCallbackObject == nullptr) {
59 return;
60 }
61 connectDfsCB->jsCallbackObject->SetConnectDfsEnv(env);
62 HILOGI("connectDfsCB set env success");
63 if (connectDfsCB->dfsConnectCB.callback != nullptr) {
64 connectDfsCB->jsCallbackObject->
65 SetConnectDfsCBRef(connectDfsCB->dfsConnectCB.callback);
66 HILOGI("connectDfsCB set callback success");
67 } else {
68 connectDfsCB->jsCallbackObject->
69 SetConnectDfsPromiseRef(connectDfsCB->cbBase.deferred);
70 HILOGI("connectDfsCB set promise success");
71 }
72
73 connectDfsCB->result = Storage::DistributedFile::DistributedFileDaemonManager::GetInstance().
74 OpenP2PConnectionEx(connectDfsCB->networkId, connectDfsCB->jsCallbackObject);
75 HILOGI(" cbExec end ret = %{public}d", connectDfsCB->result);
76 }
77
cbCompl(napi_env env,napi_status status,void * data)78 void cbCompl(napi_env env, napi_status status, void *data)
79 {
80 HILOGI("cbCompl for connectDfs called");
81 auto connectDfsCB = static_cast<ConnectDfsCB *>(data);
82 napi_value result[NARG_CNT::TWO] = { nullptr };
83 napi_get_undefined(env, &result[NARG_POS::SECOND]);
84 if (connectDfsCB->result == ERRNO_NOERR) {
85 napi_get_undefined(env, &result[NARG_POS::FIRST]);
86 napi_resolve_deferred(env, connectDfsCB->cbBase.deferred, result[NARG_POS::SECOND]);
87 } else {
88 result[NARG_POS::FIRST] = NError(connectDfsCB->result).GetNapiErr(env);
89 napi_reject_deferred(env, connectDfsCB->cbBase.deferred, result[NARG_POS::FIRST]);
90 }
91 napi_delete_async_work(env, connectDfsCB->cbBase.asyncWork);
92 delete connectDfsCB;
93 connectDfsCB = nullptr;
94 HILOGI("cbCompl for connectDfs end");
95 }
96
GetListenerFromOptionArg(napi_env env,const NFuncArg & funcArg)97 tuple<bool, NVal> ConnectDfs::GetListenerFromOptionArg(napi_env env, const NFuncArg &funcArg)
98 {
99 NVal op(env, funcArg[NARG_POS::SECOND]);
100 if (!op.HasProp("onStatus") || op.GetProp("onStatus").TypeIs(napi_undefined)) {
101 return { true, NVal() };
102 }
103 NVal onStatus = op.GetProp("onStatus");
104 if (!onStatus.TypeIs(napi_function)) {
105 HILOGE("Illegal dfsListeners.onStatus type");
106 return { false, NVal() };
107 }
108 return { true, onStatus };
109 }
110
ParseJsOperand(napi_env env,NVal paramFromJsArg)111 tuple<bool, std::string> ConnectDfs::ParseJsOperand(napi_env env, NVal paramFromJsArg)
112 {
113 auto [succ, param, ignore] = paramFromJsArg.ToUTF8String();
114 if (!succ) {
115 HILOGE("parse parameter failed.");
116 return { false, "" };
117 }
118 std::string paramStr = std::string(param.get());
119 return { true, paramStr };
120 }
121
ParseJsParam(napi_env env,NFuncArg & funcArg,ConnectDfsCB * connectDfsCB)122 int ConnectDfs::ParseJsParam(napi_env env, NFuncArg &funcArg, ConnectDfsCB *connectDfsCB)
123 {
124 if (!funcArg.InitArgs(NARG_CNT::TWO)) {
125 HILOGE("Number of arguments unmatched");
126 return E_PARAMS;
127 }
128 auto [succNetworkId, networkId] = ParseJsOperand(env, { env, funcArg[NARG_POS::FIRST] });
129 auto [succDfsListeners, dfsListeners] = GetListenerFromOptionArg(env, funcArg);
130 if (!succNetworkId || !succDfsListeners) {
131 HILOGE("The first/second argument requires string/napi_function");
132 return E_PARAMS;
133 }
134 connectDfsCB->networkId = networkId;
135 napi_create_reference(env, dfsListeners.val_, 1, &connectDfsCB->dfsConnectCB.callback);
136 return ERRNO_NOERR;
137 }
138
Async(napi_env env,napi_callback_info info)139 napi_value ConnectDfs::Async(napi_env env, napi_callback_info info)
140 {
141 HILOGI("ConnectDfs::Async called");
142 ConnectDfsCB *connectDfsCB = CreateConnectDfsCBCBInfo(env);
143 if (connectDfsCB == nullptr) {
144 NError(E_PARAMS).ThrowErr(env);
145 return nullptr;
146 }
147 NFuncArg funcArg(env, info);
148 auto result = ParseJsParam(env, funcArg, connectDfsCB);
149 if (result != ERRNO_NOERR) {
150 NError(result).ThrowErr(env);
151 delete connectDfsCB;
152 connectDfsCB = nullptr;
153 return nullptr;
154 }
155
156 napi_value ret = nullptr;
157 napi_status status = napi_create_promise(env, &connectDfsCB->cbBase.deferred, &ret);
158 if (status != napi_ok) {
159 HILOGE("INNER BUG. Cannot create promise for %{public}d", status);
160 delete connectDfsCB;
161 connectDfsCB = nullptr;
162 return nullptr;
163 }
164
165 status = napi_create_async_work(env, nullptr, NVal::CreateUTF8String(env, "ResourceName").val_,
166 cbExec, cbCompl, static_cast<void *>(connectDfsCB), &connectDfsCB->cbBase.asyncWork);
167 if (status != napi_ok) {
168 HILOGE("INNER BUG. Failed to create async work for %{public}d", status);
169 delete connectDfsCB;
170 connectDfsCB = nullptr;
171 return nullptr;
172 }
173
174 status = napi_queue_async_work(env, connectDfsCB->cbBase.asyncWork);
175 if (status != napi_ok) {
176 HILOGE("INNER BUG. Failed to queue async work for %{public}d", status);
177 delete connectDfsCB;
178 connectDfsCB = nullptr;
179 return nullptr;
180 }
181
182 if (ret == nullptr) {
183 HILOGE("napi_async_work ret = nullptr");
184 NError(E_PARAMS).ThrowErr(env);
185 return NVal::CreateUndefined(env).val_;
186 }
187 HILOGI("ConnectDfs end");
188 return ret;
189 }
190
CheckAndGetParameters(ConnectDfsCB * connectDfsCB,napi_handle_scope * scope)191 ConnectDfsCB *CheckAndGetParameters(ConnectDfsCB *connectDfsCB, napi_handle_scope *scope)
192 {
193 HILOGI("ConnectDfsCB::CheckAndGetParameters GetParam called");
194
195 if (connectDfsCB == nullptr) {
196 HILOGE("ConnectDfsCB, GetParam connectDfsCB is null");
197 return nullptr;
198 }
199 napi_open_handle_scope(connectDfsCB->cbBase.cbInfo.env, scope);
200 if (scope == nullptr) {
201 delete connectDfsCB;
202 connectDfsCB = nullptr;
203 return nullptr;
204 }
205 HILOGI("ConnectDfsCB::CheckAndGetParameters GetParam end");
206 return connectDfsCB;
207 }
208
SetConnectDfsEnv(const napi_env & env)209 void NAPIDfsListener::SetConnectDfsEnv(const napi_env &env)
210 {
211 env_ = env;
212 }
213
SetConnectDfsCBRef(const napi_ref & ref)214 void NAPIDfsListener::SetConnectDfsCBRef(const napi_ref &ref)
215 {
216 onStatusRef_ = ref;
217 }
218
SetConnectDfsPromiseRef(const napi_deferred & promiseDeferred)219 void NAPIDfsListener::SetConnectDfsPromiseRef(const napi_deferred &promiseDeferred)
220 {
221 promiseDeferred_ = promiseDeferred;
222 }
223
WrapInt32(napi_env & env,int32_t num,const std::string & paramName)224 napi_value WrapInt32(napi_env &env, int32_t num, const std::string ¶mName)
225 {
226 HILOGI("WrapInt32 called");
227 napi_value jsObject = nullptr;
228 napi_create_object(env, &jsObject);
229 napi_value jsValue = nullptr;
230 HILOGD("WrapInt32 called. %{public}s = %{public}d", paramName.c_str(), num);
231 napi_create_int32(env, num, &jsValue);
232 napi_set_named_property(env, jsObject, paramName.c_str(), jsValue);
233
234 return jsObject;
235 }
236
WrapString(napi_env & env,const std::string & param,const std::string & paramName)237 napi_value WrapString(napi_env &env, const std::string ¶m, const std::string ¶mName)
238 {
239 HILOGI("WrapString called");
240 napi_value jsValue = nullptr;
241 HILOGD("WrapString called. %{public}s = %{public}s", paramName.c_str(), param.c_str());
242 napi_create_string_utf8(env, param.c_str(), NAPI_AUTO_LENGTH, &jsValue);
243
244 return jsValue;
245 }
246
UvWorkAfterOnStaus(ConnectDfsCB * connectDfsCB)247 void UvWorkAfterOnStaus(ConnectDfsCB *connectDfsCB)
248 {
249 HILOGI("UvWorkAfterOnStaus called");
250 napi_handle_scope scope = nullptr;
251 connectDfsCB = CheckAndGetParameters(connectDfsCB, &scope);
252 if (connectDfsCB == nullptr) {
253 return;
254 }
255 HILOGI("UvWorkAfterOnStaus, status = %{public}d", connectDfsCB->status);
256
257 napi_value result[NARG_CNT::TWO] = {nullptr};
258 result[NARG_POS::FIRST] = WrapString(connectDfsCB->cbBase.cbInfo.env, connectDfsCB->networkId.c_str(), "networkId");
259 result[NARG_POS::SECOND] = WrapInt32(connectDfsCB->cbBase.cbInfo.env, connectDfsCB->status, "status");
260 if (connectDfsCB->cbBase.deferred == nullptr) {
261 napi_value callback = nullptr;
262 napi_value undefined = nullptr;
263 napi_get_undefined(connectDfsCB->cbBase.cbInfo.env, &undefined);
264 napi_value callResult = nullptr;
265 napi_get_reference_value(connectDfsCB->cbBase.cbInfo.env,
266 connectDfsCB->cbBase.cbInfo.callback, &callback);
267 napi_call_function(connectDfsCB->cbBase.cbInfo.env, undefined, callback, NARG_CNT::TWO, result, &callResult);
268 if (connectDfsCB->cbBase.cbInfo.callback != nullptr) {
269 napi_delete_reference(connectDfsCB->cbBase.cbInfo.env, connectDfsCB->cbBase.cbInfo.callback);
270 }
271 } else {
272 napi_value res[NARG_CNT::TWO] = { nullptr };
273 napi_get_undefined(connectDfsCB->cbBase.cbInfo.env, &res[NARG_POS::SECOND]);
274 if (connectDfsCB->status == ERRNO_NOERR) {
275 napi_resolve_deferred(connectDfsCB->cbBase.cbInfo.env,
276 connectDfsCB->cbBase.deferred, res[NARG_POS::SECOND]);
277 } else {
278 res[NARG_POS::FIRST] = NError(connectDfsCB->status).GetNapiErr(connectDfsCB->cbBase.cbInfo.env);
279 napi_reject_deferred(connectDfsCB->cbBase.cbInfo.env, connectDfsCB->cbBase.deferred, res[NARG_POS::FIRST]);
280 }
281 }
282
283 napi_close_handle_scope(connectDfsCB->cbBase.cbInfo.env, scope);
284 delete connectDfsCB;
285 connectDfsCB = nullptr;
286 HILOGI("UvWorkAfterOnStaus end");
287 }
288
OnStatus(const std::string & networkId,int32_t status)289 void NAPIDfsListener::OnStatus(const std::string &networkId, int32_t status)
290 {
291 HILOGI("NAPIDfsListener::OnStatus called");
292
293 auto connectDfsCB = new (std::nothrow) ConnectDfsCB;
294 if (connectDfsCB == nullptr) {
295 HILOGE("NAPIDfsListener::OnStatus, connectDfsCb == nullptr");
296 return;
297 }
298 connectDfsCB->cbBase.cbInfo.env = env_;
299 if (onStatusRef_ != nullptr) {
300 connectDfsCB->cbBase.cbInfo.callback = onStatusRef_;
301 } else {
302 connectDfsCB->cbBase.deferred = promiseDeferred_;
303 }
304 connectDfsCB->networkId = networkId;
305 connectDfsCB->status = status;
306
307 auto task = [connectDfsCB] () {
308 UvWorkAfterOnStaus(connectDfsCB);
309 };
310 auto rev = napi_send_event(env_, task, napi_eprio_immediate);
311 if (rev != ERRNO_NOERR) {
312 delete connectDfsCB;
313 connectDfsCB = nullptr;
314 }
315 HILOGI("NAPIDfsListener::OnStatus end");
316 }
317
318 } // namespace ModuleFileIO
319 } // namespace FileManagement
320 } // namespace OHOS