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