1 /*
2 * Copyright (c) 2023-2024 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 "stream_n_exporter.h"
17
18 #include <cstdio>
19 #include <cstdlib>
20 #include <cstring>
21 #include <cinttypes>
22 #include <memory>
23 #include <securec.h>
24 #include <sstream>
25 #include <string>
26
27 #include "common_func.h"
28 #include "file_utils.h"
29 #include "filemgmt_libhilog.h"
30 #include "rust_file.h"
31 #include "stream_entity.h"
32
33 namespace OHOS {
34 namespace FileManagement {
35 namespace ModuleFileIO {
36 using namespace std;
37 using namespace OHOS::FileManagement::LibN;
38 std::mutex StreamNExporter::mutex;
GetFilePtr(StreamEntity * streamEntity)39 std::shared_ptr<FILE> StreamNExporter::GetFilePtr(StreamEntity *streamEntity)
40 {
41 std::lock_guard<std::mutex> lock(mutex);
42 if (streamEntity) {
43 return streamEntity->fp;
44 }
45 return nullptr;
46 }
47
GetEntityOf(napi_env env,NFuncArg & funcArg)48 StreamEntity* StreamNExporter::GetEntityOf(napi_env env, NFuncArg &funcArg)
49 {
50 NClass &nClass = NClass::GetInstance();
51 lock_guard<std::mutex>(nClass.wrapLock);
52 if (nClass.wrapReleased) {
53 return nullptr;
54 }
55 return NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
56 }
57
FlushSync(napi_env env,napi_callback_info info)58 napi_value StreamNExporter::FlushSync(napi_env env, napi_callback_info info)
59 {
60 NFuncArg funcArg(env, info);
61 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
62 HILOGE("Number of arguments unmatched");
63 NError(EINVAL).ThrowErr(env);
64 return nullptr;
65 }
66
67 auto streamEntity = GetEntityOf(env, funcArg);
68 if (streamEntity == nullptr) {
69 NError(UNKROWN_ERR).ThrowErr(env);
70 return nullptr;
71 }
72 auto fp = GetFilePtr(streamEntity);
73 if (fp == nullptr) {
74 HILOGE("Failed to get entity of Stream");
75 NError(EIO).ThrowErr(env);
76 return nullptr;
77 }
78
79 int ret = fflush(fp.get());
80 if (ret < 0) {
81 HILOGE("Failed to fflush file in the stream, ret: %{public}d", ret);
82 NError(errno).ThrowErr(env);
83 return nullptr;
84 }
85 return NVal::CreateUndefined(env).val_;
86 }
87
Flush(napi_env env,napi_callback_info info)88 napi_value StreamNExporter::Flush(napi_env env, napi_callback_info info)
89 {
90 NFuncArg funcArg(env, info);
91 if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
92 HILOGE("Number of arguments unmatched");
93 NError(EINVAL).ThrowErr(env);
94 return nullptr;
95 }
96
97 auto streamEntity = GetEntityOf(env, funcArg);
98 if (streamEntity == nullptr) {
99 NError(UNKROWN_ERR).ThrowErr(env);
100 return nullptr;
101 }
102 auto fp = GetFilePtr(streamEntity);
103 if (fp == nullptr) {
104 HILOGE("Failed to get entity of Stream");
105 NError(EIO).ThrowErr(env);
106 return nullptr;
107 }
108
109 auto cbExec = [fp]() -> NError {
110 if (!fp) {
111 HILOGE("Stream has been closed in flush cbExec possibly");
112 return NError(EIO);
113 }
114 int ret = fflush(fp.get());
115 if (ret < 0) {
116 HILOGE("Failed to fflush file in the stream");
117 return NError(errno);
118 } else {
119 return NError(ERRNO_NOERR);
120 }
121 };
122 auto cbCompl = [](napi_env env, NError err) -> NVal {
123 if (err) {
124 return { env, err.GetNapiErr(env) };
125 }
126 return { NVal::CreateUndefined(env) };
127 };
128
129 NVal thisVar(env, funcArg.GetThisVar());
130 if (funcArg.GetArgc() == NARG_CNT::ZERO) {
131 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_STREAM_FLUSH_NAME, cbExec, cbCompl).val_;
132 } else {
133 NVal cb(env, funcArg[NARG_POS::FIRST]);
134 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_STREAM_FLUSH_NAME, cbExec, cbCompl).val_;
135 }
136 }
137
ReadSync(napi_env env,napi_callback_info cbInfo)138 napi_value StreamNExporter::ReadSync(napi_env env, napi_callback_info cbInfo)
139 {
140 NFuncArg funcArg(env, cbInfo);
141 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
142 HILOGE("Number of arguments unmatched");
143 NError(EINVAL).ThrowErr(env);
144 return nullptr;
145 }
146
147 auto streamEntity = GetEntityOf(env, funcArg);
148 if (streamEntity == nullptr) {
149 NError(UNKROWN_ERR).ThrowErr(env);
150 return nullptr;
151 }
152 auto fp = GetFilePtr(streamEntity);
153 if (fp == nullptr) {
154 HILOGE("Failed to get entity of Stream");
155 NError(EIO).ThrowErr(env);
156 return nullptr;
157 }
158
159 auto [succ, buf, len, offset] =
160 CommonFunc::GetReadArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
161 if (!succ) {
162 HILOGE("Failed to resolve buf and options");
163 return nullptr;
164 }
165
166 if (offset >= 0) {
167 int ret = fseek(fp.get(), static_cast<long>(offset), SEEK_SET);
168 if (ret < 0) {
169 HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret);
170 NError(errno).ThrowErr(env);
171 return nullptr;
172 }
173 }
174
175 size_t actLen = fread(buf, 1, len, fp.get());
176 if ((actLen != static_cast<size_t>(len) && !feof(fp.get())) || ferror(fp.get())) {
177 HILOGE("Invalid buffer size and pointer, actlen: %{public}zu", actLen);
178 NError(EIO).ThrowErr(env);
179 return nullptr;
180 }
181
182 return NVal::CreateInt64(env, actLen).val_;
183 }
184
CloseSync(napi_env env,napi_callback_info cbInfo)185 napi_value StreamNExporter::CloseSync(napi_env env, napi_callback_info cbInfo)
186 {
187 NFuncArg funcArg(env, cbInfo);
188 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
189 HILOGE("Number of arguments unmatched");
190 NError(EINVAL).ThrowErr(env);
191 return nullptr;
192 }
193 auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
194 if (!streamEntity) {
195 HILOGE("Failed to get entity of Stream, may closed twice");
196 NError(EIO).ThrowErr(env);
197 return nullptr;
198 }
199 {
200 std::lock_guard<std::mutex> lock(mutex);
201 (void)NClass::RemoveEntityOfFinal<StreamEntity>(env, funcArg.GetThisVar());
202 }
203 return NVal::CreateUndefined(env).val_;
204 }
205
WriteSync(napi_env env,napi_callback_info cbInfo)206 napi_value StreamNExporter::WriteSync(napi_env env, napi_callback_info cbInfo)
207 {
208 NFuncArg funcArg(env, cbInfo);
209 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
210 HILOGE("Number of arguments unmatched");
211 NError(EINVAL).ThrowErr(env);
212 return nullptr;
213 }
214
215 auto streamEntity = GetEntityOf(env, funcArg);
216 if (streamEntity == nullptr) {
217 NError(UNKROWN_ERR).ThrowErr(env);
218 return nullptr;
219 }
220 auto fp = GetFilePtr(streamEntity);
221 if (fp == nullptr) {
222 HILOGE("Failed to get entity of Stream");
223 NError(EIO).ThrowErr(env);
224 return nullptr;
225 }
226
227 auto [succ, bufGuard, buf, len, offset] =
228 CommonFunc::GetWriteArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
229 if (!succ) {
230 HILOGE("Failed to resolve buf and options");
231 return nullptr;
232 }
233 if (offset >= 0) {
234 int ret = fseek(fp.get(), static_cast<long>(offset), SEEK_SET);
235 if (ret < 0) {
236 HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret);
237 NError(errno).ThrowErr(env);
238 return nullptr;
239 }
240 }
241
242 size_t writeLen = fwrite(buf, 1, len, fp.get());
243 if ((writeLen == 0) && (writeLen != len)) {
244 HILOGE("Failed to fwrite stream");
245 NError(EIO).ThrowErr(env);
246 return nullptr;
247 }
248
249 return NVal::CreateInt64(env, static_cast<int64_t>(writeLen)).val_;
250 }
251
WriteExec(napi_env env,NFuncArg & funcArg,shared_ptr<FILE> fp)252 static napi_value WriteExec(napi_env env, NFuncArg &funcArg, shared_ptr<FILE> fp)
253 {
254 auto [succ, bufGuard, buf, len, offset] =
255 CommonFunc::GetWriteArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
256 if (!succ) {
257 HILOGE("Failed to resolve buf and options");
258 return nullptr;
259 }
260
261 auto arg = CreateSharedPtr<AsyncWriteArg>(move(bufGuard));
262 if (arg == nullptr) {
263 HILOGE("Failed to request heap memory.");
264 NError(ENOMEM).ThrowErr(env);
265 return nullptr;
266 }
267 auto cbExec = [arg, buf = buf, len = len, fp, offset = offset]() -> NError {
268 if (!fp.get()) {
269 HILOGE("Stream has been closed in write cbExec possibly");
270 return NError(EIO);
271 }
272 if (offset >= 0) {
273 int ret = fseek(fp.get(), static_cast<long>(offset), SEEK_SET);
274 if (ret < 0) {
275 HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret);
276 return NError(errno);
277 }
278 }
279 arg->actLen = fwrite(buf, 1, len, fp.get());
280 if ((arg->actLen == 0) && (arg->actLen != len)) {
281 HILOGE("Failed to fwrite stream");
282 return NError(EIO);
283 }
284 return NError(ERRNO_NOERR);
285 };
286
287 auto cbCompl = [arg](napi_env env, NError err) -> NVal {
288 if (err) {
289 return { env, err.GetNapiErr(env) };
290 }
291 return { NVal::CreateInt64(env, static_cast<int64_t>(arg->actLen)) };
292 };
293 NVal thisVar(env, funcArg.GetThisVar());
294 if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
295 !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
296 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_STREAM_WRITE_NAME, cbExec, cbCompl).val_;
297 } else {
298 int cbIdx = ((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD);
299 NVal cb(env, funcArg[cbIdx]);
300 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_STREAM_WRITE_NAME, cbExec, cbCompl).val_;
301 }
302 }
303
Write(napi_env env,napi_callback_info cbInfo)304 napi_value StreamNExporter::Write(napi_env env, napi_callback_info cbInfo)
305 {
306 NFuncArg funcArg(env, cbInfo);
307 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
308 HILOGE("Number of arguments unmatched");
309 NError(EINVAL).ThrowErr(env);
310 return nullptr;
311 }
312
313 auto streamEntity = GetEntityOf(env, funcArg);
314 if (streamEntity == nullptr) {
315 NError(UNKROWN_ERR).ThrowErr(env);
316 return nullptr;
317 }
318 auto fp = GetFilePtr(streamEntity);
319 if (fp == nullptr) {
320 HILOGE("Failed to get entity of Stream");
321 NError(EIO).ThrowErr(env);
322 return nullptr;
323 }
324 return WriteExec(env, funcArg, fp);
325 }
326
ReadExec(napi_env env,NFuncArg & funcArg,shared_ptr<FILE> fp)327 static napi_value ReadExec(napi_env env, NFuncArg &funcArg, shared_ptr<FILE> fp)
328 {
329 auto [succ, buf, len, offset] =
330 CommonFunc::GetReadArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
331 if (!succ) {
332 HILOGE("Failed to resolve buf and options");
333 NError(EINVAL).ThrowErr(env);
334 return nullptr;
335 }
336
337 auto arg = CreateSharedPtr<AsyncReadArg>(NVal(env, funcArg[NARG_POS::FIRST]));
338 if (arg == nullptr) {
339 HILOGE("Failed to request heap memory.");
340 NError(ENOMEM).ThrowErr(env);
341 return nullptr;
342 }
343 auto cbExec = [arg, buf = buf, len = len, fp, offset = offset]() -> NError {
344 if (!fp.get()) {
345 HILOGE("Stream has been closed in read cbExec possibly");
346 return NError(EIO);
347 }
348 if (offset >= 0) {
349 if (fseek(fp.get(), static_cast<long>(offset), SEEK_SET) < 0) {
350 HILOGE("Failed to set the offset location of the file stream pointer");
351 return NError(errno);
352 }
353 }
354 size_t actLen = fread(buf, 1, len, fp.get());
355 if ((actLen != static_cast<size_t>(len) && !feof(fp.get())) || ferror(fp.get())) {
356 HILOGE("Invalid buffer size and pointer, actlen: %{public}zu", actLen);
357 return NError(EIO);
358 } else {
359 arg->lenRead = actLen;
360 return NError(ERRNO_NOERR);
361 }
362 };
363
364 auto cbCompl = [arg](napi_env env, NError err) -> NVal {
365 if (err) {
366 return { env, err.GetNapiErr(env) };
367 }
368 return { NVal::CreateInt64(env, arg->lenRead) };
369 };
370
371 NVal thisVar(env, funcArg.GetThisVar());
372 if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
373 !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
374 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_STREAM_READ_NAME, cbExec, cbCompl).val_;
375 } else {
376 int cbIdx = ((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD);
377 NVal cb(env, funcArg[cbIdx]);
378 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_STREAM_READ_NAME, cbExec, cbCompl).val_;
379 }
380 }
381
Read(napi_env env,napi_callback_info cbInfo)382 napi_value StreamNExporter::Read(napi_env env, napi_callback_info cbInfo)
383 {
384 NFuncArg funcArg(env, cbInfo);
385 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
386 HILOGE("Number of arguments unmatched");
387 NError(EINVAL).ThrowErr(env);
388 return nullptr;
389 }
390
391 auto streamEntity = GetEntityOf(env, funcArg);
392 if (streamEntity == nullptr) {
393 NError(UNKROWN_ERR).ThrowErr(env);
394 return nullptr;
395 }
396 auto fp = GetFilePtr(streamEntity);
397 if (fp == nullptr) {
398 HILOGE("Failed to get entity of Stream");
399 NError(EIO).ThrowErr(env);
400 return nullptr;
401 }
402 return ReadExec(env, funcArg, fp);
403 }
404
Close(napi_env env,napi_callback_info cbInfo)405 napi_value StreamNExporter::Close(napi_env env, napi_callback_info cbInfo)
406 {
407 NFuncArg funcArg(env, cbInfo);
408 if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
409 HILOGE("Number of arguments unmatched");
410 NError(EINVAL).ThrowErr(env);
411 return nullptr;
412 }
413 auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
414 if (!streamEntity) {
415 HILOGE("Failed to get entity of Stream, may closed twice");
416 NError(EIO).ThrowErr(env);
417 return nullptr;
418 }
419 StreamEntity* ret = nullptr;
420 {
421 std::lock_guard<std::mutex> lock(mutex);
422 ret = NClass::RemoveEntityOfFinal<StreamEntity>(env, funcArg.GetThisVar());
423 }
424 if (!ret) {
425 NError(EINVAL).ThrowErr(env);
426 return nullptr;
427 }
428 auto cbExec = []() -> NError {
429 return NError(ERRNO_NOERR);
430 };
431
432 auto cbCompl = [](napi_env env, NError err) -> NVal {
433 if (err) {
434 return { env, err.GetNapiErr(env) };
435 } else {
436 return NVal::CreateUndefined(env);
437 }
438 };
439
440 NVal thisVar(env, funcArg.GetThisVar());
441 if (funcArg.GetArgc() == NARG_CNT::ZERO) {
442 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_STREAM_CLOSE_NAME, cbExec, cbCompl).val_;
443 } else {
444 NVal cb(env, funcArg[NARG_POS::FIRST]);
445 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_STREAM_CLOSE_NAME, cbExec, cbCompl).val_;
446 }
447 }
448
Seek(napi_env env,napi_callback_info cbInfo)449 napi_value StreamNExporter::Seek(napi_env env, napi_callback_info cbInfo)
450 {
451 NFuncArg funcArg(env, cbInfo);
452 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
453 HILOGE("Number of arguments unmatched");
454 NError(EINVAL).ThrowErr(env);
455 return nullptr;
456 }
457
458 auto streamEntity = GetEntityOf(env, funcArg);
459 if (streamEntity == nullptr) {
460 NError(UNKROWN_ERR).ThrowErr(env);
461 return nullptr;
462 }
463 auto fp = GetFilePtr(streamEntity);
464 if (fp == nullptr) {
465 HILOGE("Failed to get entity of Stream");
466 NError(EIO).ThrowErr(env);
467 return nullptr;
468 }
469 auto [succGetOffset, offset] = NVal(env, funcArg[NARG_POS::FIRST]).ToInt64();
470 if (!succGetOffset) {
471 HILOGE("Invalid offset from JS first argument");
472 NError(EINVAL).ThrowErr(env);
473 return nullptr;
474 }
475
476 int whence = SEEK_SET;
477 if (funcArg.GetArgc() == NARG_CNT::TWO) {
478 auto [succGetWhence, pos] = NVal(env, funcArg[NARG_POS::SECOND]).ToInt32(SEEK_SET);
479 if (!succGetWhence || pos < SEEK_SET || pos > SEEK_END) {
480 HILOGE("Invalid whence from JS third argument");
481 NError(EINVAL).ThrowErr(env);
482 return nullptr;
483 }
484 whence = pos;
485 }
486
487 if (offset >= 0) {
488 int ret = fseek(fp.get(), static_cast<long>(offset), whence);
489 if (ret < 0) {
490 HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret);
491 NError(errno).ThrowErr(env);
492 return nullptr;
493 }
494 }
495 int64_t res = ftell(fp.get());
496 if (res < 0) {
497 HILOGE("Failed to tell, error:%{public}d", errno);
498 NError(errno).ThrowErr(env);
499 return nullptr;
500 }
501
502 return NVal::CreateInt64(env, res).val_;
503 }
504
Constructor(napi_env env,napi_callback_info cbInfo)505 napi_value StreamNExporter::Constructor(napi_env env, napi_callback_info cbInfo)
506 {
507 NFuncArg funcArg(env, cbInfo);
508 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
509 HILOGE("Number of arguments unmatched");
510 NError(EINVAL).ThrowErr(env);
511 return nullptr;
512 }
513
514 auto streamEntity = CreateUniquePtr<StreamEntity>();
515 if (streamEntity == nullptr) {
516 HILOGE("Failed to request heap memory.");
517 NError(ENOMEM).ThrowErr(env);
518 return nullptr;
519 }
520 if (!NClass::SetEntityFor<StreamEntity>(env, funcArg.GetThisVar(), move(streamEntity))) {
521 HILOGE("INNER BUG. Failed to wrap entity for obj stream");
522 NError(EIO).ThrowErr(env);
523 return nullptr;
524 }
525 return funcArg.GetThisVar();
526 }
527
Export()528 bool StreamNExporter::Export()
529 {
530 vector<napi_property_descriptor> props = {
531 NVal::DeclareNapiFunction("writeSync", WriteSync),
532 NVal::DeclareNapiFunction("flush", Flush),
533 NVal::DeclareNapiFunction("flushSync", FlushSync),
534 NVal::DeclareNapiFunction("readSync", ReadSync),
535 NVal::DeclareNapiFunction("closeSync", CloseSync),
536 NVal::DeclareNapiFunction("write", Write),
537 NVal::DeclareNapiFunction("read", Read),
538 NVal::DeclareNapiFunction("close", Close),
539 NVal::DeclareNapiFunction("seek", Seek),
540 };
541
542 string className = GetClassName();
543 bool succ = false;
544 napi_value cls = nullptr;
545 tie(succ, cls) = NClass::DefineClass(exports_.env_, className, StreamNExporter::Constructor, move(props));
546 if (!succ) {
547 HILOGE("INNER BUG. Failed to define class");
548 NError(EIO).ThrowErr(exports_.env_);
549 return false;
550 }
551 succ = NClass::SaveClass(exports_.env_, className, cls);
552 if (!succ) {
553 HILOGE("INNER BUG. Failed to save class");
554 NError(EIO).ThrowErr(exports_.env_);
555 return false;
556 }
557
558 return exports_.AddProp(className, cls);
559 }
560
GetClassName()561 string StreamNExporter::GetClassName()
562 {
563 return StreamNExporter::className_;
564 }
565
StreamNExporter(napi_env env,napi_value exports)566 StreamNExporter::StreamNExporter(napi_env env, napi_value exports) : NExporter(env, exports) {}
567
~StreamNExporter()568 StreamNExporter::~StreamNExporter() {}
569 } // namespace ModuleFileIO
570 } // namespace FileManagement
571 } // namespace OHOS
572