• 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 #include "recent_n_exporter.h"
16 
17 #include <sys/stat.h>
18 #include <sys/time.h>
19 #include <tuple>
20 #include <unistd.h>
21 
22 #include "access_token.h"
23 #include "accesstoken_kit.h"
24 #include "file_uri.h"
25 #include "file_utils.h"
26 #include "hilog_wrapper.h"
27 #include "ipc_skeleton.h"
28 #include "tokenid_kit.h"
29 #include "user_access_common_utils.h"
30 
31 namespace OHOS {
32 namespace FileManagement {
33 namespace Recent {
34 using namespace std;
35 using namespace LibN;
36 using namespace AppFileService::ModuleFileUri;
37 
38 using namespace FileAccessFwk;
39 static std::mutex g_recentPathMutex;
CheckPermission(const std::string & permission)40 static bool CheckPermission(const std::string &permission)
41 {
42     Security::AccessToken::AccessTokenID tokenCaller = IPCSkeleton::GetCallingTokenID();
43     return Security::AccessToken::AccessTokenKit::VerifyAccessToken(tokenCaller, permission) ==
44         Security::AccessToken::PermissionState::PERMISSION_GRANTED;
45 }
46 
IsSystemApp()47 static bool IsSystemApp()
48 {
49     uint64_t accessTokenIDEx = OHOS::IPCSkeleton::GetCallingFullTokenID();
50     return OHOS::Security::AccessToken::TokenIdKit::IsSystemAppByFullTokenID(accessTokenIDEx);
51 }
52 
CheckSystemAppAndPermission(const std::string & permission,napi_env env)53 static bool CheckSystemAppAndPermission(const std::string &permission, napi_env env)
54 {
55     if (!IsSystemApp()) {
56         HILOG_ERROR("FileTrashNExporter::Recover check IsSystemAppByFullTokenID failed");
57         NError(E_PERMISSION_SYS).ThrowErr(env);
58         return false;
59     }
60     if (!CheckPermission(FILE_ACCESS_PERMISSION)) {
61         HILOG_ERROR("Check permission error");
62         NError(E_PERMISSION).ThrowErr(env);
63         return false;
64     }
65     return true;
66 }
67 
SetFileTime(napi_env env,const string & recentFilePath)68 static napi_value SetFileTime(napi_env env, const string &recentFilePath)
69 {
70     if (lutimes(recentFilePath.c_str(), nullptr) < 0) {
71         HILOG_ERROR("Failed to lutimes recent link, errno=%{public}d", errno);
72         NError(errno).ThrowErr(env);
73         return nullptr;
74     }
75     struct stat statBuf;
76     if (lstat(recentFilePath.c_str(), &statBuf) < 0) {
77         HILOG_ERROR("Failed to stat uri, errno=%{public}d", errno);
78     }
79     return NVal::CreateUndefined(env).val_;
80 }
81 
AddRecentFile(napi_env env,napi_callback_info cbinfo)82 napi_value RecentNExporter::AddRecentFile(napi_env env, napi_callback_info cbinfo)
83 {
84     if (!CheckSystemAppAndPermission(FILE_ACCESS_PERMISSION, env)) {
85         HILOG_ERROR("AddRecentFile CheckSystemAppAndPermission error");
86         return nullptr;
87     }
88 
89     NFuncArg funcArg(env, cbinfo);
90     if (!funcArg.InitArgs(NARG_CNT::ONE)) {
91         HILOG_ERROR("Number of arguments unmatched");
92         NError(EINVAL).ThrowErr(env);
93         return nullptr;
94     }
95     auto [succ, uri, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
96     FileUri fileUri(string(uri.get()));
97     auto filePath = fileUri.GetRealPath();
98     struct stat statBuf;
99     if (stat(filePath.c_str(), &statBuf) < 0) {
100         HILOG_ERROR("Failed to stat uri, errno=%{public}d", errno);
101         NError(errno).ThrowErr(env);
102         return nullptr;
103     }
104     string recentFilePath = RecentNExporter::recentPath_ + to_string(statBuf.st_dev) + "_" + to_string(statBuf.st_ino);
105     auto lstatRet = lstat(recentFilePath.c_str(), &statBuf);
106     if (lstatRet == 0) {
107         auto accessRet = access(recentFilePath.c_str(), F_OK);
108         if (accessRet < 0 && errno == ENOENT) {
109             if (unlink(recentFilePath.c_str()) < 0) {
110                 HILOG_ERROR("Failed to unlink invalid recent link, errno=%{public}d", errno);
111                 NError(errno).ThrowErr(env);
112                 return nullptr;
113             }
114         } else if (accessRet == 0) {
115             return SetFileTime(env, recentFilePath);
116         }
117     } else if (lstatRet < 0 && errno != ENOENT) {
118         HILOG_ERROR("Failed to lstat uri, errno=%{public}d", errno);
119         NError(errno).ThrowErr(env);
120         return nullptr;
121     }
122     if (symlink(filePath.c_str(), recentFilePath.c_str()) < 0) {
123         HILOG_ERROR("Failed to symlink uri, errno=%{public}d", errno);
124         NError(errno).ThrowErr(env);
125         return nullptr;
126     }
127     return NVal::CreateUndefined(env).val_;
128 }
129 
RemoveRecentFile(napi_env env,napi_callback_info cbinfo)130 napi_value RecentNExporter::RemoveRecentFile(napi_env env, napi_callback_info cbinfo)
131 {
132     if (!CheckSystemAppAndPermission(FILE_ACCESS_PERMISSION, env)) {
133         HILOG_ERROR("AddRecentFile CheckSystemAppAndPermission error");
134         return nullptr;
135     }
136 
137     NFuncArg funcArg(env, cbinfo);
138     if (!funcArg.InitArgs(NARG_CNT::ONE)) {
139         HILOG_ERROR("Number of arguments unmatched");
140         NError(EINVAL).ThrowErr(env);
141         return nullptr;
142     }
143     auto [succ, uri, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
144     FileUri fileUri(string(uri.get()));
145     auto filePath = fileUri.GetPath();
146     struct stat statBuf;
147     if (stat(filePath.c_str(), &statBuf) < 0) {
148         HILOG_ERROR("Failed to stat uri, errno=%{public}d", errno);
149         NError(errno).ThrowErr(env);
150         return nullptr;
151     }
152     string recentFilePath = RecentNExporter::recentPath_ + to_string(statBuf.st_dev) + "_" + to_string(statBuf.st_ino);
153     if (unlink(recentFilePath.c_str()) < 0) {
154         HILOG_ERROR("Failed to unlink uri, errno=%{public}d", errno);
155         NError(errno).ThrowErr(env);
156     }
157     return nullptr;
158 }
159 
Deleter(struct NameListArg * arg)160 static void Deleter(struct NameListArg *arg)
161 {
162     if (arg == nullptr) {
163         HILOG_ERROR("invalid argument arg is nullptr");
164         return;
165     }
166     if (arg->namelist == nullptr) {
167         HILOG_ERROR("arg->namelist is nullptr");
168         return;
169     }
170     for (int32_t i = 0; i < arg->direntNum; i++) {
171         free((arg->namelist)[i]);
172         (arg->namelist)[i] = nullptr;
173     }
174     free(arg->namelist);
175     delete arg;
176     arg = nullptr;
177 }
178 
GetFileMtime(const string & fileName)179 static int64_t GetFileMtime(const string &fileName)
180 {
181     string filePath = RecentNExporter::recentPath_ + fileName;
182     struct stat statBuf;
183     if (lstat(filePath.c_str(), &statBuf) < 0) {
184         HILOG_ERROR("Failed to lstat uri, errno=%{public}d, fileName=%{private}s", errno, fileName.c_str());
185         return errno;
186     }
187     return static_cast<int64_t>(statBuf.st_mtime);
188 }
189 
GetName(const string & path)190 static string GetName(const string &path)
191 {
192     auto pos = path.find_last_of('/');
193     if (pos == string::npos) {
194         HILOGE("Failed to split filename from path, path: %{private}s", path.c_str());
195     }
196     return path.substr(pos + 1);
197 }
198 
SortReceneFile(const struct dirent ** a,const struct dirent ** b)199 static int SortReceneFile(const struct dirent **a, const struct dirent **b)
200 {
201     return GetFileMtime(string((*b)->d_name)) - GetFileMtime(string((*a)->d_name));
202 }
203 
FilterFunc(const struct dirent * filename)204 static int FilterFunc(const struct dirent *filename)
205 {
206     if (filename == nullptr) {
207         HILOGE("filename is bullptr");
208         return false;
209     }
210     if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") {
211         return false;
212     }
213     return true;
214 }
215 
CheckRealFileExist(const string & recentFilePath)216 static tuple<int, struct stat> CheckRealFileExist(const string &recentFilePath)
217 {
218     struct stat statBuf;
219     int accessRet = access(recentFilePath.c_str(), F_OK);
220     if (accessRet < 0 && errno != ENOENT) {
221         HILOG_ERROR("Failed to access file, errno=%{public}d", errno);
222         return { errno, statBuf };
223     }
224     if (accessRet == 0) {
225         if (stat(recentFilePath.c_str(), &statBuf) < 0) {
226             HILOG_ERROR("Failed to stat file, errno=%{public}d", errno);
227             return { errno, statBuf };
228         }
229         string oldRecentFilePath =
230             RecentNExporter::recentPath_ + to_string(statBuf.st_dev) + "_" + to_string(statBuf.st_ino);
231         if (oldRecentFilePath == recentFilePath) {
232             return { 0, statBuf };
233         }
234     }
235     if (unlink(recentFilePath.c_str()) < 0) {
236         HILOG_ERROR("Failed to unlink non-existent file, errno=%{public}d", errno);
237         return { errno, statBuf };
238     }
239     return { -1, statBuf };
240 }
241 
GetFileInfo(napi_env env,const string & filePath,const struct stat & realFileStatBuf,const struct stat & linkFileStatBuf)242 static napi_value GetFileInfo(napi_env env, const string &filePath, const struct stat &realFileStatBuf,
243     const struct stat &linkFileStatBuf)
244 {
245     FileUri fileUri(filePath);
246     NVal obj = NVal::CreateObject(env);
247     obj.AddProp({
248         NVal::DeclareNapiProperty("uri", NVal::CreateUTF8String(env, fileUri.ToString()).val_),
249         NVal::DeclareNapiProperty("srcPath", NVal::CreateUTF8String(env, filePath).val_),
250         NVal::DeclareNapiProperty("fileName", NVal::CreateUTF8String(env, GetName(filePath)).val_),
251         NVal::DeclareNapiProperty("size", NVal::CreateInt64(env, realFileStatBuf.st_size).val_),
252         NVal::DeclareNapiProperty("mtime", NVal::CreateInt64(env, realFileStatBuf.st_mtim.tv_sec).val_),
253         NVal::DeclareNapiProperty("ctime", NVal::CreateInt64(env, linkFileStatBuf.st_mtim.tv_sec).val_),
254         NVal::DeclareNapiProperty("mode", NVal::CreateInt64(env, realFileStatBuf.st_mode).val_),
255     });
256     return obj.val_;
257 }
258 
GetListFileResult(napi_env env,struct NameListArg * pNameList)259 static napi_value GetListFileResult(napi_env env, struct NameListArg* pNameList)
260 {
261     napi_value res = nullptr;
262     if (napi_create_array(env, &res) != napi_ok) {
263         HILOG_ERROR("Failed to create array");
264         NError(UNKROWN_ERR).ThrowErr(env);
265         return nullptr;
266     }
267     auto buf = CreateUniquePtr<char[]>(BUF_SIZE);
268     int index = 0;
269     for (int32_t i = 0; i < pNameList->direntNum; ++i) {
270         string recentFilePath = RecentNExporter::recentPath_ + string((*(pNameList->namelist[i])).d_name);
271         if (index < MAX_RECENT_SIZE) {
272             auto [checkRealFileRes, realFileStatBuf] = CheckRealFileExist(recentFilePath);
273             if (checkRealFileRes < 0) {
274                 continue;
275             } else if (checkRealFileRes > 0) {
276                 NError(checkRealFileRes).ThrowErr(env);
277                 return nullptr;
278             }
279             auto readLinkRes = readlink(recentFilePath.c_str(), buf.get(), BUF_SIZE);
280             if (readLinkRes < 0) {
281                 HILOG_ERROR("Failed to readlink uri, errno=%{public}d", errno);
282                 NError(errno).ThrowErr(env);
283                 return nullptr;
284             }
285             struct stat linkFileStatBuf;
286             if (lstat(recentFilePath.c_str(), &linkFileStatBuf) < 0) {
287                 HILOG_ERROR("Failed to lstat file, errno=%{public}d", errno);
288                 NError(errno).ThrowErr(env);
289                 return nullptr;
290             }
291             if (napi_set_element(env, res, index, GetFileInfo(env, string(buf.get(), readLinkRes),
292                 realFileStatBuf, linkFileStatBuf)) != napi_ok) {
293                 HILOG_ERROR("Failed to set element");
294                 NError(UNKROWN_ERR).ThrowErr(env);
295                 return nullptr;
296             }
297             ++index;
298         } else {
299             if (unlink(recentFilePath.c_str()) < 0) {
300                 HILOG_ERROR("Failed to unlink file, errno=%{public}d", errno);
301                 NError(errno).ThrowErr(env);
302                 return nullptr;
303             }
304         }
305     }
306     HILOG_INFO("The count of recent file is %{public}d", index);
307     return res;
308 }
309 
ListFileCore(napi_env env)310 static napi_value ListFileCore(napi_env env)
311 {
312     unique_ptr<struct NameListArg, decltype(Deleter)*> pNameList = { new (nothrow) struct NameListArg, Deleter };
313     if (!pNameList) {
314         HILOG_ERROR("Failed to request heap memory.");
315         NError(ENOMEM).ThrowErr(env);
316         return nullptr;
317     }
318     pNameList->direntNum = scandir(RecentNExporter::recentPath_.c_str(),
319         &(pNameList->namelist), FilterFunc, SortReceneFile);
320     if (pNameList->direntNum < 0) {
321         HILOG_ERROR("Failed to scan dir, errno=%{public}d", errno);
322         NError(errno).ThrowErr(env);
323         return nullptr;
324     }
325     return GetListFileResult(env, pNameList.get());
326 }
327 
ListRecentFile(napi_env env,napi_callback_info cbinfo)328 napi_value RecentNExporter::ListRecentFile(napi_env env, napi_callback_info cbinfo)
329 {
330     if (!CheckSystemAppAndPermission(FILE_ACCESS_PERMISSION, env)) {
331         HILOG_ERROR("ListRecentFile CheckSystemAppAndPermission error");
332         return nullptr;
333     }
334 
335     NFuncArg funcArg(env, cbinfo);
336     if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
337         HILOG_ERROR("Number of arguments unmatched");
338         NError(EINVAL).ThrowErr(env);
339         return nullptr;
340     }
341     return ListFileCore(env);
342 }
343 
Export()344 bool RecentNExporter::Export()
345 {
346     return exports_.AddProp({
347         NVal::DeclareNapiFunction("add", AddRecentFile),
348         NVal::DeclareNapiFunction("remove", RemoveRecentFile),
349         NVal::DeclareNapiFunction("listFile", ListRecentFile),
350     });
351 }
352 
GetClassName()353 string RecentNExporter::GetClassName()
354 {
355     return RecentNExporter::className;
356 }
357 
InitRecentPath()358 void RecentNExporter::InitRecentPath()
359 {
360     if (RecentNExporter::recentPath_.empty()) {
361         std::unique_lock<std::mutex> lock(g_recentPathMutex);
362         if (!RecentNExporter::recentPath_.empty()) {
363             return ;
364         }
365         RecentNExporter::recentPath_ = "/storage/Users/currentUser/.Recent/";
366         std::string deviceType;
367         if (IsFullMountEnable()) {
368             std::string userName;
369             if (GetUserName(userName) && userName != "") {
370                 RecentNExporter::recentPath_ = "/storage/Users/" + userName + "/.Recent/";
371             }
372         }
373         HILOG_INFO("GetRecentDir %{public}s", RecentNExporter::recentPath_.c_str());
374     }
375 }
376 
RecentNExporter(napi_env env,napi_value exports)377 RecentNExporter::RecentNExporter(napi_env env, napi_value exports) : NExporter(env, exports)
378 {
379     InitRecentPath();
380 }
381 
~RecentNExporter()382 RecentNExporter::~RecentNExporter() {}
383 } // namespace Recent
384 } // namespace FileManagement
385 } // namespace OHOS
386