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 <cstring>
17 #include <dirent.h>
18 #include <iostream>
19 #include <memory>
20 #include <sstream>
21 #include <string_view>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 
25 #include "filemgmt_libn.h"
26 #include "hilog_wrapper.h"
27 
28 namespace OHOS::FileManagement {
29 
30 using namespace FileManagement::LibN;
31 using namespace std;
32 
33 constexpr int DIR_DEFAULT_PERM = 0770;
34 constexpr int FILTER_MATCH = 1;
35 constexpr int FILTER_DISMATCH = 0;
36 constexpr int MODE_FORCE_MOVE = 0;
37 constexpr uint64_t TIME_CONVERT_BASE = 1000000000;
38 constexpr int SECOND_TO_MILLISECOND = 1000;
39 
40 struct NameListArg {
41     struct dirent** namelist = { nullptr };
42     int direntNum = 0;
43 };
44 
45 struct StatEntity {
46     uv_stat_t stat_;
47 };
48 
Deleter(struct NameListArg *arg)49 static void Deleter(struct NameListArg *arg)
50 {
51     if (arg == nullptr) {
52         HILOG_ERROR("invalid argument");
53         return;
54     }
55     for (int i = 0; i < arg->direntNum; i++) {
56         free((arg->namelist)[i]);
57         (arg->namelist)[i] = nullptr;
58     }
59     free(arg->namelist);
60     delete arg;
61     arg = nullptr;
62 }
63 
FilterFunc(const struct dirent *filename)64 static int32_t FilterFunc(const struct dirent *filename)
65 {
66     if (filename == nullptr) {
67         return FILTER_DISMATCH;
68     }
69     if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") {
70         return FILTER_DISMATCH;
71     }
72     return FILTER_MATCH;
73 }
74 
fs_req_cleanup(uv_fs_t* req)75 static void fs_req_cleanup(uv_fs_t* req)
76 {
77     uv_fs_req_cleanup(req);
78     if (req) {
79         delete req;
80         req = nullptr;
81     }
82 }
83 
CheckFsStatByPath(const string &path, uv_fs_t* req)84 static int CheckFsStatByPath(const string &path, uv_fs_t* req)
85 {
86     int ret = uv_fs_stat(nullptr, req, path.c_str(), nullptr);
87     if (ret < 0) {
88         HILOG_ERROR("Failed to stat file with path");
89         return ret;
90     }
91     return ERRNO_NOERR;
92 }
93 
GetStat(const string &path, StatEntity &statEntity)94 static bool GetStat(const string &path, StatEntity &statEntity)
95 {
96     unique_ptr<uv_fs_t, decltype(fs_req_cleanup)*> stat_req = {
97         new (nothrow) uv_fs_t, fs_req_cleanup };
98     if (!stat_req) {
99         HILOG_ERROR("Failed to request heap memory.");
100         return false;
101     }
102     auto err = CheckFsStatByPath(path, stat_req.get());
103     if (!err) {
104         statEntity = StatEntity { stat_req->statbuf };
105         return true;
106     }
107     return false;
108 }
109 
CheckDir(const string &path)110 static bool CheckDir(const string &path)
111 {
112     struct stat fileInformation;
113     if (stat(path.c_str(), &fileInformation) == 0 && (fileInformation.st_mode & S_IFDIR)) {
114         return true;
115     } else {
116         HILOG_ERROR("Failed to stat file");
117     }
118     return false;
119 }
120 
Access(const string &path)121 static tuple<bool, int> Access(const string &path)
122 {
123     unique_ptr<uv_fs_t, decltype(fs_req_cleanup)*> access_req = { new uv_fs_t, fs_req_cleanup };
124     if (!access_req) {
125         HILOG_ERROR("Failed to request heap memory.");
126         return {false, ENOMEM};
127     }
128     int ret = uv_fs_access(nullptr, access_req.get(), path.c_str(), 0, nullptr);
129     if (ret < 0 && (string_view(uv_err_name(ret)) != "ENOENT")) {
130         HILOG_ERROR("Failed to access file by path");
131         return {false, ret};
132     }
133     bool isExist = (ret == 0);
134     return {isExist, ERRNO_NOERR};
135 }
136 
Mkdir(const string &path)137 static bool Mkdir(const string &path)
138 {
139     unique_ptr<uv_fs_t, decltype(fs_req_cleanup)*> mkdir_req = { new uv_fs_t, fs_req_cleanup };
140     if (!mkdir_req) {
141         HILOG_ERROR("Failed to request heap memory.");
142         return false;
143     }
144     int ret = uv_fs_mkdir(nullptr, mkdir_req.get(), path.c_str(), DIR_DEFAULT_PERM, nullptr);
145     if (ret < 0) {
146         HILOG_ERROR("Failed to create directory");
147         return false;
148     }
149     if (ret == 0) {
150         return true;
151     }
152     return false;
153 }
154 
CopyAndDeleteFile(const string &src, const string &dest)155 static int CopyAndDeleteFile(const string &src, const string &dest)
156 {
157     // 获取源文件时间
158     StatEntity statEntity;
159     if (!GetStat(src, statEntity)) {
160         HILOG_ERROR("Failed to get file stat.");
161         return EINVAL;
162     }
163     // 拼接秒数和纳秒数
164     uint64_t acTimeLong =  static_cast<uint64_t>(statEntity.stat_.st_atim.tv_sec * TIME_CONVERT_BASE +
165         statEntity.stat_.st_atim.tv_nsec);
166     uint64_t modTimeLong = static_cast<uint64_t>(statEntity.stat_.st_mtim.tv_sec * TIME_CONVERT_BASE +
167         statEntity.stat_.st_mtim.tv_nsec);
168     double acTime = static_cast<long double>(acTimeLong) / TIME_CONVERT_BASE;
169     double modTime = static_cast<long double>(modTimeLong) / TIME_CONVERT_BASE;
170 
171     int ret = 0;
172     uv_fs_t copyfile_req;
173     ret = uv_fs_copyfile(nullptr, &copyfile_req, src.c_str(), dest.c_str(), MODE_FORCE_MOVE, nullptr);
174     uv_fs_req_cleanup(&copyfile_req);
175 
176     // 设置目标文件时间
177     uv_fs_t utime_req;
178     uv_fs_utime(nullptr, &utime_req, dest.c_str(), acTime, modTime, nullptr);
179     uv_fs_req_cleanup(&utime_req);
180 
181     if (ret < 0) {
182         HILOG_ERROR("Failed to move file using copyfile interface.");
183         return ret;
184     }
185     uv_fs_t unlink_req;
186     ret = uv_fs_unlink(nullptr, &unlink_req, src.c_str(), nullptr);
187     if (ret < 0) {
188         HILOG_ERROR("Failed to unlink src file");
189         ret = uv_fs_unlink(nullptr, &unlink_req, dest.c_str(), nullptr);
190         if (ret < 0) {
191             HILOG_ERROR("Failed to unlink dest file");
192         }
193         uv_fs_req_cleanup(&unlink_req);
194         return ret;
195     }
196     uv_fs_req_cleanup(&unlink_req);
197     return ERRNO_NOERR;
198 }
199 
RenameFile(const string &src, const string &dest)200 static int RenameFile(const string &src, const string &dest)
201 {
202     unique_ptr<uv_fs_t, decltype(fs_req_cleanup)*> rename_req = {
203         new uv_fs_t, fs_req_cleanup };
204     if (!rename_req) {
205         HILOG_ERROR("RenameFile: Failed to request heap memory.");
206         return false;
207     }
208     int ret = uv_fs_rename(nullptr, rename_req.get(), src.c_str(), dest.c_str(), nullptr);
209     if (ret < 0 && (string_view(uv_err_name(ret)) == "EXDEV")) {
210         HILOG_DEBUG("RenameFile: using CopyAndDeleteFile.");
211         return CopyAndDeleteFile(src, dest);
212     }
213     if (ret < 0) {
214         HILOG_ERROR("RenameFile: Failed to move file using rename syscall ret %{public}d ", ret);
215         return ret;
216     }
217     return ERRNO_NOERR;
218 }
219 
RmDirent(const string &fpath)220 static NError RmDirent(const string &fpath)
221 {
222     unique_ptr<uv_fs_t, decltype(fs_req_cleanup)*> scandir_req = {
223         new (nothrow) uv_fs_t, fs_req_cleanup };
224     if (!scandir_req) {
225         HILOG_ERROR("Failed to request heap memory.");
226         return NError(ENOMEM);
227     }
228     int ret = 0;
229     ret = uv_fs_scandir(nullptr, scandir_req.get(), fpath.c_str(), 0, nullptr);
230     if (ret < 0) {
231         HILOG_ERROR("Failed to scandir, ret: %{public}d", ret);
232         return NError(ret);
233     }
234     uv_dirent_t dent;
235     while (uv_fs_scandir_next(scandir_req.get(), &dent) != UV_EOF) {
236         string filePath = fpath + "/" + string(dent.name);
237         if (dent.type == UV_DIRENT_FILE) {
238             unique_ptr<uv_fs_t, decltype(fs_req_cleanup)*> unlink_req = {
239                 new (nothrow) uv_fs_t, fs_req_cleanup };
240             if (!unlink_req) {
241                 HILOG_ERROR("Failed to request heap memory.");
242                 return NError(ENOMEM);
243             }
244             ret = uv_fs_unlink(nullptr, unlink_req.get(), filePath.c_str(), nullptr);
245             if (ret < 0) {
246                 HILOG_ERROR("Failed to unlink file, ret: %{public}d", ret);
247                 return NError(ret);
248             }
249         } else if (dent.type == UV_DIRENT_DIR) {
250             auto rmDirentRes = RmDirent(filePath);
251             if (rmDirentRes) {
252                 return rmDirentRes;
253             }
254         }
255     }
256     unique_ptr<uv_fs_t, decltype(fs_req_cleanup)*> rmdir_req = {
257         new (nothrow) uv_fs_t, fs_req_cleanup };
258     if (!rmdir_req) {
259         HILOG_ERROR("Failed to request heap memory.");
260         return NError(ENOMEM);
261     }
262     ret = uv_fs_rmdir(nullptr, rmdir_req.get(), fpath.c_str(), nullptr);
263     if (ret < 0) {
264         HILOG_ERROR("Failed to rmdir empty dir, ret: %{public}d", ret);
265         return NError(ret);
266     }
267     return NError(ERRNO_NOERR);
268 }
269 
ScanDir(const string &path)270 static int ScanDir(const string &path)
271 {
272     unique_ptr<struct NameListArg, decltype(Deleter)*> pNameList = { new (nothrow) struct NameListArg, Deleter };
273     if (!pNameList) {
274         HILOG_ERROR("Failed to request heap memory.");
275         return ENOMEM;
276     }
277     HILOG_INFO("RecursiveFunc: scandir path = %{public}s", path.c_str());
278     return scandir(path.c_str(), &(pNameList->namelist), FilterFunc, alphasort);
279 }
280 } // OHOS::FileManagement