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 #include "read_text.h"
16
17 #include <fcntl.h>
18 #include <securec.h>
19 #include <sys/stat.h>
20 #include <tuple>
21 #include <unistd.h>
22 #include "../../common/file_helper/fd_guard.h"
23 #include "../../common/napi/n_async/n_async_work_callback.h"
24 #include "../../common/napi/n_async/n_async_work_promise.h"
25 #include "../../common/napi/n_func_arg.h"
26 #include "../common_func.h"
27
28 namespace OHOS {
29 namespace DistributedFS {
30 namespace ModuleFileIO {
31 using namespace std;
32
GetReadTextArg(napi_env env,napi_value argOption)33 static tuple<bool, int64_t, bool, size_t, unique_ptr<char[]>> GetReadTextArg(napi_env env, napi_value argOption)
34 {
35 NVal op(env, argOption);
36 int64_t position = -1;
37 size_t len = 0;
38 bool hasLen = false;
39 unique_ptr<char[]> encoding = nullptr;
40
41 if (op.HasProp("position") && !op.GetProp("position").TypeIs(napi_undefined)) {
42 bool succ = false;
43 tie(succ, position) = op.GetProp("position").ToInt64();
44 if (!succ || position < 0) {
45 return { false, position, hasLen, len, nullptr };
46 }
47 }
48
49 if (op.HasProp("length") && !op.GetProp("length").TypeIs(napi_undefined)) {
50 auto [succ, length] = op.GetProp("length").ToInt64();
51 if (!succ || length < 0 || length > UINT_MAX) {
52 return { false, position, hasLen, len, nullptr };
53 }
54 len = static_cast<size_t>(length);
55 hasLen = true;
56 }
57
58 if (op.HasProp("encoding")) {
59 auto [succ, encoding, unuse] = op.GetProp("encoding").ToUTF8String("utf-8");
60 string_view encodingStr(encoding.get());
61 if (!succ || encodingStr != "utf-8") {
62 return { false, position, hasLen, len, nullptr };
63 }
64 }
65
66 return { true, position, hasLen, len, move(encoding) };
67 }
68
Sync(napi_env env,napi_callback_info info)69 napi_value ReadText::Sync(napi_env env, napi_callback_info info)
70 {
71 NFuncArg funcArg(env, info);
72 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
73 UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
74 return nullptr;
75 }
76
77 auto [resGetFirstArg, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
78 if (!resGetFirstArg) {
79 UniError(EINVAL).ThrowErr(env, "Invalid path");
80 return nullptr;
81 }
82
83 auto [resGetReadTextArg, position, hasLen, len, encoding] = GetReadTextArg(env, funcArg[NARG_POS::SECOND]);
84 if (!resGetReadTextArg) {
85 UniError(EINVAL).ThrowErr(env, "Invalid option");
86 return nullptr;
87 }
88
89 struct stat statbf;
90 FDGuard sfd;
91 sfd.SetFD(open(path.get(), O_RDONLY));
92 if ((!sfd) || (fstat(sfd.GetFD(), &statbf) == -1)) {
93 UniError(errno).ThrowErr(env);
94 return nullptr;
95 }
96
97 if (position > statbf.st_size) {
98 UniError(EINVAL).ThrowErr(env, "Invalid position");
99 return nullptr;
100 }
101
102 len = (!hasLen || len > static_cast<size_t>(statbf.st_size)) ? statbf.st_size : len;
103 std::unique_ptr<char[]> readbuf = std::make_unique<char[]>(len + 1);
104 if (readbuf == nullptr) {
105 UniError(EINVAL).ThrowErr(env, "file is too large");
106 return nullptr;
107 }
108
109 if (memset_s(readbuf.get(), len + 1, 0, len + 1) != EOK) {
110 UniError(errno).ThrowErr(env, "dfs mem error");
111 return nullptr;
112 }
113 ssize_t ret = 0;
114 if (position >= 0) {
115 ret = pread(sfd.GetFD(), readbuf.get(), len, position);
116 } else {
117 ret = read(sfd.GetFD(), readbuf.get(), len);
118 }
119 if (ret == -1) {
120 UniError(EINVAL).ThrowErr(env, "Invalid read file");
121 return nullptr;
122 }
123
124 return NVal::CreateUTF8String(env, readbuf.get(), ret).val_;
125 }
126
AsyncExec(const std::string & path,std::shared_ptr<AsyncReadTextArg> arg,int64_t position,bool hasLen,size_t len)127 static UniError AsyncExec(const std::string &path, std::shared_ptr<AsyncReadTextArg> arg, int64_t position,
128 bool hasLen, size_t len)
129 {
130 if (arg == nullptr) {
131 return UniError(ENOMEM);
132 }
133
134 FDGuard sfd;
135 struct stat statbf;
136 sfd.SetFD(open(path.c_str(), O_RDONLY));
137 if (sfd.GetFD() == -1) {
138 return UniError(EINVAL);
139 }
140
141 if (fstat(sfd.GetFD(), &statbf) == -1) {
142 return UniError(EINVAL);
143 }
144
145 if (position > statbf.st_size) {
146 return UniError(EINVAL);
147 }
148
149 len = (!hasLen || len > static_cast<size_t>(statbf.st_size)) ? statbf.st_size : len;
150 arg->buf = std::make_unique<char[]>(len);
151 if (arg->buf == nullptr) {
152 return UniError(ENOMEM);
153 }
154
155 if (position >= 0) {
156 arg->len = pread(sfd.GetFD(), arg->buf.get(), len, position);
157 } else {
158 arg->len = read(sfd.GetFD(), arg->buf.get(), len);
159 }
160
161 if (arg->len == -1) {
162 return UniError(EINVAL);
163 }
164
165 return UniError(ERRNO_NOERR);
166 }
167
Async(napi_env env,napi_callback_info info)168 napi_value ReadText::Async(napi_env env, napi_callback_info info)
169 {
170 NFuncArg funcArg(env, info);
171 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
172 UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
173 return nullptr;
174 }
175
176 auto [resGetFirstArg, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
177 if (!resGetFirstArg) {
178 UniError(EINVAL).ThrowErr(env, "Invalid path");
179 return nullptr;
180 }
181
182 auto [resGetSecondArg, position, hasLen, len, encoding] = GetReadTextArg(env, funcArg[NARG_POS::SECOND]);
183 if (!resGetSecondArg) {
184 UniError(EINVAL).ThrowErr(env, "Invalid option");
185 return nullptr;
186 }
187
188 auto arg = make_shared<AsyncReadTextArg>(NVal(env, funcArg.GetThisVar()));
189 if (arg == nullptr) {
190 return nullptr;
191 }
192
193 auto cbExec =
194 [path = string(path.get()), arg, position = position, hasLen = hasLen, len = len](napi_env env) -> UniError {
195 return AsyncExec(path, arg, position, hasLen, len);
196 };
197
198 auto cbComplete = [arg](napi_env env, UniError err) -> NVal {
199 if (err) {
200 return { env, err.GetNapiErr(env) };
201 } else {
202 return NVal::CreateUTF8String(env, arg->buf.get(), arg->len);
203 }
204 };
205
206 NVal thisVar(env, funcArg.GetThisVar());
207 if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
208 !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
209 return NAsyncWorkPromise(env, thisVar).Schedule("FileIOReadText", cbExec, cbComplete).val_;
210 } else {
211 NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD)]);
212 return NAsyncWorkCallback(env, thisVar, cb).Schedule("FileIOReadText", cbExec, cbComplete).val_;
213 }
214 }
215 } // namespace ModuleFileIO
216 } // namespace DistributedFS
217 } // namespace OHOS