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