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 3097ee4a4706ebd4fcf417733cb1eaa3eca8695c..f3f8a3c40dbb505a92628b383eacc9878dd7ffcc 100644 --- a/frameworks/js/napi/file_access_module/napi_fileaccess_helper.cpp +++ b/frameworks/js/napi/file_access_module/napi_fileaccess_helper.cpp @@ -46,6 +46,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; @@ -678,7 +680,7 @@ static napi_value CreateObjectArray(napi_env env, std::vector result return copyResultArray; } -std::tuple GetCopyArguments(napi_env env, NFuncArg &funcArg) +static std::tuple GetCopyArguments(napi_env env, NFuncArg &funcArg) { bool retStatus = false; std::unique_ptr srcPath; @@ -717,6 +719,35 @@ std::tuple GetCopyArguments(napi_env env, return std::make_tuple(true, srcPathStr, destPathStr, force); } +static 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); @@ -745,7 +776,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); @@ -763,31 +793,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) || thirdArg.TypeIs(napi_undefined)) { - 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 80fe58d6e9313ae73faeed0fa64205aa6109288c..e08c07b4138a752ebcd7a4a54e17c76e56bb9dfc 100644 --- a/interfaces/inner_api/file_access/include/file_access_helper.h +++ b/interfaces/inner_api/file_access/include/file_access_helper.h @@ -105,6 +105,12 @@ private: std::shared_ptr GetConnectInfo(const std::string &bundleName); int CopyInsideService(Uri &sourceUri, Uri &destUri, std::vector ©Result, bool force = false); + 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 2b690e94cb000966e48052c20aef39991927f433..a669d63cfe322046ad3e82d229ae0f259bf93381 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 130b4ab3469d0f722dc31f5d0f9240876e996561..87708f0db1000225209c672a68f0d52b0a3bab65 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 @@ -318,17 +318,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) +static 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"); @@ -348,16 +340,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; - } - +static 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"); @@ -367,7 +356,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; @@ -392,6 +381,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, code: %{public}d", ret); + 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 df45fb58f25f88bcbd84a871c604c26afcf02477..5ca6efd5f4c07dfa9cee22e177d1b60416143da4 100644 --- a/interfaces/inner_api/file_access/src/file_access_helper.cpp +++ b/interfaces/inner_api/file_access/src/file_access_helper.cpp @@ -646,7 +646,7 @@ int FileAccessHelper::Move(Uri &sourceFile, Uri &targetParent, Uri &newFile) return ERR_OK; } -static bool IsMediaUri(Uri &uri) +bool FileAccessHelper::IsMediaUri(Uri &uri) { string scheme = uri.GetScheme(); if (scheme == FILE_SCHEME_NAME) { @@ -655,7 +655,34 @@ static bool IsMediaUri(Uri &uri) return false; } -static int ThrowExceptionByErrorCode(int errCode, CopyResult ©Result) +bool FileAccessHelper::IsExternalUri(Uri &uri) +{ + string scheme = uri.GetScheme(); + if (scheme == FILE_SCHEME_NAME) { + return uri.GetAuthority() == 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; @@ -666,6 +693,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; @@ -679,12 +707,22 @@ 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::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::CopyInsideService(Uri &sourceUri, Uri &destUri, std::vector ©Result, bool force) { StartTrace(HITRACE_TAG_FILEMANAGEMENT, "CopyInsideService"); @@ -694,23 +732,24 @@ 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, "" }; + ret = TranslateCopyResult(ret, result); copyResult.push_back(result); - return COPY_EXCEPTION; + return ret; } FinishTrace(HITRACE_TAG_FILEMANAGEMENT); return ret; @@ -720,29 +759,38 @@ 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 { - 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 e80da2eb40718ed35a6eb4b47885c679c88fd61a..96b723e001454166fb366d035104b97b67e7da10 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 @@ -32,6 +32,7 @@ #include "napi_common_util.h" #include "napi_common_want.h" #include "napi_remote_object.h" +#include "n_error.h" namespace OHOS { namespace FileAccessFwk { @@ -42,10 +43,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) @@ -584,6 +588,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 24284248546a2118204b52753f554d51a9c5268e..997fc220ceedbb89e84468c9542fdf804a466a59 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 @@ -27,8 +27,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; const CREATE_EVENT_CODE = 0x00000100; @@ -459,6 +463,173 @@ 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 = FILE_PREFIX_NAME + BUNDLE_NAME + sourcePath; + let destFileUri = FILE_PREFIX_NAME + 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} { sourceFileUri = this.decode(sourceFileUri); if (sourceFileUri === '') { @@ -792,4 +963,4 @@ export default class FileExtAbility extends Extension { } return ERR_OK; } -}; \ No newline at end of file +}; diff --git a/services/native/file_access_service/include/file_access_service.h b/services/native/file_access_service/include/file_access_service.h index 8ad0e962938120afc0ce704d25d8f7bd95307ba4..afc984ecaa4d4f88ebd18a90978ddfb0b57a3cf5 100644 --- a/services/native/file_access_service/include/file_access_service.h +++ b/services/native/file_access_service/include/file_access_service.h @@ -16,18 +16,68 @@ #ifndef FILE_ACCESS_SERVICE_H #define FILE_ACCESS_SERVICE_H +#include #include #include #include +#include #include #include "file_access_service_stub.h" +#include "holder_manager.h" #include "iremote_object.h" #include "uri.h" namespace OHOS { namespace FileAccessFwk { +class ObserverContext { + public: + ObserverContext(sptr obs): obs_(obs) {} + virtual ~ObserverContext() = default; + sptr obs_ = nullptr; + void Ref() + { + if (ref_ == std::numeric_limits::max()) { + HILOG_ERROR("Ref is over max"); + return; + } + ref_++; + } + + void UnRef() + { + if (ref_ == 0) { + HILOG_ERROR("Ref is already zero"); + return; + } + ref_--; + } + + bool IsValid() + { + return ref_ > 0; + } + + bool EqualTo(std::shared_ptr observerContext) + { + return obs_->AsObject() == observerContext->obs_->AsObject(); + } + + private: + std::atomic ref_; +}; + +class ObserverNode { + public: + ObserverNode(const bool needChildNote): needChildNote_(needChildNote) {} + virtual ~ObserverNode() = default; + std::shared_ptr parent_; + std::vector> children_; + std::vector obsCodeList_; + bool needChildNote_; +}; + class FileAccessService final : public SystemAbility, public FileAccessServiceStub { DECLARE_SYSTEM_ABILITY(FileAccessService) @@ -45,11 +95,17 @@ public: int32_t OnChange(Uri uri, NotifyType notifyType) override; private: + void SendListNotify(const std::vector list, NotifyMessage ¬ifyMessage); + void RemoveRelations(std::string &uriStr, std::shared_ptr obsNode); + int FindUri(const std::string &uriStr, std::shared_ptr &outObsNode); FileAccessService(); bool IsServiceReady() const; static sptr instance_; bool ready_ = false; static std::mutex mutex_; + std::mutex nodeMutex_; + std::unordered_map> relationshipMap_; + HolderManager> obsManager_; }; } // namespace FileAccessFwk } // namespace OHOS diff --git a/services/native/file_access_service/include/holder_manager.h b/services/native/file_access_service/include/holder_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..fb287c46142cf1a853e3ed48fc3c915268588bb6 --- /dev/null +++ b/services/native/file_access_service/include/holder_manager.h @@ -0,0 +1,113 @@ +/* + * 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 HOLDER_MANAGER_H_ +#define HOLDER_MANAGER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace OHOS { +namespace FileAccessFwk { +template +class HolderManager { +public: + static constexpr int CODE_CAN_NOT_FIND = 0; + HolderManager() {} + + ~HolderManager() + { + std::lock_guard guard(holderMutex_); + holder_.clear(); + } + + uint32_t save(Type content) + { + uint32_t id; + do { + id = genId(); + } while (exist(id)); + std::lock_guard guard(holderMutex_); + holder_.insert(std::pair(id, content)); + return id; + } + + Type get(uint32_t id) + { + std::lock_guard guard(holderMutex_); + auto iter = holder_.find(id); + if (iter != holder_.end()) { + return iter->second; + } + return Type(); + } + + Type pop(uint32_t id) + { + std::lock_guard guard(holderMutex_); + auto iter = holder_.find(id); + if (iter != holder_.end()) { + auto res = iter->second; + holder_.erase(id); + return res; + } + return Type(); + } + + void release(uint32_t id) + { + std::lock_guard guard(holderMutex_); + if (holder_.count(id)) { + holder_.erase(id); + } + } + + bool exist(uint32_t id) + { + std::lock_guard guard(holderMutex_); + return holder_.count(id); + } + + uint32_t getId(std::function func) + { + auto haveIter = find_if(holder_.begin(), holder_.end(), + [func](const std::pair type) { + return func(type.second); + }); + if (haveIter == holder_.end()) { + return CODE_CAN_NOT_FIND; + } + return haveIter->first; + } + +private: + // key is automatic growth number + std::map holder_; + std::mutex holderMutex_; + std::atomic gId_ = 1; + uint32_t genId() + { + return gId_++; + } +}; +} // namespace FileAccessFwk +} // namespace OHOS +#endif // FRAMEWORKS_INNERKITSIMPL_UTILS_INCLUDE_IMAGE_HOLDER_MANAGER_H_ diff --git a/services/native/file_access_service/src/file_access_service.cpp b/services/native/file_access_service/src/file_access_service.cpp index 2e8c3e5e45eb0e0e8d6436f5eba3355b2b2c62d1..e1ccd19fb50e28ebedb32d6b910f8d98a6d55d88 100644 --- a/services/native/file_access_service/src/file_access_service.cpp +++ b/services/native/file_access_service/src/file_access_service.cpp @@ -80,19 +80,209 @@ bool FileAccessService::IsServiceReady() const return ready_; } +static bool IsParentUri(const string &comparedUriStr, string &srcUriStr) +{ + if ((comparedUriStr.empty()) || (srcUriStr.empty())) { + HILOG_ERROR("Uri is empty"); + return false; + } + size_t slashIndex = srcUriStr.rfind("/"); + if (slashIndex != string::npos) { + if (comparedUriStr.compare(srcUriStr.substr(0, slashIndex)) == 0) { + return true; + } + } + return false; +} + +static bool IsChildUri(const string &comparedUriStr, string &srcUriStr) +{ + if ((comparedUriStr.empty()) || (srcUriStr.empty())) { + HILOG_ERROR("Uri is empty"); + return false; + } + size_t slashIndex = comparedUriStr.rfind("/"); + if (slashIndex != string::npos) { + if (srcUriStr.compare(comparedUriStr.substr(0, slashIndex)) == 0) { + return true; + } + } + return false; +} + int32_t FileAccessService::RegisterNotify(Uri uri, const sptr &observer, bool notifyForDescendants) { + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "RegisterNotify"); + shared_ptr obsContext = make_shared(observer); + // find if obsManager_ has this callback. + uint32_t code = obsManager_.getId([obsContext](const shared_ptr &afterContext) { + return obsContext->EqualTo(afterContext); + }); + if (code == HolderManager>::CODE_CAN_NOT_FIND) { + // this is new callback, save this context + obsContext->Ref(); + code = obsManager_.save(obsContext); + } else { + // this callback is already in manager, add ref. + obsManager_.get(code)->Ref(); + } + string uriStr = uri.ToString(); + std::lock_guard lock(nodeMutex_); + auto iter = relationshipMap_.find(uriStr); + shared_ptr obsNode; + if (iter != relationshipMap_.end()) { + obsNode = iter->second; + // this node has this callback or not, if has this, unref manager. + auto haveCodeIter = find_if(obsNode->obsCodeList_.begin(), obsNode->obsCodeList_.end(), + [code](const uint32_t &listCode) { return code == listCode; }); + if (haveCodeIter != obsNode->obsCodeList_.end()) { + obsManager_.get(code)->UnRef(); + if (obsNode->needChildNote_ == notifyForDescendants) { + HILOG_DEBUG("Register same uri and same callback and same notifyForDescendants"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ERR_OK; + } + // need modify obsNode notifyForDescendants + obsNode->needChildNote_ = notifyForDescendants; + HILOG_DEBUG("Register same uri and same callback but need modify notifyForDescendants"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ERR_OK; + } + obsNode->obsCodeList_.push_back(code); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ERR_OK; + } + obsNode = make_shared(notifyForDescendants); + // add new node relations. + for (auto &[comUri, node] : relationshipMap_) { + if (IsParentUri(comUri, uriStr)) { + obsNode->parent_ = node; + node->children_.push_back(obsNode); + } + if (IsChildUri(comUri, uriStr)) { + obsNode->children_.push_back(node); + node->parent_ = obsNode; + } + } + // obsCodeList_ is to save callback number + obsNode->obsCodeList_.push_back(code); + relationshipMap_.insert(make_pair(uriStr, obsNode)); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ERR_OK; +} + +void FileAccessService::RemoveRelations(string &uriStr, shared_ptr obsNode) +{ + std::lock_guard lock(nodeMutex_); + for (auto &[comUri, node] : relationshipMap_) { + auto childrenIter = find_if(node->children_.begin(), node->children_.end(), + [obsNode](const std::shared_ptr &obsListNode) { return obsNode == obsListNode; }); + if (childrenIter != node->children_.end()) { + node->children_.erase(childrenIter); + } + if (IsChildUri(comUri, uriStr)) { + node->parent_ = nullptr; + } + } + relationshipMap_.erase(uriStr); +} + +int FileAccessService::FindUri(const string &uriStr, shared_ptr &outObsNode) +{ + std::lock_guard lock(nodeMutex_); + auto iter = relationshipMap_.find(uriStr); + if (iter == relationshipMap_.end()) { + HILOG_ERROR("Can not find uri"); + return E_CAN_NOT_FIND_URI; + } + outObsNode = iter->second; return ERR_OK; } int32_t FileAccessService::UnregisterNotify(Uri uri, const sptr &observer) { + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "UnregisterNotify"); + if (observer->AsObject() == nullptr) { + HILOG_ERROR("UnregisterNotify failed with invalid observer"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return E_IPCS; + } + string uriStr = uri.ToString(); + shared_ptr obsNode; + if (FindUri(uriStr, obsNode) != ERR_OK) { + HILOG_ERROR("Can not find unregisterNotify uri"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return E_CAN_NOT_FIND_URI; + } + // find if obsManager_ has this callback. + shared_ptr obsContext = make_shared(observer); + uint32_t code = obsManager_.getId([obsContext](const shared_ptr &afterContext) { + return obsContext->EqualTo(afterContext); + }); + if (code == HolderManager>::CODE_CAN_NOT_FIND) { + HILOG_ERROR("Can not find observer"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return E_CALLBACK_IS_NOT_REGISTER; + } + // find if this node has this callback. + auto haveCodeIter = find_if(obsNode->obsCodeList_.begin(), obsNode->obsCodeList_.end(), + [code](const uint32_t &listCode) { return code == listCode; }); + if (haveCodeIter == obsNode->obsCodeList_.end()) { + HILOG_ERROR("Uri node observer list don not has this observer"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return E_CALLBACK_AND_URI_HAS_NOT_RELATIONS; + } + obsNode->obsCodeList_.erase(haveCodeIter); + obsManager_.get(code)->UnRef(); + // node has other observers, do not need remove. + if (obsNode->obsCodeList_.size() != 0) { + HILOG_DEBUG("Has code do not stopWatcher"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ERR_OK; + } + // if data refcount is invalid, release this code. + if (!obsManager_.get(code)->IsValid()) { + obsManager_.release(code); + } + RemoveRelations(uriStr, obsNode); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); return ERR_OK; } +void FileAccessService::SendListNotify(const vector list, NotifyMessage ¬ifyMessage) +{ + for (uint32_t code : list) { + if (obsManager_.get(code) == nullptr) { + HILOG_ERROR("Failed to get obs code = %{private}ud", code); + } else { + obsManager_.get(code)->obs_->OnChange(notifyMessage); + } + } +} + int32_t FileAccessService::OnChange(Uri uri, NotifyType notifyType) { + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "OnChange"); + string uriStr = uri.ToString(); + shared_ptr node; + if (FindUri(uriStr, node) != ERR_OK) { + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + HILOG_ERROR("Can not find onChange uri"); + return E_CAN_NOT_FIND_URI; + } + NotifyMessage notifyMessage; + notifyMessage.notifyType_ = notifyType; + std::vector uris {uriStr}; + notifyMessage.uris_ = uris; + SendListNotify(node->obsCodeList_, notifyMessage); + if ((node->parent_ == nullptr) || (!node->parent_->needChildNote_)) { + HILOG_DEBUG("Do not need notify parent"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ERR_OK; + } + SendListNotify(node->parent_->obsCodeList_, notifyMessage); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); return ERR_OK; } } // namespace FileAccessFwk diff --git a/utils/file_access_framework_errno.h b/utils/file_access_framework_errno.h index 93c0b841162b95de076ecf9b291aae147d2f79a6..6f19f01917d43a72bcf042dc4e4b3172fbf18480 100644 --- a/utils/file_access_framework_errno.h +++ b/utils/file_access_framework_errno.h @@ -36,9 +36,12 @@ 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_CALLBACK_AND_URI_HAS_NOT_RELATIONS, // Uri and callback do not has relations, can not unregister + E_CALLBACK_IS_NOT_REGISTER, // CallBack is not registered, can not unregister + E_CAN_NOT_FIND_URI, // Can not find registered uri + 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