1 /*
2 * Copyright (c) 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 "randomaccessfile_n_exporter.h"
17
18 #include "file_utils.h"
19 #include "randomaccessfile_entity.h"
20 #include "../common_func.h"
21
22 namespace OHOS {
23 namespace FileManagement {
24 namespace ModuleFileIO {
25 using namespace std;
26 using namespace OHOS::FileManagement::LibN;
27
GetRAFEntity(napi_env env,napi_value raf_entity)28 static tuple<bool, RandomAccessFileEntity*> GetRAFEntity(napi_env env, napi_value raf_entity)
29 {
30 auto rafEntity = NClass::GetEntityOf<RandomAccessFileEntity>(env, raf_entity);
31 if (!rafEntity) {
32 return { false, nullptr };
33 }
34 return { true, rafEntity };
35 }
36
DoReadRAF(napi_env env,void * buf,size_t len,int fd,int64_t offset)37 static int DoReadRAF(napi_env env, void* buf, size_t len, int fd, int64_t offset)
38 {
39 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> read_req = {
40 new (nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
41 if (read_req == nullptr) {
42 HILOGE("Failed to request heap memory.");
43 return ENOMEM;
44 }
45 uv_buf_t iov = uv_buf_init(static_cast<char *>(buf), len);
46 int ret = uv_fs_read(nullptr, read_req.get(), fd, &iov, 1, offset, nullptr);
47 return ret;
48 }
49
DoWriteRAF(napi_env env,void * buf,size_t len,int fd,int64_t offset)50 static int DoWriteRAF(napi_env env, void* buf, size_t len, int fd, int64_t offset)
51 {
52 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> write_req = {
53 new (nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
54 if (write_req == nullptr) {
55 HILOGE("Failed to request heap memory.");
56 return ENOMEM;
57 }
58 uv_buf_t iov = uv_buf_init(static_cast<char *>(buf), len);
59 int ret = uv_fs_write(nullptr, write_req.get(), fd, &iov, 1, offset, nullptr);
60 return ret;
61 }
62
GetFD(napi_env env,napi_callback_info info)63 napi_value RandomAccessFileNExporter::GetFD(napi_env env, napi_callback_info info)
64 {
65 NFuncArg funcArg(env, info);
66 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
67 HILOGE("Number of arguments unmatched");
68 NError(EINVAL).ThrowErr(env);
69 return nullptr;
70 }
71 auto [succEntity, rafEntity] = GetRAFEntity(env, funcArg.GetThisVar());
72 if (!succEntity) {
73 HILOGE("Failed to get entity of RandomAccessFile");
74 NError(EIO).ThrowErr(env);
75 return nullptr;
76 }
77 return NVal::CreateInt32(env, rafEntity->fd.get()->GetFD()).val_;
78 }
79
GetFPointer(napi_env env,napi_callback_info info)80 napi_value RandomAccessFileNExporter::GetFPointer(napi_env env, napi_callback_info info)
81 {
82 NFuncArg funcArg(env, info);
83 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
84 HILOGE("Number of arguments unmatched");
85 NError(EINVAL).ThrowErr(env);
86 return nullptr;
87 }
88 auto [succEntity, rafEntity] = GetRAFEntity(env, funcArg.GetThisVar());
89 if (!succEntity) {
90 HILOGE("Failed to get entity of RandomAccessFile");
91 NError(EIO).ThrowErr(env);
92 return nullptr;
93 }
94 return NVal::CreateInt64(env, rafEntity->filePointer).val_;
95 }
96
SetFilePointerSync(napi_env env,napi_callback_info info)97 napi_value RandomAccessFileNExporter::SetFilePointerSync(napi_env env, napi_callback_info info)
98 {
99 NFuncArg funcArg(env, info);
100 if (!funcArg.InitArgs(NARG_CNT::ONE)) {
101 HILOGE("Number of arguments unmatched");
102 NError(EINVAL).ThrowErr(env);
103 return nullptr;
104 }
105 auto [succEntity, rafEntity] = GetRAFEntity(env, funcArg.GetThisVar());
106 if (!succEntity) {
107 HILOGE("Failed to get entity of RandomAccessFile");
108 NError(EIO).ThrowErr(env);
109 return nullptr;
110 }
111 auto [succ, fp] = NVal(env, funcArg[NARG_POS::FIRST]).ToInt64();
112 if (!succ) {
113 HILOGE("Invalid filePointer");
114 NError(EINVAL).ThrowErr(env);
115 return nullptr;
116 }
117 rafEntity->filePointer = fp;
118 return NVal::CreateUndefined(env).val_;
119 }
120
CalculateOffset(int64_t offset,int64_t fPointer)121 static int64_t CalculateOffset(int64_t offset, int64_t fPointer)
122 {
123 if (offset < 0) {
124 HILOGI("No specified offset provided");
125 offset = fPointer;
126 } else {
127 offset += fPointer;
128 }
129 return offset;
130 }
131
ReadSync(napi_env env,napi_callback_info info)132 napi_value RandomAccessFileNExporter::ReadSync(napi_env env, napi_callback_info info)
133 {
134 NFuncArg funcArg(env, info);
135 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
136 HILOGE("Number of arguments unmatched");
137 NError(EINVAL).ThrowErr(env);
138 return nullptr;
139 }
140 auto [succEntity, rafEntity] = GetRAFEntity(env, funcArg.GetThisVar());
141 if (!succEntity) {
142 HILOGE("Failed to get entity of RandomAccessFile");
143 NError(EIO).ThrowErr(env);
144 return nullptr;
145 }
146 auto [succ, buf, len, offset] =
147 CommonFunc::GetReadArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
148 if (!succ) {
149 HILOGE("Invalid buffer/options");
150 NError(EINVAL).ThrowErr(env);
151 return nullptr;
152 }
153 offset = CalculateOffset(offset, rafEntity->filePointer);
154 int actLen = DoReadRAF(env, buf, len, rafEntity->fd.get()->GetFD(), offset);
155 if (actLen < 0) {
156 HILOGE("Failed to read file for %{public}d", actLen);
157 NError(actLen).ThrowErr(env);
158 return nullptr;
159 }
160 rafEntity->filePointer = offset + actLen;
161 return NVal::CreateInt64(env, actLen).val_;
162 }
163
164 struct AsyncIORafReadArg {
165 int lenRead { 0 };
166 NRef rafRefReadBuf;
167
AsyncIORafReadArgOHOS::FileManagement::ModuleFileIO::AsyncIORafReadArg168 explicit AsyncIORafReadArg(NVal jsReadBuf) : rafRefReadBuf(jsReadBuf) {}
169 ~AsyncIORafReadArg() = default;
170 };
171
ReadExec(napi_env env,NFuncArg & funcArg,RandomAccessFileEntity * rafEntity)172 static napi_value ReadExec(napi_env env, NFuncArg &funcArg, RandomAccessFileEntity* rafEntity)
173 {
174 bool succ = false;
175 void *buf = nullptr;
176 size_t len = 0;
177 int64_t offset = 0;
178 tie(succ, buf, len, offset) = CommonFunc::GetReadArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
179 if (!succ) {
180 HILOGE("Invalid buffer/options");
181 NError(EINVAL).ThrowErr(env);
182 return nullptr;
183 }
184
185 auto arg = CreateSharedPtr<AsyncIORafReadArg>(NVal(env, funcArg[NARG_POS::FIRST]));
186 if (arg == nullptr) {
187 HILOGE("Failed to request heap memory.");
188 NError(ENOMEM).ThrowErr(env);
189 return nullptr;
190 }
191 offset = CalculateOffset(offset, rafEntity->filePointer);
192 auto cbExec = [env, arg, buf, len, offset, rafEntity]() -> NError {
193 if (!rafEntity || !rafEntity->fd.get()) {
194 HILOGE("RandomAccessFile has been closed in read cbExec possibly");
195 return NError(EIO);
196 }
197 int actLen = DoReadRAF(env, buf, len, rafEntity->fd.get()->GetFD(), offset);
198 if (actLen < 0) {
199 return NError(actLen);
200 }
201 arg->lenRead = actLen;
202 rafEntity->filePointer = offset + actLen;
203 return NError(ERRNO_NOERR);
204 };
205 auto cbCompl = [arg](napi_env env, NError err) -> NVal {
206 if (err) {
207 return { env, err.GetNapiErr(env) };
208 }
209 return { NVal::CreateInt64(env, static_cast<int64_t>(arg->lenRead)) };
210 };
211
212 NVal thisVar(env, funcArg.GetThisVar());
213 if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
214 !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
215 return NAsyncWorkPromise(env, thisVar).Schedule(readProcedureName, cbExec, cbCompl).val_;
216 } else {
217 int cbIdx = ((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD);
218 NVal cb(env, funcArg[cbIdx]);
219 return NAsyncWorkCallback(env, thisVar, cb).Schedule(readProcedureName, cbExec, cbCompl).val_;
220 }
221 }
222
Read(napi_env env,napi_callback_info info)223 napi_value RandomAccessFileNExporter::Read(napi_env env, napi_callback_info info)
224 {
225 NFuncArg funcArg(env, info);
226 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
227 HILOGE("Number of arguments unmatched");
228 NError(EINVAL).ThrowErr(env);
229 return nullptr;
230 }
231 auto[succ, rafEntity] = GetRAFEntity(env, funcArg.GetThisVar());
232 if (!succ) {
233 HILOGE("Failed to get entity of RandomAccessFile");
234 NError(EIO).ThrowErr(env);
235 return nullptr;
236 }
237 return ReadExec(env, funcArg, rafEntity);
238 }
239
WriteSync(napi_env env,napi_callback_info info)240 napi_value RandomAccessFileNExporter::WriteSync(napi_env env, napi_callback_info info)
241 {
242 NFuncArg funcArg(env, info);
243 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
244 HILOGE("Number of arguments unmatched");
245 NError(EINVAL).ThrowErr(env);
246 return nullptr;
247 }
248 auto [succEntity, rafEntity] = GetRAFEntity(env, funcArg.GetThisVar());
249 if (!succEntity) {
250 HILOGE("Failed to get entity of RandomAccessFile");
251 NError(EIO).ThrowErr(env);
252 return nullptr;
253 }
254 auto [succ, ignore, buf, len, offset] =
255 CommonFunc::GetWriteArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
256 if (!succ) {
257 HILOGE("Invalid buffer/options");
258 NError(EINVAL).ThrowErr(env);
259 return nullptr;
260 }
261 offset = CalculateOffset(offset, rafEntity->filePointer);
262 int writeLen = DoWriteRAF(env, buf, len, rafEntity->fd.get()->GetFD(), offset);
263 if (writeLen < 0) {
264 NError(writeLen).ThrowErr(env);
265 return nullptr;
266 }
267 rafEntity->filePointer = offset + writeLen;
268 return NVal::CreateInt64(env, writeLen).val_;
269 }
270
271 struct AsyncIORafWriteArg {
272 NRef rafRefWriteArrayBuf;
273 int actLen = 0;
AsyncIORafWriteArgOHOS::FileManagement::ModuleFileIO::AsyncIORafWriteArg274 explicit AsyncIORafWriteArg(NVal refWriteArrayBuf) : rafRefWriteArrayBuf(refWriteArrayBuf) {}
275 ~AsyncIORafWriteArg() = default;
276 };
277
WriteExec(napi_env env,NFuncArg & funcArg,RandomAccessFileEntity * rafEntity)278 static napi_value WriteExec(napi_env env, NFuncArg &funcArg, RandomAccessFileEntity* rafEntity)
279 {
280 bool succ = false;
281 void *buf = nullptr;
282 size_t len = 0;
283 int64_t offset = 0;
284 tie(succ, ignore, buf, len, offset) =
285 CommonFunc::GetWriteArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
286 if (!succ) {
287 HILOGE("Invalid buffer/options");
288 NError(EINVAL).ThrowErr(env);
289 return nullptr;
290 }
291
292 auto arg = CreateSharedPtr<AsyncIORafWriteArg>(NVal(env, funcArg[NARG_POS::FIRST]));
293 if (arg == nullptr) {
294 HILOGE("Failed to request heap memory.");
295 NError(ENOMEM).ThrowErr(env);
296 return nullptr;
297 }
298 offset = CalculateOffset(offset, rafEntity->filePointer);
299 auto cbExec = [env, arg, buf, len, fd = rafEntity->fd.get()->GetFD(), offset, rafEntity]() -> NError {
300 if (!rafEntity || !rafEntity->fd.get()) {
301 HILOGE("RandomAccessFile has been closed in write cbExec possibly");
302 return NError(EIO);
303 }
304 int writeLen = DoWriteRAF(env, buf, len, fd, offset);
305 if (writeLen < 0) {
306 HILOGE("Failed to write file for %{public}d", writeLen);
307 return NError(writeLen);
308 }
309 arg->actLen = writeLen;
310 rafEntity->filePointer = offset + writeLen;
311 return NError(ERRNO_NOERR);
312 };
313
314 auto cbCompl = [arg](napi_env env, NError err) -> NVal {
315 if (err) {
316 return { env, err.GetNapiErr(env) };
317 } else {
318 return { NVal::CreateInt64(env, arg->actLen) };
319 }
320 };
321
322 NVal thisVar(env, funcArg.GetThisVar());
323 if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
324 !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
325 return NAsyncWorkPromise(env, thisVar).Schedule(writeProcedureName, cbExec, cbCompl).val_;
326 } else {
327 int cbIdx = ((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD);
328 NVal cb(env, funcArg[cbIdx]);
329 return NAsyncWorkCallback(env, thisVar, cb).Schedule(writeProcedureName, cbExec, cbCompl).val_;
330 }
331 }
332
Write(napi_env env,napi_callback_info info)333 napi_value RandomAccessFileNExporter::Write(napi_env env, napi_callback_info info)
334 {
335 NFuncArg funcArg(env, info);
336 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
337 HILOGE("Number of arguments unmatched");
338 NError(EINVAL).ThrowErr(env);
339 return nullptr;
340 }
341 auto[succ, rafEntity] = GetRAFEntity(env, funcArg.GetThisVar());
342 if (!succ) {
343 HILOGE("Failed to get entity of RandomAccessFile");
344 NError(EIO).ThrowErr(env);
345 return nullptr;
346 }
347 return WriteExec(env, funcArg, rafEntity);
348 }
349
CloseFd(int fd)350 static NError CloseFd(int fd)
351 {
352 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> close_req = {
353 new uv_fs_t, CommonFunc::fs_req_cleanup };
354 if (!close_req) {
355 HILOGE("Failed to request heap memory.");
356 return NError(ENOMEM);
357 }
358 int ret = uv_fs_close(nullptr, close_req.get(), fd, nullptr);
359 if (ret < 0) {
360 HILOGE("Failed to close file with ret: %{public}d", ret);
361 return NError(ret);
362 }
363 return NError(ERRNO_NOERR);
364 }
365
CloseSync(napi_env env,napi_callback_info info)366 napi_value RandomAccessFileNExporter::CloseSync(napi_env env, napi_callback_info info)
367 {
368 NFuncArg funcArg(env, info);
369 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
370 HILOGE("Number of arguments unmatched");
371 NError(EINVAL).ThrowErr(env);
372 return nullptr;
373 }
374 auto [succEntity, rafEntity] = GetRAFEntity(env, funcArg.GetThisVar());
375 if (!succEntity) {
376 HILOGE("Failed to get entity of RandomAccessFile");
377 NError(EIO).ThrowErr(env);
378 return nullptr;
379 }
380 auto err = CloseFd(rafEntity->fd.get()->GetFD());
381 if (err) {
382 err.ThrowErr(env);
383 return nullptr;
384 }
385 auto fp = NClass::RemoveEntityOfFinal<RandomAccessFileEntity>(env, funcArg.GetThisVar());
386 if (!fp) {
387 HILOGE("Failed to remove entity of RandomAccessFile");
388 NError(EIO).ThrowErr(env);
389 return nullptr;
390 }
391 return NVal::CreateUndefined(env).val_;
392 }
393
Constructor(napi_env env,napi_callback_info info)394 napi_value RandomAccessFileNExporter::Constructor(napi_env env, napi_callback_info info)
395 {
396 NFuncArg funcArg(env, info);
397 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
398 HILOGE("Number of arguments unmatched");
399 NError(EINVAL).ThrowErr(env);
400 return nullptr;
401 }
402
403 auto rafEntity = CreateUniquePtr<RandomAccessFileEntity>();
404 if (rafEntity == nullptr) {
405 HILOGE("Failed to request heap memory.");
406 NError(ENOMEM).ThrowErr(env);
407 return nullptr;
408 }
409 if (!NClass::SetEntityFor<RandomAccessFileEntity>(env, funcArg.GetThisVar(), move(rafEntity))) {
410 HILOGE("INNER BUG. Failed to wrap entity for obj RandomAccessFile");
411 NError(EIO).ThrowErr(env);
412 return nullptr;
413 }
414 return funcArg.GetThisVar();
415 }
416
Export()417 bool RandomAccessFileNExporter::Export()
418 {
419 vector<napi_property_descriptor> props = {
420 NVal::DeclareNapiFunction("read", Read),
421 NVal::DeclareNapiFunction("readSync", ReadSync),
422 NVal::DeclareNapiFunction("write", Write),
423 NVal::DeclareNapiFunction("writeSync", WriteSync),
424 NVal::DeclareNapiFunction("setFilePointer", SetFilePointerSync),
425 NVal::DeclareNapiFunction("close", CloseSync),
426 NVal::DeclareNapiGetter("fd", GetFD),
427 NVal::DeclareNapiGetter("filePointer", GetFPointer),
428 };
429
430 string className = GetClassName();
431 bool succ = false;
432 napi_value classValue = nullptr;
433 tie(succ, classValue) = NClass::DefineClass(exports_.env_, className,
434 RandomAccessFileNExporter::Constructor, move(props));
435 if (!succ) {
436 HILOGE("INNER BUG. Failed to define class");
437 NError(EIO).ThrowErr(exports_.env_);
438 return false;
439 }
440 succ = NClass::SaveClass(exports_.env_, className, classValue);
441 if (!succ) {
442 HILOGE("INNER BUG. Failed to define class");
443 NError(EIO).ThrowErr(exports_.env_);
444 return false;
445 }
446
447 return exports_.AddProp(className, classValue);
448 }
449
GetClassName()450 string RandomAccessFileNExporter::GetClassName()
451 {
452 return RandomAccessFileNExporter::className_;
453 }
454
RandomAccessFileNExporter(napi_env env,napi_value exports)455 RandomAccessFileNExporter::RandomAccessFileNExporter(napi_env env, napi_value exports) : NExporter(env, exports) {}
456
~RandomAccessFileNExporter()457 RandomAccessFileNExporter::~RandomAccessFileNExporter() {}
458 } // namespace ModuleFileIO
459 } // namespace FileManagement
460 } // namespace OHOS