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