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 { new char[]{ "utf-8" } };
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
OpenFile(const std::string & path)122 static int OpenFile(const std::string& path)
123 {
124 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
125 new uv_fs_t, CommonFunc::fs_req_cleanup
126 };
127 if (open_req == nullptr) {
128 HILOGE("Failed to request heap memory.");
129 return -ENOMEM;
130 }
131
132 return uv_fs_open(nullptr, open_req.get(), path.c_str(), O_RDONLY,
133 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, nullptr);
134 }
135
ReadFromFile(int fd,int64_t offset,string & buffer)136 static int ReadFromFile(int fd, int64_t offset, string& buffer)
137 {
138 uv_buf_t readbuf = uv_buf_init(const_cast<char *>(buffer.c_str()), static_cast<unsigned int>(buffer.size()));
139 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> read_req = {
140 new uv_fs_t, CommonFunc::fs_req_cleanup };
141 if (read_req == nullptr) {
142 HILOGE("Failed to request heap memory.");
143 return -ENOMEM;
144 }
145 return uv_fs_read(nullptr, read_req.get(), fd, &readbuf, 1, offset, nullptr);
146 }
147
Sync(napi_env env,napi_callback_info info)148 napi_value ReadText::Sync(napi_env env, napi_callback_info info)
149 {
150 NFuncArg funcArg(env, info);
151 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
152 HILOGE("Number of arguments unmatched");
153 NError(EINVAL).ThrowErr(env);
154 return nullptr;
155 }
156
157 auto [resGetFirstArg, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
158 if (!resGetFirstArg) {
159 HILOGE("Invalid path");
160 NError(EINVAL).ThrowErr(env);
161 return nullptr;
162 }
163
164 auto [resGetReadTextArg, offset, hasLen, len, encoding] = GetReadTextArg(env, funcArg[NARG_POS::SECOND]);
165 if (!resGetReadTextArg) {
166 NError(EINVAL).ThrowErr(env);
167 return nullptr;
168 }
169
170 OHOS::DistributedFS::FDGuard sfd;
171 int fd = OpenFile(path.get());
172 if (fd < 0) {
173 HILOGD("Failed to open file by ret: %{public}d", fd);
174 NError(fd).ThrowErr(env);
175 return nullptr;
176 }
177 sfd.SetFD(fd);
178
179 struct stat statbf;
180 if ((!sfd) || (fstat(sfd.GetFD(), &statbf) < 0)) {
181 HILOGE("Failed to get stat of file by fd: %{public}d", sfd.GetFD());
182 NError(errno).ThrowErr(env);
183 return nullptr;
184 }
185
186 if (offset > statbf.st_size) {
187 HILOGE("Invalid offset: %{public}" PRIu64, offset);
188 NError(EINVAL).ThrowErr(env);
189 return nullptr;
190 }
191
192 len = (!hasLen || len > statbf.st_size) ? statbf.st_size : len;
193 string buffer(len, '\0');
194 int readRet = ReadFromFile(sfd.GetFD(), offset, buffer);
195 if (readRet < 0) {
196 HILOGE("Failed to read file by fd: %{public}d", fd);
197 NError(readRet).ThrowErr(env);
198 return nullptr;
199 }
200
201 return NVal::CreateUTF8String(env, buffer.c_str(), readRet).val_;
202 }
203
Async(napi_env env,napi_callback_info info)204 napi_value ReadText::Async(napi_env env, napi_callback_info info)
205 {
206 NFuncArg funcArg(env, info);
207 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
208 HILOGE("Number of arguments unmatched");
209 NError(EINVAL).ThrowErr(env);
210 return nullptr;
211 }
212
213 auto [resGetFirstArg, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
214 if (!resGetFirstArg) {
215 HILOGE("Invalid path");
216 NError(EINVAL).ThrowErr(env);
217 return nullptr;
218 }
219
220 auto [resGetSecondArg, offset, hasLen, len, encoding] = GetReadTextArg(env, funcArg[NARG_POS::SECOND]);
221 if (!resGetSecondArg) {
222 NError(EINVAL).ThrowErr(env);
223 return nullptr;
224 }
225 auto arg = CreateSharedPtr<AsyncReadTextArg>(NVal(env, funcArg.GetThisVar()));
226 if (arg == nullptr) {
227 HILOGE("Failed to request heap memory.");
228 NError(ENOMEM).ThrowErr(env);
229 return nullptr;
230 }
231 auto cbExec = [path = string(path.get()), arg, offset = offset, hasLen = hasLen, len = len]() -> NError {
232 return ReadTextAsync(path, arg, offset, hasLen, len);
233 };
234
235 auto cbComplete = [arg](napi_env env, NError err) -> NVal {
236 if (err) {
237 return { env, err.GetNapiErr(env) };
238 } else {
239 return NVal::CreateUTF8String(env, arg->buffer.c_str(), arg->len);
240 }
241 };
242
243 NVal thisVar(env, funcArg.GetThisVar());
244 if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
245 !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
246 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_READTEXT_NAME, cbExec, cbComplete).val_;
247 } else {
248 NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD)]);
249 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_READTEXT_NAME, cbExec, cbComplete).val_;
250 }
251 }
252 } // namespace ModuleFileIO
253 } // namespace FileManagement
254 } // namespace OHOS