diff --git a/interfaces/kits/js/BUILD.gn b/interfaces/kits/js/BUILD.gn index f98ab96396b03094261a549a6b763023eb4b2284..9b31c1e171f88569c100ef597768a4aaaba28657 100644 --- a/interfaces/kits/js/BUILD.gn +++ b/interfaces/kits/js/BUILD.gn @@ -711,6 +711,7 @@ ohos_shared_library("ani_fs_class") { "src/mod_fs/properties/ani/connectdfs_ani.cpp", "src/mod_fs/properties/ani/copy_ani.cpp", "src/mod_fs/properties/ani/copy_file_ani.cpp", + "src/mod_fs/properties/ani/copydir_ani.cpp", "src/mod_fs/properties/ani/create_randomaccessfile_ani.cpp", "src/mod_fs/properties/ani/create_stream_ani.cpp", "src/mod_fs/properties/ani/disconnectdfs_ani.cpp", @@ -741,6 +742,7 @@ ohos_shared_library("ani_fs_class") { "src/mod_fs/properties/copy_core.cpp", "src/mod_fs/properties/copy_file_core.cpp", "src/mod_fs/properties/copy_listener/trans_listener_core.cpp", + "src/mod_fs/properties/copydir_core.cpp", "src/mod_fs/properties/create_randomaccessfile_core.cpp", "src/mod_fs/properties/create_stream_core.cpp", "src/mod_fs/properties/dfs_listener/file_dfs_listener_stub.cpp", 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 294b230fb1c1c9730dc4bcc938ceee27116dd20f..7d15255e59a906d5c4cb261aba13e2ab3f23669d 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 @@ -20,21 +20,22 @@ #include "access_ani.h" #include "bind_function.h" #include "close_ani.h" -#include "copy_ani.h" #include "connectdfs_ani.h" +#include "copy_ani.h" #include "copy_file_ani.h" +#include "copydir_ani.h" #include "create_randomaccessfile_ani.h" +#include "create_stream_ani.h" #include "disconnectdfs_ani.h" +#include "dup_ani.h" #include "fdatasync_ani.h" -#include "create_stream_ani.h" #include "fdopen_stream_ani.h" -#include "dup_ani.h" #include "file_ani.h" #include "filemgmt_libhilog.h" #include "fsync_ani.h" #include "listfile_ani.h" -#include "lstat_ani.h" #include "lseek_ani.h" +#include "lstat_ani.h" #include "mkdir_ani.h" #include "mkdtemp_ani.h" #include "move_ani.h" @@ -53,9 +54,9 @@ #include "task_signal_ani.h" #include "truncate_ani.h" #include "unlink_ani.h" +#include "utimes_ani.h" #include "write_ani.h" #include "xattr_ani.h" -#include "utimes_ani.h" using namespace OHOS::FileManagement::ModuleFileIO::ANI; @@ -64,8 +65,8 @@ static ani_status BindRafFileMethods(ani_env *env) static const char *className = "L@ohos/file/fs/RandomAccessFileInner;"; std::array methods = { - ani_native_function { "setFilePointer0", nullptr, - reinterpret_cast(RandomAccessFileAni::SetFilePointer) }, + ani_native_function { + "setFilePointer0", nullptr, reinterpret_cast(RandomAccessFileAni::SetFilePointer) }, ani_native_function { "close", nullptr, reinterpret_cast(RandomAccessFileAni::Close) }, ani_native_function { "writeSync0", nullptr, reinterpret_cast(RandomAccessFileAni::WriteSync) }, ani_native_function { "readSync0", nullptr, reinterpret_cast(RandomAccessFileAni::ReadSync) }, @@ -149,27 +150,29 @@ static ani_status BindStaticMethods(ani_env *env) std::array methods = { ani_native_function { "closeSync", nullptr, reinterpret_cast(CloseAni::CloseSync) }, - ani_native_function { "copySync", nullptr, reinterpret_cast(CopyAni::CopySync) }, + ani_native_function { "connectDfs", nullptr, reinterpret_cast(ConnectDfsAni::ConnectDfsSync) }, + ani_native_function { "copyDirSync", nullptr, reinterpret_cast(CopyDirAni::CopyDirSync) }, ani_native_function { "copyFileSync", nullptr, reinterpret_cast(CopyFileAni::CopyFileSync) }, - ani_native_function { "createRandomAccessFileSync", nullptr, reinterpret_cast( - CreateRandomAccessFileAni::CreateRandomAccessFileSync) }, - ani_native_function { "doAccessSync", nullptr, reinterpret_cast(AccessAni::AccessSync3) }, - ani_native_function { "fsyncSync", nullptr, reinterpret_cast(FsyncAni::FsyncSync) }, - ani_native_function { "fdatasyncSync", nullptr, reinterpret_cast(FDataSyncAni::FDataSyncSync) }, - ani_native_function { "getxattrSync", nullptr, reinterpret_cast(XattrAni::GetXattrSync) }, + ani_native_function { "copySync", nullptr, reinterpret_cast(CopyAni::CopySync) }, + ani_native_function { "createRandomAccessFileSync", nullptr, + reinterpret_cast(CreateRandomAccessFileAni::CreateRandomAccessFileSync) }, ani_native_function { "createStreamSync", nullptr, reinterpret_cast(CreateStreamAni::CreateStreamSync) }, + ani_native_function { "disConnectDfs", nullptr, reinterpret_cast(DisConnectDfsAni::DisConnectDfsSync) }, ani_native_function { "doAccessSync", nullptr, reinterpret_cast(AccessAni::AccessSync3) }, + ani_native_function { "dup", nullptr, reinterpret_cast(DupAni::Dup) }, + ani_native_function { "fdatasyncSync", nullptr, reinterpret_cast(FDataSyncAni::FDataSyncSync) }, ani_native_function { "fdopenStreamSync", nullptr, reinterpret_cast(FdopenStreamAni::FdopenStreamSync) }, - ani_native_function { "dup", nullptr, reinterpret_cast(DupAni::Dup) }, + ani_native_function { "fsyncSync", nullptr, reinterpret_cast(FsyncAni::FsyncSync) }, + ani_native_function { "getxattrSync", nullptr, reinterpret_cast(XattrAni::GetXattrSync) }, ani_native_function { "listFileSync", nullptr, reinterpret_cast(ListFileAni::ListFileSync) }, - ani_native_function { "lstatSync", nullptr, reinterpret_cast(LstatAni::LstatSync) }, ani_native_function { "lseekSync", nullptr, reinterpret_cast(LseekAni::LseekSync) }, + ani_native_function { "lstatSync", nullptr, reinterpret_cast(LstatAni::LstatSync) }, ani_native_function { "mkdirSync", "Lstd/core/String;:V", reinterpret_cast(MkdirkAni::MkdirSync0) }, ani_native_function { "mkdirSync", "Lstd/core/String;Z:V", reinterpret_cast(MkdirkAni::MkdirSync1) }, - ani_native_function { "movedirSync", nullptr, reinterpret_cast(MoveDirAni::MoveDirSync) }, ani_native_function { "mkdtempSync", nullptr, reinterpret_cast(MkdtempAni::MkdtempSync) }, + ani_native_function { "movedirSync", nullptr, reinterpret_cast(MoveDirAni::MoveDirSync) }, ani_native_function { "moveFileSync", nullptr, reinterpret_cast(MoveAni::MoveFileSync) }, ani_native_function { "openSync", nullptr, reinterpret_cast(OpenAni::OpenSync) }, ani_native_function { "readlinesSync", nullptr, reinterpret_cast(ReadLinesAni::ReadLinesSync) }, @@ -182,10 +185,8 @@ static ani_status BindStaticMethods(ani_env *env) ani_native_function { "symlinkSync", nullptr, reinterpret_cast(SymlinkAni::SymlinkSync) }, ani_native_function { "truncateSync", nullptr, reinterpret_cast(TruncateAni::TruncateSync) }, ani_native_function { "unlinkSync", nullptr, reinterpret_cast(UnlinkAni::UnlinkSync) }, - ani_native_function { "writeSync", nullptr, reinterpret_cast(WriteAni::WriteSync) }, ani_native_function { "utimes", nullptr, reinterpret_cast(UtimesAni::Utimes) }, - ani_native_function { "connectDfs", nullptr, reinterpret_cast(ConnectDfsAni::ConnectDfsSync) }, - ani_native_function { "disConnectDfs", nullptr, reinterpret_cast(DisConnectDfsAni::DisConnectDfsSync) } + ani_native_function { "writeSync", nullptr, reinterpret_cast(WriteAni::WriteSync) }, }; return BindClass(env, className, methods); } 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 958b70e7bf308e582e3224a2ed1951565fa95570..170b123df78d48c61556f0fe883c0848670bf31d 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 @@ -104,7 +104,7 @@ function closeSync(file: number | File): void { function connectDfs(networkId: string, listeners: DfsListeners): Promise { return new Promise((resolve: (result: undefined) => void, reject: (e: BusinessError) => void): void => { let promise = taskpool.execute((networkId: string, listeners: DfsListeners): void => - FileIoImpl.connectDfs(networkId, listeners), networkId, listeners); + FileIoImpl.connectDfs(networkId, listeners), networkId, listeners); promise.then((): void => { resolve(undefined); }).catch((e: BusinessError): void => { @@ -133,7 +133,7 @@ function getxattr(path: string, key: string): Promise { let promise = taskpool.execute(FileIoImpl.getxattrSync, path, key); promise.then((ret: NullishType): void => { let result = ret as string; - resolve(result); + resolve(result); }).catch((e: BusinessError): void => { reject(e); }); @@ -144,6 +144,59 @@ function dup(fd: number): File { return FileIoImpl.dup(fd); } +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) } @@ -297,7 +350,7 @@ function mkdtemp(prefix: string): Promise { let promise = taskpool.execute(FileIoImpl.mkdtempSync, prefix); promise.then((ret: NullishType): void => { let result = ret as string; - resolve(result); + resolve(result); }).catch((e: BusinessError): void => { reject(e); }); @@ -871,7 +924,7 @@ function rename(oldPath: string, newPath: string): Promise { } function createRandomAccessFileSync(file: string | File, mode?: number, options?: RandomAccessFileOptions): RandomAccessFile { - return FileIoImpl.createRandomAccessFileSync(file, mode, options); + return FileIoImpl.createRandomAccessFileSync(file, mode, options); } function createRandomAccessFile(file: string | File, mode?: number, @@ -907,7 +960,7 @@ function createRandomAccessFile(file: string | File, mode?: number, }); }); } -function fdopenStream(fd: number, mode: string): Promise{ +function fdopenStream(fd: number, mode: string): Promise { return new Promise((resolve: (result: Stream) => void, reject: (e: BusinessError) => void) => { let promise = taskpool.execute(FileIoImpl.fdopenStreamSync, fd, mode); promise.then((ret: NullishType): void => { @@ -991,7 +1044,7 @@ function createStream(path: string, mode: string): Promise { }); } -function createStream(path: string, mode: string, callback: AsyncCallback): void{ +function createStream(path: string, mode: string, callback: AsyncCallback): void { let promise = taskpool.execute(FileIoImpl.createStreamSync, path, mode); promise.then((ret: NullishType): void => { let e = new BusinessError(); @@ -1004,7 +1057,7 @@ function createStream(path: string, mode: string, callback: AsyncCallback; displayName?: Array; @@ -1299,7 +1367,7 @@ class RandomAccessFileInner implements RandomAccessFile { native close(): void; writeSync(buffer: ArrayBuffer | string, options?: WriteOptions): number { - let length = options? this.writeSync0(buffer, options) : this.writeSync0(buffer); + let length = options ? this.writeSync0(buffer, options) : this.writeSync0(buffer); this.filePointer += length; return length; } @@ -1353,7 +1421,7 @@ class RandomAccessFileInner implements RandomAccessFile { } readSync(buffer: ArrayBuffer, options?: ReadOptions): number { - const length = options? this.readSync0(buffer, options) : this.readSync0(buffer); + const length = options ? this.readSync0(buffer, options) : this.readSync0(buffer); this.filePointer += length; return length; } @@ -1435,7 +1503,7 @@ class FileInner implements File { native getParent(): String; native lockSync(exclusive?: boolean): void; - + lock(exclusive?: boolean): Promise { return new Promise((resolve: (result: undefined) => void, reject: (e: BusinessError) => void): void => { if (exclusive === undefined) { @@ -1472,7 +1540,7 @@ class FileInner implements File { callback(e, undefined); }); } - + lock(exclusive: boolean, callback: AsyncCallback): void { let promise = taskpool.execute((exclusive: boolean): undefined => { return this.lockSync(exclusive); @@ -1959,24 +2027,26 @@ class FileIoImpl { static native connectDfs(networkId: string, listeners: DfsListeners): 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 disConnectDfs(networkId: string): void; static native fdatasyncSync(fd: number): void; - + static native getxattrSync(path: string, key: string): string; static native createStreamSync(path: string, mode: string): Stream; - + static native fdopenStreamSync(fd: number, mode: string): Stream; - + static native dup(fd: number): File; static native listFileSync(path: string, options?: ListFileOptions): string[]; static native lstatSync(path: string): Stat; - + static native lseekSync(fd: number, offset: number, whence?: WhenceType): number; static native mkdirSync(path: string): void; @@ -1984,7 +2054,7 @@ class FileIoImpl { static native mkdirSync(path: string, recursion: boolean): void; static native movedirSync(src: string, dest: string, mode?: number): void; - + static native mkdtempSync(prefix: string): string; static native moveFileSync(src: String, dest: String, mode?: number): void; @@ -1992,7 +2062,7 @@ class FileIoImpl { static native openSync(path: String, mode?: number): File; static native readlinesSync(filePath: string, options?: Options): ReaderIterator; - + static native readSync(fd: number, buffer: ArrayBuffer, options?: ReadOptions): number; static native readTextSync(filePath: string, options?: ReadTextOptions): string; @@ -2010,11 +2080,11 @@ class FileIoImpl { static native writeSync(fd: number, buffer: string | ArrayBuffer, options?: WriteOptions): number; static native fsyncSync(fd: number): void; - + static native renameSync(oldPath: string, newPath: string): void; static native createRandomAccessFileSync(file: string | File, mode?: number, - options?: RandomAccessFileOptions): RandomAccessFile; + options?: RandomAccessFileOptions): RandomAccessFile; static native symlinkSync(target: string, srcPath: string): void; 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..ed21f7f5ce65c10a34a0cbb5fbeab1a31429c933 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/ani/copydir_ani.cpp @@ -0,0 +1,156 @@ +/* + * 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(className, &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) { + 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) { + HILOGE("Convert conflict files array failed"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return; + } + 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/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