From 6da19e9762e24d7805454d90cb8653691500d9c5 Mon Sep 17 00:00:00 2001 From: nieben Date: Mon, 21 Apr 2025 21:12:34 +0800 Subject: [PATCH 1/3] atomic file Change-Id: I8054f3644ce3139cf2c78314c28c5407463e770a --- interfaces/kits/js/BUILD.gn | 4 + .../js/src/mod_fs/ani/bind_function_class.cpp | 25 ++ .../js/src/mod_fs/ani/ets/@ohos.file.fs.ets | 170 +++++++++++- .../class_atomicfile/ani/atomicfile_ani.cpp | 248 ++++++++++++++++++ .../class_atomicfile/ani/atomicfile_ani.h | 40 +++ .../mod_fs/class_atomicfile/fs_atomicfile.cpp | 219 ++++++++++++++++ .../mod_fs/class_atomicfile/fs_atomicfile.h | 56 ++++ .../class_atomicfile/fs_atomicfile_entity.h | 31 +++ 8 files changed, 791 insertions(+), 2 deletions(-) create mode 100644 interfaces/kits/js/src/mod_fs/class_atomicfile/ani/atomicfile_ani.cpp create mode 100644 interfaces/kits/js/src/mod_fs/class_atomicfile/ani/atomicfile_ani.h create mode 100644 interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.cpp create mode 100644 interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.h create mode 100644 interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile_entity.h diff --git a/interfaces/kits/js/BUILD.gn b/interfaces/kits/js/BUILD.gn index 0c172dcbc..5bc59351d 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 5086029c2..c1fbf6c43 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/AtomicFile;"; + + std::array methods = { + // ani_native_function { "nativeConstructor", nullptr, reinterpret_cast(AtomicFileAni::Constructor) }, + 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) }, + }; + + 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 e8fa2c76b..3bf2a2b99 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,60 @@ export class WriteStream extends stream.Writable { } } +export class AtomicFile { + private nativePtr: long = 0; + private native nativeConstructor(path: string): long; + private native getPath(): string; + private writeStream: WriteStream | undefined; + + // constructor(path: string) { + // this.nativePtr = this.nativeConstructor(path); + // } + + native constructor(path: string); + + native getBaseFile(): File; + + openRead(): ReadStream { + let path = this.getPath(); + return createReadStream(path); + }; + + 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 +2212,6 @@ export interface Filter { type TaskSignal = fileIo.TaskSignal; type DfsListeners = fileIo.DfsListeners; - export class FileIoImpl { static { @@ -2233,3 +2291,111 @@ export class FileIoImpl { static native utimes(path: string, mtime: number): void; } + +// function atomicFileTest() { +// try { +// console.log('atomicFileTest start'); +// let file = new AtomicFile(`/data/local/tmp/a.txt`); +// let writeSream = file.startWrite(); +// console.log('startWrite success'); +// writeSream.write("abcdef", "utf-8", ()=> { +// file.finishWrite(); +// console.log('finishWrite success'); + +// setTimeout(()=>{ +// let File = file.getBaseFile(); +// console.log('getBaseFile File.fd is:, path:, name:', File.fd, File.path, File.path); + +// let readStream = file.openRead(); +// const data = readStream.read(); +// if (!data) { +// console.log('openRead data is null'); +// return; +// } +// console.log('openRead data is:', data); + +// //let dataFully = file.readFully(); +// //let decoder = util.TextDecoder.create('utf-8'); +// //let str = decoder.decodeToString(new Uint8Array(dataFully)); +// //console.log('readFully str is :', str); +// }, 1000); +// }) +// console.log('atomicFileTest end'); +// } catch (err) { +// console.log('failed!, Cause: ', JSON.stringify(err) ?? ''); +// } +// } + +// function atomicFileDeleteTest() { +// console.log('atomicFileDeleteTest start'); +// try { +// let file = new AtomicFile(`/data/local/tmp/b.txt`); +// let writeSream = file.startWrite(); +// console.log('startWrite success'); +// writeSream.write("abcdef", "utf-8", ()=> { +// file.finishWrite(); +// console.log('finishWrite success'); + +// setTimeout(()=>{ +// //let data = file.readFully(); +// //let decoder = util.TextDecoder.create('utf-8'); +// //let str = decoder.decodeToString(new Uint8Array(data)); +// //console.log('readFully str is :', str); + +// file.delete(); +// console.log('delete success'); +// }, 1000); +// }) +// console.log('atomicFileDeleteTest end'); +// } catch (err) { +// console.log('failed!, Cause: ', JSON.stringify(err) ?? ''); +// } +// } + +// function atomicFileFailTest() { +// console.log('atomicFileFailTest start'); +// let file = new AtomicFile(`/data/local/tmp/c.txt`); +// try { +// let writeSream = file.startWrite(); +// console.log('startWrite success'); +// writeSream.write("abcdef", "utf-16", ()=> { +// }) +// console.log('atomicFileFailTest end'); +// } catch (err) { +// file.failWrite(); +// console.log('failWrite success'); +// console.log('failed!, Cause: ', JSON.stringify(err) ?? ''); +// } +// } + +// function atomicFileUnfinishTest() { +// console.log('atomicFileUnfinishTest start'); +// try { +// let file = new AtomicFile(`/data/local/tmp/d.txt`); +// let writeSream = file.startWrite(); +// console.log('startWrite success'); +// writeSream.write("abcdef", "utf-8", ()=> { +// setTimeout(()=>{ +// let File = file.getBaseFile(); +// console.log('getBaseFile File.fd is:, path:, name:', File.fd, File.path, File.path); + +// //let data = file.readFully(); +// //let decoder = util.TextDecoder.create('utf-8'); +// //let str = decoder.decodeToString(new Uint8Array(data)); +// //console.log('readFully str is :', str); +// }, 1000); +// }) +// console.log('atomicFileUnfinishTest end'); +// } catch (err) { +// console.log('failed!, Cause: ', JSON.stringify(err) ?? ''); +// } +// } + +// function main() { +// console.println("---------- hello ani --------------"); +// atomicFileTest(); +// atomicFileDeleteTest(); +// atomicFileFailTest(); +// atomicFileUnfinishTest(); +// console.println("---------- hello ani end ---------------"); +// } \ 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 000000000..8a87b0036 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_atomicfile/ani/atomicfile_ani.cpp @@ -0,0 +1,248 @@ +/* + * 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; + +// ani_long AtomicFileAni::Constructor(ani_env *env, [[maybe_unused]] ani_class clazz, ani_string path) +// { +// auto [succ, filePath] = TypeConverter::ToUTF8String(env, path); +// if (!succ) { +// HILOGE("The first argument requires filepath"); +// ErrorHandler::Throw(env, EINVAL); +// return ani_long(0); +// } +// auto file = FsAtomicFile::Constructor(filePath); +// if (file == nullptr) { +// ErrorHandler::Throw(env, UNKNOWN_ERR); +// return ani_long(0); +// } + +// return reinterpret_cast(file.get()); +// } + +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()))) { + 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, UNKNOWN_ERR); + 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 file = Unwrap(env, object); + if (file == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + + auto ret = file->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, fsFile); + if (result == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + + return result; +} + +static tuple WrapBufferData(ani_env *env, unique_ptr bufferData) +{ + size_t length = bufferData->length; + ani_arraybuffer externalBuffer = nullptr; + ani_status ret = env->CreateArrayBufferExternal(bufferData->buffer, bufferData->length, + FsAtomicFile::FinalizeCallback, bufferData.release(), &externalBuffer); + if (ret != ANI_OK) { + HILOGE("CreateArrayBufferExternal err: %{private}d", ret); + return { ret, nullptr }; + } + + 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 file = Unwrap(env, object); + if (file == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + + auto [bufferData, err] = file->ReadFully(); + if (err != ERRNO_NOERR) { + ErrorHandler::Throw(env, err); + return nullptr; + } + auto [ret, obj] = WrapBufferData(env, move(bufferData)); + if (ret != 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; + } + + 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; + } + + file->FailWrite(); + return; +} + +void AtomicFileAni::Delete(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto file = Unwrap(env, object); + if (file == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return; + } + + auto ret = file->Delete(); + if (ret != ERRNO_NOERR) { + ErrorHandler::Throw(env, ret); + 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 000000000..20890fa14 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_atomicfile/ani/atomicfile_ani.h @@ -0,0 +1,40 @@ +/* + * 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 ani_long Constructor(ani_env *env, [[maybe_unused]] ani_class clazz, ani_string path); + static ani_string GetPath(ani_env *env, [[maybe_unused]] ani_object object); + static ani_object GetBaseFile(ani_env *env, [[maybe_unused]] ani_object object); + static ani_object ReadFully(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 000000000..04493d1b1 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.cpp @@ -0,0 +1,219 @@ +/* + * 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 "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"; + +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->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 }; +} + +std::tuple, int32_t> FsAtomicFile::ReadFully() +{ + 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 { nullptr, errno }; + } + + auto file = std::unique_ptr( + std::fopen(result, "rb"), &std::fclose); + if (!file) { + HILOGE("Failed to open file, err:%{public}d", errno); + return { nullptr, errno }; + } + + return ReadFileToBuffer(file.get()); +} + +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() +{ + 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() +{ + if (!fs::remove(entity->newFileName)) { + HILOGW("Failed to remove file"); + } + std::string tmpNewFileName = entity->baseFileName; + entity->newFileName = tmpNewFileName.append(TEMP_FILE_SUFFIX); + + return; +} + +int FsAtomicFile::Delete() +{ + bool errFlag = false; + std::error_code fsErrcode; + if (fs::exists(entity->newFileName, fsErrcode) && !fs::remove(entity->newFileName, fsErrcode)) { + errFlag = true; + } + if (fs::exists(entity->baseFileName, fsErrcode) && !fs::remove(entity->baseFileName, fsErrcode)) { + errFlag = true; + } + if (errFlag) { + HILOGE("Failed to remove file, err:%{public}s", fsErrcode.message().c_str()); + return fsErrcode.value(); + } + + entity->newFileName.clear(); + entity->baseFileName.clear(); + return ERRNO_NOERR; +} + +// std::shared_ptr FsAtomicFile::Constructor(string path) +// { +// auto atomicFileEntity = CreateUniquePtr(); +// if (atomicFileEntity == nullptr) { +// HILOGE("Failed to request heap memory"); +// return nullptr; +// } +// atomicFileEntity->baseFileName = path; +// atomicFileEntity->newFileName = path.append(TEMP_FILE_SUFFIX); + +// auto file = new FsAtomicFile(move(atomicFileEntity)); +// std::shared_ptr result(file); + +// return result; +// } + +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 000000000..9d320de75 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.h @@ -0,0 +1,56 @@ +/* + * 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: + static shared_ptr Constructor(string path); + string GetPath(); + FsResult GetBaseFile(); + tuple, int32_t> ReadFully(); + FsResult StartWrite(); + void FinishWrite(); + void FailWrite(); + int 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 000000000..71bcb9ee1 --- /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 -- Gitee From e748a88e5efaa400dac08893f7c522947ba2ca9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E9=91=AB?= Date: Tue, 29 Apr 2025 19:09:57 +0800 Subject: [PATCH 2/3] =?UTF-8?q?atomic=5Fstream=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I9b93ada1105223279728bca5acca65f4dc68ce3a --- .../js/src/mod_fs/ani/bind_function_class.cpp | 4 +- .../js/src/mod_fs/ani/ets/@ohos.file.fs.ets | 126 +---------- .../class_atomicfile/ani/atomicfile_ani.cpp | 212 +++++++++++++----- .../class_atomicfile/ani/atomicfile_ani.h | 6 +- .../mod_fs/class_atomicfile/fs_atomicfile.cpp | 69 +++--- .../mod_fs/class_atomicfile/fs_atomicfile.h | 7 +- 6 files changed, 213 insertions(+), 211 deletions(-) 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 c1fbf6c43..07e3773e0 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 @@ -164,10 +164,9 @@ static ani_status BindTaskSignalClassMethods(ani_env *env) static ani_status BindAtomicFileMethods(ani_env *env) { - static const char *className = "L@ohos/file/fs/AtomicFile;"; + static const char *className = "L@ohos/file/fs/fileIo/AtomicFile;"; std::array methods = { - // ani_native_function { "nativeConstructor", nullptr, reinterpret_cast(AtomicFileAni::Constructor) }, 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) }, @@ -176,6 +175,7 @@ static ani_status BindAtomicFileMethods(ani_env *env) 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); 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 3bf2a2b99..e66347a4c 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 @@ -2049,23 +2049,19 @@ export class WriteStream extends stream.Writable { } export class AtomicFile { + static { + loadLibrary("ani_file_fs"); + } + private nativePtr: long = 0; - private native nativeConstructor(path: string): long; private native getPath(): string; - private writeStream: WriteStream | undefined; - - // constructor(path: string) { - // this.nativePtr = this.nativeConstructor(path); - // } + private writeStream: WriteStream; native constructor(path: string); native getBaseFile(): File; - openRead(): ReadStream { - let path = this.getPath(); - return createReadStream(path); - }; + native openRead(): ReadStream; native readFully(): ArrayBuffer; @@ -2290,112 +2286,4 @@ export class FileIoImpl { static native symlinkSync(target: string, srcPath: string): void; static native utimes(path: string, mtime: number): void; -} - -// function atomicFileTest() { -// try { -// console.log('atomicFileTest start'); -// let file = new AtomicFile(`/data/local/tmp/a.txt`); -// let writeSream = file.startWrite(); -// console.log('startWrite success'); -// writeSream.write("abcdef", "utf-8", ()=> { -// file.finishWrite(); -// console.log('finishWrite success'); - -// setTimeout(()=>{ -// let File = file.getBaseFile(); -// console.log('getBaseFile File.fd is:, path:, name:', File.fd, File.path, File.path); - -// let readStream = file.openRead(); -// const data = readStream.read(); -// if (!data) { -// console.log('openRead data is null'); -// return; -// } -// console.log('openRead data is:', data); - -// //let dataFully = file.readFully(); -// //let decoder = util.TextDecoder.create('utf-8'); -// //let str = decoder.decodeToString(new Uint8Array(dataFully)); -// //console.log('readFully str is :', str); -// }, 1000); -// }) -// console.log('atomicFileTest end'); -// } catch (err) { -// console.log('failed!, Cause: ', JSON.stringify(err) ?? ''); -// } -// } - -// function atomicFileDeleteTest() { -// console.log('atomicFileDeleteTest start'); -// try { -// let file = new AtomicFile(`/data/local/tmp/b.txt`); -// let writeSream = file.startWrite(); -// console.log('startWrite success'); -// writeSream.write("abcdef", "utf-8", ()=> { -// file.finishWrite(); -// console.log('finishWrite success'); - -// setTimeout(()=>{ -// //let data = file.readFully(); -// //let decoder = util.TextDecoder.create('utf-8'); -// //let str = decoder.decodeToString(new Uint8Array(data)); -// //console.log('readFully str is :', str); - -// file.delete(); -// console.log('delete success'); -// }, 1000); -// }) -// console.log('atomicFileDeleteTest end'); -// } catch (err) { -// console.log('failed!, Cause: ', JSON.stringify(err) ?? ''); -// } -// } - -// function atomicFileFailTest() { -// console.log('atomicFileFailTest start'); -// let file = new AtomicFile(`/data/local/tmp/c.txt`); -// try { -// let writeSream = file.startWrite(); -// console.log('startWrite success'); -// writeSream.write("abcdef", "utf-16", ()=> { -// }) -// console.log('atomicFileFailTest end'); -// } catch (err) { -// file.failWrite(); -// console.log('failWrite success'); -// console.log('failed!, Cause: ', JSON.stringify(err) ?? ''); -// } -// } - -// function atomicFileUnfinishTest() { -// console.log('atomicFileUnfinishTest start'); -// try { -// let file = new AtomicFile(`/data/local/tmp/d.txt`); -// let writeSream = file.startWrite(); -// console.log('startWrite success'); -// writeSream.write("abcdef", "utf-8", ()=> { -// setTimeout(()=>{ -// let File = file.getBaseFile(); -// console.log('getBaseFile File.fd is:, path:, name:', File.fd, File.path, File.path); - -// //let data = file.readFully(); -// //let decoder = util.TextDecoder.create('utf-8'); -// //let str = decoder.decodeToString(new Uint8Array(data)); -// //console.log('readFully str is :', str); -// }, 1000); -// }) -// console.log('atomicFileUnfinishTest end'); -// } catch (err) { -// console.log('failed!, Cause: ', JSON.stringify(err) ?? ''); -// } -// } - -// function main() { -// console.println("---------- hello ani --------------"); -// atomicFileTest(); -// atomicFileDeleteTest(); -// atomicFileFailTest(); -// atomicFileUnfinishTest(); -// console.println("---------- hello ani end ---------------"); -// } \ No newline at end of file +} \ 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 index 8a87b0036..11f633734 100644 --- 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 @@ -28,22 +28,9 @@ namespace ANI { using namespace std; using namespace OHOS::FileManagement::ModuleFileIO; -// ani_long AtomicFileAni::Constructor(ani_env *env, [[maybe_unused]] ani_class clazz, ani_string path) -// { -// auto [succ, filePath] = TypeConverter::ToUTF8String(env, path); -// if (!succ) { -// HILOGE("The first argument requires filepath"); -// ErrorHandler::Throw(env, EINVAL); -// return ani_long(0); -// } -// auto file = FsAtomicFile::Constructor(filePath); -// if (file == nullptr) { -// ErrorHandler::Throw(env, UNKNOWN_ERR); -// return ani_long(0); -// } - -// return reinterpret_cast(file.get()); -// } +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); @@ -60,7 +47,8 @@ void AtomicFileAni::Constructor(ani_env *env, ani_object obj, ani_string pathObj return; } - if (ANI_OK != env->Object_SetFieldByName_Long(obj, "nativePtr", reinterpret_cast(ret.GetData()))) { + 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; @@ -83,7 +71,7 @@ ani_string AtomicFileAni::GetPath(ani_env *env, [[maybe_unused]] ani_object obje { auto file = Unwrap(env, object); if (file == nullptr) { - ErrorHandler::Throw(env, UNKNOWN_ERR); + ErrorHandler::Throw(env, E_PARAMS); return nullptr; } @@ -100,20 +88,21 @@ ani_string AtomicFileAni::GetPath(ani_env *env, [[maybe_unused]] ani_object obje ani_object AtomicFileAni::GetBaseFile(ani_env *env, [[maybe_unused]] ani_object object) { - auto file = Unwrap(env, object); - if (file == nullptr) { - ErrorHandler::Throw(env, UNKNOWN_ERR); + auto aotomicFile = Unwrap(env, object); + if (aotomicFile == nullptr) { + ErrorHandler::Throw(env, E_PARAMS); return nullptr; } - auto ret = file->GetBaseFile(); + 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, fsFile); + auto result = FileWrapper::Wrap(env, move(fsFile)); if (result == nullptr) { ErrorHandler::Throw(env, UNKNOWN_ERR); return nullptr; @@ -122,54 +111,160 @@ ani_object AtomicFileAni::GetBaseFile(ani_env *env, [[maybe_unused]] ani_object return result; } -static tuple WrapBufferData(ani_env *env, unique_ptr bufferData) +static ani_object CreateReadStream(ani_env *env, ani_string filePath) { - size_t length = bufferData->length; - ani_arraybuffer externalBuffer = nullptr; - ani_status ret = env->CreateArrayBufferExternal(bufferData->buffer, bufferData->length, - FsAtomicFile::FinalizeCallback, bufferData.release(), &externalBuffer); - if (ret != ANI_OK) { - HILOGE("CreateArrayBufferExternal err: %{private}d", ret); - return { ret, nullptr }; + 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 const char *className = "Lescompat/Uint8Array;"; +static ani_object CreateWriteStream(ani_env *env, ani_string filePath) +{ + static const char *className = "L@ohos/file/fs/fileIo/WriteStream;"; ani_class cls; - if ((ret = env->FindClass(className, &cls)) != ANI_OK) { - HILOGE("Not found %{private}s, err: %{private}d", className, ret); - return { ret, nullptr }; + if (ANI_OK != env->FindClass(className, &cls)) { + HILOGE("Cannot find class %s", className); + return 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 }; + 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; } - 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); + 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, obj }; + return { ANI_OK, static_cast(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 file = Unwrap(env, object); - if (file == nullptr) { - ErrorHandler::Throw(env, UNKNOWN_ERR); + auto aotomicFile = Unwrap(env, object); + if (aotomicFile == nullptr) { + ErrorHandler::Throw(env, E_PARAMS); return nullptr; } - auto [bufferData, err] = file->ReadFully(); - if (err != ERRNO_NOERR) { - ErrorHandler::Throw(env, err); + auto ret = aotomicFile->ReadFully(); + if (!ret.IsSuccess()) { + ErrorHandler::Throw(env, ret.GetError()); return nullptr; } - auto [ret, obj] = WrapBufferData(env, move(bufferData)); - if (ret != ANI_OK) { + + auto &bufferData = ret.GetData().value(); + auto [succ, obj] = WrapBufferData(env, move(bufferData)); + if (succ != ANI_OK) { ErrorHandler::Throw(env, UNKNOWN_ERR); return nullptr; } @@ -210,6 +305,7 @@ void AtomicFileAni::FinishWrite(ani_env *env, [[maybe_unused]] ani_object object return; } + HILOGE("Do FinishWrite"); file->FinishWrite(); return; } @@ -221,22 +317,22 @@ void AtomicFileAni::FailWrite(ani_env *env, [[maybe_unused]] ani_object object) ErrorHandler::Throw(env, UNKNOWN_ERR); return; } - + HILOGE("Do FailWrite"); file->FailWrite(); return; } void AtomicFileAni::Delete(ani_env *env, [[maybe_unused]] ani_object object) { - auto file = Unwrap(env, object); - if (file == nullptr) { - ErrorHandler::Throw(env, UNKNOWN_ERR); + auto aotomicFile = Unwrap(env, object); + if (aotomicFile == nullptr) { + ErrorHandler::Throw(env, E_PARAMS); return; } - auto ret = file->Delete(); - if (ret != ERRNO_NOERR) { - ErrorHandler::Throw(env, ret); + auto ret = aotomicFile->Delete(); + if (!ret.IsSuccess()) { + ErrorHandler::Throw(env, ret.GetError()); return; } 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 index 20890fa14..59b89a14a 100644 --- 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 @@ -24,10 +24,12 @@ namespace ModuleFileIO { namespace ANI { class AtomicFileAni final { public: - static ani_long Constructor(ani_env *env, [[maybe_unused]] ani_class clazz, ani_string path); - static ani_string GetPath(ani_env *env, [[maybe_unused]] ani_object object); + 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_object 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); 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 index 04493d1b1..29855773e 100644 --- a/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.cpp +++ b/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.cpp @@ -24,6 +24,7 @@ #include #include "fs_atomicfile_entity.h" +#include "filemgmt_libfs.h" #include "file_instantiator.h" #include "file_utils.h" @@ -36,6 +37,14 @@ 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); @@ -49,6 +58,11 @@ string FsAtomicFile::GetPath() 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); @@ -107,23 +121,32 @@ static std::tuple, int32_t> ReadFileToBuffer(FILE* f return { std::move(bufferData), ERRNO_NOERR }; } -std::tuple, int32_t> FsAtomicFile::ReadFully() +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 { nullptr, 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 { nullptr, errno }; + return FsResult>::Error(errno); } - return ReadFileToBuffer(file.get()); + auto [bufferData, errCode] = ReadFileToBuffer(file.get()); + if (errCode != ERRNO_NOERR) { + return FsResult>::Error(errCode); + } + return FsResult>::Success(move(bufferData)); } FsResult FsAtomicFile::StartWrite() @@ -146,6 +169,7 @@ FsResult FsAtomicFile::StartWrite() 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); @@ -155,6 +179,7 @@ void FsAtomicFile::FinishWrite() 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"); } @@ -164,42 +189,32 @@ void FsAtomicFile::FailWrite() return; } -int FsAtomicFile::Delete() +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(entity->newFileName, fsErrcode) && !fs::remove(entity->newFileName, fsErrcode)) { + if (fs::exists(rafentity->newFileName, fsErrcode) && !fs::remove(rafentity->newFileName, fsErrcode)) { errFlag = true; } - if (fs::exists(entity->baseFileName, fsErrcode) && !fs::remove(entity->baseFileName, fsErrcode)) { + 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 fsErrcode.value(); + return FsResult::Error(fsErrcode.value()); } - entity->newFileName.clear(); - entity->baseFileName.clear(); - return ERRNO_NOERR; + rafentity->newFileName.clear(); + rafentity->baseFileName.clear(); + return FsResult::Success(); } -// std::shared_ptr FsAtomicFile::Constructor(string path) -// { -// auto atomicFileEntity = CreateUniquePtr(); -// if (atomicFileEntity == nullptr) { -// HILOGE("Failed to request heap memory"); -// return nullptr; -// } -// atomicFileEntity->baseFileName = path; -// atomicFileEntity->newFileName = path.append(TEMP_FILE_SUFFIX); - -// auto file = new FsAtomicFile(move(atomicFileEntity)); -// std::shared_ptr result(file); - -// return result; -// } - FsResult FsAtomicFile::Constructor(string path) { auto atomicFileEntity = CreateUniquePtr(); 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 index 9d320de75..940b1ba9a 100644 --- a/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.h +++ b/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.h @@ -36,14 +36,15 @@ struct BufferData { class FsAtomicFile final { public: - static shared_ptr Constructor(string path); + FsAtomicFileEntity* GetEntity(); + static FsResult Constructor(string path); string GetPath(); FsResult GetBaseFile(); - tuple, int32_t> ReadFully(); + FsResult> ReadFully(); FsResult StartWrite(); void FinishWrite(); void FailWrite(); - int Delete(); + FsResult Delete(); static void FinalizeCallback(void *finalizeData, [[maybe_unused]] void *finalizeHint); private: -- Gitee From 1efe4e298084c3e34d340e2e47cd74a43910279f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E9=91=AB?= Date: Tue, 29 Apr 2025 21:44:34 +0800 Subject: [PATCH 3/3] =?UTF-8?q?stream=E4=BF=AE=E6=94=B9=5F2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Iaab06d4057ed6536888cda3aa68f4ce2ceb41d58 --- .../js/src/mod_fs/class_atomicfile/ani/atomicfile_ani.cpp | 4 ++-- .../kits/js/src/mod_fs/class_atomicfile/ani/atomicfile_ani.h | 2 +- .../kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) 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 index 11f633734..ba35442b6 100644 --- 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 @@ -209,7 +209,7 @@ ani_object AtomicFileAni::OpenRead(ani_env *env, [[maybe_unused]] ani_object obj return CreateStream(env, object, READ_STREAM_CLASS, entity->baseFileName); } -static tuple WrapBufferData(ani_env *env, unique_ptr bufferData) +static tuple WrapBufferData(ani_env *env, unique_ptr bufferData) { ani_arraybuffer externalBuffer = nullptr; @@ -224,7 +224,7 @@ static tuple WrapBufferData(ani_env *env, unique_ptr(externalBuffer) }; + return { ANI_OK, externalBuffer }; // static const char *className = "Lescompat/Uint8Array;"; // ani_class cls; 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 index 59b89a14a..423f7b4b9 100644 --- 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 @@ -27,7 +27,7 @@ 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_object ReadFully(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); 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 index 29855773e..ebc4a7643 100644 --- a/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.cpp +++ b/interfaces/kits/js/src/mod_fs/class_atomicfile/fs_atomicfile.cpp @@ -121,7 +121,7 @@ static std::tuple, int32_t> ReadFileToBuffer(FILE* f return { std::move(bufferData), ERRNO_NOERR }; } -FsResult> FsAtomicFile::ReadFully() +FsResult> FsAtomicFile::ReadFully() { if (entity == nullptr) { HILOGE("Failed to get atomicFileEntity"); -- Gitee