diff --git a/interfaces/kits/js/src/common/ani_helper/ani_signature.cpp b/interfaces/kits/js/src/common/ani_helper/ani_signature.cpp index 3b4e5f12d68aed3884f55cb20cfc76ef2624453f..f46a4fdd45e9e34bfe79a44feca6b6b06e2952d1 100644 --- a/interfaces/kits/js/src/common/ani_helper/ani_signature.cpp +++ b/interfaces/kits/js/src/common/ani_helper/ani_signature.cpp @@ -154,5 +154,15 @@ const string Impl::StatvfsImpl::classDesc = Impl::StatvfsImpl::classType.Descrip const Type HASH::HashStreamImpl::classType = Builder::BuildClass("@ohos.file.hash.HashStreamImpl"); const string HASH::HashStreamImpl::classDesc = HASH::HashStreamImpl::classType.Descriptor(); const string HASH::HashStreamImpl::ctorSig = Builder::BuildSignatureDescriptor({ BuiltInTypes::stringType }); +// FS::ReadStreamOptionsInner +const Type FS::ReadStreamOptionsInner::classType = Builder::BuildClass("@ohos.file.fs.ReadStreamOptionsInner"); +const string FS::ReadStreamOptionsInner::classDesc = FS::ReadStreamOptionsInner::classType.Descriptor(); +const string FS::ReadStreamOptionsInner::ctorSig = Builder::BuildSignatureDescriptor({}); +const string FS::ReadStreamOptionsInner::ctorDesc = Builder::BuildConstructorName(); +// FS::WriteStreamOptionsInner +const Type FS::WriteStreamOptionsInner::classType = Builder::BuildClass("@ohos.file.fs.WriteStreamOptionsInner"); +const string FS::WriteStreamOptionsInner::classDesc = FS::ReadStreamOptionsInner::classType.Descriptor(); +const string FS::WriteStreamOptionsInner::ctorSig = Builder::BuildSignatureDescriptor({}); +const string FS::WriteStreamOptionsInner::ctorDesc = Builder::BuildConstructorName(); } // namespace OHOS::FileManagement::ModuleFileIO::ANI::AniSignature \ No newline at end of file diff --git a/interfaces/kits/js/src/common/ani_helper/ani_signature.h b/interfaces/kits/js/src/common/ani_helper/ani_signature.h index bb92179428d17fdfc3e1b69bc4aa6b2cd81a3b17..de58ba8cb391dd54eab87184554e70893709e36a 100644 --- a/interfaces/kits/js/src/common/ani_helper/ani_signature.h +++ b/interfaces/kits/js/src/common/ani_helper/ani_signature.h @@ -123,6 +123,20 @@ struct ConflictFilesInner : public BaseType { static const string ctorSig; }; +struct ReadStreamOptionsInner : public BaseType { + static const Type classType; + static const string classDesc; + static const string ctorDesc; + static const string ctorSig; +}; + +struct WriteStreamOptionsInner : public BaseType { + static const Type classType; + static const string classDesc; + static const string ctorDesc; + static const string ctorSig; +}; + struct FileInner : public BaseType { static const Type classType; static const string classDesc; diff --git a/interfaces/kits/js/src/mod_fs/class_randomaccessfile/ani/randomaccessfile_ani.cpp b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/ani/randomaccessfile_ani.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a220f84baa93943aaae7d349f7ff02c95de5fbc9 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/ani/randomaccessfile_ani.cpp @@ -0,0 +1,492 @@ +/* + * 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 "randomaccessfile_ani.h" + +#include "ani_helper.h" +#include "ani_signature.h" +#include "error_handler.h" +#include "filemgmt_libhilog.h" +#include "fs_randomaccessfile.h" +#include "type_converter.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { +using namespace std; +using namespace OHOS::FileManagement::ModuleFileIO; +using namespace OHOS::FileManagement::ModuleFileIO::ANI::AniSignature; + +const int BUF_SIZE = 1024; +const string READ_STREAM_CLASS = "ReadStream"; +const string WRITE_STREAM_CLASS = "WriteStream"; +const string OFFSET = "offset"; +const string LENGTH = "length"; + +static FsRandomAccessFile *Unwrap(ani_env *env, ani_object object) +{ + ani_long nativePtr; + auto ret = env->Object_GetFieldByName_Long(object, "nativePtr", &nativePtr); + if (ret != ANI_OK) { + HILOGE("Unwrap FsRandomAccessFile err: %{public}d", ret); + return nullptr; + } + uintptr_t ptrValue = static_cast(nativePtr); + FsRandomAccessFile *rafFile = reinterpret_cast(ptrValue); + return rafFile; +} + +static tuple> ToReadOptions(ani_env *env, ani_object obj) +{ + ReadOptions options; + ani_boolean isUndefined; + env->Reference_IsUndefined(obj, &isUndefined); + if (isUndefined) { + return { true, nullopt }; + } + + auto [succOffset, offset] = AniHelper::ParseInt64Option(env, obj, OFFSET); + if (!succOffset) { + HILOGE("Illegal option.offset parameter"); + return { false, nullopt }; + } + options.offset = offset; + + auto [succLength, length] = AniHelper::ParseInt64Option(env, obj, LENGTH); + if (!succLength) { + HILOGE("Illegal option.length parameter"); + return { false, nullopt }; + } + options.length = length; + return { true, make_optional(move(options)) }; +} + +static tuple> ToWriteOptions(ani_env *env, ani_object obj) +{ + WriteOptions options; + ani_boolean isUndefined; + env->Reference_IsUndefined(obj, &isUndefined); + if (isUndefined) { + return { true, nullopt }; + } + + auto [succOffset, offset] = AniHelper::ParseInt64Option(env, obj, OFFSET); + if (!succOffset) { + HILOGE("Illegal option.offset parameter"); + return { false, nullopt }; + } + options.offset = offset; + + auto [succLength, length] = AniHelper::ParseInt64Option(env, obj, LENGTH); + if (!succLength) { + HILOGE("Illegal option.length parameter"); + return { false, nullopt }; + } + options.length = length; + + auto [succEncoding, encoding] = AniHelper::ParseEncoding(env, obj); + if (!succEncoding) { + HILOGE("Illegal option.encoding parameter"); + return { false, nullopt }; + } + options.encoding = encoding; + return { true, make_optional(move(options)) }; +} + +static tuple ParseStringBuffer(ani_env *env, const ani_object &buf) +{ + ani_class cls; + auto classDesc = BuiltInTypes::String::classDesc.c_str(); + env->FindClass(classDesc, &cls); + + ani_boolean isString; + env->Object_InstanceOf(buf, cls, &isString); + if (!isString) { + return { false, {} }; + } + auto result = static_cast(buf); + return { true, move(result) }; +} + +static tuple ParseArrayBuffer(ani_env *env, const ani_object &buf) +{ + ani_class cls; + auto classDesc = BuiltInTypes::ArrayBuffer::classDesc.c_str(); + env->FindClass(classDesc, &cls); + + ani_boolean isArrayBuffer; + env->Object_InstanceOf(buf, cls, &isArrayBuffer); + if (!isArrayBuffer) { + return { false, {} }; + } + auto result = static_cast(buf); + return { true, move(result) }; +} + +void RandomAccessFileAni::SetFilePointer(ani_env *env, [[maybe_unused]] ani_object object, ani_double fp) +{ + auto rafFile = Unwrap(env, object); + if (rafFile == nullptr) { + HILOGE("Cannot unwrap rafFile!"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return; + } + auto ret = rafFile->SetFilePointerSync(static_cast(fp)); + if (!ret.IsSuccess()) { + HILOGE("SetFilePointerSync failed!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return; + } +} + +void RandomAccessFileAni::Close(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto rafFile = Unwrap(env, object); + if (rafFile == nullptr) { + HILOGE("Cannot unwrap rafFile!"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return; + } + auto ret = rafFile->CloseSync(); + if (!ret.IsSuccess()) { + HILOGE("close rafFile failed!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return; + } +} + +ani_double RandomAccessFileAni::WriteSync( + ani_env *env, [[maybe_unused]] ani_object object, ani_object buf, ani_object options) +{ + auto rafFile = Unwrap(env, object); + if (rafFile == nullptr) { + HILOGE("Cannot unwrap rafFile!"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return -1; + } + + auto [succOp, op] = ToWriteOptions(env, options); + if (!succOp) { + HILOGE("Failed to resolve options!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + + auto [isString, stringBuffer] = ParseStringBuffer(env, buf); + if (isString) { + auto [succBuf, buffer] = TypeConverter::ToUTF8String(env, stringBuffer); + if (!succBuf) { + HILOGE("Failed to resolve stringBuffer!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + auto ret = rafFile->WriteSync(buffer, op); + if (!ret.IsSuccess()) { + HILOGE("write buffer failed!"); + ErrorHandler::Throw(env, ret.GetError()); + return -1; + } + return static_cast(ret.GetData().value()); + } + + auto [isArrayBuffer, arrayBuffer] = ParseArrayBuffer(env, buf); + if (isArrayBuffer) { + auto [succBuf, buffer] = TypeConverter::ToArrayBuffer(env, arrayBuffer); + if (!succBuf) { + HILOGE("Failed to resolve arrayBuffer!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + auto ret = rafFile->WriteSync(buffer, op); + if (!ret.IsSuccess()) { + HILOGE("write buffer failed!"); + ErrorHandler::Throw(env, ret.GetError()); + return -1; + } + return static_cast(ret.GetData().value()); + } + HILOGE("Unsupported buffer type!"); + ErrorHandler::Throw(env, EINVAL); + return -1; +} + +ani_double RandomAccessFileAni::ReadSync( + ani_env *env, [[maybe_unused]] ani_object object, ani_arraybuffer buf, ani_object options) +{ + auto rafFile = Unwrap(env, object); + if (rafFile == nullptr) { + HILOGE("Cannot unwrap rafFile!"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return -1; + } + + auto [succBuf, arrayBuffer] = TypeConverter::ToArrayBuffer(env, buf); + if (!succBuf) { + HILOGE("Failed to resolve arrayBuffer!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + + auto [succOp, op] = ToReadOptions(env, options); + if (!succOp) { + HILOGE("Failed to resolve options!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + + auto ret = rafFile->ReadSync(arrayBuffer, op); + if (!ret.IsSuccess()) { + HILOGE("Read file content failed!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return -1; + } + return static_cast(ret.GetData().value()); +} + +static ani_string GetFilePath(ani_env *env, const int fd) +{ + auto dstFd = dup(fd); + if (dstFd < 0) { + HILOGE("Failed to get valid fd, fail reason: %{public}s, fd: %{public}d", strerror(errno), fd); + return nullptr; + } + + string path = "/proc/self/fd/" + to_string(dstFd); + auto buf = CreateUniquePtr(BUF_SIZE); + int readLinkRes = readlink(path.c_str(), buf.get(), BUF_SIZE); + if (readLinkRes < 0) { + close(dstFd); + return nullptr; + } + + close(dstFd); + auto [succ, filePath] = TypeConverter::ToAniString(env, string(buf.get())); + if (!succ) { + return nullptr; + } + return move(filePath); +} + +static ani_object CreateReadStreamOptions(ani_env *env, int64_t start, int64_t end) +{ + static const char *className = FS::ReadStreamOptionsInner::classType; + ani_class cls; + if (ANI_OK != env->FindClass(className, &cls)) { + HILOGE("Cannot find class %{public}s", className); + return nullptr; + } + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, FS::ReadStreamOptionsInner::ctorDesc, + FS::ReadStreamOptionsInner::ctorSig, &ctor)) { + HILOGE("Cannot find constructor method for class %{public}s", className); + return nullptr; + } + ani_object obj; + if (ANI_OK != env->Object_New(cls, ctor, &obj)) { + HILOGE("New %{public}s obj Failed", className); + return nullptr; + } + + ani_field startField = nullptr; + ani_field endField = nullptr; + if (ANI_OK != env->Class_FindField(cls, "start", &startField)) { + HILOGE("Cannot find start in class %{public}s", className); + return nullptr; + } + if (ANI_OK != env->Class_FindField(cls, "end", &endField)) { + HILOGE("Cannot find end in class %{public}s", className); + return nullptr; + } + + if (start >= 0) { + env->Object_SetField_Int(obj, startField, start); + } + if (end >= 0) { + env->Object_SetField_Int(obj, endField, end); + } + if (obj == nullptr) { + HILOGE("CreateReadStreamOptions is nullptr"); + } + + return move(obj); +} + +static ani_object CreateWriteStreamOptions(ani_env *env, int64_t start, int flags) +{ + static const char *className = FS::WriteStreamOptionsInner::classType; + ani_class cls; + if (ANI_OK != env->FindClass(className, &cls)) { + HILOGE("Cannot find class %{public}s", className); + return nullptr; + } + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, FS::WriteStreamOptionsInner::ctorDesc, + FS::WriteStreamOptionsInner::ctorSig, &ctor)) { + HILOGE("Cannot find constructor method for class %{public}s", className); + return nullptr; + } + ani_object obj; + if (ANI_OK != env->Object_New(cls, ctor, &obj)) { + HILOGE("New %{public}s obj Failed", className); + return nullptr; + } + + ani_field modeField = nullptr; + ani_field startField = nullptr; + if (ANI_OK != env->Class_FindField(cls, "mode", &modeField)) { + HILOGE("Cannot find mode in class %{public}s", className); + return nullptr; + } + if (ANI_OK != env->Class_FindField(cls, "start", &startField)) { + HILOGE("Cannot find start in class %{public}s", className); + return nullptr; + } + + env->Object_SetField_Int(obj, modeField, flags); + if (start >= 0) { + env->Object_SetField_Int(obj, startField, start); + } + + return move(obj); +} + +static ani_object CreateReadStream(ani_env *env, ani_string filePath, ani_object options) +{ + static const char *className = "L@ohos/file/fs/fileIo/ReadStream;"; + ani_class cls; + if (ANI_OK != env->FindClass(className, &cls)) { + HILOGE("Cannot find class %{public}s", className); + return nullptr; + } + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, "", "Lstd/core/String;L@ohos/file/fs/ReadStreamOptions;:V", &ctor)) { + HILOGE("Cannot find constructor method for class %{public}s", className); + return nullptr; + } + ani_object obj; + if (ANI_OK != env->Object_New(cls, ctor, &obj, filePath, options)) { + HILOGE("New %{public}s obj Failed", className); + return nullptr; + } + + return move(obj); +} + +static ani_object CreateWriteStream(ani_env *env, ani_string filePath, ani_object options) +{ + static const char *className = "L@ohos/file/fs/fileIo/WriteStream;"; + ani_class cls; + if (ANI_OK != env->FindClass(className, &cls)) { + HILOGE("Cannot find class %{public}s", className); + return nullptr; + } + ani_method ctor; + if (ANI_OK != + env->Class_FindMethod(cls, "", "Lstd/core/String;L@ohos/file/fs/WriteStreamOptions;:V", &ctor)) { + HILOGE("Cannot find constructor method for class %{public}s", className); + return nullptr; + } + ani_object obj; + if (ANI_OK != env->Object_New(cls, ctor, &obj, filePath, options)) { + HILOGE("New %{public}s obj Failed", className); + return nullptr; + } + + return move(obj); +} + +static ani_object CreateStream(ani_env *env, const string &streamName, RandomAccessFileEntity *rafEntity, int flags) +{ + ani_string filePath = GetFilePath(env, rafEntity->fd.get()->GetFD()); + if (!filePath) { + HILOGE("Get file path failed, errno=%{public}d", errno); + ErrorHandler::Throw(env, errno); + return nullptr; + } + + if (streamName == READ_STREAM_CLASS) { + ani_object obj = CreateReadStreamOptions(env, rafEntity->start, rafEntity->end); + return CreateReadStream(env, filePath, obj); + } + if (streamName == WRITE_STREAM_CLASS) { + ani_object obj = CreateWriteStreamOptions(env, rafEntity->start, flags); + return CreateWriteStream(env, filePath, obj); + } + + return nullptr; +} + +ani_object RandomAccessFileAni::GetReadStream(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto rafFile = Unwrap(env, object); + if (rafFile == nullptr) { + HILOGE("Cannot unwrap rafFile!"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + + auto entity = rafFile->GetRAFEntity(); + if (!entity) { + HILOGE("Get RandomAccessFileEntity failed!"); + ErrorHandler::Throw(env, EIO); + return nullptr; + } + + int flags = fcntl(entity->fd.get()->GetFD(), F_GETFL); + unsigned int uflags = static_cast(flags); + if (((uflags & O_ACCMODE) != O_RDONLY) && ((uflags & O_ACCMODE) != O_RDWR)) { + HILOGE("Failed to check Permission"); + ErrorHandler::Throw(env, EACCES); + return nullptr; + } + + return CreateStream(env, READ_STREAM_CLASS, entity, flags); +} + +ani_object RandomAccessFileAni::GetWriteStream(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto rafFile = Unwrap(env, object); + if (rafFile == nullptr) { + HILOGE("Cannot unwrap rafFile!"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + + auto entity = rafFile->GetRAFEntity(); + if (!entity) { + HILOGE("Get RandomAccessFileEntity failed!"); + ErrorHandler::Throw(env, EIO); + return nullptr; + } + + int flags = fcntl(entity->fd.get()->GetFD(), F_GETFL); + unsigned int uflags = static_cast(flags); + if (((uflags & O_ACCMODE) != O_WRONLY) && ((uflags & O_ACCMODE) != O_RDWR)) { + HILOGE("Failed to check Permission"); + ErrorHandler::Throw(env, EACCES); + return nullptr; + } + + return CreateStream(env, WRITE_STREAM_CLASS, entity, flags); +} + +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_stream/ani/stream_ani.cpp b/interfaces/kits/js/src/mod_fs/class_stream/ani/stream_ani.cpp new file mode 100644 index 0000000000000000000000000000000000000000..39c26d8b72dc6cb9f6681345d3263d614e095348 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_stream/ani/stream_ani.cpp @@ -0,0 +1,291 @@ +/* + * 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 "stream_ani.h" + +#include +#include + +#include "ani_helper.h" +#include "ani_signature.h" +#include "error_handler.h" +#include "filemgmt_libhilog.h" +#include "fs_stream.h" +#include "fs_utils.h" +#include "type_converter.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { +using namespace std; +using namespace OHOS::FileManagement::ModuleFileIO; +using namespace OHOS::FileManagement::ModuleFileIO::ANI::AniSignature; + +static tuple> ToReadOptions(ani_env *env, ani_object obj) +{ + ReadOptions options; + ani_boolean isUndefined; + env->Reference_IsUndefined(obj, &isUndefined); + if (isUndefined) { + return { true, nullopt }; + } + + auto [succOffset, offset] = AniHelper::ParseInt64Option(env, obj, "offset"); + if (!succOffset) { + HILOGE("Illegal option.offset parameter"); + return { false, nullopt }; + } + options.offset = offset; + + auto [succLength, length] = AniHelper::ParseInt64Option(env, obj, "length"); + if (!succLength) { + HILOGE("Illegal option.length parameter"); + return { false, nullopt }; + } + options.length = length; + return { true, make_optional(move(options)) }; +} + +static tuple> ToWriteOptions(ani_env *env, ani_object obj) +{ + WriteOptions options; + ani_boolean isUndefined; + env->Reference_IsUndefined(obj, &isUndefined); + if (isUndefined) { + return { true, nullopt }; + } + + auto [succOffset, offset] = AniHelper::ParseInt64Option(env, obj, "offset"); + if (!succOffset) { + HILOGE("Illegal option.offset parameter"); + return { false, nullopt }; + } + options.offset = offset; + + auto [succLength, length] = AniHelper::ParseInt64Option(env, obj, "length"); + if (!succLength) { + HILOGE("Illegal option.length parameter"); + return { false, nullopt }; + } + options.length = length; + + auto [succEncoding, encoding] = AniHelper::ParseEncoding(env, obj); + if (!succEncoding) { + HILOGE("Illegal option.encoding parameter"); + return { false, nullopt }; + } + options.encoding = encoding; + return { true, make_optional(move(options)) }; +} + +static std::tuple ParseStringBuffer(ani_env *env, const ani_object &buf) +{ + auto classDesc = BuiltInTypes::String::classDesc.c_str(); + ani_class cls; + env->FindClass(classDesc, &cls); + + ani_boolean isString; + env->Object_InstanceOf(buf, cls, &isString); + if (!isString) { + HILOGE("Object_InstanceOf is failed"); + return { false, {} }; + } + auto result = static_cast(buf); + return { true, std::move(result) }; +} + +static std::tuple ParseArrayBuffer(ani_env *env, const ani_object &buf) +{ + auto classDesc = BuiltInTypes::ArrayBuffer::classDesc.c_str(); + ani_class cls; + env->FindClass(classDesc, &cls); + + ani_boolean isArrayBuffer; + env->Object_InstanceOf(buf, cls, &isArrayBuffer); + if (!isArrayBuffer) { + return { false, {} }; + } + auto result = static_cast(buf); + return { true, std::move(result) }; +} + +static FsStream *Unwrap(ani_env *env, ani_object object) +{ + ani_long nativePtr; + auto ret = env->Object_GetFieldByName_Long(object, "nativePtr", &nativePtr); + if (ret != ANI_OK) { + HILOGE("Unwrap fsFile err: %{private}d", ret); + return nullptr; + } + uintptr_t ptrValue = static_cast(nativePtr); + FsStream *stream = reinterpret_cast(ptrValue); + return stream; +} + +void StreamAni::Close(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto fsStream = Unwrap(env, object); + if (fsStream == nullptr) { + HILOGE("Cannot unwrap fsStream!"); + ErrorHandler::Throw(env, EINVAL); + return; + } + + auto ret = fsStream->Close(); + if (!ret.IsSuccess()) { + HILOGE("Cannot close fsStream!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return; + } +} + +void StreamAni::Flush(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto fsStream = Unwrap(env, object); + if (fsStream == nullptr) { + HILOGE("Cannot unwrap fsStream!"); + ErrorHandler::Throw(env, EINVAL); + return; + } + + auto ret = fsStream->Flush(); + if (!ret.IsSuccess()) { + HILOGE("Cannot flush fsStream!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return; + } +} + +ani_double StreamAni::Write(ani_env *env, [[maybe_unused]] ani_object object, ani_object buf, ani_object options) +{ + auto fsStream = Unwrap(env, object); + if (fsStream == nullptr) { + HILOGE("Cannot unwrap fsStream!"); + return -1; + } + + auto [succOp, op] = ToWriteOptions(env, options); + if (!succOp) { + HILOGE("Failed to resolve options!"); + return -1; + } + + auto [isString, stringBuffer] = ParseStringBuffer(env, buf); + if (isString) { + auto [succBuf, buffer] = TypeConverter::ToUTF8String(env, stringBuffer); + if (!succBuf) { + HILOGE("Failed to resolve stringBuffer!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + auto ret = fsStream->Write(buffer, op); + if (!ret.IsSuccess()) { + HILOGE("write string failed!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return -1; + } + return ret.GetData().value(); + } + + auto [isArrayBuffer, arrayBuffer] = ParseArrayBuffer(env, buf); + if (isArrayBuffer) { + auto [succBuf, buffer] = TypeConverter::ToArrayBuffer(env, arrayBuffer); + if (!succBuf) { + HILOGE("Failed to resolve arrayBuffer!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + auto ret = fsStream->Write(buffer, op); + if (!ret.IsSuccess()) { + HILOGE("write buffer failed!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return -1; + } + return static_cast(ret.GetData().value()); + } + + HILOGE("Unsupported buffer type!"); + ErrorHandler::Throw(env, EINVAL); + return -1; +} + +ani_double StreamAni::Read(ani_env *env, [[maybe_unused]] ani_object object, ani_arraybuffer buffer, ani_object options) +{ + auto fsStream = Unwrap(env, object); + if (fsStream == nullptr) { + HILOGE("Cannot unwrap fsStream!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + + auto [succBuf, arrayBuffer] = TypeConverter::ToArrayBuffer(env, buffer); + if (!succBuf) { + HILOGE("Failed to resolve arrayBuffer!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + + auto [succOp, op] = ToReadOptions(env, options); + if (!succOp) { + HILOGE("Failed to resolve options!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + + auto ret = fsStream->Read(arrayBuffer, op); + if (!ret.IsSuccess()) { + HILOGE("write buffer failed!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return -1; + } + return static_cast(ret.GetData().value()); +} + +ani_double StreamAni::Seek(ani_env *env, [[maybe_unused]] ani_object object, ani_double offset, ani_object whence) +{ + auto fsStream = Unwrap(env, object); + if (fsStream == nullptr) { + HILOGE("Cannot unwrap fsStream!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + + auto [succ, typeOpt] = TypeConverter::ToOptionalInt32(env, whence); + if (!succ) { + HILOGE("Invalied whence type"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + + auto ret = fsStream->Seek(static_cast(offset), typeOpt); + if (!ret.IsSuccess()) { + HILOGE("seek failed!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return -1; + } + return static_cast(ret.GetData().value()); +} + +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_stream/ani/stream_ani.h b/interfaces/kits/js/src/mod_fs/class_stream/ani/stream_ani.h new file mode 100644 index 0000000000000000000000000000000000000000..91fdb9852a432c0dbc754b8763ab53d6c5046802 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_stream/ani/stream_ani.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INTERFACES_KITS_JS_SRC_MOD_FS_STREAM_ANI_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_STREAM_ANI_H + +#include + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { + +class StreamAni final { +public: + static void Close(ani_env *env, [[maybe_unused]] ani_object object); + static void Flush(ani_env *env, [[maybe_unused]] ani_object object); + static ani_double Write(ani_env *env, [[maybe_unused]] ani_object object, ani_object buf, ani_object options); + static ani_double Read( + ani_env *env, [[maybe_unused]] ani_object object, ani_arraybuffer buffer, ani_object options); + static ani_double Seek(ani_env *env, [[maybe_unused]] ani_object object, ani_double offset, ani_object whence); +}; +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS + +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_STREAM_ANI_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_stream/ani/stream_wrapper.cpp b/interfaces/kits/js/src/mod_fs/class_stream/ani/stream_wrapper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c882f9dda1125661faf91bfa8ff19036b57404c2 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_stream/ani/stream_wrapper.cpp @@ -0,0 +1,78 @@ +/* + * 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 "stream_wrapper.h" + +#include + +#include "ani_helper.h" +#include "ani_signature.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; +using namespace OHOS::FileManagement::ModuleFileIO::ANI::AniSignature; + +FsStream *StreamWrapper::Unwrap(ani_env *env, ani_object object) +{ + ani_long nativePtr; + auto ret = env->Object_GetFieldByName_Long(object, "nativePtr", &nativePtr); + if (ret != ANI_OK) { + HILOGE("Unwrap fsFile err: %{private}d", ret); + return nullptr; + } + uintptr_t ptrValue = static_cast(nativePtr); + FsStream *stream = reinterpret_cast(ptrValue); + return stream; +} + +ani_object StreamWrapper::Wrap(ani_env *env, const FsStream *stream) +{ + if (stream == nullptr) { + HILOGE("FsStream pointer is null!"); + return nullptr; + } + auto classDesc = FS::StreamInner::classDesc.c_str(); + ani_class cls; + if (ANI_OK != env->FindClass(classDesc, &cls)) { + HILOGE("Cannot find class %s", classDesc); + return nullptr; + } + auto ctorDesc = FS::StreamInner::ctorDesc.c_str(); + auto ctorSig = FS::StreamInner::ctorSig.c_str(); + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, ctorDesc, ctorSig, &ctor)) { + HILOGE("Cannot find constructor method for class %s", classDesc); + return nullptr; + } + ani_long ptr = static_cast(reinterpret_cast(stream)); + ani_object obj; + if (ANI_OK != env->Object_New(cls, ctor, &obj, ptr)) { + HILOGE("New %s obj Failed!", classDesc); + return nullptr; + } + + return obj; +} + +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_stream/ani/stream_wrapper.h b/interfaces/kits/js/src/mod_fs/class_stream/ani/stream_wrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..a99f6d4ad2c85500c8193730d661693d83a2044c --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_stream/ani/stream_wrapper.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_STREAM_WRAPPER_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_STREAM_WRAPPER_H + +#include +#include "fs_stream.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { + +class StreamWrapper final { +public: + static FsStream *Unwrap(ani_env *env, ani_object object); + static ani_object Wrap(ani_env *env, const FsStream *stream); +}; +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS + +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_STREAM_WRAPPER_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_stream/fs_stream.cpp b/interfaces/kits/js/src/mod_fs/class_stream/fs_stream.cpp new file mode 100644 index 0000000000000000000000000000000000000000..13dcc47a4b495ca22519dfc38df21c1e7a40fe81 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_stream/fs_stream.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fs_stream.h" + +#include +#include + +#include "file_utils.h" +#include "filemgmt_libhilog.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; +const string UTF_8 = "utf-8"; + +std::shared_ptr FsStream::GetFilePtr() +{ + std::lock_guard lock(mtx); + if (!streamEntity) { + return nullptr; + } + return streamEntity->fp; +} + +static tuple ValidWriteArg(const size_t bufLen, const optional &options) +{ + size_t retLen = 0; + int64_t offset = -1; + bool succ = false; + + if (bufLen > UINT_MAX) { + HILOGE("The Size of buffer is too large"); + return { false, 0, offset }; + } + + optional lengthOp = nullopt; + optional offsetOp = nullopt; + optional encodingOp = nullopt; + if (options.has_value()) { + WriteOptions op = options.value(); + lengthOp = op.length; + offsetOp = op.offset; + encodingOp = op.encoding; + } + + tie(succ, retLen) = FsUtils::GetActualLen(bufLen, 0, lengthOp); + if (!succ) { + HILOGE("Failed to get actual length"); + return { false, 0, offset }; + } + + if (offsetOp.has_value()) { + offset = offsetOp.value(); + if (offset < 0) { + HILOGE("option.offset shall be positive number"); + return { false, 0, offset }; + } + } + + if (encodingOp.has_value()) { + if (encodingOp.value() != UTF_8) { + HILOGE("option.encoding shall be utf-8"); + return { false, 0, offset }; + } + } + return { true, retLen, offset }; +} + +static tuple ValidReadArg(const size_t bufLen, const optional &options) +{ + size_t retLen = 0; + int64_t offset = -1; + bool succ = false; + + if (bufLen > UINT_MAX) { + HILOGE("The Size of buffer is too large"); + return { false, 0, offset }; + } + + optional lengthOp = nullopt; + optional offsetOp = nullopt; + if (options.has_value()) { + ReadOptions op = options.value(); + lengthOp = op.length; + offsetOp = op.offset; + } + + tie(succ, retLen) = FsUtils::GetActualLen(bufLen, 0, lengthOp); + if (!succ) { + HILOGE("Failed to get actual length"); + return { false, 0, offset }; + } + + if (offsetOp.has_value()) { + offset = offsetOp.value(); + if (offset < 0) { + HILOGE("option.offset shall be positive number"); + return { false, 0, offset }; + } + } + return { true, retLen, offset }; +} + +FsResult FsStream::Write(const ArrayBuffer &buf, const optional &options) +{ + auto fp = GetFilePtr(); + if (!fp) { + HILOGE("Failed to get file ptr"); + return FsResult::Error(EIO); + } + + auto [succ, retLen, offset] = ValidWriteArg(buf.length, options); + if (!succ) { + HILOGE("Invalid options"); + return FsResult::Error(EINVAL); + } + + if (offset >= 0) { + int ret = fseek(fp.get(), static_cast(offset), SEEK_SET); + if (ret < 0) { + HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret); + return FsResult::Error(errno); + } + } + + size_t writeLen = fwrite(buf.buf, 1, retLen, fp.get()); + if ((writeLen == 0) && (writeLen != retLen)) { + HILOGE("Failed to fwrite stream"); + return FsResult::Error(EIO); + } + return FsResult::Success(writeLen); +} + +FsResult FsStream::Write(const string &buf, const optional &options) +{ + auto fp = GetFilePtr(); + if (!fp) { + HILOGE("Failed to get file ptr"); + return FsResult::Error(EIO); + } + + size_t bufLen = static_cast(buf.length()); + + auto [succ, retLen, offset] = ValidWriteArg(bufLen, options); + if (!succ) { + HILOGE("Invalid options"); + return FsResult::Error(EINVAL); + } + + if (offset >= 0) { + int ret = fseek(fp.get(), static_cast(offset), SEEK_SET); + if (ret < 0) { + HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret); + return FsResult::Error(errno); + } + } + + size_t writeLen = fwrite(buf.c_str(), 1, retLen, fp.get()); + if ((writeLen == 0) && (writeLen != retLen)) { + HILOGE("Failed to fwrite stream"); + return FsResult::Error(EIO); + } + return FsResult::Success(writeLen); +} + +FsResult FsStream::Read(ArrayBuffer &buf, const optional &options) +{ + auto fp = GetFilePtr(); + if (!fp) { + HILOGE("Failed to get file ptr"); + return FsResult::Error(EIO); + } + + auto [succ, retLen, offset] = ValidReadArg(buf.length, options); + if (!succ) { + HILOGE("Invalid options"); + return FsResult::Error(EINVAL); + } + + if (offset >= 0) { + int ret = fseek(fp.get(), static_cast(offset), SEEK_SET); + if (ret < 0) { + HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret); + return FsResult::Error(errno); + } + } + + size_t actLen = fread(buf.buf, 1, retLen, fp.get()); + if ((actLen != static_cast(retLen) && !feof(fp.get())) || ferror(fp.get())) { + HILOGE("Invalid buffer size or pointer, actlen: %{public}zu", actLen); + return FsResult::Error(EIO); + } + + return FsResult::Success(actLen); +} + +FsResult FsStream::Flush() +{ + auto fp = GetFilePtr(); + if (fp == nullptr) { + HILOGE("Failed to get entity of Stream"); + return FsResult::Error(EIO); + } + + int ret = fflush(fp.get()); + if (ret < 0) { + HILOGE("Failed to fflush file in the stream, ret: %{public}d", ret); + return FsResult::Error(errno); + } + + return FsResult::Success(); +} + +FsResult FsStream::Close() +{ + if (!streamEntity) { + HILOGE("Failed to get entity of Stream, may closed twice"); + return FsResult::Error(EIO); + } + streamEntity = nullptr; + return FsResult::Success(); +} + +FsResult FsStream::Seek(const int64_t &offset, const optional &typeOpt) +{ + int whence = SEEK_SET; + + auto fp = GetFilePtr(); + if (fp == nullptr) { + HILOGE("Failed to get file ptr"); + return FsResult::Error(ENOENT); + } + + if (typeOpt.has_value()) { + int pos = typeOpt.value(); + if (pos < SEEK_SET || pos > SEEK_END) { + HILOGE("Invalid whence"); + return FsResult::Error(EINVAL); + } + whence = pos; + } + + if (offset >= 0) { + int ret = fseek(fp.get(), static_cast(offset), whence); + if (ret < 0) { + HILOGE("Failed to set the offset location of the file stream pointer, ret: %{public}d", ret); + return FsResult::Error(errno); + } + } + + int64_t res = ftell(fp.get()); + if (res < 0) { + HILOGE("Failed to tell, error:%{public}d", errno); + return FsResult::Error(errno); + } + + return FsResult::Success(res); +} + +FsResult FsStream::Constructor() +{ + auto rafEntity = CreateUniquePtr(); + if (rafEntity == nullptr) { + HILOGE("Failed to request heap memory."); + return FsResult::Error(ENOMEM); + } + FsStream *fsStreamPtr = new FsStream(move(rafEntity)); + + if (fsStreamPtr == nullptr) { + HILOGE("Failed to create FsStream object on heap."); + return FsResult::Error(ENOMEM); + } + + return FsResult::Success(move(fsStreamPtr)); +} + +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS diff --git a/interfaces/kits/js/src/mod_fs/class_stream/fs_stream.h b/interfaces/kits/js/src/mod_fs/class_stream/fs_stream.h new file mode 100644 index 0000000000000000000000000000000000000000..a6b9661e006e246e3396e9698695b2f74c8733c9 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_stream/fs_stream.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_STREAM_FS_STREAM_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_STREAM_FS_STREAM_H + +#include "stream_entity.h" + +#include +#include +#include + +#include "filemgmt_libfs.h" +#include "fs_utils.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; + +struct WriteOptions { + optional length = nullopt; + optional offset = nullopt; + optional encoding = nullopt; +}; + +struct ReadOptions { + optional length = nullopt; + optional offset = nullopt; +}; + +class FsStream final { +public: + StreamEntity *GetStreamEntity() const + { + return streamEntity.get(); + } + + FsStream(const FsStream &) = delete; + FsStream &operator=(const FsStream &) = delete; + + std::shared_ptr GetFilePtr(); + FsResult Write(const ArrayBuffer &buf, const optional &options = nullopt); + FsResult Write(const string &buf, const optional &options = nullopt); + FsResult Read(ArrayBuffer &buf, const optional &options = nullopt); + FsResult Close(); + FsResult Flush(); + FsResult Seek(const int64_t &offset, const optional &typeOpt = nullopt); + + ~FsStream() = default; + static FsResult Constructor(); + +private: + std::mutex mtx; + unique_ptr streamEntity; + explicit FsStream(unique_ptr entity) : streamEntity(move(entity)) {} +}; + +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_STREAM_FS_STREAM_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_stream/stream_instantiator.cpp b/interfaces/kits/js/src/mod_fs/class_stream/stream_instantiator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e28dfa77ce4a984cdf93ce3a48b12c84e9882978 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_stream/stream_instantiator.cpp @@ -0,0 +1,79 @@ +/* + * 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 "stream_instantiator.h" + +#include "file_utils.h" +#include "filemgmt_libhilog.h" +#include "stream_entity.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; + +FsResult StreamInstantiator::InstantiateStream(FILE *file) +{ + FsResult result = FsStream::Constructor(); + if (!result.IsSuccess()) { + HILOGE("Failed to instantiate class"); + int ret = fclose(file); + if (ret < 0) { + HILOGE("Failed to close file"); + } + return FsResult::Error(EIO); + } + + const FsStream *objStream = result.GetData().value(); + if (!objStream) { + HILOGE("Failed to get FsStream"); + int ret = fclose(file); + if (ret < 0) { + HILOGE("Failed to close file"); + } + return FsResult::Error(EIO); + } + + auto *streamEntity = objStream->GetStreamEntity(); + if (!streamEntity) { + HILOGE("Failed to get streamEntity"); + int ret = fclose(file); + if (ret < 0) { + HILOGE("Failed to close file"); + } + delete objStream; + objStream = nullptr; + return FsResult::Error(EIO); + } + + auto fp = std::shared_ptr(file, fclose); + if (fp == nullptr) { + HILOGE("Failed to request heap memory."); + int ret = fclose(file); + if (ret < 0) { + HILOGE("Failed to close file"); + } + delete objStream; + objStream = nullptr; + return FsResult::Error(ENOMEM); + } + + streamEntity->fp.swap(fp); + return result; +} + +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_stream/stream_instantiator.h b/interfaces/kits/js/src/mod_fs/class_stream/stream_instantiator.h new file mode 100644 index 0000000000000000000000000000000000000000..bf98d7b5305804dc460e210491fcd66ab79ffe96 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_stream/stream_instantiator.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_STREAM_INSTANTIATOR_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_STREAM_INSTANTIATOR_H + +#include "filemgmt_libfs.h" +#include "fs_stream.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; + +class StreamInstantiator { +public: + static FsResult InstantiateStream(FILE *file); +}; + +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_STREAM_INSTANTIATOR_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/ani/create_stream_ani.cpp b/interfaces/kits/js/src/mod_fs/properties/ani/create_stream_ani.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc0d767286d762946a0060479d7741f55cd9ece4 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/ani/create_stream_ani.cpp @@ -0,0 +1,72 @@ +/* + * 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_stream_ani.h" + +#include "create_stream_core.h" +#include "error_handler.h" +#include "filemgmt_libhilog.h" +#include "stream_wrapper.h" +#include "type_converter.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { + +using namespace std; +using namespace OHOS::FileManagement::ModuleFileIO; + +ani_object CreateStreamAni::CreateStreamSync( + ani_env *env, [[maybe_unused]] ani_class clazz, ani_string path, ani_string mode) +{ + auto [succPath, srcPath] = TypeConverter::ToUTF8String(env, path); + if (!succPath) { + HILOGE("Invalid path"); + ErrorHandler::Throw(env, EINVAL); + return nullptr; + } + + auto [succMode, openMode] = TypeConverter::ToUTF8String(env, mode); + if (!succMode) { + HILOGE("Invalid mode"); + ErrorHandler::Throw(env, EINVAL); + return nullptr; + } + + FsResult ret = CreateStreamCore::DoCreateStream(srcPath, openMode); + if (!ret.IsSuccess()) { + HILOGE("create stream failed"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return nullptr; + } + + const FsStream *stream = ret.GetData().value(); + auto result = StreamWrapper::Wrap(env, move(stream)); + if (result == nullptr) { + delete stream; + stream = nullptr; + HILOGE("Wrap failed"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + return result; +} + +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/ani/create_stream_ani.h b/interfaces/kits/js/src/mod_fs/properties/ani/create_stream_ani.h new file mode 100644 index 0000000000000000000000000000000000000000..2052b6b4d07e5705439f08de2b3c401bf3febfdb --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/ani/create_stream_ani.h @@ -0,0 +1,34 @@ +/* + * 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_STREAM_ANI_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_CREATE_STREAM_ANI_H + +#include + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { +class CreateStreamAni final { +public: + static ani_object CreateStreamSync( + ani_env *env, [[maybe_unused]] ani_class clazz, ani_string path, ani_string mode); +}; +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_CREATE_STREAM_ANI_H \ No newline at end of file