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