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 "create_randomaccessfile.h"
16 
17 #include "class_file/file_entity.h"
18 #include "class_randomaccessfile/randomaccessfile_entity.h"
19 #include "class_randomaccessfile/randomaccessfile_n_exporter.h"
20 #include "common_func.h"
21 #include "file_utils.h"
22 
23 namespace OHOS {
24 namespace FileManagement {
25 namespace ModuleFileIO {
26 using namespace std;
27 using namespace OHOS::FileManagement::LibN;
28 
29 struct RandomAccessFileOps {
30     int64_t fp;
31     int64_t start;
32     int64_t end;
33 };
34 
GetFileEntity(napi_env env,napi_value objFile)35 static FileEntity* GetFileEntity(napi_env env, napi_value objFile)
36 {
37     auto fileEntity = NClass::GetEntityOf<FileEntity>(env, objFile);
38     if (!fileEntity) {
39         HILOGE("Failed to get file entity");
40         return nullptr;
41     }
42     if (!fileEntity->fd_) {
43         HILOGE("The fd of entity is not exist");
44         return nullptr;
45     }
46     return fileEntity;
47 }
48 
ParseJsFile(napi_env env,napi_value pathOrFileFromJsArg)49 static tuple<bool, FileInfo, int> ParseJsFile(napi_env env, napi_value pathOrFileFromJsArg)
50 {
51     auto [isPath, path, ignore] = NVal(env, pathOrFileFromJsArg).ToUTF8StringPath();
52     if (isPath) {
53         OHOS::DistributedFS::FDGuard sfd;
54         auto fdg = CreateUniquePtr<DistributedFS::FDGuard>(sfd, false);
55         if (fdg == nullptr) {
56             HILOGE("Failed to request heap memory.");
57             close(sfd);
58             return { false, FileInfo { false, nullptr, nullptr }, ENOMEM};
59         }
60         return { true, FileInfo { true, move(path), move(fdg) }, ERRNO_NOERR};
61     }
62     auto fileEntity = GetFileEntity(env, pathOrFileFromJsArg);
63     if (fileEntity) {
64         auto fd = fileEntity->fd_.get()->GetFD();
65         if (fd < 0) {
66             HILOGE("Invalid fd");
67             return { false, FileInfo { false, nullptr, nullptr }, EINVAL};
68         }
69         auto dupFd = dup(fd);
70         if (dupFd < 0) {
71             HILOGE("Failed to get valid fd, fail reason: %{public}s, fd: %{public}d", strerror(errno), fd);
72             return { false, FileInfo { false, nullptr, nullptr }, EINVAL};
73         }
74         auto fdg = CreateUniquePtr<DistributedFS::FDGuard>(dupFd, false);
75         if (fdg == nullptr) {
76             HILOGE("Failed to request heap memory.");
77             close(dupFd);
78             return { false, FileInfo { false, nullptr, nullptr }, ENOMEM};
79         }
80         return { true, FileInfo { false, nullptr, move(fdg) }, ERRNO_NOERR};
81     }
82     HILOGE("The first argument requires filepath/file");
83     return { false, FileInfo { false, nullptr, nullptr }, EINVAL};
84 }
85 
GetRafOptions(napi_env env,napi_value options)86 static tuple<bool, int64_t, int64_t> GetRafOptions(napi_env env, napi_value options)
87 {
88     NVal op = NVal(env, options);
89     int64_t opStart = INVALID_POS;
90     int64_t opEnd = INVALID_POS;
91     if (op.HasProp("start")) {
92         auto [succ, start] = op.GetProp("start").ToInt64();
93         if (!succ || start < 0) {
94             NError(EINVAL).ThrowErr(env, "Invalid option.start, positive integer is desired");
95             return {false, opStart, opEnd};
96         }
97         opStart = start;
98     }
99     if (op.HasProp("end")) {
100         auto [succ, end] = op.GetProp("end").ToInt64();
101         if (!succ || end < 0) {
102             NError(EINVAL).ThrowErr(env, "Invalid option.end, positive integer is desired");
103             return {false, opStart, opEnd};
104         }
105         opEnd = end;
106     }
107     return {true, opStart, opEnd};
108 }
109 
GetJsFlags(napi_env env,const NFuncArg & funcArg,FileInfo & fileInfo)110 static tuple<bool, unsigned int, int64_t, int64_t> GetJsFlags(napi_env env, const NFuncArg &funcArg, FileInfo &fileInfo)
111 {
112     unsigned int flags = O_RDONLY;
113     int64_t start = INVALID_POS;
114     int64_t end = INVALID_POS;
115     if (fileInfo.isPath && funcArg.GetArgc() >= NARG_CNT::TWO) {
116         auto [succ, mode] = NVal(env, funcArg[NARG_POS::SECOND]).ToInt32(0);
117         if (!succ || mode < 0) {
118             HILOGE("Invalid flags");
119             NError(EINVAL).ThrowErr(env);
120             return {false, flags, start, end};
121         }
122         flags = static_cast<unsigned int>(mode);
123         (void)CommonFunc::ConvertJsFlags(flags);
124     }
125 
126     bool succOpt;
127     if (funcArg.GetArgc() == NARG_CNT::THREE && !NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_function)) {
128         tie(succOpt, start, end) = GetRafOptions(env, funcArg[NARG_POS::THIRD]);
129         if (!succOpt) {
130             HILOGE("invalid RandomAccessFile options");
131             NError(EINVAL).ThrowErr(env);
132             return {false, flags, start, end};
133         }
134     }
135 
136     return {true, flags, start, end};
137 }
138 
InstantiateRandomAccessFile(napi_env env,std::unique_ptr<DistributedFS::FDGuard> fdg,struct RandomAccessFileOps ops,bool async=false)139 static NVal InstantiateRandomAccessFile(napi_env env,
140                                         std::unique_ptr<DistributedFS::FDGuard> fdg,
141                                         struct RandomAccessFileOps ops,
142                                         bool async = false)
143 {
144     napi_value objRAF = NClass::InstantiateClass(env, RandomAccessFileNExporter::className_, {});
145     if (!objRAF) {
146         HILOGE("Cannot instantiate randomaccessfile");
147         if (async) {
148             return {env, NError(EIO).GetNapiErr(env)};
149         }
150         NError(EIO).ThrowErr(env);
151         return NVal();
152     }
153     auto rafEntity = NClass::GetEntityOf<RandomAccessFileEntity>(env, objRAF);
154     if (!rafEntity) {
155         HILOGE("Cannot instantiate randomaccessfile because of void entity");
156         if (async) {
157             return {env, NError(EIO).GetNapiErr(env)};
158         }
159         NError(EIO).ThrowErr(env);
160         return NVal();
161     }
162     rafEntity->fd.swap(fdg);
163     rafEntity->filePointer = ops.fp;
164     rafEntity->start = ops.start;
165     rafEntity->end = ops.end;
166     return {env, objRAF};
167 }
168 
Sync(napi_env env,napi_callback_info info)169 napi_value CreateRandomAccessFile::Sync(napi_env env, napi_callback_info info)
170 {
171     NFuncArg funcArg(env, info);
172     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
173         HILOGE("Number of arguments unmatched");
174         NError(EINVAL).ThrowErr(env);
175         return nullptr;
176     }
177     auto [succ, fileInfo, err] = ParseJsFile(env, funcArg[NARG_POS::FIRST]);
178     if (!succ) {
179         NError(err).ThrowErr(env);
180         return nullptr;
181     }
182     if (fileInfo.isPath) {
183         auto [succFlags, flags, ignoreStart, ignoreEnd] = GetJsFlags(env, funcArg, fileInfo);
184         if (!succFlags) {
185             return nullptr;
186         }
187         std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
188             new uv_fs_t, CommonFunc::fs_req_cleanup };
189         if (!open_req) {
190             HILOGE("Failed to request heap memory.");
191             NError(ENOMEM).ThrowErr(env);
192             return nullptr;
193         }
194         int ret = uv_fs_open(nullptr, open_req.get(), fileInfo.path.get(), flags, S_IRUSR |
195             S_IWUSR | S_IRGRP | S_IWGRP, NULL);
196         if (ret < 0) {
197             NError(ret).ThrowErr(env);
198             return nullptr;
199         }
200 
201         fileInfo.fdg->SetFD(open_req.get()->result, false);
202     }
203     if (funcArg.GetArgc() == NARG_CNT::THREE) {
204         auto [succ, start, end] = GetRafOptions(env, funcArg[NARG_POS::THIRD]);
205         if (succ) {
206             return InstantiateRandomAccessFile(env, move(fileInfo.fdg), {0, start, end}).val_;
207         }
208     }
209     return InstantiateRandomAccessFile(env, move(fileInfo.fdg), {0, INVALID_POS, INVALID_POS}).val_;
210 }
211 
212 struct AsyncCreateRandomAccessFileArg {
213     int fd = 0;
214 };
215 
AsyncExec(shared_ptr<AsyncCreateRandomAccessFileArg> arg,shared_ptr<FileInfo> fileInfo,unsigned int flags)216 NError AsyncExec(shared_ptr<AsyncCreateRandomAccessFileArg> arg,
217     shared_ptr<FileInfo> fileInfo, unsigned int flags)
218 {
219     if (fileInfo->isPath) {
220         std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
221             new uv_fs_t, CommonFunc::fs_req_cleanup };
222         int ret = uv_fs_open(nullptr, open_req.get(), fileInfo->path.get(), flags, S_IRUSR |
223             S_IWUSR | S_IRGRP | S_IWGRP, NULL);
224         if (ret < 0) {
225             return NError(ret);
226         }
227         fileInfo->fdg->SetFD(open_req.get()->result, false);
228     }
229     arg->fd = fileInfo->fdg->GetFD();
230     return NError(ERRNO_NOERR);
231 }
232 
Async(napi_env env,napi_callback_info info)233 napi_value CreateRandomAccessFile::Async(napi_env env, napi_callback_info info)
234 {
235     NFuncArg funcArg(env, info);
236     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
237         HILOGE("Number of arguments unmatched");
238         NError(EINVAL).ThrowErr(env);
239         return nullptr;
240     }
241     auto [succ, fileInfo, err] = ParseJsFile(env, funcArg[NARG_POS::FIRST]);
242     if (!succ) {
243         NError(err).ThrowErr(env);
244         return nullptr;
245     }
246     auto [succFlags, flags, start, end] = GetJsFlags(env, funcArg, fileInfo);
247     if (!succFlags) {
248         return nullptr;
249     }
250     auto arg = CreateSharedPtr<AsyncCreateRandomAccessFileArg>();
251     if (arg == nullptr) {
252         HILOGE("Failed to request heap memory.");
253         NError(ENOMEM).ThrowErr(env);
254         return nullptr;
255     }
256     auto movedFileInfo = CreateSharedPtr<FileInfo>(move(fileInfo));
257     if (movedFileInfo == nullptr) {
258         HILOGE("Failed to request heap memory.");
259         NError(ENOMEM).ThrowErr(env);
260         return nullptr;
261     }
262     auto cbExec = [arg, movedFileInfo, flags = flags]() -> NError {
263         return AsyncExec(arg, movedFileInfo, flags);
264     };
265 
266     auto cbCompl = [arg, movedFileInfo, start = start, end = end](napi_env env, NError err) -> NVal {
267         if (err) {
268             return { env, err.GetNapiErr(env) };
269         }
270         return InstantiateRandomAccessFile(env, move(movedFileInfo->fdg), {0, start, end}, true);
271     };
272     NVal thisVar(env, funcArg.GetThisVar());
273     if (funcArg.GetArgc() == NARG_CNT::ONE ||
274         (funcArg.GetArgc() == NARG_CNT::TWO && NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_number)) ||
275         (funcArg.GetArgc() == NARG_CNT::THREE && NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_object))) {
276         return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_CREATERAT_NAME, cbExec, cbCompl).val_;
277     } else {
278         int cbIdx = ((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD);
279         NVal cb(env, funcArg[cbIdx]);
280         return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_CREATERAT_NAME, cbExec, cbCompl).val_;
281     }
282 }
283 } // namespace ModuleFileIO
284 } // namespace FileManagement
285 } // namespace OHOS