1 /*
2 * Copyright (c) 2022-2023 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_entity.h"
17 #include "file_n_exporter.h"
18
19 #include <cstdio>
20 #include <cstdlib>
21 #include <cstring>
22 #include <memory>
23 #include <sys/file.h>
24 #include <tuple>
25
26 #include "file_utils.h"
27 #include "filemgmt_libhilog.h"
28 #include "filemgmt_libn.h"
29 #include "../common_func.h"
30 #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
31 #include "file_uri.h"
32 #endif
33
34 namespace OHOS {
35 namespace FileManagement {
36 namespace ModuleFileIO {
37 using namespace std;
38 using namespace OHOS::FileManagement::LibN;
39
GetFileEntity(napi_env env,napi_value raf_entity)40 static FileEntity *GetFileEntity(napi_env env, napi_value raf_entity)
41 {
42 auto rafEntity = NClass::GetEntityOf<FileEntity>(env, raf_entity);
43 if (!rafEntity) {
44 HILOGE("Failed to get file entity");
45 NError(EINVAL).ThrowErr(env);
46 return nullptr;
47 }
48 if (!rafEntity->fd_) {
49 HILOGE("rafEntity fd is not exist");
50 NError(EINVAL).ThrowErr(env);
51 return nullptr;
52 }
53 return rafEntity;
54 }
55
GetFD(napi_env env,napi_callback_info info)56 napi_value FileNExporter::GetFD(napi_env env, napi_callback_info info)
57 {
58 NFuncArg funcArg(env, info);
59 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
60 HILOGE("Number of arguments unmatched");
61 NError(EINVAL).ThrowErr(env);
62 return nullptr;
63 }
64 auto rafEntity = GetFileEntity(env, funcArg.GetThisVar());
65 if (!rafEntity) {
66 HILOGE("Failed to get file entity");
67 return nullptr;
68 }
69 return NVal::CreateInt32(env, rafEntity->fd_.get()->GetFD()).val_;
70 }
71
72 #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
RealPathCore(const string & srcPath)73 static tuple<int, unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*>> RealPathCore(const string &srcPath)
74 {
75 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> realpath_req = {
76 new (std::nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
77 if (!realpath_req) {
78 HILOGE("Failed to request heap memory.");
79 return { ENOMEM, move(realpath_req)};
80 }
81 int ret = uv_fs_realpath(nullptr, realpath_req.get(), srcPath.c_str(), nullptr);
82 return { ret, move(realpath_req) };
83 }
84
GetExclusive(napi_env env,NFuncArg & funcArg,bool & exclusive)85 static bool GetExclusive(napi_env env, NFuncArg &funcArg, bool &exclusive)
86 {
87 if (funcArg.GetArgc() >= NARG_CNT::ONE) {
88 bool succ = false;
89 tie(succ, exclusive) = NVal(env, funcArg[NARG_POS::FIRST]).ToBool(exclusive);
90 if (!succ) {
91 HILOGE("Invalid exclusive");
92 NError(EINVAL).ThrowErr(env);
93 return false;
94 }
95 }
96 return true;
97 }
98
GetPath(napi_env env,napi_callback_info info)99 napi_value FileNExporter::GetPath(napi_env env, napi_callback_info info)
100 {
101 NFuncArg funcArg(env, info);
102 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
103 HILOGE("Number of arguments unmatched");
104 NError(EINVAL).ThrowErr(env);
105 return nullptr;
106 }
107 auto fileEntity = GetFileEntity(env, funcArg.GetThisVar());
108 if (!fileEntity) {
109 HILOGE("Failed to get file entity");
110 NError(EINVAL).ThrowErr(env);
111 return nullptr;
112 }
113 if (fileEntity->uri_.length() != 0) {
114 AppFileService::ModuleFileUri::FileUri fileUri(fileEntity->uri_);
115 return NVal::CreateUTF8String(env, fileUri.GetPath()).val_;
116 }
117 auto [realPathRes, realPath] = RealPathCore(fileEntity->path_);
118 if (realPathRes != ERRNO_NOERR) {
119 HILOGE("Failed to get real path");
120 NError(realPathRes).ThrowErr(env);
121 return nullptr;
122 }
123 return NVal::CreateUTF8String(env, string(static_cast<const char *>(realPath->ptr))).val_;
124 }
125
GetName(napi_env env,napi_callback_info info)126 napi_value FileNExporter::GetName(napi_env env, napi_callback_info info)
127 {
128 NFuncArg funcArg(env, info);
129 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
130 HILOGE("Number of arguments unmatched");
131 NError(EINVAL).ThrowErr(env);
132 return nullptr;
133 }
134 auto fileEntity = GetFileEntity(env, funcArg.GetThisVar());
135 if (!fileEntity) {
136 HILOGE("Failed to get file entity");
137 NError(EINVAL).ThrowErr(env);
138 return nullptr;
139 }
140 if (fileEntity->uri_.length() != 0) {
141 AppFileService::ModuleFileUri::FileUri fileUri(fileEntity->uri_);
142 return NVal::CreateUTF8String(env, fileUri.GetName()).val_;
143 }
144 auto [realPathRes, realPath] = RealPathCore(fileEntity->path_);
145 if (realPathRes != ERRNO_NOERR) {
146 HILOGE("Failed to get real path");
147 NError(realPathRes).ThrowErr(env);
148 return nullptr;
149 }
150 string path(static_cast<const char *>(realPath->ptr));
151 auto pos = path.find_last_of('/');
152 if (pos == string::npos) {
153 HILOGE("Failed to split filename from path");
154 NError(ENOENT).ThrowErr(env);
155 return nullptr;
156 }
157 return NVal::CreateUTF8String(env, path.substr(pos + 1)).val_;
158 }
159
GetParent(napi_env env,napi_callback_info info)160 napi_value FileNExporter::GetParent(napi_env env, napi_callback_info info)
161 {
162 NFuncArg funcArg(env, info);
163 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
164 HILOGE("Number of arguments unmatched");
165 NError(EINVAL).ThrowErr(env);
166 return nullptr;
167 }
168 auto fileEntity = GetFileEntity(env, funcArg.GetThisVar());
169 if (!fileEntity) {
170 HILOGE("Failed to get file entity");
171 NError(EINVAL).ThrowErr(env);
172 return nullptr;
173 }
174
175 string path(fileEntity->path_);
176 if (fileEntity->uri_.length() != 0) {
177 AppFileService::ModuleFileUri::FileUri fileUri(fileEntity->uri_);
178 path = fileUri.GetPath();
179 } else {
180 auto [realPathRes, realPath] = RealPathCore(path);
181 if (realPathRes) {
182 HILOGE("Failed to get real path");
183 NError(realPathRes).ThrowErr(env);
184 return nullptr;
185 }
186 path = static_cast<const char *>(realPath->ptr);
187 }
188 auto pos = path.find_last_of('/');
189 if (pos == string::npos) {
190 HILOGE("Failed to split filename from path");
191 NError(ENOENT).ThrowErr(env);
192 return nullptr;
193 }
194 return NVal::CreateUTF8String(env, path.substr(0, pos)).val_;
195 }
196
Lock(napi_env env,napi_callback_info info)197 napi_value FileNExporter::Lock(napi_env env, napi_callback_info info)
198 {
199 NFuncArg funcArg(env, info);
200 if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::TWO)) {
201 HILOGE("Number of arguments unmatched");
202 NError(EINVAL).ThrowErr(env);
203 return nullptr;
204 }
205
206 auto fileEntity = GetFileEntity(env, funcArg.GetThisVar());
207 if (!fileEntity) {
208 HILOGE("Failed to get file entity");
209 NError(EINVAL).ThrowErr(env);
210 return nullptr;
211 }
212
213 bool exclusive = false;
214 if (!GetExclusive(env, funcArg, exclusive)) {
215 return nullptr;
216 }
217 auto cbExec = [exclusive, fileEntity]() -> NError {
218 if (!fileEntity || !fileEntity->fd_.get()) {
219 HILOGE("File has been closed in Lock cbExec possibly");
220 return NError(EIO);
221 }
222 int ret = 0;
223 auto mode = exclusive ? LOCK_EX : LOCK_SH;
224 ret = flock(fileEntity->fd_.get()->GetFD(), mode);
225 if (ret < 0) {
226 HILOGE("Failed to lock file");
227 return NError(errno);
228 } else {
229 return NError(ERRNO_NOERR);
230 }
231 };
232
233 auto cbCompl = [](napi_env env, NError err) -> NVal {
234 if (err) {
235 return { env, err.GetNapiErr(env) };
236 }
237 return NVal::CreateUndefined(env);
238 };
239
240 NVal thisVar(env, funcArg.GetThisVar());
241 if (funcArg.GetArgc() == NARG_CNT::ZERO || (funcArg.GetArgc() == NARG_CNT::ONE &&
242 !NVal(env, funcArg[NARG_POS::FIRST]).TypeIs(napi_function))) {
243 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_LOCK_NAME, cbExec, cbCompl).val_;
244 } else {
245 int cbIdx = ((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::FIRST);
246 NVal cb(env, funcArg[cbIdx]);
247 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_LOCK_NAME, cbExec, cbCompl).val_;
248 }
249 }
250
TryLock(napi_env env,napi_callback_info info)251 napi_value FileNExporter::TryLock(napi_env env, napi_callback_info info)
252 {
253 NFuncArg funcArg(env, info);
254 if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
255 HILOGE("Number of arguments unmatched");
256 NError(EINVAL).ThrowErr(env);
257 return nullptr;
258 }
259
260 auto fileEntity = GetFileEntity(env, funcArg.GetThisVar());
261 if (!fileEntity) {
262 HILOGE("Failed to get file entity");
263 NError(EINVAL).ThrowErr(env);
264 return nullptr;
265 }
266
267 bool exclusive = false;
268 if (!GetExclusive(env, funcArg, exclusive)) {
269 return nullptr;
270 }
271
272 int ret = 0;
273 auto mode = exclusive ? LOCK_EX : LOCK_SH;
274 ret = flock(fileEntity->fd_.get()->GetFD(), mode | LOCK_NB);
275 if (ret < 0) {
276 HILOGE("Failed to try to lock file");
277 NError(errno).ThrowErr(env);
278 }
279
280 return NVal::CreateUndefined(env).val_;
281 }
282
UnLock(napi_env env,napi_callback_info info)283 napi_value FileNExporter::UnLock(napi_env env, napi_callback_info info)
284 {
285 NFuncArg funcArg(env, info);
286 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
287 HILOGE("Number of arguments unmatched");
288 NError(EINVAL).ThrowErr(env);
289 return nullptr;
290 }
291
292 auto fileEntity = GetFileEntity(env, funcArg.GetThisVar());
293 if (!fileEntity) {
294 HILOGE("Failed to get file entity");
295 NError(EINVAL).ThrowErr(env);
296 return nullptr;
297 }
298
299 int ret = 0;
300 ret = flock(fileEntity->fd_.get()->GetFD(), LOCK_UN);
301 if (ret < 0) {
302 HILOGE("Failed to unlock file");
303 NError(errno).ThrowErr(env);
304 return nullptr;
305 }
306 return NVal::CreateUndefined(env).val_;
307 }
308 #endif
309
Constructor(napi_env env,napi_callback_info info)310 napi_value FileNExporter::Constructor(napi_env env, napi_callback_info info)
311 {
312 NFuncArg funcArg(env, info);
313 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
314 HILOGE("Number of arguments unmatched");
315 NError(EINVAL).ThrowErr(env);
316 return nullptr;
317 }
318
319 auto rafEntity = CreateUniquePtr<FileEntity>();
320 if (rafEntity == nullptr) {
321 HILOGE("Failed to request heap memory.");
322 NError(ENOMEM).ThrowErr(env);
323 return nullptr;
324 }
325 if (!NClass::SetEntityFor<FileEntity>(env, funcArg.GetThisVar(), move(rafEntity))) {
326 HILOGE("Failed to set file entity");
327 NError(EIO).ThrowErr(env);
328 return nullptr;
329 }
330 return funcArg.GetThisVar();
331 }
332
Export()333 bool FileNExporter::Export()
334 {
335 vector<napi_property_descriptor> props = {
336 NVal::DeclareNapiGetter("fd", GetFD),
337 #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
338 NVal::DeclareNapiGetter("path", GetPath),
339 NVal::DeclareNapiGetter("name", GetName),
340 NVal::DeclareNapiFunction("lock", Lock),
341 NVal::DeclareNapiFunction("tryLock", TryLock),
342 NVal::DeclareNapiFunction("unlock", UnLock),
343 NVal::DeclareNapiFunction("getParent", GetParent),
344 #endif
345 };
346
347 #ifdef WIN_PLATFORM
348 string className = GetNExporterName();
349 #else
350 string className = GetClassName();
351 #endif
352 bool succ = false;
353 napi_value classValue = nullptr;
354 tie(succ, classValue) = NClass::DefineClass(exports_.env_, className,
355 FileNExporter::Constructor, move(props));
356 if (!succ) {
357 HILOGE("Define class exceptions");
358 NError(EIO).ThrowErr(exports_.env_);
359 return false;
360 }
361 succ = NClass::SaveClass(exports_.env_, className, classValue);
362 if (!succ) {
363 HILOGE("Save class exceptions");
364 NError(EIO).ThrowErr(exports_.env_);
365 return false;
366 }
367
368 return exports_.AddProp(className, classValue);
369 }
370
371 #ifdef WIN_PLATFORM
GetNExporterName()372 string FileNExporter::GetNExporterName()
373 #else
374 string FileNExporter::GetClassName()
375 #endif
376 {
377 return FileNExporter::className_;
378 }
379
FileNExporter(napi_env env,napi_value exports)380 FileNExporter::FileNExporter(napi_env env, napi_value exports) : NExporter(env, exports) {}
~FileNExporter()381 FileNExporter::~FileNExporter() {}
382 } // namespace ModuleFileIO
383 } // namespace FileManagement
384 } // namespace OHOS