diff --git a/interfaces/kits/js/BUILD.gn b/interfaces/kits/js/BUILD.gn index 0c172dcbc84de0cac6b91df6002c8d852968a723..5bc59351d0468df0f34ed26263dd98c13f95b4e1 100644 --- a/interfaces/kits/js/BUILD.gn +++ b/interfaces/kits/js/BUILD.gn @@ -662,6 +662,8 @@ ohos_shared_library("ani_file_fs") { include_dirs = [ "include/ipc", "src/mod_fs/ani", + "src/mod_fs/class_atomicfile", + "src/mod_fs/class_atomicfile/ani", "src/mod_fs/class_file", "src/mod_fs/class_file/ani", "src/mod_fs/class_randomaccessfile", @@ -685,6 +687,8 @@ ohos_shared_library("ani_file_fs") { "src/common/ani_helper/type_converter.cpp", "src/common/file_helper/fd_guard.cpp", "src/mod_fs/ani/bind_function_class.cpp", + "src/mod_fs/class_atomicfile/ani/atomicfile_ani.cpp", + "src/mod_fs/class_atomicfile/fs_atomicfile.cpp", "src/mod_fs/class_file/ani/file_ani.cpp", "src/mod_fs/class_file/ani/file_wrapper.cpp", "src/mod_fs/class_file/file_instantiator.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 5086029c2b2765034cfc98be53ec77a6b052191a..07e3773e0f017c62765a80f92d76ece075e56a5b 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 @@ -18,6 +18,7 @@ #include #include "access_ani.h" +#include "atomicfile_ani.h" #include "bind_function.h" #include "close_ani.h" #include "connectdfs_ani.h" @@ -161,6 +162,25 @@ static ani_status BindTaskSignalClassMethods(ani_env *env) return BindClass(env, className, methods); } +static ani_status BindAtomicFileMethods(ani_env *env) +{ + static const char *className = "L@ohos/file/fs/fileIo/AtomicFile;"; + + std::array methods = { + ani_native_function { "getPath", nullptr, reinterpret_cast(AtomicFileAni::GetPath) }, + ani_native_function { "getBaseFile", nullptr, reinterpret_cast(AtomicFileAni::GetBaseFile) }, + ani_native_function { "readFully", nullptr, reinterpret_cast(AtomicFileAni::ReadFully) }, + ani_native_function { "nativeStartWrite", nullptr, reinterpret_cast(AtomicFileAni::StartWrite) }, + ani_native_function { "nativeFinishWrite", nullptr, reinterpret_cast(AtomicFileAni::FinishWrite) }, + ani_native_function { "nativeFailWrite", nullptr, reinterpret_cast(AtomicFileAni::FailWrite) }, + ani_native_function { "delete", nullptr, reinterpret_cast(AtomicFileAni::Delete) }, + ani_native_function { "", "Lstd/core/String;:V", reinterpret_cast(AtomicFileAni::Constructor) }, + ani_native_function { "openRead", nullptr, reinterpret_cast(AtomicFileAni::OpenRead) }, + }; + + return BindClass(env, className, methods); +} + static ani_status BindStaticMethods(ani_env *env) { static const char *className = "L@ohos/file/fs/FileIoImpl;"; @@ -266,6 +286,11 @@ ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) if ((status = BindWatcherClassMethods(env)) != ANI_OK) { HILOGE("Cannot bind native methods for Watcher Class"); return status; + } + + if ((status = BindAtomicFileMethods(env)) != ANI_OK) { + HILOGE("Cannot bind native methods for AtomicFile Class!"); + return status; }; *result = ANI_VERSION_1; 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 e8fa2c76b075561079fd9bf88589c471047d6430..e66347a4c3ecb287f049b4132030e6e63ba5caf3 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 @@ -14,6 +14,17 @@ */ import { BusinessError, AsyncCallback } from '@ohos.base'; import stream from '@ohos.util.stream'; +//import { util, buffer } from '@kit.ArkTS'; + +const UNKNOWN_ERR: number = 13900042 +const UNKNOWN_MSG: string = "Unknown error" + +function createBusinessError(code: number, msg: string): BusinessError { + let err = new BusinessError(); + err.code = code; + err.message = msg; + return err; +} namespace fileIo { export namespace OpenMode { @@ -2037,12 +2048,56 @@ export class WriteStream extends stream.Writable { } } +export class AtomicFile { + static { + loadLibrary("ani_file_fs"); + } + + private nativePtr: long = 0; + private native getPath(): string; + private writeStream: WriteStream; + + native constructor(path: string); + + native getBaseFile(): File; + + native openRead(): ReadStream; + + native readFully(): ArrayBuffer; + + native nativeStartWrite(): string; + startWrite(): WriteStream { + let tempPath = this.nativeStartWrite(); + this.writeStream = createWriteStream(tempPath); + return this.writeStream; + }; + + native nativeFinishWrite(): void; + finishWrite(): void { + if (!this.writeStream) { + throw createBusinessError(UNKNOWN_ERR, UNKNOWN_MSG); + } + this.writeStream.close(); + this.nativeFinishWrite(); + }; + + native nativeFailWrite(): void; + failWrite(): void { + if (!this.writeStream) { + throw createBusinessError(UNKNOWN_ERR, UNKNOWN_MSG); + } + this.writeStream.close(); + this.nativeFailWrite(); + }; + + native delete(): void; +} + export enum WhenceType { SEEK_SET = 0, SEEK_CUR = 1, SEEK_END = 2 } - } export interface Watcher { @@ -2153,7 +2208,6 @@ export interface Filter { type TaskSignal = fileIo.TaskSignal; type DfsListeners = fileIo.DfsListeners; - export class FileIoImpl { static { @@ -2232,4 +2286,4 @@ export class FileIoImpl { static native symlinkSync(target: string, srcPath: string): void; static native utimes(path: string, mtime: number): void; -} +} \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_atomicfile/ani/atomicfile_ani.cpp b/interfaces/kits/js/src/mod_fs/class_atomicfile/ani/atomicfile_ani.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba35442b6399e2a3fbc01a232b2f8ef95d2da369 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_atomicfile/ani/atomicfile_ani.cpp @@ -0,0 +1,344 @@ +/* + * 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 "atomicfile_ani.h" + +#include "error_handler.h" +#include "file_wrapper.h" +#include "filemgmt_libhilog.h" +#include "fs_atomicfile.h" +#include "type_converter.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { +using namespace std; +using namespace OHOS::FileManagement::ModuleFileIO; + +const std::string READ_STREAM_CLASS = "ReadStream"; +const std::string WRITE_STREAM_CLASS = "WriteStream"; +const std::string TEMP_FILE_SUFFIX = "_XXXXXX"; + +void AtomicFileAni::Constructor(ani_env *env, ani_object obj, ani_string pathObj){ + auto [succ, filePath] = TypeConverter::ToUTF8String(env, pathObj); + if (!succ) { + HILOGE("Invalid path"); + ErrorHandler::Throw(env, E_PARAMS); + return; + } + + auto ret = FsAtomicFile::Constructor(filePath); + if (!ret.IsSuccess()) { + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return; + } + + if (ANI_OK != env->Object_SetFieldByName_Long( + obj, "nativePtr", reinterpret_cast(ret.GetData().value()))) { + HILOGE("Failed to wrap entity for obj AtomicFile"); + ErrorHandler::Throw(env, EIO); + return; + } +} + +static FsAtomicFile* Unwrap(ani_env *env, ani_object object) +{ + ani_long file; + auto ret = env->Object_GetFieldByName_Long(object, "nativePtr", &file); + if (ret != ANI_OK) { + HILOGE("Unwrap file err: %{private}d", ret); + return nullptr; + } + + return reinterpret_cast(file); +} + +ani_string AtomicFileAni::GetPath(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto file = Unwrap(env, object); + if (file == nullptr) { + ErrorHandler::Throw(env, E_PARAMS); + return nullptr; + } + + string path = file->GetPath(); + auto [succ, result] = TypeConverter::ToAniString(env, path); + if (!succ) { + HILOGE("ToAniString failed"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + + return result; +} + +ani_object AtomicFileAni::GetBaseFile(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto aotomicFile = Unwrap(env, object); + if (aotomicFile == nullptr) { + ErrorHandler::Throw(env, E_PARAMS); + return nullptr; + } + + auto ret = aotomicFile->GetBaseFile(); + if (!ret.IsSuccess()) { + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return nullptr; + } + + const FsFile *fsFile = ret.GetData().value(); + auto result = FileWrapper::Wrap(env, move(fsFile)); + if (result == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + + return result; +} + +static ani_object CreateReadStream(ani_env *env, ani_string filePath) +{ + static const char *className = "L@ohos/file/fs/fileIo/ReadStream;"; + ani_class cls; + if (ANI_OK != env->FindClass(className, &cls)) { + HILOGE("Cannot find class %s", className); + return nullptr; + } + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, "", "Lstd/core/String;:V", &ctor)) { + HILOGE("Cannot find constructor method for class %s", className); + return nullptr; + } + ani_object obj; + if (ANI_OK != env->Object_New(cls, ctor, &obj, filePath)) { + HILOGE("New %s obj Failed", className); + return nullptr; + } + + return move(obj); +} + +static ani_object CreateWriteStream(ani_env *env, ani_string filePath) +{ + static const char *className = "L@ohos/file/fs/fileIo/WriteStream;"; + ani_class cls; + if (ANI_OK != env->FindClass(className, &cls)) { + HILOGE("Cannot find class %s", className); + return nullptr; + } + ani_method ctor; + if (ANI_OK != + env->Class_FindMethod(cls, "", "Lstd/core/String;L@ohos/file/fs/WriteStreamOptions;:V", &ctor)) { + HILOGE("Cannot find constructor method for class %s", className); + return nullptr; + } + ani_object obj; + if (ANI_OK != env->Object_New(cls, ctor, &obj, filePath)) { + HILOGE("New %s obj Failed", className); + return nullptr; + } + + return move(obj); +} + +static ani_object CreateStream( + ani_env *env, [[maybe_unused]] ani_object object, const std::string &streamName, const std::string &fineName) +{ + auto aotomicFile = Unwrap(env, object); + if (aotomicFile == nullptr) { + ErrorHandler::Throw(env, E_PARAMS); + return nullptr; + } + + auto [succ, filePath] = TypeConverter::ToAniString(env, fineName); + if (!succ) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + + if (streamName == READ_STREAM_CLASS) { + auto stream = CreateReadStream(env, filePath); + if (stream == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + return move(stream); + } + if (streamName == WRITE_STREAM_CLASS) { + auto stream = CreateWriteStream(env, filePath); + if (stream == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + return move(stream); + } + + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; +} + +ani_object AtomicFileAni::OpenRead(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto aotomicFile = Unwrap(env, object); + if (aotomicFile == nullptr) { + ErrorHandler::Throw(env, E_PARAMS); + return nullptr; + } + + auto entity = aotomicFile->GetEntity(); + if (entity == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + + return CreateStream(env, object, READ_STREAM_CLASS, entity->baseFileName); +} + +static tuple WrapBufferData(ani_env *env, unique_ptr bufferData) +{ + ani_arraybuffer externalBuffer = nullptr; + + uint8_t* buffer = bufferData->buffer; + size_t length = bufferData->length; + HILOGE("AtomicFile Arraybuffer: %s", buffer); + ani_status ret = env->CreateArrayBuffer(length, reinterpret_cast(&buffer), &externalBuffer); + bufferData.release(); + + if (ret != ANI_OK) { + HILOGE("CreateArrayBufferExternal err: %{private}d", ret); + return { ret, nullptr }; + } + + return { ANI_OK, externalBuffer }; + + // static const char *className = "Lescompat/Uint8Array;"; + // ani_class cls; + // if ((ret = env->FindClass(className, &cls)) != ANI_OK) { + // HILOGE("Not found %{private}s, err: %{private}d", className, ret); + // return { ret, nullptr }; + // } + + // ani_method ctor; + // if ((ret = env->Class_FindMethod(cls, "", "Lescompat/ArrayBuffer;II:V", &ctor)) != ANI_OK) { + // HILOGE("Not found ctor, err: %{private}d", ret); + // return { ret, nullptr }; + // } + + // ani_object obj = {}; + // if ((ret = env->Object_New(cls, ctor, &obj, externalBuffer, ani_int(0), ani_int(length))) != ANI_OK) { + // HILOGE("New Uint8Array err: %{private}d", ret); + // return { ret, nullptr }; + // } + + // return { ANI_OK, obj }; +} + +ani_object AtomicFileAni::ReadFully(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto aotomicFile = Unwrap(env, object); + if (aotomicFile == nullptr) { + ErrorHandler::Throw(env, E_PARAMS); + return nullptr; + } + + auto ret = aotomicFile->ReadFully(); + if (!ret.IsSuccess()) { + ErrorHandler::Throw(env, ret.GetError()); + return nullptr; + } + + auto &bufferData = ret.GetData().value(); + auto [succ, obj] = WrapBufferData(env, move(bufferData)); + if (succ != ANI_OK) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + + return obj; +} + +ani_string AtomicFileAni::StartWrite(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto file = Unwrap(env, object); + if (file == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + + auto ret = file->StartWrite(); + if (!ret.IsSuccess()) { + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return nullptr; + } + string tempPath = ret.GetData().value(); + auto [succ, result] = TypeConverter::ToAniString(env, tempPath); + if (!succ) { + HILOGE("ToAniString failed"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + + return result; +} + +void AtomicFileAni::FinishWrite(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto file = Unwrap(env, object); + if (file == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return; + } + + HILOGE("Do FinishWrite"); + file->FinishWrite(); + return; +} + +void AtomicFileAni::FailWrite(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto file = Unwrap(env, object); + if (file == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return; + } + HILOGE("Do FailWrite"); + file->FailWrite(); + return; +} + +void AtomicFileAni::Delete(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto aotomicFile = Unwrap(env, object); + if (aotomicFile == nullptr) { + ErrorHandler::Throw(env, E_PARAMS); + return; + } + + auto ret = aotomicFile->Delete(); + if (!ret.IsSuccess()) { + ErrorHandler::Throw(env, ret.GetError()); + return; + } + + return; +} +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_atomicfile/ani/atomicfile_ani.h b/interfaces/kits/js/src/mod_fs/class_atomicfile/ani/atomicfile_ani.h new file mode 100644 index 0000000000000000000000000000000000000000..423f7b4b98340a4377c3384ca0a53859f70746f0 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_atomicfile/ani/atomicfile_ani.h @@ -0,0 +1,42 @@ +/* + * 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_ATOMICFILE_ANI_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_ATOMICFILE_ANI_H + +#include + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { +class AtomicFileAni final { +public: + static void Constructor(ani_env *env, ani_object obj, ani_string pathObj); + static ani_object GetBaseFile(ani_env *env, [[maybe_unused]] ani_object object); + static ani_object OpenRead(ani_env *env, [[maybe_unused]] ani_object object); + static ani_arraybuffer ReadFully(ani_env *env, [[maybe_unused]] ani_object object); + + static ani_string GetPath(ani_env *env, [[maybe_unused]] ani_object object); + static ani_string StartWrite(ani_env *env, [[maybe_unused]] ani_object object); + static void FinishWrite(ani_env *env, [[maybe_unused]] ani_object object); + static void FailWrite(ani_env *env, [[maybe_unused]] ani_object object); + static void Delete(ani_env *env, [[maybe_unused]] ani_object object); +}; +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_ATOMICFILE_ANI_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.cpp b/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ebc4a76431cb18366ea906a962ae2360359b5ce1 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.cpp @@ -0,0 +1,234 @@ +/* + * 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 "fs_atomicfile.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "fs_atomicfile_entity.h" +#include "filemgmt_libfs.h" +#include "file_instantiator.h" +#include "file_utils.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace fs = std::filesystem; + +const std::string READ_STREAM_CLASS = "ReadStream"; +const std::string WRITE_STREAM_CLASS = "WriteStream"; +const std::string TEMP_FILE_SUFFIX = "_XXXXXX"; + +FsAtomicFileEntity* FsAtomicFile::GetEntity() +{ + if (!entity) { + return nullptr; + } + return entity.get(); +} + +void FsAtomicFile::FinalizeCallback(void *finalizeData, [[maybe_unused]] void *finalizeHint) +{ + BufferData *bufferData = static_cast(finalizeData); + delete bufferData; +} + +string FsAtomicFile::GetPath() +{ + return entity->baseFileName; +} + +FsResult FsAtomicFile::GetBaseFile() +{ + if (entity == nullptr) { + HILOGE("Failed to get atomicFileEntity"); + return FsResult::Error(UNKNOWN_ERR); + } + + if (entity->baseFileName.size() >= PATH_MAX) { + HILOGE("Base file name is too long"); + return FsResult::Error(UNKNOWN_ERR); + } + + char realPath[PATH_MAX]; + char *result = realpath(entity->baseFileName.c_str(), realPath); + if (result == nullptr) { + HILOGE("Failed to resolve real path, err:%{public}d", errno); + return FsResult::Error(errno); + } + + int fd = open(result, O_RDONLY); + if (fd < 0) { + HILOGE("Failed to open file, err:%{public}d", errno); + return FsResult::Error(errno); + } + + return FileInstantiator::InstantiateFile(fd, entity->baseFileName, false); +} + +static std::tuple, int32_t> ReadFileToBuffer(FILE* fp) +{ + int fd = fileno(fp); + if (fd < 0) { + HILOGE("Failed to get file descriptor, err:%{public}d", errno); + return { nullptr, UNKNOWN_ERR }; + } + + struct stat fileStat {}; + if (fstat(fd, &fileStat) < 0) { + HILOGE("Failed to get file stats, err:%{public}d", errno); + return { nullptr, errno }; + } + + long fileSize = fileStat.st_size; + if (fileSize <= 0) { + HILOGE("Invalid file size"); + return { nullptr, EIO }; + } + + auto bufferData = std::make_unique(); + bufferData->buffer = new(std::nothrow) uint8_t[fileSize]; + if (bufferData->buffer == nullptr) { + HILOGE("Failed to allocate memory"); + return { nullptr, ENOMEM }; + } + bufferData->length = fread(bufferData->buffer, sizeof(uint8_t), fileSize, fp); + if ((bufferData->length != static_cast(fileSize) && !feof(fp)) || ferror(fp)) { + HILOGE("Failed to read file, actual length is:%zu, fileSize:%ld", bufferData->length, fileSize); + delete[] bufferData->buffer; + bufferData->buffer = nullptr; + bufferData->length = 0; + return { nullptr, EIO }; + } + return { std::move(bufferData), ERRNO_NOERR }; +} + +FsResult> FsAtomicFile::ReadFully() +{ + if (entity == nullptr) { + HILOGE("Failed to get atomicFileEntity"); + return FsResult>::Error(UNKNOWN_ERR); + } + + char realPath[PATH_MAX]; + char *result = realpath(entity->baseFileName.c_str(), realPath); + if (result == nullptr) { + HILOGE("Failed to resolve file real path, err:%{public}d", errno); + return FsResult>::Error(errno); + } + + auto file = std::unique_ptr( + std::fopen(result, "rb"), &std::fclose); + if (!file) { + HILOGE("Failed to open file, err:%{public}d", errno); + return FsResult>::Error(errno); + } + + auto [bufferData, errCode] = ReadFileToBuffer(file.get()); + if (errCode != ERRNO_NOERR) { + return FsResult>::Error(errCode); + } + return FsResult>::Success(move(bufferData)); +} + +FsResult FsAtomicFile::StartWrite() +{ + fs::path filePath = entity->newFileName; + fs::path parentPath = filePath.parent_path(); + if (access(parentPath.c_str(), F_OK) != 0) { + HILOGE("Parent directory does not exist, err:%{public}d", errno); + return FsResult::Error(ENOENT); + } + + char *tmpfile = const_cast(entity->newFileName.c_str()); + if (mkstemp(tmpfile) == -1) { + HILOGE("Fail to create tmp file err:%{public}d!", errno); + return FsResult::Error(ENOENT); + } + + return FsResult::Success(entity->newFileName); +} + +void FsAtomicFile::FinishWrite() +{ + HILOGE("AtomicFile FinishWrite path new: %s, old: %s", entity->newFileName.c_str(), entity->baseFileName.c_str()); + std::rename(entity->newFileName.c_str(), entity->baseFileName.c_str()); + std::string tmpNewFileName = entity->baseFileName; + entity->newFileName = tmpNewFileName.append(TEMP_FILE_SUFFIX); + + return; +} + +void FsAtomicFile::FailWrite() +{ + HILOGE("AtomicFile FailWrite path new: %s, old: %s", entity->newFileName.c_str(), entity->baseFileName.c_str()); + if (!fs::remove(entity->newFileName)) { + HILOGW("Failed to remove file"); + } + std::string tmpNewFileName = entity->baseFileName; + entity->newFileName = tmpNewFileName.append(TEMP_FILE_SUFFIX); + + return; +} + +FsResult FsAtomicFile::Delete() +{ + auto rafentity = GetEntity(); + if (rafentity == nullptr) { + HILOGE("Failed to get atomicFileEntity"); + return FsResult::Error(UNKNOWN_ERR); + } + + bool errFlag = false; + std::error_code fsErrcode; + if (fs::exists(rafentity->newFileName, fsErrcode) && !fs::remove(rafentity->newFileName, fsErrcode)) { + errFlag = true; + } + if (fs::exists(rafentity->baseFileName, fsErrcode) && !fs::remove(rafentity->baseFileName, fsErrcode)) { + errFlag = true; + } + if (errFlag) { + HILOGE("Failed to remove file, err:%{public}s", fsErrcode.message().c_str()); + return FsResult::Error(fsErrcode.value()); + } + + rafentity->newFileName.clear(); + rafentity->baseFileName.clear(); + return FsResult::Success(); +} + +FsResult FsAtomicFile::Constructor(string path) +{ + auto atomicFileEntity = CreateUniquePtr(); + if (atomicFileEntity == nullptr) { + HILOGE("Failed to request heap memory"); + return FsResult::Error(ENOMEM); + } + atomicFileEntity->baseFileName = path; + atomicFileEntity->newFileName = path.append(TEMP_FILE_SUFFIX); + + auto file = new FsAtomicFile(move(atomicFileEntity)); + + return FsResult::Success(file); +} +} // namespace ModuleFileIO +} // namespace DistributedFS +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.h b/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.h new file mode 100644 index 0000000000000000000000000000000000000000..940b1ba9ae9345559d6174e32b59e94f423a1b91 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.h @@ -0,0 +1,57 @@ +/* + * 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_CLASS_ATOMICFILE_FS_ATOMICFILE_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_ATOMICFILE_FS_ATOMICFILE_H + +#include "fs_atomicfile_entity.h" +#include "fs_file.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; + +struct BufferData { + uint8_t* buffer = nullptr; + size_t length = 0; + + ~BufferData() + { + delete[] buffer; + } +}; + +class FsAtomicFile final { +public: + FsAtomicFileEntity* GetEntity(); + static FsResult Constructor(string path); + string GetPath(); + FsResult GetBaseFile(); + FsResult> ReadFully(); + FsResult StartWrite(); + void FinishWrite(); + void FailWrite(); + FsResult Delete(); + static void FinalizeCallback(void *finalizeData, [[maybe_unused]] void *finalizeHint); + +private: + unique_ptr entity; + explicit FsAtomicFile(unique_ptr entity) : entity(move(entity)) {} +}; +} // namespace ModuleFileIO +} // namespace DistributedFS +} // namespace OHOS +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_ATOMICFILE_FS_ATOMICFILE_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile_entity.h b/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile_entity.h new file mode 100644 index 0000000000000000000000000000000000000000..71bcb9ee18e96c6f3b174dcc380aeae781d7a2b3 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile_entity.h @@ -0,0 +1,31 @@ +/* + * 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_CLASS_ATOMICFILE_FS_ATOMICFILE_ENTITY_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_ATOMICFILE_FS_ATOMICFILE_ENTITY_H + +#include + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +struct FsAtomicFileEntity { + std::string baseFileName = ""; + std::string newFileName = ""; +}; +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_ATOMICFILE_FS_ATOMICFILE_ENTITY_H \ No newline at end of file