From 332535a50b1f34bc74ef57b2c0f4e3848249718e Mon Sep 17 00:00:00 2001 From: zhongning5 Date: Wed, 4 Dec 2024 16:33:52 +0800 Subject: [PATCH] cherry pick 0605bd2 from https://gitee.com/zhong-ning1/filemanagement_file_api/pulls/867 add atomicfile napi Signed-off-by: zhongning5 --- .../atomicfile_n_exporter.cpp | 477 ++++++++++++++++++ 1 file changed, 477 insertions(+) create mode 100644 interfaces/kits/js/src/mod_fs/class_atomicfile/atomicfile_n_exporter.cpp 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 100644 index 000000000..085e9da21 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_atomicfile/atomicfile_n_exporter.cpp @@ -0,0 +1,477 @@ +/* + * 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 +#include +#include +#include +#include + +#include "atomicfile_entity.h" +#include "class_file/file_entity.h" +#include "class_file/file_n_exporter.h" +#include "common_func.h" +#include "file_utils.h" +#include "filemgmt_libhilog.h" +#include "filemgmt_libn.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace fs = std::filesystem; +using namespace OHOS::FileManagement::LibN; + +namespace { +const std::string READ_STREAM_CLASS = "ReadStream"; +const std::string WRITE_STREAM_CLASS = "WriteStream"; + +struct BufferData { + uint8_t* buffer = nullptr; + size_t length = 0; + + ~BufferData() + { + delete[] buffer; + } +}; +} + +static void FinalizeCallback(napi_env env, void *finalizeData, void *finalizeHint) +{ + BufferData *bufferData = static_cast(finalizeData); + 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(E_PARAMS).ThrowErr(env); + return nullptr; + } + + auto rafEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); + if (rafEntity == nullptr) { + HILOGE("Failed to get atomicFile"); + NError(E_PARAMS).ThrowErr(env); + return nullptr; + } + + return rafEntity; +} + +static napi_value CreateStream(napi_env env, napi_callback_info info, const std::string &streamName, + const std::string &fineName) +{ + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::ZERO)) { + HILOGE("Number of arguments unmatched"); + NError(E_PARAMS).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(UNKROWN_ERR).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(UNKROWN_ERR).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(UNKROWN_ERR).ThrowErr(env); + return nullptr; + } + + return NVal(env, streamObj).val_; +} + +static napi_value CallFunctionByName(napi_env env, napi_value objStream, const std::string &funcName) +{ + napi_valuetype valuetype; + napi_typeof(env, objStream, &valuetype); + if (valuetype != napi_object) { + HILOGE("Valuetype is unmatched"); + NError(UNKROWN_ERR).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(UNKROWN_ERR).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(UNKROWN_ERR).ThrowErr(env); + return nullptr; + } + + status = napi_call_function(env, objStream, value, 0, nullptr, nullptr); + if (status != napi_ok) { + HILOGE("Failed to call %{public}s function", funcName.c_str()); + NError(UNKROWN_ERR).ThrowErr(env); + return nullptr; + } + + return nullptr; +} + +static NVal InstantiateFile(napi_env env, int fd, std::string path, bool isUri) +{ + napi_value objFile = NClass::InstantiateClass(env, FileNExporter::className_, {}); + if (!objFile) { + HILOGE("Failed to instantiate class"); + int ret = close(fd); + if (ret < 0) { + HILOGE("Failed to close fd"); + } + NError(UNKROWN_ERR).ThrowErr(env); + return NVal(); + } + + auto fileEntity = NClass::GetEntityOf(env, objFile); + if (!fileEntity) { + HILOGE("Failed to get fileEntity"); + int ret = close(fd); + if (ret < 0) { + HILOGE("Failed to close fd"); + } + NError(UNKROWN_ERR).ThrowErr(env); + return NVal(); + } + auto fdg = CreateUniquePtr(fd, false); + if (fdg == nullptr) { + HILOGE("Failed to request heap memory."); + close(fd); + NError(EIO).ThrowErr(env); + return NVal(); + } + fileEntity->fd_.swap(fdg); + if (isUri) { + fileEntity->path_ = ""; + fileEntity->uri_ = path; + } else { + fileEntity->path_ = path; + fileEntity->uri_ = ""; + } + return { env, objFile }; +} + +napi_value AtomicFileNExporter::GetBaseFile(napi_env env, napi_callback_info info) +{ + auto rafEntity = GetAtomicFileEntity(env, info); + if (rafEntity == nullptr || rafEntity->baseFileName.size() >= PATH_MAX) { + HILOGE("AtomicFile Failed to get atomicFile"); + return nullptr; + } + + char realPath[PATH_MAX]; + char *result = realpath(rafEntity->baseFileName.c_str(), realPath); + if (result == nullptr) { + HILOGE("Failed to resolve real path, err:%{public}d", errno); + NError(UNKROWN_ERR).ThrowErr(env); + return nullptr; + } + + int fd = open(realPath, O_RDONLY); + if (fd < 0) { + HILOGE("Failed to open file, err:%{public}d", errno); + NError(UNKROWN_ERR).ThrowErr(env); + return nullptr; + } + + return InstantiateFile(env, fd, rafEntity->baseFileName, false).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)) { + if (!fs::remove(rafEntity->newFileName)) { + HILOGW("Failed to remove file"); + } + } + return CreateStream(env, info, READ_STREAM_CLASS, rafEntity->baseFileName); +} + +std::unique_ptr ReadFileToBuffer(napi_env env, FILE* fp) +{ + auto bufferData = std::make_unique(); + if (fp == nullptr) { + HILOGE("Invalid file pointer"); + return nullptr; + } + + int ret = fseek(fp, 0, SEEK_END); + if (ret < 0) { + HILOGE("Failed to call fseek"); + NError(EIO).ThrowErr(env); + return nullptr; + } + + long fileSize = ftell(fp); + if (fileSize < 0) { + HILOGE("Failed to get file size"); + NError(EIO).ThrowErr(env); + return nullptr; + } + + ret = fseek(fp, 0, SEEK_SET); + if (ret < 0) { + HILOGE("Failed to call fseek"); + NError(EIO).ThrowErr(env); + return nullptr; + } + + if (fileSize > 0) { + bufferData->buffer = new(std::nothrow) uint8_t[fileSize]; + if (bufferData->buffer == nullptr) { + HILOGE("Failed to allocate memory"); + NError(EIO).ThrowErr(env); + return nullptr; + } + bufferData->length = fread(bufferData->buffer, sizeof(uint8_t), fileSize, fp); + if ((bufferData->length != fileSize && !feof(fp)) || ferror(fp)) { + HILOGE("Failed to read file, actual length is:%zu, fileSize:%ld", bufferData->length, fileSize); + NError(EIO).ThrowErr(env); + 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 || rafEntity->baseFileName.size() >= PATH_MAX) { + HILOGE("Failed to get atomicFile"); + return nullptr; + } + + char realPath[PATH_MAX]; + char *result = realpath(rafEntity->baseFileName.c_str(), realPath); + if (result == nullptr) { + HILOGE("Failed to resolve file real path, err:%{public}d", errno); + NError(EIO).ThrowErr(env); + return nullptr; + } + + auto file = std::unique_ptr( + std::fopen(realPath, "rb"), &std::fclose); + if (!file) { + HILOGE("Failed to open file, err:%{public}d", errno); + NError(EIO).ThrowErr(env); + return nullptr; + } + + auto bufferData = ReadFileToBuffer(env, file.get()); + if (!bufferData || bufferData->buffer == nullptr) { + HILOGE("Failed to read file, err:%{public}d", errno); + return nullptr; + } + + napi_value externalBuffer = nullptr; + size_t length = bufferData->length; + napi_status status = napi_create_external_arraybuffer( + env, bufferData->buffer, bufferData->length, FinalizeCallback, bufferData.release(), &externalBuffer); + if (status != napi_ok) { + NError(UNKROWN_ERR).ThrowErr(env); + return nullptr; + } + + napi_value outputArray = nullptr; + status = napi_create_typedarray(env, napi_int8_array, length, externalBuffer, 0, &outputArray); + if (status != napi_ok) { + NError(UNKROWN_ERR).ThrowErr(env); + 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) { + fs::path filePath = rafEntity->newFileName; + fs::path parentPath = filePath.parent_path(); + std::error_code errCode; + fs::create_directory(parentPath, errCode); + if (errCode.operator bool()) { + HILOGE("Fail to create directory, err:%{public}s!", errCode.message().c_str()); + NError(ENOENT).ThrowErr(env); + return nullptr; + } + fs::permissions(parentPath, fs::perms::owner_all | fs::perms::group_all | fs::perms::others_exec, + fs::perm_options::replace, errCode); + if (errCode.operator bool()) { + HILOGE("Fail to change permissions err:%{public}s!", errCode.message().c_str()); + NError(EPERM).ThrowErr(env); + return nullptr; + } + + if (mkstemp(tmpfile) == -1) { + HILOGE("Fail to create tmp file err:%{public}d!", errno); + NError(ENOENT).ThrowErr(env); + return nullptr; + } + } + + napi_value writeStream = CreateStream(env, info, WRITE_STREAM_CLASS, rafEntity->newFileName); + if (writeStream == nullptr) { + HILOGE("Failed to create write stream"); + return nullptr; + } + napi_status status = napi_create_reference(env, writeStream, 1, &rafEntity->writeStreamObj); + if (status != napi_ok) { + HILOGE("Failed to create reference"); + NError(UNKROWN_ERR).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 NVal::CreateUndefined(env).val_; + } + + 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(UNKROWN_ERR).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(UNKROWN_ERR).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 NVal::CreateUndefined(env).val_; + } + + 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(UNKROWN_ERR).ThrowErr(env); + return NVal::CreateUndefined(env).val_; + } + + CallFunctionByName(env, writeStream, "closeSync"); + + if (!fs::remove(rafEntity->newFileName)) { + HILOGW("Failed to remove file"); + } + + status = napi_delete_reference(env, rafEntity->writeStreamObj); + if (status != napi_ok) { + HILOGE("Failed to delete reference"); + NError(UNKROWN_ERR).ThrowErr(env); + } + 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 NVal::CreateUndefined(env).val_; + } + + if (fs::exists(rafEntity->newFileName) && !fs::remove(rafEntity->newFileName)) { + HILOGE("Failed to remove file"); + NError(EPERM).ThrowErr(env); + return NVal::CreateUndefined(env).val_; + } + + if (fs::exists(rafEntity->baseFileName) && !fs::remove(rafEntity->baseFileName)) { + HILOGE("Failed to remove file"); + NError(EPERM).ThrowErr(env); + return NVal::CreateUndefined(env).val_; + } + + rafEntity->newFileName.clear(); + rafEntity->baseFileName.clear(); + + return NVal::CreateUndefined(env).val_; +} + +std::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 -- Gitee