1 /*
2 * Copyright (c) 2021 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 "file_manager_napi.h"
17
18 #include <cstring>
19 #include <dirent.h>
20 #include <fcntl.h>
21 #include <unistd.h>
22
23 #include "file_manager_napi_def.h"
24 #include "file_manager_proxy.h"
25 #include "file_manager_service_def.h"
26 #include "file_manager_service_errno.h"
27 #include "ifms_client.h"
28 #include "log.h"
29 namespace OHOS {
30 namespace FileManagerService {
31 using namespace std;
32 using namespace DistributedFS;
33 struct AsyncFileInfoArg {
34 NRef ref_;
35 vector<shared_ptr<FileInfo>> fileRes_;
AsyncFileInfoArgOHOS::FileManagerService::AsyncFileInfoArg36 explicit AsyncFileInfoArg(NVal ref) : ref_(ref), fileRes_() {};
37 ~AsyncFileInfoArg() = default;
38 };
39
40 struct AsyncUriArg {
41 NRef ref_;
42 string uri_;
AsyncUriArgOHOS::FileManagerService::AsyncUriArg43 explicit AsyncUriArg(NVal ref) : ref_(ref), uri_() {};
44 ~AsyncUriArg() = default;
45 };
46
GetFmsClient()47 tuple<bool, IFmsClient*> FileManagerNapi::GetFmsClient()
48 {
49 if (fmsClient_ == nullptr) {
50 fmsClient_ = IFmsClient::GetFmsInstance();
51 }
52 if (fmsClient_ == nullptr) {
53 ERR_LOG("fms get instance fails");
54 return make_tuple(false, nullptr);
55 } else {
56 return make_tuple(true, fmsClient_);
57 }
58 }
59
DealWithErrno(int err)60 UniError DealWithErrno(int err)
61 {
62 unordered_map<int, int> errMap = {
63 {FAIL, ESRCH},
64 {E_CREATE_FAIL, EPERM},
65 {E_NOEXIST, ENOENT},
66 {E_EMPTYFOLDER, ENOTDIR},
67 {SUCCESS, ERRNO_NOERR},
68 };
69 if (errMap.count(err) == 0) {
70 ERR_LOG("unhandler err number %{public}d", err);
71 return UniError(EACCES);
72 } else {
73 return UniError(errMap[err]);
74 }
75 }
76
GetDevInfoArg(const NVal & prop,DevInfo & dev)77 bool GetDevInfoArg(const NVal &prop, DevInfo &dev)
78 {
79 if (prop.HasProp("name")) {
80 bool ret = false;
81 unique_ptr<char[]> name;
82 tie(ret, name, ignore) = prop.GetProp("name").ToUTF8String();
83 if (!ret) {
84 return false;
85 }
86 dev.SetName(std::string(name.get()));
87 }
88 return true;
89 }
90
GetCreateFileArgs(napi_env env,NFuncArg & funcArg)91 tuple<bool, unique_ptr<char[]>, unique_ptr<char[]>, CmdOptions> GetCreateFileArgs(napi_env env, NFuncArg &funcArg)
92 {
93 bool succ = false;
94 unique_ptr<char[]> path;
95 unique_ptr<char[]> fileName;
96 CmdOptions option("local", "", 0, MAX_NUM, false);
97 tie(succ, path, ignore) = NVal(env, funcArg[CreateFileArgs::CF_PATH]).ToUTF8String();
98 if (!succ) {
99 return {false, nullptr, nullptr, option};
100 }
101 tie(succ, fileName, ignore) = NVal(env, funcArg[CreateFileArgs::CF_FILENAME]).ToUTF8String();
102 if (!succ) {
103 return {false, nullptr, nullptr, option};
104 }
105
106 if (funcArg.GetArgc() < CreateFileArgs::CF_OPTION) {
107 return {false, nullptr, nullptr, option};
108 }
109
110 NVal op(env, NVal(env, funcArg[CreateFileArgs::CF_OPTION]).val_);
111 if (op.TypeIs(napi_function)) {
112 return {true, move(path), move(fileName), option};
113 }
114
115 option.SetHasOpt(true);
116 if (!op.HasProp("dev")) {
117 return {true, move(path), move(fileName), option};
118 }
119
120 NVal prop(op.GetProp("dev"));
121 DevInfo dev("local", "");
122 if (!GetDevInfoArg(prop, dev)) {
123 ERR_LOG("CreateFile func get dev para fails");
124 option.SetDevInfo(dev);
125 return {false, nullptr, nullptr, option};
126 }
127
128 option.SetDevInfo(dev);
129 return {true, move(path), move(fileName), option};
130 }
131
CreateFile(napi_env env,napi_callback_info info)132 napi_value FileManagerNapi::CreateFile(napi_env env, napi_callback_info info)
133 {
134 NFuncArg funcArg(env, info);
135 if (!funcArg.InitArgs(CREATE_FILE_PARA_MIN, CREATE_FILE_PARA_MAX)) {
136 UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
137 return nullptr;
138 }
139 bool succ = false;
140 unique_ptr<char[]> path;
141 unique_ptr<char[]> fileName;
142 CmdOptions option;
143 tie(succ, path, fileName, option) = GetCreateFileArgs(env, funcArg);
144 if (!succ) {
145 UniError(EINVAL).ThrowErr(env, "CreateFile func get args fails");
146 return nullptr;
147 }
148 auto arg = make_shared<AsyncUriArg>(NVal(env, funcArg.GetThisVar()));
149 auto cbExec = [arg, path = string(path.get()), fileName = string(fileName.get()), option = option]
150 (napi_env env) -> UniError {
151 IFmsClient* client = nullptr;
152 bool succ = false;
153 tie(succ, client) = GetFmsClient();
154 if (!succ) {
155 return UniError(ESRCH);
156 }
157 string uri = "";
158 int err = client->CreateFile(path, fileName, option, arg->uri_);
159 return DealWithErrno(err);
160 };
161 auto cbComplete = [arg](napi_env env, UniError err) -> NVal {
162 if (err) {
163 return { env, err.GetNapiErr(env) };
164 } else {
165 return NVal::CreateUTF8String(env, arg->uri_);
166 }
167 };
168 string procedureName = "CreateFile";
169 size_t argc = funcArg.GetArgc();
170 NVal thisVar(env, funcArg.GetThisVar());
171 if (argc == CREATE_FILE_PARA_MIN || (argc != CREATE_FILE_PARA_MAX && option.GetHasOpt())) {
172 return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbComplete).val_;
173 } else {
174 int cbIdx = (!option.GetHasOpt() ?
175 CreateFileArgs::CF_CALLBACK_WITHOUT_OP : CreateFileArgs::CF_CALLBACK_WITH_OP);
176 NVal cb(env, funcArg[cbIdx]);
177 return NAsyncWorkCallback(env, thisVar, cb).Schedule(procedureName, cbExec, cbComplete).val_;
178 }
179 }
180
CreateFileArray(napi_env env,shared_ptr<AsyncFileInfoArg> arg)181 static bool CreateFileArray(napi_env env, shared_ptr<AsyncFileInfoArg> arg)
182 {
183 for (size_t i = 0; i < arg->fileRes_.size(); i++) {
184 NVal obj = NVal::CreateObject(env);
185 shared_ptr<FileInfo> res = arg->fileRes_[i];
186 if (res == nullptr) {
187 ERR_LOG("inner error, lack of memory, file count %{public}d", arg->fileRes_.size());
188 return false;
189 }
190 obj.AddProp("name", NVal::CreateUTF8String(env, res->GetName()).val_);
191 obj.AddProp("path", NVal::CreateUTF8String(env, res->GetPath()).val_);
192 obj.AddProp("type", NVal::CreateUTF8String(env, res->GetType()).val_);
193 obj.AddProp("size", NVal::CreateInt64(env, res->GetSize()).val_);
194 obj.AddProp("added_time", NVal::CreateInt64(env, res->GetAddedTime()).val_);
195 obj.AddProp("modified_time", NVal::CreateInt64(env, res->GetModifiedTime()).val_);
196 napi_set_property(env, arg->ref_.Deref(env).val_, NVal::CreateInt32(env, i).val_, obj.val_);
197 }
198 return true;
199 }
200
GetRootArgs(napi_env env,NFuncArg & funcArg,CmdOptions & option)201 bool GetRootArgs(napi_env env, NFuncArg &funcArg, CmdOptions &option)
202 {
203 NVal op(env, NVal(env, funcArg[GetRootArgs::GR_OPTION]).val_);
204 if (op.TypeIs(napi_function)) {
205 return true;
206 }
207
208 option.SetHasOpt(true);
209 if (!op.HasProp("dev")) {
210 return true;
211 }
212
213 NVal prop(op.GetProp("dev"));
214 DevInfo dev("local", "");
215 if (!GetDevInfoArg(prop, dev)) {
216 option.SetDevInfo(dev);
217 return false;
218 }
219
220 option.SetDevInfo(dev);
221 return true;
222 }
223
GetRoot(napi_env env,napi_callback_info info)224 napi_value FileManagerNapi::GetRoot(napi_env env, napi_callback_info info)
225 {
226 NFuncArg funcArg(env, info);
227 if (!funcArg.InitArgs(GET_ROOT_PARA_MIN, GET_ROOT_PARA_MAX)) {
228 UniError(EINVAL).ThrowErr(env, "Number of argments unmatched");
229 return nullptr;
230 }
231
232 CmdOptions option("local", "", 0, MAX_NUM, false);
233 if (funcArg.GetArgc() != 0) {
234 if (!GetRootArgs(env, funcArg, option)) {
235 UniError(EINVAL).ThrowErr(env, "GetRoot func get dev para fails");
236 return nullptr;
237 }
238 }
239
240 napi_value fileArr;
241 napi_create_array(env, &fileArr);
242 auto arg = make_shared<AsyncFileInfoArg>(NVal(env, fileArr));
243 auto cbExec = [option = option, arg] (napi_env env) -> UniError {
244 IFmsClient* client = nullptr;
245 bool succ = false;
246 tie(succ, client) = GetFmsClient();
247 if (!succ) {
248 return UniError(ESRCH);
249 }
250 int err = client->GetRoot(option, arg->fileRes_);
251 return DealWithErrno(err);
252 };
253 auto cbComplete = [arg](napi_env env, UniError err) -> NVal {
254 if (!err && !CreateFileArray(env, arg)) {
255 err = UniError(ENOMEM);
256 }
257 if (err) {
258 return { env, err.GetNapiErr(env) };
259 } else {
260 return NVal(env, arg->ref_.Deref(env).val_);
261 }
262 };
263 string procedureName = "GetRoot";
264 size_t argc = funcArg.GetArgc();
265 NVal thisVar(env, funcArg.GetThisVar());
266 if (argc == GET_ROOT_PARA_MIN || (argc != GET_ROOT_PARA_MAX && option.GetHasOpt())) {
267 return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbComplete).val_;
268 } else {
269 int cbIdx = (!option.GetHasOpt() ? GetRootArgs::GR_CALLBACK_WITHOUT_OP : GetRootArgs::GR_CALLBACK_WITH_OP);
270 NVal cb(env, funcArg[cbIdx]);
271 return NAsyncWorkCallback(env, thisVar, cb).Schedule(procedureName, cbExec, cbComplete).val_;
272 }
273 }
274
GetListFileOption(const NVal & argv,CmdOptions & option)275 bool GetListFileOption(const NVal &argv, CmdOptions &option)
276 {
277 bool ret = false;
278 if (argv.HasProp("dev")) {
279 NVal prop(argv.GetProp("dev"));
280 DevInfo dev("local", "");
281 if (!GetDevInfoArg(prop, dev)) {
282 option.SetDevInfo(dev);
283 return false;
284 }
285 option.SetDevInfo(dev);
286 }
287 if (argv.HasProp("offset")) {
288 int64_t offset;
289 tie(ret, offset) = argv.GetProp("offset").ToInt64();
290 if (!ret) {
291 ERR_LOG("ListFileArgs LF_OPTION offset para fails");
292 return false;
293 }
294 option.SetOffset(offset);
295 }
296 if (argv.HasProp("count")) {
297 int64_t count;
298 tie(ret, count) = argv.GetProp("count").ToInt64();
299 if (!ret) {
300 ERR_LOG("ListFileArgs LF_OPTION count para fails");
301 return false;
302 }
303 option.setCount(count);
304 }
305 return true;
306 }
307
GetListFileArg(napi_env env,NFuncArg & funcArg)308 tuple<bool, unique_ptr<char[]>, unique_ptr<char[]>, CmdOptions> GetListFileArg(
309 napi_env env, NFuncArg &funcArg)
310 {
311 bool succ = false;
312 unique_ptr<char[]> path;
313 unique_ptr<char[]> type;
314 CmdOptions option("local", "", 0, MAX_NUM, false);
315 tie(succ, path, ignore) = NVal(env, funcArg[ListFileArgs::LF_PATH]).ToUTF8String();
316 if (!succ) {
317 ERR_LOG("ListFileArgs LF_PATH para fails");
318 return {false, nullptr, nullptr, option};
319 }
320 tie(succ, type, ignore) = NVal(env, funcArg[ListFileArgs::LF_TYPE]).ToUTF8String();
321 if (!succ) {
322 ERR_LOG("ListFileArgs LF_TYPE para fails");
323 return {false, nullptr, nullptr, option};
324 }
325
326 NVal op(env, NVal(env, funcArg[ListFileArgs::LF_OPTION]).val_);
327 if (op.TypeIs(napi_function)) {
328 return {true, move(type), move(path), option};
329 }
330 option.SetHasOpt(true);
331 if (!GetListFileOption(op, option)) {
332 return {false, nullptr, nullptr, option};
333 }
334 return {true, move(type), move(path), option};
335 }
336
ListFile(napi_env env,napi_callback_info info)337 napi_value FileManagerNapi::ListFile(napi_env env, napi_callback_info info)
338 {
339 NFuncArg funcArg(env, info);
340 if (!funcArg.InitArgs(LIST_FILE_PARA_MIN, LIST_FILE_PARA_MAX)) {
341 UniError(EINVAL).ThrowErr(env, "Number of argments unmatched");
342 return nullptr;
343 }
344 bool succ = false;
345 unique_ptr<char[]> type;
346 unique_ptr<char[]> path;
347 CmdOptions option;
348 tie(succ, type, path, option) = GetListFileArg(env, funcArg);
349 if (!succ) {
350 UniError(EINVAL).ThrowErr(env, "Get argments fails");
351 return nullptr;
352 }
353 napi_value fileArr;
354 napi_create_array(env, &fileArr);
355 auto arg = make_shared<AsyncFileInfoArg>(NVal(env, fileArr));
356 auto cbExec = [type = string(type.get()), path = string(path.get()), option, arg](napi_env env) -> UniError {
357 IFmsClient* client = nullptr;
358 bool succ = false;
359 tie(succ, client) = GetFmsClient();
360 if (!succ) {
361 return UniError(ESRCH);
362 }
363 int err = client->ListFile(type, path, option, arg->fileRes_);
364 return DealWithErrno(err);
365 };
366
367 auto cbComplete = [arg](napi_env env, UniError err) -> NVal {
368 if (!err && !CreateFileArray(env, arg)) {
369 err = UniError(ENOMEM);
370 }
371 if (err) {
372 return { env, err.GetNapiErr(env) };
373 } else {
374 return NVal(env, arg->ref_.Deref(env).val_);
375 }
376 };
377 string procedureName = "ListFile";
378 size_t argc = funcArg.GetArgc();
379 NVal thisVar(env, funcArg.GetThisVar());
380 if (argc == LIST_FILE_PARA_MIN || (argc != LIST_FILE_PARA_MAX && option.GetHasOpt())) {
381 return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbComplete).val_;
382 } else {
383 int cbIdx = ((!option.GetHasOpt()) ? ListFileArgs::LF_CALLBACK_WITHOUT_OP : ListFileArgs::LF_CALLBACK_WITH_OP);
384 NVal cb(env, funcArg[cbIdx]);
385 return NAsyncWorkCallback(env, thisVar, cb).Schedule(procedureName, cbExec, cbComplete).val_;
386 }
387 }
388
Mkdir(napi_env env,napi_callback_info info)389 napi_value FileManagerNapi::Mkdir(napi_env env, napi_callback_info info)
390 {
391 return NVal::CreateUndefined(env).val_;
392 }
393
Export()394 bool FileManagerNapi::Export()
395 {
396 return exports_.AddProp( {
397 NVal::DeclareNapiFunction("listFile", ListFile),
398 NVal::DeclareNapiFunction("createFile", CreateFile),
399 NVal::DeclareNapiFunction("getRoot", GetRoot),
400 });
401 }
402
GetClassName()403 string FileManagerNapi::GetClassName()
404 {
405 return FileManagerNapi::className_;
406 }
407
FileManagerNapi(napi_env env,napi_value exports)408 FileManagerNapi::FileManagerNapi(napi_env env, napi_value exports) : NExporter(env, exports) {}
409
~FileManagerNapi()410 FileManagerNapi::~FileManagerNapi() {}
411 } // namespace FileManagerService
412 } // namespace OHOS
413