From fa13b5f47ad81f5d3202a80379a0339661fb6193 Mon Sep 17 00:00:00 2001 From: zhongning5 Date: Thu, 31 Oct 2024 18:30:46 +0800 Subject: [PATCH] =?UTF-8?q?atomicfile=20=E6=8E=A5=E5=8F=A3=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhongning5 --- interfaces/kits/js/BUILD.gn | 1 + .../class_atomicFile/atomicFile_entity.h | 30 ++ .../atomicFile_n_exporter.cpp | 449 ++++++++++++++++++ .../class_atomicFile/atomicFile_n_exporter.h | 47 ++ interfaces/kits/js/src/mod_fs/module.cpp | 2 + interfaces/kits/ts/streamrw/src/streamrw.ts | 4 + 6 files changed, 533 insertions(+) create mode 100755 interfaces/kits/js/src/mod_fs/class_atomicFile/atomicFile_entity.h create mode 100755 interfaces/kits/js/src/mod_fs/class_atomicFile/atomicFile_n_exporter.cpp create mode 100755 interfaces/kits/js/src/mod_fs/class_atomicFile/atomicFile_n_exporter.h diff --git a/interfaces/kits/js/BUILD.gn b/interfaces/kits/js/BUILD.gn index 3c9b376ee..66685b7b6 100644 --- a/interfaces/kits/js/BUILD.gn +++ b/interfaces/kits/js/BUILD.gn @@ -215,6 +215,7 @@ ohos_shared_library("fs") { "${filemanagement_service_path}/distributedfiledaemon/include/ipc", ] sources += [ + "src/mod_fs/class_atomicFile/atomicFile_n_exporter.cpp", "src/mod_fs/class_randomaccessfile/randomaccessfile_n_exporter.cpp", "src/mod_fs/class_readeriterator/readeriterator_n_exporter.cpp", "src/mod_fs/class_stream/stream_n_exporter.cpp", diff --git a/interfaces/kits/js/src/mod_fs/class_atomicFile/atomicFile_entity.h b/interfaces/kits/js/src/mod_fs/class_atomicFile/atomicFile_entity.h new file mode 100755 index 000000000..14dccc140 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_atomicFile/atomicFile_entity.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 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_ATOMIC_FILE_ENTITY_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_ATOMIC_FILE_ENTITY_H + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +struct AtomicFileEntity { + std::string baseFileName = ""; + std::string newFileName = ""; + napi_ref writeStreamObj = nullptr; +}; +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS +#endif // INTERFACES_KITS_JS_SRC_MOD_FILEIO_CLASS_ATOMIC_FILE_ENTITY_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_atomicFile/atomicFile_n_exporter.cpp b/interfaces/kits/js/src/mod_fs/class_atomicFile/atomicFile_n_exporter.cpp new file mode 100755 index 000000000..5e0cf98c6 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_atomicFile/atomicFile_n_exporter.cpp @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2024 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_n_exporter.h" +#include "atomicFile_entity.h" + +#include +#include +#include +#include +#include + +#include "file_utils.h" +#include "filemgmt_libhilog.h" +#include "filemgmt_libn.h" +#include "../common_func.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; +namespace fs = std::filesystem; +using namespace OHOS::FileManagement::LibN; + +const string READ_STREAM_CLASS = "ReadStream"; +const string WRITE_STREAM_CLASS = "WriteStream"; + +typedef struct { + uint8_t* buffer; + size_t length; +} BufferData; + +static void FinalizeCallback(napi_env env, void *finalizeData, void *finalizeHint) +{ + BufferData *bufferData = static_cast(finalizeData); + delete[] bufferData->buffer; + delete bufferData; +} + +static AtomicFileEntity* GetAtomicFileEntity(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::ZERO)) { + HILOGE("Number of arguments unmatched"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + auto rafEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (rafEntity == nullptr) { + HILOGE("Failed to get atomicFile"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + return rafEntity; +} + +static napi_value CreateStream(napi_env env, napi_callback_info info, const string &streamName, const string &fineName) +{ + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::ZERO)) { + HILOGE("Number of arguments unmatched"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + const char moduleName[] = "@ohos.file.streamrw"; + napi_value streamrw; + napi_status status = napi_load_module(env, moduleName, &streamrw); + if (status != napi_ok) { + HILOGE("Failed to load module"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + napi_value constructor = nullptr; + status = napi_get_named_property(env, streamrw, streamName.c_str(), &constructor); + if (status != napi_ok) { + HILOGE("Failed to get named property"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + napi_value filePath = NVal::CreateUTF8String(env, fineName).val_; + napi_value argv[NARG_CNT::ONE] = {filePath}; + napi_value streamObj; + size_t argc = 1; + status = napi_new_instance(env, constructor, argc, argv, &streamObj); + if (status != napi_ok) { + HILOGE("Failed to create napi new instance"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + return NVal(env, streamObj).val_; +} + +static napi_value CallFunctionByName(napi_env env, napi_value objStream, string funcName) +{ + napi_valuetype valuetype; + napi_typeof(env, objStream, &valuetype); + if (valuetype != napi_object) { + HILOGE("Valuetype is unmatched"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + napi_value key; + napi_status status = napi_create_string_utf8(env, funcName.c_str(), funcName.length(), &key); + if (status != napi_ok) { + HILOGE("Failed to create string utf8"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + napi_value value; + status = napi_get_property(env, objStream, key, &value); + if (status != napi_ok) { + HILOGE("Failed to get property"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + napi_value argv[0] = {}; + status = napi_call_function(env, objStream, value, 0, argv, nullptr); + if (status != napi_ok) { + HILOGE("Failed to call %{public}s function", funcName.c_str()); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + return nullptr; +} + +napi_value AtomicFileNExporter::GetBaseFile(napi_env env, napi_callback_info info) +{ + auto rafEntity = GetAtomicFileEntity(env, info); + if (rafEntity == nullptr) { + HILOGE("Failed to get atomicFile"); + return nullptr; + } + return NVal::CreateUTF8String(env, rafEntity->baseFileName).val_; +} + +napi_value AtomicFileNExporter::OpenRead(napi_env env, napi_callback_info info) +{ + auto rafEntity = GetAtomicFileEntity(env, info); + if (rafEntity == nullptr) { + HILOGE("Failed to get atomicFile"); + return nullptr; + } + + if (fs::exists(rafEntity->newFileName) && fs::exists(rafEntity->baseFileName)) { + fs::remove(rafEntity->newFileName); + } + + return CreateStream(env, info, READ_STREAM_CLASS, rafEntity->baseFileName); +} + +static BufferData ReadFileToBuffer(napi_env env, FILE* fp) +{ + BufferData bufferData{nullptr, 0}; + + if (fp == nullptr) { + HILOGE("Invalid file pointer"); + return bufferData; + } + + int ret = fseek(fp, 0, SEEK_END); + if (ret < 0) { + NError(EINVAL).ThrowErr(env, "Failed to call fseek"); + return bufferData; + } + + long filesize = ftell(fp); + if (filesize < 0) { + NError(EINVAL).ThrowErr(env, "Failed to get file size"); + return bufferData; + } + + ret = fseek(fp, 0, SEEK_SET); + if (ret < 0) { + NError(EINVAL).ThrowErr(env, "Failed to call fseek"); + return bufferData; + } + + if (filesize > 0) { + bufferData.buffer = new(std::nothrow) uint8_t[filesize]; + if (bufferData.buffer == nullptr) { + HILOGE("Failed to allocate memory"); + return bufferData; + } + + bufferData.length = fread(bufferData.buffer, sizeof(char), filesize, fp); + if ((bufferData.length != filesize && !feof(fp)) || ferror(fp)) { + HILOGE("Failed to read file, actual length is:%zu", bufferData.length); + delete[] bufferData.buffer; + bufferData.buffer = nullptr; + bufferData.length = 0; + } + } + + return bufferData; +} + +napi_value AtomicFileNExporter::ReadFully(napi_env env, napi_callback_info info) +{ + auto rafEntity = GetAtomicFileEntity(env, info); + if (rafEntity == nullptr) { + HILOGE("Failed to get atomicFile"); + return nullptr; + } + + std::unique_ptr file(std::fopen(rafEntity->baseFileName.c_str(), "rb"), std::fclose); + FILE* fp = file.get(); + + napi_value outputArray = nullptr; + BufferData bufferData = ReadFileToBuffer(env, fp); + if (bufferData.buffer != nullptr) { + napi_value externalBuffer = nullptr; + BufferData* bufferDataPtr = new(std::nothrow) BufferData{bufferData}; + if (bufferDataPtr == nullptr) { + delete[] bufferData.buffer; + bufferData.buffer = nullptr; + bufferData.length = 0; + NError(EINVAL).ThrowErr(env, "Failed to allocate memory"); + return nullptr; + } + + napi_status status = napi_create_external_arraybuffer( + env, bufferData.buffer, bufferData.length, FinalizeCallback, bufferDataPtr, &externalBuffer); + if (status != napi_ok) { + NError(EINVAL).ThrowErr(env, "Failed to create external arraybuffer"); + delete[] bufferData.buffer; + bufferData.buffer = nullptr; + bufferData.length = 0; + delete bufferDataPtr; + return nullptr; + } + + status = napi_create_typedarray(env, napi_int8_array, bufferData.length, externalBuffer, 0, &outputArray); + if (status != napi_ok) { + NError(EINVAL).ThrowErr(env, "Failed to create typedarray"); + return nullptr; + } + } + + return outputArray; +} + +napi_value AtomicFileNExporter::StartWrite(napi_env env, napi_callback_info info) +{ + auto rafEntity = GetAtomicFileEntity(env, info); + if (rafEntity == nullptr) { + HILOGE("Failed to get atomicFile"); + return nullptr; + } + + char *tmpfile = const_cast(rafEntity->newFileName.c_str()); + if (mkstemp(tmpfile) == -1) { + HILOGE("Fail to create tmp file:%{public}s err:%{public}d!", tmpfile, errno); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + napi_value writeStream = CreateStream(env, info, WRITE_STREAM_CLASS, rafEntity->newFileName); + if (writeStream == nullptr) { + fs::path filePath = rafEntity->newFileName; + fs::path parentPath = filePath.parent_path(); + fs::create_directory(parentPath); + std::filesystem::permissions(parentPath, fs::perms::owner_all | fs::perms::group_all | fs::perms::others_exec); + writeStream = CreateStream(env, info, WRITE_STREAM_CLASS, rafEntity->newFileName); + if (writeStream == nullptr) { + HILOGE("Fail to create write stream"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + } + + napi_status status = napi_create_reference(env, writeStream, 1, &rafEntity->writeStreamObj); + if (status != napi_ok) { + HILOGE("Failed to create reference"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + return writeStream; +} + +napi_value AtomicFileNExporter::FinishWrite(napi_env env, napi_callback_info info) +{ + auto rafEntity = GetAtomicFileEntity(env, info); + if (rafEntity == nullptr) { + HILOGE("Failed to get atomicFile"); + return nullptr; + } + + napi_value writeStream; + napi_status status = napi_get_reference_value(env, rafEntity->writeStreamObj, &writeStream); + if (status != napi_ok) { + HILOGE("Failed to get reference value"); + NError(EINVAL).ThrowErr(env); + return NVal::CreateUndefined(env).val_; + } + + CallFunctionByName(env, writeStream, "closeSync"); + std::rename(rafEntity->newFileName.c_str(), rafEntity->baseFileName.c_str()); + + status = napi_delete_reference(env, rafEntity->writeStreamObj); + if (status != napi_ok) { + HILOGE("Failed to delete reference"); + NError(EINVAL).ThrowErr(env); + return NVal::CreateUndefined(env).val_; + } + return NVal::CreateUndefined(env).val_; +} + +napi_value AtomicFileNExporter::FailWrite(napi_env env, napi_callback_info info) +{ + auto rafEntity = GetAtomicFileEntity(env, info); + if (rafEntity == nullptr) { + HILOGE("Failed to get atomicFile"); + return nullptr; + } + + napi_value writeStream; + napi_status status = napi_get_reference_value(env, rafEntity->writeStreamObj, &writeStream); + if (status != napi_ok) { + HILOGE("Failed to get reference value"); + NError(EINVAL).ThrowErr(env); + return NVal::CreateUndefined(env).val_; + } + + CallFunctionByName(env, writeStream, "closeSync"); + fs::remove(rafEntity->newFileName); + + status = napi_delete_reference(env, rafEntity->writeStreamObj); + if (status != napi_ok) { + HILOGE("Failed to delete reference"); + NError(EINVAL).ThrowErr(env); + return NVal::CreateUndefined(env).val_; + } + return NVal::CreateUndefined(env).val_; +} + +napi_value AtomicFileNExporter::Delete(napi_env env, napi_callback_info info) +{ + auto rafEntity = GetAtomicFileEntity(env, info); + if (rafEntity == nullptr) { + HILOGE("Failed to get atomicFile"); + return nullptr; + } + fs::remove(rafEntity->newFileName); + fs::remove(rafEntity->baseFileName); + rafEntity->newFileName = ""; + rafEntity->baseFileName = ""; + + return NVal::CreateUndefined(env).val_; +} + +napi_value AtomicFileNExporter::Constructor(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::ONE)) { + HILOGE("Number of arguments unmatched"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + auto [resGetSecondArg, file, num] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); + if (!resGetSecondArg) { + HILOGE("Invalid path"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + auto atomicFileEntity = CreateUniquePtr(); + if (atomicFileEntity == nullptr) { + HILOGE("Failed to request heap memory."); + NError(ENOMEM).ThrowErr(env); + return nullptr; + } + std::string filePath = file.get(); + atomicFileEntity->baseFileName = filePath; + atomicFileEntity->newFileName = filePath.append("_XXXXXX"); + if (!NClass::SetEntityFor(env, funcArg.GetThisVar(), move(atomicFileEntity))) { + HILOGE("Failed to wrap entity for obj AtomicFile"); + NError(EIO).ThrowErr(env); + return nullptr; + } + return funcArg.GetThisVar(); +} + +bool AtomicFileNExporter::Export() +{ + vector props = { +#if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM) + NVal::DeclareNapiFunction("getBaseFile", GetBaseFile), + NVal::DeclareNapiFunction("openRead", OpenRead), + NVal::DeclareNapiFunction("readFully", ReadFully), + NVal::DeclareNapiFunction("startWrite", StartWrite), + NVal::DeclareNapiFunction("finishWrite", FinishWrite), + NVal::DeclareNapiFunction("failWrite", FailWrite), + NVal::DeclareNapiFunction("delete", Delete), +#endif + }; + + string className = GetClassName(); + bool succ = false; + napi_value classValue = nullptr; + tie(succ, classValue) = NClass::DefineClass( + exports_.env_, className, AtomicFileNExporter::Constructor, move(props)); + if (!succ) { + HILOGE("INNER BUG. Failed to define class"); + NError(EIO).ThrowErr(exports_.env_); + return false; + } + succ = NClass::SaveClass(exports_.env_, className, classValue); + if (!succ) { + HILOGE("INNER BUG. Failed to save class"); + NError(EIO).ThrowErr(exports_.env_); + return false; + } + + return exports_.AddProp(className, classValue); +} + +string AtomicFileNExporter::GetClassName() +{ + return AtomicFileNExporter::className_; +} + +AtomicFileNExporter::AtomicFileNExporter(napi_env env, napi_value exports) : NExporter(env, exports) {} +AtomicFileNExporter::~AtomicFileNExporter() {} +} // namespace ModuleFileIO +} // namespace DistributedFS +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_atomicFile/atomicFile_n_exporter.h b/interfaces/kits/js/src/mod_fs/class_atomicFile/atomicFile_n_exporter.h new file mode 100755 index 000000000..fa7005741 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_atomicFile/atomicFile_n_exporter.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 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_FILEIO_CLASS_ATOMIC_FILE_N_EXPORTER_H +#define INTERFACES_KITS_JS_SRC_MOD_FILEIO_CLASS_ATOMIC_FILE_N_EXPORTER_H + +#include "filemgmt_libn.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace OHOS::FileManagement::LibN; +class AtomicFileNExporter final : public NExporter { +public: + inline static const std::string className_ = "AtomicFile"; + + bool Export() override; + std::string GetClassName() override; + + static napi_value Constructor(napi_env env, napi_callback_info cbinfo); + static napi_value GetBaseFile(napi_env env, napi_callback_info cbinfo); + static napi_value OpenRead(napi_env env, napi_callback_info cbinfo); + static napi_value ReadFully(napi_env env, napi_callback_info cbinfo); + static napi_value StartWrite(napi_env env, napi_callback_info cbinfo); + static napi_value FinishWrite(napi_env env, napi_callback_info cbinfo); + static napi_value FailWrite(napi_env env, napi_callback_info cbinfo); + static napi_value Delete(napi_env env, napi_callback_info cbinfo); + + AtomicFileNExporter(napi_env env, napi_value exports); + ~AtomicFileNExporter() override; +}; +} // namespace ModuleFileIO +} // namespace DistributedFS +} // namespace OHOS +#endif \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/module.cpp b/interfaces/kits/js/src/mod_fs/module.cpp index ad976623b..df66b983a 100644 --- a/interfaces/kits/js/src/mod_fs/module.cpp +++ b/interfaces/kits/js/src/mod_fs/module.cpp @@ -21,6 +21,7 @@ #include "class_file/file_n_exporter.h" #include "class_stat/stat_n_exporter.h" #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM) +#include "class_atomicFile/atomicFile_n_exporter.h" #include "class_randomaccessfile/randomaccessfile_n_exporter.h" #include "class_readeriterator/readeriterator_n_exporter.h" #include "class_stream/stream_n_exporter.h" @@ -45,6 +46,7 @@ static napi_value Export(napi_env env, napi_value exports) products.emplace_back(make_unique(env, exports)); products.emplace_back(make_unique(env, exports)); #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM) + products.emplace_back(make_unique(env, exports)); products.emplace_back(make_unique(env, exports)); products.emplace_back(make_unique(env, exports)); products.emplace_back(make_unique(env, exports)); diff --git a/interfaces/kits/ts/streamrw/src/streamrw.ts b/interfaces/kits/ts/streamrw/src/streamrw.ts index a81d5b5ff..58e8b8bb0 100644 --- a/interfaces/kits/ts/streamrw/src/streamrw.ts +++ b/interfaces/kits/ts/streamrw/src/streamrw.ts @@ -141,6 +141,10 @@ class WriteStream extends stream.Writable { this.stream?.close(); } + closeSync(): void { + this.stream?.closeSync(); + } + doInitialize(callback: Function): void { callback(); } -- Gitee