From 7a251cc656cea350e8af4e6bb694d659cbdf18f3 Mon Sep 17 00:00:00 2001 From: zhangsaiyang Date: Mon, 27 Nov 2023 21:12:48 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9B=E5=BA=A6=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhangsaiyang --- bundle.json | 5 +- file_api.gni | 1 + interfaces/kits/js/BUILD.gn | 8 + .../kits/js/src/mod_fs/properties/copy.cpp | 822 ++++++++++++++++++ .../kits/js/src/mod_fs/properties/copy.h | 136 +++ 5 files changed, 970 insertions(+), 2 deletions(-) create mode 100644 interfaces/kits/js/src/mod_fs/properties/copy.cpp create mode 100644 interfaces/kits/js/src/mod_fs/properties/copy.h diff --git a/bundle.json b/bundle.json index 46dd10022..dd6270ec6 100644 --- a/bundle.json +++ b/bundle.json @@ -31,14 +31,15 @@ "common_event_service", "c_utils", "data_share", + "device_manager", + "dfs_service", "eventhandler", "hilog", "ipc", "napi", "samgr", "app_file_service", - "build_framework", - "os_account" + "build_framework" ], "third_party": [ "bounds_checking_function", diff --git a/file_api.gni b/file_api.gni index d843a2cc7..65ca0707c 100644 --- a/file_api.gni +++ b/file_api.gni @@ -18,6 +18,7 @@ file_api_path = "//foundation/filemanagement/file_api" hiviewdfx_hilog_path = "//base/hiviewdfx/hilog" src_path = "${file_api_path}/interfaces/kits/js/src" utils_path = "${file_api_path}/utils" +filemanagement_service_path = "//foundation/filemanagement/dfs_service/services" use_mac = "${current_os}_${current_cpu}" == "mac_x64" || "${current_os}_${current_cpu}" == "mac_arm64" diff --git a/interfaces/kits/js/BUILD.gn b/interfaces/kits/js/BUILD.gn index 08089732b..1c75faf82 100644 --- a/interfaces/kits/js/BUILD.gn +++ b/interfaces/kits/js/BUILD.gn @@ -108,6 +108,7 @@ ohos_shared_library("fileio") { external_deps = [ "hilog:libhilog", "napi:ace_napi", + "dfs_service:distributed_file_daemon_kit_inner", ] defines = [ "OPENSSL_SUPPRESS_DEPRECATED" ] } @@ -119,6 +120,7 @@ ohos_shared_library("fs") { relative_install_dir = "module/file" include_dirs = [ + "${filemanagement_service_path}/distributedfiledaemon/include/ipc", "${src_path}/common", "${src_path}/common/file_helper", "${src_path}/mod_fs", @@ -186,6 +188,7 @@ ohos_shared_library("fs") { "src/mod_fs/class_stream/stream_n_exporter.cpp", "src/mod_fs/class_watcher/watcher_entity.cpp", "src/mod_fs/class_watcher/watcher_n_exporter.cpp", + "src/mod_fs/properties/copy.cpp", "src/mod_fs/properties/copy_file.cpp", "src/mod_fs/properties/copydir.cpp", "src/mod_fs/properties/create_randomaccessfile.cpp", @@ -202,9 +205,12 @@ ohos_shared_library("fs") { "src/mod_fs/properties/watcher.cpp", ] external_deps += [ + "ability_base:want", "ability_base:zuri", "ability_runtime:ability_manager", "ability_runtime:abilitykit_native", + "ability_runtime:uri_permission_mgr", + "access_token:libaccesstoken_sdk", "access_token:libtokenid_sdk", "app_file_service:fileuri_native", "bundle_framework:appexecfwk_base", @@ -212,6 +218,8 @@ ohos_shared_library("fs") { "c_utils:utils", "data_share:datashare_common", "data_share:datashare_consumer", + "device_manager:devicemanagersdk", + "dfs_service:distributed_file_daemon_kit_inner", "ipc:ipc_core", "samgr:samgr_proxy", ] diff --git a/interfaces/kits/js/src/mod_fs/properties/copy.cpp b/interfaces/kits/js/src/mod_fs/properties/copy.cpp new file mode 100644 index 000000000..49dffc2c4 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/copy.cpp @@ -0,0 +1,822 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "copy.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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::AppExecFwk; +const std::string FILE_PREFIX_NAME = "file://"; +const std::string NETWORK_PARA = "?networkid="; +constexpr int DISMATCH = 0; +constexpr int MATCH = 1; +constexpr int BUF_SIZE = 1024; +bool Copy::run_ = false; +//std::thread Copy::notifyEventHandler_; +std::recursive_mutex Copy::mutex_; +std::map> Copy::jsCbMap_; +//std::map> Copy::fileVisitFlags_; + +struct ConflictFilesInfo { + std::string srcFiles; + std::string destFiles; + ConflictFilesInfo(const std::string &src, const std::string &dest) : srcFiles(src), destFiles(dest) {} + ~ConflictFilesInfo() = default; +}; + +enum CopyDirMode { DIRMODE_FILE_COPY_THROW_ERR = 0, DIRMODE_FILE_COPY_REPLACE }; + +static int RecurCopyDir(const string &srcPath, const string &destPath, std::shared_ptr infos); + +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() +{ + auto bundleMgr = GetBundleMgr(); + if (bundleMgr == nullptr) { + HILOGE("failed to get bunlde manager sa"); + return ""; + } + AppExecFwk::BundleInfo bundleInfo; + // todo 0 means what? + auto ret = bundleMgr->GetBundleInfoForSelf(0, bundleInfo); + if (ret != 0) { + return ""; + } + 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 tuple ParseJsOperand(napi_env env, NVal pathOrFdFromJsArg) +{ + HILOGI("tyx::ParseJsOperand in."); + auto [succ, uri, ignore] = pathOrFdFromJsArg.ToUTF8String(); + HILOGI("tyx::ParseJsOperand uri ptr = %{public}p.", uri.get()); + std::string uriStr = std::string(uri.get()); + if (!succ) { + HILOGE("tyx::parse uri failed. uri = %{public}s", uriStr.c_str()); + return { false, uriStr }; + } + HILOGI("tyx::parse uri end. uri = %{public}s", uriStr.c_str()); + if (IsValidUri(uriStr)) { + return { true, uriStr }; + } + return { false, uriStr }; +} + +static std::string ConvertUriToPath(const std::string &uri) +{ + HILOGI("tyx::ConvertUriToPath uri = %{public}s", uri.c_str()); + if (!IsValidUri(uri)) { + return ""; + } + std::string currentBundleName = GetCurrentBundleName(); + std::string temp = uri.substr(FILE_PREFIX_NAME.size(), uri.size()); + return temp.substr(currentBundleName.size(), temp.size()); +} + +static tuple GetListenerFromOptionArg(napi_env env, const NFuncArg &funcArg) +{ + if (funcArg.GetArgc() >= NARG_CNT::THREE) { + NVal op(env, funcArg[NARG_POS::THIRD]); + + if (op.HasProp("progressListener") && !op.GetProp("progressListener").TypeIs(napi_undefined)) { + if (!op.GetProp("progressListener").TypeIs(napi_function)) { + HILOGE("Illegal options.progressListener type"); + return { false, NVal() }; + } + HILOGI("tyx::GetListenerFromOptionArg end, get listener"); + return { true, op.GetProp("progressListener") }; + } + } + return { true, NVal() }; +} + +static bool IsRemoteUri(const std::string &uri) +{ + // NETWORK_PARA + return uri.find(NETWORK_PARA) != uri.npos; +} + +static bool IsDirectory(const std::string &path) +{ + HILOGI("tyx::IsDirectory in, path = %{public}s, ", path.c_str()); + struct stat buf; + int ret = stat(path.c_str(), &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(const std::string &path) +{ + struct stat buf; + int ret = stat(path.c_str(), &buf); + HILOGI("tyx::IsFile in, path = %{public}s, ret = %{public}d", path.c_str(), ret); + if (ret == -1) { + HILOGI("tyx::stat failed, errno is %{public}d, ", errno); + return false; + } + return (buf.st_mode & S_IFMT) == S_IFREG; +} + +static uint64_t GetFileSize(const std::string &path) +{ + struct stat buf; + int ret = stat(path.c_str(), &buf); + if (ret == -1) { + HILOGI("tyx::stat failed, errno is %{public}d, ", errno); + return false; + } + HILOGI("tyx::GetFileSize in, path = %{public}s, size = %{public}lld", path.c_str(), buf.st_size); + return buf.st_size; +} + +static std::string GetDirNameFromDirPath(std::string dirPath) +{ + auto size = dirPath.rfind("/"); + if (size == dirPath.npos) { + return ""; + } + return dirPath.substr(size); +} + +static uint64_t GetDirSize(std::shared_ptr infos, std::string path) +{ + auto basicDirName = GetDirNameFromDirPath(path); + DIR *dir; + struct dirent *entry; + struct stat st; + long int size = 0; + + dir = opendir(path.c_str()); + if (dir == nullptr) { + HILOGE("tyx::opendir failed, errno is %{public}d, ", errno); + return size; + } + + while ((entry = readdir(dir)) != nullptr) { + auto tempPath = path + "/" + entry->d_name; + if (stat(tempPath.c_str(), &st) == -1) { + closedir(dir); + return size; + } + if (S_ISDIR(st.st_mode)) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + size += GetDirSize(infos, path); + } else { + size += st.st_size; + } + } + + closedir(dir); + HILOGI("tyx::GetDirSize in, path = %{public}s, size = %{public}ld", path.c_str(), size); + return size; +} + +static bool CheckValidParam(const std::string &srcUri, const std::string &destUri) +{ + auto srcPath = ConvertUriToPath(srcUri); // todo 这个只能检查本地的,不能检查远端uri。远端uri是文件还是目录,不好校验 + auto destPath = ConvertUriToPath(destUri); + if (IsDirectory(srcPath)) { + return IsDirectory(destPath); + } + if (IsFile(srcPath)) { + return !IsDirectory(destPath); + } + return false; +} + +static int CopyFile(const string &src, const string &dest) +{ + HILOGI("tyx::CopyFile, src = %{public}s, dest = %{public}s", src.c_str(), dest.c_str()); + filesystem::path dstPath(dest); + filesystem::path srcPath(src); + std::error_code errCode; + if (!filesystem::copy_file(srcPath, dstPath, filesystem::copy_options::overwrite_existing, errCode)) { + HILOGE("Failed to copy file, error code: %{public}d", errCode.value()); + return errCode.value(); + } + return ERRNO_NOERR; +} + +static int MakeDir(const string &path) +{ + filesystem::path destDir(path); + std::error_code errCode; + if (!filesystem::create_directory(destDir, errCode)) { + HILOGE("Failed to create directory, error code: %{public}d", errCode.value()); + return errCode.value(); + } + return ERRNO_NOERR; +} + +static int CopySubDir(const string &srcPath, const string &destPath, std::shared_ptr infos) +{ + if (!filesystem::exists(destPath)) { + int res = MakeDir(destPath); + if (res != ERRNO_NOERR) { + HILOGE("Failed to mkdir"); + return res; + } + } + uint32_t watchEvents = IN_MODIFY; + int newWd = inotify_add_watch(infos->notifyFd, destPath.c_str(), watchEvents); + HILOGI("tyx::inotify_add_watch, newWd: %{public}d", newWd); + { + std::lock_guard lock(Copy::mutex_); + auto iter = Copy::jsCbMap_.find(*infos); + // std::vector>> wds; + // std::vector>> wds; + std::shared_ptr receiveInfo = std::make_shared(); + receiveInfo->path = destPath; + iter->second->wds.push_back({ newWd, receiveInfo }); + HILOGI("tyx::inotify_add_watch, wds.push_back, newWd: %{public}d, destPath = %{public}s", newWd, + destPath.c_str()); + } + return RecurCopyDir(srcPath, destPath, infos); +} + +static int FilterFunc(const struct dirent *filename) +{ + if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") { + return DISMATCH; + } + return MATCH; +} + +struct NameList { + struct dirent **namelist = { nullptr }; + int direntNum = 0; +}; + +static void Deleter(struct NameList *arg) +{ + for (int i = 0; i < arg->direntNum; i++) { + free((arg->namelist)[i]); + (arg->namelist)[i] = nullptr; + } + free(arg->namelist); +} + +static int RecurCopyDir(const string &srcPath, const string &destPath, std::shared_ptr infos) +{ + unique_ptr pNameList = { new (nothrow) struct NameList, Deleter }; + if (pNameList == nullptr) { + HILOGE("Failed to request heap memory."); + return ENOMEM; + } + int num = scandir(srcPath.c_str(), &(pNameList->namelist), FilterFunc, alphasort); + pNameList->direntNum = num; + + for (int i = 0; i < num; i++) { + string src = srcPath + '/' + string((pNameList->namelist[i])->d_name); + string dest = destPath + '/' + string((pNameList->namelist[i])->d_name); + auto ret = ((pNameList->namelist[i])->d_type == DT_DIR) ? CopySubDir(src, dest, infos) : CopyFile(src, dest); + if (ret != ERRNO_NOERR) { + HILOGE("Failed to copy, error is %{public}d", ret); + return ret; + } + } + return ERRNO_NOERR; +} + +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 int CopyDirFunc(const string &src, const string &dest, std::shared_ptr infos) +{ + HILOGI("tyx::CopyDirFunc in, src = %{public}s, dest = %{public}s", src.c_str(), dest.c_str()); + size_t found = string(src).rfind('/'); + if (found == std::string::npos) { + return EINVAL; + } + string dirName = string(src).substr(found); + string destStr = dest + dirName; + return CopySubDir(src, destStr, infos); +} + +static NError CopyFileFromSoftBus(std::string srcUri, std::string destUri, sptr transListener) +{ + auto networkId = GetNetworkIdFromUri(srcUri); + HILOGI("CopyFileFromSoftBus, networkId = %{public}s.", networkId.c_str()); + auto ret = Storage::DistributedFile::DistributedFileDaemonManager::GetInstance().PrepareSession( + srcUri.c_str(), destUri.c_str(), networkId, transListener); + HILOGI("tPrepareSession, ret = %{public}d.", ret); + std::unique_lock lock(transListener->cvMutex_); + transListener->cv_.wait(lock, [&transListener]() { + return transListener->copyEvent_ == SUCCESS || transListener->copyEvent_ == FAILED;}); + if (transListener->copyEvent_ == FAILED){ + return NError(-1); + } + return NError(ERRNO_NOERR); +} + +NError Copy::SubscribeLocalListener(napi_env env, std::shared_ptr para, std::shared_ptr infos) +{ + if (para->listener_.TypeIs(napi_function)) { + HILOGI("tyx::has listener."); + para->notifyFd_ = inotify_init(); + if (para->notifyFd_ < 0) { + HILOGE("Failed to init notify errCode:%{public}d", errno); + // todo 抛出错误码 + return NError(EINVAL); + } + // uint32_t watchEvents = IN_MODIFY | IN_ATTRIB | IN_MOVED_TO | IN_CREATE; + uint32_t watchEvents = IN_MODIFY; + int newWd = inotify_add_watch(para->notifyFd_, para->destPath_.c_str(), watchEvents); + if (newWd < 0) { + HILOGE("tyx::Failed to start notify errCode:%{public}d", errno); + // todo 抛出正确的异常 + return NError(EINVAL); + } + infos->srcPath = para->srcPath_; + infos->destPath = para->destPath_; + infos->isFile = IsFile(para->srcPath_); + infos->notifyFd = para->notifyFd_; + infos->wd = newWd; + HILOGI("tyx::Get notifyFd_, notifyFd_: %{public}d, destPath: %{public}s", para->notifyFd_, + infos->destPath.c_str()); + std::shared_ptr receiveInfo = std::make_shared(); + receiveInfo->path = infos->destPath; + if (infos->isFile) { + receiveInfo->isFile = true; + infos->totalSize = GetFileSize(infos->srcPath); + } else { + infos->totalSize = GetDirSize(infos, infos->srcPath); + } + std::shared_ptr callback = std::make_shared(env, para->listener_); + callback->wds.push_back({ infos->wd, receiveInfo }); + HILOGE("tyx::SubscribeLocalListener inotify_add_watch in callback, wd:%{public}d", infos->wd); + callback->totalSize = infos->totalSize; + RegisterListener(infos, callback); + } + return NError(ERRNO_NOERR); +} + +void Copy::RegisterListener(std::shared_ptr infos, std::shared_ptr callback) +{ + HILOGI("tyx::Copy::RegisterListener in. srcUri is %{public}s, destUri is %{public}s", infos->srcUri.c_str(), + infos->destUri.c_str()); + std::lock_guard lock(mutex_); + auto iter = jsCbMap_.find(*infos); + if (iter != jsCbMap_.end()) { + HILOGI("tyx::Copy::RegisterListener, already registered."); + return; + } + HILOGI("tyx::Copy::RegisterListener, first time to register."); + jsCbMap_.insert({ *infos, callback }); + HILOGI("tyx::Copy::RegisterListener, insert map."); + return; +} + +void Copy::UnregisterListener(std::shared_ptr infos) +{ + if (infos == nullptr) { + HILOGI("tyx::Copy::UnregisterListener, infos is nullptr"); + } + HILOGI("tyx::Copy::UnregisterListener in. srcUri is %{public}s, destUri is %{public}s", infos->srcUri.c_str(), + infos->destUri.c_str()); + std::lock_guard lock(mutex_); + auto iter = jsCbMap_.find(*infos); + if (iter == jsCbMap_.end()) { + HILOGI("tyx::Copy::UnregisterListener, it is not be registered."); + return; + } + jsCbMap_.erase(*infos); + HILOGI("tyx::Copy::UnregisterListener end."); +} + +static void ReceiveComplete(uv_work_t *work, int stat) +{ + if (work == nullptr) { + HILOGE("uv_work_t pointer is nullptr."); + return; + } + + std::shared_ptr entry(static_cast(work->data), [work](UvEntry *data) { + delete data; + delete work; + }); + if (entry == nullptr) { + HILOGE("entry pointer is nullptr."); + return; + } + + napi_handle_scope scope = nullptr; + napi_env env = entry->callback->env; + napi_status status = napi_open_handle_scope(env, &scope); + if (status != napi_ok) { + HILOGE("Failed to open handle scope, status: %{public}d.", status); + return; + } + HILOGI("tyx::ReceiveComplete, entry->bytesUpload = %{public}llu, entry->bytesTotal = %{public}llu", + entry->bytesUpload, entry->bytesTotal); + NVal obj = NVal::CreateObject(env); + obj.AddProp("processedSize", NVal::CreateInt32(env, entry->bytesUpload).val_); // todo 暂时改为uint32,后面改为uint64 + obj.AddProp("totalSize", NVal::CreateInt32(env, entry->bytesTotal).val_); + + napi_value result = nullptr; + napi_value jsCallback = entry->callback->nRef.Deref(env).val_; + status = napi_call_function(env, nullptr, jsCallback, 1, &(obj.val_), &result); + if (status != napi_ok) { + HILOGE("Failed to get result, status: %{public}d.", status); + } + if (entry->bytesUpload == entry->bytesTotal) { + Copy::UnregisterListener(entry->fileInfos); + } + status = napi_close_handle_scope(env, scope); + if (status != napi_ok) { + HILOGE("Failed to close scope, status: %{public}d.", status); + } +} + +void Copy::OnFileReceive(std::shared_ptr infos) +{ + HILOGI("tyx::Copy::OnFileReceive in."); + uv_work_t *work = new (std::nothrow) uv_work_t; + if (work == nullptr) { + HILOGE("Failed to create uv_work_t pointer"); + return; + } + napi_env env; + uv_loop_s *loop = nullptr; + std::shared_ptr callback = nullptr; + { + std::lock_guard lock(mutex_); + auto iter = jsCbMap_.find(*infos); + if (iter == jsCbMap_.end()) { + HILOGE("Failed to find callback"); + return; + } + callback = iter->second; + } + UvEntry *entry = new (std::nothrow) UvEntry(callback, infos); + if (entry == nullptr) { + HILOGE("entry ptr is nullptr."); + return; + } + entry->bytesUpload = callback->progressSize; + entry->bytesTotal = callback->totalSize; + env = callback->env; + HILOGI("tyx::Copy::OnFileReceive, entry->bytesUpload = %{public}llu, entry->bytesTotal = %{public}llu", + entry->bytesUpload, entry->bytesTotal); + work->data = entry; + napi_get_uv_event_loop(env, &loop); + uv_queue_work( + loop, work, [](uv_work_t *work) {}, reinterpret_cast(ReceiveComplete)); +} + +// todo 放在其他线程去执行uv_loop中, 这里没校验env +void Copy::GetNotifyEvent(std::shared_ptr infos) +{ + HILOGI("tyx::GetNotifyEvent in."); + if (run_) { + return; + } + run_ = true; + char buf[BUF_SIZE] = { 0 }; + struct inotify_event *event = nullptr; + fd_set fds; + FD_ZERO(&fds); + FD_SET(infos->notifyFd, &fds); + HILOGI("tyx::GetNotifyEvent fds set."); + while (run_) { + if (infos->notifyFd < 0) { + HILOGE("Failed to run Listener Thread because notifyFd_:%{public}d", infos->notifyFd); + break; + } + HILOGI("tyx::GetNotifyEvent infos->notifyFd = %{public}d.", infos->notifyFd); + auto ret = select(infos->notifyFd + 1, &fds, nullptr, nullptr, nullptr); + HILOGI("tyx::GetNotifyEvent select, ret = %{public}d.", ret); + if (ret > 0) { + HILOGI("tyx::GetNotifyEvent select in."); + int len, index = 0; + while (((len = read(infos->notifyFd, &buf, sizeof(buf))) < 0) && (errno == EINTR)) { + }; + string tempFileName; + while (index < len) { + event = reinterpret_cast(buf + index); + HILOGI("tyx::GetNotifyEvent select in event->wd = %{public}d.", event->wd); + // todo 这个锁的范围太大了 + std::lock_guard lock(mutex_); + auto iter = jsCbMap_.find(*infos); + auto wds = iter->second->wds; + auto it = wds.begin(); + for (; it != wds.end(); ++it) { + if (it->first == event->wd) { + tempFileName = it->second->path; // todo 文件拷贝,tempFileName是文件名; 目录拷贝,tempFileName是目录名。当前未区分,按照目录处理 + HILOGI("tyx::GetNotifyEvent select in tempFileName = %{public}s.", tempFileName.c_str()); + break; + } + } + if (it == wds.end()) { + break; + } + string fileName = tempFileName; + if (event->len > 0) { // 文件夹下的文件处理 + HILOGI("tyx::file receive, event->name is %{public}s", event->name); + fileName += "/" + string(event->name); + auto file = it->second->fileList.find(fileName); + auto size = GetFileSize(fileName); + if (file == it->second->fileList.end()) { + it->second->fileList.insert({fileName, size}); + iter->second->progressSize += size; + } else { + iter->second->progressSize += (size - file->second); + file->second = size; + } + HILOGI("tyx::file receive, event->len > 0, infos->progressSize is %{public}llu", iter->second->progressSize); + } else { + HILOGI("tyx::GetNotifyEvent select in fileName = %{public}s.", fileName.c_str()); +// auto size = GetFileSize(fileName); +// iter->second->progressSize += (size - it->second->fileSize); +// it->second->fileSize = size; + iter->second->progressSize = GetFileSize(fileName); + HILOGI("tyx::file receive, infos->progressSize is %{public}llu", iter->second->progressSize); + } + OnFileReceive(infos); + if (iter->second->progressSize == iter->second->totalSize) { + HILOGI("tyx::file receive end. callback->progressSize == callback.totalSize"); + run_ = false; + for (auto item : iter->second->wds) { + HILOGI("tyx::file inotify_rm_watch. wd = %{public}d", item.first); + inotify_rm_watch(infos->notifyFd, item.first); + } + // inotify_rm_watch(infos->notifyFd, infos->wd); + HILOGI("tyx::close notifyFd, notifyFd = %{public}d", infos->notifyFd); + close(infos->notifyFd); + } + index += sizeof(struct inotify_event) + event->len; + } + } + HILOGI("tyx::GetNotifyEvent 584."); + } +} + +napi_value Copy::Async(napi_env env, napi_callback_info info) +{ + HILOGI("tyx::Copy::Async in."); + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::FOUR)) { + HILOGE("Number of arguments unmatched"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + auto [succSrc, srcUri] = ParseJsOperand(env, { env, funcArg[NARG_POS::FIRST] }); + // HILOGI("tyx::CopyFile::Async ParseJsOperand src end."); + auto [succDest, destUri] = 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); + return nullptr; + } + sptr transListener = new (std::nothrow) TransListener; + auto [succOptions, listener] = GetListenerFromOptionArg(env, funcArg); + transListener->callback_ = std::make_shared(env, listener); + if (!succOptions) { + HILOGE("Failed to convert listener."); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + if (!IsRemoteUri(srcUri) && !CheckValidParam(srcUri, destUri)) { + HILOGE("type of srcUri or destUri is wrong."); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + auto para = CreateSharedPtr(srcUri, destUri, listener, funcArg.GetThisVar()); + para->srcPath_ = ConvertUriToPath(para->srcUri_); + para->destPath_ = ConvertUriToPath(para->destUri_); + if (para == nullptr) { + HILOGE("Failed to request heap memory."); + NError(ENOMEM).ThrowErr(env); + return nullptr; + } + std::shared_ptr infos = std::make_shared(); + infos->srcUri = para->srcUri_; + infos->destUri = para->destUri_; + if (IsRemoteUri(para->srcUri_)) { + // todo register listener + } else { + // infos插入到map中当作key值,key值是不可以变化的,所以Infos不能再变了,只能变callbackObject + SubscribeLocalListener(env, para, infos); + } + auto cbExec = [para, env, &funcArg, infos, transListener]() -> NError { + if (IsRemoteUri(para->srcUri_)) { + // copyRemoteUri + auto ret = CopyFileFromSoftBus(para->srcUri_, para->destUri_, transListener); + return ret; + } + if (infos->notifyFd != -1) { + std::thread([infos] { + GetNotifyEvent(infos); + }).detach(); + } + if (IsFile(para->srcPath_) && !IsDirectory(para->destPath_)) { + // copyFile + auto ret = CopyFile(para->srcPath_.c_str(), para->destPath_.c_str()); + return NError(ret); + } + if (IsDirectory(para->srcPath_) && !IsFile(para->destPath_)) { + // copyDir + auto ret = CopyDirFunc(para->srcPath_.c_str(), para->destPath_.c_str(), infos); + return NError(ret); + } + return NError(EINVAL); + }; + + auto cbCompl = [infos](napi_env env, NError err) -> NVal { + if (err) { + return { env, err.GetNapiErr(env) }; + } + // + // if (notifyEventHandler_.joinable()) { + // notifyEventHandler_.join(); + // } + return { NVal::CreateUndefined(env) }; + }; + + NVal thisVar(env, funcArg.GetThisVar()); + if (funcArg.GetArgc() == NARG_CNT::TWO || + (funcArg.GetArgc() == NARG_CNT::THREE && !NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_function))) { + return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_COPY_NAME, cbExec, cbCompl).val_; + } else { + NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::THREE) ? NARG_POS::THIRD : NARG_POS::FOURTH)]); + return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_COPY_NAME, cbExec, cbCompl).val_; + } +} + +static void CallbackComplete(uv_work_t *work, int stat) +{ + if (work == nullptr) { + HILOGE("Failed to get uv_queue_work pointer"); + return; + } + + JsCallbackObject *jsCallbackObject = + reinterpret_cast(work->data); + do { + if (jsCallbackObject == nullptr) { + HILOGE("Failed to create context pointer"); + break; + } + if (!jsCallbackObject->nRef) { + HILOGE("Failed to get nref reference"); + break; + } + napi_handle_scope scope = nullptr; + napi_status status = napi_open_handle_scope(jsCallbackObject->env, &scope); + if (status != napi_ok) { + HILOGE("Failed to open handle scope, status: %{public}d", status); + break; + } + napi_env env = jsCallbackObject->env; + napi_value jsCallback = jsCallbackObject->nRef.Deref(env).val_; + NVal objn = NVal::CreateObject(env); + objn.AddProp("totalSize", NVal::CreateBigUint64(env, jsCallbackObject->totalSize).val_); + objn.AddProp("processedSize", NVal::CreateBigUint64(env, jsCallbackObject->progressSize).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(jsCallbackObject->env, scope); + if (status != napi_ok) { + HILOGE("Failed to close handle scope, status: %{public}d", status); + } + } while (0); + delete jsCallbackObject; + delete work; +} + +int32_t TransListener::OnFileReceive(uint64_t totalBytes, uint64_t processedBytes) +{ + uv_loop_s *loop = nullptr; + napi_get_uv_event_loop(callback_->env, &loop); + if (loop == nullptr) { + HILOGE("Failed to get uv event loop"); + return -1; + } + + uv_work_t *work = new (std::nothrow) uv_work_t; + if (work == nullptr) { + HILOGE("Failed to create uv_work_t pointer"); + return -1; + } + + if (!callback_) { + HILOGE("Failed to parse watcher callback"); + delete work; + return -1; + } + + callback_->totalSize = totalBytes; + callback_->progressSize = processedBytes; + 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; + } + return 0; +} + +int32_t TransListener::OnFinished(string sessionName) +{ + HILOGD("OnFinished"); + copyEvent_ = FAILED; + cv_.notify_all(); + return 0; +} + +int32_t TransListener::OnFailed(string sessionName) +{ + HILOGD("OnFailed"); + copyEvent_ = SUCCESS; + cv_.notify_all(); + return 0; +} + + +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/copy.h b/interfaces/kits/js/src/mod_fs/properties/copy.h new file mode 100644 index 000000000..e3a424ffe --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/copy.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FILEMANAGEMENT_FILE_API_COPY_H +#define FILEMANAGEMENT_FILE_API_COPY_H + +#include + +//#include "block_data.h" +#include "common_func.h" +#include "filemgmt_libn.h" +#include "n_async/n_ref.h" +#include +#include "file_trans_listener_stub.h" + +const int NONE = 0; +const int SUCCESS = 1 ; +const int FAILED = 2; +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; +using namespace OHOS::FileManagement::LibN; +struct FileSize { + std::string fileName; + uint64_t proceededSize; +}; +struct ReceiveInfo { + std::string path; + std::map fileList; + bool isFile; + uint64_t fileSize; +}; +struct JsCallbackObject { + napi_env env = nullptr; + LibN::NRef nRef; + std::vector>> wds; + uint64_t totalSize = 0; + uint64_t progressSize = 0; + explicit JsCallbackObject(napi_env env, LibN::NVal jsVal) : env(env), nRef(jsVal) {} + ~JsCallbackObject() = default; +}; + +struct FileInfos { + std::string srcUri; + std::string destUri; + std::string srcPath; + std::string destPath; + bool isFile = true; + uint64_t totalSize = 0; + uint64_t progressSize = 0; + int32_t notifyFd = -1; + int wd = -1; + bool operator==(const FileInfos &infos) const + { + return (srcUri == infos.srcUri && destUri == infos.destUri); + } + bool operator<(const FileInfos &infos) const + { + if (srcUri == infos.srcUri) { + return destUri < infos.destUri; + } + return (srcUri < infos.srcUri || destUri < infos.destUri); + } +}; + +struct UvEntry { + std::shared_ptr callback; + std::shared_ptr fileInfos; + uint64_t bytesUpload = 0; + uint64_t bytesTotal = 0; + UvEntry(std::shared_ptr &cb, std::shared_ptr fileInfos) + : callback(cb), fileInfos(fileInfos) + { + } +}; +class TransListener : public Storage::DistributedFile::FileTransListenerStub { +public: + int32_t OnFileReceive(uint64_t totalBytes, uint64_t processedBytes) override; + int32_t OnFinished(string sessionName) override; + int32_t OnFailed(string sessionName) override; + std::condition_variable cv_; + int copyEvent_ = NONE; + std::mutex cvMutex_; + std::shared_ptr callback_; +}; + +class CopyPara { +public: + std::string srcUri_; + std::string destUri_; + std::string srcPath_; + std::string destPath_; + NVal listener_; + napi_value thisVar_; + int32_t notifyFd_ = -1; + + CopyPara(std::string &srcUri, std::string &destUri, NVal listener, napi_value thisVar) + : srcUri_(srcUri), destUri_(destUri), listener_(listener), thisVar_(thisVar){}; +}; + +class Copy final { +public: + static napi_value Async(napi_env env, napi_callback_info info); + static std::recursive_mutex mutex_; + static std::map> jsCbMap_; + static void UnregisterListener(std::shared_ptr fileInfos); + +private: + static void RegisterListener(std::shared_ptr fileInfos, std::shared_ptr callback); + static NError SubscribeLocalListener( + napi_env env, std::shared_ptr para, std::shared_ptr infos); + static void OnFileReceive(std::shared_ptr infos); + static void GetNotifyEvent(std::shared_ptr infos); + static bool run_; +// static std::thread notifyEventHandler_; + // static std::map>> fileVisitFlags_; +}; +const string PROCEDURE_COPY_NAME = "FileIOCopy"; +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS + +#endif // FILEMANAGEMENT_FILE_API_COPY_H -- Gitee