1 /*
2 * Copyright (c) 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 "create_randomaccessfile.h"
16
17 #include <cstdio>
18 #include <cstdlib>
19 #include <cstring>
20 #include <memory>
21 #include <sstream>
22 #include <uv.h>
23
24 #include "../../common/log.h"
25 #include "../../common/napi/n_async/n_async_work_callback.h"
26 #include "../../common/napi/n_async/n_async_work_promise.h"
27 #include "../../common/napi/n_class.h"
28 #include "../../common/napi/n_func_arg.h"
29 #include "../../common/uni_error.h"
30 #include "../common_func.h"
31
32 #include "../class_randomaccessfile/randomaccessfile_entity.h"
33 #include "../class_randomaccessfile/randomaccessfile_n_exporter.h"
34
35 namespace OHOS {
36 namespace DistributedFS {
37 namespace ModuleFileIO {
38 using namespace std;
39
40 struct FileInfo {
41 bool isPath = false;
42 unique_ptr<char[]> path;
43 FDGuard fdg;
44 };
45
CheckFilePath(napi_env env,string path)46 static bool CheckFilePath(napi_env env, string path)
47 {
48 if (access(path.c_str(), 0) == 0) {
49 struct stat fileStat;
50 int ret = stat(path.c_str(), &fileStat);
51 if (ret != 0) {
52 UniError(errno).ThrowErr(env, "Cannot stat filepath");
53 return false;
54 }
55 if ((fileStat.st_mode & S_IFMT) != S_IFREG) {
56 UniError(EINVAL).ThrowErr(env, "Invalid filepath");
57 return false;
58 }
59 }
60 return true;
61 }
62
ParseJsFileAndFP(napi_env env,napi_value pathOrFdFromJsArg,napi_value FPFromJs)63 static tuple<bool, FileInfo, size_t> ParseJsFileAndFP(napi_env env, napi_value pathOrFdFromJsArg, napi_value FPFromJs)
64 {
65 auto [succ, fp] = NVal(env, FPFromJs).ToInt32();
66 if (succ) {
67 auto [isPath, path, ignore] = NVal(env, pathOrFdFromJsArg).ToUTF8String();
68 if (isPath) {
69 if (CheckFilePath(env, string(path.get()))) {
70 return { true, FileInfo { true, move(path), {} }, fp };
71 }
72 return { false, FileInfo { false, {}, {} }, -1 };
73 }
74 auto [isFd, fd] = NVal(env, pathOrFdFromJsArg).ToInt32();
75 if (isFd) {
76 if (fd < 0) {
77 UniError(EINVAL).ThrowErr(env, "Invalid fd");
78 return { false, FileInfo { false, {}, {} }, -1 };
79 }
80 return { true, FileInfo { false, {}, { fd, false } }, fp };
81 }
82 UniError(EINVAL).ThrowErr(env, "The first argument requires filepath/fd");
83 }
84 UniError(EINVAL).ThrowErr(env, "Invalid fp");
85 return { false, FileInfo { false, {}, {} }, -1 };
86 };
87
GetJsFlags(napi_env env,const NFuncArg & funcArg,FileInfo & fileInfo)88 static tuple<bool, int> GetJsFlags(napi_env env, const NFuncArg &funcArg, FileInfo &fileInfo)
89 {
90 int flags = O_RDONLY;
91 bool succ = false;
92 if (fileInfo.isPath) {
93 if (funcArg.GetArgc() >= NARG_CNT::THREE && !NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_function)) {
94 tie(succ, flags) = NVal(env, funcArg[NARG_POS::THIRD]).ToInt32();
95 if (!succ) {
96 UniError(EINVAL).ThrowErr(env, "Invalid flags");
97 return { false, flags };
98 }
99 (void)CommonFunc::ConvertJsFlags(flags);
100 }
101 }
102 return { true, flags };
103 }
104
InstantiateRandomAccessFile(napi_env env,int fd,size_t fp)105 static NVal InstantiateRandomAccessFile(napi_env env, int fd, size_t fp)
106 {
107 napi_value objRAF = NClass::InstantiateClass(env, RandomAccessFileNExporter::className_, {});
108 if (!objRAF) {
109 UniError(EIO).ThrowErr(env, "Cannot instantiate randomaccessfile");
110 return NVal();
111 }
112
113 auto rafEntity = NClass::GetEntityOf<RandomAccessFileEntity>(env, objRAF);
114 if (!rafEntity) {
115 UniError(EIO).ThrowErr(env, "Cannot instantiate randomaccessfile because of void entity");
116 return NVal();
117 }
118 auto fdg = make_unique<FDGuard>(fd);
119 rafEntity->fd_.swap(fdg);
120 rafEntity->fpointer = fp;
121 return { env, objRAF };
122 }
123
Sync(napi_env env,napi_callback_info info)124 napi_value CreateRandomAccessFile::Sync(napi_env env, napi_callback_info info)
125 {
126 NFuncArg funcArg(env, info);
127 if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::THREE)) {
128 UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
129 return nullptr;
130 }
131 auto [succ, fileInfo, fp] = ParseJsFileAndFP(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
132 if (!succ) {
133 return nullptr;
134 }
135
136 if (fileInfo.isPath) {
137 auto [succFlags, flags] = GetJsFlags(env, funcArg, fileInfo);
138 if (!succFlags) {
139 return nullptr;
140 }
141 uv_loop_s *loop = nullptr;
142 napi_get_uv_event_loop(env, &loop);
143 uv_fs_t open_req;
144 int ret = uv_fs_open(loop, &open_req, fileInfo.path.get(), flags, S_IRUSR |
145 S_IWUSR | S_IRGRP | S_IWGRP, NULL);
146 if (ret < 0) {
147 UniError(errno).ThrowErr(env);
148 return nullptr;
149 }
150 fileInfo.fdg.SetFD(open_req.result, false);
151 uv_fs_req_cleanup(&open_req);
152 }
153 return InstantiateRandomAccessFile(env, fileInfo.fdg.GetFD(), fp).val_;
154 }
155
156 struct AsyncCreateRandomAccessFileArg {
157 int fd;
158 size_t fp;
159 };
160
Async(napi_env env,napi_callback_info info)161 napi_value CreateRandomAccessFile::Async(napi_env env, napi_callback_info info)
162 {
163 NFuncArg funcArg(env, info);
164 if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::FOUR)) {
165 UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
166 return nullptr;
167 }
168 auto [succ, fileInfo, fp] = ParseJsFileAndFP(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]);
169 if (!succ) {
170 return nullptr;
171 }
172 auto [succFlags, flags] = GetJsFlags(env, funcArg, fileInfo);
173 if (!succFlags) {
174 return nullptr;
175 }
176 auto arg = make_shared<AsyncCreateRandomAccessFileArg>();
177 auto cbExec = [arg, fileInfo = make_shared<FileInfo>(move(fileInfo)), fp = fp, flags =
178 flags](napi_env env) -> UniError {
179 if (fileInfo->isPath) {
180 uv_loop_s *loop = nullptr;
181 napi_get_uv_event_loop(env, &loop);
182 uv_fs_t open_req;
183 int ret = uv_fs_open(loop, &open_req, fileInfo->path.get(), flags, S_IRUSR |
184 S_IWUSR | S_IRGRP | S_IWGRP, NULL);
185 if (ret < 0) {
186 return UniError(errno);
187 }
188 fileInfo->fdg.SetFD(open_req.result, false);
189 uv_fs_req_cleanup(&open_req);
190 }
191 arg->fd = fileInfo->fdg.GetFD();
192 arg->fp = fp;
193 return UniError(ERRNO_NOERR);
194 };
195 auto cbCompl = [arg](napi_env env, UniError err) -> NVal {
196 if (err) {
197 return { env, err.GetNapiErr(env) };
198 }
199 return InstantiateRandomAccessFile(env, arg->fd, arg->fp);
200 };
201 NVal thisVar(env, funcArg.GetThisVar());
202 if (funcArg.GetArgc() == NARG_CNT::TWO || (funcArg.GetArgc() == NARG_CNT::THREE &&
203 NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_number))) {
204 return NAsyncWorkPromise(env, thisVar).Schedule(createRAFProcedureName, cbExec, cbCompl).val_;
205 } else {
206 int cbIdx = ((funcArg.GetArgc() == NARG_CNT::FOUR) ? NARG_POS::FOURTH : NARG_POS::THIRD);
207 NVal cb(env, funcArg[cbIdx]);
208 return NAsyncWorkCallback(env, thisVar, cb).Schedule(createRAFProcedureName, cbExec, cbCompl).val_;
209 }
210 }
211 } // namespace ModuleFileIO
212 } // namespace DistributedFS
213 } // namespace OHOS