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_fileio/class_randomaccessfile/randomaccessfile_n_exporter.cpp b/interfaces/kits/js/src/mod_fileio/class_randomaccessfile/randomaccessfile_n_exporter.cpp index 74098271a104a5a8259f56d71d4edf67d59cf05d..97f1167371189532d5c06e32b79dc68d4eb01adf 100644 --- a/interfaces/kits/js/src/mod_fileio/class_randomaccessfile/randomaccessfile_n_exporter.cpp +++ b/interfaces/kits/js/src/mod_fileio/class_randomaccessfile/randomaccessfile_n_exporter.cpp @@ -174,7 +174,7 @@ napi_value RandomAccessFileNExporter::ReadSync(napi_env env, napi_callback_info UniError(EINVAL).ThrowErr(env, "Invalid buffer/options"); return nullptr; } - ssize_t actLen = DoReadRAF(env, buf, len, rafEntity->fd_.get()->GetFD(), rafEntity->fpointer + pos); + size_t actLen = DoReadRAF(env, buf, len, rafEntity->fd_.get()->GetFD(), rafEntity->fpointer + pos); if (actLen < 0) { UniError(errno).ThrowErr(env); return nullptr; @@ -184,7 +184,7 @@ napi_value RandomAccessFileNExporter::ReadSync(napi_env env, napi_callback_info } struct AsyncIOReadArg { - ssize_t lenRead { 0 }; + size_t lenRead { 0 }; int offset { 0 }; NRef refReadBuf; @@ -212,7 +212,7 @@ static napi_value ReadExec(napi_env env, NFuncArg &funcArg) auto arg = make_shared(NVal(env, funcArg[NARG_POS::FIRST])); auto cbExec = [arg, buf, len, hasPos, pos, offset, rafEntity](napi_env env) -> UniError { - ssize_t actLen = DoReadRAF(env, buf, len, rafEntity->fd_.get()->GetFD(), rafEntity->fpointer + pos); + size_t actLen = DoReadRAF(env, buf, len, rafEntity->fd_.get()->GetFD(), rafEntity->fpointer + pos); if (actLen < 0) { return UniError(errno); } 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/common_func.cpp b/interfaces/kits/js/src/mod_fs/common_func.cpp index f8f9f0e29574ff0e9de3753fa1719865d39df56b..bcb6c355cbc02ad79b167be05619a914ed6d2fa6 100644 --- a/interfaces/kits/js/src/mod_fs/common_func.cpp +++ b/interfaces/kits/js/src/mod_fs/common_func.cpp @@ -96,21 +96,21 @@ static tuple GetActualLen(napi_env env, int64_t bufLen, int64_t bu return { true, retLen }; } -int CommonFunc::ConvertJsFlags(int &flags) +unsigned int CommonFunc::ConvertJsFlags(unsigned int &flags) { - static constexpr int USR_O_RDONLY = 00; - static constexpr int USR_O_WRONLY = 01; - static constexpr int USR_O_RDWR = 02; - static constexpr int USR_O_CREAT = 0100; - static constexpr int USR_O_EXCL = 0200; - static constexpr int USR_O_TRUNC = 01000; - static constexpr int USR_O_APPEND = 02000; - static constexpr int USR_O_NONBLOCK = 04000; - static constexpr int USR_O_DIRECTORY = 0200000; - static constexpr int USR_O_NOFOLLOW = 0400000; - static constexpr int USR_O_SYNC = 04010000; + static constexpr unsigned int USR_O_RDONLY = 00; + static constexpr unsigned int USR_O_WRONLY = 01; + static constexpr unsigned int USR_O_RDWR = 02; + static constexpr unsigned int USR_O_CREAT = 0100; + static constexpr unsigned int USR_O_EXCL = 0200; + static constexpr unsigned int USR_O_TRUNC = 01000; + static constexpr unsigned int USR_O_APPEND = 02000; + static constexpr unsigned int USR_O_NONBLOCK = 04000; + static constexpr unsigned int USR_O_DIRECTORY = 0200000; + static constexpr unsigned int USR_O_NOFOLLOW = 0400000; + static constexpr unsigned int USR_O_SYNC = 04010000; - int flagsABI = 0; + unsigned int flagsABI = 0; flagsABI |= ((flags & USR_O_RDONLY) == USR_O_RDONLY) ? O_RDONLY : 0; flagsABI |= ((flags & USR_O_WRONLY) == USR_O_WRONLY) ? O_WRONLY : 0; flagsABI |= ((flags & USR_O_RDWR) == USR_O_RDWR) ? O_RDWR : 0; @@ -175,6 +175,22 @@ void CommonFunc::fs_req_cleanup(uv_fs_t* req) } } +string CommonFunc::GetModeFromFlags(unsigned int flags) +{ + const string RDONLY = "r"; + const string WRONLY = "w"; + const string APPEND = "a"; + const string TRUNC = "t"; + string mode = RDONLY; + mode += (((flags & O_RDWR) == O_RDWR) ? WRONLY : ""); + mode = (((flags & O_WRONLY) == O_WRONLY) ? WRONLY : mode); + if (mode != RDONLY) { + mode += ((flags & O_TRUNC) ? TRUNC : ""); + mode += ((flags & O_APPEND) ? APPEND : ""); + } + return mode; +} + tuple, unique_ptr> CommonFunc::GetCopyPathArg(napi_env env, napi_value srcPath, napi_value dstPath) diff --git a/interfaces/kits/js/src/mod_fs/common_func.h b/interfaces/kits/js/src/mod_fs/common_func.h index 2409a0b8fd631f4aa8ec518374b45f2a317ecfe8..0736d7b45d324f07718761bb953a232b67972e66 100644 --- a/interfaces/kits/js/src/mod_fs/common_func.h +++ b/interfaces/kits/js/src/mod_fs/common_func.h @@ -45,7 +45,7 @@ struct FileInfo { void InitOpenMode(napi_env env, napi_value exports); struct CommonFunc { - static int ConvertJsFlags(int &flags); + static unsigned int ConvertJsFlags(unsigned int &flags); static LibN::NVal InstantiateStat(napi_env env, struct stat &buf); static LibN::NVal InstantiateStream(napi_env env, std::unique_ptr fp); static std::tuple GetReadArg(napi_env env, @@ -58,6 +58,7 @@ struct CommonFunc { napi_value srcPath, napi_value dstPath); static void fs_req_cleanup(uv_fs_t* req); + static std::string GetModeFromFlags(unsigned int flags); }; } // namespace ModuleFileIO } // namespace FileManagement 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..41b81f76235bea00d4cf9eb293595728d72e6034 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/listfile.cpp @@ -0,0 +1,382 @@ +/* + * 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++) { + dirents.emplace_back(*namelist[i]); + 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/open.cpp b/interfaces/kits/js/src/mod_fs/properties/open.cpp index 2904bbfa0c193ff605e683158126c57fcabe24d4..1f773f58d52e3fd85469d1cfd914b8acf67f4c63 100644 --- a/interfaces/kits/js/src/mod_fs/properties/open.cpp +++ b/interfaces/kits/js/src/mod_fs/properties/open.cpp @@ -33,13 +33,14 @@ namespace ModuleFileIO { using namespace std; using namespace OHOS::FileManagement::LibN; -static tuple GetJsFlags(napi_env env, const NFuncArg &funcArg) +static tuple GetJsFlags(napi_env env, const NFuncArg &funcArg) { - int mode = O_RDONLY; + unsigned int mode = O_RDONLY; bool succ = false; if (funcArg.GetArgc() >= NARG_CNT::TWO && NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_number)) { tie(succ, mode) = NVal(env, funcArg[NARG_POS::SECOND]).ToInt32(); - if (!succ) { + unsigned int invalidMode = (O_WRONLY | O_RDWR); + if (!succ || ((mode & invalidMode) == invalidMode)) { HILOGE("Invalid mode"); NError(EINVAL).ThrowErr(env); return { false, mode }; 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" } },