1 /*
2  * Copyright (c) 2021-2022 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 <memory>
22 #include <sstream>
23 #include <string>
24 #include "flush.h"
25 #include "securec.h"
26 
27 #include "../../common/log.h"
28 #include "../../common/napi/n_async/n_async_work_callback.h"
29 #include "../../common/napi/n_async/n_async_work_promise.h"
30 #include "../../common/napi/n_class.h"
31 #include "../../common/napi/n_func_arg.h"
32 #include "../../common/uni_error.h"
33 #include "../common_func.h"
34 #include "stream_entity.h"
35 
36 namespace OHOS {
37 namespace DistributedFS {
38 namespace ModuleFileIO {
39 using namespace std;
40 
ReadSync(napi_env env,napi_callback_info info)41 napi_value StreamNExporter::ReadSync(napi_env env, napi_callback_info info)
42 {
43     NFuncArg funcArg(env, info);
44     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
45         UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
46         return nullptr;
47     }
48 
49     /* To get entity */
50     bool succ = false;
51     FILE *filp = nullptr;
52     auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
53     if (!streamEntity || !streamEntity->fp) {
54         UniError(EBADF).ThrowErr(env, "Stream may have been closed");
55         return nullptr;
56     } else {
57         filp = streamEntity->fp.get();
58     }
59 
60     void *buf = nullptr;
61     size_t len = 0;
62     int64_t pos = -1;
63     tie(succ, buf, len, pos, ignore) =
64         CommonFunc::GetReadArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
65     if (!succ) {
66         return nullptr;
67     }
68 
69     if (pos >= 0 && (fseek(filp, static_cast<long>(pos), SEEK_SET) == -1)) {
70         UniError(errno).ThrowErr(env);
71         return nullptr;
72     }
73 
74     size_t actLen = fread(buf, 1, len, filp);
75     if ((actLen != len && !feof(filp)) || ferror(filp)) {
76         UniError(errno).ThrowErr(env);
77     }
78 
79     return NVal::CreateInt64(env, actLen).val_;
80 }
81 
CloseSync(napi_env env,napi_callback_info info)82 napi_value StreamNExporter::CloseSync(napi_env env, napi_callback_info info)
83 {
84     NFuncArg funcArg(env, info);
85     if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
86         UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
87         return nullptr;
88     }
89 
90     auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
91     if (!streamEntity || !streamEntity->fp) {
92         UniError(EINVAL).ThrowErr(env, "Stream may have been closed yet");
93         return nullptr;
94     }
95     streamEntity->fp.reset();
96     (void)NClass::RemoveEntityOfFinal<StreamEntity>(env, funcArg.GetThisVar());
97 
98     return NVal::CreateUndefined(env).val_;
99 }
100 
WriteSync(napi_env env,napi_callback_info info)101 napi_value StreamNExporter::WriteSync(napi_env env, napi_callback_info info)
102 {
103     NFuncArg funcArg(env, info);
104     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
105         UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
106         return nullptr;
107     }
108 
109     bool succ = false;
110     FILE *filp = nullptr;
111     auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
112     if (!streamEntity || !streamEntity->fp) {
113         UniError(EBADF).ThrowErr(env, "Stream may has been closed");
114         return nullptr;
115     } else {
116         filp = streamEntity->fp.get();
117     }
118 
119     void *buf = nullptr;
120     size_t len = 0;
121     int64_t position = -1;
122     unique_ptr<char[]> bufGuard = nullptr;
123     tie(succ, bufGuard, buf, len, position) =
124         CommonFunc::GetWriteArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
125     if (!succ) {
126         return nullptr;
127     }
128     if (position >= 0 && (fseek(filp, static_cast<long>(position), SEEK_SET) == -1)) {
129         UniError(errno).ThrowErr(env);
130         return nullptr;
131     }
132 
133     size_t writeLen = fwrite(buf, 1, len, filp);
134     if ((writeLen == 0) && (writeLen != len)) {
135         UniError(errno).ThrowErr(env);
136         return nullptr;
137     }
138 
139     return NVal::CreateInt64(env, writeLen).val_;
140 }
141 
142 struct AsyncWriteArg {
143     NRef refWriteArrayBuf;
144     unique_ptr<char[]> guardWriteStr = nullptr;
145     size_t actLen { 0 };
146 
AsyncWriteArgOHOS::DistributedFS::ModuleFileIO::AsyncWriteArg147     explicit AsyncWriteArg(NVal refWriteArrayBuf) : refWriteArrayBuf(refWriteArrayBuf) {}
AsyncWriteArgOHOS::DistributedFS::ModuleFileIO::AsyncWriteArg148     explicit AsyncWriteArg(unique_ptr<char[]> &&guardWriteStr) : guardWriteStr(move(guardWriteStr)) {}
149     ~AsyncWriteArg() = default;
150 };
151 
Write(napi_env env,napi_callback_info info)152 napi_value StreamNExporter::Write(napi_env env, napi_callback_info info)
153 {
154     NFuncArg funcArg(env, info);
155     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
156         UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
157         return nullptr;
158     }
159 
160     bool succ = false;
161     FILE *filp = nullptr;
162     int64_t position = -1;
163     auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
164     if (!streamEntity || !streamEntity->fp) {
165         UniError(EBADF).ThrowErr(env, "Stream may has been closed");
166         return nullptr;
167     }
168     filp = streamEntity->fp.get();
169 
170     unique_ptr<char[]> bufGuard = nullptr;
171     void *buf = nullptr;
172     size_t len = 0;
173     tie(succ, bufGuard, buf, len, position) =
174         CommonFunc::GetWriteArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
175     if (!succ) {
176         return nullptr;
177     }
178 
179     auto arg = make_shared<AsyncWriteArg>(move(bufGuard));
180     auto cbExec = [arg, buf, len, filp, position](napi_env env) -> UniError {
181         if (position >= 0 && (fseek(filp, static_cast<long>(position), SEEK_SET) == -1)) {
182             UniError(errno).ThrowErr(env);
183             return UniError(errno);
184         }
185         arg->actLen = fwrite(buf, 1, len, filp);
186         if ((arg->actLen == 0) && (arg->actLen != len)) {
187             return UniError(errno);
188         }
189         return UniError(ERRNO_NOERR);
190     };
191 
192     auto cbCompl = [arg](napi_env env, UniError err) -> NVal {
193         if (err) {
194             return { env, err.GetNapiErr(env) };
195         }
196         return { NVal::CreateInt64(env, arg->actLen) };
197     };
198 
199     NVal thisVar(env, funcArg.GetThisVar());
200     if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
201         !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
202         return NAsyncWorkPromise(env, thisVar).Schedule("FileIOStreamWrite", cbExec, cbCompl).val_;
203     } else {
204         int cbIdx = ((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD);
205         NVal cb(env, funcArg[cbIdx]);
206         return NAsyncWorkCallback(env, thisVar, cb).Schedule("FileIOStreamWrite", cbExec, cbCompl).val_;
207     }
208 
209     return NVal::CreateUndefined(env).val_;
210 }
211 
212 struct AsyncReadArg {
213     size_t lenRead { 0 };
214     NRef refReadBuf;
215     int64_t offset { 0 };
216 
AsyncReadArgOHOS::DistributedFS::ModuleFileIO::AsyncReadArg217     explicit AsyncReadArg(NVal jsReadBuf) : refReadBuf(jsReadBuf) {}
218     ~AsyncReadArg() = default;
219 };
220 
Read(napi_env env,napi_callback_info info)221 napi_value StreamNExporter::Read(napi_env env, napi_callback_info info)
222 {
223     NFuncArg funcArg(env, info);
224     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
225         UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
226         return nullptr;
227     }
228 
229     /* To get entity */
230     auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
231     if (!streamEntity || !streamEntity->fp) {
232         UniError(EBADF).ThrowErr(env, "Stream may have been closed");
233         return nullptr;
234     }
235     FILE *filp = nullptr;
236     filp = streamEntity->fp.get();
237 
238     bool succ = false;
239     void *buf = nullptr;
240     size_t len = 0;
241     int64_t position = -1;
242     int64_t offset = 0;
243     tie(succ, buf, len, position, offset) =
244         CommonFunc::GetReadArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
245     if (!succ) {
246         return nullptr;
247     }
248 
249     auto arg = make_shared<AsyncReadArg>(NVal(env, funcArg[NARG_POS::FIRST]));
250     auto cbExec = [arg, buf, position, filp, len, offset](napi_env env) -> UniError {
251         if (position >= 0 && (fseek(filp, static_cast<long>(position), SEEK_SET) == -1)) {
252             UniError(errno).ThrowErr(env);
253             return UniError(errno);
254         }
255         size_t actLen = fread(buf, 1, len, filp);
256         if ((actLen != len && !feof(filp)) || ferror(filp)) {
257             return UniError(errno);
258         } else {
259             arg->lenRead = actLen;
260             arg->offset = offset;
261             return UniError(ERRNO_NOERR);
262         }
263     };
264 
265     auto cbCompl = [arg](napi_env env, UniError err) -> NVal {
266         if (err) {
267             return { env, err.GetNapiErr(env) };
268         }
269         NVal obj = NVal::CreateObject(env);
270         obj.AddProp({
271             NVal::DeclareNapiProperty("bytesRead", NVal::CreateInt64(env, arg->lenRead).val_),
272             NVal::DeclareNapiProperty("buffer", arg->refReadBuf.Deref(env).val_),
273             NVal::DeclareNapiProperty("offset", NVal::CreateInt64(env, arg->offset).val_)
274             });
275         return { obj };
276     };
277 
278     NVal thisVar(env, funcArg.GetThisVar());
279     if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
280         !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
281         return NAsyncWorkPromise(env, thisVar).Schedule("FileIOStreamRead", cbExec, cbCompl).val_;
282     } else {
283         NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD)]);
284         return NAsyncWorkCallback(env, thisVar, cb).Schedule("FileIOStreamRead", cbExec, cbCompl).val_;
285     }
286 
287     return NVal::CreateUndefined(env).val_;
288 }
289 
Close(napi_env env,napi_callback_info info)290 napi_value StreamNExporter::Close(napi_env env, napi_callback_info info)
291 {
292     NFuncArg funcArg(env, info);
293     if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
294         UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
295         return nullptr;
296     }
297 
298     auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
299     if (!streamEntity || !streamEntity->fp) {
300         UniError(EBADF).ThrowErr(env, "Stream may has been closed");
301         return nullptr;
302     }
303 
304     auto fp = NClass::RemoveEntityOfFinal<StreamEntity>(env, funcArg.GetThisVar());
305     if (!fp) {
306         UniError(EINVAL).ThrowErr(env);
307         return nullptr;
308     }
309 
310     auto cbExec = [](napi_env env) -> UniError {
311         return UniError(ERRNO_NOERR);
312     };
313 
314     auto cbCompl = [](napi_env env, UniError err) -> NVal {
315         if (err) {
316             return { env, err.GetNapiErr(env) };
317         } else {
318             return NVal::CreateUndefined(env);
319         }
320     };
321 
322     string procedureName = "FileIOStreamClose";
323     NVal thisVar(env, funcArg.GetThisVar());
324     if (funcArg.GetArgc() == NARG_CNT::ZERO) {
325         return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbCompl).val_;
326     } else {
327         NVal cb(env, funcArg[NARG_POS::FIRST]);
328         return NAsyncWorkCallback(env, thisVar, cb).Schedule(procedureName, cbExec, cbCompl).val_;
329     }
330 
331     return NVal::CreateUndefined(env).val_;
332 }
333 
Constructor(napi_env env,napi_callback_info info)334 napi_value StreamNExporter::Constructor(napi_env env, napi_callback_info info)
335 {
336     NFuncArg funcArg(env, info);
337     if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
338         UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
339         return nullptr;
340     }
341 
342     unique_ptr<StreamEntity> streamEntity = make_unique<StreamEntity>();
343     if (!NClass::SetEntityFor<StreamEntity>(env, funcArg.GetThisVar(), move(streamEntity))) {
344         UniError(EIO).ThrowErr(env, "INNER BUG. Failed to wrap entity for obj stream");
345         return nullptr;
346     }
347     return funcArg.GetThisVar();
348 }
349 
Export()350 bool StreamNExporter::Export()
351 {
352     vector<napi_property_descriptor> props = {
353         NVal::DeclareNapiFunction("writeSync", WriteSync),
354         NVal::DeclareNapiFunction("flush", Flush::Async),
355         NVal::DeclareNapiFunction("flushSync", Flush::Sync),
356         NVal::DeclareNapiFunction("readSync", ReadSync),
357         NVal::DeclareNapiFunction("closeSync", CloseSync),
358         NVal::DeclareNapiFunction("write", Write),
359         NVal::DeclareNapiFunction("read", Read),
360         NVal::DeclareNapiFunction("close", Close),
361     };
362 
363     string className = GetClassName();
364     bool succ = false;
365     napi_value cls = nullptr;
366     tie(succ, cls) = NClass::DefineClass(exports_.env_, className, StreamNExporter::Constructor, move(props));
367     if (!succ) {
368         UniError(EIO).ThrowErr(exports_.env_, "INNER BUG. Failed to define class");
369         return false;
370     }
371     succ = NClass::SaveClass(exports_.env_, className, cls);
372     if (!succ) {
373         UniError(EIO).ThrowErr(exports_.env_, "INNER BUG. Failed to save class");
374         return false;
375     }
376 
377     return exports_.AddProp(className, cls);
378 }
379 
GetClassName()380 string StreamNExporter::GetClassName()
381 {
382     return StreamNExporter::className_;
383 }
384 
StreamNExporter(napi_env env,napi_value exports)385 StreamNExporter::StreamNExporter(napi_env env, napi_value exports) : NExporter(env, exports) {}
386 
~StreamNExporter()387 StreamNExporter::~StreamNExporter() {}
388 } // namespace ModuleFileIO
389 } // namespace DistributedFS
390 } // namespace OHOS
391