• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-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 "open.h"
16 
17 #include <cstdio>
18 #include <cstdlib>
19 #include <cstring>
20 #include <memory>
21 
22 #include "class_file/file_entity.h"
23 #include "class_file/file_n_exporter.h"
24 #include "common_func.h"
25 #include "filemgmt_libhilog.h"
26 #include "file_utils.h"
27 #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
28 #include "ability.h"
29 #include "bundle_info.h"
30 #include "bundle_mgr_proxy.h"
31 #include "datashare_helper.h"
32 #include "ipc_skeleton.h"
33 #include "iservice_registry.h"
34 #include "remote_uri.h"
35 #include "status_receiver_host.h"
36 #include "system_ability_definition.h"
37 #include "file_uri.h"
38 #endif
39 
40 namespace OHOS {
41 namespace FileManagement {
42 namespace ModuleFileIO {
43 using namespace std;
44 using namespace OHOS::FileManagement::LibN;
45 #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
46 using namespace OHOS::DistributedFS::ModuleRemoteUri;
47 using namespace OHOS::AppExecFwk;
48 #endif
49 
50 const std::string PROCEDURE_OPEN_NAME = "FileIOOpen";
51 const std::string MEDIALIBRARY_DATA_URI = "datashare:///media";
52 const std::string FILE_DATA_URI = "file://";
53 const std::string PATH_SHARE = "/data/storage/el2/share";
54 const std::string MODE_RW = "/rw/";
55 const std::string MODE_R = "/r/";
56 const std::string MEDIA = "media";
57 const std::string DATASHARE = "datashare";
58 
GetJsFlags(napi_env env,const NFuncArg & funcArg)59 static tuple<bool, unsigned int> GetJsFlags(napi_env env, const NFuncArg &funcArg)
60 {
61     unsigned int flags = O_RDONLY;
62     if (funcArg.GetArgc() >= NARG_CNT::TWO) {
63         auto [succ, mode] = NVal(env, funcArg[NARG_POS::SECOND]).ToInt32(O_RDONLY);
64         int32_t invalidMode = (O_WRONLY | O_RDWR);
65         if (!succ || mode < 0 || ((mode & invalidMode) == invalidMode)) {
66             HILOGE("Invalid mode");
67             NError(EINVAL).ThrowErr(env);
68             return { false, flags };
69         }
70         flags = static_cast<unsigned int>(mode);
71         (void)CommonFunc::ConvertJsFlags(flags);
72     }
73     return { true, flags };
74 }
75 
InstantiateFile(napi_env env,int fd,string pathOrUri,bool isUri)76 static NVal InstantiateFile(napi_env env, int fd, string pathOrUri, bool isUri)
77 {
78     napi_value objFile = NClass::InstantiateClass(env, FileNExporter::className_, {});
79     if (!objFile) {
80         HILOGE("Failed to instantiate class");
81         NError(EIO).ThrowErr(env);
82         int ret = close(fd);
83         if (ret < 0) {
84             HILOGE("Failed to close fd");
85         }
86         return NVal();
87     }
88 
89     auto fileEntity = NClass::GetEntityOf<FileEntity>(env, objFile);
90     if (!fileEntity) {
91         HILOGE("Failed to get fileEntity");
92         NError(EIO).ThrowErr(env);
93         int ret = close(fd);
94         if (ret < 0) {
95             HILOGE("Failed to close fd");
96         }
97         return NVal();
98     }
99     auto fdg = CreateUniquePtr<DistributedFS::FDGuard>(fd, false);
100     if (fdg == nullptr) {
101         HILOGE("Failed to request heap memory.");
102         NError(ENOMEM).ThrowErr(env);
103         return NVal();
104     }
105     fileEntity->fd_.swap(fdg);
106     if (isUri) {
107         fileEntity->path_ = "";
108         fileEntity->uri_ = pathOrUri;
109     } else {
110         fileEntity->path_ = pathOrUri;
111         fileEntity->uri_ = "";
112     }
113     return { env, objFile };
114 }
115 
116 #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
117 
OpenFileByDatashare(string path,unsigned int flags)118 static int OpenFileByDatashare(string path, unsigned int flags)
119 {
120     std::shared_ptr<DataShare::DataShareHelper> dataShareHelper = nullptr;
121     int fd = -1;
122     sptr<FileIoToken> remote = new (std::nothrow) IRemoteStub<FileIoToken>();
123     if (!remote) {
124         HILOGE("Failed to get remote object");
125         return -ENOMEM;
126     }
127 
128     dataShareHelper = DataShare::DataShareHelper::Creator(remote->AsObject(), MEDIALIBRARY_DATA_URI);
129     if (!dataShareHelper) {
130         HILOGE("Failed to connect to datashare");
131         return -E_PERMISSION;
132     }
133     Uri uri(path);
134     fd = dataShareHelper->OpenFile(uri, CommonFunc::GetModeFromFlags(flags));
135     return fd;
136 }
137 #endif
138 
Sync(napi_env env,napi_callback_info info)139 napi_value Open::Sync(napi_env env, napi_callback_info info)
140 {
141     NFuncArg funcArg(env, info);
142     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
143         HILOGE("Number of arguments unmatched");
144         NError(EINVAL).ThrowErr(env);
145         return nullptr;
146     }
147     auto [succPath, path, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
148     if (!succPath) {
149         HILOGE("Invalid path");
150         NError(EINVAL).ThrowErr(env);
151         return nullptr;
152     }
153     auto [succMode, mode] = GetJsFlags(env, funcArg);
154     if (!succMode) {
155         return nullptr;
156     }
157     string pathStr = string(path.get());
158 #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
159     Uri uri(pathStr);
160     string bundleName = uri.GetAuthority();
161     string uriType = uri.GetScheme();
162     string uriPath = uri.GetPath();
163     if (uriType == SCHEME_FILE) {
164         AppFileService::ModuleFileUri::FileUri fileUri(pathStr);
165         pathStr = fileUri.GetRealPath();
166         if (bundleName == MEDIA && (pathStr.find(".") == string::npos ||
167             access(pathStr.c_str(), F_OK) != 0)) {
168             int ret = OpenFileByDatashare(uri.ToString(), mode);
169             if (ret >= 0) {
170                 auto file = InstantiateFile(env, ret, uri.ToString(), true).val_;
171                 return file;
172             }
173             HILOGE("Failed to open file by Datashare");
174             NError(-ret).ThrowErr(env);
175             return nullptr;
176         }
177     } else if (uriType == DATASHARE) {
178         // datashare:////#fdFromBinder=xx
179         int fd = -1;
180         if (RemoteUri::IsRemoteUri(pathStr, fd, mode)) {
181             if (fd >= 0) {
182                 auto file = InstantiateFile(env, fd, pathStr, true).val_;
183                 return file;
184             }
185             HILOGE("Failed to open file by RemoteUri");
186             NError(E_INVAL).ThrowErr(env);
187             return nullptr;
188         }
189     }
190 #endif
191     std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
192         new uv_fs_t, CommonFunc::fs_req_cleanup };
193     if (!open_req) {
194         HILOGE("Failed to request heap memory.");
195         NError(ENOMEM).ThrowErr(env);
196         return nullptr;
197     }
198     int ret = uv_fs_open(nullptr, open_req.get(), pathStr.c_str(), mode, S_IRUSR |
199         S_IWUSR | S_IRGRP | S_IWGRP, nullptr);
200     if (ret < 0) {
201         HILOGE("Failed to open file for libuv error %{public}d", ret);
202         NError(ret).ThrowErr(env);
203         return nullptr;
204     }
205     auto file = InstantiateFile(env, ret, pathStr, false).val_;
206     return file;
207 }
208 
209 struct AsyncOpenFileArg {
210     int fd;
211     string path;
212     string uri;
213 };
214 
AsyncCbExec(shared_ptr<AsyncOpenFileArg> arg,string path,unsigned int mode,napi_env env)215 static NError AsyncCbExec(shared_ptr<AsyncOpenFileArg> arg, string path, unsigned int mode, napi_env env)
216 {
217     string pathStr = path;
218 #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
219     Uri uri(path);
220     string bundleName = uri.GetAuthority();
221     string uriType = uri.GetScheme();
222     string uriPath = uri.GetPath();
223     if (uriType == SCHEME_FILE) {
224         AppFileService::ModuleFileUri::FileUri fileUri(path);
225         pathStr = fileUri.GetRealPath();
226         if (bundleName == MEDIA && (pathStr.find(".") == string::npos ||
227             access(pathStr.c_str(), F_OK) != 0)) {
228             int ret = OpenFileByDatashare(path, mode);
229             if (ret >= 0) {
230                 arg->fd = ret;
231                 arg->path = "";
232                 arg->uri = path;
233                 return NError(ERRNO_NOERR);
234             }
235             HILOGE("Failed to open file by Datashare");
236             return NError(-ret);
237         }
238     } else if (uriType == DATASHARE) {
239         // datashare:////#fdFromBinder=xx
240         int fd = -1;
241         if (RemoteUri::IsRemoteUri(path, fd, mode)) {
242             if (fd >= 0) {
243                 arg->fd = fd;
244                 arg->path = "";
245                 arg->uri = path;
246                 return NError(ERRNO_NOERR);
247             }
248             HILOGE("Failed to open file by RemoteUri");
249             return NError(E_INVAL);
250         }
251     }
252 #endif
253     std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
254         new uv_fs_t, CommonFunc::fs_req_cleanup
255     };
256     if (!open_req) {
257         HILOGE("Failed to request heap memory.");
258         return NError(ENOMEM);
259     }
260     int ret = uv_fs_open(nullptr, open_req.get(), pathStr.c_str(), mode, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
261         nullptr);
262     if (ret < 0) {
263         HILOGE("Failed to open file for libuv error %{public}d", ret);
264         return NError(ret);
265     }
266     arg->fd = ret;
267     arg->path = pathStr;
268     arg->uri = "";
269     return NError(ERRNO_NOERR);
270 }
271 
Async(napi_env env,napi_callback_info info)272 napi_value Open::Async(napi_env env, napi_callback_info info)
273 {
274     NFuncArg funcArg(env, info);
275     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
276         HILOGE("Number of arguments unmatched");
277         NError(EINVAL).ThrowErr(env);
278         return nullptr;
279     }
280     auto [succPath, path, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
281     if (!succPath) {
282         HILOGE("Invalid path");
283         NError(EINVAL).ThrowErr(env);
284         return nullptr;
285     }
286     auto [succMode, mode] = GetJsFlags(env, funcArg);
287     if (!succMode) {
288         return nullptr;
289     }
290     auto arg = CreateSharedPtr<AsyncOpenFileArg>();
291     if (arg == nullptr) {
292         HILOGE("Failed to request heap memory.");
293         NError(ENOMEM).ThrowErr(env);
294         return nullptr;
295     }
296     auto cbExec = [arg, path = string(path.get()), mode = mode, env = env]() -> NError {
297         return AsyncCbExec(arg, path, mode, env);
298     };
299     auto cbCompl = [arg](napi_env env, NError err) -> NVal {
300         if (err) {
301             return { env, err.GetNapiErr(env) };
302         }
303         bool isUri = false;
304         if (arg->path.empty() && arg->uri.size()) {
305             isUri = true;
306             return InstantiateFile(env, arg->fd, arg->uri, isUri);
307         }
308         return InstantiateFile(env, arg->fd, arg->path, isUri);
309     };
310     NVal thisVar(env, funcArg.GetThisVar());
311     if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
312         !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
313         return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_OPEN_NAME, cbExec, cbCompl).val_;
314     } else {
315         int cbIdx = ((funcArg.GetArgc() == NARG_CNT::THREE) ? NARG_POS::THIRD : NARG_POS::SECOND);
316         NVal cb(env, funcArg[cbIdx]);
317         return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_OPEN_NAME, cbExec, cbCompl).val_;
318     }
319 }
320 } // namespace ModuleFileIO
321 } // namespace FileManagement
322 } // namespace OHOS