1 /*
2  * Copyright (c) 2024 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 "file_fs_impl.h"
17 #include "n_error.h"
18 #include "securec.h"
19 #include "copy_dir.h"
20 
21 using namespace OHOS;
22 using namespace OHOS::FFI;
23 using namespace OHOS::FileManagement;
24 using namespace OHOS::CJSystemapi;
25 using namespace OHOS::FileManagement::LibN;
26 
27 namespace {
28 
29 static int RecurCopyDir(const std::string &srcPath, const std::string &destPath, const int mode,
30     std::vector<struct ConflictFiles> &errfiles);
31 
MakeDir(const std::string & path)32 static int MakeDir(const std::string &path)
33 {
34     std::filesystem::path destDir(path);
35     std::error_code errCode;
36     if (!std::filesystem::create_directory(destDir, errCode)) {
37         LOGE("Failed to create directory, error code: %{public}d", errCode.value());
38         return errCode.value();
39     }
40     return OHOS::FileManagement::LibN::ERRNO_NOERR;
41 }
42 
43 struct NameList {
44     struct dirent** namelist = { nullptr };
45     int direntNum = 0;
46 };
47 
RemoveFile(const std::string & destPath)48 static int RemoveFile(const std::string &destPath)
49 {
50     std::filesystem::path destFile(destPath);
51     std::error_code errCode;
52     if (!std::filesystem::remove(destFile, errCode)) {
53         LOGE("Failed to remove file with path, error code: %{public}d", errCode.value());
54         return errCode.value();
55     }
56     return OHOS::FileManagement::LibN::ERRNO_NOERR;
57 }
58 
Deleter(struct NameList * arg)59 static void Deleter(struct NameList *arg)
60 {
61     for (int i = 0; i < arg->direntNum; i++) {
62         free((arg->namelist)[i]);
63         (arg->namelist)[i] = nullptr;
64     }
65     free(arg->namelist);
66     arg->namelist = nullptr;
67     delete arg;
68     arg = nullptr;
69 }
70 
CopyFile(const std::string & src,const std::string & dest,const int mode)71 static int CopyFile(const std::string &src, const std::string &dest, const int mode)
72 {
73     std::filesystem::path dstPath(dest);
74     if (std::filesystem::exists(dstPath)) {
75         int ret = (mode == DIRMODE_FILE_COPY_THROW_ERR) ? EEXIST : RemoveFile(dest);
76         if (ret) {
77             LOGE("Failed to copy file due to existing destPath with throw err");
78             return ret;
79         }
80     }
81     std::filesystem::path srcPath(src);
82     std::error_code errCode;
83     if (!std::filesystem::copy_file(srcPath, dstPath, std::filesystem::copy_options::overwrite_existing, errCode)) {
84         LOGE("Failed to copy file, error code: %{public}d", errCode.value());
85         return errCode.value();
86     }
87     return OHOS::FileManagement::LibN::ERRNO_NOERR;
88 }
89 
CopySubDir(const std::string & srcPath,const std::string & destPath,const int mode,std::vector<struct ConflictFiles> & errfiles)90 static int CopySubDir(const std::string &srcPath, const std::string &destPath, const int mode,
91     std::vector<struct ConflictFiles> &errfiles)
92 {
93     if (!std::filesystem::exists(destPath)) {
94         int res = MakeDir(destPath);
95         if (res != OHOS::FileManagement::LibN::ERRNO_NOERR) {
96             LOGE("Failed to mkdir");
97             return res;
98         }
99     }
100     return RecurCopyDir(srcPath, destPath, mode, errfiles);
101 }
102 
FilterFunc(const struct dirent * filename)103 static int FilterFunc(const struct dirent *filename)
104 {
105     if (std::string_view(filename->d_name) == "." || std::string_view(filename->d_name) == "..") {
106         return DISMATCH;
107     }
108     return MATCH;
109 }
110 
RecurCopyDir(const std::string & srcPath,const std::string & destPath,const int mode,std::vector<struct ConflictFiles> & errfiles)111 static int RecurCopyDir(const std::string &srcPath, const std::string &destPath, const int mode,
112     std::vector<struct ConflictFiles> &errfiles)
113 {
114     std::unique_ptr<struct NameList, decltype(Deleter)*> pNameList = {new (std::nothrow) struct NameList, Deleter};
115     if (pNameList == nullptr) {
116         LOGE("Failed to request heap memory.");
117         return ENOMEM;
118     }
119     int num = scandir(srcPath.c_str(), &(pNameList->namelist), FilterFunc, alphasort);
120     pNameList->direntNum = num;
121 
122     for (int i = 0; i < num; i++) {
123         if ((pNameList->namelist[i])->d_type == DT_DIR) {
124             std::string srcTemp = srcPath + '/' + std::string((pNameList->namelist[i])->d_name);
125             std::string destTemp = destPath + '/' + std::string((pNameList->namelist[i])->d_name);
126             LOGI("srcTemp %{public}s from", srcTemp.c_str());
127             LOGI("destTemp %{public}s to", destTemp.c_str());
128             int res = CopySubDir(srcTemp, destTemp, mode, errfiles);
129             if (res == OHOS::FileManagement::LibN::ERRNO_NOERR) {
130                 continue;
131             }
132             return res;
133         } else {
134             LOGI("srcPath %{public}s from", srcPath.c_str());
135             LOGI("destPath %{public}s to", destPath.c_str());
136             std::string src = srcPath + '/' + std::string((pNameList->namelist[i])->d_name);
137             std::string dest = destPath + '/' + std::string((pNameList->namelist[i])->d_name);
138             LOGI("CopyFile %{public}s from", src.c_str());
139             LOGI("CopyFile %{public}s to", dest.c_str());
140             int res = CopyFile(src, dest, mode);
141             if (res == EEXIST) {
142                 errfiles.emplace_back(src, dest);
143                 continue;
144             } else if (res == OHOS::FileManagement::LibN::ERRNO_NOERR) {
145                 continue;
146             } else {
147                 LOGE("Failed to copy file for error %{public}d", res);
148                 return res;
149             }
150         }
151     }
152     return OHOS::FileManagement::LibN::ERRNO_NOERR;
153 }
154 
AllowToCopy(const std::string & src,const std::string & dest)155 static bool AllowToCopy(const std::string& src, const std::string& dest)
156 {
157     if (dest.find(src) == 0 || std::filesystem::path(src).parent_path() == dest) {
158         return false;
159     }
160     return true;
161 }
162 
CopyDirFunc(const std::string & src,const std::string & dest,const int mode,std::vector<struct ConflictFiles> & errfiles)163 static int CopyDirFunc(const std::string &src, const std::string &dest, const int mode,
164     std::vector<struct ConflictFiles> &errfiles)
165 {
166     size_t found = std::string(src).rfind('/');
167     if (found == std::string::npos) {
168         LOGE("CopyDirFunc EINVAL");
169         return EINVAL;
170     }
171     std::string dirName = std::string(src).substr(found);
172     std::string destStr = dest + dirName;
173     LOGI("destStr: %{public}s", destStr.c_str());
174     if (!std::filesystem::exists(destStr)) {
175         int res = MakeDir(destStr);
176         if (res != OHOS::FileManagement::LibN::ERRNO_NOERR) {
177             LOGE("Failed to mkdir");
178             return res;
179         }
180     }
181     int res = RecurCopyDir(src, destStr, mode, errfiles);
182     if (!errfiles.empty() && res == OHOS::FileManagement::LibN::ERRNO_NOERR) {
183         LOGE("CopyDirFunc EEXIST");
184         return EEXIST;
185     }
186     return res;
187 }
188 
VectorToCConflict(std::vector<struct ConflictFiles> & errfiles)189 static CConflictFiles* VectorToCConflict(std::vector<struct ConflictFiles> &errfiles)
190 {
191     CConflictFiles* result = new(std::nothrow) CConflictFiles[errfiles.size()];
192     if (result == nullptr) {
193         return nullptr;
194     }
195     size_t temp = 0;
196     for (size_t i = 0; i < errfiles.size(); i++) {
197         size_t srcFilesLen = errfiles[i].srcFiles.length() + 1;
198         result[i].srcFiles = new(std::nothrow) char[srcFilesLen];
199         if (result[i].srcFiles == nullptr) {
200             break;
201         }
202         if (strcpy_s(result[i].srcFiles, srcFilesLen, errfiles[i].srcFiles.c_str()) != 0) {
203             delete[] result[i].srcFiles;
204             result[i].srcFiles = nullptr;
205             break;
206         }
207         size_t destFilesLen = errfiles[i].destFiles.length() + 1;
208         result[i].destFiles = new(std::nothrow) char[destFilesLen];
209         if (result[i].destFiles == nullptr) {
210             delete[] result[i].srcFiles;
211             result[i].srcFiles = nullptr;
212             break;
213         }
214         if (strcpy_s(result[i].destFiles, destFilesLen, errfiles[i].destFiles.c_str()) != 0) {
215             delete[] result[i].srcFiles;
216             delete[] result[i].destFiles;
217 
218             result[i].srcFiles = nullptr;
219             result[i].destFiles = nullptr;
220             break;
221         }
222         temp++;
223     }
224     if (temp != errfiles.size()) {
225         for (size_t j = temp; j > 0; j--) {
226             delete[] result[j - 1].srcFiles;
227             delete[] result[j - 1].destFiles;
228 
229             result[j - 1].srcFiles = nullptr;
230             result[j - 1].destFiles = nullptr;
231         }
232         delete[] result;
233         return nullptr;
234     }
235     return result;
236 }
237 
238 }
239 
240 namespace OHOS {
241 namespace CJSystemapi {
242 
CopyDir(const std::string & src,const std::string & dest,int mode)243 RetDataCArrConflictFiles CopyDirImpl::CopyDir(const std::string& src, const std::string& dest, int mode)
244 {
245     LOGI("FS_TEST:: FileFsImpl::CopyDir start");
246     RetDataCArrConflictFiles ret = { .code = EINVAL, .data = { .head = nullptr, .size = 0 } };
247     if (!std::filesystem::is_directory(std::filesystem::status(dest))) {
248         LOGE("Invalid dest");
249         return ret;
250     }
251     if (!AllowToCopy(src, dest)) {
252         LOGE("Failed to copy file");
253         return ret;
254     }
255     if (mode < COPYMODE_MIN || mode > COPYMODE_MAX) {
256         LOGE("Invalid mode");
257         return ret;
258     }
259     LOGI("FS_TEST:: FileFsImpl::Copy parameter check passed");
260     std::vector<struct ConflictFiles> errfiles = {};
261     ret.code = CopyDirFunc(src, dest, mode, errfiles);
262     ret.data.size = (int64_t)errfiles.size();
263     ret.data.head = VectorToCConflict(errfiles);
264     return ret;
265 }
266 
267 }
268 } // namespace OHOS::CJSystemapi