diff --git a/interfaces/kits/js/src/mod_fs/properties/copy_file.cpp b/interfaces/kits/js/src/mod_fs/properties/copy_file.cpp index 71bb328b3d3d4161c803ac2b4569ecda17072ffb..9b55568905e41fd2c87c3607d468cefd1f8e098a 100644 --- a/interfaces/kits/js/src/mod_fs/properties/copy_file.cpp +++ b/interfaces/kits/js/src/mod_fs/properties/copy_file.cpp @@ -23,15 +23,84 @@ #include #include +#include "accesstoken_kit.h" +#include "bundle_mgr_client_impl.h" #include "common_func.h" +#include "distributed_file_daemon_manager.h" #include "file_utils.h" #include "filemgmt_libhilog.h" +#include "if_system_ability_manager.h" +#include "ipc_skeleton.h" +#include "iservice_registry.h" +#include "system_ability_definition.h" +#include "uri.h" +#include "uri_permission_manager_client.h" +#include "want.h" namespace OHOS { namespace FileManagement { namespace ModuleFileIO { using namespace std; using namespace OHOS::FileManagement::LibN; +using namespace OHOS::AppExecFwk; +const std::string FILE_PREFIX_NAME = "file://"; +const std::string NETWORK_PARA = "?networkid="; + +static sptr GetBundleMgr() +{ + sptr systemAbilityManager = + OHOS::SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (systemAbilityManager == nullptr) { + HILOGE("systemAbilityManager is nullptr"); + return nullptr; + } + sptr remoteObject = systemAbilityManager->GetSystemAbility(BUNDLE_MGR_SERVICE_SYS_ABILITY_ID); + if (remoteObject == nullptr) { + HILOGE("remoteObject is nullptr"); + return nullptr; + } + return iface_cast(remoteObject); +} + +static std::string GetCurrentBundleName() +{ + HILOGI("tyx::GetCurrentBundleName in."); + auto bundleMgr = GetBundleMgr(); + if (bundleMgr == nullptr) { + HILOGE("failed to get bunlde manager sa"); + return ""; + } + AppExecFwk::BundleInfo bundleInfo; + auto ret = bundleMgr->GetBundleInfoForSelf(0, bundleInfo); + HILOGI("tyx::GetCurrentBundleName GetBundleInfoForSelf end, ret = %{public}d.", ret); + HILOGI("tyx::GetCurrentBundleName GetBundleInfoForSelf end, bundleInfo.name = %{public}s.", bundleInfo.name.c_str()); + return bundleInfo.name; +} + +static bool IsValidUri(const std::string &uri) +{ + HILOGI("tyx::IsValidUri uri."); + if (uri.find(FILE_PREFIX_NAME) != 0) { + return false; + } + // 1、获取当前应用信息 + std::string currentBundleName = GetCurrentBundleName(); + // 2、检查是否为当前应用沙箱下的uri + std::string temp = uri.substr(FILE_PREFIX_NAME.size(), uri.size()); + return temp.find(currentBundleName) == 0; +} + +static bool IsRemoteUri(const std::string &uri) +{ + // NETWORK_PARA + return uri.find(NETWORK_PARA) != uri.npos; +} + +static std::string GetNetworkIdFromUri(const std::string &uri) +{ + HILOGI("tyx::GetNetworkIdFromUri uri."); + return uri.substr(uri.find(NETWORK_PARA) + NETWORK_PARA.size(), uri.size()); +} static NError IsAllPath(FileInfo& srcFile, FileInfo& destFile) { @@ -60,6 +129,40 @@ static NError IsAllPath(FileInfo& srcFile, FileInfo& destFile) return NError(ERRNO_NOERR); } +static NError CopyFileFromSoftBus(const char *remoteUri, const char *destPath) +{ + HILOGI("tyx::CopyFileFromSoftBus in."); +#if 0 + // todo 1、Uri鉴权-元能力 + auto &permissionClient = AAFwk::UriPermissionManagerClient::GetInstance(); + // bool VerifyUriPermission(const Uri &uri, uint32_t flag, uint32_t tokenId) + OHOS::Uri uri(srcFile.uri); + auto isVerified = permissionClient.VerifyUriPermission(uri, AAFwk::Want::FLAG_AUTH_READ_URI_PERMISSION, + IPCSkeleton::GetCallingTokenID()); + if (!isVerified) { + // todo uri权限校验失败,抛出错误码。 + return NError(-1); + } +#endif + // todo 2、建链-dfs_service-软总线 + auto packageName = GetCurrentBundleName(); + if (packageName.empty()) { + // todo 抛出错误码, + return NError(-1); + } + + auto networkId = GetNetworkIdFromUri(remoteUri); // 复制端的设备id,粘贴端调用此接口 + HILOGI("tyx::CopyFileFromSoftBus, networkId = %{public}s.", networkId.c_str()); + auto ret = Storage::DistributedFile::DistributedFileDaemonManager::GetInstance().PrepareSession( + remoteUri, destPath, networkId); + HILOGI("tyx::PrepareSession, ret = %{public}d.", ret); + if (ret != 0) { + // todo 建联过程中的错误,抛出错误码 + return NError(-1); + } + return NError(ERRNO_NOERR); +} + static NError SendFileCore(FileInfo& srcFdg, FileInfo& destFdg, struct stat& statbf) { std::unique_ptr sendfile_req = { @@ -161,6 +264,32 @@ static NError OpenFile(FileInfo& srcFile, FileInfo& destFile) return SendFileCore(srcFile, destFile, statbf); } +static tuple GetModeFromOptionArg(napi_env env, const NFuncArg& funcArg) +{ + int32_t mode = 0; + bool succ = false; + if (funcArg.GetArgc() >= NARG_CNT::THREE) { + NVal op(env, funcArg[NARG_POS::THIRD]); + + if (op.HasProp("mode") && !op.GetProp("mode").TypeIs(napi_undefined)) { + tie(succ, mode) = op.GetProp("mode").ToInt32(); + if (!succ || mode != 0) { + HILOGE("Illegal options.mode parameter"); + return { false, mode, NVal() }; + } + } + + if (op.HasProp("progressListener") && !op.GetProp("progressListener").TypeIs(napi_undefined)) { + if (!op.GetProp("progressListener").TypeIs(napi_function)) { + HILOGE("Illegal options.progressListener type"); + return { false, mode, NVal() }; + } + return { true, mode, op.GetProp("progressListener") }; + } + } + return { true, mode, NVal() }; +} + static tuple ParseJsMode(napi_env env, const NFuncArg& funcArg) { bool succ = false; @@ -174,10 +303,47 @@ static tuple ParseJsMode(napi_env env, const NFuncArg& funcArg) return { true, mode }; } +static bool IsDirectory(char* path) +{ + struct stat buf; + int ret = stat(path, &buf); + if (ret == -1) { + HILOGI("tyx::stat failed, errno is %{public}d, ", errno); + return false; + } + return (buf.st_mode & S_IFMT) == S_IFDIR; +} + +static bool IsFile(char* path) +{ + struct stat buf; + int ret = stat(path, &buf); + if (ret == -1) { + HILOGI("tyx::stat failed, errno is %{public}d, ", errno); + return false; + } + return (buf.st_mode & S_IFMT) == S_IFREG; +} + +static bool CheckValidParam(FileInfo &src, FileInfo &dest) +{ + if (src.isPath && dest.isPath) { + return IsValidUri(src.path.get()) ? IsRemoteUri(src.path.get()) && IsDirectory(dest.path.get()) + : IsFile(src.path.get()) && IsFile(dest.path.get()); + } + + if (src.isPath) { + return IsFile(src.path.get()); + } + return dest.isPath ? IsFile(dest.path.get()) : true; +} + static tuple ParseJsOperand(napi_env env, NVal pathOrFdFromJsArg) { + HILOGI("tyx::ParseJsOperand in."); auto [isPath, path, ignore] = pathOrFdFromJsArg.ToUTF8String(); if (isPath) { + HILOGI("tyx::isPath. path = %{public}s", path.get()); return { true, FileInfo { true, move(path), {} } }; } @@ -191,9 +357,8 @@ static tuple ParseJsOperand(napi_env env, NVal pathOrFdFromJsArg } return { true, FileInfo { false, {}, move(fdg) } }; } - return { false, FileInfo { false, {}, {} } }; -}; +} napi_value CopyFile::Sync(napi_env env, napi_callback_info info) { @@ -237,6 +402,7 @@ napi_value CopyFile::Sync(napi_env env, napi_callback_info info) napi_value CopyFile::Async(napi_env env, napi_callback_info info) { + HILOGI("tyx::CopyFile::Async in."); NFuncArg funcArg(env, info); if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::FOUR)) { HILOGE("Number of arguments unmatched"); @@ -245,7 +411,9 @@ napi_value CopyFile::Async(napi_env env, napi_callback_info info) } auto [succSrc, src] = ParseJsOperand(env, { env, funcArg[NARG_POS::FIRST] }); + HILOGI("tyx::CopyFile::Async ParseJsOperand src end."); auto [succDest, dest] = ParseJsOperand(env, { env, funcArg[NARG_POS::SECOND] }); + HILOGI("tyx::CopyFile::Async ParseJsOperand dest end."); if (!succSrc || !succDest) { HILOGE("The first/second argument requires filepath/fd"); NError(EINVAL).ThrowErr(env); @@ -253,8 +421,36 @@ napi_value CopyFile::Async(napi_env env, napi_callback_info info) } auto [succMode, mode] = ParseJsMode(env, funcArg); + HILOGI("tyx::CopyFile::Async ParseJsOperand mode end."); if (!succMode) { HILOGE("Failed to convert mode to int32"); + auto [succOptions, mode, listener] = GetModeFromOptionArg(env, funcArg); + if (!succOptions) { + HILOGE("Failed to convert options"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + // 注册listener + void *nativeObject = nullptr; + napi_status status = napi_unwrap(env, funcArg.GetThisVar(), &nativeObject);//js对象转换为c++对象 + if (status != napi_ok) { + HILOGE("Cannot umwarp for pointer: %d", status); + NError(ENOMEM).ThrowErr(env); + return nullptr; + } + std::shared_ptr callback = + std::make_shared(env, listener); + + if (callback == nullptr) { + return nullptr; + } + auto copyFileObject = reinterpret_cast(nativeObject); + copyFileObject->RegisterListener(1, callback); + } + + bool validParam = CheckValidParam(src, dest); + if (!validParam) { + HILOGE("tyx::Param is not valid."); NError(EINVAL).ThrowErr(env); return nullptr; } @@ -266,6 +462,9 @@ napi_value CopyFile::Async(napi_env env, napi_callback_info info) return nullptr; } auto cbExec = [para]() -> NError { + if (IsRemoteUri(para->src_.path.get()) && IsDirectory(para->dest_.path.get())) { + return CopyFileFromSoftBus(para->src_.path.get(), para->dest_.path.get()); + } if (para->src_.isPath && para->dest_.isPath) { return IsAllPath(para->src_, para->dest_); } @@ -288,6 +487,114 @@ napi_value CopyFile::Async(napi_env env, napi_callback_info info) return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_COPYFILE_NAME, cbExec, cbCompl).val_; } } + +static void CallbackComplete(uv_work_t *work, int stat) +{ + if (work == nullptr) { + HILOGE("Failed to get uv_queue_work pointer"); + HILOGE("Failed to get uv_queue_work pointer"); + return; + } + + CopyFile::JSCallbackContext *callbackContext = + reinterpret_cast(work->data); + do { + if (callbackContext == nullptr) { + HILOGE("Failed to create context pointer"); + break; + } + if (!callbackContext->ref_) { + HILOGE("Failed to get nref reference"); + break; + } + napi_handle_scope scope = nullptr; + napi_status status = napi_open_handle_scope(callbackContext->env_, &scope); + if (status != napi_ok) { + HILOGE("Failed to open handle scope, status: %{public}d", status); + break; + } + napi_env env = callbackContext->env_; + napi_value jsCallback = callbackContext->ref_.Deref(env).val_; + NVal objn = NVal::CreateObject(env); + objn.AddProp("totalSize", NVal::CreateBigUint64(env, callbackContext->bytesTotal_).val_); + objn.AddProp("reveivedSize", NVal::CreateBigUint64(env, callbackContext->bytesUpload_).val_); + napi_value retVal = nullptr; + status = napi_call_function(env, nullptr, jsCallback, 1, &(objn.val_), &retVal); + if (status != napi_ok) { + HILOGE("Failed to call napi_call_function, status: %{public}d", status); + } + status = napi_close_handle_scope(callbackContext->env_, scope); + if (status != napi_ok) { + HILOGE("Failed to close handle scope, status: %{public}d", status); + } + } while (0); + delete callbackContext; + delete work; +} + +void CopyFile::RegisterListener(int sessionld, std::shared_ptr callback) +{ + HILOGE("RegisterListener %d{public}s", sessionld); + std::lock_guard lock(mutex_); + if (jsCbMap_.find(sessionld) == jsCbMap_.end()) { + HILOGE("session %d{public}s not registered!", sessionld); + } + HILOGE("Add %d{public}s callbackObj into jsCbMap_", sessionld); + jsCbMap_.insert(make_pair(sessionld, callback)); +} + +void CopyFile::UnRegisterListener(int sessionld) +{ + HILOGE("UnRegisterListener %d{public}s", sessionld); + std::lock_guard lock(mutex_); + if (jsCbMap_.find(sessionld) == jsCbMap_.end()) { + HILOGE("session %d{public}s not unRegistered!", sessionld); + return; + } + jsCbMap_.erase(sessionld); +} + +void CopyFile::OnFileReceive(int sessionld, uint64_t bytesTotal, uint64_t bytesUpload) +{ + auto iter = jsCbMap_.find(sessionld); + if (iter == jsCbMap_.end()) { + HILOGE("Failed to get session id map"); + return ; + } + auto callback = iter->second; + + uv_loop_s *loop = nullptr; + napi_get_uv_event_loop(callback->env_, &loop); + if (loop == nullptr) { + HILOGE("Failed to get uv event loop"); + return; + } + + uv_work_t *work = new (std::nothrow) uv_work_t; + if (work == nullptr) { + HILOGE("Failed to create uv_work_t pointer"); + return; + } + + if (!callback) { + HILOGE("Failed to parse watcher callback"); + delete work; + return; + } + + callback->bytesTotal_ = bytesTotal; + callback->bytesUpload_ = bytesUpload; + auto jsCallback = callback.get(); + work->data = jsCallback; + + int ret = uv_queue_work_with_qos( + loop, work, [](uv_work_t *work) {}, reinterpret_cast(CallbackComplete), + uv_qos_user_initiated); + if (ret != 0) { + HILOGE("Failed to execute libuv work queue, ret: %{public}d", ret); + delete work; + } +} } // namespace ModuleFileIO } // namespace FileManagement -} // namespace OHOS +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/copy_file.h b/interfaces/kits/js/src/mod_fs/properties/copy_file.h index 2801c4a43a9793bab23a910d6b0e41d78b9c9ab1..fdd7c629466e1b1f716445a8f67862c585eef9ab 100755 --- a/interfaces/kits/js/src/mod_fs/properties/copy_file.h +++ b/interfaces/kits/js/src/mod_fs/properties/copy_file.h @@ -15,18 +15,36 @@ #ifndef INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_COPY_FILE_H #define INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_COPY_FILE_H +#include +#include #include "common_func.h" #include "filemgmt_libn.h" +#include "file_receive_listener.h" namespace OHOS { namespace FileManagement { namespace ModuleFileIO { +using namespace OHOS::FileManagement::LibN; using namespace std; -class CopyFile final { +class CopyFile final : public FileReceiveListener { public: + class JSCallbackContext { + public: + explicit JSCallbackContext(napi_env env, NVal val) : env_(env), ref_(val) {} + ~JSCallbackContext() {} + napi_env env_; + NRef ref_; + uint64_t bytesTotal_ = 0; + uint64_t bytesUpload_ = 0; + }; static napi_value Async(napi_env env, napi_callback_info info); static napi_value Sync(napi_env env, napi_callback_info info); + void RegisterListener(int sessionld, std::shared_ptr callback); + void UnRegisterListener(int sessionld); + void OnFileReceive(int sessionld, uint64_t bytesTotal, uint64_t bytesUpload) override; + std::map> jsCbMap_; + std::recursive_mutex mutex_; }; class Para { diff --git a/utils/filemgmt_libn/include/n_val.h b/utils/filemgmt_libn/include/n_val.h index f721a8912ea7c1451f399fef128b9dbdbe026a32..cb16fc3be311bae0bbc8df0d4078020976938ccc 100644 --- a/utils/filemgmt_libn/include/n_val.h +++ b/utils/filemgmt_libn/include/n_val.h @@ -66,6 +66,7 @@ public: static NVal CreateInt64(napi_env env, int64_t val); static NVal CreateInt32(napi_env env, int32_t val); static NVal CreateUint32(napi_env env, int32_t val); + static NVal CreateBigUint64(napi_env env, uint64_t val); static NVal CreateObject(napi_env env); static NVal CreateBool(napi_env env, bool val); static NVal CreateUTF8String(napi_env env, std::string str); diff --git a/utils/filemgmt_libn/src/n_val.cpp b/utils/filemgmt_libn/src/n_val.cpp index 91e3f9afb2571a5c0855e0f3f49716368bc6ff8e..a8bb1997c48748c831687930eb59f8ae7a22756b 100644 --- a/utils/filemgmt_libn/src/n_val.cpp +++ b/utils/filemgmt_libn/src/n_val.cpp @@ -321,6 +321,13 @@ NVal NVal::CreateUint32(napi_env env, int32_t val) return {env, res}; } +NVal NVal::CreateBigUint64(napi_env env, uint64_t val) +{ + napi_value res = nullptr; + napi_create_bigint_uint64(env, val, &res); + return { env, res }; +} + NVal NVal::CreateObject(napi_env env) { napi_value res = nullptr;