1 /*
2 * Copyright (c) 2023 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 "filemgmt_libhilog.h"
29 #include "flush.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
ReadSync(napi_env env,napi_callback_info cbInfo)38 napi_value StreamNExporter::ReadSync(napi_env env, napi_callback_info cbInfo)
39 {
40 NFuncArg funcArg(env, cbInfo);
41 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
42 HILOGE("Number of arguments unmatched");
43 NError(EINVAL).ThrowErr(env);
44 return nullptr;
45 }
46
47 auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
48 if (!streamEntity || !streamEntity->fp) {
49 HILOGE("Stream may have been closed");
50 NError(EIO).ThrowErr(env);
51 return nullptr;
52 }
53 FILE *filp = nullptr;
54 filp = streamEntity->fp.get();
55
56 auto [succ, buf, len, hasOffset, offset] =
57 CommonFunc::GetReadArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
58 if (!succ) {
59 HILOGE("Failed to resolve buf and options");
60 return nullptr;
61 }
62
63 int ret = fseek(filp, offset, SEEK_SET);
64 if (hasOffset && (ret < 0)) {
65 HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret);
66 NError(errno).ThrowErr(env);
67 return nullptr;
68 }
69
70 size_t actLen = fread(buf, 1, len, filp);
71 if ((actLen != static_cast<size_t>(len) && !feof(filp)) || ferror(filp)) {
72 HILOGE("Invalid buffer size and pointer, actlen: %{public}zu", actLen);
73 NError(EIO).ThrowErr(env);
74 return nullptr;
75 }
76
77 return NVal::CreateInt64(env, actLen).val_;
78 }
79
CloseSync(napi_env env,napi_callback_info cbInfo)80 napi_value StreamNExporter::CloseSync(napi_env env, napi_callback_info cbInfo)
81 {
82 NFuncArg funcArg(env, cbInfo);
83 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
84 HILOGE("Number of arguments unmatched");
85 NError(EINVAL).ThrowErr(env);
86 return nullptr;
87 }
88
89 auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
90 if (!streamEntity || !streamEntity->fp) {
91 HILOGE("Stream may have been closed yet");
92 NError(EIO).ThrowErr(env);
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 cbInfo)101 napi_value StreamNExporter::WriteSync(napi_env env, napi_callback_info cbInfo)
102 {
103 NFuncArg funcArg(env, cbInfo);
104 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
105 HILOGE("Number of arguments unmatched");
106 NError(EINVAL).ThrowErr(env);
107 return nullptr;
108 }
109
110 auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
111 if (!streamEntity || !streamEntity->fp) {
112 HILOGE("Stream may has been closed");
113 NError(EIO).ThrowErr(env);
114 return nullptr;
115 }
116 FILE *filp = nullptr;
117 filp = streamEntity->fp.get();
118
119 auto [succ, bufGuard, buf, len, hasOffset, offset] =
120 CommonFunc::GetWriteArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
121 if (!succ) {
122 HILOGE("Failed to resolve buf and options");
123 return nullptr;
124 }
125 int ret = fseek(filp, static_cast<long>(offset), SEEK_SET);
126 if (hasOffset && (ret < 0)) {
127 HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret);
128 NError(errno).ThrowErr(env);
129 return nullptr;
130 }
131
132 size_t writeLen = fwrite(buf, 1, len, filp);
133 if ((writeLen == 0) && (writeLen != len)) {
134 HILOGE("Failed to fwrite stream");
135 NError(EIO).ThrowErr(env);
136 return nullptr;
137 }
138
139 return NVal::CreateInt64(env, static_cast<int64_t>(writeLen)).val_;
140 }
141
HasOption(napi_env env,napi_value optionFromJsArg)142 static bool HasOption(napi_env env, napi_value optionFromJsArg)
143 {
144 NVal op = NVal(env, optionFromJsArg);
145 if (op.HasProp("offset") || op.HasProp("length") || op.HasProp("encoding") || !op.TypeIs(napi_function)) {
146 return true;
147 }
148 return false;
149 }
150
Write(napi_env env,napi_callback_info cbInfo)151 napi_value StreamNExporter::Write(napi_env env, napi_callback_info cbInfo)
152 {
153 NFuncArg funcArg(env, cbInfo);
154 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
155 HILOGE("Number of arguments unmatched");
156 NError(EINVAL).ThrowErr(env);
157 return nullptr;
158 }
159
160 auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
161 if (!streamEntity || !streamEntity->fp) {
162 HILOGE("Stream may has been closed");
163 NError(EIO).ThrowErr(env);
164 return nullptr;
165 }
166
167 FILE *filp = nullptr;
168 filp = streamEntity->fp.get();
169
170 auto [succ, bufGuard, buf, len, hasOffset, offset] =
171 CommonFunc::GetWriteArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
172 if (!succ) {
173 HILOGE("Failed to resolve buf and options");
174 return nullptr;
175 }
176
177 auto arg = make_shared<AsyncWrtieArg>(move(bufGuard));
178 if (!arg) {
179 HILOGE("Failed to request heap memory.");
180 NError(ENOMEM).ThrowErr(env);
181 return nullptr;
182 }
183 auto cbExec = [arg, buf = buf, len = len, filp, hasOffset = hasOffset, offset = offset]() -> NError {
184 int ret = fseek(filp, static_cast<long>(offset), SEEK_SET);
185 if (hasOffset && (ret < 0)) {
186 HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret);
187 return NError(errno);
188 }
189 arg->actLen = fwrite(buf, 1, len, filp);
190 if ((arg->actLen == 0) && (arg->actLen != len)) {
191 HILOGE("Failed to fwrite stream");
192 return NError(EIO);
193 }
194 return NError(ERRNO_NOERR);
195 };
196
197 auto cbCompl = [arg](napi_env env, NError err) -> NVal {
198 if (err) {
199 return { env, err.GetNapiErr(env) };
200 }
201 return { NVal::CreateInt64(env, static_cast<int64_t>(arg->actLen)) };
202 };
203
204 NVal thisVar(env, funcArg.GetThisVar());
205 bool hasOp = false;
206 if (funcArg.GetArgc() == NARG_CNT::TWO) {
207 hasOp = HasOption(env, funcArg[NARG_POS::SECOND]);
208 }
209 if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO && hasOp)) {
210 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_STREAM_WRITE_NAME, cbExec, cbCompl).val_;
211 } else {
212 int cbIdx = ((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD);
213 NVal cb(env, funcArg[cbIdx]);
214 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_STREAM_WRITE_NAME, cbExec, cbCompl).val_;
215 }
216 }
217
Read(napi_env env,napi_callback_info cbInfo)218 napi_value StreamNExporter::Read(napi_env env, napi_callback_info cbInfo)
219 {
220 NFuncArg funcArg(env, cbInfo);
221 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
222 HILOGE("Number of arguments unmatched");
223 NError(EINVAL).ThrowErr(env);
224 return nullptr;
225 }
226
227 auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
228 if (!streamEntity || !streamEntity->fp) {
229 HILOGE("Stream may have been closed");
230 NError(EIO).ThrowErr(env);
231 return nullptr;
232 }
233 FILE *filp = nullptr;
234 filp = streamEntity->fp.get();
235
236 auto [succ, buf, len, hasOffset, offset] =
237 CommonFunc::GetReadArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
238 if (!succ) {
239 HILOGE("Failed to resolve buf and options");
240 NError(EINVAL).ThrowErr(env);
241 return nullptr;
242 }
243
244 auto arg = make_shared<AsyncReadArg>(NVal(env, funcArg[NARG_POS::FIRST]));
245 if (!arg) {
246 HILOGE("Failed to request heap memory.");
247 NError(ENOMEM).ThrowErr(env);
248 return nullptr;
249 }
250 auto cbExec = [arg, buf = buf, len = len, filp, hasOffset = hasOffset, offset = offset]() -> NError {
251 int ret = fseek(filp, offset, SEEK_SET);
252 if (hasOffset && (ret < 0)) {
253 HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret);
254 return NError(errno);
255 }
256 size_t actLen = fread(buf, 1, len, filp);
257 if ((actLen != static_cast<size_t>(len) && !feof(filp)) || ferror(filp)) {
258 HILOGE("Invalid buffer size and pointer, actlen: %{public}zu", actLen);
259 return NError(EIO);
260 } else {
261 arg->lenRead = actLen;
262 return NError(ERRNO_NOERR);
263 }
264 };
265
266 auto cbCompl = [arg](napi_env env, NError err) -> NVal {
267 if (err) {
268 return { env, err.GetNapiErr(env) };
269 }
270 return { NVal::CreateInt64(env, arg->lenRead) };
271 };
272
273 NVal thisVar(env, funcArg.GetThisVar());
274 bool hasOp = false;
275 if (funcArg.GetArgc() == NARG_CNT::TWO) {
276 hasOp = HasOption(env, funcArg[NARG_POS::SECOND]);
277 }
278 if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO && hasOp)) {
279 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_STREAM_READ_NAME, cbExec, cbCompl).val_;
280 } else {
281 int cbIdx = ((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD);
282 NVal cb(env, funcArg[cbIdx]);
283 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_STREAM_READ_NAME, cbExec, cbCompl).val_;
284 }
285 }
286
Close(napi_env env,napi_callback_info cbInfo)287 napi_value StreamNExporter::Close(napi_env env, napi_callback_info cbInfo)
288 {
289 NFuncArg funcArg(env, cbInfo);
290 if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
291 HILOGE("Number of arguments unmatched");
292 NError(EINVAL).ThrowErr(env);
293 return nullptr;
294 }
295
296 auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, funcArg.GetThisVar());
297 if (!streamEntity || !streamEntity->fp) {
298 HILOGE("Stream may has been closed");
299 NError(EIO).ThrowErr(env);
300 return nullptr;
301 }
302
303 auto fp = NClass::RemoveEntityOfFinal<StreamEntity>(env, funcArg.GetThisVar());
304 if (!fp) {
305 NError(EINVAL).ThrowErr(env);
306 return nullptr;
307 }
308
309 auto cbExec = []() -> NError {
310 return NError(ERRNO_NOERR);
311 };
312
313 auto cbCompl = [](napi_env env, NError err) -> NVal {
314 if (err) {
315 return { env, err.GetNapiErr(env) };
316 } else {
317 return NVal::CreateUndefined(env);
318 }
319 };
320
321 NVal thisVar(env, funcArg.GetThisVar());
322 if (funcArg.GetArgc() == NARG_CNT::ZERO) {
323 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_STREAM_CLOSE_NAME, cbExec, cbCompl).val_;
324 } else {
325 NVal cb(env, funcArg[NARG_POS::FIRST]);
326 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_STREAM_CLOSE_NAME, cbExec, cbCompl).val_;
327 }
328 }
329
Constructor(napi_env env,napi_callback_info cbInfo)330 napi_value StreamNExporter::Constructor(napi_env env, napi_callback_info cbInfo)
331 {
332 NFuncArg funcArg(env, cbInfo);
333 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
334 HILOGE("Number of arguments unmatched");
335 NError(EINVAL).ThrowErr(env);
336 return nullptr;
337 }
338
339 unique_ptr<StreamEntity> streamEntity = make_unique<StreamEntity>();
340 if (!NClass::SetEntityFor<StreamEntity>(env, funcArg.GetThisVar(), move(streamEntity))) {
341 HILOGE("INNER BUG. Failed to wrap entity for obj stream");
342 NError(EIO).ThrowErr(env);
343 return nullptr;
344 }
345 return funcArg.GetThisVar();
346 }
347
Export()348 bool StreamNExporter::Export()
349 {
350 vector<napi_property_descriptor> props = {
351 NVal::DeclareNapiFunction("writeSync", WriteSync),
352 NVal::DeclareNapiFunction("flush", Flush::Async),
353 NVal::DeclareNapiFunction("flushSync", Flush::Sync),
354 NVal::DeclareNapiFunction("readSync", ReadSync),
355 NVal::DeclareNapiFunction("closeSync", CloseSync),
356 NVal::DeclareNapiFunction("write", Write),
357 NVal::DeclareNapiFunction("read", Read),
358 NVal::DeclareNapiFunction("close", Close),
359 };
360
361 string className = GetClassName();
362 bool succ = false;
363 napi_value cls = nullptr;
364 tie(succ, cls) = NClass::DefineClass(exports_.env_, className, StreamNExporter::Constructor, move(props));
365 if (!succ) {
366 HILOGE("INNER BUG. Failed to define class");
367 NError(EIO).ThrowErr(exports_.env_);
368 return false;
369 }
370 succ = NClass::SaveClass(exports_.env_, className, cls);
371 if (!succ) {
372 HILOGE("INNER BUG. Failed to save class");
373 NError(EIO).ThrowErr(exports_.env_);
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 FileManagement
390 } // namespace OHOS
391