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