• 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 <vector>
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     vector<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     std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> stat_req = {
103         new (std::nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
104     if (!stat_req) {
105         HILOGE("Failed to request heap memory.");
106         return ENOMEM;
107     }
108     int ret = uv_fs_stat(nullptr, stat_req.get(), src.c_str(), nullptr);
109     if (ret < 0) {
110         HILOGE("Failed to stat srcPath");
111         return ret;
112     }
113     filesystem::path dstPath(dest);
114     if (filesystem::exists(dstPath)) {
115         int removeRes = RemovePath(dest);
116         if (removeRes != 0) {
117             HILOGE("Failed to remove dest file");
118             return removeRes;
119         }
120     }
121     filesystem::path srcPath(src);
122     std::error_code errCode;
123     if (!filesystem::copy_file(srcPath, dstPath, filesystem::copy_options::overwrite_existing, errCode)) {
124         HILOGE("Failed to copy file, error code: %{public}d", errCode.value());
125         return errCode.value();
126     }
127     std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> utime_req = {
128         new (std::nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
129     if (!utime_req) {
130         HILOGE("Failed to request heap memory.");
131         return ENOMEM;
132     }
133     double atime = static_cast<double>(stat_req->statbuf.st_atim.tv_sec) +
134         static_cast<double>(stat_req->statbuf.st_atim.tv_nsec) / NS;
135     double mtime = static_cast<double>(stat_req->statbuf.st_mtim.tv_sec) +
136         static_cast<double>(stat_req->statbuf.st_mtim.tv_nsec) / NS;
137     ret = uv_fs_utime(nullptr, utime_req.get(), dstPath.c_str(), atime, mtime, nullptr);
138     if (ret < 0) {
139         HILOGE("Failed to utime dstPath");
140         return ret;
141     }
142     return RemovePath(src);
143 }
144 
RenameFile(const string & src,const string & dest,const int mode)145 static int RenameFile(const string &src, const string &dest, const int mode)
146 {
147     filesystem::path dstPath(dest);
148     if (mode == DIRMODE_FILE_THROW_ERR) {
149         if (filesystem::exists(dstPath)) {
150             HILOGE("Failed to move file due to existing destPath with throw err");
151             return EEXIST;
152         }
153     }
154     filesystem::path srcPath(src);
155     std::error_code errCode;
156     filesystem::rename(srcPath, dstPath, errCode);
157     if (errCode.value() == EXDEV) {
158         HILOGE("Failed to rename file due to EXDEV");
159         return CopyAndDeleteFile(src, dest);
160     }
161     return errCode.value();
162 }
163 
FilterFunc(const struct dirent * filename)164 static int32_t FilterFunc(const struct dirent *filename)
165 {
166     if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") {
167         return FILE_DISMATCH;
168     }
169     return FILE_MATCH;
170 }
171 
RenameDir(const string & src,const string & dest,const int mode,vector<struct ErrFiles> & errfiles)172 static int RenameDir(const string &src, const string &dest, const int mode, vector<struct ErrFiles> &errfiles)
173 {
174     filesystem::path destPath(dest);
175     if (!filesystem::exists(destPath)) {
176         filesystem::path srcPath(src);
177         std::error_code errCode;
178         filesystem::rename(srcPath, destPath, errCode);
179         if (errCode.value() == EXDEV) {
180             HILOGE("Failed to rename file due to EXDEV");
181             if (filesystem::create_directory(destPath, errCode)) {
182                 return RecurMoveDir(src, dest, mode, errfiles);
183             } else {
184                 HILOGE("Failed to create directory, error code: %{public}d", errCode.value());
185                 return errCode.value();
186             }
187         }
188         if (errCode.value() != 0) {
189             HILOGE("Failed to rename file, error code: %{public}d", errCode.value());
190             return errCode.value();
191         }
192     } else {
193         return RecurMoveDir(src, dest, mode, errfiles);
194     }
195     return ERRNO_NOERR;
196 }
197 
198 struct NameListArg {
199     struct dirent** namelist;
200     int num;
201 };
202 
Deleter(struct NameListArg * arg)203 static void Deleter(struct NameListArg *arg)
204 {
205     for (int i = 0; i < arg->num; i++) {
206         free((arg->namelist)[i]);
207         (arg->namelist)[i] = nullptr;
208     }
209     free(arg->namelist);
210 }
211 
RecurMoveDir(const string & srcPath,const string & destPath,const int mode,vector<struct ErrFiles> & errfiles)212 static int RecurMoveDir(const string &srcPath, const string &destPath, const int mode,
213     vector<struct ErrFiles> &errfiles)
214 {
215     unique_ptr<struct NameListArg, decltype(Deleter)*> ptr = {new struct NameListArg, Deleter};
216     if (!ptr) {
217         HILOGE("Failed to request heap memory.");
218         return ENOMEM;
219     }
220     int num = scandir(srcPath.c_str(), &(ptr->namelist), FilterFunc, alphasort);
221     ptr->num = num;
222 
223     for (int i = 0; i < num; i++) {
224         if ((ptr->namelist[i])->d_type == DT_DIR) {
225             string srcTemp = srcPath + '/' + string((ptr->namelist[i])->d_name);
226             string destTemp = destPath + '/' + string((ptr->namelist[i])->d_name);
227             size_t size = errfiles.size();
228             int res = RenameDir(srcTemp, destTemp, mode, errfiles);
229             if (res != ERRNO_NOERR) {
230                 return res;
231             }
232             if (size != errfiles.size()) {
233                 continue;
234             }
235             res = RemovePath(srcTemp);
236             if (res) {
237                 return res;
238             }
239         } else {
240             string src = srcPath + '/' + string((ptr->namelist[i])->d_name);
241             string dest = destPath + '/' + string((ptr->namelist[i])->d_name);
242             int res = RenameFile(src, dest, mode);
243             if (res == EEXIST && mode == DIRMODE_FILE_THROW_ERR) {
244                 errfiles.emplace_back(src, dest);
245                 continue;
246             }
247             if (res != ERRNO_NOERR) {
248                 HILOGE("Failed to rename file for error %{public}d", res);
249                 return res;
250             }
251         }
252     }
253     return ERRNO_NOERR;
254 }
255 
MoveDirFunc(const string & src,const string & dest,const int mode,vector<struct ErrFiles> & errfiles)256 static int MoveDirFunc(const string &src, const string &dest, const int mode, vector<struct ErrFiles> &errfiles)
257 {
258     size_t found = string(src).rfind('/');
259     if (found == std::string::npos) {
260         return EINVAL;
261     }
262     if (access(src.c_str(), W_OK) != 0) {
263         HILOGE("Failed to move src directory due to doesn't exist or hasn't write permission");
264         return errno;
265     }
266     string dirName = string(src).substr(found);
267     string destStr = dest + dirName;
268     auto [destStrExist, destStrEmpty] = JudgeExistAndEmpty(destStr);
269     if (destStrExist && !destStrEmpty) {
270         if (mode == DIRMODE_DIRECTORY_REPLACE) {
271             int removeRes = RmDirectory(destStr);
272             if (removeRes) {
273                 HILOGE("Failed to remove dest directory in DIRMODE_DIRECTORY_REPLACE");
274                 return removeRes;
275             }
276         }
277         if (mode == DIRMODE_DIRECTORY_THROW_ERR) {
278             HILOGE("Failed to move directory in DIRMODE_DIRECTORY_THROW_ERR");
279             return ENOTEMPTY;
280         }
281     }
282     int res = RenameDir(src, destStr, mode, errfiles);
283     if (res == ERRNO_NOERR) {
284         if (mode == DIRMODE_FILE_THROW_ERR && errfiles.size() != 0) {
285             HILOGE("Failed to movedir with some conflicted files");
286             return EEXIST;
287         }
288         int removeRes = RmDirectory(src);
289         if (removeRes) {
290             HILOGE("Failed to remove src directory");
291             return removeRes;
292         }
293     }
294     return res;
295 }
296 
GetErrData(napi_env env,vector<struct ErrFiles> & errfiles)297 static napi_value GetErrData(napi_env env, vector<struct ErrFiles> &errfiles)
298 {
299     napi_value res = nullptr;
300     napi_status status = napi_create_array(env, &res);
301     if (status != napi_ok) {
302         HILOGE("Failed to create array");
303         return nullptr;
304     }
305     for (size_t i = 0; i < errfiles.size(); i++) {
306         NVal obj = NVal::CreateObject(env);
307         obj.AddProp("srcFile", NVal::CreateUTF8String(env, errfiles[i].srcFiles).val_);
308         obj.AddProp("destFile", NVal::CreateUTF8String(env, errfiles[i].destFiles).val_);
309         status = napi_set_element(env, res, i, obj.val_);
310         if (status != napi_ok) {
311             HILOGE("Failed to set element on data");
312             return nullptr;
313         }
314     }
315     return res;
316 }
317 
Sync(napi_env env,napi_callback_info info)318 napi_value MoveDir::Sync(napi_env env, napi_callback_info info)
319 {
320     NFuncArg funcArg(env, info);
321     if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::THREE)) {
322         HILOGE("Number of arguments unmatched");
323         NError(EINVAL).ThrowErr(env);
324         return nullptr;
325     }
326     auto [succ, src, dest, mode] = ParseJsOperand(env, funcArg);
327     if (!succ) {
328         NError(EINVAL).ThrowErr(env);
329         return nullptr;
330     }
331 
332     vector<struct ErrFiles> errfiles = {};
333     int ret = MoveDirFunc(src.get(), dest.get(), mode, errfiles);
334     if (ret == EEXIST && mode == DIRMODE_FILE_THROW_ERR) {
335         NError(ret).ThrowErrAddData(env, EEXIST, GetErrData(env, errfiles));
336         return nullptr;
337     } else if (ret) {
338         NError(ret).ThrowErr(env);
339         return nullptr;
340     }
341     return NVal::CreateUndefined(env).val_;
342 }
343 
344 struct MoveDirArgs {
345     vector<ErrFiles> errfiles;
346     int errNo = 0;
347     ~MoveDirArgs() = default;
348 };
349 
Async(napi_env env,napi_callback_info info)350 napi_value MoveDir::Async(napi_env env, napi_callback_info info)
351 {
352     NFuncArg funcArg(env, info);
353     if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::FOUR)) {
354         HILOGE("Number of arguments unmatched");
355         NError(EINVAL).ThrowErr(env);
356         return nullptr;
357     }
358     auto [succ, src, dest, mode] = ParseJsOperand(env, funcArg);
359     if (!succ) {
360         NError(EINVAL).ThrowErr(env);
361         return nullptr;
362     }
363     auto arg = CreateSharedPtr<MoveDirArgs>();
364     if (arg == nullptr) {
365         HILOGE("Failed to request heap memory.");
366         NError(ENOMEM).ThrowErr(env);
367         return nullptr;
368     }
369     auto cbExec = [srcPath = string(src.get()), destPath = string(dest.get()), mode = mode, arg]() -> NError {
370         arg->errNo = MoveDirFunc(srcPath, destPath, mode, arg->errfiles);
371         if (arg->errNo) {
372             return NError(arg->errNo);
373         }
374         return NError(ERRNO_NOERR);
375     };
376 
377     auto cbComplCallback = [arg, mode = mode](napi_env env, NError err) -> NVal {
378         if (arg->errNo == EEXIST && mode == DIRMODE_FILE_THROW_ERR) {
379             napi_value data = err.GetNapiErr(env);
380             napi_status status = napi_set_named_property(env, data, FILEIO_TAG_ERR_DATA.c_str(),
381                 GetErrData(env, arg->errfiles));
382             if (status != napi_ok) {
383                 HILOGE("Failed to set data property on Error");
384             }
385             return { env, data };
386         } else if (arg->errNo) {
387             return { env, err.GetNapiErr(env) };
388         }
389         return NVal::CreateUndefined(env);
390     };
391 
392     NVal thisVar(env, funcArg.GetThisVar());
393     if (funcArg.GetArgc() == NARG_CNT::TWO || (funcArg.GetArgc() == NARG_CNT::THREE &&
394             !NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_function))) {
395         return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_MOVEDIR_NAME, cbExec, cbComplCallback).val_;
396     } else {
397         int cbIdex = ((funcArg.GetArgc() == NARG_CNT::THREE) ? NARG_POS::THIRD : NARG_POS::FOURTH);
398         NVal cb(env, funcArg[cbIdex]);
399         return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_MOVEDIR_NAME, cbExec, cbComplCallback).val_;
400     }
401 }
402 
403 } // ModuleFileIO
404 } // FileManagement
405 } // OHOS