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