Remove boost dependency
[platform/core/appfw/app-installers.git] / src / common / step / backup / step_copy_backup.cc
1 // Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by a apache 2.0 license that can be
3 // found in the LICENSE file.
4
5 #include "common/step/backup/step_copy_backup.h"
6
7 #include <sys/types.h>
8 #include <tzplatform_config.h>
9
10 #include <algorithm>
11 #include <cassert>
12 #include <cstdint>
13 #include <filesystem>
14 #include <string>
15 #include <system_error>
16
17 #include "common/utils/paths.h"
18 #include "common/shared_dirs.h"
19 #include "common/utils/file_util.h"
20 #include "common/utils/user_util.h"
21
22 namespace ci = common_installer;
23 namespace fs = std::filesystem;
24
25 namespace {
26
27 const char kExternalMemoryMountPoint[] = ".mmc";
28 const char kSharedResPath[] = "shared/res";
29
30 bool CheckFreeSpace(const fs::path& backup_path, const fs::path& shared_path) {
31   int64_t shared_size = ci::GetDirectorySize(shared_path);
32
33   if (!ci::CheckFreeSpaceAtPath(shared_size, backup_path))
34     return false;
35
36   return true;
37 }
38
39 bool CreateSharedRes(const fs::path& src, const fs::path& dst) {
40   std::error_code error;
41
42   fs::create_directories(dst / kSharedResPath, error);
43   if (error) {
44     LOG(ERROR) << "Cannot create package directory";
45     return false;
46   }
47
48   if (!ci::CopyOwnershipAndPermissions(src / "shared", dst / "shared") ||
49       !ci::CopyOwnershipAndPermissions(src / kSharedResPath,
50           dst / kSharedResPath))
51     return false;
52
53   return true;
54 }
55
56 bool Move(const std::filesystem::path& from,
57     const std::filesystem::path& to,
58     common_installer::FSFlag flag = common_installer::FSFlag::FS_NONE) {
59   if (fs::is_directory(from)) {
60     if (!common_installer::MoveDir(from, to / from.filename(), flag)) {
61       LOG(ERROR) << "Failed to move directory: " << from;
62       return false;
63     }
64   } else {
65     if (!common_installer::MoveFile(from, to / from.filename())) {
66       LOG(ERROR) << "Fail to move file: " << from;
67       return false;
68     }
69   }
70
71   return true;
72 }
73
74 }  // namespace
75
76 namespace common_installer {
77 namespace backup {
78
79 Step::Status StepCopyBackup::precheck() {
80   if (context_->pkgid.get().empty()) {
81     LOG(ERROR) << "pkgid attribute is empty";
82     return Step::Status::PACKAGE_NOT_FOUND;
83   }
84
85   if (context_->root_application_path.get().empty()) {
86     LOG(ERROR) << "root_application_path attribute is empty";
87     return Step::Status::INVALID_VALUE;
88   }
89
90   install_path_ = context_->GetPkgPath();
91   backup_path_ = GetBackupPathForPackagePath(context_->GetPkgPath());
92
93   // We only "copy" shared dir for backup.
94   // So if there is no shared dir, we don't need to check free space.
95   fs::path shared_dir = install_path_ / "shared";
96   if (!fs::exists(shared_dir))
97     return Status::OK;
98
99   if (!CheckFreeSpace(backup_path_, shared_dir)) {
100     LOG(ERROR) << "not enough space for backup";
101     return Step::Status::OUT_OF_SPACE;
102   }
103
104   return Status::OK;
105 }
106
107 Step::Status StepCopyBackup::process() {
108   if (!CleanBackupDirectory())
109     return Status::APP_DIR_ERROR;
110
111   if (!Backup())
112     return Status::APP_DIR_ERROR;
113
114   if (!NewContent())
115     return Status::APP_DIR_ERROR;
116
117   RemoveContent();
118
119   return Status::OK;
120 }
121
122 Step::Status StepCopyBackup::clean() {
123   LOG(DEBUG) << "Remove Applications files backup directory";
124   CleanBackupDirectory();
125
126   if (context_->external_storage)
127     context_->external_storage->Commit();
128
129   return Status::OK;
130 }
131
132 Step::Status StepCopyBackup::undo() {
133   if (context_->external_storage)
134     context_->external_storage->Abort();
135
136   if (!fs::exists(backup_path_))
137     return Status::OK;
138
139   if (!RollbackApplicationDirectory()) {
140     LOG(ERROR) << "Failed to revert package directory";
141     return Status::APP_DIR_ERROR;
142   }
143
144   LOG(DEBUG) << "Application files reverted from backup";
145
146   return Status::OK;
147 }
148
149 bool StepCopyBackup::Backup() {
150   std::error_code error;
151
152   if (!fs::exists(backup_path_)) {
153     fs::create_directories(backup_path_, error);
154     if (error) {
155       LOG(ERROR) << "Failed to create backup directory: " << backup_path_;
156       return false;
157     }
158   }
159   // create copy of old package content skipping the external memory mount point
160   for (fs::directory_iterator iter(context_->GetPkgPath());
161        iter != fs::directory_iterator(); ++iter) {
162     if (iter->path().filename() == kExternalMemoryMountPoint)
163       continue;
164
165     // external storage directories are mounted
166     // therefore move only content
167     if (context_->external_storage) {
168       auto& ext_dirs = context_->external_storage->external_dirs();
169       auto found = std::find(ext_dirs.begin(), ext_dirs.end(),
170                              iter->path().filename().string());
171       if (found != ext_dirs.end()) {
172         bool done = MoveMountPointContent(iter->path(),
173             backup_path_ / iter->path().filename());
174         if (!done) {
175           LOG(ERROR) << "Failed to move: " << iter->path();
176           return false;
177         }
178         continue;
179       }
180     }
181
182     if (!Move(iter->path(), backup_path_))
183       return false;
184   }
185
186   AddRecoveryInfo();
187
188   LOG(INFO) << "Old package context saved to: " << backup_path_;
189   return true;
190 }
191
192
193 bool StepCopyBackup::MoveMountPointContent(const std::filesystem::path& from,
194                                            const std::filesystem::path& to) {
195   std::error_code error;
196   fs::create_directories(to, error);
197
198   for (fs::directory_iterator iter(from);
199        iter != fs::directory_iterator(); ++iter) {
200     if (fs::is_symlink(symlink_status(iter->path()))) {
201       fs::copy_symlink(iter->path(), to / iter->path().filename(), error);
202       if (error) {
203         LOG(ERROR) << "Failed to backup package symlink: " << iter->path();
204         return false;
205       }
206     } else {
207       if (!Move(iter->path(), to)) {
208         LOG(ERROR) << "Fail to backup package file: " << iter->path();
209         return false;
210       }
211     }
212   }
213
214   return true;
215 }
216
217 void StepCopyBackup::RemoveContent() {
218   if (context_->request_type.get() == RequestType::Update &&
219     !context_->external_storage && fs::exists(install_path_ / ".mmc")) {
220     LOG(WARNING) << "Remove unnecessary files for external storage";
221
222     std::error_code error;
223     fs::remove((install_path_ / ".mmc"), error);
224     if (error)
225       LOG(WARNING) << "error while remove files";
226   }
227 }
228
229 bool StepCopyBackup::NewContent() {
230   ci::RemoveRWDirectories(context_->unpacked_dir_path.get());
231
232   std::error_code error;
233   fs::create_directories(install_path_.parent_path(), error);
234   if (error) {
235     LOG(ERROR) << "Cannot create package directory";
236     return false;
237   }
238
239   if (ShouldBackupSharedRes()) {
240     if (!CreateSharedRes(backup_path_, context_->GetPkgPath()))
241       return false;
242   }
243
244   for (fs::directory_iterator iter(context_->unpacked_dir_path.get());
245        iter != fs::directory_iterator(); ++iter) {
246     if (!Move(iter->path(), install_path_, FS_MERGE_SKIP))
247       return false;
248   }
249
250   // If other application tries to access shared/res of package being installed,
251   // it will be failed due to permission deny. Set its permission before
252   // StepChangeOwnershipAndPermission to prevent it.
253   uid_t uid = context_->uid.get();
254   std::optional<gid_t> gid = common_installer::GetGidByUid(uid);
255   if (!gid) {
256     LOG(ERROR) << "Failed to get gid";
257     return false;
258   }
259   if (fs::exists(install_path_ / "shared/res") &&
260       !common_installer::SetOwnershipAll(
261           install_path_ / "shared/res", uid, *gid)) {
262     LOG(ERROR) << "Failed to set ownership";
263     return false;
264   }
265   LOG(INFO) << "Successfully move: " << context_->unpacked_dir_path.get()
266             << " to: " << install_path_ << " directory";
267
268   ci::RemoveAll(context_->unpacked_dir_path.get());
269   return true;
270 }
271
272 bool StepCopyBackup::CleanBackupDirectory() {
273   return ci::RemoveAll(backup_path_);
274 }
275
276 bool StepCopyBackup::RollbackApplicationDirectory() {
277   std::error_code error;
278   fs::path root_path = context_->GetPkgPath();
279   if (fs::exists(root_path)) {
280     for (fs::directory_iterator iter(root_path);
281          iter != fs::directory_iterator(); ++iter) {
282       fs::remove_all(iter->path(), error);
283       if (error)
284         return false;
285     }
286   }
287
288   for (fs::directory_iterator iter(backup_path_);
289        iter != fs::directory_iterator(); ++iter) {
290     if (!Move(iter->path(), root_path)) {
291       LOG(ERROR) << "Failed to recovery backup file(" << iter->path() << ")";
292       return false;
293     }
294   }
295   RemoveAll(backup_path_);
296
297   uid_t uid = context_->uid.get();
298   // restore ownership changed during installation
299   if (!SetPackageDirectoryOwnerAndPermissions(context_->GetPkgPath(), uid))
300     return false;
301
302   return true;
303 }
304
305 void StepCopyBackup::AddRecoveryInfo() {
306   recovery::RecoveryFile* recovery_file =
307       context_->recovery_info.get().recovery_file.get();
308   recovery_file->set_backup_done(true);
309   recovery_file->WriteAndCommitFileContent();
310 }
311
312 bool StepCopyBackup::ShouldBackupSharedRes() {
313   if (fs::exists(backup_path_ / kSharedResPath) &&
314       fs::exists(context_->unpacked_dir_path.get() / kSharedResPath))
315     return true;
316
317   return false;
318 }
319
320 }  // namespace backup
321 }  // namespace common_installer