Refactor file & file visitor classes
[platform/upstream/csr-framework.git] / src / framework / service / file-system.cpp
1 /*
2  *  Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License
15  */
16 /*
17  * @file        file-system.cpp
18  * @author      Dongsun Lee (ds73.lee@samsung.com)
19  * @version     1.0
20  * @brief
21  */
22 #include "service/file-system.h"
23
24 #include <regex>
25 #include <system_error>
26 #include <cstdio>
27 #include <cstring>
28 #include <cerrno>
29 #include <unistd.h>
30
31 #include "common/audit/logger.h"
32 #include "common/exception.h"
33 #include "service/app-deleter.h"
34 #include "service/fs-utils.h"
35 #include "service/dir-blacklist.h"
36
37 #include <pkgmgr-info.h>
38
39 namespace Csr {
40
41 namespace {
42
43 inline std::regex makeRegexpr(const char *str)
44 {
45         return std::regex(str, std::regex_constants::extended);
46 }
47
48 std::vector<std::regex> g_regexprs{
49 #ifdef PLATFORM_VERSION_3
50         makeRegexpr("^(/opt/usr/apps/([^/]+))"),               // /opt/usr/apps/{pkgid}/
51         makeRegexpr("^(/home/([^/]+)/apps_rw/([^/]+))"),       // /home/{user}/apps_rw/{pkgid}/
52         makeRegexpr("^(/opt/home/([^/]+)/apps_rw/([^/]+))"),   // /opt/home/{user}/apps_rw/{pkgid}/
53         makeRegexpr("^(/sdcard/app2sd/([^/]+)/([^/]+))"),      // /sdcard/app2sd/{user}/{pkgid}/
54         makeRegexpr("^(/sdcard/app2sd/([^/]+))"),              // /sdcard/app2sd/{pkgid}/
55         makeRegexpr("^(/sdcard/apps/([^/]+)/apps_rw/([^/]+))") // /sdcard/apps/{user}/apps_rw/{pkgid}/
56 #else
57         makeRegexpr("^(/usr/apps/([^/]+))"),                   // /usr/apps/{pkgid}/
58         makeRegexpr("^(/opt/usr/apps/([^/]+))"),               // /opt/usr/apps/{pkgid}/
59         makeRegexpr("^(/sdcard/apps/([^/]+))"),                // /sdcard/apps/{pkgid}/
60         makeRegexpr("^(/sdcard/app2sd/([^/]+))"),              // /sdcard/app2sd/{pkgid}/
61 #endif
62 };
63
64 } // namespace anonymous
65
66 int File::getPkgTypes(const std::string &user, const std::string &pkgid)
67 {
68         pkgmgrinfo_pkginfo_h handle;
69
70 #ifdef PLATFORM_VERSION_3
71         int ret = -1;
72         if (user.empty())
73                 ret = ::pkgmgrinfo_pkginfo_get_pkginfo(pkgid.c_str(), &handle);
74         else
75                 ret = ::pkgmgrinfo_pkginfo_get_usr_pkginfo(pkgid.c_str(), getUid(user), &handle);
76 #else
77         (void) user;
78         auto ret = ::pkgmgrinfo_pkginfo_get_pkginfo(pkgid.c_str(), &handle);
79 #endif
80
81         if (ret != PMINFO_R_OK) {
82                 INFO("Extracted pkgid[" << pkgid << "] from filepath isn't pkg id. "
83                          "It's not package.");
84                 return 0;
85         }
86
87         auto type = static_cast<int>(Type::Package);
88
89         bool isPreloaded = false;
90         ret = ::pkgmgrinfo_pkginfo_is_preload(handle, &isPreloaded);
91
92         if (ret != PMINFO_R_OK)
93                 ERROR("Failed to ::pkgmgrinfo_pkginfo_is_preload: " << ret);
94
95         if (isPreloaded)
96                 type |= static_cast<int>(Type::PreLoaded);
97
98         ::pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
99
100         return type;
101 }
102
103 std::string File::getPkgPath(const std::string &path)
104 {
105         std::smatch matched;
106
107         for (const auto &rege : g_regexprs) {
108                 if (!std::regex_search(path, matched, rege))
109                         continue;
110
111                 std::string pkgPath;
112                 std::string pkgUser;
113                 std::string pkgId;
114
115                 if (matched.size() == 3) {
116                         pkgPath = matched[1];
117                         pkgId = matched[2];
118                 } else if (matched.size() == 4) {
119                         pkgPath = matched[1];
120                         pkgUser = matched[2];
121                         pkgId = matched[3];
122                 } else {
123                         continue;
124                 }
125
126                 auto type = File::getPkgTypes(pkgUser, pkgId);
127
128                 return ((type & static_cast<int>(Type::Package)) &&
129                            (!(type & static_cast<int>(Type::PreLoaded)))) ? pkgPath : path;
130         }
131
132         return path;
133 }
134
135 File::File(const std::string &fpath, const FilePtr &parentdir, int type,
136                    std::unique_ptr<struct stat> &&statptr) :
137         m_path(fpath), m_type(type), m_statptr(std::move(statptr))
138 {
139         if (parentdir != nullptr) {
140                 if (parentdir->isPackage()) {
141                         this->m_appPkgPath = parentdir->getAppPkgPath();
142                         this->m_appPkgId = parentdir->getAppPkgId();
143                         this->m_appUser = parentdir->getAppUser();
144
145                         this->m_type |= static_cast<int>(File::Type::Package);
146
147                         if (parentdir->isPreloaded())
148                                 this->m_type |= static_cast<int>(File::Type::PreLoaded);
149
150                         return;
151                 } else if (!this->isDir()) {
152                         this->m_type &= ~(static_cast<int>(File::Type::Package) |
153                                                           static_cast<int>(File::Type::PreLoaded));
154                 }
155         }
156
157         std::smatch matched;
158
159         for (const auto &rege : g_regexprs) {
160                 if (!std::regex_search(this->m_path, matched, rege))
161                         continue;
162
163                 if (matched.size() == 3) {
164                         this->m_appPkgPath = matched[1];
165                         this->m_appPkgId = matched[2];
166                         this->m_appUser.clear();
167                 } else if (matched.size() == 4) {
168                         this->m_appPkgPath = matched[1];
169                         this->m_appUser = matched[2];
170                         this->m_appPkgId = matched[3];
171                 } else {
172                         continue;
173                 }
174
175                 this->m_type |= File::getPkgTypes(this->m_appUser, this->m_appPkgId);
176
177                 break;
178         }
179 }
180
181 void File::remove() const
182 {
183         if (this->isInApp()) {
184                 DEBUG("remove app: " << this->m_appPkgId);
185                 AppDeleter::remove(this->m_appPkgId, this->m_appUser);
186         } else {
187                 DEBUG("remove file: " << this->m_path);
188                 if (::remove(this->m_path.c_str()) != 0)
189                         ThrowExc(CSR_ERROR_REMOVE_FAILED,
190                                          "Failed to remove file: " << this->m_path << " with errno: " << errno);
191         }
192 }
193
194 FilePtr File::createIfModified(const std::string &fpath, const FilePtr &parentdir, time_t modifiedSince)
195 {
196         return File::createInternal(fpath, parentdir, modifiedSince, true);
197 }
198
199 FilePtr File::create(const std::string &fpath, const FilePtr &parentdir, time_t modifiedSince)
200 {
201         return File::createInternal(fpath, parentdir, modifiedSince, false);
202 }
203
204 FilePtr File::createInternal(const std::string &fpath, const FilePtr &parentdir,
205                                                          time_t modifiedSince, bool isModifiedOnly)
206 {
207         auto statptr = getStat(fpath);
208
209         if (statptr == nullptr)
210                 ThrowExcWarn(CSR_ERROR_FILE_DO_NOT_EXIST, "file not exist or no permission: " <<
211                                          fpath);
212         else if (!S_ISREG(statptr->st_mode) && !S_ISDIR(statptr->st_mode))
213                 ThrowExc(CSR_ERROR_FILE_SYSTEM, "file type is not reguler or dir: " << fpath);
214
215         auto type = static_cast<int>(S_ISREG(statptr->st_mode) ? Type::File : Type::Directory);
216
217         if (modifiedSince == -1 || statptr->st_ctime > modifiedSince) {
218                 DEBUG("file[" << fpath << "] is changed since[" << modifiedSince << "]");
219                 type |= static_cast<int>(Type::Modified);
220         }
221
222         if (isModifiedOnly && !(type & static_cast<int>(Type::Modified)))
223                 return nullptr;
224         else
225                 return FilePtr(new File(fpath, parentdir, type, std::move(statptr)));
226 }
227
228 FsVisitor::DirPtr FsVisitor::openDir(const std::string &dir)
229 {
230         return std::unique_ptr<DIR, int(*)(DIR *)>(::opendir(dir.c_str()), ::closedir);
231 }
232
233 FsVisitorPtr FsVisitor::create(const std::string &dirpath, bool isBasedOnName, time_t modifiedSince)
234 {
235         auto statptr = getStat(dirpath);
236         if (statptr == nullptr)
237                 ThrowExcWarn(CSR_ERROR_FILE_DO_NOT_EXIST, "directory not exist or no "
238                                          "permission: " << dirpath);
239         else if (!S_ISDIR(statptr->st_mode))
240                 ThrowExc(CSR_ERROR_FILE_SYSTEM, "file type is not directory: " << dirpath);
241         else
242                 return FsVisitorPtr(new FsVisitor(dirpath, isBasedOnName, modifiedSince));
243 }
244
245 FsVisitor::FsVisitor(const std::string &dirpath, bool isBasedOnName, time_t modifiedSince) :
246         m_since(modifiedSince), m_dirptr(nullptr, ::closedir),
247         m_entryBuf(static_cast<struct dirent *>(::malloc(offsetof(struct dirent, d_name) + NAME_MAX + 1))),
248         m_isDone(true), m_isBasedOnName(isBasedOnName)
249 {
250         if (this->m_entryBuf == nullptr)
251                 throw std::bad_alloc();
252
253         this->m_dirs.emplace(File::create(dirpath, nullptr));
254 }
255
256 FsVisitor::~FsVisitor()
257 {
258         ::free(this->m_entryBuf);
259 }
260
261 FilePtr FsVisitor::next()
262 {
263         while (true) {
264                 if (this->m_isDone) {
265                         while (!this->m_dirs.empty() &&
266                                    !(this->m_dirptr = openDir(this->m_dirs.front()->getPath())) &&
267                                    isInBlackList(this->m_dirs.front()->getPath()))
268                                 this->m_dirs.pop();
269
270                         if (this->m_dirs.empty()) {
271                                 this->m_currentdir.reset();
272                                 this->m_isDone = true;
273                                 return nullptr;
274                         } else {
275                                 this->m_currentdir = std::move(this->m_dirs.front());
276                                 this->m_dirs.pop();
277                                 this->m_isDone = false;
278                                 DEBUG("dir opened: " << this->m_currentdir->getPath());
279                         }
280                 }
281
282                 struct dirent *result = nullptr;
283                 const auto &parent_dirpath = this->m_currentdir->getPath();
284                 if (::readdir_r(this->m_dirptr.get(), this->m_entryBuf, &result) != 0) {
285                         ERROR("readdir_r error on dir: " << parent_dirpath <<
286                                   " with errno: " << errno << ". Silently ignore this error & dir stream"
287                                   " to reduce side-effect of traversing all the other file systems.");
288                         this->m_isDone = true;
289                         continue;
290                 } else if (result == nullptr) {
291                         DEBUG("End of stream of dir: " << parent_dirpath);
292                         this->m_isDone = true;
293                         continue;
294                 }
295
296                 const auto &name = result->d_name;
297                 auto name_size = ::strlen(name);
298
299                 if (name_size == 0)
300                         continue;
301
302                 auto fullpath = (parent_dirpath.back() == '/') ?
303                                 (parent_dirpath + name) : (parent_dirpath + "/" + name);
304
305                 if (result->d_type == DT_DIR) {
306                         if ((name_size == 1 && name[0] == '.') ||
307                                 (name_size == 2 && name[0] == '.' && name[1] == '.'))
308                                 continue;
309
310                         FilePtr dirptr;
311                         try {
312                                 dirptr = File::create(fullpath, this->m_currentdir);
313                         } catch (const Exception &e) {
314                                 if (e.error() == CSR_ERROR_FILE_DO_NOT_EXIST) {
315                                         WARN("Perm denied to create file on pkg path: " << fullpath);
316                                         continue;
317                                 } else {
318                                         throw;
319                                 }
320                         }
321
322                         if (this->m_isBasedOnName && dirptr->isInApp())
323                                 return dirptr;
324
325                         DEBUG("push dir to dirs queue: " << fullpath);
326                         this->m_dirs.emplace(std::move(dirptr));
327                 } else if (result->d_type == DT_REG) {
328                         try {
329                                 auto fileptr = File::createIfModified(
330                                                 fullpath, this->m_currentdir, this->m_since);
331
332                                 if (fileptr)
333                                         return fileptr;
334                         } catch (const Exception &e) {
335                                 if (e.error() == CSR_ERROR_FILE_DO_NOT_EXIST)
336                                         WARN("file not exist: " << fullpath << " msg: " << e.what());
337                                 else if (e.error() == CSR_ERROR_FILE_SYSTEM)
338                                         WARN("file type is not regular...? can it be happened?"
339                                                  " :" << fullpath << " msg: " << e.what());
340                                 else
341                                         throw;
342                         }
343                 }
344         }
345
346         return nullptr;
347 }
348
349 } // namespace Csr