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