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