• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = %{private}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 = %{private}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 (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: pathTemp = %{private}s", pathTemp.c_str());
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: curFilePath = %{private}s", path.c_str());
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: realFilePath After = %{private}s", realFilePath.c_str());
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: path = %{private}s", path.c_str());
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 fail for %{private}s ", recoveredPath.c_str());
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: path = %{private}s", path.c_str());
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: parentPath = %{private}s", parentPath.c_str());
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 = %{private}s", path.c_str());
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: toDeletePath = %{private}s", toDeletePath.c_str());
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: sourceFilePath = %{private}s", sourceFilePath.c_str());
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     for (size_t j = 0; j < filePathList.size(); j++) {
500         string filePath = filePathList[j];
501         HILOG_INFO("RecoverFilePart: filePath  = %{private}s", filePath.c_str());
502         string sourceFilePath = FindSourceFilePath(filePath);
503         HILOG_INFO("RecoverFilePart: sourceFilePath  = %{private}s", sourceFilePath.c_str());
504 
505         size_t lastSlashPos = sourceFilePath.find_last_of("/");
506         string fileName = sourceFilePath.substr(lastSlashPos + 1);
507         string sourceFilePathOnly = sourceFilePath.substr(0, lastSlashPos);
508         map<string, string>::iterator iter = dirPath2UpdatedNameMap.find(sourceFilePathOnly);
509         if (iter != dirPath2UpdatedNameMap.end()) {
510             sourceFilePath = iter->second + "/" + fileName;
511         }
512         int moveRet = MoveFile(filePath, sourceFilePath);
513         if (moveRet != ERRNO_NOERR) {
514             return moveRet;
515         }
516     }
517     return ERRNO_NOERR;
518 }
519 
MakeAndFindUpdateNameDir(vector<string> filterDirPathList)520 static map<string, string> MakeAndFindUpdateNameDir(vector<string> filterDirPathList)
521 {
522     map<string, string> dirPath2UpdatedNameMap;
523     for (size_t j = 0; j < filterDirPathList.size(); j++) {
524         string dirPath = filterDirPathList[j];
525         string sourceFilePath = FindSourceFilePath(dirPath);
526         HILOG_DEBUG("MakeAndFindUpdateNameDir: sourceFilePath  = %{private}s", sourceFilePath.c_str());
527         string newDestPath = sourceFilePath;
528         if (Mkdirs(sourceFilePath, true, newDestPath)) {
529             HILOG_DEBUG("MakeAndFindUpdateNameDir: newDestPath  = %{private}s", newDestPath.c_str());
530             if (newDestPath != sourceFilePath) {
531                 dirPath2UpdatedNameMap.insert(make_pair(sourceFilePath, newDestPath));
532             }
533         }
534     }
535     return dirPath2UpdatedNameMap;
536 }
537 
RecoverDir(napi_env env,const string & dirPath)538 static napi_value RecoverDir(napi_env env, const string &dirPath)
539 {
540     vector<string> dirents;
541     unique_ptr<struct NameListArg, decltype(Deleter)*> pNameList = { new (nothrow) struct NameListArg, Deleter };
542     if (!pNameList) {
543         HILOG_ERROR("RecoverDir: Failed to request heap memory.");
544         return nullptr;
545     }
546     int ret = RecursiveFunc(dirPath, dirents);
547     if (ret != ERRNO_NOERR) {
548         HILOG_ERROR("RecoverDir: Failed to Recursive Dir.");
549         return nullptr;
550     }
551     dirents.emplace_back(dirPath);
552 
553     // 区分目录和文件
554     vector<string> dirPathList;
555     vector<string> filePathList;
556     for (size_t j = 0; j < dirents.size(); j++) {
557         string dirent = dirents[j];
558         if (CheckDir(dirent)) {
559             dirPathList.emplace_back(dirent);
560         } else {
561             filePathList.emplace_back(dirent);
562         }
563     }
564 
565     // 新建目录并获取因为存在同名目录修改过名称的目录
566     map<string, string> dirPath2UpdatedNameMap = MakeAndFindUpdateNameDir(dirPathList);
567 
568     // 处理文件部分
569     auto retRecoveFilePart = RecoverFilePart(filePathList, dirPath2UpdatedNameMap);
570     if (retRecoveFilePart != ERRNO_NOERR) {
571         NError(retRecoveFilePart).ThrowErr(env);
572         HILOG_ERROR("RecoverFilePart: Failed to Recover File in Dir.");
573         return nullptr;
574     }
575 
576     // 删除目录
577     auto err = RmDirent(GetToDeletePath(dirPath, env));
578     if (err) {
579         err.ThrowErr(env);
580         return nullptr;
581     }
582 
583     return NVal::CreateUndefined(env).val_;
584 }
585 
Recover(napi_env env,napi_callback_info info)586 napi_value FileTrashNExporter::Recover(napi_env env, napi_callback_info info)
587 {
588     if (!CheckSystemAppAndPermission(FILE_ACCESS_PERMISSION, env)) {
589         HILOG_ERROR("Recover CheckSystemAppAndPermission error");
590         return nullptr;
591     }
592 
593     NFuncArg funcArg(env, info);
594     if (!funcArg.InitArgs(NARG_CNT::ONE)) {
595         HILOG_ERROR("Number of arguments unmatched");
596         NError(EINVAL).ThrowErr(env);
597         return nullptr;
598     }
599     bool succ = false;
600     unique_ptr<char[]> uriPtr;
601     tie(succ, uriPtr, ignore) = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
602     if (!succ) {
603         NError(EINVAL).ThrowErr(env);
604         return nullptr;
605     }
606     string uriStr = uriPtr.get();
607     HILOG_DEBUG("Recover: uriPtr.get()  = %{private}s", uriStr.c_str());
608 
609     // 获取沙箱目录地址
610     AppFileService::ModuleFileUri::FileUri fileUri(uriStr);
611     string path = fileUri.GetPath();
612     // 判断绝对路径
613     if (!GetRealPath(path)) {
614         NError(EINVAL).ThrowErr(env);
615         HILOG_ERROR("Recover: Invalid Path");
616         return nullptr;
617     }
618     HILOG_DEBUG("Recover: path  = %{private}s", path.c_str());
619 
620     // 判断是否是回收站路径
621     if (path.find(FileTrashNExporter::trashPath_) == string::npos) {
622         NError(EINVAL).ThrowErr(env);
623         HILOG_ERROR("Recover: path  = %{private}s is not Trash path", path.c_str());
624         return nullptr;
625     }
626 
627     // 判断路径是否存在
628     auto [isExist, ret] = Access(path);
629     if (!isExist) {
630         NError(EINVAL).ThrowErr(env);
631         HILOG_ERROR("Recover: Path is not exist");
632         return nullptr;
633     }
634 
635     if (!CheckDir(path)) {
636         return RecoverFile(env, path);
637     }
638     return RecoverDir(env, path);
639 }
640 
CompletelyDelete(napi_env env,napi_callback_info info)641 napi_value FileTrashNExporter::CompletelyDelete(napi_env env, napi_callback_info info)
642 {
643     if (!CheckSystemAppAndPermission(FILE_ACCESS_PERMISSION, env)) {
644         HILOG_ERROR("CompletelyDelete CheckSystemAppAndPermission error");
645         return nullptr;
646     }
647 
648     NFuncArg funcArg(env, info);
649     if (!funcArg.InitArgs(NARG_CNT::ONE)) {
650         HILOG_ERROR("Number of arguments unmatched");
651         NError(EINVAL).ThrowErr(env);
652         return nullptr;
653     }
654     bool succ = false;
655     unique_ptr<char[]> uriPtr;
656     tie(succ, uriPtr, ignore) = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
657     if (!succ) {
658         NError(EINVAL).ThrowErr(env);
659         HILOG_ERROR("Recover: Invalid arguments");
660         return nullptr;
661     }
662 
663     string uriStr = uriPtr.get();
664     HILOG_DEBUG("Recover: uriPtr.get()  = %{private}s", uriStr.c_str());
665 
666     // 获取沙箱目录地址
667     AppFileService::ModuleFileUri::FileUri fileUri(uriStr);
668     string path = fileUri.GetPath();
669     // 判断绝对路径
670     if (!GetRealPath(path)) {
671         NError(EINVAL).ThrowErr(env);
672         HILOG_ERROR("Recover: Invalid Path");
673         return nullptr;
674     }
675     HILOG_DEBUG("Recover: path  = %{private}s", path.c_str());
676 
677     // 判断是否是回收站路径
678     if (path.find(FileTrashNExporter::trashPath_) == string::npos) {
679         NError(EINVAL).ThrowErr(env);
680         HILOG_ERROR("Recover: path  = %{private}s is not Trash path", path.c_str());
681         return nullptr;
682     }
683 
684     // 判断路径是否存在
685     auto [isExist, ret] = Access(path);
686     if (!isExist) {
687         NError(EINVAL).ThrowErr(env);
688         HILOG_ERROR("Recover: Path is not exist");
689         return nullptr;
690     }
691 
692     // 删除目录
693     auto err = RmDirent(GetToDeletePath(path, env));
694     if (err) {
695         err.ThrowErr(env);
696         return nullptr;
697     }
698     return NVal::CreateUndefined(env).val_;
699 }
700 
Export()701 bool FileTrashNExporter::Export()
702 {
703     return exports_.AddProp({
704         NVal::DeclareNapiFunction("listFile", ListFile),
705         NVal::DeclareNapiFunction("recover", Recover),
706         NVal::DeclareNapiFunction("completelyDelete", CompletelyDelete)
707     });
708 }
709 
GetClassName()710 string FileTrashNExporter::GetClassName()
711 {
712     return FileTrashNExporter::className_;
713 }
714 
InitTrashPath()715 void FileTrashNExporter::InitTrashPath()
716 {
717     if (FileTrashNExporter::trashPath_.empty()) {
718         std::unique_lock<std::mutex> lock(g_trashPathMutex);
719         if (!FileTrashNExporter::trashPath_.empty()) {
720             return ;
721         }
722         FileTrashNExporter::trashPath_ = "/storage/Users/currentUser/.Trash";
723         std::string deviceType;
724         if (IsFullMountEnable()) {
725             std::string userName;
726             if (GetUserName(userName) && userName != "") {
727                 FileTrashNExporter::trashPath_ = "/storage/Users/" + userName + "/.Trash";
728             }
729         }
730         HILOG_INFO("GetRecentDir %{private}s", FileTrashNExporter::trashPath_.c_str());
731     }
732 }
733 
FileTrashNExporter(napi_env env,napi_value exports)734 FileTrashNExporter::FileTrashNExporter(napi_env env, napi_value exports) : NExporter(env, exports)
735 {
736     InitTrashPath();
737 }
738 
~FileTrashNExporter()739 FileTrashNExporter::~FileTrashNExporter() {}
740 } // namespace Trash
741 } // namespace OHOS