* 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 "read_text.h"
#include <cinttypes>
#include <fcntl.h>
#include <sys/stat.h>
#include <tuple>
#include <unistd.h>
#include "common_func.h"
#include "file_utils.h"
#include "filemgmt_libhilog.h"
namespace OHOS {
namespace FileManagement {
namespace ModuleFileIO {
using namespace std;
using namespace OHOS::FileManagement::LibN;
static tuple<bool, int64_t, bool, int64_t, unique_ptr<char[]>> GetReadTextArg(napi_env env, napi_value argOption)
{
NVal op(env, argOption);
int64_t offset = -1;
int64_t len = 0;
bool succ = false;
bool hasLen = false;
unique_ptr<char[]> encoding { new char[]{ "utf-8" } };
if (op.HasProp("offset") && !op.GetProp("offset").TypeIs(napi_undefined)) {
tie(succ, offset) = op.GetProp("offset").ToInt64();
if (!succ || offset < 0) {
HILOGE("Illegal option.offset parameter");
return { false, offset, hasLen, len, nullptr };
}
}
if (op.HasProp("length") && !op.GetProp("length").TypeIs(napi_undefined)) {
tie(succ, len) = op.GetProp("length").ToInt64();
if (!succ || len < 0 || len > UINT_MAX) {
HILOGE("Illegal option.length parameter");
return { false, offset, hasLen, len, nullptr };
}
hasLen = true;
}
if (op.HasProp("encoding")) {
tie(succ, encoding, ignore) = op.GetProp("encoding").ToUTF8String("utf-8");
string_view encodingStr(encoding.get());
if (!succ || encodingStr != "utf-8") {
HILOGE("Illegal option.encoding parameter");
return { false, offset, hasLen, len, nullptr };
}
}
return { true, offset, hasLen, len, move(encoding) };
}
static NError ReadTextAsync(const std::string &path, std::shared_ptr<AsyncReadTextArg> arg, int64_t offset,
bool hasLen, int64_t len)
{
OHOS::DistributedFS::FDGuard sfd;
struct stat statbf;
std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
new uv_fs_t, CommonFunc::fs_req_cleanup };
if (!open_req) {
HILOGE("Failed to request heap memory.");
return NError(ENOMEM);
}
int ret = uv_fs_open(nullptr, open_req.get(), path.c_str(), O_RDONLY,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, nullptr);
int err = errno;
if (ret < 0) {
HILOGE("Failed to open file, ret: %{public}d", ret);
return NError(err);
}
sfd.SetFD(ret);
if (sfd.GetFD() < 0) {
HILOGE("Failed to open file by path");
return NError(errno);
}
if (fstat(sfd.GetFD(), &statbf) < 0) {
HILOGE("Failed to get stat of file by fd: %{public}d", sfd.GetFD());
return NError(errno);
}
if (offset > statbf.st_size) {
HILOGE("Invalid offset");
return NError(EINVAL);
}
len = (!hasLen || len > statbf.st_size) ? statbf.st_size : len;
string buffer(len, '\0');
uv_buf_t readbuf = uv_buf_init(const_cast<char *>(buffer.c_str()), static_cast<unsigned int>(len));
std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> read_req = {
new uv_fs_t, CommonFunc::fs_req_cleanup };
if (!read_req) {
HILOGE("Failed to request heap memory.");
return NError(ENOMEM);
}
arg->len = uv_fs_read(nullptr, read_req.get(), sfd.GetFD(), &readbuf, 1, offset, nullptr);
err = errno;
if (arg->len < 0) {
HILOGE("Failed to read file by fd: %{public}d", sfd.GetFD());
return NError(err);
}
arg->buffer = buffer;
return NError(ERRNO_NOERR);
}
static int OpenFile(const std::string& path)
{
std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
new uv_fs_t, CommonFunc::fs_req_cleanup
};
if (open_req == nullptr) {
HILOGE("Failed to request heap memory.");
return -ENOMEM;
}
return uv_fs_open(nullptr, open_req.get(), path.c_str(), O_RDONLY,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, nullptr);
}
static int ReadFromFile(int fd, int64_t offset, string& buffer)
{
uv_buf_t readbuf = uv_buf_init(const_cast<char *>(buffer.c_str()), static_cast<unsigned int>(buffer.size()));
std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> read_req = {
new uv_fs_t, CommonFunc::fs_req_cleanup };
if (read_req == nullptr) {
HILOGE("Failed to request heap memory.");
return -ENOMEM;
}
return uv_fs_read(nullptr, read_req.get(), fd, &readbuf, 1, offset, nullptr);
}
napi_value ReadText::Sync(napi_env env, napi_callback_info info)
{
NFuncArg funcArg(env, info);
if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
HILOGE("Number of arguments unmatched");
NError(EINVAL).ThrowErr(env);
return nullptr;
}
auto [resGetFirstArg, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
if (!resGetFirstArg) {
HILOGE("Invalid path");
NError(EINVAL).ThrowErr(env);
return nullptr;
}
auto [resGetReadTextArg, offset, hasLen, len, encoding] = GetReadTextArg(env, funcArg[NARG_POS::SECOND]);
if (!resGetReadTextArg) {
NError(EINVAL).ThrowErr(env);
return nullptr;
}
OHOS::DistributedFS::FDGuard sfd;
int fd = OpenFile(path.get());
if (fd < 0) {
HILOGD("Failed to open file by ret: %{public}d", fd);
NError(fd).ThrowErr(env);
return nullptr;
}
sfd.SetFD(fd);
struct stat statbf;
if ((!sfd) || (fstat(sfd.GetFD(), &statbf) < 0)) {
HILOGE("Failed to get stat of file by fd: %{public}d", sfd.GetFD());
NError(errno).ThrowErr(env);
return nullptr;
}
if (offset > statbf.st_size) {
HILOGE("Invalid offset: %{public}" PRIu64, offset);
NError(EINVAL).ThrowErr(env);
return nullptr;
}
len = (!hasLen || len > statbf.st_size) ? statbf.st_size : len;
string buffer(len, '\0');
int readRet = ReadFromFile(sfd.GetFD(), offset, buffer);
if (readRet < 0) {
HILOGE("Failed to read file by fd: %{public}d", fd);
NError(readRet).ThrowErr(env);
return nullptr;
}
return NVal::CreateUTF8String(env, buffer.c_str(), readRet).val_;
}
napi_value ReadText::Async(napi_env env, napi_callback_info info)
{
NFuncArg funcArg(env, info);
if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
HILOGE("Number of arguments unmatched");
NError(EINVAL).ThrowErr(env);
return nullptr;
}
auto [resGetFirstArg, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
if (!resGetFirstArg) {
HILOGE("Invalid path");
NError(EINVAL).ThrowErr(env);
return nullptr;
}
auto [resGetSecondArg, offset, hasLen, len, encoding] = GetReadTextArg(env, funcArg[NARG_POS::SECOND]);
if (!resGetSecondArg) {
NError(EINVAL).ThrowErr(env);
return nullptr;
}
auto arg = CreateSharedPtr<AsyncReadTextArg>(NVal(env, funcArg.GetThisVar()));
if (arg == nullptr) {
HILOGE("Failed to request heap memory.");
NError(ENOMEM).ThrowErr(env);
return nullptr;
}
auto cbExec = [path = string(path.get()), arg, offset = offset, hasLen = hasLen, len = len]() -> NError {
return ReadTextAsync(path, arg, offset, hasLen, len);
};
auto cbComplete = [arg](napi_env env, NError err) -> NVal {
if (err) {
return { env, err.GetNapiErr(env) };
} else {
return NVal::CreateUTF8String(env, arg->buffer.c_str(), arg->len);
}
};
NVal thisVar(env, funcArg.GetThisVar());
if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
!NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_READTEXT_NAME, cbExec, cbComplete).val_;
} else {
NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD)]);
return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_READTEXT_NAME, cbExec, cbComplete).val_;
}
}
}
}
}