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