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