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 21using namespace OHOS; 22using namespace OHOS::FFI; 23using namespace OHOS::FileManagement; 24using namespace OHOS::CJSystemapi; 25using namespace OHOS::FileManagement::LibN; 26 27namespace { 28 29static int RecurCopyDir(const std::string &srcPath, const std::string &destPath, const int mode, 30 std::vector<struct ConflictFiles> &errfiles); 31 32static 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 43struct NameList { 44 struct dirent** namelist = { nullptr }; 45 int direntNum = 0; 46}; 47 48static 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 59static 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 71static 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 90static 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 103static 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 111static 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 155static 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 163static 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 189static 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 240namespace OHOS { 241namespace CJSystemapi { 242 243RetDataCArrConflictFiles 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