1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "rmdirent.h"
17 
18 #include <cstring>
19 #include <dirent.h>
20 #include <filesystem>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <tuple>
24 #include <unistd.h>
25 
26 #include "common_func.h"
27 #include "filemgmt_libhilog.h"
28 #include "uv.h"
29 
30 namespace OHOS {
31 namespace FileManagement {
32 namespace ModuleFileIO {
33 using namespace std;
34 using namespace OHOS::FileManagement::LibN;
35 
36 #ifdef __MUSL__
RmDirent(const string & fpath)37 static NError RmDirent(const string &fpath)
38 {
39     std::filesystem::path strToPath(fpath);
40     std::error_code errCode;
41     std::uintmax_t num = std::filesystem::remove_all(strToPath, errCode);
42     if (errCode.value() != ERRNO_NOERR) {
43         HILOGD("Failed to remove directory, error code: %{public}d", errCode.value());
44         return NError(errCode.value());
45     }
46     if (!num || std::filesystem::exists(strToPath, errCode)) {
47         HILOGE("Failed to remove directory, dirPath does not exist");
48         return NError(ENOENT);
49     }
50     if (errCode.value() != ERRNO_NOERR) {
51         HILOGE("fs exists fail, error code: %{public}d", errCode.value());
52         return NError(errCode.value());
53     }
54     return NError(ERRNO_NOERR);
55 }
56 
57 #else
RmDirent(const string & fpath)58 static NError RmDirent(const string &fpath)
59 {
60     std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> scandir_req = {
61         new (std::nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
62     if (!scandir_req) {
63         HILOGE("Failed to request heap memory.");
64         return NError(ENOMEM);
65     }
66     int ret = 0;
67     ret = uv_fs_scandir(nullptr, scandir_req.get(), fpath.c_str(), 0, nullptr);
68     if (ret < 0) {
69         HILOGE("Failed to scandir, ret: %{public}d", ret);
70         return NError(ret);
71     }
72     uv_dirent_t dent;
73     while (uv_fs_scandir_next(scandir_req.get(), &dent) != UV_EOF) {
74         string filePath = fpath + "/" + string(dent.name);
75         if (dent.type == UV_DIRENT_FILE) {
76             std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> unlink_req = {
77                 new (std::nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
78             if (!unlink_req) {
79                 HILOGE("Failed to request heap memory.");
80                 return NError(ENOMEM);
81             }
82             ret = uv_fs_unlink(nullptr, unlink_req.get(), filePath.c_str(), nullptr);
83             if (ret < 0) {
84                 HILOGE("Failed to unlink file, ret: %{public}d", ret);
85                 return NError(ret);
86             }
87         } else if (dent.type == UV_DIRENT_DIR) {
88             auto rmDirentRes = RmDirent(filePath);
89             if (rmDirentRes) {
90                 return rmDirentRes;
91             }
92         }
93     }
94     std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> rmdir_req = {
95         new (std::nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
96     if (!rmdir_req) {
97         HILOGE("Failed to request heap memory.");
98         return NError(ENOMEM);
99     }
100     ret = uv_fs_rmdir(nullptr, rmdir_req.get(), fpath.c_str(), nullptr);
101     if (ret < 0) {
102         HILOGE("Failed to rmdir empty dir, ret: %{public}d", ret);
103         return NError(ret);
104     }
105     return NError(ERRNO_NOERR);
106 }
107 #endif
108 
Sync(napi_env env,napi_callback_info info)109 napi_value Rmdirent::Sync(napi_env env, napi_callback_info info)
110 {
111     NFuncArg funcArg(env, info);
112     if (!funcArg.InitArgs(NARG_CNT::ONE)) {
113         HILOGE("Number of arguments unmatched");
114         NError(EINVAL).ThrowErr(env);
115         return nullptr;
116     }
117 
118     auto [succ, path, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
119     if (!succ) {
120         HILOGE("Invalid path from JS first argument");
121         NError(EINVAL).ThrowErr(env);
122         return nullptr;
123     }
124 
125     auto err = RmDirent(string(path.get()));
126     if (err) {
127         err.ThrowErr(env);
128         return nullptr;
129     }
130 
131     return NVal::CreateUndefined(env).val_;
132 }
133 
Async(napi_env env,napi_callback_info info)134 napi_value Rmdirent::Async(napi_env env, napi_callback_info info)
135 {
136     NFuncArg funcArg(env, info);
137     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
138         HILOGE("Number of arguments unmatched");
139         NError(EINVAL).ThrowErr(env);
140         return nullptr;
141     }
142 
143     auto [succ, path, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
144     if (!succ) {
145         HILOGE("Invalid path from JS first argument");
146         NError(EINVAL).ThrowErr(env);
147         return nullptr;
148     }
149 
150     auto cbExec = [tmpPath = string(path.get())]() -> NError {
151         return RmDirent(tmpPath);
152     };
153     auto cbCompl = [](napi_env env, NError err) -> NVal {
154         if (err) {
155             return { env, err.GetNapiErr(env) };
156         } else {
157             return NVal::CreateUndefined(env);
158         }
159     };
160 
161     NVal thisVar(env, funcArg.GetThisVar());
162     size_t argc = funcArg.GetArgc();
163     if (argc == NARG_CNT::ONE) {
164         return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_RMDIRENT_NAME, cbExec, cbCompl).val_;
165     } else {
166         NVal cb(env, funcArg[NARG_POS::SECOND]);
167         return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_RMDIRENT_NAME, cbExec, cbCompl).val_;
168     }
169 }
170 } // namespace ModuleFileIO
171 } // namespace FileManagement
172 } // namespace OHOS