1/*
2 * Copyright (c) 2024 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
16#include "ffi_remote_data.h"
17#include "cj_common_ffi.h"
18#include "uni_error.h"
19#include "uri.h"
20#include "open.h"
21#include "datashare_helper.h"
22#include "utils.h"
23#include "iremote_stub.h"
24#include "file_uri.h"
25#include "want.h"
26#include "ability_manager_client.h"
27#include "remote_uri.h"
28#include "file_utils.h"
29#include "securec.h"
30#include "file_impl.h"
31
32static const std::string PROCEDURE_OPEN_NAME = "FileIOOpen";
33static const std::string MEDIALIBRARY_DATA_URI = "datashare:///media";
34static const std::string FILE_DATA_URI = "file://";
35static const std::string PATH_SHARE = "/data/storage/el2/share";
36static const std::string MODE_RW = "/rw/";
37static const std::string MODE_R = "/r/";
38static const std::string DOCS = "docs";
39static const std::string DATASHARE = "datashare";
40static const std::string SCHEME_BROKER = "content";
41constexpr uint32_t MAX_WANT_FLAG = 4;
42
43namespace {
44using Uri = OHOS::Uri;
45using namespace OHOS;
46using namespace DistributedFS;
47using namespace OHOS::CJSystemapi;
48using namespace OHOS::CJSystemapi::FileFs;
49using namespace OHOS::FileManagement;
50using namespace OHOS::FileManagement::ModuleFileIO;
51using namespace OHOS::DistributedFS::ModuleRemoteUri;
52using namespace std;
53
54static int OpenFileByPath(const std::string &path, unsigned int mode)
55{
56    std::unique_ptr<uv_fs_t, decltype(CommonFunc::FsReqCleanup)*> open_req = {
57        new (std::nothrow) uv_fs_t, CommonFunc::FsReqCleanup };
58    if (!open_req) {
59        LOGE("Failed to request heap memory.");
60        return -ENOMEM;
61    }
62    int ret = uv_fs_open(nullptr, open_req.get(), path.c_str(), mode, S_IRUSR |
63        S_IWUSR | S_IRGRP | S_IWGRP, nullptr);
64    if (ret < 0) {
65        LOGE("Failed to open OpenFileByPath error %{public}d", ret);
66    }
67    return ret;
68}
69
70static int OpenFileByDatashare(const std::string &path, int64_t flags)
71{
72    std::shared_ptr<DataShare::DataShareHelper> dataShareHelper = nullptr;
73    sptr<FileIoToken> remote = new (std::nothrow) OHOS::IRemoteStub<FileIoToken>();
74    if (!remote) {
75        LOGE("Failed to get remote object");
76        return -ENOMEM;
77    }
78
79    dataShareHelper = DataShare::DataShareHelper::Creator(remote->AsObject(), MEDIALIBRARY_DATA_URI);
80    if (!dataShareHelper) {
81        LOGE("Failed to connect to datashare");
82        return -E_PERMISSION;
83    }
84    Uri uri(path);
85    int fd = dataShareHelper->OpenFile(uri, CommonFunc::GetModeFromFlags(flags));
86    return fd;
87}
88
89static std::tuple<int, std::string> OpenByFileDataUri(Uri &uri, const std::string &uriStr, unsigned int mode)
90{
91    std::string bundleName = uri.GetAuthority();
92    AppFileService::ModuleFileUri::FileUri fileUri(uriStr);
93    std::string realPath = fileUri.GetRealPath();
94    if ((bundleName == MEDIA || bundleName == DOCS) && access(realPath.c_str(), F_OK) != 0) {
95        int res = OpenFileByDatashare(uri.ToString(), mode);
96        if (res < 0) {
97            LOGE("Failed to open file by Datashare error %{public}d", res);
98        }
99        return { res, uri.ToString() };
100    }
101    int ret = OpenFileByPath(realPath, mode);
102    if (ret < 0) {
103        if (bundleName == MEDIA) {
104            ret = OpenFileByDatashare(uriStr, mode);
105            if (ret < 0) {
106                LOGE("Failed to open file by Datashare error %{public}d", ret);
107            }
108        } else {
109            LOGE("Failed to open file for libuv error %{public}d", ret);
110        }
111    }
112    return { ret, uriStr };
113}
114
115static std::tuple<int, std::string> OpenFileByBroker(const Uri &uri, uint32_t mode)
116{
117    uint32_t flag = (mode % MAX_WANT_FLAG) > 0 ?
118        AAFwk::Want::FLAG_AUTH_WRITE_URI_PERMISSION :
119        AAFwk::Want::FLAG_AUTH_READ_URI_PERMISSION;
120    int ret = AAFwk::AbilityManagerClient::GetInstance()->OpenFile(uri, flag);
121    if (ret < 0) {
122        LOGE("Failed to open file by Broker error %{public}d", ret);
123    }
124    return { ret, uri.ToString() };
125}
126
127static std::tuple<int, std::string> CheckDataShareUri(const std::string &path, uint32_t mode)
128{
129    // datashare:////#fdFromBinder=xx
130    int fd = -1;
131    if (RemoteUri::IsRemoteUri(path, fd, mode)) {
132        if (fd >= 0) {
133            return { fd, path };
134        }
135        LOGE("Failed to open file by RemoteUri");
136    }
137    return { -EINVAL, path };
138}
139
140static std::tuple<int, std::string> OpenFileByUri(const std::string &path, uint32_t mode)
141{
142    Uri uri(path);
143    std::string uriType = uri.GetScheme();
144    if (uriType == SCHEME_FILE) {
145        return OpenByFileDataUri(uri, path, mode);
146    } else if (uriType == SCHEME_BROKER) {
147        return OpenFileByBroker(uri, mode);
148    } else if (uriType == DATASHARE) {
149        return CheckDataShareUri(path, mode);
150    }
151    LOGE("Failed to open file by invalid uri");
152    return { -EINVAL, path };
153}
154
155FileEntity* InstantiateFile(int fd, const std::string& pathOrUri, bool isUri)
156{
157    auto fdg = CreateUniquePtr<DistributedFS::FDGuard>(fd, false);
158    if (fdg == nullptr) {
159        LOGE("Failed to request heap memory.");
160        close(fd);
161        return nullptr;
162    }
163    FileEntity *fileEntity = new(std::nothrow) FileEntity();
164    if (fileEntity == nullptr) {
165        return nullptr;
166    }
167    fileEntity->fd_.swap(fdg);
168    if (isUri) {
169        fileEntity->path_ = "";
170        fileEntity->uri_ = pathOrUri;
171    } else {
172        fileEntity->path_ = pathOrUri;
173        fileEntity->uri_ = "";
174    }
175    return fileEntity;
176}
177
178static tuple<bool, unsigned int> GetCjFlags(int64_t mode)
179{
180    unsigned int flags = O_RDONLY;
181    unsigned int invalidMode = (O_WRONLY | O_RDWR);
182    if (mode < 0 || (static_cast<unsigned int>(mode) & invalidMode) == invalidMode) {
183        LOGE("Invalid mode");
184        return { false, flags };
185    }
186    flags = static_cast<unsigned int>(mode);
187    (void)CommonFunc::ConvertCjFlags(flags);
188    return { true, flags };
189}
190}
191
192namespace OHOS {
193namespace CJSystemapi {
194namespace FileFs {
195using namespace OHOS::FFI;
196std::tuple<int32_t, sptr<FileEntity>> FileEntity::Open(const char* path, int64_t mode)
197{
198    auto [succMode, flags] = GetCjFlags(mode);
199    if (!succMode) {
200        return {EINVAL, nullptr};
201    }
202    std::string pathStr(path);
203#if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
204    if (pathStr.find("://") != string::npos) {
205        auto [res, uriStr] = OpenFileByUri(pathStr, flags);
206        if (res < 0) {
207            return {res, nullptr};
208        }
209        auto fileEntity = InstantiateFile(res, uriStr, true);
210        if (fileEntity == nullptr) {
211            return { ENOMEM, nullptr};
212        }
213        auto fileUri = FFIData::Create<FileEntity>(std::move(fileEntity->fd_), fileEntity->path_, fileEntity->uri_);
214        delete(fileEntity);
215        fileEntity = nullptr;
216        if (!fileUri) {
217            return {ENOMEM, nullptr};
218        }
219        return {SUCCESS_CODE, fileUri};
220    }
221#endif
222    int ret = OpenFileByPath(pathStr, flags);
223    if (ret < 0) {
224        LOGE("Failed to open file for libuv error %{public}d", ret);
225        return {ret, nullptr};
226    }
227    auto file = InstantiateFile(ret, pathStr, false);
228    if (file == nullptr) {
229        return { ENOMEM, nullptr};
230    }
231    auto filePath = FFIData::Create<FileEntity>(std::move(file->fd_), file->path_, file->uri_);
232    delete(file);
233    file = nullptr;
234    if (!filePath) {
235        return {ENOMEM, nullptr};
236    }
237    return {SUCCESS_CODE, filePath};
238}
239
240std::tuple<int32_t, sptr<FileEntity>> FileEntity::Dup(int32_t fd)
241{
242    LOGI("FS_TEST::FileEntity::Dup start");
243    if (fd < 0) {
244        LOGE("Invalid fd");
245        return {EINVAL, nullptr};
246    }
247    int dstFd = dup(fd);
248    if (dstFd < 0) {
249        LOGE("Failed to dup fd, errno: %{public}d", errno);
250        return {errno, nullptr};
251    }
252    unique_ptr<uv_fs_t, decltype(CommonFunc::FsReqCleanup)*> readlink_req = {
253        new (std::nothrow) uv_fs_t, CommonFunc::FsReqCleanup };
254    if (!readlink_req) {
255        LOGE("Failed to request heap memory.");
256        return {ENOMEM, nullptr};
257    }
258    string path = "/proc/self/fd/" + to_string(dstFd);
259    int ret = uv_fs_readlink(nullptr, readlink_req.get(), path.c_str(), nullptr);
260    if (ret < 0) {
261        LOGE("Failed to readlink fd, ret: %{public}d", ret);
262        return {ret, nullptr};
263    }
264    auto fdPrt = CreateUniquePtr<DistributedFS::FDGuard>(dstFd, false);
265    if (fdPrt == nullptr) {
266        LOGE("Failed to request heap memory.");
267        return {ENOMEM, nullptr};
268    }
269    auto pathStr = string(static_cast<const char *>(readlink_req->ptr));
270    auto fileEntity = FFIData::Create<FileEntity>(std::move(fdPrt), pathStr, "");
271    if (!fileEntity) {
272        return {ENOMEM, nullptr};
273    }
274    return {SUCCESS_CODE, fileEntity};
275}
276
277static tuple<int, unique_ptr<uv_fs_t, decltype(CommonFunc::FsReqCleanup)*>> RealPathCore(const string &srcPath)
278{
279    std::unique_ptr<uv_fs_t, decltype(CommonFunc::FsReqCleanup)*> realpath_req = {
280        new (std::nothrow) uv_fs_t, CommonFunc::FsReqCleanup };
281    if (!realpath_req) {
282        LOGE("Failed to request heap memory.");
283        return { ENOMEM, move(realpath_req)};
284    }
285    int ret = uv_fs_realpath(nullptr, realpath_req.get(), srcPath.c_str(), nullptr);
286    if (ret < 0) {
287        LOGE("Failed to realpath, ret: %{public}d", ret);
288        return { ret, move(realpath_req)};
289    }
290    return { ERRNO_NOERR, move(realpath_req) };
291}
292
293int FileEntity::GetFD(int64_t id)
294{
295    auto fileEntity = FFIData::GetData<FileEntity>(id);
296    if (!fileEntity) {
297        LOGE("FileEntity instance not exist %{public}" PRId64, id);
298        return ERR_INVALID_INSTANCE_CODE;
299    }
300    return fileEntity->fd_.get()->GetFD();
301}
302
303const char* FileEntity::GetPath(int64_t id)
304{
305    auto fileEntity = FFIData::GetData<FileEntity>(id);
306    if (!fileEntity) {
307        LOGE("FileEntity instance not exist %{public}" PRId64, id);
308        return nullptr;
309    }
310    auto [realPathRes, realPath] = RealPathCore(fileEntity->path_);
311    if (realPathRes != ERRNO_NOERR) {
312        return nullptr;
313    }
314    string tempPath = string(static_cast<char*>(realPath->ptr));
315    char* value = static_cast<char*>(malloc((tempPath.size() + 1) * sizeof(char)));
316    if (value == nullptr) {
317        return nullptr;
318    }
319    if (strcpy_s(value, tempPath.size() + 1, tempPath.c_str()) != 0) {
320        free(value);
321        value = nullptr;
322        return nullptr;
323    }
324    return value;
325}
326
327const char* FileEntity::GetName(int64_t id)
328{
329    string path = FileEntity::GetPath(id);
330    auto pos = path.find_last_of('/');
331    if (pos == string::npos) {
332        LOGE("Failed to split filename from path");
333        return nullptr;
334    }
335    auto name = path.substr(pos + 1);
336    char* value = static_cast<char*>(malloc((name.size() + 1) * sizeof(char)));
337    if (value == nullptr) {
338        return nullptr;
339    }
340    if (strcpy_s(value, name.size() + 1, name.c_str()) != 0) {
341        free(value);
342        return nullptr;
343    }
344    return value;
345}
346
347int FileEntity::TryLock(int64_t id, bool exclusive)
348{
349    auto fileEntity = FFIData::GetData<FileEntity>(id);
350    if (!fileEntity) {
351        LOGE("FileEntity instance not exist %{public}" PRId64, id);
352        return ERR_INVALID_INSTANCE_CODE;
353    }
354    int ret = 0;
355    auto mode = exclusive ? LOCK_EX : LOCK_SH;
356    ret = flock(fileEntity->fd_.get()->GetFD(), static_cast<unsigned int>(mode) | LOCK_NB);
357    if (ret < 0) {
358        LOGE("Failed to try to lock file");
359        return GetErrorCode(ETXTBSY);
360    }
361    return SUCCESS_CODE;
362}
363
364int FileEntity::UnLock(int64_t id)
365{
366    auto fileEntity = FFIData::GetData<FileEntity>(id);
367    if (!fileEntity) {
368        LOGE("FileEntity instance not exist %{public}" PRId64, id);
369        return ERR_INVALID_INSTANCE_CODE;
370    }
371    int ret = 0;
372    ret = flock(fileEntity->fd_.get()->GetFD(), LOCK_UN);
373    if (ret < 0) {
374        LOGE("Failed to unlock file");
375        return GetErrorCode(ETXTBSY);
376    }
377    return SUCCESS_CODE;
378}
379
380RetDataCString FileEntity::GetParent()
381{
382    LOGI("FS_TEST::FileEntity::GetParent start");
383    RetDataCString ret = { .code = EINVAL, .data = nullptr };
384    string path(path_);
385    if (uri_.length() != 0) {
386        AppFileService::ModuleFileUri::FileUri fileUri(uri_);
387        path = fileUri.GetRealPath();
388    }
389    auto [realPathRes, realPath] = RealPathCore(path);
390    if (realPathRes != ERRNO_NOERR) {
391        LOGE("realPath error");
392        ret.code = realPathRes;
393        return ret;
394    }
395    path = string(static_cast<const char *>(realPath->ptr));
396    auto pos = path.find_last_of('/');
397    if (pos == string::npos) {
398        LOGE("Failed to split filename from path");
399        ret.code = ENOENT;
400        return ret;
401    }
402    ret.code = SUCCESS_CODE;
403    auto parent = path.substr(0, pos);
404    char* result = new(std::nothrow) char[parent.length() + 1];
405    if (result == nullptr) {
406        ret.code = ENOMEM;
407        return ret;
408    }
409    if (strcpy_s(result, parent.length() + 1, parent.c_str()) != 0) {
410        ret.code = ENOMEM;
411        delete[] (result);
412        result = nullptr;
413        return ret;
414    }
415    ret.data = result;
416    LOGI("FS_TEST::FileEntity::GetParent success");
417    return ret;
418}
419
420} // namespace FileFs
421} // namespace CJSystemapi
422} // namespace OHOS