• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #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 
29 namespace OHOS {
30 namespace FileManagement {
31 namespace Recent {
32 using namespace std;
33 using namespace LibN;
34 using namespace AppFileService::ModuleFileUri;
35 
CheckPermission(const std::string & permission)36 static bool CheckPermission(const std::string &permission)
37 {
38     Security::AccessToken::AccessTokenID tokenCaller = IPCSkeleton::GetCallingTokenID();
39     return Security::AccessToken::AccessTokenKit::VerifyAccessToken(tokenCaller, permission) ==
40         Security::AccessToken::PermissionState::PERMISSION_GRANTED;
41 }
42 
SetFileTime(napi_env env,const string & recentFilePath)43 static napi_value SetFileTime(napi_env env, const string &recentFilePath)
44 {
45     if (lutimes(recentFilePath.c_str(), nullptr) < 0) {
46         HILOG_ERROR("Failed to lutimes recent link, errno=%{public}d", errno);
47         NError(errno).ThrowErr(env);
48         return nullptr;
49     }
50     struct stat statBuf;
51     if (lstat(recentFilePath.c_str(), &statBuf) < 0) {
52         HILOG_ERROR("Failed to stat uri, errno=%{public}d", errno);
53     }
54     return NVal::CreateUndefined(env).val_;
55 }
56 
AddRecentFile(napi_env env,napi_callback_info cbinfo)57 napi_value RecentNExporter::AddRecentFile(napi_env env, napi_callback_info cbinfo)
58 {
59     if (!CheckPermission(FILE_ACCESS_PERMISSION)) {
60         HILOG_ERROR("Failed to get file access permission");
61         NError(E_PERMISSION).ThrowErr(env);
62         return nullptr;
63     }
64     NFuncArg funcArg(env, cbinfo);
65     if (!funcArg.InitArgs(NARG_CNT::ONE)) {
66         HILOG_ERROR("Number of arguments unmatched");
67         NError(EINVAL).ThrowErr(env);
68         return nullptr;
69     }
70     auto [succ, uri, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
71     FileUri fileUri(string(uri.get()));
72     auto filePath = fileUri.GetPath();
73     struct stat statBuf;
74     if (stat(filePath.c_str(), &statBuf) < 0) {
75         HILOG_ERROR("Failed to stat uri, errno=%{public}d", errno);
76         NError(errno).ThrowErr(env);
77         return nullptr;
78     }
79     string recentFilePath = RECENT_PATH + to_string(statBuf.st_dev) + "_" + to_string(statBuf.st_ino);
80     auto lstatRet = lstat(recentFilePath.c_str(), &statBuf);
81     if (lstatRet == 0) {
82         auto accessRet = access(recentFilePath.c_str(), F_OK);
83         if (accessRet < 0 && errno == ENOENT) {
84             if (unlink(recentFilePath.c_str()) < 0) {
85                 HILOG_ERROR("Failed to unlink invalid recent link, errno=%{public}d", errno);
86                 NError(errno).ThrowErr(env);
87                 return nullptr;
88             }
89         } else if (accessRet == 0) {
90             return SetFileTime(env, recentFilePath);
91         }
92     } else if (lstatRet < 0 && errno != ENOENT) {
93         HILOG_ERROR("Failed to lstat uri, errno=%{public}d", errno);
94         NError(errno).ThrowErr(env);
95         return nullptr;
96     }
97     if (symlink(filePath.c_str(), recentFilePath.c_str()) < 0) {
98         HILOG_ERROR("Failed to symlink uri, errno=%{public}d", errno);
99         NError(errno).ThrowErr(env);
100         return nullptr;
101     }
102     return NVal::CreateUndefined(env).val_;
103 }
104 
RemoveRecentFile(napi_env env,napi_callback_info cbinfo)105 napi_value RecentNExporter::RemoveRecentFile(napi_env env, napi_callback_info cbinfo)
106 {
107     if (!CheckPermission(FILE_ACCESS_PERMISSION)) {
108         HILOG_ERROR("Failed to get file access permission");
109         NError(E_PERMISSION).ThrowErr(env);
110         return nullptr;
111     }
112     NFuncArg funcArg(env, cbinfo);
113     if (!funcArg.InitArgs(NARG_CNT::ONE)) {
114         HILOG_ERROR("Number of arguments unmatched");
115         NError(EINVAL).ThrowErr(env);
116         return nullptr;
117     }
118     auto [succ, uri, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
119     FileUri fileUri(string(uri.get()));
120     auto filePath = fileUri.GetPath();
121     struct stat statBuf;
122     if (stat(filePath.c_str(), &statBuf) < 0) {
123         HILOG_ERROR("Failed to stat uri, errno=%{public}d", errno);
124         NError(errno).ThrowErr(env);
125         return nullptr;
126     }
127     string recentFilePath = RECENT_PATH + to_string(statBuf.st_dev) + "_" + to_string(statBuf.st_ino);
128     if (unlink(recentFilePath.c_str()) < 0) {
129         HILOG_ERROR("Failed to unlink uri, errno=%{public}d", errno);
130         NError(errno).ThrowErr(env);
131     }
132     return nullptr;
133 }
134 
Deleter(struct NameListArg * arg)135 static void Deleter(struct NameListArg *arg)
136 {
137     for (int i = 0; i < arg->direntNum; i++) {
138         free((arg->namelist)[i]);
139         (arg->namelist)[i] = nullptr;
140     }
141     free(arg->namelist);
142 }
143 
GetFileMtime(const string & fileName)144 static int64_t GetFileMtime(const string &fileName)
145 {
146     string filePath = RECENT_PATH + fileName;
147     struct stat statBuf;
148     if (lstat(filePath.c_str(), &statBuf) < 0) {
149         HILOG_ERROR("Failed to lstat uri, errno=%{public}d, fileName=%{public}s", errno, fileName.c_str());
150         return errno;
151     }
152     return static_cast<int64_t>(statBuf.st_mtime);
153 }
154 
GetName(const string & path)155 static string GetName(const string &path)
156 {
157     auto pos = path.find_last_of('/');
158     if (pos == string::npos) {
159         HILOGE("Failed to split filename from path, path: %{public}s", path.c_str());
160     }
161     return path.substr(pos + 1);
162 }
163 
SortReceneFile(const struct dirent ** a,const struct dirent ** b)164 static int SortReceneFile(const struct dirent **a, const struct dirent **b)
165 {
166     return GetFileMtime(string((*b)->d_name)) - GetFileMtime(string((*a)->d_name));
167 }
168 
FilterFunc(const struct dirent * filename)169 static int FilterFunc(const struct dirent *filename)
170 {
171     if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") {
172         return false;
173     }
174     return true;
175 }
176 
CheckRealFileExist(const string & recentFilePath)177 static tuple<int, struct stat> CheckRealFileExist(const string &recentFilePath)
178 {
179     struct stat statBuf;
180     int accessRet = access(recentFilePath.c_str(), F_OK);
181     if (accessRet < 0 && errno != ENOENT) {
182         HILOG_ERROR("Failed to access file, errno=%{public}d", errno);
183         return { errno, statBuf };
184     }
185     if (accessRet == 0) {
186         if (stat(recentFilePath.c_str(), &statBuf) < 0) {
187             HILOG_ERROR("Failed to stat file, errno=%{public}d", errno);
188             return { errno, statBuf };
189         }
190         string oldRecentFilePath = RECENT_PATH + to_string(statBuf.st_dev) + "_" + to_string(statBuf.st_ino);
191         if (oldRecentFilePath == recentFilePath) {
192             return { 0, statBuf };
193         }
194     }
195     if (unlink(recentFilePath.c_str()) < 0) {
196         HILOG_ERROR("Failed to unlink non-existent file, errno=%{public}d", errno);
197         return { errno, statBuf };
198     }
199     return { -1, statBuf };
200 }
201 
GetFileInfo(napi_env env,const string & filePath,const struct stat & realFileStatBuf,const struct stat & linkFileStatBuf)202 static napi_value GetFileInfo(napi_env env, const string &filePath, const struct stat &realFileStatBuf,
203     const struct stat &linkFileStatBuf)
204 {
205     FileUri fileUri(filePath);
206     NVal obj = NVal::CreateObject(env);
207     obj.AddProp({
208         NVal::DeclareNapiProperty("uri", NVal::CreateUTF8String(env, fileUri.ToString()).val_),
209         NVal::DeclareNapiProperty("srcPath", NVal::CreateUTF8String(env, filePath).val_),
210         NVal::DeclareNapiProperty("fileName", NVal::CreateUTF8String(env, GetName(filePath)).val_),
211         NVal::DeclareNapiProperty("size", NVal::CreateInt64(env, realFileStatBuf.st_size).val_),
212         NVal::DeclareNapiProperty("mtime", NVal::CreateInt64(env, realFileStatBuf.st_mtim.tv_sec).val_),
213         NVal::DeclareNapiProperty("ctime", NVal::CreateInt64(env, linkFileStatBuf.st_mtim.tv_sec).val_),
214         NVal::DeclareNapiProperty("mode", NVal::CreateInt64(env, realFileStatBuf.st_mode).val_),
215     });
216     return obj.val_;
217 }
218 
GetListFileResult(napi_env env,struct NameListArg * pNameList)219 static napi_value GetListFileResult(napi_env env, struct NameListArg* pNameList)
220 {
221     napi_value res = nullptr;
222     if (napi_create_array(env, &res) != napi_ok) {
223         HILOG_ERROR("Failed to create array");
224         NError(UNKROWN_ERR).ThrowErr(env);
225         return nullptr;
226     }
227     auto buf = CreateUniquePtr<char[]>(BUF_SIZE);
228     for (int i = 0, index = 0; i < pNameList->direntNum; ++i) {
229         string recentFilePath = RECENT_PATH + string((*(pNameList->namelist[i])).d_name);
230         if (index < MAX_RECENT_SIZE) {
231             auto [checkRealFileRes, realFileStatBuf] = CheckRealFileExist(recentFilePath);
232             if (checkRealFileRes < 0) {
233                 continue;
234             } else if (checkRealFileRes > 0) {
235                 NError(checkRealFileRes).ThrowErr(env);
236                 return nullptr;
237             }
238             auto readLinkRes = readlink(recentFilePath.c_str(), buf.get(), BUF_SIZE);
239             if (readLinkRes < 0) {
240                 HILOG_ERROR("Failed to readlink uri, errno=%{public}d", errno);
241                 NError(errno).ThrowErr(env);
242                 return nullptr;
243             }
244             struct stat linkFileStatBuf;
245             if (lstat(recentFilePath.c_str(), &linkFileStatBuf) < 0) {
246                 HILOG_ERROR("Failed to lstat file, errno=%{public}d", errno);
247                 NError(errno).ThrowErr(env);
248                 return nullptr;
249             }
250             if (napi_set_element(env, res, index, GetFileInfo(env, string(buf.get(), readLinkRes),
251                 realFileStatBuf, linkFileStatBuf)) != napi_ok) {
252                 HILOG_ERROR("Failed to set element");
253                 NError(UNKROWN_ERR).ThrowErr(env);
254                 return nullptr;
255             }
256             ++index;
257         } else {
258             if (unlink(recentFilePath.c_str()) < 0) {
259                 HILOG_ERROR("Failed to unlink file, errno=%{public}d", errno);
260                 NError(errno).ThrowErr(env);
261                 return nullptr;
262             }
263         }
264     }
265     return res;
266 }
267 
ListFileCore(napi_env env)268 static napi_value ListFileCore(napi_env env)
269 {
270     unique_ptr<struct NameListArg, decltype(Deleter)*> pNameList = { new (nothrow) struct NameListArg, Deleter };
271     if (!pNameList) {
272         HILOG_ERROR("Failed to request heap memory.");
273         NError(ENOMEM).ThrowErr(env);
274         return nullptr;
275     }
276     pNameList->direntNum = scandir(RECENT_PATH.c_str(), &(pNameList->namelist), FilterFunc, SortReceneFile);
277     if (pNameList->direntNum < 0) {
278         HILOG_ERROR("Failed to scan dir, errno=%{public}d", errno);
279         NError(errno).ThrowErr(env);
280         return nullptr;
281     }
282     return GetListFileResult(env, pNameList.get());
283 }
284 
ListRecentFile(napi_env env,napi_callback_info cbinfo)285 napi_value RecentNExporter::ListRecentFile(napi_env env, napi_callback_info cbinfo)
286 {
287     if (!CheckPermission(FILE_ACCESS_PERMISSION)) {
288         HILOG_ERROR("Failed to get file access permission");
289         NError(E_PERMISSION).ThrowErr(env);
290         return nullptr;
291     }
292     NFuncArg funcArg(env, cbinfo);
293     if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
294         HILOG_ERROR("Number of arguments unmatched");
295         NError(EINVAL).ThrowErr(env);
296         return nullptr;
297     }
298     return ListFileCore(env);
299 }
300 
Export()301 bool RecentNExporter::Export()
302 {
303     return exports_.AddProp({
304         NVal::DeclareNapiFunction("add", AddRecentFile),
305         NVal::DeclareNapiFunction("remove", RemoveRecentFile),
306         NVal::DeclareNapiFunction("listFile", ListRecentFile),
307     });
308 }
309 
GetClassName()310 string RecentNExporter::GetClassName()
311 {
312     return RecentNExporter::className;
313 }
314 
RecentNExporter(napi_env env,napi_value exports)315 RecentNExporter::RecentNExporter(napi_env env, napi_value exports) : NExporter(env, exports) {}
316 
~RecentNExporter()317 RecentNExporter::~RecentNExporter() {}
318 } // namespace Recent
319 } // namespace FileManagement
320 } // namespace OHOS
321