From 222fab2fb3de746d3ce1314e026d62b2e09686f4 Mon Sep 17 00:00:00 2001 From: dinghong Date: Tue, 6 Aug 2024 21:01:34 +0800 Subject: [PATCH] add fs copy Change-Id: Ic08887d965090d3da38ead0d09c7a7cb8ebec920 Signed-off-by: dinghong --- interfaces/kits/cj/BUILD.gn | 6 + interfaces/kits/cj/src/copy.cpp | 750 ++++++++++++++++++++ interfaces/kits/cj/src/copy.h | 179 +++++ interfaces/kits/cj/src/file_fs_ffi.cpp | 54 ++ interfaces/kits/cj/src/file_fs_ffi.h | 4 + interfaces/kits/cj/src/file_fs_impl.h | 1 - interfaces/kits/cj/src/file_fs_mock.cpp | 4 + interfaces/kits/cj/src/task_signal_impl.cpp | 40 ++ interfaces/kits/cj/src/task_signal_impl.h | 43 ++ interfaces/kits/cj/src/translistener.cpp | 249 +++++++ interfaces/kits/cj/src/translistener.h | 64 ++ interfaces/kits/cj/src/uni_error.h | 18 + 12 files changed, 1411 insertions(+), 1 deletion(-) create mode 100644 interfaces/kits/cj/src/copy.cpp create mode 100644 interfaces/kits/cj/src/copy.h create mode 100644 interfaces/kits/cj/src/task_signal_impl.cpp create mode 100644 interfaces/kits/cj/src/task_signal_impl.h create mode 100644 interfaces/kits/cj/src/translistener.cpp create mode 100644 interfaces/kits/cj/src/translistener.h diff --git a/interfaces/kits/cj/BUILD.gn b/interfaces/kits/cj/BUILD.gn index ea93d4d6e..5fda2a785 100644 --- a/interfaces/kits/cj/BUILD.gn +++ b/interfaces/kits/cj/BUILD.gn @@ -74,6 +74,7 @@ ohos_shared_library("cj_file_fs_ffi") { "${utils_path}/filemgmt_libn/include", "${file_api_path}/interfaces/kits/cj/src", "${file_api_path}/interfaces/kits/native/remote_uri", + "${file_api_path}/interfaces/kits/native/task_signal", "${file_api_path}/interfaces/kits/rust/include", ] @@ -88,6 +89,7 @@ ohos_shared_library("cj_file_fs_ffi") { "${file_api_path}/interfaces/kits/js:build_kits_js", "${file_api_path}/interfaces/kits/js:fs", "${file_api_path}/interfaces/kits/native:remote_uri_native", + "${file_api_path}/interfaces/kits/native:task_signal_native", "${file_api_path}/interfaces/kits/rust:rust_file", "${utils_path}/filemgmt_libhilog:filemgmt_libhilog", "${utils_path}/filemgmt_libn:filemgmt_libn", @@ -104,6 +106,7 @@ ohos_shared_library("cj_file_fs_ffi") { "c_utils:utils", "data_share:datashare_common", "data_share:datashare_consumer", + "dfs_service:distributed_file_daemon_kit_inner", "hilog:libhilog", "ipc:ipc_core", "napi:ace_napi", @@ -113,6 +116,7 @@ ohos_shared_library("cj_file_fs_ffi") { ] sources = [ "../js/src/common/file_helper/fd_guard.cpp", + "src/copy.cpp", "src/copy_dir.cpp", "src/copy_file.cpp", "src/fdatasync.cpp", @@ -131,6 +135,8 @@ ohos_shared_library("cj_file_fs_ffi") { "src/stream_ffi.cpp", "src/stream_impl.cpp", "src/symlink.cpp", + "src/task_signal_impl.cpp", + "src/translistener.cpp", "src/uni_error.cpp", "src/utils.cpp", "src/watcher_impl.cpp", diff --git a/interfaces/kits/cj/src/copy.cpp b/interfaces/kits/cj/src/copy.cpp new file mode 100644 index 000000000..d86d390a5 --- /dev/null +++ b/interfaces/kits/cj/src/copy.cpp @@ -0,0 +1,750 @@ +/* + * Copyright (c) 2024 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 "datashare_helper.h" +#include "file_uri.h" +#include "file_utils.h" +#include "iservice_registry.h" +#include "translistener.h" + +namespace OHOS { +namespace CJSystemapi { +using namespace FileFs; +namespace fs = std::filesystem; +const std::string NETWORK_PARA = "?networkid="; +const std::string MEDIALIBRARY_DATA_URI = "datashare:///media"; +constexpr int DISMATCH = 0; +constexpr int MATCH = 1; +constexpr int BUF_SIZE = 1024; +constexpr size_t MAX_SIZE = 1024 * 1024 * 128; +constexpr std::chrono::milliseconds NOTIFY_PROGRESS_DELAY(300); +std::recursive_mutex CopyImpl::mutex_; +std::map> CopyImpl::cjCbMap_; + +static int OpenSrcFile(const std::string &srcPth, std::shared_ptr infos, int32_t &srcFd) +{ + Uri uri(infos->srcUri); + if (uri.GetAuthority() == "media") { + std::shared_ptr dataShareHelper = nullptr; + sptr remote = new (std::nothrow) IRemoteStub(); + if (!remote) { + LOGE("Failed to get remote object"); + return ENOMEM; + } + dataShareHelper = DataShare::DataShareHelper::Creator(remote->AsObject(), MEDIALIBRARY_DATA_URI); + if (!dataShareHelper) { + LOGE("Failed to connect to datashare"); + return E_PERMISSION; + } + srcFd = dataShareHelper->OpenFile(uri, CommonFunc::GetModeFromFlags(O_RDONLY)); + if (srcFd < 0) { + LOGE("Open media uri by data share fail. ret = %{public}d", srcFd); + return EPERM; + } + } else { + srcFd = open(srcPth.c_str(), O_RDONLY); + if (srcFd < 0) { + LOGE("Error opening src file descriptor. errno = %{public}d", errno); + return errno; + } + } + return ERRNO_NOERR; +} + +static int SendFileCore(std::unique_ptr srcFdg, + std::unique_ptr destFdg, + std::shared_ptr infos) +{ + std::unique_ptr sendFileReq = { + new (std::nothrow) uv_fs_t, CommonFunc::FsReqCleanup }; + if (sendFileReq == nullptr) { + LOGE("Failed to request heap memory."); + return ENOMEM; + } + int64_t offset = 0; + struct stat srcStat{}; + if (fstat(srcFdg->GetFD(), &srcStat) < 0) { + LOGE("Failed to get stat of file by fd: %{public}d ,errno = %{public}d", srcFdg->GetFD(), errno); + return errno; + } + int32_t ret = 0; + int64_t size = static_cast(srcStat.st_size); + while (size >= 0) { + ret = uv_fs_sendfile(nullptr, sendFileReq.get(), destFdg->GetFD(), srcFdg->GetFD(), + offset, MAX_SIZE, nullptr); + if (ret < 0) { + LOGE("Failed to sendfile by errno : %{public}d", errno); + return errno; + } + if (infos != nullptr && infos->taskSignal != nullptr) { + if (infos->taskSignal->CheckCancelIfNeed(infos->srcPath)) { + return ECANCELED; + } + } + offset += static_cast(ret); + size -= static_cast(ret); + if (ret == 0) { + break; + } + } + if (size != 0) { + LOGE("The execution of the sendfile task was terminated, remaining file size %{public}" PRIu64, size); + return EIO; + } + return ERRNO_NOERR; +} + +static int FilterFunc(const struct dirent *filename) +{ + if (std::string_view(filename->d_name) == "." || std::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); +} + +std::string CopyImpl::GetRealPath(const std::string& path) +{ + fs::path tempPath(path); + fs::path realPath{}; + for (const auto& component : tempPath) { + if (component == ".") { + continue; + } else if (component == "..") { + realPath = realPath.parent_path(); + } else { + realPath /= component; + } + } + return realPath.string(); +} + +bool CopyImpl::IsFile(const std::string &path) +{ + struct stat buf {}; + int ret = stat(path.c_str(), &buf); + if (ret == -1) { + LOGI("stat failed, errno is %{public}d, ", errno); + return false; + } + return (buf.st_mode & S_IFMT) == S_IFREG; +} + +std::tuple CopyImpl::GetFileSize(const std::string &path) +{ + struct stat buf {}; + int ret = stat(path.c_str(), &buf); + if (ret == -1) { + LOGI("Stat failed."); + return { errno, 0 }; + } + return { ERRNO_NOERR, buf.st_size }; +} + +bool CopyImpl::CheckFileValid(const std::string &filePath, std::shared_ptr infos) +{ + return infos->filePaths.count(filePath) != 0; +} + +int CopyImpl::UpdateProgressSize(const std::string &filePath, + std::shared_ptr receivedInfo, std::shared_ptr callback) +{ + auto [err, fileSize] = GetFileSize(filePath); + if (err != ERRNO_NOERR) { + LOGE("GetFileSize failed, err: %{public}d.", err); + return err; + } + auto size = fileSize; + auto iter = receivedInfo->fileList.find(filePath); + if (iter == receivedInfo->fileList.end()) { + receivedInfo->fileList.insert({ filePath, size }); + callback->progressSize += size; + } else { // file + if (size > iter->second) { + callback->progressSize += (size - iter->second); + iter->second = size; + } + } + return ERRNO_NOERR; +} + +void CopyImpl::CheckOrCreatePath(const std::string &destPath) +{ + if (!std::filesystem::exists(destPath)) { + LOGI("destPath not exist, destPath = %{public}s", destPath.c_str()); + auto file = open(destPath.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (file < 0) { + LOGE("Error opening file descriptor. errno = %{public}d", errno); + } + close(file); + } +} + +int CopyImpl::MakeDir(const std::string &path) +{ + std::filesystem::path destDir(path); + std::error_code errCode; + if (!std::filesystem::create_directory(destDir, errCode)) { + LOGE("Failed to create directory, error code: %{public}d", errCode.value()); + return errCode.value(); + } + return ERRNO_NOERR; +} + +int CopyImpl::CopySubDir(const std::string &srcPath, const std::string &destPath, std::shared_ptr infos) +{ + if (!std::filesystem::exists(destPath)) { + int res = MakeDir(destPath); + if (res != ERRNO_NOERR) { + LOGE("Failed to mkdir"); + return res; + } + } + uint32_t watchEvents = IN_MODIFY; + if (infos->notifyFd >= 0) { + int newWd = inotify_add_watch(infos->notifyFd, destPath.c_str(), watchEvents); + if (newWd < 0) { + LOGE("inotify_add_watch, newWd is unvaild, newWd = %{public}d", newWd); + return errno; + } + { + std::lock_guard lock(CopyImpl::mutex_); + auto iter = CopyImpl::cjCbMap_.find(*infos); + auto receiveInfo = FileManagement::CreateSharedPtr(); + if (receiveInfo == nullptr) { + LOGE("Failed to request heap memory."); + return ENOMEM; + } + receiveInfo->path = destPath; + if (iter == CopyImpl::cjCbMap_.end() || iter->second == nullptr) { + LOGE("Failed to find infos, srcPath = %{public}s, destPath = %{public}s", infos->srcPath.c_str(), + infos->destPath.c_str()); + return UNKNOWN_ERR; + } + iter->second->wds.push_back({ newWd, receiveInfo }); + } + } + return RecurCopyDir(srcPath, destPath, infos); +} + +int CopyImpl::RecurCopyDir(const std::string &srcPath, const std::string &destPath, std::shared_ptr infos) +{ + std::unique_ptr pNameList = { new (std::nothrow) struct NameList, Deleter }; + if (pNameList == nullptr) { + LOGE("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++) { + std::string src = srcPath + '/' + std::string((pNameList->namelist[i])->d_name); + std::string dest = destPath + '/' + std::string((pNameList->namelist[i])->d_name); + if ((pNameList->namelist[i])->d_type == DT_LNK) { + continue; + } + int ret = ERRNO_NOERR; + if ((pNameList->namelist[i])->d_type == DT_DIR) { + ret = CopySubDir(src, dest, infos); + } else { + infos->filePaths.insert(dest); + ret = CopyFile(src, dest, infos); + } + if (ret != ERRNO_NOERR) { + return ret; + } + } + return ERRNO_NOERR; +} + +bool CopyImpl::IsDirectory(const std::string &path) +{ + struct stat buf {}; + int ret = stat(path.c_str(), &buf); + if (ret == -1) { + LOGE("stat failed, errno is %{public}d, path is %{public}s", errno, path.c_str()); + return false; + } + return (buf.st_mode & S_IFMT) == S_IFDIR; +} + +int CopyImpl::CopyDirFunc(const std::string &src, const std::string &dest, std::shared_ptr infos) +{ + LOGI("CopyDirFunc in, src = %{public}s, dest = %{public}s", src.c_str(), dest.c_str()); + size_t found = dest.find(src); + if (found != std::string::npos && found == 0) { + return EINVAL; + } + fs::path srcPath = fs::u8path(src); + std::string dirName; + if (srcPath.has_parent_path()) { + dirName = srcPath.parent_path().filename(); + } + std::string destStr = dest + "/" + dirName; + return CopySubDir(src, destStr, infos); +} + +uint64_t CopyImpl::GetDirSize(std::shared_ptr infos, std::string path) +{ + std::unique_ptr pNameList = { new (std::nothrow) struct NameList, Deleter }; + if (pNameList == nullptr) { + LOGE("Failed to request heap memory."); + return ENOMEM; + } + int num = scandir(path.c_str(), &(pNameList->namelist), FilterFunc, alphasort); + pNameList->direntNum = num; + + long int size = 0; + for (int i = 0; i < num; i++) { + std::string dest = path + '/' + std::string((pNameList->namelist[i])->d_name); + if ((pNameList->namelist[i])->d_type == DT_LNK) { + continue; + } + if ((pNameList->namelist[i])->d_type == DT_DIR) { + size += static_cast(GetDirSize(infos, dest)); + } else { + struct stat st {}; + if (stat(dest.c_str(), &st) == -1) { + return size; + } + size += st.st_size; + } + } + return size; +} + +std::shared_ptr CopyImpl::InitCjFileInfo( + const std::string& srcUri, const std::string& destUri, sptr info) +{ + auto infos = FileManagement::CreateSharedPtr(); + if (infos == nullptr) { + LOGE("Failed to request heap memory by create FileInfos struct."); + return nullptr; + } + infos->srcUri = srcUri; + infos->destUri = destUri; + infos->listenerId = info->listenerId; + infos->copySignalId = info->signalId; + AppFileService::ModuleFileUri::FileUri srcFileUri(srcUri); + AppFileService::ModuleFileUri::FileUri destFileUri(destUri); + infos->srcPath = srcFileUri.GetRealPath(); + infos->destPath = destFileUri.GetPath(); + infos->srcPath = GetRealPath(infos->srcPath); + infos->destPath = GetRealPath(infos->destPath); + infos->notifyTime = std::chrono::steady_clock::now() + NOTIFY_PROGRESS_DELAY; + if (info->listenerId > 0) { + infos->hasListener = true; + } + auto signal = FFI::FFIData::GetData(infos->copySignalId); + if (signal != nullptr && signal->signalEntity != nullptr) { + infos->taskSignal = signal->signalEntity->taskSignal_; + } + return infos; +} + +void CopyImpl::ReceiveComplete(CProgress data, + std::shared_ptr infos, std::shared_ptr callback) +{ + if (callback == nullptr) { + LOGE("callback pointer is nullptr."); + return; + } + auto processedSize = data.processedSize; + if (processedSize < callback->maxProgressSize) { + return; + } + callback->maxProgressSize = processedSize; + + if (callback->callback == nullptr) { + LOGI("Empty callback."); + return; + } + callback->callback(data); +} + +void CopyImpl::OnFileReceive(std::shared_ptr infos) +{ + auto callback = GetRegisteredListener(infos); + if (callback == nullptr) { + LOGE("failed to get listener progress"); + return; + } + CProgress data = {.processedSize = callback->progressSize, + .totalSize = callback->totalSize}; + ReceiveComplete(data, infos, callback); +} + +std::shared_ptr CopyImpl::GetReceivedInfo(int wd, std::shared_ptr callback) +{ + auto it = find_if(callback->wds.begin(), callback->wds.end(), [wd](const auto& item) { + return item.first == wd; + }); + if (it != callback->wds.end()) { + return it->second; + } + return nullptr; +} + +int CopyImpl::CopyFile(const std::string &src, const std::string &dest, std::shared_ptr infos) +{ + LOGI("src = %{public}s, dest = %{public}s", src.c_str(), dest.c_str()); + int32_t srcFd = -1; + int32_t ret = OpenSrcFile(src, infos, srcFd); + if (srcFd < 0) { + return ret; + } + auto destFd = open(dest.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (destFd < 0) { + LOGE("Error opening dest file descriptor. errno = %{public}d", errno); + close(srcFd); + return errno; + } + auto srcFdg = FileManagement::CreateUniquePtr(srcFd, true); + auto destFdg = FileManagement::CreateUniquePtr(destFd, true); + if (srcFdg == nullptr || destFdg == nullptr) { + LOGE("Failed to request heap memory."); + close(srcFd); + close(destFd); + return ENOMEM; + } + return SendFileCore(move(srcFdg), move(destFdg), infos); +} + +std::shared_ptr CopyImpl::GetRegisteredListener(std::shared_ptr infos) +{ + std::lock_guard lock(mutex_); + auto iter = cjCbMap_.find(*infos); + if (iter == cjCbMap_.end()) { + LOGE("It is not registered."); + return nullptr; + } + return iter->second; +} + +std::tuple CopyImpl::HandleProgress( + inotify_event *event, std::shared_ptr infos, std::shared_ptr callback) +{ + if (callback == nullptr) { + return { true, EINVAL, false }; + } + auto receivedInfo = GetReceivedInfo(event->wd, callback); + if (receivedInfo == nullptr) { + return { true, EINVAL, false }; + } + std::string fileName = receivedInfo->path; + if (event->len > 0) { // files under subdir + fileName += "/" + std::string(event->name); + if (!CheckFileValid(fileName, infos)) { + return { true, EINVAL, false }; + } + auto err = UpdateProgressSize(fileName, receivedInfo, callback); + if (err != ERRNO_NOERR) { + return { false, err, false }; + } + } else { + auto [err, fileSize] = GetFileSize(fileName); + if (err != ERRNO_NOERR) { + return { false, err, false }; + } + callback->progressSize = fileSize; + } + return { true, callback->errorCode, true }; +} + +void CopyImpl::ReadNotifyEvent(std::shared_ptr infos) +{ + char buf[BUF_SIZE] = { 0 }; + struct inotify_event *event = nullptr; + int len = 0; + int64_t index = 0; + auto callback = GetRegisteredListener(infos); + while (((len = read(infos->notifyFd, &buf, sizeof(buf))) < 0) && (errno == EINTR)) {} + while (infos->run && index < len) { + event = reinterpret_cast(buf + index); + auto [needContinue, errCode, needSend] = HandleProgress(event, infos, callback); + if (!needContinue) { + infos->exceptionCode = errCode; + return; + } + if (needContinue && !needSend) { + index += static_cast(sizeof(struct inotify_event) + event->len); + continue; + } + if (callback->progressSize == callback->totalSize) { + infos->run = false; + return; + } + auto currentTime = std::chrono::steady_clock::now(); + if (currentTime >= infos->notifyTime) { + OnFileReceive(infos); + infos->notifyTime = currentTime + NOTIFY_PROGRESS_DELAY; + } + index += static_cast(sizeof(struct inotify_event) + event->len); + } +} + +void CopyImpl::GetNotifyEvent(std::shared_ptr infos) +{ + auto callback = GetRegisteredListener(infos); + if (callback == nullptr) { + infos->exceptionCode = EINVAL; + return; + } + prctl(PR_SET_NAME, "NotifyThread"); + nfds_t nfds = 2; + struct pollfd fds[2]; + fds[0].events = 0; + fds[1].events = POLLIN; + fds[0].fd = infos->eventFd; + fds[1].fd = infos->notifyFd; + while (infos->run && infos->exceptionCode == ERRNO_NOERR && infos->eventFd != -1 && infos->notifyFd != -1) { + auto ret = poll(fds, nfds, -1); + if (ret > 0) { + if (static_cast(fds[0].revents) & POLLNVAL) { + infos->run = false; + return; + } + if (static_cast(fds[1].revents) & POLLIN) { + ReadNotifyEvent(infos); + } + } else if (ret < 0 && errno == EINTR) { + continue; + } else { + infos->exceptionCode = errno; + return; + } + } +} + +void CopyImpl::StartNotify(std::shared_ptr infos, std::shared_ptr callback) +{ + if (infos->hasListener && callback != nullptr) { + callback->notifyHandler = std::thread([infos] { + GetNotifyEvent(infos); + }); + } +} + +int CopyImpl::ExecCopy(std::shared_ptr infos) +{ + if (IsFile(infos->srcPath) && IsFile(infos->destPath)) { + // copyFile + return CopyFile(infos->srcPath.c_str(), infos->destPath.c_str(), infos); + } + if (IsDirectory(infos->srcPath) && IsDirectory(infos->destPath)) { + if (infos->srcPath.back() != '/') { + infos->srcPath += '/'; + } + if (infos->destPath.back() != '/') { + infos->destPath += '/'; + } + // copyDir + return CopyDirFunc(infos->srcPath.c_str(), infos->destPath.c_str(), infos); + } + return EINVAL; +} + +std::shared_ptr CopyImpl::RegisterListener(std::shared_ptr& infos) +{ + auto callback = FileManagement::CreateSharedPtr(infos->listenerId); + if (callback == nullptr) { + LOGE("Failed to request heap memory by create callback object."); + return nullptr; + } + std::lock_guard lock(mutex_); + auto iter = cjCbMap_.find(*infos); + if (iter != cjCbMap_.end()) { + LOGE("Regist copy listener, already registered.") + return nullptr; + } + cjCbMap_.insert({*infos, callback}); + return callback; +} + +void CopyImpl::UnregisterListener(std::shared_ptr infos) +{ + if (infos == nullptr) { + LOGE("infos is nullptr"); + return; + } + std::lock_guard lock(mutex_); + auto iter = cjCbMap_.find(*infos); + if (iter == cjCbMap_.end()) { + LOGI("It is not be registered."); + return; + } + cjCbMap_.erase(*infos); +} + +bool CopyImpl::IsRemoteUri(const std::string& uri) +{ + return uri.find(NETWORK_PARA) != uri.npos; +} + +int64_t CopyImpl::DoCopy(std::shared_ptr infos, std::shared_ptr callback) +{ + if (IsRemoteUri(infos->srcUri)) { + if (infos->taskSignal != nullptr) { + infos->taskSignal->MarkRemoteTask(); + } + auto ret = TransListener::CopyFileFromSoftBus( + infos->srcUri, infos->destUri, infos, std::move(callback)); + return ret; + } + auto result = ExecLocal(infos, callback); + CloseNotifyFd(infos, callback); + infos->run = false; + WaitNotifyFinished(callback); + if (result != ERRNO_NOERR) { + infos->exceptionCode = result; + return infos->exceptionCode; + } + CopyComplete(infos, callback); + return infos->exceptionCode; +} + +int64_t CopyImpl::ExecLocal(std::shared_ptr& infos, std::shared_ptr& callback) +{ + if (IsFile(infos->srcPath)) { + if (infos->srcPath == infos->destPath) { + LOGE("The src and dest is same, path = %{public}s", infos->srcPath.c_str()); + return EINVAL; + } + CheckOrCreatePath(infos->destPath); + } + if (!infos->hasListener) { + return ExecCopy(infos); + } + auto ret = SubscribeLocalListener(infos, callback); + if (ret != ERRNO_NOERR) { + LOGE("Failed to subscribe local listener, errno = %{public}" PRIu64, ret); + return ret; + } + StartNotify(infos, callback); + return ExecCopy(infos); +} + +int64_t CopyImpl::SubscribeLocalListener(std::shared_ptr& infos, std::shared_ptr& callback) +{ + infos->notifyFd = inotify_init(); + if (infos->notifyFd < 0) { + LOGE("Failed to init inotify, errno:%{public}d", errno); + return errno; + } + infos->eventFd = eventfd(0, EFD_CLOEXEC); + if (infos->eventFd < 0) { + LOGE("Failed to init eventFd, errno:%{public}d", errno); + return errno; + } + callback->notifyFd = infos->notifyFd; + callback->eventFd = infos->eventFd; + int newWd = inotify_add_watch(infos->notifyFd, infos->destPath.c_str(), IN_MODIFY); + if (newWd < 0) { + auto errCode = errno; + LOGE("Failed to add watch, errno = %{public}d, notifyFd: %{public}d, destPath: %{public}s", + errno, infos->notifyFd, infos->destPath.c_str()); + CloseNotifyFd(infos, callback); + return errCode; + } + auto receiveInfo = FileManagement::CreateSharedPtr(); + if (receiveInfo == nullptr) { + LOGE("Failed to request heap memory."); + inotify_rm_watch(infos->notifyFd, newWd); + CloseNotifyFd(infos, callback); + return ENOMEM; + } + receiveInfo->path = infos->destPath; + callback->wds.push_back({ newWd, receiveInfo }); + if (IsDirectory(infos->srcPath)) { + callback->totalSize = GetDirSize(infos, infos->srcPath); + return ERRNO_NOERR; + } + auto [err, fileSize] = GetFileSize(infos->srcPath); + if (err == ERRNO_NOERR) { + callback->totalSize = fileSize; + } + return err; +} + +void CopyImpl::CloseNotifyFd(std::shared_ptr& infos, std::shared_ptr& callback) +{ + callback->CloseFd(); + infos->eventFd = -1; + infos->notifyFd = -1; +} + +void CopyImpl::WaitNotifyFinished(std::shared_ptr& callback) +{ + if (callback != nullptr) { + if (callback->notifyHandler.joinable()) { + callback->notifyHandler.join(); + } + } +} + +void CopyImpl::CopyComplete(std::shared_ptr& infos, std::shared_ptr& callback) +{ + if (callback != nullptr && infos->hasListener) { + callback->progressSize = callback->totalSize; + OnFileReceive(infos); + } +} + +void CopyImpl::Copy(const char* srcUri, const char* destUri, sptr info) +{ + if (srcUri == nullptr || destUri == nullptr) { + LOGE("Invalid input."); + return; + } + std::string src(srcUri); + std::string dest(destUri); + std::shared_ptr infos = InitCjFileInfo(src, dest, info); + if (infos == nullptr) { + return; + } + auto callback = RegisterListener(infos); + if (callback == nullptr) { + return; + } + DoCopy(infos, callback); + UnregisterListener(infos); +} + +CopyInfo::~CopyInfo() {} +} +} \ No newline at end of file diff --git a/interfaces/kits/cj/src/copy.h b/interfaces/kits/cj/src/copy.h new file mode 100644 index 000000000..0aaf81c1c --- /dev/null +++ b/interfaces/kits/cj/src/copy.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2024 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 OHOS_FILE_FS_COPY_H +#define OHOS_FILE_FS_COPY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "utils.h" +#include "cj_lambda.h" +#include "ffi_remote_data.h" +#include "macro.h" +#include "task_signal.h" +#include "task_signal_impl.h" +#include "uni_error.h" + +extern "C" { +struct CProgress { + uint64_t processedSize; + uint64_t totalSize; +}; +} + +namespace OHOS { +namespace CJSystemapi { +const uint64_t MAX_VALUE = 0x7FFFFFFFFFFFFFFF; +struct ReceiveInfo { + std::string path; + std::map fileList; +}; + +struct CjCallbackObject { + int64_t callbackId = 0; + int32_t notifyFd = -1; + int32_t eventFd = -1; + std::function callback; + std::vector>> wds; + uint64_t totalSize = 0; + uint64_t progressSize = 0; + uint64_t maxProgressSize = 0; + int32_t errorCode = 0; + std::thread notifyHandler; + explicit CjCallbackObject(int64_t id) : callbackId(id) + { + if (callbackId == 0) { + callback = nullptr; + return; + } + callback = CJLambda::Create(reinterpret_cast(callbackId)); + } + void CloseFd() + { + if (eventFd != -1) { + close(eventFd); + eventFd = -1; + } + if (notifyFd == -1) { + return; + } + for (auto item : wds) { + inotify_rm_watch(notifyFd, item.first); + } + close(notifyFd); + notifyFd = -1; + } + ~CjCallbackObject() + { + CloseFd(); + } +}; + +struct FileInfos { + std::string srcUri; + std::string destUri; + std::string srcPath; + std::string destPath; + std::chrono::steady_clock::time_point notifyTime; + int32_t notifyFd = -1; + int32_t eventFd = -1; + bool run = true; + bool hasListener = false; + int64_t listenerId = 0; + int64_t copySignalId = 0; + int64_t exceptionCode = FileFs::ERRNO_NOERR; + // sptr copySignal; + std::shared_ptr taskSignal = nullptr; + std::set filePaths; + 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; + } +}; + +class CopyInfo : public OHOS::FFI::FFIData { + friend class CopyImpl; +public: + CopyInfo(int64_t listener, int64_t signal) : listenerId(listener), signalId(signal) {} + ~CopyInfo(); +private: + int64_t listenerId = 0; + int64_t signalId = 0; +}; + +class CopyImpl final { +public: + static std::map> cjCbMap_; + static std::recursive_mutex mutex_; + static void UnregisterListener(std::shared_ptr fileInfos); + static void Copy(const char* srcUri, const char* destUri, sptr info); +private: + static int UpdateProgressSize(const std::string &filePath, + std::shared_ptr receivedInfo, + std::shared_ptr callback); + static void StartNotify(std::shared_ptr infos, std::shared_ptr callback); + static bool IsRemoteUri(const std::string& uri); + static int64_t DoCopy(std::shared_ptr infos, std::shared_ptr callback); + static int64_t ExecLocal(std::shared_ptr& infos, std::shared_ptr& callback); + static void CloseNotifyFd(std::shared_ptr& infos, std::shared_ptr& callback); + static void WaitNotifyFinished(std::shared_ptr& callback); + static void CopyComplete(std::shared_ptr& infos, std::shared_ptr& callback); + static std::shared_ptr InitCjFileInfo( + const std::string& srcUri, const std::string& destUri, sptr info); + static std::shared_ptr RegisterListener(std::shared_ptr& infos); + static bool IsFile(const std::string &path); + static void CheckOrCreatePath(const std::string &destPath); + static int64_t SubscribeLocalListener(std::shared_ptr& infos, + std::shared_ptr& callback); + static int ExecCopy(std::shared_ptr infos); + static int CopyFile(const std::string &src, const std::string &dest, std::shared_ptr infos); + static bool IsDirectory(const std::string &path); + static std::tuple GetFileSize(const std::string &path); + static int MakeDir(const std::string &path); + static int CopySubDir(const std::string &srcPath, const std::string &destPath, std::shared_ptr infos); + static int CopyDirFunc(const std::string &src, const std::string &dest, std::shared_ptr infos); + static uint64_t GetDirSize(std::shared_ptr infos, std::string path); + static int RecurCopyDir(const std::string &srcPath, const std::string &destPath, std::shared_ptr infos); + static void GetNotifyEvent(std::shared_ptr infos); + static bool CheckFileValid(const std::string &filePath, std::shared_ptr infos); + static void OnFileReceive(std::shared_ptr infos); + static std::shared_ptr GetRegisteredListener(std::shared_ptr infos); + static std::shared_ptr GetReceivedInfo(int wd, std::shared_ptr callback); + static void ReadNotifyEvent(std::shared_ptr infos); + static std::string GetRealPath(const std::string& path); + static std::tuple HandleProgress(inotify_event *event, + std::shared_ptr infos, + std::shared_ptr callback); + static void ReceiveComplete(CProgress data, + std::shared_ptr infos, + std::shared_ptr callback); +}; +} +} + +#endif // OHOS_FILE_FS_COPY_H diff --git a/interfaces/kits/cj/src/file_fs_ffi.cpp b/interfaces/kits/cj/src/file_fs_ffi.cpp index c30942825..419307c58 100644 --- a/interfaces/kits/cj/src/file_fs_ffi.cpp +++ b/interfaces/kits/cj/src/file_fs_ffi.cpp @@ -14,6 +14,7 @@ */ #include "file_fs_ffi.h" +#include "copy.h" #include "copy_file.h" #include "fdatasync.h" #include "fsync.h" @@ -32,6 +33,59 @@ namespace CJSystemapi { namespace FileFs { extern "C" { +int64_t FfiOHOSFileFsCreateCopyOptions(int64_t callbackId, int64_t signalId) +{ + LOGI("FS_TEST::FfiOHOSFileFsCreateCopyOptions"); + auto instance = FFIData::Create(callbackId, signalId); + if (!instance) { + LOGE("Failed to create CopyImpl."); + return 0; + } + return instance->GetID(); +} + +int64_t FfiOHOSFileFsCreateTaskSignal() +{ + LOGI("FS_TEST::FfiOHOSFileFsCreateTaskSignal"); + auto instance = FFIData::Create(); + if (!instance || !instance->signalEntity || !instance->signalEntity->taskSignal_) { + LOGE("Failed to create TaskSignalImpl."); + return 0; + } + return instance->GetID(); +} + +int64_t FfiOHOSFileFsTaskSignalCancel(int64_t id) +{ + LOGI("FS_TEST::FfiOHOSFileFsTaskSignalCancel"); + auto instance = FFIData::GetData(id); + if (!instance || !instance->signalEntity || !instance->signalEntity->taskSignal_) { + LOGE("Failed to create TaskSignalImpl."); + return ERR_INVALID_INSTANCE_CODE; + } + return instance->signalEntity->taskSignal_->Cancel(); +} + +void FfiOHOSFileFsCopy(const char* src, const char* dest, int64_t opt) +{ + LOGI("FS_TEST::FfiOHOSFileFsCopy"); + if (opt == 0) { + auto emptyInfo = FFIData::Create(0, 0); + if (!emptyInfo) { + LOGE("Failed to create empty CopyInfo"); + return; + } + CopyImpl::Copy(src, dest, emptyInfo); + return; + } + auto instance = FFIData::GetData(opt); + if (!instance) { + LOGE("Failed to get CopyInfo"); + return; + } + CopyImpl::Copy(src, dest, instance); +} + RetDataI64 FfiOHOSFileFsStatByID(int32_t file) { LOGI("FS_TEST::FfiOHOSFileFsStatByID"); diff --git a/interfaces/kits/cj/src/file_fs_ffi.h b/interfaces/kits/cj/src/file_fs_ffi.h index 73f187826..cfb634aaf 100644 --- a/interfaces/kits/cj/src/file_fs_ffi.h +++ b/interfaces/kits/cj/src/file_fs_ffi.h @@ -82,5 +82,9 @@ extern "C" { FFI_EXPORT int FfiOHOSFileFsFdatasync(int32_t fd); FFI_EXPORT int FfiOHOSFileFsFsync(int32_t fd); FFI_EXPORT int FfiOHOSFileFsSymlink(const char* target, const char* srcPath); + FFI_EXPORT int64_t FfiOHOSFileFsCreateCopyOptions(int64_t callbackId, int64_t signalId); + FFI_EXPORT int64_t FfiOHOSFileFsCreateTaskSignal(); + FFI_EXPORT int64_t FfiOHOSFileFsTaskSignalCancel(int64_t id); + FFI_EXPORT void FfiOHOSFileFsCopy(const char* src, const char* dest, int64_t opt); } #endif \ No newline at end of file diff --git a/interfaces/kits/cj/src/file_fs_impl.h b/interfaces/kits/cj/src/file_fs_impl.h index 87c11576a..af5810f76 100644 --- a/interfaces/kits/cj/src/file_fs_impl.h +++ b/interfaces/kits/cj/src/file_fs_impl.h @@ -35,7 +35,6 @@ namespace OHOS { namespace CJSystemapi { -constexpr int ERRNO_NOERR = 0; constexpr int DIR_DEFAULT_PERM = 0770; constexpr int FILE_DISMATCH = 0; diff --git a/interfaces/kits/cj/src/file_fs_mock.cpp b/interfaces/kits/cj/src/file_fs_mock.cpp index 20e305ddc..1d27e4c9e 100644 --- a/interfaces/kits/cj/src/file_fs_mock.cpp +++ b/interfaces/kits/cj/src/file_fs_mock.cpp @@ -93,4 +93,8 @@ FFI_EXPORT int FfiOHOSStatIsFIFO = 0; FFI_EXPORT int FfiOHOSStatIsFile = 0; FFI_EXPORT int FfiOHOSStatIsSocket = 0; FFI_EXPORT int FfiOHOSStatIsSymbolicLink = 0; +FFI_EXPORT int FfiOHOSFileFsCreateCopyOptions = 0; +FFI_EXPORT int FfiOHOSFileFsCreateTaskSignal = 0; +FFI_EXPORT int FfiOHOSFileFsTaskSignalCancel = 0; +FFI_EXPORT int FfiOHOSFileFsCopy = 0; } diff --git a/interfaces/kits/cj/src/task_signal_impl.cpp b/interfaces/kits/cj/src/task_signal_impl.cpp new file mode 100644 index 000000000..b62e3f62e --- /dev/null +++ b/interfaces/kits/cj/src/task_signal_impl.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 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 "task_signal_impl.h" +#include +#include "macro.h" + +namespace OHOS::CJSystemapi { +using namespace DistributedFS::ModuleTaskSignal; + +TaskSignalImpl::TaskSignalImpl() +{ + signalEntity = std::make_shared(); + if (signalEntity == nullptr) { + LOGE("Failed to create TaskSignalEntity."); + return; + } + signalEntity->taskSignal_ = std::make_shared(); + if (signalEntity->taskSignal_ == nullptr) { + LOGE("Failed to create native task signal."); + } +} + +TaskSignalEntity::~TaskSignalEntity() {} + +void TaskSignalEntity::OnCancel() {} + +} // namespace OHOS::CJSystemapi diff --git a/interfaces/kits/cj/src/task_signal_impl.h b/interfaces/kits/cj/src/task_signal_impl.h new file mode 100644 index 000000000..38d363250 --- /dev/null +++ b/interfaces/kits/cj/src/task_signal_impl.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 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 OHOS_FILE_FS_TASKSIGNAL_H +#define OHOS_FILE_FS_TASKSIGNAL_H + +#include "cj_common_ffi.h" +#include "file_utils.h" +#include "ffi_remote_data.h" +#include "task_signal.h" +#include "task_signal_listener.h" + +namespace OHOS { +namespace CJSystemapi { +class TaskSignalEntity : public DistributedFS::ModuleTaskSignal::TaskSignalListener { +public: + TaskSignalEntity() = default; + ~TaskSignalEntity() override; + void OnCancel() override; + std::shared_ptr taskSignal_ = nullptr; +}; + +class TaskSignalImpl : public OHOS::FFI::FFIData { +public: + TaskSignalImpl(); + std::shared_ptr signalEntity = nullptr; +}; +} +} + +#endif // OHOS_FILE_FS_COPY_H diff --git a/interfaces/kits/cj/src/translistener.cpp b/interfaces/kits/cj/src/translistener.cpp new file mode 100644 index 000000000..637e14149 --- /dev/null +++ b/interfaces/kits/cj/src/translistener.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2024 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 "translistener.h" +#include +#include +#include +#include "sandbox_helper.h" +#include "uri.h" +#include "uni_error.h" + +using Uri = OHOS::Uri; +namespace OHOS { +namespace CJSystemapi { +using namespace FileFs; +const std::string NETWORK_PARA = "?networkid="; +const std::string FILE_MANAGER_AUTHORITY = "docs"; +const std::string MEDIA_AUTHORITY = "media"; +const std::string DISTRIBUTED_PATH = "/data/storage/el2/distributedfiles/"; + +void TransListener::RmDir(const std::string &path) +{ + LOGI("RmDirm path : %{public}s", path.c_str()); + std::filesystem::path pathName(path); + std::error_code errCode; + if (std::filesystem::exists(pathName, errCode)) { + std::filesystem::remove_all(pathName, errCode); + if (errCode.value() != 0) { + LOGE("Failed to remove directory, error code: %{public}d", errCode.value()); + } + } else { + LOGE("pathName is not exists, error code: %{public}d", errCode.value()); + } +} + +std::string TransListener::CreateDfsCopyPath() +{ + std::random_device rd; + std::string random = std::to_string(rd()); + while (std::filesystem::exists(DISTRIBUTED_PATH + random)) { + random = std::to_string(rd()); + } + return random; +} + +int32_t TransListener::CopyFileFromSoftBus(const std::string& srcUri, const std::string& destUri, + std::shared_ptr fileInfos, std::shared_ptr callback) +{ + LOGI("CopyFileFromSoftBus begin."); + sptr transListener = new (std::nothrow) TransListener(); + if (transListener == nullptr) { + LOGE("new trans listener failed"); + return ENOMEM; + } + transListener->callback_ = std::move(callback); + Storage::DistributedFile::HmdfsInfo info{}; + Uri uri(destUri); + info.authority = uri.GetAuthority(); + info.sandboxPath = AppFileService::SandboxHelper::Decode(uri.GetPath()); + std::string disSandboxPath; + auto ret = PrepareCopySession(srcUri, destUri, transListener, info, disSandboxPath); + if (ret != 0) { + LOGE("PrepareCopySession failed, ret = %{public}d.", ret); + return EIO; + } + if (fileInfos->taskSignal != nullptr) { + fileInfos->taskSignal->SetFileInfoOfRemoteTask(info.sessionName, fileInfos->srcPath); + } + std::unique_lock lock(transListener->cvMutex_); + transListener->cv_.wait(lock, [&transListener]() { + return transListener->copyEvent_.copyResult == SUCCESS || + transListener->copyEvent_.copyResult == FAILED; + }); + LOGI("dfs PrepareSession Finish, result is %{public}d", transListener->copyEvent_.copyResult); + if (transListener->copyEvent_.copyResult == FAILED) { + if (info.authority != FILE_MANAGER_AUTHORITY && info.authority != MEDIA_AUTHORITY) { + RmDir(disSandboxPath); + } + auto it = softbusErr2ErrCodeTable.find(transListener->copyEvent_.errorCode); + if (it == softbusErr2ErrCodeTable.end()) { + return EIO; + } + return it->second; + } + if (info.authority == FILE_MANAGER_AUTHORITY || info.authority == MEDIA_AUTHORITY) { + LOGE("Public or media path not copy"); + return 0; + } + ret = CopyToSandBox(srcUri, disSandboxPath, info.sandboxPath); + RmDir(disSandboxPath); + if (ret != 0) { + LOGE("CopyToSandBox failed, ret = %{public}d.", ret); + return EIO; + } + return 0; +} + +int32_t TransListener::PrepareCopySession(const std::string &srcUri, + const std::string &destUri, + TransListener* transListener, + Storage::DistributedFile::HmdfsInfo &info, + std::string &disSandboxPath) +{ + std::string tmpDir; + if (info.authority != FILE_MANAGER_AUTHORITY && info.authority != MEDIA_AUTHORITY) { + tmpDir = CreateDfsCopyPath(); + disSandboxPath = DISTRIBUTED_PATH + tmpDir; + std::error_code errCode; + if (!std::filesystem::create_directory(disSandboxPath, errCode)) { + LOGE("Create dir failed, error code: %{public}d", errCode.value()); + return errCode.value(); + } + + auto pos = info.sandboxPath.rfind('/'); + if (pos == std::string::npos) { + LOGE("invalid file path"); + return EIO; + } + auto sandboxDir = info.sandboxPath.substr(0, pos); + if (std::filesystem::exists(sandboxDir, errCode)) { + info.dirExistFlag = true; + } + } + + info.copyPath = tmpDir; + auto networkId = GetNetworkIdFromUri(srcUri); + LOGI("dfs PrepareSession begin."); + auto ret = Storage::DistributedFile::DistributedFileDaemonManager::GetInstance().PrepareSession(srcUri, destUri, + networkId, transListener, info); + if (ret != ERRNO_NOERR) { + LOGE("PrepareSession failed, ret = %{public}d.", ret); + if (info.authority != FILE_MANAGER_AUTHORITY && info.authority != MEDIA_AUTHORITY) { + RmDir(disSandboxPath); + } + return EIO; + } + return ERRNO_NOERR; +} + +int32_t TransListener::CopyToSandBox(const std::string &srcUri, const std::string &disSandboxPath, + const std::string &sandboxPath) +{ + std::error_code errCode; + if (std::filesystem::exists(sandboxPath) && std::filesystem::is_directory(sandboxPath)) { + LOGI("Copy dir"); + std::filesystem::copy(disSandboxPath, sandboxPath, + std::filesystem::copy_options::recursive | std::filesystem::copy_options::update_existing, errCode); + if (errCode.value() != 0) { + LOGE("Copy dir failed: errCode: %{public}d", errCode.value()); + return EIO; + } + } else { + LOGI("Copy file."); + Uri uri(srcUri); + auto fileName = GetFileName(uri.GetPath()); + if (fileName.empty()) { + LOGE("Get filename failed"); + RmDir(disSandboxPath); + return EIO; + } + std::filesystem::copy(disSandboxPath + fileName, sandboxPath, std::filesystem::copy_options::update_existing, + errCode); + if (errCode.value() != 0) { + LOGE("Copy file failed: errCode: %{public}d", errCode.value()); + return EIO; + } + } + LOGI("Copy file success."); + return ERRNO_NOERR; +} + +std::string TransListener::GetFileName(const std::string &path) +{ + auto pos = path.find_last_of('/'); + if (pos == std::string::npos) { + LOGE("invalid path"); + return ""; + } + return AppFileService::SandboxHelper::Decode(path.substr(pos)); +} + +std::string TransListener::GetNetworkIdFromUri(const std::string &uri) +{ + return uri.substr(uri.find(NETWORK_PARA) + NETWORK_PARA.size(), uri.size()); +} + +void TransListener::CallbackComplete(TransListener* transListener, CProgress progress) +{ + if (transListener == nullptr || + transListener->callback_ == nullptr || + transListener->callback_->callback == nullptr) { + LOGE("Failed to get copy progress callback."); + return; + } + transListener->callback_->callback(progress); +} + +int32_t TransListener::OnFileReceive(uint64_t totalBytes, uint64_t processedBytes) +{ + std::lock_guard lock(callbackMutex_); + if (callback_ == nullptr) { + LOGE("Failed to parse watcher callback"); + return ENOMEM; + } + CProgress progress = { .processedSize = processedBytes, .totalSize = totalBytes }; + std::thread([this, progress]() { + CallbackComplete(this, progress); + }).detach(); + return ERRNO_NOERR; +} + +int32_t TransListener::OnFinished(const std::string &sessionName) +{ + LOGI("OnFinished"); + { + std::lock_guard lock(callbackMutex_); + callback_ = nullptr; + } + copyEvent_.copyResult = SUCCESS; + cv_.notify_all(); + return ERRNO_NOERR; +} + +int32_t TransListener::OnFailed(const std::string &sessionName, int32_t errorCode) +{ + LOGI("OnFailed, errorCode is %{public}d", errorCode); + { + std::lock_guard lock(callbackMutex_); + callback_ = nullptr; + } + copyEvent_.copyResult = FAILED; + copyEvent_.errorCode = errorCode; + cv_.notify_all(); + return ERRNO_NOERR; +} +} +} diff --git a/interfaces/kits/cj/src/translistener.h b/interfaces/kits/cj/src/translistener.h new file mode 100644 index 000000000..9009f8672 --- /dev/null +++ b/interfaces/kits/cj/src/translistener.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024 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 OHOS_FILE_FS_TRANSLISTENER_H +#define OHOS_FILE_FS_TRANSLISTENER_H + +#include + +#include "copy.h" +#include "distributed_file_daemon_manager.h" +#include "file_trans_listener_stub.h" +#include "hmdfs_info.h" + +namespace OHOS { +namespace CJSystemapi { +constexpr int NONE = 0; +constexpr int SUCCESS = 1; +constexpr int FAILED = 2; +struct CopyEvent { + int copyResult = NONE; + int32_t errorCode = 0; +}; +class TransListener : public Storage::DistributedFile::FileTransListenerStub { +public: + int32_t OnFileReceive(uint64_t totalBytes, uint64_t processedBytes) override; + int32_t OnFinished(const std::string& sessionName) override; + int32_t OnFailed(const std::string& sessionName, int32_t errorCode) override; + static int32_t CopyFileFromSoftBus(const std::string& srcUri, const std::string& destUri, + std::shared_ptr fileInfos, std::shared_ptr callback); +private: + static std::string GetNetworkIdFromUri(const std::string &uri); + static void CallbackComplete(TransListener* transListener, CProgress progress); + static void RmDir(const std::string &path); + static std::string CreateDfsCopyPath(); + static std::string GetFileName(const std::string &path); + static int32_t CopyToSandBox(const std::string &srcUri, + const std::string &disSandboxPath, const std::string &sandboxPath); + static int32_t PrepareCopySession(const std::string &srcUri, + const std::string &destUri, + TransListener* transListener, + Storage::DistributedFile::HmdfsInfo &info, + std::string &disSandboxPath); + std::mutex cvMutex_; + std::condition_variable cv_; + CopyEvent copyEvent_; + std::mutex callbackMutex_; + std::shared_ptr callback_; +}; +} +} + +#endif // OHOS_FILE_FS_TRANS_LISTENER_H \ No newline at end of file diff --git a/interfaces/kits/cj/src/uni_error.h b/interfaces/kits/cj/src/uni_error.h index 5d0e9ecd0..47e218f3d 100644 --- a/interfaces/kits/cj/src/uni_error.h +++ b/interfaces/kits/cj/src/uni_error.h @@ -28,11 +28,19 @@ namespace CJSystemapi { namespace FileFs { constexpr int UNKNOWN_ERR = -1; +constexpr int ERRNO_NOERR = 0; constexpr int STORAGE_SERVICE_SYS_CAP_TAG = 13600000; constexpr int FILEIO_SYS_CAP_TAG = 13900000; constexpr int USER_FILE_MANAGER_SYS_CAP_TAG = 14000000; constexpr int USER_FILE_SERVICE_SYS_CAP_TAG = 14300000; constexpr int DISTRIBUTEDFILE_SERVICE_SYS_CAP_TAG = 22400000; +constexpr int SOFTBUS_TRANS_FILE_PERMISSION_DENIED = -426114938; +constexpr int SOFTBUS_TRANS_FILE_DISK_QUOTA_EXCEEDED = -426114937; +constexpr int SOFTBUS_TRANS_FILE_NO_MEMORY = -426114936; +constexpr int SOFTBUS_TRANS_FILE_NETWORK_ERROR = -426114935; +constexpr int SOFTBUS_TRANS_FILE_NOT_FOUND = -426114934; +constexpr int SOFTBUS_TRANS_FILE_EXISTED = -426114933; +constexpr int DFS_CANCEL_SUCCESS = 204; enum ErrCodeSuffixOfFileIO { E_PERM = 1, @@ -135,6 +143,16 @@ enum CommonErrCode { E_OSNOTSUPPORT = 901 }; +static inline std::unordered_map softbusErr2ErrCodeTable { + {SOFTBUS_TRANS_FILE_PERMISSION_DENIED, EPERM}, + {SOFTBUS_TRANS_FILE_DISK_QUOTA_EXCEEDED, EIO}, + {SOFTBUS_TRANS_FILE_NO_MEMORY, ENOMEM}, + {SOFTBUS_TRANS_FILE_NETWORK_ERROR, ENETUNREACH}, + {SOFTBUS_TRANS_FILE_NOT_FOUND, ENOENT}, + {SOFTBUS_TRANS_FILE_EXISTED, EEXIST}, + {DFS_CANCEL_SUCCESS, ECANCELED}, +}; + const std::unordered_map uvCode2ErrCodeTable { { "EPERM", EPERM }, { "ENOENT", ENOENT }, -- Gitee