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 connectDfsCB->jsCallbackObject->SetConnectDfsEnv(env);
59 HILOGI("connectDfsCB set env success");
60 if (connectDfsCB->dfsConnectCB.callback != nullptr) {
61 connectDfsCB->jsCallbackObject->
62 SetConnectDfsCBRef(connectDfsCB->dfsConnectCB.callback);
63 HILOGI("connectDfsCB set callback success");
64 } else {
65 connectDfsCB->jsCallbackObject->
66 SetConnectDfsPromiseRef(connectDfsCB->cbBase.deferred);
67 HILOGI("connectDfsCB set promise success");
68 }
69
70 connectDfsCB->result = Storage::DistributedFile::DistributedFileDaemonManager::GetInstance().
71 OpenP2PConnectionEx(connectDfsCB->networkId, connectDfsCB->jsCallbackObject);
72 HILOGI(" cbExec end ret = %{public}d", connectDfsCB->result);
73 }
74
cbCompl(napi_env env,napi_status status,void * data)75 void cbCompl(napi_env env, napi_status status, void *data)
76 {
77 HILOGI("cbCompl for connectDfs called");
78 auto connectDfsCB = static_cast<ConnectDfsCB *>(data);
79 napi_value result[NARG_CNT::TWO] = { nullptr };
80 napi_get_undefined(env, &result[NARG_POS::SECOND]);
81 if (connectDfsCB->result == ERRNO_NOERR) {
82 napi_get_undefined(env, &result[NARG_POS::FIRST]);
83 napi_resolve_deferred(env, connectDfsCB->cbBase.deferred, result[NARG_POS::SECOND]);
84 } else {
85 result[NARG_POS::FIRST] = NError(connectDfsCB->result).GetNapiErr(env);
86 napi_reject_deferred(env, connectDfsCB->cbBase.deferred, result[NARG_POS::FIRST]);
87 }
88 napi_delete_async_work(env, connectDfsCB->cbBase.asyncWork);
89 delete connectDfsCB;
90 connectDfsCB = nullptr;
91 HILOGI("cbCompl for connectDfs end");
92 }
93
GetListenerFromOptionArg(napi_env env,const NFuncArg & funcArg)94 tuple<bool, NVal> ConnectDfs::GetListenerFromOptionArg(napi_env env, const NFuncArg &funcArg)
95 {
96 NVal op(env, funcArg[NARG_POS::SECOND]);
97 if (!op.HasProp("onStatus") || op.GetProp("onStatus").TypeIs(napi_undefined)) {
98 return { true, NVal() };
99 }
100 NVal onStatus = op.GetProp("onStatus");
101 if (!onStatus.TypeIs(napi_function)) {
102 HILOGE("Illegal dfsListeners.onStatus type");
103 return { false, NVal() };
104 }
105 return { true, onStatus };
106 }
107
ParseJsOperand(napi_env env,NVal paramFromJsArg)108 tuple<bool, std::string> ConnectDfs::ParseJsOperand(napi_env env, NVal paramFromJsArg)
109 {
110 auto [succ, param, ignore] = paramFromJsArg.ToUTF8String();
111 if (!succ) {
112 HILOGE("parse parameter failed.");
113 return { false, "" };
114 }
115 std::string paramStr = std::string(param.get());
116 return { true, paramStr };
117 }
118
ParseJsParam(napi_env env,NFuncArg & funcArg,ConnectDfsCB * connectDfsCB)119 int ConnectDfs::ParseJsParam(napi_env env, NFuncArg &funcArg, ConnectDfsCB *connectDfsCB)
120 {
121 if (!funcArg.InitArgs(NARG_CNT::TWO)) {
122 HILOGE("Number of arguments unmatched");
123 return E_PARAMS;
124 }
125 auto [succNetworkId, networkId] = ParseJsOperand(env, { env, funcArg[NARG_POS::FIRST] });
126 auto [succDfsListeners, dfsListeners] = GetListenerFromOptionArg(env, funcArg);
127 if (!succNetworkId || !succDfsListeners) {
128 HILOGE("The first/second argument requires string/napi_function");
129 return E_PARAMS;
130 }
131 connectDfsCB->networkId = networkId;
132 napi_create_reference(env, dfsListeners.val_, 1, &connectDfsCB->dfsConnectCB.callback);
133 return ERRNO_NOERR;
134 }
135
Async(napi_env env,napi_callback_info info)136 napi_value ConnectDfs::Async(napi_env env, napi_callback_info info)
137 {
138 HILOGI("ConnectDfs::Async called");
139 ConnectDfsCB *connectDfsCB = CreateConnectDfsCBCBInfo(env);
140 if (connectDfsCB == nullptr) {
141 NError(E_PARAMS).ThrowErr(env);
142 return nullptr;
143 }
144 NFuncArg funcArg(env, info);
145 auto result = ParseJsParam(env, funcArg, connectDfsCB);
146 if (result != ERRNO_NOERR) {
147 NError(result).ThrowErr(env);
148 delete connectDfsCB;
149 connectDfsCB = nullptr;
150 return nullptr;
151 }
152
153 napi_value ret = nullptr;
154 napi_status status = napi_create_promise(env, &connectDfsCB->cbBase.deferred, &ret);
155 if (status != napi_ok) {
156 HILOGE("INNER BUG. Cannot create promise for %{public}d", status);
157 delete connectDfsCB;
158 connectDfsCB = nullptr;
159 return nullptr;
160 }
161
162 status = napi_create_async_work(env, nullptr, NVal::CreateUTF8String(env, "ResourceName").val_,
163 cbExec, cbCompl, static_cast<void *>(connectDfsCB), &connectDfsCB->cbBase.asyncWork);
164 if (status != napi_ok) {
165 HILOGE("INNER BUG. Failed to create async work for %{public}d", status);
166 delete connectDfsCB;
167 connectDfsCB = nullptr;
168 return nullptr;
169 }
170
171 status = napi_queue_async_work(env, connectDfsCB->cbBase.asyncWork);
172 if (status != napi_ok) {
173 HILOGE("INNER BUG. Failed to queue async work for %{public}d", status);
174 delete connectDfsCB;
175 connectDfsCB = nullptr;
176 return nullptr;
177 }
178
179 if (ret == nullptr) {
180 HILOGE("napi_async_work ret = nullptr");
181 NError(E_PARAMS).ThrowErr(env);
182 return NVal::CreateUndefined(env).val_;
183 }
184 HILOGI("ConnectDfs end");
185 return ret;
186 }
187
CheckAndGetParameters(uv_work_t * work,napi_handle_scope * scope)188 ConnectDfsCB *CheckAndGetParameters(uv_work_t *work, napi_handle_scope *scope)
189 {
190 HILOGI("ConnectDfsCB::CheckAndGetParameters GetParam called");
191 if (work == nullptr) {
192 HILOGE("ConnectDfsCB, GetParam work is null");
193 return nullptr;
194 }
195 ConnectDfsCB *connectDfsCB = static_cast<ConnectDfsCB *>(work->data);
196 if (connectDfsCB == nullptr) {
197 HILOGE("ConnectDfsCB, GetParam connectDfsCB is null");
198 delete work;
199 work = nullptr;
200 return nullptr;
201 }
202 napi_open_handle_scope(connectDfsCB->cbBase.cbInfo.env, scope);
203 if (scope == nullptr) {
204 delete connectDfsCB;
205 connectDfsCB = nullptr;
206 delete work;
207 work = nullptr;
208 return nullptr;
209 }
210 HILOGI("ConnectDfsCB::CheckAndGetParameters GetParam end");
211 return connectDfsCB;
212 }
213
SetConnectDfsEnv(const napi_env & env)214 void NAPIDfsListener::SetConnectDfsEnv(const napi_env &env)
215 {
216 env_ = env;
217 }
218
SetConnectDfsCBRef(const napi_ref & ref)219 void NAPIDfsListener::SetConnectDfsCBRef(const napi_ref &ref)
220 {
221 onStatusRef_ = ref;
222 }
223
SetConnectDfsPromiseRef(const napi_deferred & promiseDeferred)224 void NAPIDfsListener::SetConnectDfsPromiseRef(const napi_deferred &promiseDeferred)
225 {
226 promiseDeferred_ = promiseDeferred;
227 }
228
WrapInt32(napi_env & env,int32_t num,const std::string & paramName)229 napi_value WrapInt32(napi_env &env, int32_t num, const std::string ¶mName)
230 {
231 HILOGI("WrapInt32 called");
232 napi_value jsObject = nullptr;
233 napi_create_object(env, &jsObject);
234 napi_value jsValue = nullptr;
235 HILOGD("WrapInt32 called. %{public}s = %{public}d", paramName.c_str(), num);
236 napi_create_int32(env, num, &jsValue);
237 napi_set_named_property(env, jsObject, paramName.c_str(), jsValue);
238
239 return jsObject;
240 }
241
WrapString(napi_env & env,const std::string & param,const std::string & paramName)242 napi_value WrapString(napi_env &env, const std::string ¶m, const std::string ¶mName)
243 {
244 HILOGI("WrapString called");
245 napi_value jsValue = nullptr;
246 HILOGD("WrapString called. %{public}s = %{public}s", paramName.c_str(), param.c_str());
247 napi_create_string_utf8(env, param.c_str(), NAPI_AUTO_LENGTH, &jsValue);
248
249 return jsValue;
250 }
251
UvWorkAfterOnStaus(uv_work_t * work,int status)252 void UvWorkAfterOnStaus(uv_work_t *work, int status)
253 {
254 HILOGI("UvWorkAfterOnStaus called");
255 napi_handle_scope scope = nullptr;
256 ConnectDfsCB *connectDfsCB = CheckAndGetParameters(work, &scope);
257 if (connectDfsCB == nullptr) {
258 return;
259 }
260 HILOGI("UvWorkAfterOnStaus, status = %{public}d", connectDfsCB->status);
261
262 napi_value result[NARG_CNT::TWO] = {nullptr};
263 result[NARG_POS::FIRST] = WrapString(connectDfsCB->cbBase.cbInfo.env, connectDfsCB->networkId.c_str(), "networkId");
264 result[NARG_POS::SECOND] = WrapInt32(connectDfsCB->cbBase.cbInfo.env, connectDfsCB->status, "status");
265 if (connectDfsCB->cbBase.deferred == nullptr) {
266 napi_value callback = nullptr;
267 napi_value undefined = nullptr;
268 napi_get_undefined(connectDfsCB->cbBase.cbInfo.env, &undefined);
269 napi_value callResult = nullptr;
270 napi_get_reference_value(connectDfsCB->cbBase.cbInfo.env,
271 connectDfsCB->cbBase.cbInfo.callback, &callback);
272 napi_call_function(connectDfsCB->cbBase.cbInfo.env, undefined, callback, NARG_CNT::TWO, result, &callResult);
273 if (connectDfsCB->cbBase.cbInfo.callback != nullptr) {
274 napi_delete_reference(connectDfsCB->cbBase.cbInfo.env, connectDfsCB->cbBase.cbInfo.callback);
275 }
276 } else {
277 napi_value res[NARG_CNT::TWO] = { nullptr };
278 napi_get_undefined(connectDfsCB->cbBase.cbInfo.env, &res[NARG_POS::SECOND]);
279 if (connectDfsCB->status == ERRNO_NOERR) {
280 napi_resolve_deferred(connectDfsCB->cbBase.cbInfo.env,
281 connectDfsCB->cbBase.deferred, res[NARG_POS::SECOND]);
282 } else {
283 res[NARG_POS::FIRST] = NError(connectDfsCB->status).GetNapiErr(connectDfsCB->cbBase.cbInfo.env);
284 napi_reject_deferred(connectDfsCB->cbBase.cbInfo.env, connectDfsCB->cbBase.deferred, res[NARG_POS::FIRST]);
285 }
286 }
287
288 napi_close_handle_scope(connectDfsCB->cbBase.cbInfo.env, scope);
289 delete connectDfsCB;
290 connectDfsCB = nullptr;
291 delete work;
292 work = nullptr;
293 HILOGI("UvWorkAfterOnStaus end");
294 }
295
OnStatus(const std::string & networkId,int32_t status)296 void NAPIDfsListener::OnStatus(const std::string &networkId, int32_t status)
297 {
298 HILOGI("NAPIDfsListener::OnStatus called");
299 uv_loop_s *loop = nullptr;
300
301 napi_get_uv_event_loop(env_, &loop);
302 if (loop == nullptr) {
303 HILOGE("NAPIDfsListener::OnStatus, loop == nullptr");
304 return;
305 }
306
307 uv_work_t *work = new (std::nothrow) uv_work_t;
308 if (work == nullptr) {
309 return;
310 }
311
312 auto connectDfsCB = new (std::nothrow) ConnectDfsCB;
313 if (connectDfsCB == nullptr) {
314 HILOGE("NAPIDfsListener::OnStatus, connectDfsCb == nullptr");
315 delete work;
316 work = nullptr;
317 return;
318 }
319 connectDfsCB->cbBase.cbInfo.env = env_;
320 if (onStatusRef_ != nullptr) {
321 connectDfsCB->cbBase.cbInfo.callback = onStatusRef_;
322 } else {
323 connectDfsCB->cbBase.deferred = promiseDeferred_;
324 }
325 connectDfsCB->networkId = networkId;
326 connectDfsCB->status = status;
327 work->data = static_cast<void *>(connectDfsCB);
328
329 int rev = uv_queue_work(
330 loop, work, [](uv_work_t *work) {}, UvWorkAfterOnStaus);
331 if (rev != ERRNO_NOERR) {
332 delete connectDfsCB;
333 connectDfsCB = nullptr;
334 delete work;
335 work = nullptr;
336 }
337 HILOGI("NAPIDfsListener::OnStatus end");
338 }
339
340 } // namespace ModuleFileIO
341 } // namespace FileManagement
342 } // namespace OHOS