• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "movedir.h"
17 
18 #include <dirent.h>
19 #include <filesystem>
20 #include <memory>
21 #include <string_view>
22 #include <tuple>
23 #include <unistd.h>
24 #include <deque>
25 
26 #include "common_func.h"
27 #include "file_utils.h"
28 #include "filemgmt_libhilog.h"
29 
30 namespace OHOS {
31 namespace FileManagement {
32 namespace ModuleFileIO {
33 using namespace std;
34 using namespace OHOS::FileManagement::LibN;
35 
36 static int RecurMoveDir(const string &srcPath, const string &destPath, const int mode,
37     deque<struct ErrFiles> &errfiles);
38 
JudgeExistAndEmpty(const string & path)39 static tuple<bool, bool> JudgeExistAndEmpty(const string &path)
40 {
41     filesystem::path pathName(path);
42     if (filesystem::exists(pathName)) {
43         if (filesystem::is_empty(pathName)) {
44             return { true, true };
45         }
46         return { true, false };
47     }
48     return { false, false };
49 }
50 
RmDirectory(const string & path)51 static int RmDirectory(const string &path)
52 {
53     filesystem::path pathName(path);
54     if (filesystem::exists(pathName)) {
55         std::error_code errCode;
56         (void)filesystem::remove_all(pathName, errCode);
57         if (errCode.value() != 0) {
58             HILOGE("Failed to remove directory, error code: %{public}d", errCode.value());
59             return errCode.value();
60         }
61     }
62     return ERRNO_NOERR;
63 }
64 
RemovePath(const string & pathStr)65 static int RemovePath(const string& pathStr)
66 {
67     filesystem::path pathTarget(pathStr);
68     std::error_code errCode;
69     if (!filesystem::remove(pathTarget, errCode)) {
70         HILOGE("Failed to remove file or directory, error code: %{public}d", errCode.value());
71         return errCode.value();
72     }
73     return ERRNO_NOERR;
74 }
75 
ParseJsOperand(napi_env env,const NFuncArg & funcArg)76 static tuple<bool, unique_ptr<char[]>, unique_ptr<char[]>, int> ParseJsOperand(napi_env env, const NFuncArg& funcArg)
77 {
78     auto [resGetFirstArg, src, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
79     if (!resGetFirstArg || !filesystem::is_directory(filesystem::status(src.get()))) {
80         HILOGE("Invalid src");
81         return { false, nullptr, nullptr, 0 };
82     }
83     auto [resGetSecondArg, dest, unused] = NVal(env, funcArg[NARG_POS::SECOND]).ToUTF8String();
84     if (!resGetSecondArg || !filesystem::is_directory(filesystem::status(dest.get()))) {
85         HILOGE("Invalid dest");
86         return { false, nullptr, nullptr, 0 };
87     }
88     int mode = 0;
89     if (funcArg.GetArgc() >= NARG_CNT::THREE) {
90         bool resGetThirdArg = false;
91         tie(resGetThirdArg, mode) = NVal(env, funcArg[NARG_POS::THIRD]).ToInt32(mode);
92         if (!resGetThirdArg || (mode < DIRMODE_MIN || mode > DIRMODE_MAX)) {
93             HILOGE("Invalid mode");
94             return { false, nullptr, nullptr, 0 };
95         }
96     }
97     return { true, move(src), move(dest), mode };
98 }
99 
CopyAndDeleteFile(const string & src,const string & dest)100 static int CopyAndDeleteFile(const string &src, const string &dest)
101 {
102     filesystem::path dstPath(dest);
103     if (filesystem::exists(dstPath)) {
104         int removeRes = RemovePath(dest);
105         if (removeRes != 0) {
106             HILOGE("Failed to remove dest file");
107             return removeRes;
108         }
109     }
110     filesystem::path srcPath(src);
111     std::error_code errCode;
112     if (!filesystem::copy_file(srcPath, dstPath, filesystem::copy_options::overwrite_existing, errCode)) {
113         HILOGE("Failed to copy file, error code: %{public}d", errCode.value());
114         return errCode.value();
115     }
116     return RemovePath(src);
117 }
118 
RenameFile(const string & src,const string & dest,const int mode,deque<struct ErrFiles> & errfiles)119 static int RenameFile(const string &src, const string &dest, const int mode, deque<struct ErrFiles> &errfiles)
120 {
121     filesystem::path dstPath(dest);
122     if (filesystem::exists(dstPath)) {
123         if (filesystem::is_directory(dstPath)) {
124             errfiles.emplace_front(src, dest);
125             return ERRNO_NOERR;
126         }
127         if (mode == DIRMODE_FILE_THROW_ERR) {
128             errfiles.emplace_back(src, dest);
129             return ERRNO_NOERR;
130         }
131     }
132     filesystem::path srcPath(src);
133     std::error_code errCode;
134     filesystem::rename(srcPath, dstPath, errCode);
135     if (errCode.value() == EXDEV) {
136         HILOGE("Failed to rename file due to EXDEV");
137         return CopyAndDeleteFile(src, dest);
138     }
139     return errCode.value();
140 }
141 
FilterFunc(const struct dirent * filename)142 static int32_t FilterFunc(const struct dirent *filename)
143 {
144     if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") {
145         return FILE_DISMATCH;
146     }
147     return FILE_MATCH;
148 }
149 
RenameDir(const string & src,const string & dest,const int mode,deque<struct ErrFiles> & errfiles)150 static int RenameDir(const string &src, const string &dest, const int mode, deque<struct ErrFiles> &errfiles)
151 {
152     filesystem::path destPath(dest);
153     if (filesystem::exists(destPath)) {
154         return RecurMoveDir(src, dest, mode, errfiles);
155     }
156     filesystem::path srcPath(src);
157     std::error_code errCode;
158     filesystem::rename(srcPath, destPath, errCode);
159     if (errCode.value() == EXDEV) {
160         HILOGE("Failed to rename file due to EXDEV");
161         if (!filesystem::create_directory(destPath, errCode)) {
162             HILOGE("Failed to create directory, error code: %{public}d", errCode.value());
163             return errCode.value();
164         }
165         return RecurMoveDir(src, dest, mode, errfiles);
166     }
167     if (errCode.value() != 0) {
168         HILOGE("Failed to rename file, error code: %{public}d", errCode.value());
169         return errCode.value();
170     }
171     return ERRNO_NOERR;
172 }
173 
174 struct NameListArg {
175     struct dirent** namelist;
176     int num;
177 };
178 
Deleter(struct NameListArg * arg)179 static void Deleter(struct NameListArg *arg)
180 {
181     for (int i = 0; i < arg->num; i++) {
182         free((arg->namelist)[i]);
183         (arg->namelist)[i] = nullptr;
184     }
185     free(arg->namelist);
186 }
187 
RecurMoveDir(const string & srcPath,const string & destPath,const int mode,deque<struct ErrFiles> & errfiles)188 static int RecurMoveDir(const string &srcPath, const string &destPath, const int mode,
189     deque<struct ErrFiles> &errfiles)
190 {
191     filesystem::path dpath(destPath);
192     if (!filesystem::is_directory(dpath)) {
193         errfiles.emplace_front(srcPath, destPath);
194         return ERRNO_NOERR;
195     }
196 
197     unique_ptr<struct NameListArg, decltype(Deleter)*> ptr = {new struct NameListArg, Deleter};
198     if (!ptr) {
199         HILOGE("Failed to request heap memory.");
200         return ENOMEM;
201     }
202     int num = scandir(srcPath.c_str(), &(ptr->namelist), FilterFunc, alphasort);
203     ptr->num = num;
204 
205     for (int i = 0; i < num; i++) {
206         if ((ptr->namelist[i])->d_type == DT_DIR) {
207             string srcTemp = srcPath + '/' + string((ptr->namelist[i])->d_name);
208             string destTemp = destPath + '/' + string((ptr->namelist[i])->d_name);
209             size_t size = errfiles.size();
210             int res = RenameDir(srcTemp, destTemp, mode, errfiles);
211             if (res != ERRNO_NOERR) {
212                 return res;
213             }
214             if (size != errfiles.size()) {
215                 continue;
216             }
217             res = RemovePath(srcTemp);
218             if (res) {
219                 return res;
220             }
221         } else {
222             string src = srcPath + '/' + string((ptr->namelist[i])->d_name);
223             string dest = destPath + '/' + string((ptr->namelist[i])->d_name);
224             int res = RenameFile(src, dest, mode, errfiles);
225             if (res != ERRNO_NOERR) {
226                 HILOGE("Failed to rename file for error %{public}d", res);
227                 return res;
228             }
229         }
230     }
231     return ERRNO_NOERR;
232 }
233 
MoveDirFunc(const string & src,const string & dest,const int mode,deque<struct ErrFiles> & errfiles)234 static int MoveDirFunc(const string &src, const string &dest, const int mode, deque<struct ErrFiles> &errfiles)
235 {
236     size_t found = string(src).rfind('/');
237     if (found == std::string::npos) {
238         return EINVAL;
239     }
240     if (access(src.c_str(), W_OK) != 0) {
241         HILOGE("Failed to move src directory due to doesn't exist or hasn't write permission");
242         return errno;
243     }
244     string dirName = string(src).substr(found);
245     string destStr = dest + dirName;
246     auto [destStrExist, destStrEmpty] = JudgeExistAndEmpty(destStr);
247     if (destStrExist && !destStrEmpty) {
248         if (mode == DIRMODE_DIRECTORY_REPLACE) {
249             int removeRes = RmDirectory(destStr);
250             if (removeRes) {
251                 HILOGE("Failed to remove dest directory in DIRMODE_DIRECTORY_REPLACE");
252                 return removeRes;
253             }
254         }
255         if (mode == DIRMODE_DIRECTORY_THROW_ERR) {
256             HILOGE("Failed to move directory in DIRMODE_DIRECTORY_THROW_ERR");
257             return ENOTEMPTY;
258         }
259     }
260     int res = RenameDir(src, destStr, mode, errfiles);
261     if (res == ERRNO_NOERR) {
262         if (!errfiles.empty()) {
263             HILOGE("Failed to movedir with some conflicted files");
264             return EEXIST;
265         }
266         int removeRes = RmDirectory(src);
267         if (removeRes) {
268             HILOGE("Failed to remove src directory");
269             return removeRes;
270         }
271     }
272     return res;
273 }
274 
GetErrData(napi_env env,deque<struct ErrFiles> & errfiles)275 static napi_value GetErrData(napi_env env, deque<struct ErrFiles> &errfiles)
276 {
277     napi_value res = nullptr;
278     napi_status status = napi_create_array(env, &res);
279     if (status != napi_ok) {
280         HILOGE("Failed to create array");
281         return nullptr;
282     }
283     size_t index = 0;
284     for (auto &iter : errfiles) {
285         NVal obj = NVal::CreateObject(env);
286         obj.AddProp("srcFile", NVal::CreateUTF8String(env, iter.srcFiles).val_);
287         obj.AddProp("destFile", NVal::CreateUTF8String(env, iter.destFiles).val_);
288         status = napi_set_element(env, res, index++, obj.val_);
289         if (status != napi_ok) {
290             HILOGE("Failed to set element on data");
291             return nullptr;
292         }
293     }
294     return res;
295 }
296 
Sync(napi_env env,napi_callback_info info)297 napi_value MoveDir::Sync(napi_env env, napi_callback_info info)
298 {
299     NFuncArg funcArg(env, info);
300     if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::THREE)) {
301         HILOGE("Number of arguments unmatched");
302         NError(EINVAL).ThrowErr(env);
303         return nullptr;
304     }
305     auto [succ, src, dest, mode] = ParseJsOperand(env, funcArg);
306     if (!succ) {
307         NError(EINVAL).ThrowErr(env);
308         return nullptr;
309     }
310 
311     deque<struct ErrFiles> errfiles = {};
312     int ret = MoveDirFunc(src.get(), dest.get(), mode, errfiles);
313     if (ret == EEXIST) {
314         NError(ret).ThrowErrAddData(env, EEXIST, GetErrData(env, errfiles));
315         return nullptr;
316     } else if (ret) {
317         NError(ret).ThrowErr(env);
318         return nullptr;
319     }
320     return NVal::CreateUndefined(env).val_;
321 }
322 
323 struct MoveDirArgs {
324     deque<ErrFiles> errfiles;
325     int errNo = 0;
326     ~MoveDirArgs() = default;
327 };
328 
Async(napi_env env,napi_callback_info info)329 napi_value MoveDir::Async(napi_env env, napi_callback_info info)
330 {
331     NFuncArg funcArg(env, info);
332     if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::FOUR)) {
333         HILOGE("Number of arguments unmatched");
334         NError(EINVAL).ThrowErr(env);
335         return nullptr;
336     }
337     auto [succ, src, dest, mode] = ParseJsOperand(env, funcArg);
338     if (!succ) {
339         NError(EINVAL).ThrowErr(env);
340         return nullptr;
341     }
342     auto arg = CreateSharedPtr<MoveDirArgs>();
343     if (arg == nullptr) {
344         HILOGE("Failed to request heap memory.");
345         NError(ENOMEM).ThrowErr(env);
346         return nullptr;
347     }
348     auto cbExec = [srcPath = string(src.get()), destPath = string(dest.get()), mode = mode, arg]() -> NError {
349         arg->errNo = MoveDirFunc(srcPath, destPath, mode, arg->errfiles);
350         if (arg->errNo) {
351             return NError(arg->errNo);
352         }
353         return NError(ERRNO_NOERR);
354     };
355 
356     auto cbComplCallback = [arg, mode = mode](napi_env env, NError err) -> NVal {
357         if (arg->errNo == EEXIST) {
358             napi_value data = err.GetNapiErr(env);
359             napi_status status = napi_set_named_property(env, data, FILEIO_TAG_ERR_DATA.c_str(),
360                 GetErrData(env, arg->errfiles));
361             if (status != napi_ok) {
362                 HILOGE("Failed to set data property on Error");
363             }
364             return { env, data };
365         } else if (arg->errNo) {
366             return { env, err.GetNapiErr(env) };
367         }
368         return NVal::CreateUndefined(env);
369     };
370 
371     NVal thisVar(env, funcArg.GetThisVar());
372     if (funcArg.GetArgc() == NARG_CNT::TWO || (funcArg.GetArgc() == NARG_CNT::THREE &&
373             !NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_function))) {
374         return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_MOVEDIR_NAME, cbExec, cbComplCallback).val_;
375     } else {
376         int cbIdex = ((funcArg.GetArgc() == NARG_CNT::THREE) ? NARG_POS::THIRD : NARG_POS::FOURTH);
377         NVal cb(env, funcArg[cbIdex]);
378         return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_MOVEDIR_NAME, cbExec, cbComplCallback).val_;
379     }
380 }
381 
382 } // ModuleFileIO
383 } // FileManagement
384 } // OHOS