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