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