diff --git a/interfaces/kits/js/BUILD.gn b/interfaces/kits/js/BUILD.gn index 9334220be9d28e01972e60ed84bec52d044d4e8a..f3febd44cdbe65cecc2e63a73fa1fed91078311e 100644 --- a/interfaces/kits/js/BUILD.gn +++ b/interfaces/kits/js/BUILD.gn @@ -683,6 +683,7 @@ ohos_shared_library("ani_fs_class") { "src/mod_fs/properties/ani/access_ani.cpp", "src/mod_fs/properties/ani/close_ani.cpp", "src/mod_fs/properties/ani/copy_file_ani.cpp", + "src/mod_fs/properties/ani/create_randomaccessfile_ani.cpp", "src/mod_fs/properties/ani/listfile_ani.cpp", "src/mod_fs/properties/ani/mkdir_ani.cpp", "src/mod_fs/properties/ani/move_ani.cpp", @@ -695,6 +696,7 @@ ohos_shared_library("ani_fs_class") { "src/mod_fs/properties/ani/write_ani.cpp", "src/mod_fs/properties/close_core.cpp", "src/mod_fs/properties/copy_file_core.cpp", + "src/mod_fs/properties/create_randomaccessfile_core.cpp", "src/mod_fs/properties/listfile_core.cpp", "src/mod_fs/properties/mkdir_core.cpp", "src/mod_fs/properties/move_core.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 065495c9ade9c4c775ce494fa218d0c382077459..14d599ddf67facec8cde69e06d451f53fc6e43fe 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 @@ -21,6 +21,7 @@ #include "bind_function.h" #include "close_ani.h" #include "copy_file_ani.h" +#include "create_randomaccessfile_ani.h" #include "file_ani.h" #include "filemgmt_libhilog.h" #include "listfile_ani.h" @@ -75,6 +76,8 @@ static ani_status BindStaticMethods(ani_env *env) std::array methods = { ani_native_function { "closeSync", nullptr, reinterpret_cast(CloseAni::CloseSync) }, ani_native_function { "copyFileSync", nullptr, reinterpret_cast(CopyFileAni::CopyFileSync) }, + ani_native_function { "createRandomAccessFileSync", nullptr, reinterpret_cast( + CreateRandomAccessFileAni::CreateRandomAccessFileSync) }, ani_native_function { "doAccessSync", nullptr, reinterpret_cast(AccessAni::AccessSync3) }, ani_native_function { "listFileSync", nullptr, reinterpret_cast(ListFileAni::ListFileSync) }, ani_native_function { "mkdirSync", "Lstd/core/String;:V", reinterpret_cast(MkdirkAni::MkdirSync0) }, 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 67eb95add30a0cd803f0542ae46c90f870189651..6c1f7f559680c6f12edec975f7026e420a71dec9 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 @@ -492,6 +492,72 @@ function stat(file: string | number, callback: AsyncCallback): void }); } +function createRandomAccessFileSync(file: string | File, mode?: number, + options?: RandomAccessFileOptions): RandomAccessFile { + return FileIoImpl.createRandomAccessFileSync(file, mode, options); +} + +function createRandomAccessFile(file: string | File, mode?: number, + options?: RandomAccessFileOptions): Promise { + return new Promise((resolve: (result: RandomAccessFile) => void, + reject: (e: BusinessError) => void) => { + if (mode === undefined) { + let promise = taskpool.execute(FileIoImpl.createRandomAccessFileSync, file); + promise.then((ret: NullishType): void => { + let raffile = ret as RandomAccessFileInner; + resolve(raffile); + }).catch((e: BusinessError): void => { + reject(e); + }); + return; + } + if (mode !== undefined && options === undefined) { + let promise = taskpool.execute(FileIoImpl.createRandomAccessFileSync, file, mode); + promise.then((ret: NullishType): void => { + let raffile = ret as RandomAccessFileInner; + resolve(raffile); + }).catch((e: BusinessError): void => { + reject(e); + }); + return; + } + let promise = taskpool.execute(FileIoImpl.createRandomAccessFileSync, file, mode, options); + promise.then((ret: NullishType): void => { + let raffile = ret as RandomAccessFileInner; + resolve(raffile); + }).catch((e: BusinessError): void => { + reject(e); + }); + }); +} + +function createRandomAccessFile(file: string | File, callback: AsyncCallback): void { + let promise = taskpool.execute(FileIoImpl.createRandomAccessFileSync, file); + promise.then((ret: NullishType): void => { + let e = new BusinessError(); + e.code = 0; + let raffile = ret as RandomAccessFile; + callback(e, raffile); + }).catch((e: BusinessError): void => { + let f: RandomAccessFile = new RandomAccessFileInner(0); + callback(e, f); + }); +} + +function createRandomAccessFile(file: string | File, mode: number, + callback: AsyncCallback): void { + let promise = taskpool.execute(FileIoImpl.createRandomAccessFileSync, file, mode); + promise.then((ret: NullishType): void => { + let e = new BusinessError(); + e.code = 0; + let raffile = ret as RandomAccessFile; + callback(e, raffile); + }).catch((e: BusinessError): void => { + let f: RandomAccessFile = new RandomAccessFileInner(0); + callback(e, f); + }); +} + export interface Filter { suffix?: Array; displayName?: Array; @@ -659,4 +725,6 @@ class FileIoImpl { static native writeSync(fd: number, buffer: string | ArrayBuffer, options?: WriteOptions): number; + static native createRandomAccessFileSync(file: string | File, mode?: number, + options?: RandomAccessFileOptions): RandomAccessFile; } diff --git a/interfaces/kits/js/src/mod_fs/properties/ani/create_randomaccessfile_ani.cpp b/interfaces/kits/js/src/mod_fs/properties/ani/create_randomaccessfile_ani.cpp new file mode 100644 index 0000000000000000000000000000000000000000..acb9e29c34fc433d8471bec1376ed109ee5a8b4e --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/ani/create_randomaccessfile_ani.cpp @@ -0,0 +1,198 @@ +/* + * 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 "create_randomaccessfile_ani.h" + +#include "ani_helper.h" +#include "create_randomaccessfile_core.h" +#include "error_handler.h" +#include "filemgmt_libhilog.h" +#include "type_converter.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { + +using namespace std; +using namespace OHOS::FileManagement::ModuleFileIO; + +static ani_object Wrap(ani_env *env, const FsRandomAccessFile *rafFile) +{ + static const char *className = "L@ohos/file/fs/RandomAccessFileInner;"; + 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, "", "J:V", &ctor)) { + HILOGE("Cannot find constructor method for class %s", className); + return nullptr; + } + ani_long ptr = static_cast(reinterpret_cast(rafFile)); + ani_object obj; + if (ANI_OK != env->Object_New(cls, ctor, &obj, ptr)) { + HILOGE("New %s obj Failed!", className); + return nullptr; + } + const auto &fdRet = rafFile->GetFD(); + if (!fdRet.IsSuccess()) { + HILOGE("GetFD Failed!"); + return nullptr; + } + const auto &fd = fdRet.GetData().value(); + if (ANI_OK != AniHelper::SetPropertyValue(env, cls, obj, "fd", static_cast(fd))) { + HILOGE("Set fd field value failed!"); + return nullptr; + } + const auto &fpRet = rafFile->GetFPointer(); + if (!fpRet.IsSuccess()) { + HILOGE("GetFPointer Failed!"); + return nullptr; + } + const auto &fp = fpRet.GetData().value(); + if (ANI_OK != AniHelper::SetPropertyValue(env, cls, obj, "filePointer", static_cast(fp))) { + HILOGE("Set fp field value failed!"); + return nullptr; + } + return obj; +} + +static tuple JudgeFile(ani_env *env, ani_object obj) +{ + ani_class stringClass; + env->FindClass("Lstd/core/String;", &stringClass); + ani_boolean isString = false; + env->Object_InstanceOf(obj, stringClass, &isString); + if (isString) { + return { true, true }; + } + + ani_class fileClass; + env->FindClass("L@ohos/file/fs/FileInner;", &fileClass); + ani_boolean isFile = false; + env->Object_InstanceOf(obj, fileClass, &isFile); + if (isFile) { + return { true, false }; + } + HILOGE("Invalid file type"); + return { false, false }; +} + +static tuple> ToRafOptions(ani_env *env, ani_object obj) +{ + RandomAccessFileOptions options; + ani_boolean isUndefined; + env->Reference_IsUndefined(obj, &isUndefined); + if (isUndefined) { + return { true, nullopt }; + } + + auto [succStart, start] = AniHelper::ParseInt64Option(env, obj, "start"); + if (!succStart) { + HILOGE("Illegal option.start parameter"); + return { false, nullopt }; + } + options.start = start; + + auto [succEnd, end] = AniHelper::ParseInt64Option(env, obj, "end"); + if (!succEnd) { + HILOGE("Illegal option.end parameter"); + return { false, nullopt }; + } + options.end = end; + + return { true, make_optional(move(options)) }; +} + +static ani_object CreateRandomAccessFileByString(ani_env *env, ani_object file, ani_object mode, + optional op) +{ + auto [succPath, path] = TypeConverter::ToUTF8String(env, static_cast(file)); + if (!succPath) { + HILOGE("Parse file path failed"); + return nullptr; + } + auto [succMode, modeOp] = TypeConverter::ToOptionalInt32(env, mode); + if (!succMode) { + HILOGE("Invalid mode"); + ErrorHandler::Throw(env, EINVAL); + return nullptr; + } + FsResult ret = CreateRandomAccessFileCore::DoCreateRandomAccessFile(path, modeOp, op); + if (!ret.IsSuccess()) { + HILOGE("CreateRandomAccessFile failed"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return nullptr; + } + const FsRandomAccessFile *refFile = ret.GetData().value(); + auto result = Wrap(env, move(refFile)); + if (result == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + return result; +} + +ani_object CreateRandomAccessFileAni::CreateRandomAccessFileSync(ani_env *env, [[maybe_unused]] ani_class clazz, + ani_object file, ani_object mode, ani_object options) +{ + auto [succOp, op] = ToRafOptions(env, options); + if (!succOp) { + HILOGE("Failed to resolve options!"); + ErrorHandler::Throw(env, EINVAL); + return nullptr; + } + + auto [succ, isPath] = JudgeFile(env, file); + if (!succ) { + HILOGE("Judge file argument failed"); + ErrorHandler::Throw(env, EINVAL); + return nullptr; + } + + if (isPath) { + return CreateRandomAccessFileByString(env, file, mode, op); + } else { + ani_double fdOp; + if (ANI_OK != env->Object_GetPropertyByName_Double(file, "fd", &fdOp)) { + HILOGE("Get fd in class file failed"); + ErrorHandler::Throw(env, EINVAL); + return nullptr; + } + int32_t fd = static_cast(fdOp); + FsResult ret = CreateRandomAccessFileCore::DoCreateRandomAccessFile(fd, op); + if (!ret.IsSuccess()) { + HILOGE("CreateRandomAccessFile failed"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return nullptr; + } + const FsRandomAccessFile *refFile = ret.GetData().value(); + auto result = Wrap(env, move(refFile)); + if (result == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + return result; + } +} + +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS diff --git a/interfaces/kits/js/src/mod_fs/properties/ani/create_randomaccessfile_ani.h b/interfaces/kits/js/src/mod_fs/properties/ani/create_randomaccessfile_ani.h new file mode 100644 index 0000000000000000000000000000000000000000..eb794059659181dfc0edb82d7373ba42b2257f54 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/ani/create_randomaccessfile_ani.h @@ -0,0 +1,37 @@ +/* + * 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_PROPERTIES_ANI_CREATE_RANDOMACCESSFILE_ANI_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_ANI_CREATE_RANDOMACCESSFILE_ANI_H + +#include + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { + +class CreateRandomAccessFileAni final { +public: + static ani_object CreateRandomAccessFileSync(ani_env *env, [[maybe_unused]] ani_class clazz, + ani_object file, ani_object mode, ani_object options); +}; + +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS + +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_ANI_CREATE_RANDOMACCESSFILE_ANI_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/create_randomaccessfile_core.cpp b/interfaces/kits/js/src/mod_fs/properties/create_randomaccessfile_core.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ef94cd44cc449dc0c5f4fcfab661739735504ef --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/create_randomaccessfile_core.cpp @@ -0,0 +1,212 @@ +/* + * 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 "create_randomaccessfile_core.h" + +#include + +#include "file_entity.h" +#include "file_utils.h" +#include "filemgmt_libhilog.h" +#include "fs_randomaccessfile.h" +#include "fs_utils.h" +#include "randomaccessfile_entity.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; + +static tuple ParseStringToFileInfo(const string &path) +{ + if (strlen(path.c_str()) < 0) { + HILOGE("The first argument requires filepath/file"); + return { false, FileInfo { false, nullptr, nullptr }, EINVAL}; + } + OHOS::DistributedFS::FDGuard sfd; + auto fdg = CreateUniquePtr(sfd, false); + if (fdg == nullptr) { + HILOGE("Failed to request heap memory."); + close(sfd); + return { false, FileInfo { false, nullptr, nullptr }, ENOMEM}; + } + size_t length = path.length() + 1; + auto chars = std::make_unique(length); + auto ret = strncpy_s(chars.get(), length, path.c_str(), length - 1); + if (ret != EOK) { + HILOGE("Copy file path failed!"); + return { false, FileInfo { false, nullptr, nullptr }, ENOMEM}; + } + return { true, FileInfo { true, move(chars), move(fdg) }, ERRNO_NOERR}; +} + +static tuple ParseFdToFileInfo(const int32_t &fd) +{ + if (fd < 0) { + HILOGE("Invalid fd"); + return { false, FileInfo { false, nullptr, nullptr }, EINVAL}; + } + auto dupFd = dup(fd); + if (dupFd < 0) { + HILOGE("Failed to get valid fd, fail reason: %{public}s, fd: %{public}d", strerror(errno), fd); + return { false, FileInfo { false, nullptr, nullptr }, EINVAL}; + } + auto fdg = CreateUniquePtr(dupFd, false); + if (fdg == nullptr) { + HILOGE("Failed to request heap memory."); + close(dupFd); + return { false, FileInfo { false, nullptr, nullptr }, ENOMEM}; + } + return { true, FileInfo { false, nullptr, move(fdg) }, ERRNO_NOERR}; +} + +static tuple ValidRafOptions(const optional &options) +{ + RandomAccessFileOptions op = options.value(); + int64_t opStart = INVALID_POS; + int64_t opEnd = INVALID_POS; + + optional startOp = op.start; + optional endOp = op.end; + + if (startOp.has_value()) { + int64_t start = 0; + start = startOp.value(); + if (start < 0) { + HILOGE("Invalid option.start, positive integer is desired"); + return {false, opStart, opEnd}; + } + opStart = start; + } + if (endOp.has_value()) { + int64_t end = 0; + end = endOp.value(); + if (end < 0) { + HILOGE("Invalid option.end, positive integer is desired"); + return {false, opStart, opEnd}; + } + opEnd = end; + } + return {true, opStart, opEnd}; +} + +static tuple ValidAndConvertFlags(const optional &mode, + const optional &options, FileInfo &fileInfo) +{ + uint32_t flags = O_RDONLY; + int64_t start = INVALID_POS; + int64_t end = INVALID_POS; + if (fileInfo.isPath && mode.has_value()) { + auto modeValue = mode.value(); + if (modeValue < 0) { + HILOGE("Invalid flags"); + return {false, flags, start, end}; + } + flags = FsUtils::ConvertFlags(static_cast(modeValue)); + } + if (options.has_value()) { + auto [succOpt, start, end] = ValidRafOptions(options); + if (!succOpt) { + HILOGE("invalid RandomAccessFile options"); + return {false, flags, start, end}; + } + } + + return {true, flags, start, end}; +} + +static FsResult InstantiateRandomAccessFile(unique_ptr fdg, + int64_t fp, + int64_t start = INVALID_POS, + int64_t end = INVALID_POS) +{ + FsResult result = FsRandomAccessFile::Constructor(); + if (!result.IsSuccess()) { + HILOGE("Failed to instantiate class"); + return FsResult::Error(EIO); + } + + const FsRandomAccessFile *objRAF = result.GetData().value(); + if (!objRAF) { + HILOGE("Cannot instantiate randomaccessfile"); + return FsResult::Error(EIO); + } + + auto *rafEntity = objRAF->GetRAFEntity(); + if (!rafEntity) { + HILOGE("Cannot instantiate randomaccessfile because of void entity"); + return FsResult::Error(EIO); + } + rafEntity->fd.swap(fdg); + rafEntity->filePointer = fp; + rafEntity->start = start; + rafEntity->end = end; + return result; +} + +FsResult CreateRandomAccessFileCore::DoCreateRandomAccessFile( + const string &path, const optional &mode, const optional &options) +{ + auto [succ, fileInfo, err] = ParseStringToFileInfo(path); + if (!succ) { + return FsResult::Error(err); + } + + auto [succFlags, flags, ignoreStart, ignoreEnd] = ValidAndConvertFlags(mode, options, fileInfo); + if (!succFlags) { + return FsResult::Error(EINVAL); + } + + unique_ptr openReq = { new uv_fs_t, FsUtils::FsReqCleanup }; + if (!openReq) { + HILOGE("Failed to request heap memory."); + return FsResult::Error(ENOMEM); + } + + int ret = uv_fs_open(nullptr, openReq.get(), fileInfo.path.get(), flags, S_IRUSR | + S_IWUSR | S_IRGRP | S_IWGRP, NULL); + if (ret < 0) { + return FsResult::Error(ret); + } + + fileInfo.fdg->SetFD(openReq.get()->result, false); + + if (options.has_value()) { + auto [succ, start, end] = ValidRafOptions(options); + if (succ) { + return InstantiateRandomAccessFile(move(fileInfo.fdg), 0, start, end); + } + } + return InstantiateRandomAccessFile(move(fileInfo.fdg), 0); +} + +FsResult CreateRandomAccessFileCore::DoCreateRandomAccessFile( + const int32_t &fd, const optional &options) +{ + auto [succ, fileInfo, err] = ParseFdToFileInfo(fd); + if (!succ) { + return FsResult::Error(err); + } + if (options.has_value()) { + auto [succ, start, end] = ValidRafOptions(options); + if (succ) { + return InstantiateRandomAccessFile(move(fileInfo.fdg), 0, start, end); + } + } + return InstantiateRandomAccessFile(move(fileInfo.fdg), 0); +} + +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/create_randomaccessfile_core.h b/interfaces/kits/js/src/mod_fs/properties/create_randomaccessfile_core.h new file mode 100644 index 0000000000000000000000000000000000000000..aa7b3a2b0da86b1816f7a065843496728977ab2e --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/create_randomaccessfile_core.h @@ -0,0 +1,45 @@ +/* + * 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_PROPERTIES_CREATE_RANDOMACCESSFILE_CORE_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_CREATE_RANDOMACCESSFILE_CORE_H + +#include "filemgmt_libfs.h" +#include "fs_file.h" +#include "fs_randomaccessfile.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; + +struct RandomAccessFileOptions { + optional start = nullopt; + optional end = nullopt; +}; + +class CreateRandomAccessFileCore final { +public: + static FsResult DoCreateRandomAccessFile( + const string &path, const optional &mode = nullopt, + const optional &options = nullopt); + static FsResult DoCreateRandomAccessFile( + const int32_t &fd, const optional &options = nullopt); +}; +const string PROCEDURE_CREATERAT_NAME = "FileIOCreateRandomAccessFile"; +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_CREATE_RANDOMACCESSFILE_CORE_H \ No newline at end of file