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 #include "read_text.h"
16
17 #include <cinttypes>
18 #include <fcntl.h>
19 #include <sys/stat.h>
20 #include <tuple>
21 #include <unistd.h>
22
23 #include "common_func.h"
24 #include "file_utils.h"
25 #include "filemgmt_libhilog.h"
26
27 namespace OHOS {
28 namespace FileManagement {
29 namespace ModuleFileIO {
30 using namespace std;
31 using namespace OHOS::FileManagement::LibN;
32
GetReadTextArg(napi_env env,napi_value argOption)33 static tuple<bool, int64_t, bool, int64_t, unique_ptr<char[]>> GetReadTextArg(napi_env env, napi_value argOption)
34 {
35 NVal op(env, argOption);
36 int64_t offset = -1;
37 int64_t len = 0;
38 bool succ = false;
39 bool hasLen = false;
40 unique_ptr<char[]> encoding;
41
42 if (op.HasProp("offset") && !op.GetProp("offset").TypeIs(napi_undefined)) {
43 tie(succ, offset) = op.GetProp("offset").ToInt64();
44 if (!succ || offset < 0) {
45 HILOGE("Illegal option.offset parameter");
46 return { false, offset, hasLen, len, nullptr };
47 }
48 }
49
50 if (op.HasProp("length") && !op.GetProp("length").TypeIs(napi_undefined)) {
51 tie(succ, len) = op.GetProp("length").ToInt64();
52 if (!succ || len < 0 || len > UINT_MAX) {
53 HILOGE("Illegal option.length parameter");
54 return { false, offset, hasLen, len, nullptr };
55 }
56 hasLen = true;
57 }
58
59 if (op.HasProp("encoding")) {
60 tie(succ, encoding, ignore) = op.GetProp("encoding").ToUTF8String("utf-8");
61 string_view encodingStr(encoding.get());
62 if (!succ || encodingStr != "utf-8") {
63 HILOGE("Illegal option.encoding parameter");
64 return { false, offset, hasLen, len, nullptr };
65 }
66 }
67
68 return { true, offset, hasLen, len, move(encoding) };
69 }
70
ReadTextAsync(const std::string & path,std::shared_ptr<AsyncReadTextArg> arg,int64_t offset,bool hasLen,int64_t len)71 static NError ReadTextAsync(const std::string &path, std::shared_ptr<AsyncReadTextArg> arg, int64_t offset,
72 bool hasLen, int64_t len)
73 {
74 OHOS::DistributedFS::FDGuard sfd;
75 struct stat statbf;
76 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
77 new uv_fs_t, CommonFunc::fs_req_cleanup };
78 if (!open_req) {
79 HILOGE("Failed to request heap memory.");
80 return NError(ENOMEM);
81 }
82 int ret = uv_fs_open(nullptr, open_req.get(), path.c_str(), O_RDONLY,
83 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, nullptr);
84 if (ret < 0) {
85 HILOGE("Failed to open file, ret: %{public}d", ret);
86 return NError(errno);
87 }
88
89 sfd.SetFD(ret);
90 if (sfd.GetFD() < 0) {
91 HILOGE("Failed to open file by path");
92 return NError(errno);
93 }
94 if (fstat(sfd.GetFD(), &statbf) < 0) {
95 HILOGE("Failed to get stat of file by fd: %{public}d", sfd.GetFD());
96 return NError(errno);
97 }
98
99 if (offset > statbf.st_size) {
100 HILOGE("Invalid offset");
101 return NError(EINVAL);
102 }
103
104 len = (!hasLen || len > statbf.st_size) ? statbf.st_size : len;
105 string buffer(len, '\0');
106 uv_buf_t readbuf = uv_buf_init(const_cast<char *>(buffer.c_str()), static_cast<unsigned int>(len));
107 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> read_req = {
108 new uv_fs_t, CommonFunc::fs_req_cleanup };
109 if (!read_req) {
110 HILOGE("Failed to request heap memory.");
111 return NError(ENOMEM);
112 }
113 arg->len = uv_fs_read(nullptr, read_req.get(), sfd.GetFD(), &readbuf, 1, offset, nullptr);
114 if (arg->len < 0) {
115 HILOGE("Failed to read file by fd: %{public}d", sfd.GetFD());
116 return NError(errno);
117 }
118 arg->buffer = buffer;
119 return NError(ERRNO_NOERR);
120 }
121
Sync(napi_env env,napi_callback_info info)122 napi_value ReadText::Sync(napi_env env, napi_callback_info info)
123 {
124 NFuncArg funcArg(env, info);
125 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
126 HILOGE("Number of arguments unmatched");
127 NError(EINVAL).ThrowErr(env);
128 return nullptr;
129 }
130
131 auto [resGetFirstArg, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
132 if (!resGetFirstArg) {
133 HILOGE("Invalid path");
134 NError(EINVAL).ThrowErr(env);
135 return nullptr;
136 }
137
138 auto [resGetReadTextArg, offset, hasLen, len, encoding] = GetReadTextArg(env, funcArg[NARG_POS::SECOND]);
139 if (!resGetReadTextArg) {
140 NError(EINVAL).ThrowErr(env);
141 return nullptr;
142 }
143
144 OHOS::DistributedFS::FDGuard sfd;
145 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
146 new uv_fs_t, CommonFunc::fs_req_cleanup };
147 if (!open_req) {
148 HILOGE("Failed to request heap memory.");
149 NError(ENOMEM).ThrowErr(env);
150 return nullptr;
151 }
152 int ret = uv_fs_open(nullptr, open_req.get(), path.get(), O_RDONLY,
153 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, nullptr);
154 if (ret < 0) {
155 HILOGE("Failed to open file by ret: %{public}d", ret);
156 NError(errno).ThrowErr(env);
157 return nullptr;
158 }
159 sfd.SetFD(ret);
160 struct stat statbf;
161 if ((!sfd) || (fstat(sfd.GetFD(), &statbf) < 0)) {
162 HILOGE("Failed to get stat of file by fd: %{public}d", sfd.GetFD());
163 NError(errno).ThrowErr(env);
164 return nullptr;
165 }
166
167 if (offset > statbf.st_size) {
168 HILOGE("Invalid offset: %{public}" PRIu64, offset);
169 NError(EINVAL).ThrowErr(env);
170 return nullptr;
171 }
172
173 len = (!hasLen || len > statbf.st_size) ? statbf.st_size : len;
174 string buffer(len, '\0');
175 uv_buf_t readbuf = uv_buf_init(const_cast<char *>(buffer.c_str()), static_cast<unsigned int>(len));
176 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> read_req = {
177 new uv_fs_t, CommonFunc::fs_req_cleanup };
178 if (!read_req) {
179 HILOGE("Failed to request heap memory.");
180 NError(ENOMEM).ThrowErr(env);
181 return nullptr;
182 }
183 ret = uv_fs_read(nullptr, read_req.get(), sfd.GetFD(), &readbuf, 1, offset, nullptr);
184 if (ret < 0) {
185 HILOGE("Failed to read file by fd: %{public}d", sfd.GetFD());
186 NError(errno).ThrowErr(env);
187 return nullptr;
188 }
189
190 return NVal::CreateUTF8String(env, readbuf.base, ret).val_;
191 }
192
Async(napi_env env,napi_callback_info info)193 napi_value ReadText::Async(napi_env env, napi_callback_info info)
194 {
195 NFuncArg funcArg(env, info);
196 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
197 HILOGE("Number of arguments unmatched");
198 NError(EINVAL).ThrowErr(env);
199 return nullptr;
200 }
201
202 auto [resGetFirstArg, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
203 if (!resGetFirstArg) {
204 HILOGE("Invalid path");
205 NError(EINVAL).ThrowErr(env);
206 return nullptr;
207 }
208
209 auto [resGetSecondArg, offset, hasLen, len, encoding] = GetReadTextArg(env, funcArg[NARG_POS::SECOND]);
210 if (!resGetSecondArg) {
211 NError(EINVAL).ThrowErr(env);
212 return nullptr;
213 }
214 auto arg = CreateSharedPtr<AsyncReadTextArg>(NVal(env, funcArg.GetThisVar()));
215 if (arg == nullptr) {
216 HILOGE("Failed to request heap memory.");
217 NError(ENOMEM).ThrowErr(env);
218 return nullptr;
219 }
220 auto cbExec = [path = string(path.get()), arg, offset = offset, hasLen = hasLen, len = len]() -> NError {
221 return ReadTextAsync(path, arg, offset, hasLen, len);
222 };
223
224 auto cbComplete = [arg](napi_env env, NError err) -> NVal {
225 if (err) {
226 return { env, err.GetNapiErr(env) };
227 } else {
228 return NVal::CreateUTF8String(env, arg->buffer.c_str(), arg->len);
229 }
230 };
231
232 NVal thisVar(env, funcArg.GetThisVar());
233 if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
234 !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
235 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_READTEXT_NAME, cbExec, cbComplete).val_;
236 } else {
237 NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD)]);
238 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_READTEXT_NAME, cbExec, cbComplete).val_;
239 }
240 }
241 } // namespace ModuleFileIO
242 } // namespace FileManagement
243 } // namespace OHOS