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