• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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