diff --git a/interfaces/kits/js/BUILD.gn b/interfaces/kits/js/BUILD.gn index 9334220be9d28e01972e60ed84bec52d044d4e8a..76496ab66f2f6132f3ab00f2bc0bbfdcc23b2b74 100644 --- a/interfaces/kits/js/BUILD.gn +++ b/interfaces/kits/js/BUILD.gn @@ -683,6 +683,7 @@ ohos_shared_library("ani_fs_class") { "src/mod_fs/properties/ani/access_ani.cpp", "src/mod_fs/properties/ani/close_ani.cpp", "src/mod_fs/properties/ani/copy_file_ani.cpp", + "src/mod_fs/properties/ani/copydir_ani.cpp", "src/mod_fs/properties/ani/listfile_ani.cpp", "src/mod_fs/properties/ani/mkdir_ani.cpp", "src/mod_fs/properties/ani/move_ani.cpp", @@ -695,6 +696,7 @@ ohos_shared_library("ani_fs_class") { "src/mod_fs/properties/ani/write_ani.cpp", "src/mod_fs/properties/close_core.cpp", "src/mod_fs/properties/copy_file_core.cpp", + "src/mod_fs/properties/copydir_core.cpp", "src/mod_fs/properties/listfile_core.cpp", "src/mod_fs/properties/mkdir_core.cpp", "src/mod_fs/properties/move_core.cpp", diff --git a/interfaces/kits/js/src/common/ani_helper/error_handler.cpp b/interfaces/kits/js/src/common/ani_helper/error_handler.cpp index a9e87cb828a602ae4995180c6b59a41a66c8dfb7..d300bb8c2d06ceb9e7d2727f5784a0c43ab88ee6 100644 --- a/interfaces/kits/js/src/common/ani_helper/error_handler.cpp +++ b/interfaces/kits/js/src/common/ani_helper/error_handler.cpp @@ -17,11 +17,11 @@ namespace OHOS::FileManagement::ModuleFileIO::ANI { -ani_status ErrorHandler::Throw(ani_env *env, int32_t code, const std::string &errMsg) +ani_status ErrorHandler::Throw( + ani_env *env, int32_t code, const std::string &errMsg, const std::optional &errData) { const char *className = "L@ohos/base/BusinessError;"; - const char *name = "BusinessError"; - return Throw(env, className, name, code, errMsg); + return Throw(env, className, code, errMsg, errData); } } // namespace OHOS::FileManagement::ModuleFileIO::ANI \ No newline at end of file diff --git a/interfaces/kits/js/src/common/ani_helper/error_handler.h b/interfaces/kits/js/src/common/ani_helper/error_handler.h index 59c41c11436572d555aab35aa47ee003427564ef..c64dce23dbd0fd5e572f6caf2fa2e838e9372cd9 100644 --- a/interfaces/kits/js/src/common/ani_helper/error_handler.h +++ b/interfaces/kits/js/src/common/ani_helper/error_handler.h @@ -28,9 +28,10 @@ namespace OHOS::FileManagement::ModuleFileIO::ANI { class ErrorHandler { public: - static ani_status Throw(ani_env *env, int32_t code, const std::string &errMsg); + static ani_status Throw( + ani_env *env, int32_t code, const std::string &errMsg, const std::optional &errData = nullopt); - static ani_status Throw(ani_env *env, int32_t code) + static ani_status Throw(ani_env *env, int32_t code, const std::optional &errData = nullopt) { if (env == nullptr) { HILOGE("Invalid parameter env"); @@ -40,7 +41,7 @@ public: return Throw(env, std::move(err)); } - static ani_status Throw(ani_env *env, const FsError &err) + static ani_status Throw(ani_env *env, const FsError &err, const std::optional &errData = nullopt) { if (env == nullptr) { HILOGE("Invalid parameter env"); @@ -52,8 +53,8 @@ public: } private: - static ani_status Throw( - ani_env *env, const char *className, const char *name, int32_t code, const std::string &errMsg) + static ani_status Throw(ani_env *env, const char *className, int32_t code, const std::string &errMsg, + const std::optional &errData = nullopt) { if (env == nullptr) { HILOGE("Invalid parameter env"); @@ -65,12 +66,7 @@ private: return ANI_INVALID_ARGS; } - if (name == nullptr) { - HILOGE("Invalid parameter name"); - return ANI_INVALID_ARGS; - } - - auto [status, err] = CreateErrorObj(env, className, name, code, errMsg); + auto [status, err] = CreateErrorObj(env, className, code, errMsg); if (status != ANI_OK) { HILOGE("Create error object failed!"); @@ -85,8 +81,8 @@ private: return ANI_OK; } - static std::tuple CreateErrorObj( - ani_env *env, const char *className, const char *name, int32_t code, const std::string &errMsg) + static std::tuple CreateErrorObj(ani_env *env, const char *className, int32_t code, + const std::string &errMsg, const std::optional &errData = nullopt) { ani_class cls; if (ANI_OK != env->FindClass(className, &cls)) { @@ -113,11 +109,6 @@ private: } ani_status status = ANI_ERROR; - status = AniHelper::SetFieldValue(env, cls, obj, "name", name); - if (status != ANI_OK) { - HILOGE("Set field 'name' value failed"); - return { status, nullptr }; - } status = AniHelper::SetFieldValue(env, cls, obj, "message", errMsg); if (status != ANI_OK) { @@ -130,6 +121,14 @@ private: HILOGE("Set field 'code' value failed"); return { status, nullptr }; } + + if (errData.has_value()) { + status = AniHelper::SetFieldValue(env, cls, obj, "data", errData.value()); + if (status != ANI_OK) { + HILOGE("Set field 'data' value failed"); + return { status, nullptr }; + } + } ani_error err = static_cast(obj); return { ANI_OK, std::move(err) }; } diff --git a/interfaces/kits/js/src/mod_fs/ani/bind_function_class.cpp b/interfaces/kits/js/src/mod_fs/ani/bind_function_class.cpp index 065495c9ade9c4c775ce494fa218d0c382077459..0dbc821f60e6325153b3f188ab59d162597b143a 100644 --- a/interfaces/kits/js/src/mod_fs/ani/bind_function_class.cpp +++ b/interfaces/kits/js/src/mod_fs/ani/bind_function_class.cpp @@ -21,6 +21,7 @@ #include "bind_function.h" #include "close_ani.h" #include "copy_file_ani.h" +#include "copydir_ani.h" #include "file_ani.h" #include "filemgmt_libhilog.h" #include "listfile_ani.h" @@ -74,6 +75,7 @@ static ani_status BindStaticMethods(ani_env *env) std::array methods = { ani_native_function { "closeSync", nullptr, reinterpret_cast(CloseAni::CloseSync) }, + ani_native_function { "copyDirSync", nullptr, reinterpret_cast(CopyDirAni::CopyDirSync) }, ani_native_function { "copyFileSync", nullptr, reinterpret_cast(CopyFileAni::CopyFileSync) }, ani_native_function { "doAccessSync", nullptr, reinterpret_cast(AccessAni::AccessSync3) }, ani_native_function { "listFileSync", nullptr, reinterpret_cast(ListFileAni::ListFileSync) }, diff --git a/interfaces/kits/js/src/mod_fs/ani/ets/@ohos.file.fs.ets b/interfaces/kits/js/src/mod_fs/ani/ets/@ohos.file.fs.ets index 67eb95add30a0cd803f0542ae46c90f870189651..82505924877894b4718ae1a33459c09398c91b6a 100644 --- a/interfaces/kits/js/src/mod_fs/ani/ets/@ohos.file.fs.ets +++ b/interfaces/kits/js/src/mod_fs/ani/ets/@ohos.file.fs.ets @@ -100,6 +100,59 @@ function closeSync(file: number | File): void { return FileIoImpl.closeSync(file) } +function copyDirSync(src: string, dest: string, mode?: number): void { + return FileIoImpl.copyDirSync(src, dest, mode); +} + +function copyDir(src: string, dest: string, mode?: number): Promise { + return new Promise((resolve: (result: undefined) => void, reject: (e: BusinessError>) => void): void => { + if (mode === undefined) { + let promise = taskpool.execute((src: string, dest: string): undefined => + FileIoImpl.copyDirSync(src, dest), src, dest); + promise.then((ret: NullishType): void => { + resolve(undefined); + }).catch((e: BusinessError>): void => { + reject(e); + }); + } else { + let promise = taskpool.execute((src: string, dest: string, mode: number): undefined => + FileIoImpl.copyDirSync(src, dest, mode), src, dest, mode); + promise.then((ret: NullishType): void => { + resolve(undefined); + }).catch((e: BusinessError>): void => { + reject(e); + }); + } + + }); +} + +function copyDir(src: string, dest: string, callback: AsyncCallback>): void { + let promise = taskpool.execute((src: string, dest: string): undefined => + FileIoImpl.copyDirSync(src, dest), src, dest); + promise.then((ret: NullishType): void => { + let e = new BusinessError>(); + e.code = 0; + e.data = new Array(0); + callback(e, undefined); + }).catch((e: BusinessError>): void => { + callback(e, undefined); + }); +} + +function copyDir(src: string, dest: string, mode: number, callback: AsyncCallback>): void { + let promise = taskpool.execute((src: string, dest: string, mode: number): undefined => + FileIoImpl.copyDirSync(src, dest, mode), src, dest, mode); + promise.then((ret: NullishType): void => { + let e = new BusinessError>(); + e.code = 0; + e.data = new Array(0); + callback(e, undefined); + }).catch((e: BusinessError>): void => { + callback(e, undefined); + }); +} + function mkdirSync(path: string): void { return FileIoImpl.mkdirSync(path) } @@ -164,6 +217,61 @@ function mkdir(path: string, recursion: boolean, callback: AsyncCallback): }); } +function moveDirSync(src: string, dest: string, mode?: number): void { + return FileIoImpl.movedirSync(src, dest, mode) +} + +function moveDir(src: string, dest: string, mode?: number): Promise { + return new Promise((resolve: (result: undefined) => void, + reject: (e: BusinessError>) => void): void => { + if (mode === undefined) { + let promise = taskpool.execute((src: string, dest: string): undefined => { + return FileIoImpl.movedirSync(src, dest); + }, src, dest); + promise.then((ret: NullishType): void => { + resolve(undefined); + }).catch((e: BusinessError>): void => { + reject(e); + }); + } else { + let promise = taskpool.execute((src: string, dest: string, mode: number): undefined => { + return FileIoImpl.movedirSync(src, dest, mode); + }, src, dest, mode); + promise.then((ret: NullishType): void => { + resolve(undefined); + }).catch((e: BusinessError>): void => { + reject(e); + }); + } + }) +} + +function moveDir(src: string, dest: string, callback: AsyncCallback>): void { + let promise = taskpool.execute((src: string, dest: string): undefined => + FileIoImpl.movedirSync(src, dest), src, dest); + promise.then((ret: NullishType): void => { + let e = new BusinessError>(); + e.code = 0; + e.data = new Array(0); + callback(e, undefined); + }).catch((e: BusinessError>): void => { + callback(e, undefined); + }); +} + +function moveDir(src: string, dest: string, mode: number, callback: AsyncCallback>): void { + let promise = taskpool.execute((src: string, dest: string, mode: number): undefined => + FileIoImpl.movedirSync(src, dest, mode), src, dest, mode); + promise.then((ret: NullishType): void => { + let e = new BusinessError>(); + e.code = 0; + e.data = new Array(0); + callback(e, undefined); + }).catch((e: BusinessError>): void => { + callback(e, undefined); + }); +} + function moveFileSync(src: string, dest: string, mode?: number): void { return FileIoImpl.moveFileSync(src, dest, mode); } @@ -623,6 +731,21 @@ class StatInner implements Stat { native isSymbolicLink(): boolean; } +export interface ConflictFiles { + srcFile: string; + destFile: string; +} + +class ConflictFilesInner implements ConflictFiles { + srcFile: string = ""; + destFile: string = ""; + + constructor(src: string, dest: string) { + this.srcFile = src; + this.destFile = dest; + } +} + class FileIoImpl { static { @@ -633,6 +756,8 @@ class FileIoImpl { static native closeSync(file: number | File): void; + static native copyDirSync(src: string, dest: string, mode?: number): void; + static native copyFileSync(src: string | number, dest: string | number, mode?: number): void; static native listFileSync(path: string, options?: ListFileOptions): string[]; @@ -641,6 +766,8 @@ class FileIoImpl { static native mkdirSync(path: string, recursion: boolean): void; + static native movedirSync(src: string, dest: string, mode?: number): void; + static native moveFileSync(src: String, dest: String, mode?: number): void; static native openSync(path: String, mode?: number): File; diff --git a/interfaces/kits/js/src/mod_fs/properties/ani/copydir_ani.cpp b/interfaces/kits/js/src/mod_fs/properties/ani/copydir_ani.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a35cef99f74244498b40648b293864153300be0 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/ani/copydir_ani.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2025 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 "copydir_ani.h" + +#include +#include "copydir_core.h" +#include "error_handler.h" +#include "filemgmt_libhilog.h" +#include "type_converter.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { +using namespace std; +using namespace OHOS::FileManagement::ModuleFileIO; + +static tuple ToConflictFiles(ani_env *env, const ConflictFiles &files) +{ + static const char *className = "L@ohos/file/fs/ConflictFilesInner;"; + ani_class cls; + ani_status ret; + if ((ret = env->FindClass(className, &cls)) != ANI_OK) { + HILOGE("Cannot find class %{private}s, err: %{private}d", className, ret); + return { false, nullptr }; + } + + ani_method ctor; + if ((ret = env->Class_FindMethod(cls, "", "Lstd/core/String;Lstd/core/String;:V", &ctor)) != ANI_OK) { + HILOGE("Cannot find class %{private}s constructor method, err: %{private}d", className, ret); + return { false, nullptr }; + } + + auto [succSrc, src] = TypeConverter::ToAniString(env, files.srcFiles); + if (!succSrc) { + HILOGE("Convert ConflictFiles srcFiles to ani string failed!"); + return { false, nullptr }; + } + + auto [succDest, dest] = TypeConverter::ToAniString(env, files.destFiles); + if (!succSrc) { + HILOGE("Convert ConflictFiles destFiles to ani string failed!"); + return { false, nullptr }; + } + + ani_object obj; + if ((ret = env->Object_New(cls, ctor, &obj, src, dest)) != ANI_OK) { + HILOGE("Create ConflictFiles object failed!, err: %{private}d", ret); + return { false, nullptr }; + } + + return { true, obj }; +} + +static tuple> ToConflictFilesArray( + ani_env *env, const optional> &errFiles) +{ + if (!errFiles.has_value()) { + return { true, nullopt }; + } + static const char *className = "Lescompat/Array;"; + ani_class cls = nullptr; + ani_status ret; + + if ((ret = env->FindClass("Lescompat/Array;", &cls)) != ANI_OK) { + HILOGE("Cannot find class %{private}s, err: %{private}d", className, ret); + return { false, nullopt }; + } + + ani_method ctor; + if ((ret = env->Class_FindMethod(cls, "", "I:V", &ctor)) != ANI_OK) { + HILOGE("Cannot find class %{private}s constructor method, err: %{private}d", className, ret); + return { false, nullopt }; + } + + ani_object arr; + auto files = errFiles.value(); + if ((ret = env->Object_New(cls, ctor, &arr, files.size())) != ANI_OK) { + HILOGE("Create Array failed!, err: %{private}d", ret); + return { false, nullopt }; + } + + ani_size index = 0; + for (const auto &errFile : files) { + auto [succ, fileObj] = ToConflictFiles(env, errFile); + if (succ != ANI_OK) { + return { false, nullopt }; + } + + if ((ret = env->Object_CallMethodByName_Void(arr, "$_set", "ILstd/core/Object;:V", index, fileObj)) != ANI_OK) { + HILOGE("Add element to Array failed, err: %{private}d", ret); + return { false, nullopt }; + } + index++; + } + + return { true, make_optional(move(arr)) }; +} + +void CopyDirAni::CopyDirSync( + ani_env *env, [[maybe_unused]] ani_class clazz, ani_string src, ani_string dest, ani_object mode) +{ + error_code errCode; + + auto [succSrc, srcPath] = TypeConverter::ToUTF8String(env, src); + if (!succSrc) { + HILOGE("Invalid src, errCode = %{public}d", errCode.value()); + ErrorHandler::Throw(env, EINVAL); + return; + } + + auto [succDest, destPath] = TypeConverter::ToUTF8String(env, dest); + if (!succDest) { + HILOGE("Invalid dest, errCode = %{public}d", errCode.value()); + ErrorHandler::Throw(env, EINVAL); + return; + } + + auto [succMode, optMode] = TypeConverter::ToOptionalInt32(env, mode); + if (!succMode) { + HILOGE("Invalid mode"); + ErrorHandler::Throw(env, EINVAL); + return; + } + + auto [fsResult, errFiles] = CopyDirCore::DoCopyDir(srcPath, destPath, optMode); + if (!fsResult.IsSuccess()) { + HILOGE("DoCopyFile failed!"); + auto [succ, errData] = ToConflictFilesArray(env, errFiles); + if (!succ) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + } + const FsError &err = fsResult.GetError(); + ErrorHandler::Throw(env, err, errData); + return; + } +} +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/ani/copydir_ani.h b/interfaces/kits/js/src/mod_fs/properties/ani/copydir_ani.h new file mode 100644 index 0000000000000000000000000000000000000000..85c92cc74df2f882d930c79f4a20a7afa722a70e --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/ani/copydir_ani.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 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 INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_ANI_COPYDIR_ANI_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_ANI_COPYDIR_ANI_H + +#include + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { +class CopyDirAni final { +public: + static void CopyDirSync( + ani_env *env, [[maybe_unused]] ani_class clazz, ani_string src, ani_string dest, ani_object mode); +}; +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_ANI_COPYDIR_ANI_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/ani/movedir_ani.cpp b/interfaces/kits/js/src/mod_fs/properties/ani/movedir_ani.cpp new file mode 100644 index 0000000000000000000000000000000000000000..23846f7411f902c7e2b8e864bb60bc0d4287e282 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/ani/movedir_ani.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2025 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 "movedir_ani.h" + +#include +#include "error_handler.h" +#include "filemgmt_libhilog.h" +#include "movedir_core.h" +#include "type_converter.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { + +static tuple ToConflictFiles(ani_env *env, const ErrFiles &files) +{ + static const char *className = "L@ohos/file/fs/ConflictFilesInner;"; + ani_class cls; + ani_status ret; + if ((ret = env->FindClass(className, &cls)) != ANI_OK) { + HILOGE("Cannot find class %{private}s, err: %{private}d", className, ret); + return { false, nullptr }; + } + + ani_method ctor; + if ((ret = env->Class_FindMethod(cls, "", "Lstd/core/String;Lstd/core/String;:V", &ctor)) != ANI_OK) { + HILOGE("Cannot find class %{private}s constructor method, err: %{private}d", className, ret); + return { false, nullptr }; + } + + auto [succSrc, src] = TypeConverter::ToAniString(env, files.srcFiles); + if (!succSrc) { + HILOGE("Convert ConflictFiles srcFiles to ani string failed!"); + return { false, nullptr }; + } + + auto [succDest, dest] = TypeConverter::ToAniString(env, files.destFiles); + if (!succSrc) { + HILOGE("Convert ConflictFiles destFiles to ani string failed!"); + return { false, nullptr }; + } + + ani_object obj; + if ((ret = env->Object_New(cls, ctor, &obj, src, dest)) != ANI_OK) { + HILOGE("Create ConflictFiles object failed!, err: %{private}d", ret); + return { false, nullptr }; + } + + return { true, obj }; +} + +static tuple> ToConflictFilesArray( + ani_env *env, const optional> &errFiles) +{ + if (!errFiles.has_value()) { + return { true, nullopt }; + } + static const char *className = "Lescompat/Array;"; + ani_class cls = nullptr; + ani_status ret; + + if ((ret = env->FindClass("Lescompat/Array;", &cls)) != ANI_OK) { + HILOGE("Cannot find class %{private}s, err: %{private}d", className, ret); + return { false, nullopt }; + } + + ani_method ctor; + if ((ret = env->Class_FindMethod(cls, "", "I:V", &ctor)) != ANI_OK) { + HILOGE("Cannot find class %{private}s constructor method, err: %{private}d", className, ret); + return { false, nullopt }; + } + + ani_object arr; + auto files = errFiles.value(); + if ((ret = env->Object_New(cls, ctor, &arr, files.size())) != ANI_OK) { + HILOGE("Create Array failed!, err: %{private}d", ret); + return { false, nullopt }; + } + + ani_size index = 0; + for (const auto &errFile : files) { + auto [succ, fileObj] = ToConflictFiles(env, errFile); + if (succ != ANI_OK) { + return { false, nullopt }; + } + + if ((ret = env->Object_CallMethodByName_Void(arr, "$_set", "ILstd/core/Object;:V", index, fileObj)) != ANI_OK) { + HILOGE("Add element to Array failed, err: %{private}d", ret); + return { false, nullopt }; + } + index++; + } + + return { true, make_optional(move(arr)) }; +} + +void MoveDirAni::MoveDirSync( + ani_env *env, [[maybe_unused]] ani_class clazz, ani_string src, ani_string dest, ani_object mode) +{ + auto [succSrc, srcPath] = TypeConverter::ToUTF8String(env, src); + auto [succDest, destPath] = TypeConverter::ToUTF8String(env, dest); + if (!succSrc || !succDest) { + HILOGE("The first/second argument requires filepath"); + ErrorHandler::Throw(env, EINVAL); + return; + } + + auto [succMode, optMode] = TypeConverter::ToOptionalInt32(env, mode); + if (!succMode) { + HILOGE("Failed to convert mode to int32"); + ErrorHandler::Throw(env, EINVAL); + return; + } + + auto [fsResult, errFiles] = MoveDirCore::DoMoveDir(srcPath, destPath, optMode); + if (!fsResult.IsSuccess()) { + HILOGE("DoCopyFile failed!"); + auto [succ, errData] = ToConflictFilesArray(env, errFiles); + if (!succ) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + } + const FsError &err = fsResult.GetError(); + ErrorHandler::Throw(env, err, errData); + return; + } +} +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/copydir_core.cpp b/interfaces/kits/js/src/mod_fs/properties/copydir_core.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9a5a5f4eeb954cdc63c9653270a7a571ad4f128 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/copydir_core.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2025 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 "copydir_core.h" + +#include +#include +#include +#include +#include +#include + +#include "file_utils.h" +#include "filemgmt_libhilog.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; + +static int RecurCopyDir( + const string &srcPath, const string &destPath, const int mode, vector &errfiles); + +static bool EndWithSlash(const string &src) +{ + return src.back() == '/'; +} + +static bool AllowToCopy(const string &src, const string &dest) +{ + if (src == dest) { + HILOGE("Failed to copy file, the same path"); + return false; + } + if (EndWithSlash(src) ? dest.find(src) == 0 : dest.find(src + "/") == 0) { + HILOGE("Failed to copy file, dest is under src"); + return false; + } + if (filesystem::path(src).parent_path() == dest) { + HILOGE("Failed to copy file, src's parent path is dest"); + return false; + } + return true; +} + +static tuple ParseAndCheckOperand(const string &src, const string &dest, const optional &mode) +{ + error_code errCode; + if (!filesystem::is_directory(filesystem::status(src, errCode))) { + HILOGE("Invalid src, errCode = %{public}d", errCode.value()); + return { false, 0 }; + } + if (!filesystem::is_directory(filesystem::status(dest, errCode))) { + HILOGE("Invalid dest, errCode = %{public}d", errCode.value()); + return { false, 0 }; + } + if (!AllowToCopy(src, dest)) { + return { false, 0 }; + } + int32_t modeValue = 0; + if (mode.has_value()) { + modeValue = mode.value(); + if (modeValue < COPYMODE_MIN || modeValue > COPYMODE_MAX) { + HILOGE("Invalid mode"); + return { false, 0 }; + } + } + return { true, modeValue }; +} + +static int MakeDir(const string &path) +{ + filesystem::path destDir(path); + error_code errCode; + if (!filesystem::create_directory(destDir, errCode)) { + HILOGE("Failed to create directory, error code: %{public}d", errCode.value()); + return errCode.value(); + } + return ERRNO_NOERR; +} + +struct NameList { + struct dirent **namelist = { nullptr }; + int direntNum = 0; +}; + +static int RemoveFile(const string &destPath) +{ + filesystem::path destFile(destPath); + error_code errCode; + if (!filesystem::remove(destFile, errCode)) { + HILOGE("Failed to remove file with path, error code: %{public}d", errCode.value()); + return errCode.value(); + } + return ERRNO_NOERR; +} + +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); + arg->namelist = nullptr; + delete arg; + arg = nullptr; +} + +static int CopyFile(const string &src, const string &dest, const int mode) +{ + filesystem::path dstPath(dest); + error_code errCode; + if (filesystem::exists(dstPath, errCode)) { + int ret = (mode == DIRMODE_FILE_COPY_THROW_ERR) ? EEXIST : RemoveFile(dest); + if (ret) { + HILOGE("Failed to copy file due to existing destPath with throw err"); + return ret; + } + } + if (errCode.value() != ERRNO_NOERR) { + HILOGE("fs exists fail, errcode is %{public}d", errCode.value()); + return errCode.value(); + } + filesystem::path srcPath(src); + if (!filesystem::copy_file(srcPath, dstPath, filesystem::copy_options::overwrite_existing, errCode)) { + HILOGE("Failed to copy file, error code: %{public}d", errCode.value()); + return errCode.value(); + } + return ERRNO_NOERR; +} + +static int CopySubDir( + const string &srcPath, const string &destPath, const int mode, vector &errfiles) +{ + error_code errCode; + if (!filesystem::exists(destPath, errCode) && errCode.value() == ERRNO_NOERR) { + int res = MakeDir(destPath); + if (res != ERRNO_NOERR) { + HILOGE("Failed to mkdir"); + return res; + } + } else if (errCode.value() != ERRNO_NOERR) { + HILOGE("fs exists fail, errcode is %{public}d", errCode.value()); + return errCode.value(); + } + return RecurCopyDir(srcPath, destPath, mode, errfiles); +} + +static int FilterFunc(const struct dirent *filename) +{ + if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") { + return DISMATCH; + } + return MATCH; +} + +static int RecurCopyDir( + const string &srcPath, const string &destPath, const int mode, vector &errfiles) +{ + unique_ptr pNameList = { new (nothrow) struct NameList, Deleter }; + if (pNameList == nullptr) { + HILOGE("Failed to request heap memory."); + return ENOMEM; + } + int num = scandir(srcPath.c_str(), &(pNameList->namelist), FilterFunc, alphasort); + if (num < 0) { + HILOGE("scandir fail errno is %{public}d", errno); + return errno; + } + pNameList->direntNum = num; + + for (int i = 0; i < num; i++) { + if ((pNameList->namelist[i])->d_type == DT_DIR) { + string srcTemp = srcPath + '/' + string((pNameList->namelist[i])->d_name); + string destTemp = destPath + '/' + string((pNameList->namelist[i])->d_name); + int res = CopySubDir(srcTemp, destTemp, mode, errfiles); + if (res == ERRNO_NOERR) { + continue; + } + return res; + } else { + string src = srcPath + '/' + string((pNameList->namelist[i])->d_name); + string dest = destPath + '/' + string((pNameList->namelist[i])->d_name); + int res = CopyFile(src, dest, mode); + if (res == EEXIST) { + errfiles.emplace_back(src, dest); + continue; + } else if (res == ERRNO_NOERR) { + continue; + } else { + HILOGE("Failed to copy file for error %{public}d", res); + return res; + } + } + } + return ERRNO_NOERR; +} + +static int CopyDirFunc(const string &src, const string &dest, const int mode, vector &errfiles) +{ + size_t found = string(src).rfind('/'); + if (found == string::npos) { + return EINVAL; + } + string dirName = string(src).substr(found); + string destStr = dest + dirName; + error_code errCode; + if (!filesystem::exists(destStr, errCode) && errCode.value() == ERRNO_NOERR) { + int res = MakeDir(destStr); + if (res != ERRNO_NOERR) { + HILOGE("Failed to mkdir"); + return res; + } + } else if (errCode.value() != ERRNO_NOERR) { + HILOGE("fs exists fail, errcode is %{public}d", errCode.value()); + return errCode.value(); + } + int res = RecurCopyDir(src, destStr, mode, errfiles); + if (!errfiles.empty() && res == ERRNO_NOERR) { + return EEXIST; + } + return res; +} + +struct CopyDirResult CopyDirCore::DoCopyDir(const string &src, const string &dest, const optional &mode) +{ + auto [succ, modeValue] = ParseAndCheckOperand(src, dest, mode); + if (!succ) { + return { FsResult::Error(EINVAL), nullopt }; + } + + vector errfiles = {}; + int ret = CopyDirFunc(src, dest, modeValue, errfiles); + if (ret == EEXIST && modeValue == DIRMODE_FILE_COPY_THROW_ERR) { + return { FsResult::Error(EEXIST), make_optional>(move(errfiles)) }; + } else if (ret) { + return { FsResult::Error(ret), nullopt }; + } + return { FsResult::Success(), nullopt }; +} + +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/copydir_core.h b/interfaces/kits/js/src/mod_fs/properties/copydir_core.h new file mode 100644 index 0000000000000000000000000000000000000000..ab9f9b25a438171c1a4b804c414fa0388a2392eb --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/copydir_core.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 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 INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_COPYDIR_CORE_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_COPYDIR_CORE_H + +#include +#include + +#include "filemgmt_libfs.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; + +constexpr int COPYMODE_MIN = 0; +constexpr int COPYMODE_MAX = 1; +constexpr int COPYDIR_DEFAULT_PERM = 0770; + +constexpr int DISMATCH = 0; +constexpr int MATCH = 1; + +enum ModeOfCopyDir { DIRMODE_FILE_COPY_THROW_ERR = 0, DIRMODE_FILE_COPY_REPLACE }; + +struct CopyDirResult { + FsResult fsResult; + optional> errFiles; +}; + +class CopyDirCore final { +public: + static struct CopyDirResult DoCopyDir( + const string &src, const string &dest, const optional &mode = nullopt); +}; + +struct ConflictFiles { + string srcFiles; + string destFiles; + ConflictFiles(const string &src, const string &dest) : srcFiles(src), destFiles(dest) {} + ~ConflictFiles() = default; +}; + +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_COPYDIR_CORE_H \ No newline at end of file