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