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