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 "file_trash_n_exporter.h"
17
18 #include <ctime>
19
20 #include "access_token.h"
21 #include "accesstoken_kit.h"
22 #include "file_access_framework_errno.h"
23 #include "file_info.h"
24 #include "file_uri.h"
25 #include "file_util.h"
26 #include "ipc_skeleton.h"
27
28 namespace OHOS {
29 namespace Trash {
30 namespace {
31 const std::string FILE_ACCESS_PERMISSION = "ohos.permission.FILE_ACCESS_MANAGER";
32 }
33
34 using namespace FileManagement::LibN;
35 using namespace FileManagement;
36 using namespace std;
37
CheckCallingPermission(const std::string & permission)38 static bool CheckCallingPermission(const std::string &permission)
39 {
40 Security::AccessToken::AccessTokenID tokenCaller = IPCSkeleton::GetCallingTokenID();
41 int res = Security::AccessToken::AccessTokenKit::VerifyAccessToken(tokenCaller, permission);
42 if (res != Security::AccessToken::PermissionState::PERMISSION_GRANTED) {
43 HILOG_ERROR("FileTrashNExporter::CheckCallingPermission have no fileAccess permission");
44 return false;
45 }
46 return true;
47 }
48
GetRealPath(string & path)49 static bool GetRealPath(string &path)
50 {
51 unique_ptr<char[]> absPath = make_unique<char[]>(PATH_MAX + 1);
52 if (realpath(path.c_str(), absPath.get()) == nullptr) {
53 return false;
54 }
55 path = absPath.get();
56 return true;
57 }
58
GetTimeSlotFromPath(const string & path)59 static string GetTimeSlotFromPath(const string &path)
60 {
61 int slashSize = 1;
62 // 获取时间戳
63 size_t trashPathPrefixPos = path.find(TRASH_PATH);
64 size_t expectTimeSlotStartPos = trashPathPrefixPos + TRASH_PATH.length() + slashSize;
65 if (expectTimeSlotStartPos >= path.length()) {
66 return "";
67 }
68 string realFilePathWithTime = path.substr(trashPathPrefixPos + TRASH_PATH.length() + slashSize);
69 // 获取时间戳目录位置
70 size_t trashPathWithTimePrefixPos = realFilePathWithTime.find_first_of("/");
71 if (trashPathWithTimePrefixPos == string::npos) {
72 HILOG_ERROR("GetTimeSlotFromPath: Invalid path = %{public}s", path.c_str());
73 return "";
74 }
75 string timeSlot = realFilePathWithTime.substr(0, trashPathWithTimePrefixPos);
76 HILOG_DEBUG("GetTimeSlotFromPath: timeSlot = %{public}s", timeSlot.c_str());
77 return timeSlot;
78 }
79
RecursiveFunc(const string & path,vector<string> & dirents)80 static int RecursiveFunc(const string &path, vector<string> &dirents)
81 {
82 unique_ptr<struct NameListArg, decltype(Deleter)*> pNameList = { new (nothrow) struct NameListArg, Deleter };
83 if (!pNameList) {
84 HILOG_ERROR("Failed to request heap memory.");
85 return ENOMEM;
86 }
87 HILOG_DEBUG("RecursiveFunc: scandir path = %{public}s", path.c_str());
88 int num = scandir(path.c_str(), &(pNameList->namelist), FilterFunc, alphasort);
89 if (num < 0) {
90 HILOG_ERROR("RecursiveFunc: Failed to scan dir");
91 return errno;
92 }
93 pNameList->direntNum = num;
94 string pathInRecur = path;
95 for (int i = 0; i < num; i++) {
96 if ((*(pNameList->namelist[i])).d_type == DT_REG) {
97 dirents.emplace_back(path + '/' + pNameList->namelist[i]->d_name);
98 } else if ((*(pNameList->namelist[i])).d_type == DT_DIR) {
99 string pathTemp = pathInRecur;
100 pathInRecur += '/' + string((*(pNameList->namelist[i])).d_name);
101 // check if path include TRASH_SUB_DIR + "/", need to add it into dirents
102 HILOG_DEBUG("RecursiveFunc: pathTemp = %{public}s", pathTemp.c_str());
103 string timeSlot = GetTimeSlotFromPath(pathTemp);
104 if (!timeSlot.empty() && pathInRecur.rfind(TRASH_SUB_DIR + timeSlot + "/") != string::npos) {
105 // Only filter previous dir is TRASH_SUB_DIR
106 dirents.emplace_back(pathInRecur);
107 }
108 int ret = RecursiveFunc(pathInRecur, dirents);
109 if (ret != ERRNO_NOERR) {
110 HILOG_ERROR("RecursiveFunc: Failed to recursive get all dirents for %{public}d", ret);
111 return ret;
112 }
113 pathInRecur = pathTemp;
114 }
115 }
116 return ERRNO_NOERR;
117 }
118
CreateObjectArray(napi_env env,vector<FileInfo> result)119 static napi_value CreateObjectArray(napi_env env, vector<FileInfo> result)
120 {
121 uint32_t status = napi_ok;
122 napi_value fileInfoResultArray = nullptr;
123 status = napi_create_array_with_length(env, result.size(), &fileInfoResultArray);
124 if (status != napi_ok) {
125 HILOG_ERROR("Create napi array fail");
126 return nullptr;
127 }
128
129 for (size_t i = 0; i < result.size(); i++) {
130 FileInfo &tmpResult = result.at(i);
131 napi_value resultVal;
132 status |= napi_create_object(env, &resultVal);
133 napi_value tmpVal;
134 status |= napi_create_string_utf8(env, tmpResult.uri.c_str(), tmpResult.uri.length(), &tmpVal);
135 status |= napi_set_named_property(env, resultVal, "uri", tmpVal);
136 status |= napi_create_string_utf8(env, tmpResult.srcPath.c_str(), tmpResult.srcPath.length(), &tmpVal);
137 status |= napi_set_named_property(env, resultVal, "srcPath", tmpVal);
138 status |= napi_create_string_utf8(env, tmpResult.fileName.c_str(), tmpResult.fileName.length(), &tmpVal);
139 status |= napi_set_named_property(env, resultVal, "fileName", tmpVal);
140 status |= napi_create_int64(env, tmpResult.mode, &tmpVal);
141 status |= napi_set_named_property(env, resultVal, "mode", tmpVal);
142 status |= napi_create_int64(env, tmpResult.mode, &tmpVal);
143 status |= napi_set_named_property(env, resultVal, "mode", tmpVal);
144 status |= napi_create_int64(env, tmpResult.size, &tmpVal);
145 status |= napi_set_named_property(env, resultVal, "size", tmpVal);
146 status |= napi_create_int64(env, tmpResult.mtime, &tmpVal);
147 status |= napi_set_named_property(env, resultVal, "mtime", tmpVal);
148 status |= napi_create_int64(env, tmpResult.ctime, &tmpVal);
149 status |= napi_set_named_property(env, resultVal, "ctime", tmpVal);
150 status |= napi_set_element(env, fileInfoResultArray, i, resultVal);
151 if (status != napi_ok) {
152 HILOG_ERROR("Create CopyResult object error");
153 return nullptr;
154 }
155 }
156 return fileInfoResultArray;
157 }
158
FindSourceFilePath(const string & path)159 static string FindSourceFilePath(const string &path)
160 {
161 HILOG_INFO("FindSourceFilePath: curFilePath = %{public}s", path.c_str());
162 size_t slashSize = 1;
163 // 获取/trash目录位置
164 size_t trashPathPrefixPos = path.find(TRASH_PATH);
165 if (trashPathPrefixPos == string::npos) {
166 HILOG_ERROR("FindSourceFilePath: Invalid Path No Trash Path");
167 return "";
168 }
169 size_t timeSLotStartPos = trashPathPrefixPos + TRASH_PATH.length() + slashSize;
170 string realFilePathWithTime = path.substr(timeSLotStartPos);
171 // 获取时间戳目录位置
172 size_t trashPathWithTimePrefixPos = realFilePathWithTime.find_first_of("/");
173 if (trashPathWithTimePrefixPos == string::npos) {
174 HILOG_ERROR("FindSourceFilePath: : Invalid Path No timestamp");
175 return "";
176 }
177 // 获取时间戳
178 string timeSlot = realFilePathWithTime.substr(0, trashPathWithTimePrefixPos);
179 string realFilePath = realFilePathWithTime.substr(trashPathWithTimePrefixPos + slashSize);
180 size_t pos = realFilePath.rfind(TRASH_SUB_DIR + timeSlot + "/");
181 if (pos == string::npos) {
182 HILOG_ERROR("FindSourceFilePath: : Invalid Path No Trash Sub Path");
183 return "";
184 }
185 string realFilePathPrefix = realFilePath.substr(0, pos);
186 string realFileName = realFilePath.substr(pos + TRASH_SUB_DIR.length() +
187 timeSlot.length() + slashSize);
188 realFilePath = "/" + realFilePathPrefix + realFileName;
189 HILOG_INFO("FindSourceFilePath: realFilePath After = %{public}s", realFilePath.c_str());
190 return realFilePath;
191 }
192
Mkdirs(const string & path,bool isDir,string & newRecoveredPath)193 static bool Mkdirs(const string &path, bool isDir, string &newRecoveredPath)
194 {
195 HILOG_INFO("Mkdirs: path = %{public}s", path.c_str());
196 string recoveredPath = path;
197 string folderName = "";
198 size_t lastPos = 0;
199 if (recoveredPath.length() == 0) {
200 return false;
201 }
202 // if argument uri is dir, then add "/"
203 if (isDir) {
204 recoveredPath = recoveredPath + "/";
205 }
206
207 for (size_t i = 1; i < recoveredPath.length(); ++i) {
208 if (recoveredPath[i] != '/') {
209 continue;
210 }
211 recoveredPath[i] = '\0';
212 folderName = recoveredPath.substr(lastPos + 1, i);
213 lastPos = i;
214 auto [isExist, ret] = Access(recoveredPath);
215 if (!isExist && !Mkdir(recoveredPath)) {
216 HILOG_ERROR("Mkdirs fail for %{public}s ", recoveredPath.c_str());
217 return false;
218 }
219 recoveredPath[i] = '/';
220 }
221 return true;
222 }
223
GenerateNewFileNameWithSuffix(const string & destFile,int32_t distinctSuffixIndex,const string & newPrefix,const string & newSuffix)224 static string GenerateNewFileNameWithSuffix(const string &destFile, int32_t distinctSuffixIndex,
225 const string &newPrefix, const string &newSuffix)
226 {
227 // 处理存在后缀名的文件
228 auto [isExist, ret] = Access(destFile);
229 if (isExist) {
230 distinctSuffixIndex += 1;
231 string newDestFile = newPrefix + to_string(distinctSuffixIndex) + newSuffix;
232 return GenerateNewFileNameWithSuffix(newDestFile, distinctSuffixIndex, newPrefix, newSuffix);
233 } else if (!isExist && (ret == ERRNO_NOERR)) {
234 HILOG_DEBUG("GenerateNewFileNameWithSuffix: destFile = %{public}s", destFile.c_str());
235 return destFile;
236 }
237 return "";
238 }
239
GenerateNewFileNameNoSuffix(const string & destFile,int32_t distinctSuffixIndex,const string & newPrefix)240 static string GenerateNewFileNameNoSuffix(const string &destFile, int32_t distinctSuffixIndex, const string &newPrefix)
241 {
242 // 处理不存在后缀名的文件
243 auto [isExist, ret] = Access(destFile);
244 if (isExist) {
245 distinctSuffixIndex += 1;
246 string newDestFile = newPrefix + to_string(distinctSuffixIndex);
247 return GenerateNewFileNameNoSuffix(newDestFile, distinctSuffixIndex, newPrefix);
248 } else if (!isExist && (ret == ERRNO_NOERR)) {
249 HILOG_DEBUG("GenerateNewFileNameNoSuffix: destFile = %{public}s", destFile.c_str());
250 return destFile;
251 }
252 return "";
253 }
254
MoveFile(const string & srcFile,const string & destFile)255 static bool MoveFile(const string &srcFile, const string &destFile)
256 {
257 // 判断目的文件是否存在
258 auto [isExist, ret] = Access(destFile);
259 if (isExist) {
260 // 存在同名文件,需要加上数字后缀区分
261 // 获取文件前一级目录'/' 位置,从这个位置出发寻找文件后缀分隔符'.'
262 size_t slashPos = destFile.find_last_of("/");
263 if (slashPos == string::npos) {
264 HILOG_ERROR("MoveFile: : Invalid Path");
265 return false;
266 }
267 size_t suffixPos = destFile.find_first_of('.', slashPos);
268 HILOG_DEBUG("MoveFile: slashPos = %{public}zu", slashPos);
269 HILOG_DEBUG("MoveFile: suffixPos = %{public}zu", suffixPos);
270 string newDestFile = destFile;
271 int32_t distinctSuffixIndex = 1;
272 if (suffixPos == string::npos) {
273 string newPrefix = newDestFile + " ";
274 newDestFile = newPrefix + to_string(distinctSuffixIndex);
275 newDestFile = GenerateNewFileNameNoSuffix(newDestFile, distinctSuffixIndex, newPrefix);
276 } else {
277 string newPrefix = destFile.substr(0, suffixPos) + " ";
278 string newSuffix = destFile.substr(suffixPos);
279 HILOG_DEBUG("MoveFile: newPrefix = %{public}s", newPrefix.c_str());
280 HILOG_DEBUG("MoveFile: newSuffix = %{public}s", newSuffix.c_str());
281 // 查看加上数字后缀后文件是否已经存在,若存在,需要重新获取
282 newDestFile = GenerateNewFileNameWithSuffix(newPrefix + to_string(distinctSuffixIndex) + newSuffix,
283 distinctSuffixIndex, newPrefix, newSuffix);
284 }
285 HILOG_INFO("MoveFile: newDestFile = %{public}s", newDestFile.c_str());
286 return RenameFile(srcFile, newDestFile);
287 } else if (!isExist && (ret == ERRNO_NOERR)) {
288 return RenameFile(srcFile, destFile);
289 }
290 HILOG_ERROR("MoveFile: : Invalid Path");
291 return false;
292 }
293
RecurCheckIfOnlyContentInDir(const string & path,size_t trashWithTimePos,const string & trashWithTimePath)294 static string RecurCheckIfOnlyContentInDir(const string &path, size_t trashWithTimePos, const string &trashWithTimePath)
295 {
296 HILOG_INFO("RecurCheckIfOnlyContentInDir: path = %{public}s", path.c_str());
297 size_t slashPos = path.find_last_of("/");
298 if (slashPos <= trashWithTimePos) {
299 HILOG_DEBUG("RecurCheckIfOnlyContentInDir: slashPos = %{public}zu", slashPos);
300 return trashWithTimePath;
301 }
302 string parentPath = path.substr(0, slashPos);
303 HILOG_DEBUG("RecurCheckIfOnlyContentInDir: parentPath = %{public}s", parentPath.c_str());
304 int num = ScanDir(parentPath);
305 HILOG_DEBUG("RecurCheckIfOnlyContentInDir: num = %{public}d", num);
306 if (num > 1) {
307 // 同一时间戳目录下存在多个删除项,则不论是还原后的删除还是彻底删除,仅需删除该项
308 HILOG_DEBUG("RecurCheckIfOnlyContentInDir: find other items in current dir");
309 return path;
310 } else if (num == 1) {
311 // 需要向上一层目录判断
312 return RecurCheckIfOnlyContentInDir(parentPath, trashWithTimePos, trashWithTimePath);
313 } else {
314 HILOG_ERROR("RecurCheckIfOnlyContentInDir: invalid path = %{public}s", path.c_str());
315 }
316 return nullptr;
317 }
318
GetToDeletePath(const string & toDeletePath,napi_env env)319 static string GetToDeletePath(const string &toDeletePath, napi_env env)
320 {
321 HILOG_INFO("GetToDeletePath: toDeletePath = %{public}s", toDeletePath.c_str());
322 // 判断是否为有效回收站路径
323 size_t slashSize = 1;
324 // 获取/Trash目录位置
325 size_t trashPathPrefixPos = toDeletePath.find(TRASH_PATH);
326 if (trashPathPrefixPos == string::npos ||
327 trashPathPrefixPos + TRASH_PATH.length() + slashSize >= toDeletePath.length()) {
328 NError(EINVAL).ThrowErr(env);
329 return nullptr;
330 }
331 string realFilePathWithTime = toDeletePath.substr(trashPathPrefixPos + TRASH_PATH.length() + slashSize);
332 // 获取时间戳目录位置
333 size_t trashPathWithTimePrefixPos = realFilePathWithTime.find_first_of("/");
334 size_t realTimeDirPos = trashPathPrefixPos + TRASH_PATH.length() +
335 slashSize + trashPathWithTimePrefixPos;
336 // 回收站下一级的时间戳目录
337 string trashWithTimePath = toDeletePath.substr(0, realTimeDirPos);
338
339 // 从待删除目录开始向内层遍历父目录,判断父目录是否仅有一个子目录,如果是则继续向前查找,直到时间戳目录为止;
340 // 如果不是仅有一个子目录,则待删除目录即子目录
341 return RecurCheckIfOnlyContentInDir(toDeletePath, realTimeDirPos, trashWithTimePath);
342 }
343
GenerateFileInfoEntities(vector<string> filterDirents)344 static vector<FileInfo> GenerateFileInfoEntities(vector<string> filterDirents)
345 {
346 vector<FileInfo> fileInfoList;
347 for (size_t k = 0; k < filterDirents.size(); k++) {
348 string filterDirent = filterDirents[k];
349 HILOG_INFO("ListFile: After filter dirent = %{public}s", filterDirent.c_str());
350
351 string realFilePath = FindSourceFilePath(filterDirent);
352 HILOG_INFO("ListFile: After filter realFilePath = %{public}s", realFilePath.c_str());
353 size_t lastSlashPos = filterDirent.find_last_of("/");
354 string fileName = filterDirent.substr(lastSlashPos + 1);
355
356 FileInfo fileInfoEntity;
357 fileInfoEntity.uri = URI_PATH_PREFIX + filterDirent;
358 fileInfoEntity.srcPath = URI_PATH_PREFIX + realFilePath;
359 fileInfoEntity.fileName = fileName;
360
361 int32_t mode = SUPPORTS_READ | SUPPORTS_WRITE;
362 StatEntity statEntity;
363 if (GetStat(filterDirent, statEntity)) {
364 bool check = (statEntity.stat_.st_mode & S_IFMT) == S_IFDIR;
365 if (check) {
366 mode |= REPRESENTS_DIR;
367 } else {
368 mode |= REPRESENTS_FILE;
369 }
370 HILOG_DEBUG("ListFile: After filter mode = %{public}d", mode);
371
372 fileInfoEntity.mode = mode;
373 fileInfoEntity.size = static_cast<int64_t>(statEntity.stat_.st_size);
374 fileInfoEntity.mtime = static_cast<int64_t>(statEntity.stat_.st_mtim.tv_sec);
375 fileInfoEntity.ctime = static_cast<int64_t>(statEntity.stat_.st_ctim.tv_sec);
376 }
377 fileInfoList.emplace_back(fileInfoEntity);
378 }
379 return fileInfoList;
380 }
381
ListFile(napi_env env,napi_callback_info info)382 napi_value FileTrashNExporter::ListFile(napi_env env, napi_callback_info info)
383 {
384 if (!CheckCallingPermission(FILE_ACCESS_PERMISSION)) {
385 HILOG_ERROR("permission error");
386 NError(E_PERMISSION).ThrowErr(env);
387 return nullptr;
388 }
389
390 NFuncArg funcArg(env, info);
391 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
392 HILOG_ERROR("Number of arguments unmatched");
393 NError(EINVAL).ThrowErr(env);
394 return nullptr;
395 }
396 vector<string> dirents;
397 unique_ptr<struct NameListArg, decltype(Deleter)*> pNameList = { new (nothrow) struct NameListArg, Deleter };
398 if (!pNameList) {
399 NError(ENOMEM).ThrowErr(env);
400 HILOG_ERROR("Failed to request heap memory.");
401 return nullptr;
402 }
403 int ret = RecursiveFunc(TRASH_PATH, dirents);
404 if (ret != ERRNO_NOERR) {
405 NError(ENOMEM).ThrowErr(env);
406 HILOG_ERROR("ListFile: Failed to recursive all Trash items path");
407 return nullptr;
408 }
409
410 vector<string> filterDirents;
411 size_t slashSize = 1;
412 for (size_t j = 0; j < dirents.size(); j++) {
413 string dirent = dirents[j];
414 HILOG_DEBUG("ListFile: After RecursiveFunc dirent = %{public}s", dirent.c_str());
415
416 string timeSlot = GetTimeSlotFromPath(dirent);
417 if (timeSlot.empty()) {
418 continue;
419 }
420 // Only filter previous dir is TRASH_SUB_DIR
421 size_t pos = dirent.find(TRASH_SUB_DIR + timeSlot + "/");
422 if (pos != string::npos) {
423 string trashSubDir = TRASH_SUB_DIR + timeSlot;
424 if (dirent.find("/", pos + trashSubDir.length() + slashSize) == string::npos) {
425 filterDirents.emplace_back(dirent);
426 }
427 }
428 }
429 vector<FileInfo> fileInfoList = GenerateFileInfoEntities(filterDirents);
430 return CreateObjectArray(env, fileInfoList);
431 }
432
RecoverFile(napi_env env,const string & filePath)433 static napi_value RecoverFile(napi_env env, const string &filePath)
434 {
435 string sourceFilePath = FindSourceFilePath(filePath);
436 HILOG_INFO("RecoverFile: sourceFilePath = %{public}s", sourceFilePath.c_str());
437 string newDestPath = sourceFilePath;
438 if (newDestPath.length() != 0 && Mkdirs(sourceFilePath, false, newDestPath)) {
439 MoveFile(filePath, newDestPath);
440 }
441
442 // 文件已被移动,则如果前一层目录包含其他内容,则直接返回;
443 // 如果不包含,则需要一层层向父目录回退判断对应目录是否需要删除
444 size_t slashPos = filePath.find_last_of("/");
445 string parentPath = filePath.substr(0, slashPos);
446 int num = ScanDir(parentPath);
447 if (num == 0) {
448 auto err = RmDirent(GetToDeletePath(parentPath, env));
449 if (err) {
450 err.ThrowErr(env);
451 return nullptr;
452 }
453 }
454 return NVal::CreateUndefined(env).val_;
455 }
456
RecoverFilePart(vector<string> filePathList,map<string,string> dirPath2UpdatedNameMap)457 static void RecoverFilePart(vector<string> filePathList, map<string, string> dirPath2UpdatedNameMap)
458 {
459 // 处理文件
460 for (size_t j = 0; j < filePathList.size(); j++) {
461 string filePath = filePathList[j];
462 HILOG_INFO("RecoverFilePart: filePath = %{public}s", filePath.c_str());
463 string sourceFilePath = FindSourceFilePath(filePath);
464 HILOG_INFO("RecoverFilePart: sourceFilePath = %{public}s", sourceFilePath.c_str());
465
466 size_t lastSlashPos = sourceFilePath.find_last_of("/");
467 string fileName = sourceFilePath.substr(lastSlashPos + 1);
468 string sourceFilePathOnly = sourceFilePath.substr(0, lastSlashPos);
469 map<string, string>::iterator iter = dirPath2UpdatedNameMap.find(sourceFilePathOnly);
470 if (iter != dirPath2UpdatedNameMap.end()) {
471 sourceFilePath = iter->second + "/" + fileName;
472 }
473 MoveFile(filePath, sourceFilePath);
474 }
475 }
476
FilterDirsNoContains(vector<string> dirPathList)477 static vector<string> FilterDirsNoContains(vector<string> dirPathList)
478 {
479 //先处理目录,仅保留不互相包含的目录(取子目录较深的)
480 vector<string> filterDirPathList;
481 for (size_t j = 0; j < dirPathList.size(); j++) {
482 string dirPath = dirPathList[j];
483 bool isIncluded = false;
484 for (size_t k = 0; k < filterDirPathList.size(); k++) {
485 string filterDirPath = filterDirPathList[k];
486 if (StartsWith(filterDirPath, dirPath)) {
487 isIncluded = true;
488 break;
489 }
490 }
491 if (!isIncluded) {
492 filterDirPathList.emplace_back(dirPath);
493 }
494 }
495 return filterDirPathList;
496 }
497
MakeAndFindUpdateNameDir(vector<string> filterDirPathList)498 static map<string, string> MakeAndFindUpdateNameDir(vector<string> filterDirPathList)
499 {
500 map<string, string> dirPath2UpdatedNameMap;
501 for (size_t j = 0; j < filterDirPathList.size(); j++) {
502 string dirPath = filterDirPathList[j];
503 string sourceFilePath = FindSourceFilePath(dirPath);
504 HILOG_DEBUG("MakeAndFindUpdateNameDir: sourceFilePath = %{public}s", sourceFilePath.c_str());
505 string newDestPath = sourceFilePath;
506 if (Mkdirs(sourceFilePath, true, newDestPath)) {
507 HILOG_DEBUG("MakeAndFindUpdateNameDir: newDestPath = %{public}s", newDestPath.c_str());
508 if (newDestPath != sourceFilePath) {
509 dirPath2UpdatedNameMap.insert(make_pair(sourceFilePath, newDestPath));
510 }
511 }
512 }
513 return dirPath2UpdatedNameMap;
514 }
515
RecoverDir(napi_env env,const string & dirPath)516 static napi_value RecoverDir(napi_env env, const string &dirPath)
517 {
518 vector<string> dirents;
519 unique_ptr<struct NameListArg, decltype(Deleter)*> pNameList = { new (nothrow) struct NameListArg, Deleter };
520 if (!pNameList) {
521 HILOG_ERROR("RecoverDir: Failed to request heap memory.");
522 return nullptr;
523 }
524 int ret = RecursiveFunc(dirPath, dirents);
525 if (ret != ERRNO_NOERR) {
526 HILOG_ERROR("RecoverDir: Failed to Recursive Dir.");
527 return nullptr;
528 }
529 dirents.emplace_back(dirPath);
530
531 // 区分目录和文件
532 vector<string> dirPathList;
533 vector<string> filePathList;
534 for (size_t j = 0; j < dirents.size(); j++) {
535 string dirent = dirents[j];
536 if (CheckDir(dirent)) {
537 dirPathList.emplace_back(dirent);
538 } else {
539 filePathList.emplace_back(dirent);
540 }
541 }
542 // 目录从长到短排序
543 sort(dirPathList.begin(), dirPathList.end(), [&](const string &a, const string &b) {
544 return a.length() > b.length();
545 });
546
547 // 先处理目录,仅保留不互相包含的目录(取子目录较深的)
548 vector<string> filterDirPathList = FilterDirsNoContains(dirPathList);
549 if (filterDirPathList.size() == 0) {
550 HILOG_ERROR("RecoverDir: NO valid dirs found");
551 return nullptr;
552 }
553
554 // 新建目录并获取因为存在同名目录修改过名称的目录
555 map<string, string> dirPath2UpdatedNameMap = MakeAndFindUpdateNameDir(filterDirPathList);
556
557 // 处理文件部分
558 RecoverFilePart(filePathList, dirPath2UpdatedNameMap);
559
560 // 删除目录
561 auto err = RmDirent(GetToDeletePath(dirPath, env));
562 if (err) {
563 err.ThrowErr(env);
564 return nullptr;
565 }
566
567 return NVal::CreateUndefined(env).val_;
568 }
569
Recover(napi_env env,napi_callback_info info)570 napi_value FileTrashNExporter::Recover(napi_env env, napi_callback_info info)
571 {
572 if (!CheckCallingPermission(FILE_ACCESS_PERMISSION)) {
573 HILOG_ERROR("permission error");
574 NError(E_PERMISSION).ThrowErr(env);
575 return nullptr;
576 }
577
578 NFuncArg funcArg(env, info);
579 if (!funcArg.InitArgs(NARG_CNT::ONE)) {
580 HILOG_ERROR("Number of arguments unmatched");
581 NError(EINVAL).ThrowErr(env);
582 return nullptr;
583 }
584 bool succ = false;
585 unique_ptr<char[]> uriPtr;
586 tie(succ, uriPtr, ignore) = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
587 if (!succ) {
588 NError(EINVAL).ThrowErr(env);
589 return nullptr;
590 }
591 string uriStr = uriPtr.get();
592 HILOG_DEBUG("Recover: uriPtr.get() = %{public}s", uriStr.c_str());
593
594 // 获取沙箱目录地址
595 AppFileService::ModuleFileUri::FileUri fileUri(uriStr);
596 string path = fileUri.GetPath();
597 // 判断绝对路径
598 if (!GetRealPath(path)) {
599 NError(EINVAL).ThrowErr(env);
600 HILOG_ERROR("Recover: Invalid Path");
601 return nullptr;
602 }
603 HILOG_DEBUG("Recover: path = %{public}s", path.c_str());
604
605 // 判断是否是回收站路径
606 if (path.find(TRASH_PATH) == string::npos) {
607 NError(EINVAL).ThrowErr(env);
608 HILOG_ERROR("Recover: path = %{public}s is not Trash path", path.c_str());
609 return nullptr;
610 }
611
612 // 判断路径是否存在
613 auto [isExist, ret] = Access(path);
614 if (!isExist) {
615 NError(EINVAL).ThrowErr(env);
616 HILOG_ERROR("Recover: Path is not exist");
617 return nullptr;
618 }
619
620 if (!CheckDir(path)) {
621 return RecoverFile(env, path);
622 }
623 return RecoverDir(env, path);
624 }
625
CompletelyDelete(napi_env env,napi_callback_info info)626 napi_value FileTrashNExporter::CompletelyDelete(napi_env env, napi_callback_info info)
627 {
628 if (!CheckCallingPermission(FILE_ACCESS_PERMISSION)) {
629 HILOG_ERROR("permission error");
630 NError(E_PERMISSION).ThrowErr(env);
631 return nullptr;
632 }
633
634 NFuncArg funcArg(env, info);
635 if (!funcArg.InitArgs(NARG_CNT::ONE)) {
636 HILOG_ERROR("Number of arguments unmatched");
637 NError(EINVAL).ThrowErr(env);
638 return nullptr;
639 }
640 bool succ = false;
641 unique_ptr<char[]> uriPtr;
642 tie(succ, uriPtr, ignore) = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
643 if (!succ) {
644 NError(EINVAL).ThrowErr(env);
645 HILOG_ERROR("Recover: Invalid arguments");
646 return nullptr;
647 }
648
649 string uriStr = uriPtr.get();
650 HILOG_DEBUG("Recover: uriPtr.get() = %{public}s", uriStr.c_str());
651
652 // 获取沙箱目录地址
653 AppFileService::ModuleFileUri::FileUri fileUri(uriStr);
654 string path = fileUri.GetPath();
655 // 判断绝对路径
656 if (!GetRealPath(path)) {
657 NError(EINVAL).ThrowErr(env);
658 HILOG_ERROR("Recover: Invalid Path");
659 return nullptr;
660 }
661 HILOG_DEBUG("Recover: path = %{public}s", path.c_str());
662
663 // 判断是否是回收站路径
664 if (path.find(TRASH_PATH) == string::npos) {
665 NError(EINVAL).ThrowErr(env);
666 HILOG_ERROR("Recover: path = %{public}s is not Trash path", path.c_str());
667 return nullptr;
668 }
669
670 // 判断路径是否存在
671 auto [isExist, ret] = Access(path);
672 if (!isExist) {
673 NError(EINVAL).ThrowErr(env);
674 HILOG_ERROR("Recover: Path is not exist");
675 return nullptr;
676 }
677
678 // 删除目录
679 auto err = RmDirent(GetToDeletePath(path, env));
680 if (err) {
681 err.ThrowErr(env);
682 return nullptr;
683 }
684 return NVal::CreateUndefined(env).val_;
685 }
686
Export()687 bool FileTrashNExporter::Export()
688 {
689 return exports_.AddProp({
690 NVal::DeclareNapiFunction("listFile", ListFile),
691 NVal::DeclareNapiFunction("recover", Recover),
692 NVal::DeclareNapiFunction("completelyDelete", CompletelyDelete)
693 });
694 }
695
GetClassName()696 string FileTrashNExporter::GetClassName()
697 {
698 return FileTrashNExporter::className_;
699 }
700
FileTrashNExporter(napi_env env,napi_value exports)701 FileTrashNExporter::FileTrashNExporter(napi_env env, napi_value exports) : NExporter(env, exports) {}
702
~FileTrashNExporter()703 FileTrashNExporter::~FileTrashNExporter() {}
704 } // namespace Trash
705 } // namespace OHOS