diff --git a/frameworks/js/napi/file_access_module/napi_fileaccess_helper.cpp b/frameworks/js/napi/file_access_module/napi_fileaccess_helper.cpp index a0dde328c122fc19deb7191af341528167ac41ea..303b9d9bdeb5281abb45668f9b5c5137f1fa4a9f 100644 --- a/frameworks/js/napi/file_access_module/napi_fileaccess_helper.cpp +++ b/frameworks/js/napi/file_access_module/napi_fileaccess_helper.cpp @@ -47,6 +47,8 @@ namespace { static napi_ref g_constructorRef = nullptr; constexpr uint32_t INITIAL_REFCOUNT = 1; constexpr int COPY_EXCEPTION = -1; + using CallbackExec = std::function; + using CallbackComplete = std::function; } std::list> g_fileAccessHelperList; @@ -718,6 +720,35 @@ std::tuple GetCopyArguments(napi_env env, return std::make_tuple(true, srcPathStr, destPathStr, force); } +napi_value AddNAsyncWork(napi_env env, NFuncArg &funcArg, CallbackExec cbExec, CallbackComplete cbComplete) +{ + const std::string procedureName = "copy"; + NVal thisVar(env, funcArg.GetThisVar()); + + if (funcArg.GetArgc() == NARG_CNT::TWO) { + return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbComplete).val_; + } + + if (funcArg.GetArgc() == NARG_CNT::THREE) { + NVal thirdArg(env, funcArg[NARG_POS::THIRD]); + if (thirdArg.TypeIs(napi_boolean)) { + return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbComplete).val_; + } + if (thirdArg.TypeIs(napi_function)) { + return NAsyncWorkCallback(env, thisVar, thirdArg).Schedule(procedureName, cbExec, cbComplete).val_; + } + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + NVal cb(env, funcArg[NARG_POS::FOURTH]); + if (!cb.TypeIs(napi_function)) { + NError(EINVAL).ThrowErr(env); + return nullptr; + } + return NAsyncWorkCallback(env, thisVar, cb).Schedule(procedureName, cbExec, cbComplete).val_; +} + napi_value NAPI_Copy(napi_env env, napi_callback_info info) { NFuncArg funcArg(env, info); @@ -746,7 +777,6 @@ napi_value NAPI_Copy(napi_env env, napi_callback_info info) NError(E_GETRESULT).ThrowErr(env); return nullptr; } - int ret = ERR_OK; auto cbExec = [srcPathStr, destPathStr, force, result, &ret, fileAccessHelper]() -> NError { OHOS::Uri srcUri(srcPathStr); @@ -764,31 +794,7 @@ napi_value NAPI_Copy(napi_env env, napi_callback_info info) return { env, CreateObjectArray(env, *result) }; }; - const std::string procedureName = "copy"; - NVal thisVar(env, funcArg.GetThisVar()); - - if (funcArg.GetArgc() == NARG_CNT::TWO) { - return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbComplete).val_; - } - - if (funcArg.GetArgc() == NARG_CNT::THREE) { - NVal thirdArg(env, funcArg[NARG_POS::THIRD]); - if (thirdArg.TypeIs(napi_boolean)) { - return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbComplete).val_; - } - if (thirdArg.TypeIs(napi_function)) { - return NAsyncWorkCallback(env, thisVar, thirdArg).Schedule(procedureName, cbExec, cbComplete).val_; - } - NError(EINVAL).ThrowErr(env); - return nullptr; - } - - NVal cb(env, funcArg[NARG_POS::FOURTH]); - if (!cb.TypeIs(napi_function)) { - NError(EINVAL).ThrowErr(env); - return nullptr; - } - return NAsyncWorkCallback(env, thisVar, cb).Schedule(procedureName, cbExec, cbComplete).val_; + return AddNAsyncWork(env, funcArg, cbExec, cbComplete); } napi_value NAPI_Rename(napi_env env, napi_callback_info info) diff --git a/interfaces/inner_api/file_access/include/file_access_helper.h b/interfaces/inner_api/file_access/include/file_access_helper.h index fc1838242108aa53c62c0bcc15b4aceea083d99d..87686425726efa3550fa47420be07d5ededb5588 100644 --- a/interfaces/inner_api/file_access/include/file_access_helper.h +++ b/interfaces/inner_api/file_access/include/file_access_helper.h @@ -104,6 +104,20 @@ private: std::shared_ptr GetConnectInfo(const std::string &bundleName); int CopyInsideService(Uri &sourceUri, Uri &destUri, std::vector ©Result, bool force = false); + int CopyCrossService(Uri &sourceUri, Uri &destUri, std::vector ©Result, bool force = false); + int CopyDirectoryOperation(FileInfo &fileInfo, Uri &destUri, std::vector ©Result, + bool force = false); + int CopyFileOperation(FileInfo &fileInfo, Uri &destUri, CopyResult ©Result, bool force = false); + ssize_t DoSendFile(int writeFd, int readFd, ssize_t count); + int DoCreateFile(Uri &destUri, std::string &fileName, bool force, Uri &newUri); + int DoCreateDirectory(Uri &destUri, std::string &fileName, std::vector ©Result, Uri &newUri); + int GetTargetFileUri(const Uri &targetPathUri, const std::string &targetFileName, Uri &targetUri); + int GetCopyResult(const std::string &sourceUri, const std::string &destUri, int errCode, const std::string &errMsg, + std::vector ©Result); + int TranslateCopyResult(int errCode, CopyResult ©Result); + int IsDirectory(Uri &uri, bool &isDir); + bool IsExternalUri(Uri &uri); + bool IsMediaUri(Uri &uri); sptr token_ = nullptr; std::unordered_map> cMap_; diff --git a/interfaces/inner_api/file_access/include/js_file_access_ext_ability.h b/interfaces/inner_api/file_access/include/js_file_access_ext_ability.h index ac4b0bd29d7fa48a02365380b026c5d6410d7342..7ad1b3440b7d39e8a57f0c9b4b7e1eeaebc68edf 100644 --- a/interfaces/inner_api/file_access/include/js_file_access_ext_ability.h +++ b/interfaces/inner_api/file_access/include/js_file_access_ext_ability.h @@ -65,6 +65,8 @@ public: int Mkdir(const Uri &parent, const std::string &displayName, Uri &newFile) override; int Delete(const Uri &sourceFile) override; int Move(const Uri &sourceFile, const Uri &targetParent, Uri &newFile) override; + int Copy(const Uri &sourceUri, const Uri &destUri, std::vector ©Result, + bool force = false) override; int Rename(const Uri &sourceFile, const std::string &displayName, Uri &newFile) override; int ListFile(const FileInfo &fileInfo, const int64_t offset, const int64_t maxCount, const FileFilter &filter, std::vector &fileInfoVec) override; diff --git a/interfaces/inner_api/file_access/src/file_access_ext_proxy.cpp b/interfaces/inner_api/file_access/src/file_access_ext_proxy.cpp index 192c090a40d2b9e236776ec886c64128484b8db4..7ae430aa4f9b5f7539602fd0805a88f3074c30f4 100644 --- a/interfaces/inner_api/file_access/src/file_access_ext_proxy.cpp +++ b/interfaces/inner_api/file_access/src/file_access_ext_proxy.cpp @@ -317,17 +317,9 @@ int FileAccessExtProxy::Move(const Uri &sourceFile, const Uri &targetParent, Uri return ERR_OK; } -int FileAccessExtProxy::Copy(const Uri &sourceUri, const Uri &destUri, std::vector ©Result, - bool force) +int WriteCopyFuncArguments(OHOS::MessageParcel &data, const Uri &sourceUri, const Uri &destUri, bool force) { - StartTrace(HITRACE_TAG_FILEMANAGEMENT, "Copy"); - MessageParcel data; - if (!data.WriteInterfaceToken(FileAccessExtProxy::GetDescriptor())) { - HILOG_ERROR("WriteInterfaceToken failed"); - FinishTrace(HITRACE_TAG_FILEMANAGEMENT); - return E_IPCS; - } - + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "WriteCopyFuncArguments"); std::string insideInputSourceUri = sourceUri.ToString(); if (!data.WriteString(insideInputSourceUri)) { HILOG_ERROR("fail to WriteParcelable insideInputSourceUri"); @@ -347,16 +339,13 @@ int FileAccessExtProxy::Copy(const Uri &sourceUri, const Uri &destUri, std::vect FinishTrace(HITRACE_TAG_FILEMANAGEMENT); return E_IPCS; } + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ERR_OK; +} - MessageParcel reply; - MessageOption option; - int err = Remote()->SendRequest(CMD_COPY, data, reply, option); - if (err != ERR_OK) { - HILOG_ERROR("fail to SendRequest. err: %{public}d", err); - FinishTrace(HITRACE_TAG_FILEMANAGEMENT); - return err; - } - +int ReadCopyFuncResults(OHOS::MessageParcel &reply, std::vector ©Result) +{ + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "ReadCopyFuncResults"); int ret = E_IPCS; if (!reply.ReadInt32(ret)) { HILOG_ERROR("fail to ReadInt32 ret"); @@ -366,7 +355,7 @@ int FileAccessExtProxy::Copy(const Uri &sourceUri, const Uri &destUri, std::vect if (ret == ERR_OK) { HILOG_ERROR("Copy operation success"); FinishTrace(HITRACE_TAG_FILEMANAGEMENT); - return ERR_OK; + return ret; } uint32_t count = 0; @@ -391,6 +380,43 @@ int FileAccessExtProxy::Copy(const Uri &sourceUri, const Uri &destUri, std::vect copyResult.push_back(*copyResultPtr); } } + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ret; +} + +int FileAccessExtProxy::Copy(const Uri &sourceUri, const Uri &destUri, std::vector ©Result, + bool force) +{ + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "Copy"); + MessageParcel data; + if (!data.WriteInterfaceToken(FileAccessExtProxy::GetDescriptor())) { + HILOG_ERROR("WriteInterfaceToken failed"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return E_IPCS; + } + + int ret = WriteCopyFuncArguments(data, sourceUri, destUri, force); + if (ret != ERR_OK) { + HILOG_ERROR("Write copy function arguments error"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ret; + } + + MessageParcel reply; + MessageOption option; + int err = Remote()->SendRequest(CMD_COPY, data, reply, option); + if (err != ERR_OK) { + HILOG_ERROR("fail to SendRequest. err: %{public}d", err); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return err; + } + + ret = ReadCopyFuncResults(reply, copyResult); + if (ret != ERR_OK) { + HILOG_ERROR("Read copy function result error, code: %{public}d", ret); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ret; + } FinishTrace(HITRACE_TAG_FILEMANAGEMENT); return ret; diff --git a/interfaces/inner_api/file_access/src/file_access_helper.cpp b/interfaces/inner_api/file_access/src/file_access_helper.cpp index 047d16e189a11b238991523856812f2195140cd5..7b317ecc226f4777dc13425161333f7603e198fd 100644 --- a/interfaces/inner_api/file_access/src/file_access_helper.cpp +++ b/interfaces/inner_api/file_access/src/file_access_helper.cpp @@ -14,6 +14,10 @@ */ #include "file_access_helper.h" +#include +#include +#include +#include #include #include "bundle_constants.h" @@ -36,8 +40,10 @@ namespace FileAccessFwk { using namespace Media; using json = nlohmann::json; namespace { + constexpr int64_t MAX_COUNT = 1000; constexpr int COPY_EXCEPTION = -1; constexpr int COPY_NOEXCEPTION = -2; + constexpr ssize_t SEND_FILE_CHUNK_SIZE = 512 * 1024 * 1024; } std::vector FileAccessHelper::wants_; sptr g_sourceExtProxy; @@ -641,7 +647,7 @@ int FileAccessHelper::Move(Uri &sourceFile, Uri &targetParent, Uri &newFile) return ERR_OK; } -static bool IsMediaUri(Uri &uri) +bool FileAccessHelper::IsMediaUri(Uri &uri) { string path = uri.GetPath(); std::size_t len = MEDIA_BNUDLE_NAME_ALIAS.length(); @@ -652,7 +658,36 @@ static bool IsMediaUri(Uri &uri) return false; } -static int ThrowExceptionByErrorCode(int errCode, CopyResult ©Result) +bool FileAccessHelper::IsExternalUri(Uri &uri) +{ + string path = uri.GetPath(); + std::size_t len = EXTERNAL_BNUDLE_NAME.length(); + if (path.length() > len) { + string external = path.substr(1, len); + return (external == EXTERNAL_BNUDLE_NAME); + } + return false; +} + +int FileAccessHelper::IsDirectory(Uri &uri, bool &isDir) +{ + sptr proxy = FileAccessHelper::GetProxyByUri(uri); + if (proxy == nullptr) { + HILOG_ERROR("failed with invalid fileAccessExtProxy"); + return E_IPCS; + } + + FileInfo fileInfo; + int ret = proxy->GetFileInfoFromUri(uri, fileInfo); + if (ret != ERR_OK) { + HILOG_ERROR("get FileInfo from uri error, code:%{public}d", ret); + return ret; + } + isDir = (fileInfo.mode & DOCUMENT_FLAG_REPRESENTS_DIR) != 0; + return ret; +} + +int FileAccessHelper::TranslateCopyResult(int errCode, CopyResult &result) { if (errCode == COPY_EXCEPTION || errCode == ERR_OK) { return errCode; @@ -663,6 +698,7 @@ static int ThrowExceptionByErrorCode(int errCode, CopyResult ©Result) case E_IPCS: case E_URIS: case ERR_NOMEM: + case E_PERMISSION_SYS: case ERR_PERM: { HILOG_ERROR("Copy exception, terminate copy"); ret = COPY_EXCEPTION; @@ -676,9 +712,271 @@ static int ThrowExceptionByErrorCode(int errCode, CopyResult ©Result) } if (OHOS::FileManagement::LibN::errCodeTable.find(errCode) != OHOS::FileManagement::LibN::errCodeTable.end()) { - copyResult.errCode = OHOS::FileManagement::LibN::errCodeTable.at(errCode).first; - copyResult.errMsg = OHOS::FileManagement::LibN::errCodeTable.at(errCode).second; + result.errCode = OHOS::FileManagement::LibN::errCodeTable.at(errCode).first; + result.errMsg = OHOS::FileManagement::LibN::errCodeTable.at(errCode).second; + } + return ret; +} + +int FileAccessHelper::GetTargetFileUri(const Uri &targetPathUri, const std::string &targetFileName, Uri &targetUri) +{ + FileInfo fileInfo; + int ret = g_destExtProxy->GetFileInfoFromUri(targetPathUri, fileInfo); + if (ret != ERR_OK) { + HILOG_ERROR("get FileInfo from uri error, code:%{public}d", ret); + return ret; + } + + std::vector fileInfoVec; + FileFilter filter { {}, {}, {}, 0, 0, false, false }; + int64_t offset { 0 }; + do { + fileInfoVec.clear(); + ret = g_destExtProxy->ListFile(fileInfo, offset, MAX_COUNT, filter, fileInfoVec); + if (ret != ERR_OK) { + HILOG_ERROR("ListFile get result error, code:%{public}d", ret); + return ret; + } + + for (auto info : fileInfoVec) { + if (info.fileName == targetFileName) { + Uri uri { info.uri }; + targetUri = uri; + return ERR_OK; + } + } + offset += MAX_COUNT; + } while (fileInfoVec.size() == MAX_COUNT); + + return ERR_OK; +} + +ssize_t FileAccessHelper::DoSendFile(int writeFd, int readFd, ssize_t count) +{ + ssize_t leftBytes = count; + while (leftBytes > 0) { + ssize_t sendBytes = SEND_FILE_CHUNK_SIZE; + if (sendBytes > leftBytes) { + sendBytes = leftBytes; + } + + sendBytes = sendfile(writeFd, readFd, nullptr, sendBytes); + if (sendBytes < 0) { + int errCode = errno; + if (errCode == EINTR || errCode == EAGAIN) { + HILOG_ERROR("sendfile terminate by signal, try again, code: %{public}d", errCode); + continue; + } + HILOG_ERROR("sendfile system call execute error, code:%{public}d", errCode); + return errCode; + } + + leftBytes -= sendBytes; + } + return ERR_OK; +} + +int FileAccessHelper::DoCreateFile(Uri &destUri, std::string &fileName, bool force, Uri &newUri) +{ + int ret = g_destExtProxy->CreateFile(destUri, fileName, newUri); + if (ret == ERR_EXIST) { + ret = GetTargetFileUri(destUri, fileName, newUri); + if (ret != ERR_OK) { + HILOG_ERROR("Get target file uri error, code:%{public}d", ret); + return ret; + } + if (force) { + ret = g_destExtProxy->Delete(newUri); + if (ret != ERR_OK) { + HILOG_ERROR("Delete exist file error, code:%{public}d", ret); + return ret; + } + ret = g_destExtProxy->CreateFile(destUri, fileName, newUri); + if (ret != ERR_OK) { + HILOG_ERROR("Create target file error, code:%{public}d", ret); + return ret; + } + } else { + HILOG_ERROR("File is exist, terminate current copy"); + return ERR_EXIST; + } + } else if (ret != ERR_OK) { + HILOG_ERROR("Create file error, code:%{public}d", ret); + return ret; + } + return ret; +} + +int FileAccessHelper::CopyFileOperation(FileInfo &fileInfo, Uri &destUri, CopyResult &result, bool force) +{ + int ret = COPY_EXCEPTION; + int readFd = -1; + int writeFd = -1; + do { + Uri srcUri {fileInfo.uri}; + ret = g_sourceExtProxy->OpenFile(srcUri, READ, readFd); + if (ret != ERR_OK) { + HILOG_ERROR("Open source file error, code:%{public}d", ret); + break; + } + struct stat info = { 0 }; + if ((ret = fstat(readFd, &info)) < 0) { + HILOG_ERROR("fstat get stat info error, code:%{public}d", ret); + break; + } + int64_t sourceFileSize = info.st_size; + std::string sourceFileName = fileInfo.fileName; + + Uri newUri {""}; + ret = DoCreateFile(destUri, sourceFileName, force, newUri); + if (ret != ERR_OK) { + result.destUri = newUri.ToString(); + break; + } + + ret = g_destExtProxy->OpenFile(newUri, WRITE, writeFd); + if (ret != ERR_OK) { + HILOG_ERROR("Open destination file error, code:%{public}d", ret); + break; + } + + ret = DoSendFile(writeFd, readFd, sourceFileSize); + if (ret != ERR_OK) { + HILOG_ERROR("Send file execute error, code:%{public}d", ret); + break; + } + + close(readFd); + close(writeFd); + return ERR_OK; + } while (false); + + close(readFd); + close(writeFd); + result.sourceUri = fileInfo.uri; + return TranslateCopyResult(ret, result); +} + +int FileAccessHelper::GetCopyResult(const std::string &sourceUri, const std::string &destUri, int errCode, + const std::string &errMsg, std::vector ©Result) +{ + CopyResult result { sourceUri, destUri, errCode, errMsg }; + int ret = TranslateCopyResult(errCode, result); + copyResult.clear(); + copyResult.push_back(result); + return ret; +} + +int FileAccessHelper::DoCreateDirectory(Uri &destUri, std::string &fileName, std::vector ©Result, + Uri &newUri) +{ + int ret = GetTargetFileUri(destUri, fileName, newUri); + if (ret != ERR_OK) { + HILOG_ERROR("Get target file uri error, code:%{public}d", ret); + GetCopyResult("", "", ret, "", copyResult); + return COPY_EXCEPTION; + } + if (newUri.ToString().empty()) { + ret = g_destExtProxy->Mkdir(destUri, fileName, newUri); + if (ret != ERR_OK) { + HILOG_ERROR("Make directory error, code:%{public}d", ret); + GetCopyResult("", "", ret, "", copyResult); + return COPY_EXCEPTION; + } + } + return ret; +} + +int FileAccessHelper::CopyDirectoryOperation(FileInfo &fileInfo, Uri &destUri, std::vector ©Result, + bool force) +{ + std::vector fileInfoVec; + FileFilter filter { {}, {}, {}, 0, 0, false, false }; + int64_t offset { 0 }; + int copyRet = ERR_OK; + int ret = COPY_EXCEPTION; + do { + fileInfoVec.clear(); + ret = g_sourceExtProxy->ListFile(fileInfo, offset, MAX_COUNT, filter, fileInfoVec); + if (ret != ERR_OK) { + HILOG_ERROR("Get file list error, code:%{public}d", ret); + GetCopyResult(fileInfo.uri, destUri.ToString(), ret, "", copyResult); + return COPY_EXCEPTION; + } + + for (auto info : fileInfoVec) { + if (info.mode & DOCUMENT_FLAG_REPRESENTS_DIR) { + Uri targetUri { "" }; + ret = DoCreateDirectory(destUri, info.fileName, copyResult, targetUri); + if (ret == COPY_EXCEPTION) { + return ret; + } + ret = CopyDirectoryOperation(info, targetUri, copyResult, force); + if (ret == COPY_EXCEPTION) { + return ret; + } + if (ret == COPY_NOEXCEPTION) { + copyRet = ret; + } + } else if (info.mode & DOCUMENT_FLAG_REPRESENTS_FILE) { + CopyResult result; + ret = CopyFileOperation(info, destUri, result, force); + if (ret == COPY_EXCEPTION) { + copyResult.clear(); + copyResult.push_back(result); + return ret; + } + if (ret == COPY_NOEXCEPTION) { + copyResult.push_back(result); + copyRet = ret; + } + } + } + offset += MAX_COUNT; + } while (fileInfoVec.size() == MAX_COUNT); + return copyRet; +} + +int FileAccessHelper::CopyCrossService(Uri &sourceUri, Uri &destUri, std::vector ©Result, bool force) +{ + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "CopyCrossService"); + int ret = COPY_EXCEPTION; + g_sourceExtProxy = GetProxyByUri(sourceUri); + g_destExtProxy = GetProxyByUri(destUri); + if ((g_sourceExtProxy == nullptr) || (g_destExtProxy == nullptr)) { + HILOG_ERROR("failed with invalid fileAccessExtProxy"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return GetCopyResult("", "", E_IPCS, "", copyResult); + } + + FileInfo fileInfo; + ret = g_sourceExtProxy->GetFileInfoFromUri(sourceUri, fileInfo); + if (ret != ERR_OK) { + HILOG_ERROR("get FileInfo from uri error, code:%{public}d", ret); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return GetCopyResult("", "", ret, "", copyResult); } + + Uri targetUri { "" }; + if (fileInfo.mode & DOCUMENT_FLAG_REPRESENTS_DIR) { + ret = DoCreateDirectory(destUri, fileInfo.fileName, copyResult, targetUri); + if (ret == COPY_EXCEPTION) { + HILOG_ERROR("Create target directory error"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ret; + } + ret = CopyDirectoryOperation(fileInfo, targetUri, copyResult, force); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + } else if (fileInfo.mode & DOCUMENT_FLAG_REPRESENTS_FILE) { + CopyResult result; + ret = CopyFileOperation(fileInfo, destUri, result, force); + if (ret != ERR_OK) { + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + copyResult.push_back(result); + return ret; + } + } + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); return ret; } @@ -691,21 +989,21 @@ int FileAccessHelper::CopyInsideService(Uri &sourceUri, Uri &destUri, std::vecto HILOG_ERROR("Failed with invalid fileAccessExtProxy"); FinishTrace(HITRACE_TAG_FILEMANAGEMENT); CopyResult result; - ret = ThrowExceptionByErrorCode(E_IPCS, result); + ret = TranslateCopyResult(E_IPCS, result); copyResult.push_back(result); return ret; } ret = proxy->Copy(sourceUri, destUri, copyResult, force); if (ret != ERR_OK) { - HILOG_ERROR("Copy error, code:%{public}d", ret); FinishTrace(HITRACE_TAG_FILEMANAGEMENT); if ((ret == COPY_EXCEPTION) || (ret == COPY_NOEXCEPTION)) { HILOG_ERROR("Copy exception, code:%{public}d", ret); FinishTrace(HITRACE_TAG_FILEMANAGEMENT); return ret; } - CopyResult result { "", "", ret, ""}; + HILOG_ERROR("Copy error, code:%{public}d", ret); + CopyResult result { "", "", ret, "" }; copyResult.push_back(result); return COPY_EXCEPTION; } @@ -717,29 +1015,41 @@ int FileAccessHelper::Copy(Uri &sourceUri, Uri &destUri, std::vector { StartTrace(HITRACE_TAG_FILEMANAGEMENT, "Copy"); if (!IsSystemApp()) { - HILOG_ERROR("FileAccessHelper::Copy check IsSystemAppByFullTokenID failed"); + HILOG_ERROR("Copy check IsSystemAppByFullTokenID failed"); FinishTrace(HITRACE_TAG_FILEMANAGEMENT); - CopyResult result { sourceUri.ToString(), destUri.ToString(), E_PERMISSION_SYS, "" }; - copyResult.push_back(result); - return COPY_EXCEPTION; + return GetCopyResult("", "", E_PERMISSION_SYS, "", copyResult); } if (!CheckUri(sourceUri) || !CheckUri(destUri)) { HILOG_ERROR("Uri format check error"); FinishTrace(HITRACE_TAG_FILEMANAGEMENT); - CopyResult result { sourceUri.ToString(), destUri.ToString(), E_URIS, "" }; - copyResult.push_back(result); + return GetCopyResult("", "", E_URIS, "", copyResult); + } + + bool isDir = false; + int ret = IsDirectory(destUri, isDir); + if (ret != ERR_OK) { + HILOG_ERROR("Get uri type error"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + GetCopyResult("", "", ret, "", copyResult); return COPY_EXCEPTION; } + if (!isDir) { + HILOG_ERROR("Destination uri is not directory"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return GetCopyResult("", "", E_URIS, "", copyResult); + } - int ret = COPY_EXCEPTION; - if (IsMediaUri(sourceUri) && IsMediaUri(destUri)) { + ret = COPY_EXCEPTION; + if ((IsMediaUri(sourceUri) && IsMediaUri(destUri)) + || (IsExternalUri(sourceUri) && IsExternalUri(destUri))) { ret = CopyInsideService(sourceUri, destUri, copyResult, force); + } else if ((IsMediaUri(sourceUri) && IsExternalUri(destUri)) + || (IsExternalUri(sourceUri) && IsMediaUri(destUri))) { + ret = CopyCrossService(sourceUri, destUri, copyResult, force); } else { - HILOG_ERROR("External storage copy is not supported."); - CopyResult result { sourceUri.ToString(), destUri.ToString(), EPERM, "" }; - copyResult.push_back(result); - return COPY_NOEXCEPTION; + HILOG_ERROR("Unsupported copy service, invalid uri"); + return GetCopyResult("", "", E_URIS, "", copyResult); } FinishTrace(HITRACE_TAG_FILEMANAGEMENT); return ret; diff --git a/interfaces/inner_api/file_access/src/js_file_access_ext_ability.cpp b/interfaces/inner_api/file_access/src/js_file_access_ext_ability.cpp index ce717b20d73e17973c54e3ddd6debd67aa730d3b..3127c92f00f6ddbeb545791ef1dbc3e5707e219b 100644 --- a/interfaces/inner_api/file_access/src/js_file_access_ext_ability.cpp +++ b/interfaces/inner_api/file_access/src/js_file_access_ext_ability.cpp @@ -31,6 +31,7 @@ #include "napi_common_util.h" #include "napi_common_want.h" #include "napi_remote_object.h" +#include "n_error.h" namespace OHOS { namespace FileAccessFwk { @@ -41,10 +42,13 @@ namespace { constexpr size_t ARGC_THREE = 3; constexpr size_t ARGC_FOUR = 4; constexpr size_t MAX_ARG_COUNT = 5; + constexpr int COPY_EXCEPTION = -1; + constexpr int COPY_NOEXCEPTION = -2; } using namespace OHOS::AppExecFwk; using namespace OHOS::AbilityRuntime; +using namespace OHOS::FileManagement::LibN; using OHOS::Security::AccessToken::AccessTokenKit; JsFileAccessExtAbility* JsFileAccessExtAbility::Create(const std::unique_ptr &runtime) @@ -635,6 +639,116 @@ int JsFileAccessExtAbility::Move(const Uri &sourceFile, const Uri &targetParent, return ERR_OK; } +static void TranslateCopyResult(CopyResult ©Result) +{ + if (errCodeTable.find(copyResult.errCode) != errCodeTable.end()) { + copyResult.errCode = errCodeTable.at(copyResult.errCode).first; + if (copyResult.errMsg.empty()) { + copyResult.errMsg = errCodeTable.at(copyResult.errCode).second; + } + } +} + +static bool ParserGetJsCopyResult(NativeEngine &engine, NativeValue *nativeValue, + std::vector ©Result, int ©Ret) +{ + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "ParserGetJsCopyResult"); + NativeObject *obj = ConvertNativeValueTo(nativeValue); + if (obj == nullptr) { + HILOG_ERROR("Convert js object fail."); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return false; + } + + bool ret = ConvertFromJsValue(engine, obj->GetProperty("code"), copyRet); + if (copyRet == ERR_OK) { + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + HILOG_DEBUG("copy success"); + return true; + } + + NativeArray *nativeArray = ConvertNativeValueTo(obj->GetProperty("result")); + if (!ret || nativeArray == nullptr) { + HILOG_ERROR("nativeArray is nullptr"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return false; + } + + for (uint32_t i = 0; i < nativeArray->GetLength(); i++) { + NativeValue *nativeCopyResult = nativeArray->GetElement(i); + if (nativeCopyResult == nullptr) { + HILOG_ERROR("get native FileInfo fail."); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return false; + } + + obj = ConvertNativeValueTo(nativeCopyResult); + if (obj == nullptr) { + HILOG_ERROR("Convert js object fail."); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return false; + } + + CopyResult result; + if (copyRet == COPY_NOEXCEPTION) { + ret = ret && ConvertFromJsValue(engine, obj->GetProperty("sourceUri"), result.sourceUri); + ret = ret && ConvertFromJsValue(engine, obj->GetProperty("destUri"), result.destUri); + } + if ((copyRet == COPY_NOEXCEPTION) || (copyRet == COPY_EXCEPTION)) { + ret = ret && ConvertFromJsValue(engine, obj->GetProperty("errCode"), result.errCode); + } + if (!ret) { + HILOG_ERROR("Convert js value fail."); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ret; + } + + TranslateCopyResult(result); + copyResult.push_back(result); + } + + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return true; +} + +int JsFileAccessExtAbility::Copy(const Uri &sourceUri, const Uri &destUri, std::vector ©Result, bool force) +{ + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "Copy"); + auto argParser = [sourceUri, destUri, force](NativeEngine &engine, NativeValue* argv[], size_t &argc) -> bool { + NativeValue *srcNativeUri = engine.CreateString(sourceUri.ToString().c_str(), + sourceUri.ToString().length()); + NativeValue *dstNativeUri = engine.CreateString(destUri.ToString().c_str(), destUri.ToString().length()); + NativeValue *forceCopy = engine.CreateBoolean(force); + if (srcNativeUri == nullptr || dstNativeUri == nullptr || forceCopy == nullptr) { + HILOG_ERROR("create arguments native js value fail."); + return false; + } + argv[ARGC_ZERO] = srcNativeUri; + argv[ARGC_ONE] = dstNativeUri; + argv[ARGC_TWO] = forceCopy; + argc = ARGC_THREE; + return true; + }; + + int copyRet = COPY_EXCEPTION; + auto retParser = [©Result, ©Ret](NativeEngine &engine, NativeValue *result) -> bool { + return ParserGetJsCopyResult(engine, result, copyResult, copyRet); + }; + + auto errCode = CallJsMethod("copy", jsRuntime_, jsObj_.get(), argParser, retParser); + if (errCode != ERR_OK) { + HILOG_ERROR("CallJsMethod error, code:%{public}d.", errCode); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + CopyResult result { "", "", errCode, ""}; + TranslateCopyResult(result); + copyResult.push_back(result); + return COPY_EXCEPTION; + } + + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return copyRet; +} + int JsFileAccessExtAbility::Rename(const Uri &sourceFile, const std::string &displayName, Uri &newFile) { StartTrace(HITRACE_TAG_FILEMANAGEMENT, "Rename"); diff --git a/services/file_extension_hap/entry/src/main/ets/FileExtensionAbility/FileExtensionAbility.ts b/services/file_extension_hap/entry/src/main/ets/FileExtensionAbility/FileExtensionAbility.ts index 7371de9c7c005e942b2212824dc4a7bcbf44ca12..bb7140524eb2774935315cfc0ce657afe0cd412c 100644 --- a/services/file_extension_hap/entry/src/main/ets/FileExtensionAbility/FileExtensionAbility.ts +++ b/services/file_extension_hap/entry/src/main/ets/FileExtensionAbility/FileExtensionAbility.ts @@ -33,8 +33,12 @@ const DOMAIN_CODE = 0x0001; const TAG = 'ExternalFileManager'; const ERR_OK = 0; const ERR_ERROR = -1; +const COPY_EXCEPTION = -1; +const COPY_NOEXCEPTION = -2; +const E_FAULT = 13900013; const E_EXIST = 13900015; const E_NOEXIST = 13900002; +const E_INVAL = 13900020; const E_URIS = 14300002; const E_GETRESULT = 14300004; let callbackFun = null; @@ -393,6 +397,175 @@ export default class FileExtAbility extends Extension { } } + getCopyReturnValue(sourceUri, destUri, errCode, errMsg, ret) + { + let copyResult = [ + { + sourceUri: sourceUri, + destUri: destUri, + errCode: errCode, + errMsg: errMsg, + }, + ]; + return { + result: copyResult, + code: ret, + }; + } + + checkCopyArguments(sourceFileUri, targetParentUri) + { + if (!this.checkUri(sourceFileUri) || !this.checkUri(targetParentUri)) { + hilog.error(DOMAIN_CODE, TAG, 'check arguments error, invalid arguments'); + return this.getCopyReturnValue('', '', E_URIS, '', COPY_EXCEPTION); + } + + let displayName = this.getFileName(sourceFileUri); + let newFileOrDirUri = this.genNewFileUri(targetParentUri, displayName); + let oldPath = this.getPath(sourceFileUri); + let newPath = this.getPath(newFileOrDirUri); + if (oldPath === newPath) { + hilog.error(DOMAIN_CODE, TAG, 'the source and target files are the same file'); + return this.getCopyReturnValue(sourceFileUri, targetParentUri, E_INVAL, '', COPY_NOEXCEPTION); + } else if (newPath.indexOf(oldPath) === 0) { + hilog.error(DOMAIN_CODE, TAG, 'copy to a subdirectory of the source directory'); + return this.getCopyReturnValue('', '', E_FAULT, '', COPY_EXCEPTION); + } + + try { + fileio.accessSync(oldPath); + let stat = fileio.statSync(this.getPath(targetParentUri)); + if (!stat || !stat.isDirectory()) { + hilog.error(DOMAIN_CODE, TAG, 'target is not directory, invalid arguments'); + return this.getCopyReturnValue('', targetParentUri, E_INVAL, '', COPY_NOEXCEPTION); + } + } catch (e) { + hilog.error(DOMAIN_CODE, TAG, 'copy error ' + e.message); + return this.getCopyReturnValue('', '', e.code, '', COPY_EXCEPTION); + } + return { + result: [], + code: ERR_OK, + } + } + + copyCallback(sourcePath, copyContext, isDirectory) { + try { + let destPath = sourcePath.replace(copyContext.oldPath, copyContext.newPath); + if (isDirectory) { + try { + fileio.accessSync(destPath); + } catch (e) { + fileio.mkdirSync(destPath); + } + } else { + try { + fileio.accessSync(destPath); + if (copyContext.force) { + let ret = fileio.delete(destPath); + if (ret != ERR_OK) { + hilog.error(DOMAIN_CODE, TAG, 'delete failed'); + return copyContext.getCopyReturnValue('', '', ret, '', COPY_EXCEPTION); + } + fileio.copyFileSync(sourcePath, destPath); + return copyContext.getCopyReturnValue('', '', ERR_OK, '', ERR_OK); + } else { + let sourceFileUri = URI_SCHEME + '/' + BUNDLE_NAME + sourcePath; + let destFileUri = URI_SCHEME + '/' + BUNDLE_NAME + destPath; + let ret = copyContext.getCopyReturnValue(sourceFileUri, destFileUri, E_EXIST, '', COPY_NOEXCEPTION); + return ret; + } + } catch (e) { + fileio.copyFileSync(sourcePath, destPath); + return copyContext.getCopyReturnValue('', '', ERR_OK, '', ERR_OK); + } + } + } catch (e) { + hilog.error(DOMAIN_CODE, TAG, 'CopyCallback error ' + e.message); + return this.getCopyReturnValue('', '', e.code, '', COPY_EXCEPTION); + } + return copyContext.getCopyReturnValue('', '', ERR_OK, '', ERR_OK); + } + + processCopyReturnValue(ret, copyRet) + { + if (ret.code == COPY_EXCEPTION) { + copyRet = ret; + } + if (ret.code == COPY_NOEXCEPTION) { + for (let index in ret.result) { + copyRet.result.push(ret.result[index]); + } + copyRet.code = ret.code; + } + } + + CopyOperation(path, copyContext) { + let copyRet = { + result: [], + code: ERR_OK, + }; + try { + let stat = fileio.statSync(path); + if (stat.isDirectory()) { + let dir = fileio.opendirSync(path); + let hasNextFile = true; + let ret = copyContext.copyCallback(path, copyContext, true); + this.processCopyReturnValue(ret, copyRet); + if (copyRet.code == COPY_EXCEPTION) { + return copyRet; + } + while (hasNextFile) { + try { + let dirent = dir.readSync(); + let ret = this.CopyOperation(path + '/' + dirent.name, copyContext, copyContext.copyCallback); + this.processCopyReturnValue(ret, copyRet); + if (copyRet.code == COPY_EXCEPTION) { + return copyRet; + } + } catch (e) { + hasNextFile = false; + } + } + dir.closeSync(); + } else { + let ret = copyContext.copyCallback(path, copyContext, false); + this.processCopyReturnValue(ret, copyRet); + if (copyRet.code == COPY_EXCEPTION) { + return copyRet; + } + } + } catch (e) { + hilog.error(DOMAIN_CODE, TAG, 'CopyOperation error ' + e.message); + return this.getCopyReturnValue('', '', e.code, '', COPY_EXCEPTION); + } + return copyRet; + } + + copy(sourceFileUri, targetParentUri, force) { + let checkRet= this.checkCopyArguments(sourceFileUri, targetParentUri); + if (checkRet.code != ERR_OK) { + hilog.error(DOMAIN_CODE, TAG, 'copy error: check arguments error'); + return checkRet; + } + + let displayName = this.getFileName(sourceFileUri); + let newFileOrDirUri = this.genNewFileUri(targetParentUri, displayName); + let oldPath = this.getPath(sourceFileUri); + let newPath = this.getPath(newFileOrDirUri); + let sourcePath = oldPath; + let copyContext = { + force: force, + oldPath: oldPath, + newPath: newPath, + getCopyReturnValue: this.getCopyReturnValue, + copyCallback: this.copyCallback, + }; + let copyRet = this.CopyOperation(sourcePath, copyContext); + + return copyRet; + } + access(sourceFileUri): {boolean, number} { if (!this.checkUri(sourceFileUri)) { hilog.error(DOMAIN_CODE, TAG, 'access checkUri fail'); @@ -614,4 +787,4 @@ export default class FileExtAbility extends Extension { code: ERR_OK, }; } -}; \ No newline at end of file +}; diff --git a/utils/file_access_framework_errno.h b/utils/file_access_framework_errno.h index 93c0b841162b95de076ecf9b291aae147d2f79a6..7e6208f5f56527182eb9325f3d064a218a842596 100644 --- a/utils/file_access_framework_errno.h +++ b/utils/file_access_framework_errno.h @@ -36,9 +36,9 @@ enum { E_INIT_NOTIFY_AGENT, // Fail to init notification agent E_NOTIFY, // Fail to notify agent E_CONNECT, // Fail to connect file access extension ability + E_COUNT, // Too many records E_PERMISSION = 201, // Permission verification failed - E_PERMISSION_SYS, // is not system app - E_COUNT + E_PERMISSION_SYS // is not system app }; } // namespace FileAccessFwk } // namespace OHOS