diff --git a/interfaces/kits/js/BUILD.gn b/interfaces/kits/js/BUILD.gn index 4d3022ceae366f92b247cbfba62e0fc6df4bdb42..15308cf2602f8305effaaca50930596a320f4235 100644 --- a/interfaces/kits/js/BUILD.gn +++ b/interfaces/kits/js/BUILD.gn @@ -127,8 +127,10 @@ ohos_shared_library("fs") { "src/mod_fs/properties/fdatasync.cpp", "src/mod_fs/properties/fdopen_stream.cpp", "src/mod_fs/properties/fsync.cpp", + "src/mod_fs/properties/listfile.cpp", "src/mod_fs/properties/lstat.cpp", "src/mod_fs/properties/mkdtemp.cpp", + "src/mod_fs/properties/move.cpp", "src/mod_fs/properties/open.cpp", "src/mod_fs/properties/prop_n_exporter.cpp", "src/mod_fs/properties/read_text.cpp", diff --git a/interfaces/kits/js/src/mod_fs/class_file/file_entity.h b/interfaces/kits/js/src/mod_fs/class_file/file_entity.h index 6bedf0d00a7e0ac82e9ca7cf65196a9adb721dae..7af17996a75db6026791d07c5a10d883909f17ee 100644 --- a/interfaces/kits/js/src/mod_fs/class_file/file_entity.h +++ b/interfaces/kits/js/src/mod_fs/class_file/file_entity.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-2023 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 @@ -16,10 +16,14 @@ #ifndef INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_FILE_FILE_ENTITY_H #define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_FILE_FILE_ENTITY_H +#include #include +#include +#include #include #include "fd_guard.h" +#include "filemgmt_libhilog.h" namespace OHOS { namespace FileManagement { @@ -28,6 +32,23 @@ struct FileEntity { std::unique_ptr fd_ = { nullptr }; std::string path_; std::string uri_; + + virtual ~FileEntity() + { + if (!fd_.get()) { + return; + } + int32_t fd = fd_.get()->GetFD(); + int ret = flock(fd, LOCK_UN); + if (ret == 0) { + struct stat buf; + if (fstat(fd, &buf) == 0) { + HILOGI("Unlock succeeded inode = %{public}" PRIu64, buf.st_ino); + } else { + HILOGI("Failed to get inode number"); + } + } + } }; } // namespace ModuleFileIO } // namespace FileManagement diff --git a/interfaces/kits/js/src/mod_fs/class_file/file_n_exporter.cpp b/interfaces/kits/js/src/mod_fs/class_file/file_n_exporter.cpp index 6d7ad806dee9540f40ef6d5a84a92cb57be920f3..69018444db598c248717e31e2f20748404638ae2 100644 --- a/interfaces/kits/js/src/mod_fs/class_file/file_n_exporter.cpp +++ b/interfaces/kits/js/src/mod_fs/class_file/file_n_exporter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-2023 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 @@ -20,6 +20,7 @@ #include #include #include +#include #include "filemgmt_libhilog.h" #include "filemgmt_libn.h" @@ -63,6 +64,133 @@ napi_value FileNExporter::GetFD(napi_env env, napi_callback_info info) return NVal::CreateInt32(env, rafEntity->fd_.get()->GetFD()).val_; } +static bool GetExclusive(napi_env env, NFuncArg &funcArg, bool &exclusive) +{ + bool succ = true; + if (funcArg.GetArgc() >= NARG_CNT::ONE && !(NVal(env, funcArg[NARG_POS::FIRST]).TypeIs(napi_function))) { + if (NVal(env, funcArg[NARG_POS::FIRST]).TypeIs(napi_boolean)) { + tie(succ, exclusive) = NVal(env, funcArg[NARG_POS::FIRST]).ToBool(); + if (!succ) { + HILOGE("Invalid exclusive"); + NError(EINVAL).ThrowErr(env); + } + } else { + HILOGE("Invalid exclusive"); + NError(EINVAL).ThrowErr(env); + succ = false; + } + } + return succ; +} + +napi_value FileNExporter::Lock(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::TWO)) { + HILOGE("Number of arguments unmatched"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + auto fileEntity = GetFileEntity(env, funcArg.GetThisVar()); + if (!fileEntity) { + HILOGE("Failed to get file entity"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + bool exclusive = false; + if (!GetExclusive(env, funcArg, exclusive)) { + return nullptr; + } + auto cbExec = [exclusive, fileEntity]() -> NError { + int ret = 0; + auto mode = exclusive ? LOCK_EX : LOCK_SH; + ret = flock(fileEntity->fd_.get()->GetFD(), mode); + if (ret < 0) { + HILOGE("Failed to lock file"); + return NError(errno); + } else { + return NError(ERRNO_NOERR); + } + }; + + auto cbCompl = [](napi_env env, NError err) -> NVal { + if (err) { + return { env, err.GetNapiErr(env) }; + } + return NVal::CreateUndefined(env); + }; + + NVal thisVar(env, funcArg.GetThisVar()); + if (funcArg.GetArgc() == NARG_CNT::ZERO || (funcArg.GetArgc() == NARG_CNT::ONE && + NVal(env, funcArg[NARG_POS::FIRST]).TypeIs(napi_boolean))) { + return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_LOCK_NAME, cbExec, cbCompl).val_; + } else { + int cbIdx = ((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::FIRST); + NVal cb(env, funcArg[cbIdx]); + return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_LOCK_NAME, cbExec, cbCompl).val_; + } +} + +napi_value FileNExporter::TryLock(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) { + HILOGE("Number of arguments unmatched"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + auto fileEntity = GetFileEntity(env, funcArg.GetThisVar()); + if (!fileEntity) { + HILOGE("Failed to get file entity"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + bool exclusive = false; + if (!GetExclusive(env, funcArg, exclusive)) { + return nullptr; + } + + int ret = 0; + auto mode = exclusive ? LOCK_EX : LOCK_SH; + ret = flock(fileEntity->fd_.get()->GetFD(), mode | LOCK_NB); + if (ret < 0) { + HILOGE("Failed to try to lock file"); + NError(errno).ThrowErr(env); + } + + return NVal::CreateUndefined(env).val_; +} + +napi_value FileNExporter::UnLock(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::ZERO)) { + HILOGE("Number of arguments unmatched"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + auto fileEntity = GetFileEntity(env, funcArg.GetThisVar()); + if (!fileEntity) { + HILOGE("Failed to get file entity"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + int ret = 0; + ret = flock(fileEntity->fd_.get()->GetFD(), LOCK_UN); + if (ret < 0) { + HILOGE("Failed to unlock file"); + NError(errno).ThrowErr(env); + return nullptr; + } + return NVal::CreateUndefined(env).val_; +} + napi_value FileNExporter::Constructor(napi_env env, napi_callback_info info) { NFuncArg funcArg(env, info); @@ -85,6 +213,9 @@ bool FileNExporter::Export() { vector props = { NVal::DeclareNapiGetter("fd", GetFD), + NVal::DeclareNapiFunction("lock", Lock), + NVal::DeclareNapiFunction("tryLock", TryLock), + NVal::DeclareNapiFunction("unlock", UnLock), }; string className = GetClassName(); diff --git a/interfaces/kits/js/src/mod_fs/class_file/file_n_exporter.h b/interfaces/kits/js/src/mod_fs/class_file/file_n_exporter.h index f79ef539ded0b0a17ab6c3cd8630ccae8d6072c9..28cee081f8891af4f59fbfbb6fd2ce8077a0794d 100644 --- a/interfaces/kits/js/src/mod_fs/class_file/file_n_exporter.h +++ b/interfaces/kits/js/src/mod_fs/class_file/file_n_exporter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-2023 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 @@ -31,9 +31,14 @@ public: static napi_value Constructor(napi_env env, napi_callback_info cbinfo); static napi_value GetFD(napi_env env, napi_callback_info cbinfo); + static napi_value Lock(napi_env env, napi_callback_info cbinfo); + static napi_value TryLock(napi_env env, napi_callback_info cbinfo); + static napi_value UnLock(napi_env env, napi_callback_info cbinfo); FileNExporter(napi_env env, napi_value exports); ~FileNExporter() override; }; + +const std::string PROCEDURE_LOCK_NAME = "FileIOFileLock"; } // namespace ModuleFileIO } // namespace FileManagement } // namespace OHOS diff --git a/interfaces/kits/js/src/mod_fs/properties/listfile.cpp b/interfaces/kits/js/src/mod_fs/properties/listfile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d3b10af99787d5aae104d3a806dd4b6913ac025a --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/listfile.cpp @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2023 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 "listfile.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "filemgmt_libhilog.h" + +namespace OHOS::FileManagement::ModuleFileIO { +using namespace std; +using namespace OHOS::FileManagement::LibN; + +thread_local OptionArgs g_optionArgs; + +static bool CheckSuffix(vector &suffixs) +{ + for (string suffix : suffixs) { + if (suffix.length() <= 1 || suffix.length() > MAX_SUFFIX_LENGTH) { + return false; + } + if (suffix[0] != '.') { + return false; + } + for (size_t i = 1; i < suffix.length(); i++) { + if (!isalnum(suffix[i])) { + return false; + } + } + } + return true; +} + +static bool GetFileFilterParam(const NVal &argv, OHOS::DistributedFS::FileFilter *filter) +{ + bool ret = false; + if (argv.HasProp("suffix")) { + vector suffixs; + tie(ret, suffixs, ignore) = argv.GetProp("suffix").ToStringArray(); + if (!ret) { + HILOGE("Failed to get suffix prop."); + return false; + } + if (!CheckSuffix(suffixs) || suffixs.size() == 0) { + HILOGE("Invalid suffix."); + return false; + } + filter->SetSuffix(suffixs); + } + if (argv.HasProp("displayName")) { + vector displayNames; + tie(ret, displayNames, ignore) = argv.GetProp("displayName").ToStringArray(); + if (!ret) { + HILOGE("Failed to get displayname prop."); + return false; + } + if (displayNames.size() == 0) { + HILOGE("Invalid displayName."); + return false; + } + filter->SetDisplayName(displayNames); + } + if (argv.HasProp("fileSizeOver")) { + int64_t fileSizeOver; + tie(ret, fileSizeOver) = argv.GetProp("fileSizeOver").ToInt64(); + if (!ret || fileSizeOver < 0) { + HILOGE("Failed to get fileSizeOver prop."); + return false; + } + filter->SetFileSizeOver(fileSizeOver); + } + if (argv.HasProp("lastModifiedAfter")) { + double lastModifiedAfter; + tie(ret, lastModifiedAfter) = argv.GetProp("lastModifiedAfter").ToDouble(); + if (!ret || lastModifiedAfter < 0) { + HILOGE("Failed to get lastModifiedAfter prop."); + return false; + } + filter->SetLastModifiedAfter(lastModifiedAfter); + } + return true; +} + +static bool GetOptionParam(const NVal &argv, OptionArgs *optionArgs) +{ + bool succ = false; + if (argv.HasProp("listNum")) { + tie(succ, optionArgs->listNum) = argv.GetProp("listNum").ToInt64(); + if (!succ) { + HILOGE("Failed to get listNum prop."); + return false; + } else if (optionArgs->listNum < 0) { + HILOGE("Invalid listNum."); + return false; + } + } + + if (argv.HasProp("recursion")) { + tie(succ, optionArgs->recursion) = argv.GetProp("recursion").ToBool(); + if (!succ) { + HILOGE("Failed to get recursion prop."); + return false; + } + } + + if (argv.HasProp("filter")) { + NVal(filterProp) = argv.GetProp("filter"); + auto ret = GetFileFilterParam(NVal(filterProp), &optionArgs->filter); + if (!ret) { + HILOGE("Failed to get filter prop."); + return false; + } + } + return true; +} + +static bool GetOptionArg(napi_env env, const NFuncArg &funcArg, OptionArgs &optionArgs, const string &path) +{ + auto options = NVal(env, funcArg[NARG_POS::SECOND]); + if (funcArg.GetArgc() >= NARG_CNT::TWO && options.TypeIs(napi_object)) { + if (!GetOptionParam(options, &optionArgs)) { + HILOGE("Invalid options"); + return false; + } + } else if (funcArg.GetArgc() >= NARG_CNT::TWO && !options.TypeIs(napi_function)) { + HILOGE("Invalid options"); + return false; + } + optionArgs.path = path; + return true; +} + +static bool FilterSuffix(const vector& suffixs, const struct dirent& filename) +{ + for (auto iter = suffixs.begin(); iter != suffixs.end(); iter++) { + string_view sv1(*iter); + string_view sv2((string(filename.d_name)).substr((string(filename.d_name)).find('.'), + (string(filename.d_name)).length() - 1)); + if (sv1 != sv2) { + return true; + } + } + return false; +} + +static bool FilterDisplayname(const vector& displaynames, const struct dirent& filename) +{ + for (auto iter = displaynames.begin(); iter != displaynames.end(); iter++) { + int ret = fnmatch((*iter).c_str(), filename.d_name, FNM_PATHNAME | FNM_PERIOD); + if (ret == 0) { + return true; + } + } + return false; +} + +static bool FilterFilesizeOver(const int64_t fFileSizeOver, const struct dirent& filename) +{ + struct stat info; + string stPath = (g_optionArgs.path + '/' + string(filename.d_name)); + int32_t res = stat(stPath.c_str(), &info); + if (res != 0) { + HILOGE("Failed to stat file."); + return false; + } + if (fFileSizeOver > info.st_size) { + return false; + } + return true; +} + +static bool FilterLastModifyTime(const double lastModifiedAfter, const struct dirent& filename) +{ + struct stat info; + string stPath = g_optionArgs.path + '/' + string(filename.d_name); + int32_t res = stat(stPath.c_str(), &info); + if (res != 0) { + HILOGE("Failed to stat file."); + return false; + } + if (lastModifiedAfter > time(&info.st_mtime)) { + return false; + } + return true; +} + +static bool FilterResult(const struct dirent &filename) +{ + vector fSuffixs = g_optionArgs.filter.GetSuffix(); + if (!FilterSuffix(fSuffixs, filename) && fSuffixs.size() > 0) { + return false; + } + vector fDisplaynames = g_optionArgs.filter.GetDisplayName(); + if (!FilterDisplayname(fDisplaynames, filename) && fDisplaynames.size() > 0) { + return false; + } + int64_t fFileSizeOver = g_optionArgs.filter.GetFileSizeOver(); + if (!FilterFilesizeOver(fFileSizeOver, filename) && fFileSizeOver > 0) { + return false; + } + double fLastModifiedAfter = g_optionArgs.filter.GetLastModifiedAfter(); + if (!FilterLastModifyTime(fLastModifiedAfter, filename) && fLastModifiedAfter > 0) { + return false; + } + g_optionArgs.countNum++; + return true; +} + +static int32_t FilterFunc(const struct dirent *filename) +{ + if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") { + return FILTER_DISMATCH; + } + + if (filename->d_type == DT_REG) { + if (g_optionArgs.countNum < g_optionArgs.listNum || g_optionArgs.listNum == 0) { + if (FilterResult(*filename)) { + return FILTER_MATCH; + } + } + } else if (filename->d_type == DT_DIR) { + return FILTER_MATCH; + } + return FILTER_DISMATCH; +} + +static vector FileterFileRes(string path) +{ + struct dirent** namelist; + int num = scandir(path.c_str(), &(namelist), FilterFunc, alphasort); + vector dirents; + for (int i = 0; i < num; i++) { + struct dirent tmpDirent; + if (EOK == memcpy_s(&tmpDirent, sizeof(dirent), namelist[i], namelist[i]->d_reclen)) { + dirents.emplace_back(tmpDirent); + } + free(namelist[i]); + } + free(namelist); + return dirents; +} + +static void RecursiveFunc(string path, vector &dirents) +{ + struct dirent** namelist; + int num = scandir(path.c_str(), &(namelist), FilterFunc, alphasort); + for (int i = 0; i < num; i++) { + if ((*namelist[i]).d_type == DT_REG) { + struct dirent tmpDirent; + if (EOK == memcpy_s(&tmpDirent, sizeof(dirent), namelist[i], namelist[i]->d_reclen)) { + dirents.emplace_back(tmpDirent); + } + } else if ((*(namelist)[i]).d_type == DT_DIR) { + g_optionArgs.path += '/' + string((*namelist[i]).d_name); + RecursiveFunc(g_optionArgs.path, dirents); + } + free(namelist[i]); + } + free(namelist); +} + +static napi_value DoListFileVector2NV(napi_env env, vector &dirents) +{ + vector strs; + for (size_t i = 0; i < dirents.size(); i++) { + strs.emplace_back(dirents[i].d_name); + } + return NVal::CreateArrayString(env, strs).val_; +} + +static NVal DoListFileCompile(napi_env env, NError err, shared_ptr arg) +{ + if (err) { + return { env, err.GetNapiErr(env) }; + } else { + return { env, DoListFileVector2NV(env, arg->dirents) }; + } +} + +napi_value ListFile::Sync(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) { + HILOGE("Number of arguments unmatched"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + auto [succPath, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); + if (!succPath) { + HILOGE("Invalid path"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + if (!GetOptionArg(env, funcArg, g_optionArgs, string(path.get()))) { + NError(EINVAL).ThrowErr(env); + return nullptr; + } + vector direntsRes; + if (g_optionArgs.recursion) { + RecursiveFunc(path.get(), direntsRes); + } else { + direntsRes = FileterFileRes(path.get()); + } + g_optionArgs.Clear(); + return DoListFileVector2NV(env, direntsRes); +} + +napi_value ListFile::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 [succPath, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); + if (!succPath) { + HILOGE("Invalid path"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + OptionArgs optionArgsTmp = {}; + if (!GetOptionArg(env, funcArg, optionArgsTmp, string(path.get()))) { + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + auto arg = make_shared(); + if (!arg) { + HILOGE("Failed to request heap memory."); + NError(ENOMEM).ThrowErr(env); + return nullptr; + } + + auto cbExec = [arg, optionArgsTmp]() -> NError { + g_optionArgs = optionArgsTmp; + if (g_optionArgs.recursion) { + RecursiveFunc(g_optionArgs.path, arg->dirents); + } else { + arg->dirents = FileterFileRes(g_optionArgs.path); + } + g_optionArgs.Clear(); + return NError(ERRNO_NOERR); + }; + + auto cbCompl = [arg](napi_env env, NError err) -> NVal { + if (err) { + return { env, err.GetNapiErr(env) }; + } + return DoListFileCompile(env, err, arg); + }; + + NVal thisVar(env, funcArg.GetThisVar()); + + if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO && + NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_object))) { + return NAsyncWorkPromise(env, thisVar).Schedule(LIST_FILE_PRODUCE_NAME, cbExec, cbCompl).val_; + } else { + int32_t cbIdx = (funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD; + NVal cb(env, funcArg[cbIdx]); + return NAsyncWorkCallback(env, thisVar, cb).Schedule(LIST_FILE_PRODUCE_NAME, cbExec, cbCompl).val_; + } +} +} // namespace OHOS::FileManagement::ModuleFileIO \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/listfile.h b/interfaces/kits/js/src/mod_fs/properties/listfile.h new file mode 100644 index 0000000000000000000000000000000000000000..08316dae5dc1d9a575290c3358b29743d0823d3e --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/listfile.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 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_LISTFILE_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_LISTFILE_H + +#include "filemgmt_libn.h" +#include "../../common/file_filter.h" + +#include + +namespace OHOS::FileManagement::ModuleFileIO { +using namespace OHOS::FileManagement::LibN; +class ListFile { +public: + static napi_value Sync(napi_env env, napi_callback_info info); + static napi_value Async(napi_env env, napi_callback_info info); +}; + +class ListFileArgs { +public: + std::vector dirents; + explicit ListFileArgs() + { + dirents = std::vector(); + } + ~ListFileArgs() = default; +}; + +struct OptionArgs { + OHOS::DistributedFS::FileFilter filter = { {}, {}, {}, 0, 0, false, false }; + int listNum = 0; + int countNum = 0; + bool recursion = false; + std::string path = ""; + void Clear() + { + filter = { {}, {}, {}, 0, 0, false, false }; + listNum = 0; + countNum = 0; + recursion = false; + path = ""; + } +}; + +constexpr int FILTER_MATCH = 1; +constexpr int FILTER_DISMATCH = 0; +const int32_t MAX_SUFFIX_LENGTH = 256; +const std::string LIST_FILE_PRODUCE_NAME = "FileIOListFile"; +} // namespace OHOS::FileManagement::ModuleFileIO +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_LISTFILE_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/move.cpp b/interfaces/kits/js/src/mod_fs/properties/move.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bea653e94752d30de276f435952b5037d3389419 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/move.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2023 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 "move.h" + +#include +#include +#include +#include "uv.h" + +#include "common_func.h" +#include "filemgmt_libhilog.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; +using namespace OHOS::FileManagement::LibN; +static bool CheckDir(const string &path) +{ + if (!filesystem::is_directory(filesystem::status(path))) { + return false; + } + return true; +} + +static tuple, unique_ptr, int> ParseJsOperand(napi_env env, const NFuncArg& funcArg) +{ + auto [resGetFirstArg, src, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); + if (!resGetFirstArg || CheckDir(string(src.get()))) { + HILOGE("Invalid src"); + return { false, nullptr, nullptr, 0 }; + } + auto [resGetSecondArg, dest, unused] = NVal(env, funcArg[NARG_POS::SECOND]).ToUTF8String(); + if (!resGetSecondArg || CheckDir(string(dest.get()))) { + HILOGE("Invalid dest"); + return { false, nullptr, nullptr, 0 }; + } + int mode = 0; + bool resGetThirdArg = false; + if (funcArg.GetArgc() >= NARG_CNT::THREE && NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_number)) { + tie(resGetThirdArg, mode) = NVal(env, funcArg[NARG_POS::THIRD]).ToInt32(); + if (!resGetThirdArg || (mode != MODE_FORCE_MOVE && mode != MODE_THROW_ERR)) { + HILOGE("Invalid mode"); + return { false, nullptr, nullptr, 0 }; + } + } + return { true, move(src), move(dest), mode }; +} + +static int CopyAndDeleteFile(const string &src, const string &dest) +{ + int ret = 0; + uv_fs_t copyfile_req; + ret = uv_fs_copyfile(nullptr, ©file_req, src.c_str(), dest.c_str(), MODE_FORCE_MOVE, nullptr); + uv_fs_req_cleanup(©file_req); + if (ret < 0) { + HILOGE("Failed to move file using copyfile interface."); + return errno; + } + uv_fs_t unlink_req; + ret = uv_fs_unlink(nullptr, &unlink_req, src.c_str(), nullptr); + if (ret < 0) { + HILOGE("Failed to unlink src file"); + ret = uv_fs_unlink(nullptr, &unlink_req, dest.c_str(), nullptr); + if (ret < 0) { + HILOGE("Failed to unlink dest file"); + } + uv_fs_req_cleanup(&unlink_req); + return errno; + } + uv_fs_req_cleanup(&unlink_req); + return ERRNO_NOERR; +} + +static int RenameFile(const string &src, const string &dest) +{ + int ret = 0; + uv_fs_t rename_req; + ret = uv_fs_rename(nullptr, &rename_req, src.c_str(), dest.c_str(), nullptr); + if (ret < 0 && errno == EXDEV) { + return CopyAndDeleteFile(src, dest); + } + if (ret < 0) { + HILOGE("Failed to move file using rename syscall."); + return errno; + } + return ERRNO_NOERR; +} + +static int MoveFile(const string &src, const string &dest, int mode) +{ + int ret = 0; + if (mode == MODE_THROW_ERR) { + uv_fs_t access_req; + ret = uv_fs_access(nullptr, &access_req, dest.c_str(), 0, nullptr); + uv_fs_req_cleanup(&access_req); + if (ret == 0) { + HILOGE("Failed to move file due to existing destPath with MODE_THROW_ERR."); + return EEXIST; + } + if (ret < 0 && errno != ENOENT) { + HILOGE("Failed to access destPath with MODE_THROW_ERR."); + return errno; + } + } + return RenameFile(src, dest); +} + +napi_value Move::Sync(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::THREE)) { + HILOGE("Number of arguments unmatched"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + auto [succ, src, dest, mode] = ParseJsOperand(env, funcArg); + if (!succ) { + NError(EINVAL).ThrowErr(env); + return nullptr; + } + int ret = MoveFile(string(src.get()), string(dest.get()), mode); + if (ret) { + NError(ret).ThrowErr(env); + return nullptr; + } + return NVal::CreateUndefined(env).val_; +} + +napi_value Move::Async(napi_env env, napi_callback_info info) +{ + NFuncArg funcArg(env, info); + if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::FOUR)) { + HILOGE("Number of arguments unmatched"); + NError(EINVAL).ThrowErr(env); + return nullptr; + } + auto [succ, src, dest, mode] = ParseJsOperand(env, funcArg); + if (!succ) { + NError(EINVAL).ThrowErr(env); + return nullptr; + } + + auto cbExec = [srcPath = string(src.get()), destPath = string(dest.get()), mode = mode]() -> NError { + int ret = MoveFile(srcPath, destPath, mode); + if (ret) { + return NError(ret); + } + return NError(ERRNO_NOERR); + }; + + auto cbComplCallback = [](napi_env env, NError err) -> NVal { + if (err) { + return { env, err.GetNapiErr(env) }; + } + return { NVal::CreateUndefined(env) }; + }; + + + NVal thisVar(env, funcArg.GetThisVar()); + size_t argc = funcArg.GetArgc(); + if (argc == NARG_CNT::TWO || (argc == NARG_CNT::THREE && NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_number))) { + return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_MOVE_NAME, cbExec, cbComplCallback).val_; + } else { + int cbIdx = ((funcArg.GetArgc() == NARG_CNT::THREE) ? NARG_POS::THIRD : NARG_POS::FOURTH); + NVal cb(env, funcArg[cbIdx]); + return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_MOVE_NAME, cbExec, cbComplCallback).val_; + } +} +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/move.h b/interfaces/kits/js/src/mod_fs/properties/move.h new file mode 100644 index 0000000000000000000000000000000000000000..6124946792e7b613fd97e1202c12a9d5cab27f8d --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/move.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 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_MOVE_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_MOVE_H + +#include + +#include "filemgmt_libn.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +class Move final { +public: + static napi_value Sync(napi_env env, napi_callback_info info); + static napi_value Async(napi_env env, napi_callback_info info); +}; +constexpr int MODE_FORCE_MOVE = 0; +constexpr int MODE_THROW_ERR = 1; +const std::string PROCEDURE_MOVE_NAME = "FileIOMove"; +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS +#endif \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/prop_n_exporter.cpp b/interfaces/kits/js/src/mod_fs/properties/prop_n_exporter.cpp index 26df9d8e9d4c0cf57394544d8d4a0bb163303db2..606e8d1440bf23ba935701e67c72c9305e8d7d62 100644 --- a/interfaces/kits/js/src/mod_fs/properties/prop_n_exporter.cpp +++ b/interfaces/kits/js/src/mod_fs/properties/prop_n_exporter.cpp @@ -34,8 +34,10 @@ #include "fsync.h" #include "js_native_api.h" #include "js_native_api_types.h" +#include "listfile.h" #include "lstat.h" #include "mkdtemp.h" +#include "move.h" #include "open.h" #include "read_text.h" #include "rename.h" @@ -583,12 +585,16 @@ bool PropNExporter::Export() NVal::DeclareNapiFunction("fdopenStreamSync", FdopenStream::Sync), NVal::DeclareNapiFunction("fsync", Fsync::Async), NVal::DeclareNapiFunction("fsyncSync", Fsync::Sync), + NVal::DeclareNapiFunction("listFile", ListFile::Async), + NVal::DeclareNapiFunction("listFileSync", ListFile::Sync), NVal::DeclareNapiFunction("lstat", Lstat::Async), NVal::DeclareNapiFunction("lstatSync", Lstat::Sync), NVal::DeclareNapiFunction("mkdir", Mkdir), NVal::DeclareNapiFunction("mkdirSync", MkdirSync), NVal::DeclareNapiFunction("mkdtemp", Mkdtemp::Async), NVal::DeclareNapiFunction("mkdtempSync", Mkdtemp::Sync), + NVal::DeclareNapiFunction("moveFile", Move::Async), + NVal::DeclareNapiFunction("moveFileSync", Move::Sync), NVal::DeclareNapiFunction("open", Open::Async), NVal::DeclareNapiFunction("openSync", Open::Sync), NVal::DeclareNapiFunction("read", Read), diff --git a/utils/filemgmt_libn/include/n_error.h b/utils/filemgmt_libn/include/n_error.h index a60dcd610a6367e0e327d2cd30749b003cc5d057..f7d0daa15b31a10b10a803bfa52c5c406d541eed 100644 --- a/utils/filemgmt_libn/include/n_error.h +++ b/utils/filemgmt_libn/include/n_error.h @@ -77,7 +77,8 @@ enum ErrCodeSuffixOfFileIO { E_BADFD, E_RESTART, E_DQUOT, - E_UKERR + E_UKERR, + E_NOLCK }; enum ErrCodeSuffixOfUserFileManager { @@ -173,6 +174,7 @@ const std::unordered_map> errCodeTable { { ERESTART, { FILEIO_SYS_CAP_TAG + E_RESTART, "Interrupted system call should be restarted" } }, { EDQUOT, { FILEIO_SYS_CAP_TAG + E_DQUOT, "Quota exceeded" } }, { UNKROWN_ERR, { FILEIO_SYS_CAP_TAG + E_UKERR, "Unknown error" } }, + { ENOLCK, { FILEIO_SYS_CAP_TAG + E_NOLCK, "No record locks available" } }, { FILEIO_SYS_CAP_TAG + E_PERM, { FILEIO_SYS_CAP_TAG + E_PERM, "Operation not permitted" } }, { FILEIO_SYS_CAP_TAG + E_NOENT, { FILEIO_SYS_CAP_TAG + E_NOENT, "No such file or directory" } }, { FILEIO_SYS_CAP_TAG + E_SRCH, { FILEIO_SYS_CAP_TAG + E_SRCH, "No such process" } }, @@ -216,6 +218,7 @@ const std::unordered_map> errCodeTable { "Interrupted system call should be restarted" } }, { FILEIO_SYS_CAP_TAG + E_DQUOT, { FILEIO_SYS_CAP_TAG + E_DQUOT, "Quota exceeded" } }, { FILEIO_SYS_CAP_TAG + E_UKERR, { FILEIO_SYS_CAP_TAG + E_UKERR, "Unknown error" } }, + { FILEIO_SYS_CAP_TAG + E_NOLCK, { FILEIO_SYS_CAP_TAG + E_NOLCK, "No record locks available" } }, { USER_FILE_MANAGER_SYS_CAP_TAG + E_DISPLAYNAME, { USER_FILE_MANAGER_SYS_CAP_TAG + E_DISPLAYNAME, "Invalid display name" } }, { USER_FILE_MANAGER_SYS_CAP_TAG + E_URIM, { USER_FILE_MANAGER_SYS_CAP_TAG + E_URIM, "Invalid uri" } },