diff --git a/bundle.json b/bundle.json index 78b145b37c41506c2958d7904c596617eca78693..ded14f6a75e1871877a533a53154379aa192f90d 100644 --- a/bundle.json +++ b/bundle.json @@ -19,7 +19,9 @@ "SystemCapability.FileManagement.File.DistributedFile", "SystemCapability.FileManagement.File.Environment.FolderObtain" ], - "features": [], + "features": [ + "file_api_read_optimize" + ], "adapted_system_type": [ "mini","small","standard" ], "rom": "4096KB", "ram": "4096KB", diff --git a/file_api.gni b/file_api.gni index 7fc94ace62004264471a75000deb0900070d3727..16a2906232aa9e6c0be06d10d274a6f673b32e2e 100644 --- a/file_api.gni +++ b/file_api.gni @@ -23,3 +23,7 @@ utils_path = "${file_api_path}/utils" use_mac = "${current_os}_${current_cpu}" == "mac_x64" || "${current_os}_${current_cpu}" == "mac_arm64" use_mingw_win = "${current_os}_${current_cpu}" == "mingw_x86_64" + +declare_args() { + file_api_read_optimize = false +} diff --git a/interfaces/kits/hyperaio/include/hyperaio.h b/interfaces/kits/hyperaio/include/hyperaio.h new file mode 100644 index 0000000000000000000000000000000000000000..6ece2728bfd54712a941a13e5c354e790b041094 --- /dev/null +++ b/interfaces/kits/hyperaio/include/hyperaio.h @@ -0,0 +1,94 @@ +/* + * 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 FILEMANAGEMENT_FILE_API_INTERFACES_KITS_IOURING_HYPER_AIO_H +#define FILEMANAGEMENT_FILE_API_INTERFACES_KITS_IOURING_HYPER_AIO_H + +#include +#include +#include "liburing.h" + +namespace OHOS { +namespace HyperAio { +#define IOURING_APP_PREMISSION (1U << 0) + +struct ReadInfo { + int32_t fd; + uint32_t len; + uint64_t offset; + void *buf; + uint64_t userData; +}; + +struct ReadReqs { + uint32_t reqNum; + struct ReadInfo *reqs; +}; + +struct OpenInfo { + int32_t dfd; + int32_t flags; + mode_t mode; + void *path; + uint64_t userData; +}; + +struct OpenReqs { + uint32_t reqNum; + struct OpenInfo *reqs; +}; + +struct CancelInfo { + uint64_t userData; + uint64_t targetUserData; +}; + +struct CancelReqs { + uint32_t reqNum; + struct CancelInfo *reqs; +}; + +struct IoResponse { + uint64_t userData; + int32_t res; + uint32_t flags; + IoResponse(uint64_t userData, int32_t res, uint32_t flags) + : userData(userData), + res(res), + flags(flags) { + } +}; + +class HyperAio { +public: + using ProcessIoResultCallBack = std::function)>; + uint32_t SupportIouring(); + int32_t CtxInit(ProcessIoResultCallBack *callBack); + int32_t StartReadReqs(ReadReqs *req); + int32_t StartOpenReqs(OpenReqs *req); + int32_t StartCancelReqs(CancelReqs *req); + int32_t DestroyCtx(); +private: + io_uring uring_; + ProcessIoResultCallBack ioResultCallBack_ = nullptr; + std::thread harvestThread_; + std::atomic stopThread_; + std::atomic initialized_; + void HarvestRes(); + struct io_uring_sqe* GetSqeWithRetry(struct io_uring *ring); +}; +} +} +#endif \ No newline at end of file diff --git a/interfaces/kits/hyperaio/include/libhilog.h b/interfaces/kits/hyperaio/include/libhilog.h new file mode 100644 index 0000000000000000000000000000000000000000..9e882f46b4731ff0a5deaf7f7ea74405a50ac14f --- /dev/null +++ b/interfaces/kits/hyperaio/include/libhilog.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023-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 FILEMGMT_LIBHILOG_H +#define FILEMGMT_LIBHILOG_H + +#include "hilog/log.h" + +#include + +namespace OHOS { +#ifndef LOG_DOMAIN +#define LOG_DOMAIN 0xD001600 +#endif + +#ifndef LOG_TAG +#define LOG_TAG "FileManagement" +#endif + +#if defined __FILE_NAME__ +#define FILEMGMT_FILE_NAME __FILE_NAME__ +#else +#include +#define FILEMGMT_FILE_NAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) +#endif + +#define HILOGF(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_FATAL, LOG_DOMAIN, LOG_TAG, \ + "[%{public}s:%{public}d->%{public}s] " fmt, FILEMGMT_FILE_NAME, __LINE__, __FUNCTION__, ##__VA_ARGS__)) +#define HILOGE(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_ERROR, LOG_DOMAIN, LOG_TAG, \ + "[%{public}s:%{public}d->%{public}s] " fmt, FILEMGMT_FILE_NAME, __LINE__, __FUNCTION__, ##__VA_ARGS__)) +#define HILOGW(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_WARN, LOG_DOMAIN, LOG_TAG, \ + "[%{public}s:%{public}d->%{public}s] " fmt, FILEMGMT_FILE_NAME, __LINE__, __FUNCTION__, ##__VA_ARGS__)) +#define HILOGI(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_INFO, LOG_DOMAIN, LOG_TAG, \ + "[%{public}s:%{public}d->%{public}s] " fmt, FILEMGMT_FILE_NAME, __LINE__, __FUNCTION__, ##__VA_ARGS__)) +#define HILOGD(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_DEBUG, LOG_DOMAIN, LOG_TAG, \ + "[%{public}s:%{public}d->%{public}s] " fmt, FILEMGMT_FILE_NAME, __LINE__, __FUNCTION__, ##__VA_ARGS__)) +} // namespace OHOS + +#endif // FILEMGMT_LIBHILOG_H \ No newline at end of file diff --git a/interfaces/kits/hyperaio/src/hyperaio.cpp b/interfaces/kits/hyperaio/src/hyperaio.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f719cf7292d8f1f966db2842660834befc8ccbcf --- /dev/null +++ b/interfaces/kits/hyperaio/src/hyperaio.cpp @@ -0,0 +1,221 @@ +/* + * 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 "hyperaio.h" +#include "libhilog.h" +#include +#include +#include "ipc_skeleton.h" +#include "accesstoken_kit.h" + +namespace OHOS { +namespace HyperAio { +const uint32_t URING_QUEUE_SIZE = 512; +const uint32_t DELAY = 20; +const uint32_t BATCH_SIZE = 128; +const uint32_t RETRIES = 3; + +static bool HasAccessIouringPermission() +{ + Security::AccessToken::AccessTokenID tokenCaller = IPCSkeleton::GetCallingTokenID(); + const std::string permissionName = "ohos.permission.ALLOW_IOURING"; + int32_t res = Security::AccessToken::AccessTokenKit::VerifyAccessToken(tokenCaller, permissionName); + if (res != Security::AccessToken::PermissionState::PERMISSION_GRANTED) { + HILOGE("have no ALLOW_IOURING permission, AccessTokenID = %{public}u", tokenCaller); + return false; + } + return true; +} + +uint32_t HyperAio::SupportIouring() +{ + uint32_t flags = 0; + if (HasAccessIouringPermission()) { + flags |= IOURING_APP_PERMISSION; + } + return flags; +} + +int32_t HyperAio::CtxInit(ProcessIoResultCallBack *callBack) +{ + if (callBack == nullptr) { + HILOGE("callBack is null"); + return -EINVAL; + } + int32_t ret = io_uring_queue_init(URING_QUEUE_SIZE, &uring_, 0); + if (ret < 0) { + HILOGE("init io_uring failed, ret = %{public}d", ret); + return ret; + } + ioResultCallBack_ = *callBack; + stopThread_.store(false); + harvestThread_ = std::thread(&HyperAio::HarvestRes, this); + initialized_.store(true); + return EOK; +} + +struct io_uring_sqe* HyperAio::GetSqeWithRetry(struct io_uring *ring) +{ + struct io_uring_sqe *sqe; + for (uint32_t i = 0; i < RETRIES; i++) { + sqe = io_uring_get_sqe(ring); + if (sqe != nullptr) { + return sqe; + } + io_uring_submit(ring); + std::this_thread::sleep_for(std::chrono::milliseconds(DELAY)); + } + return nullptr; +} + +int32_t HyperAio::StartOpenReqs(OpenReqs *req) +{ + if (req == nullptr || req->reqs == nullptr) { + return EINVAL; + } + if (!initialized_.load()) { + HILOGE("HyperAio is not initialized"); + return EPERM; + } + uint32_t totalReqs = req->reqNum; + for (uint32_t start = 0; start < totalReqs; start += BATCH_SIZE) { + uint32_t end = std::min(totalReqs, start + BATCH_SIZE); + if (end < start) { + HILOGE("Overflow detected: start + BATCH_SIZE exceeds uint32_t max"); + return EINVAL; + } + for (uint32_t i = start; i < end; ++i) { + struct io_uring_sqe *sqe = GetSqeWithRetry(&uring_); + if (sqe == nullptr) { + HILOGE("get sqe failed"); + return ENOMEM; + } + struct OpenInfo *openInfo = &req->reqs[i]; + io_uring_sqe_set_data(sqe, reinterpret_cast(openInfo->userData)); + io_uring_prep_openat(sqe, openInfo->dfd, static_cast(openInfo->path), + openInfo->flags, openInfo->mode); + } + int32_t ret = io_uring_submit(&uring_); + if (ret < 0) { + HILOGE("submit open reqs failed, ret = %{public}d", ret); + return ret; + } + } + return EOK; +} + +int32_t HyperAio::StartReadReqs(ReadReqs *req) +{ + if (req == nullptr || req->reqs == nullptr) { + return EINVAL; + } + if (!initialized_.load()) { + HILOGE("HyperAio is not initialized"); + return EPERM; + } + uint32_t totalReqs = req->reqNum; + for (uint32_t start = 0; start < totalReqs; start += BATCH_SIZE) { + uint32_t end = std::min(totalReqs, start + BATCH_SIZE); + if (end < start) { + HILOGE("Overflow detected: start + BATCH_SIZE exceeds uint32_t max"); + return EINVAL; + } + for (uint32_t i = start; i < end; ++i) { + struct io_uring_sqe *sqe = GetSqeWithRetry(&uring_); + if (sqe == nullptr) { + HILOGE("get sqe failed"); + return ENOMEM; + } + struct ReadInfo *readInfo = &req->reqs[i]; + io_uring_sqe_set_data(sqe, reinterpret_cast(readInfo->userData)); + io_uring_prep_read(sqe, readInfo->fd, readInfo->buf, readInfo->len, readInfo->offset); + } + int32_t ret = io_uring_submit(&uring_); + if (ret < 0) { + HILOGE("submit read reqs failed, ret = %{public}d", ret); + return ret; + } + } + return EOK; +} + +int32_t HyperAio::StartCancelReqs(CancelReqs *req) +{ + if (req == nullptr || req->reqs == nullptr) { + return EINVAL; + } + if (!initialized_.load()) { + HILOGE("HyperAio is not initialized"); + return EPERM; + } + uint32_t totalReqs = req->reqNum; + for (uint32_t start = 0; start < totalReqs; start += BATCH_SIZE) { + uint32_t end = std::min(totalReqs, start + BATCH_SIZE); + if (end < start) { + HILOGE("Overflow detected: start + BATCH_SIZE exceeds uint32_t max"); + return EINVAL; + } + for (uint32_t i = start; i < end; ++i) { + struct io_uring_sqe *sqe = GetSqeWithRetry(&uring_); + if (sqe == nullptr) { + HILOGE("get sqe failed"); + return ENOMEM; + } + struct CancelInfo *cancelInfo = &req->reqs[i]; + io_uring_sqe_set_data(sqe, reinterpret_cast(cancelInfo->userData)); + io_uring_prep_cancel(sqe, reinterpret_cast(cancelInfo->targetUserData), 0); + } + int32_t ret = io_uring_submit(&uring_); + if (ret < 0) { + HILOGE("submit cancel reqs failed, ret = %{public}d", ret); + return ret; + } + } + return EOK; +} + +void HyperAio::HarvestRes() +{ + struct __kernel_timespec timeout = { .tv_sec = 0, .tv_nsec = 200 * 1e6 }; + while (!stopThread_.load()) { + struct io_uring_cqe *cqe; + int32_t ret = io_uring_wait_cqe_timeout(&uring_, &cqe, &timeout); + if (ret == -ETIME) { + continue; + } + if (ret < 0 || cqe == nullptr) { + HILOGI("wait cqe failed, ret = %{public}d", ret); + continue; + } + auto response = std::make_unique(cqe->user_data, cqe->res, cqe->flags); + io_uring_cqe_seen(&uring_, cqe); + if (ioResultCallBack_) { + ioResultCallBack_(std::move(response)); + } + } +} + +int32_t HyperAio::DestroyCtx() +{ + stopThread_.store(true); + if (harvestThread_.joinable()) { + harvestThread_.join(); + } + io_uring_queue_exit(&uring_); + initialized_.store(false); + return EOK; +} +} +} \ No newline at end of file diff --git a/interfaces/kits/js/BUILD.gn b/interfaces/kits/js/BUILD.gn index e06a5ceced045c0612723daad369a7875dd74312..9fc445f9ab59139dfe499b9fb3c1c33d8d637021 100644 --- a/interfaces/kits/js/BUILD.gn +++ b/interfaces/kits/js/BUILD.gn @@ -377,6 +377,10 @@ ohos_shared_library("file") { "hilog:libhilog", "napi:ace_napi", ] + + if (file_api_read_optimize) { + defines = [ "WEARABLE_PRODUCT" ] + } } ohos_shared_library("statfs") { diff --git a/interfaces/kits/js/src/mod_file/class_file/file_n_exporter.cpp b/interfaces/kits/js/src/mod_file/class_file/file_n_exporter.cpp index 972c56b50e24e06c9da0c230609323ca6dfcc464..01f591e74928233f30d421f2474786e3aa46a53b 100644 --- a/interfaces/kits/js/src/mod_file/class_file/file_n_exporter.cpp +++ b/interfaces/kits/js/src/mod_file/class_file/file_n_exporter.cpp @@ -905,7 +905,11 @@ void ReadArrayBufferExec(napi_env env, void *data) if (read(fdg.GetFD(), buffer.get(), len) != FAILED) { asyncCallbackInfo->result = SUCCESS; asyncCallbackInfo->len = len; +#ifdef WEARABLE_PRODUCT + asyncCallbackInfo->contents = std::move(buffer); +#else asyncCallbackInfo->contents = std::string(buffer.get()); +#endif } } } else if (statPath == ENOENT) { diff --git a/interfaces/kits/js/src/mod_file/class_file/file_n_exporter.h b/interfaces/kits/js/src/mod_file/class_file/file_n_exporter.h index b9760b8a5c75ee115c0bc81e3f16e46339401a74..f39a8069ea84c2e58d15649705fd7fe6fd626a51 100644 --- a/interfaces/kits/js/src/mod_file/class_file/file_n_exporter.h +++ b/interfaces/kits/js/src/mod_file/class_file/file_n_exporter.h @@ -172,7 +172,11 @@ struct AsyncReadBufferCallbackInfo { int result = DEFAULT_RESULT; int errorType = -1; int32_t len = 0; +#ifdef WEARABLE_PRODUCT + std::unique_ptr contents = nullptr; +#else std::string contents = ""; +#endif }; class FileNExporter final : public NExporter { 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 index 456a2f8f057faa9e4608d12622fe65bcff148b84..6958df62bed345e172ab6de31a4fac0e59f0c044 100644 --- 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 @@ -73,7 +73,7 @@ static napi_value CreateStream(napi_env env, napi_callback_info info, const std: napi_status status = napi_load_module(env, moduleName, &streamrw); if (status != napi_ok) { HILOGE("Failed to load module"); - NError(UNKROWN_ERR).ThrowErr(env, "Internal error"); + NError(UNKROWN_ERR).ThrowErr(env, "Failed to load module"); return nullptr; } @@ -81,7 +81,7 @@ static napi_value CreateStream(napi_env env, napi_callback_info info, const std: 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, "Internal error"); + NError(UNKROWN_ERR).ThrowErr(env, "Failed to get named property"); return nullptr; } @@ -92,7 +92,7 @@ static napi_value CreateStream(napi_env env, napi_callback_info info, const std: 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, "Internal error"); + NError(UNKROWN_ERR).ThrowErr(env, "Failed to create napi new instance"); return nullptr; } @@ -135,7 +135,7 @@ static NVal InstantiateFile(napi_env env, int fd, std::string path, bool isUri) if (!objFile) { close(fd); HILOGE("Failed to instantiate class"); - NError(UNKROWN_ERR).ThrowErr(env, "Internal error"); + NError(UNKROWN_ERR).ThrowErr(env, "Failed to instantiate class"); return NVal(); } @@ -143,7 +143,7 @@ static NVal InstantiateFile(napi_env env, int fd, std::string path, bool isUri) if (fileEntity == nullptr) { close(fd); HILOGE("Failed to get fileEntity"); - NError(UNKROWN_ERR).ThrowErr(env, "Internal error"); + NError(UNKROWN_ERR).ThrowErr(env, "Failed to get fileEntity"); return NVal(); } auto fdg = CreateUniquePtr(fd, false); @@ -186,7 +186,7 @@ napi_value AtomicFileNExporter::GetBaseFile(napi_env env, napi_callback_info inf auto [rafEntity, errcode] = GetAtomicFileEntity(env, info); if (errcode != 0) { if (errcode == UNKROWN_ERR) { - NError(errcode).ThrowErr(env, "Internal error"); + NError(errcode).ThrowErr(env, "Failed to get atomicFile"); } else { NError(errcode).ThrowErr(env); } @@ -195,7 +195,7 @@ napi_value AtomicFileNExporter::GetBaseFile(napi_env env, napi_callback_info inf if (rafEntity->baseFileName.size() >= PATH_MAX) { HILOGE("Base file name is too long"); - NError(UNKROWN_ERR).ThrowErr(env, "Internal error"); + NError(UNKROWN_ERR).ThrowErr(env, "Base file name is too long"); return nullptr; } @@ -221,7 +221,7 @@ napi_value AtomicFileNExporter::OpenRead(napi_env env, napi_callback_info info) auto [rafEntity, errcode] = GetAtomicFileEntity(env, info); if (errcode != 0) { if (errcode == UNKROWN_ERR) { - NError(errcode).ThrowErr(env, "Internal error"); + NError(errcode).ThrowErr(env, "Failed to get atomicFile"); } else { NError(errcode).ThrowErr(env); } @@ -273,7 +273,7 @@ napi_value AtomicFileNExporter::ReadFully(napi_env env, napi_callback_info info) auto [rafEntity, errcode] = GetAtomicFileEntity(env, info); if (errcode != 0) { if (errcode == UNKROWN_ERR) { - NError(errcode).ThrowErr(env, "Internal error"); + NError(errcode).ThrowErr(env, "Failed to get atomicFile"); } else { NError(errcode).ThrowErr(env); } @@ -299,7 +299,7 @@ napi_value AtomicFileNExporter::ReadFully(napi_env env, napi_callback_info info) auto [bufferData, readErrcode] = ReadFileToBuffer(env, file.get()); if (readErrcode != 0) { if (readErrcode == UNKROWN_ERR) { - NError(readErrcode).ThrowErr(env, "Internal error"); + NError(readErrcode).ThrowErr(env, "Failed to read file to buffer"); } else { NError(readErrcode).ThrowErr(env); } @@ -311,14 +311,14 @@ napi_value AtomicFileNExporter::ReadFully(napi_env env, napi_callback_info info) 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, "Internal error"); + NError(UNKROWN_ERR).ThrowErr(env, "Failed to create external arraybuffer"); 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, "Internal error"); + NError(UNKROWN_ERR).ThrowErr(env, "Failed to create typedarray"); return nullptr; } @@ -330,7 +330,7 @@ napi_value AtomicFileNExporter::StartWrite(napi_env env, napi_callback_info info auto [rafEntity, errcode] = GetAtomicFileEntity(env, info); if (errcode != 0) { if (errcode == UNKROWN_ERR) { - NError(errcode).ThrowErr(env, "Internal error"); + NError(errcode).ThrowErr(env, "Failed to get atomicFile"); } else { NError(errcode).ThrowErr(env); } @@ -360,7 +360,7 @@ napi_value AtomicFileNExporter::StartWrite(napi_env env, napi_callback_info info 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, "Internal error"); + NError(UNKROWN_ERR).ThrowErr(env, "Failed to create reference"); return nullptr; } return writeStream; @@ -371,7 +371,7 @@ napi_value AtomicFileNExporter::FinishWrite(napi_env env, napi_callback_info inf auto [rafEntity, errcode] = GetAtomicFileEntity(env, info); if (errcode != 0) { if (errcode == UNKROWN_ERR) { - NError(errcode).ThrowErr(env, "Internal error"); + NError(errcode).ThrowErr(env, "Failed to get atomicFile"); } else { NError(errcode).ThrowErr(env); } @@ -382,7 +382,7 @@ napi_value AtomicFileNExporter::FinishWrite(napi_env env, napi_callback_info inf 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, "Internal error"); + NError(UNKROWN_ERR).ThrowErr(env, "Failed to get reference value"); return nullptr; } @@ -394,7 +394,7 @@ napi_value AtomicFileNExporter::FinishWrite(napi_env env, napi_callback_info inf status = napi_delete_reference(env, rafEntity->writeStreamObj); if (status != napi_ok) { HILOGE("Failed to delete reference"); - NError(UNKROWN_ERR).ThrowErr(env, "Internal error"); + NError(UNKROWN_ERR).ThrowErr(env, "Failed to delete reference"); return nullptr; } return nullptr; @@ -405,7 +405,7 @@ napi_value AtomicFileNExporter::FailWrite(napi_env env, napi_callback_info info) auto [rafEntity, errcode] = GetAtomicFileEntity(env, info); if (errcode != 0) { if (errcode == UNKROWN_ERR) { - NError(errcode).ThrowErr(env, "Internal error"); + NError(errcode).ThrowErr(env, "Failed to get atomicFile"); } else { NError(errcode).ThrowErr(env); } @@ -416,7 +416,7 @@ napi_value AtomicFileNExporter::FailWrite(napi_env env, napi_callback_info info) 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, "Internal error"); + NError(UNKROWN_ERR).ThrowErr(env, "Failed to get reference value"); return nullptr; } @@ -430,7 +430,7 @@ napi_value AtomicFileNExporter::FailWrite(napi_env env, napi_callback_info info) status = napi_delete_reference(env, rafEntity->writeStreamObj); if (status != napi_ok) { HILOGE("Failed to delete reference"); - NError(UNKROWN_ERR).ThrowErr(env, "Internal error"); + NError(UNKROWN_ERR).ThrowErr(env, "Failed to delete reference"); } return nullptr; } @@ -440,7 +440,7 @@ napi_value AtomicFileNExporter::Delete(napi_env env, napi_callback_info info) auto [rafEntity, errcode] = GetAtomicFileEntity(env, info); if (errcode != 0) { if (errcode == UNKROWN_ERR) { - NError(errcode).ThrowErr(env, "Internal error"); + NError(errcode).ThrowErr(env, "Failed to get atomicFile"); } else { NError(errcode).ThrowErr(env); } diff --git a/interfaces/kits/js/src/mod_fs/class_randomaccessfile/randomaccessfile_n_exporter.cpp b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/randomaccessfile_n_exporter.cpp index 3a620f1fc2176403c9ee555e54225e1b4b016c85..bbe140fbb1839681212fdc407395cf9bcccac6c8 100644 --- a/interfaces/kits/js/src/mod_fs/class_randomaccessfile/randomaccessfile_n_exporter.cpp +++ b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/randomaccessfile_n_exporter.cpp @@ -275,8 +275,10 @@ napi_value RandomAccessFileNExporter::WriteSync(napi_env env, napi_callback_info struct AsyncIORafWriteArg { NRef rafRefWriteArrayBuf; + std::unique_ptr guardWriteStr_ = nullptr; int actLen = 0; explicit AsyncIORafWriteArg(NVal refWriteArrayBuf) : rafRefWriteArrayBuf(refWriteArrayBuf) {} + explicit AsyncIORafWriteArg(std::unique_ptr &&guardWriteStr) : guardWriteStr_(move(guardWriteStr)) {} ~AsyncIORafWriteArg() = default; }; @@ -286,7 +288,8 @@ static napi_value WriteExec(napi_env env, NFuncArg &funcArg, RandomAccessFileEnt void *buf = nullptr; size_t len = 0; int64_t offset = 0; - tie(succ, ignore, buf, len, offset) = + unique_ptr bufGuard = nullptr; + tie(succ, bufGuard, buf, len, offset) = CommonFunc::GetWriteArg(env, funcArg[NARG_POS::FIRST], funcArg[NARG_POS::SECOND]); if (!succ) { HILOGE("Invalid buffer/options"); @@ -294,7 +297,7 @@ static napi_value WriteExec(napi_env env, NFuncArg &funcArg, RandomAccessFileEnt return nullptr; } - auto arg = CreateSharedPtr(NVal(env, funcArg[NARG_POS::FIRST])); + auto arg = CreateSharedPtr(move(bufGuard)); if (arg == nullptr) { HILOGE("Failed to request heap memory."); NError(ENOMEM).ThrowErr(env); @@ -319,9 +322,8 @@ static napi_value WriteExec(napi_env env, NFuncArg &funcArg, RandomAccessFileEnt auto cbCompl = [arg](napi_env env, NError err) -> NVal { if (err) { return { env, err.GetNapiErr(env) }; - } else { - return { NVal::CreateInt64(env, arg->actLen) }; } + return { NVal::CreateInt64(env, arg->actLen) }; }; NVal thisVar(env, funcArg.GetThisVar()); diff --git a/interfaces/kits/js/src/mod_fs/properties/copy.cpp b/interfaces/kits/js/src/mod_fs/properties/copy.cpp index ffd87bebc2837bedbe60dee1932da8dd4acb3846..ba09491ce974dfd8590e8fdc212882d78f2488a5 100644 --- a/interfaces/kits/js/src/mod_fs/properties/copy.cpp +++ b/interfaces/kits/js/src/mod_fs/properties/copy.cpp @@ -53,6 +53,7 @@ const std::string NETWORK_PARA = "?networkid="; const string PROCEDURE_COPY_NAME = "FileFSCopy"; const std::string MEDIALIBRARY_DATA_URI = "datashare:///media"; const std::string MEDIA = "media"; +const std::string MTP_PATH_PREFIX = "/storage/External/mtp"; const int SLEEP_TIME = 100000; constexpr int DISMATCH = 0; constexpr int MATCH = 1; @@ -97,6 +98,10 @@ static int OpenSrcFile(const string &srcPth, std::shared_ptr infos, i srcFd = open(srcPth.c_str(), O_RDONLY); if (srcFd < 0) { HILOGE("Error opening src file descriptor. errno = %{public}d", errno); + bool isCanceled = (infos->taskSignal != nullptr) && (infos->taskSignal->CheckCancelIfNeed(srcPth)); + if (isCanceled && Copy::IsMtpDeviceFilePath(srcPth)) { + return ECANCELED; + } return errno; } } @@ -850,6 +855,9 @@ tuple> Copy::CreateFileInfos( auto taskSignalEntity = NClass::GetEntityOf(infos->env, infos->copySignal.val_); if (taskSignalEntity != nullptr) { infos->taskSignal = taskSignalEntity->taskSignal_; + if (IsMtpDeviceFilePath(infos->srcPath)) { + infos->taskSignal->SetFileInfoOfRemoteTask("", infos->srcPath); + } } } return { ERRNO_NOERR, infos }; @@ -974,6 +982,11 @@ napi_value Copy::Async(napi_env env, napi_callback_info info) return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_COPY_NAME, cbExec, cbCompl).val_; } } + +bool Copy::IsMtpDeviceFilePath(const std::string &path) +{ + return path.rfind(MTP_PATH_PREFIX, 0) != std::string::npos; +} } // namespace ModuleFileIO } // namespace FileManagement } // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/copy.h b/interfaces/kits/js/src/mod_fs/properties/copy.h index b08fd68f6ca302fb8f2623f75c77a8174ccd622e..1c2a2f913c8d863e18e8b6b73d02c65bd91453c1 100644 --- a/interfaces/kits/js/src/mod_fs/properties/copy.h +++ b/interfaces/kits/js/src/mod_fs/properties/copy.h @@ -130,6 +130,7 @@ public: static std::map> jsCbMap_; static void UnregisterListener(std::shared_ptr fileInfos); static std::recursive_mutex mutex_; + static bool IsMtpDeviceFilePath(const std::string &path); private: // operator of napi diff --git a/interfaces/kits/native/task_signal/task_signal.cpp b/interfaces/kits/native/task_signal/task_signal.cpp index 4700448d26139396a7eb2191a5aa9c1692c717da..e8c72d3bbecfec8d52f4471066bfac2d6d1f4b4d 100644 --- a/interfaces/kits/native/task_signal/task_signal.cpp +++ b/interfaces/kits/native/task_signal/task_signal.cpp @@ -17,12 +17,16 @@ #include "distributed_file_daemon_manager.h" #include "filemgmt_libhilog.h" +#include namespace OHOS { namespace DistributedFS { namespace ModuleTaskSignal { using namespace FileManagement; constexpr int CANCEL_ERR = -3; +const char CANCEL_XATTR_KEY[] = {"user.cancelcopy"}; +const std::string MTP_PATH_PREFIX = "/storage/External/mtp"; + int32_t TaskSignal::Cancel() { HILOGD("TaskSignal Cancel in."); @@ -40,6 +44,13 @@ int32_t TaskSignal::Cancel() } OnCancel(); return ret; + } else { + if (filePath_.rfind(MTP_PATH_PREFIX, 0) != std::string::npos) { + std::string value = ""; + if (setxattr(filePath_.c_str(), CANCEL_XATTR_KEY, value.c_str(), value.size(), 0) < 0) { + HILOGE("cancelcopy setxattr fail, errno is %{public}d", errno); + } + } } needCancel_.store(true); return 0; diff --git a/interfaces/kits/rust/BUILD.gn b/interfaces/kits/rust/BUILD.gn index a4cc7d69884cbd02fb53601c148ad8a0643bd072..965a4ec2138a9353e605c806118cb3e4eadf85d5 100644 --- a/interfaces/kits/rust/BUILD.gn +++ b/interfaces/kits/rust/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Huawei Device Co., Ltd. +# Copyright (c) 2023-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 @@ -26,5 +26,6 @@ ohos_rust_shared_ffi("rust_file") { rustflags = [ "-Zstack-protector=all" ] deps = [ "//third_party/rust/crates/libc:lib" ] external_deps = [ "hilog:hilog_rust" ] + innerapi_tags = [ "platformsdk" ] public_configs = [ ":public_config" ] } diff --git a/interfaces/test/unittest/class_file/rust_test.cpp b/interfaces/test/unittest/class_file/rust_test.cpp index 7b3800a8be0d056e7503b540fc5c3047296a46c0..6c60bd3b1abccf5fcc9f39c3118d3d5342604f40 100644 --- a/interfaces/test/unittest/class_file/rust_test.cpp +++ b/interfaces/test/unittest/class_file/rust_test.cpp @@ -95,10 +95,10 @@ HWTEST_F(RustTest, RustTest_ReaderIterator_0003, testing::ext::TestSize.Level1) * @tc.desc: Test function of NextLine() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGDNF */ -HWTEST_F(RustTest, RustTest_NextLine_0001, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_NextLine_0001, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_NextLine_0001"; @@ -119,10 +119,10 @@ HWTEST_F(RustTest, RustTest_NextLine_0001, testing::ext::TestSize.Level1) * @tc.desc: Test function of NextLine() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGDNF */ -HWTEST_F(RustTest, RustTest_NextLine_0002, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_NextLine_0002, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_NextLine_0002"; @@ -159,10 +159,10 @@ HWTEST_F(RustTest, RustTest_NextLine_0003, testing::ext::TestSize.Level1) * @tc.desc: Test function of Lseek() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGCS3 */ -HWTEST_F(RustTest, RustTest_Lseek_0001, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Lseek_0001, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Lseek_0001"; @@ -182,10 +182,10 @@ HWTEST_F(RustTest, RustTest_Lseek_0001, testing::ext::TestSize.Level1) * @tc.desc: Test function of Lseek() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGCS3 */ -HWTEST_F(RustTest, RustTest_Lseek_0002, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Lseek_0002, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Lseek_0002"; @@ -208,10 +208,10 @@ HWTEST_F(RustTest, RustTest_Lseek_0002, testing::ext::TestSize.Level1) * @tc.desc: Test function of Lseek() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGCS3 */ -HWTEST_F(RustTest, RustTest_Lseek_0003, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Lseek_0003, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Lseek_0003"; @@ -254,10 +254,10 @@ HWTEST_F(RustTest, RustTest_Lseek_0004, testing::ext::TestSize.Level1) * @tc.desc: Test function of Lseek() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGCS3 */ -HWTEST_F(RustTest, RustTest_Lseek_0005, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Lseek_0005, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Lseek_0005"; @@ -280,10 +280,10 @@ HWTEST_F(RustTest, RustTest_Lseek_0005, testing::ext::TestSize.Level1) * @tc.desc: Test function of Lseek() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGCS3 */ -HWTEST_F(RustTest, RustTest_Lseek_0006, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Lseek_0006, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Lseek_0006"; @@ -303,10 +303,10 @@ HWTEST_F(RustTest, RustTest_Lseek_0006, testing::ext::TestSize.Level1) * @tc.desc: Test function of Lseek() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGCS3 */ -HWTEST_F(RustTest, RustTest_Lseek_0007, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Lseek_0007, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Lseek_0007"; @@ -322,10 +322,10 @@ HWTEST_F(RustTest, RustTest_Lseek_0007, testing::ext::TestSize.Level1) * @tc.desc: Test function of Mkdirs() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGDNJ */ -HWTEST_F(RustTest, RustTest_Mkdirs_0001, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Mkdirs_0001, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Mkdirs_0001"; @@ -347,10 +347,10 @@ HWTEST_F(RustTest, RustTest_Mkdirs_0001, testing::ext::TestSize.Level1) * @tc.desc: Test function of Mkdirs() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGDNJ */ -HWTEST_F(RustTest, RustTest_Mkdirs_0002, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Mkdirs_0002, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Mkdirs_0002"; @@ -372,10 +372,10 @@ HWTEST_F(RustTest, RustTest_Mkdirs_0002, testing::ext::TestSize.Level1) * @tc.desc: Test function of Mkdirs() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGDNJ */ -HWTEST_F(RustTest, RustTest_Mkdirs_0003, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Mkdirs_0003, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Mkdirs_0003"; @@ -393,10 +393,10 @@ HWTEST_F(RustTest, RustTest_Mkdirs_0003, testing::ext::TestSize.Level1) * @tc.desc: Test function of Mkdirs() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGDNJ */ -HWTEST_F(RustTest, RustTest_Mkdirs_0004, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Mkdirs_0004, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Mkdirs_0004"; @@ -412,10 +412,10 @@ HWTEST_F(RustTest, RustTest_Mkdirs_0004, testing::ext::TestSize.Level1) * @tc.desc: Test function of Mkdirs() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGDNJ */ -HWTEST_F(RustTest, RustTest_Mkdirs_0005, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Mkdirs_0005, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Mkdirs_0005"; @@ -432,10 +432,10 @@ HWTEST_F(RustTest, RustTest_Mkdirs_0005, testing::ext::TestSize.Level1) * @tc.desc: Test function of Mkdirs() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGDNJ */ -HWTEST_F(RustTest, RustTest_Mkdirs_0006, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Mkdirs_0006, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Mkdirs_0006"; @@ -452,10 +452,10 @@ HWTEST_F(RustTest, RustTest_Mkdirs_0006, testing::ext::TestSize.Level1) * @tc.desc: Test function of Mkdirs() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGDNJ */ -HWTEST_F(RustTest, RustTest_Mkdirs_0007, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Mkdirs_0007, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Mkdirs_0007"; @@ -472,10 +472,10 @@ HWTEST_F(RustTest, RustTest_Mkdirs_0007, testing::ext::TestSize.Level1) * @tc.desc: Test function of Mkdirs() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGDNJ */ -HWTEST_F(RustTest, RustTest_Mkdirs_0008, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_Mkdirs_0008, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_Mkdirs_0008"; @@ -492,10 +492,10 @@ HWTEST_F(RustTest, RustTest_Mkdirs_0008, testing::ext::TestSize.Level1) * @tc.desc: Test function of GetParent() interface for SUCCESS. * @tc.size: MEDIUM * @tc.type: FUNC -* @tc.level Level 1 +* @tc.level Level 0 * @tc.require: AR000IGDNL */ -HWTEST_F(RustTest, RustTest_GetParent_0001, testing::ext::TestSize.Level1) +HWTEST_F(RustTest, RustTest_GetParent_0001, testing::ext::TestSize.Level0) { GTEST_LOG_(INFO) << "RustTest-begin RustTest_GetParent_0001"; diff --git a/utils/filemgmt_libn/src/n_async/n_async_work_callback.cpp b/utils/filemgmt_libn/src/n_async/n_async_work_callback.cpp index 9cad32c850d274ac7fcb7dfee9a9bcfa2141d77c..4f2e88a80ff631e5d08e328337710a53c6a5783e 100644 --- a/utils/filemgmt_libn/src/n_async/n_async_work_callback.cpp +++ b/utils/filemgmt_libn/src/n_async/n_async_work_callback.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -120,6 +120,7 @@ static void CallbackComplete(napi_env env, napi_status status, void *data) napi_value callback = ctx->cb_.Deref(env).val_; if (!bool(ctx->cb_)) { HILOGE("failed to get ref."); + napi_close_handle_scope(env, scope); return; }