1 // Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by an apache-2.0 license that can be
3 // found in the LICENSE file.
5 #include "common/step/filesystem/step_delta_patch.h"
7 #include <boost/system/error_code.hpp>
8 #include <boost/filesystem/path.hpp>
9 #include <delta/delta_handler.h>
10 #include <delta/delta_parser.h>
11 #include <sys/types.h>
19 #include "common/utils/file_util.h"
20 #include "common/utils/glist_range.h"
22 namespace bf = boost::filesystem;
23 namespace bs = boost::system;
24 namespace ci = common_installer;
28 const char kBinaryDir[] = "bin";
29 const char kCacheDir[] = "cache";
30 const char kDataDir[] = "data";
31 const char kSharedData[] = "shared/data";
32 const char kSharedTrusted[] = "shared/trusted";
34 const char kDeltaFile[] = "delta_info.xml";
35 const char kXDeltaBinary[] = "/usr/bin/xdelta3";
37 const char kExternalMemoryMountPoint[] = ".mmc";
39 bool ValidateDeltaInfo(const delta::DeltaInfo& info) {
40 for (auto& item : info.added()) {
41 if (ci::HasDirectoryClimbing(item))
44 for (auto& item : info.modified()) {
45 if (ci::HasDirectoryClimbing(item))
48 for (auto& item : info.removed()) {
49 if (ci::HasDirectoryClimbing(item))
55 void RemoveBinarySymlinks(const bf::path& dir) {
56 for (bf::directory_iterator iter(dir / kBinaryDir);
57 iter != bf::directory_iterator(); ++iter) {
58 if (bf::is_symlink(symlink_status(iter->path()))) {
59 // FIXME: note that this assumes that it is not possible to create
60 // explicitly symlinks in bin/ directory pointing to whatever
62 ci::Remove(iter->path());
67 void RemoveStorageDirectories(const bf::path& dir) {
68 ci::RemoveAll(dir / kDataDir);
69 ci::RemoveAll(dir / kCacheDir);
70 ci::RemoveAll(dir / kSharedData);
71 ci::RemoveAll(dir / kSharedTrusted);
74 void RemoveExtraIconFiles(const bf::path& dir, const bf::path& pkg_dir,
75 manifest_x* manifest) {
76 for (application_x* app : GListRange<application_x*>(manifest->application)) {
77 if (strcmp("webapp", app->type) != 0)
79 auto range = GListRange<icon_x*>(app->icon);
80 auto iter = range.begin();
81 if (iter != range.end()) {
83 std::string old_path((*iter)->text);
84 bf::path icon_copy = dir / old_path.substr(pkg_dir.string().size());
85 ci::Remove(icon_copy);
90 bool ApplyDeletedFiles(const delta::DeltaInfo& info, const bf::path& app_dir) {
91 for (auto& relative : info.removed()) {
92 if (!ci::Remove(app_dir / relative))
94 LOG(DEBUG) << "Deleted: " << relative;
99 bool ApplyModifiedFiles(const delta::DeltaInfo& info, const bf::path& app_dir,
100 const bf::path& patch_dir, bool is_readonly,
102 for (auto& relative : info.modified()) {
103 bf::path temp_file = ci::GenerateTemporaryPath(
104 bf::path(ci::GetRootAppPath(is_readonly, uid)) / "tmp_file");
105 bf::path patch_file = patch_dir / relative;
106 bf::path input = app_dir / relative;
107 if (!bf::is_regular_file(input)) {
108 LOG(ERROR) << "Cannot modify. Not a regular file: " << input;
113 const char* const argv[] = {
123 int ret = execv(argv[0], const_cast<char* const*>(argv));
125 // no other thing to -> do just quit
128 } else if (pid == -1) {
129 LOG(ERROR) << "Failed to fork with errno: " << errno;
133 waitpid(pid, &status, 0);
135 LOG(ERROR) << "xdelta3 failed with error code: " << status;
139 bs::error_code error;
140 bf::copy_file(temp_file, input, bf::copy_option::overwrite_if_exists,
143 LOG(ERROR) << "Failed to copy from " << temp_file << " to " << input;
144 bf::remove(temp_file, error);
147 ci::Remove(temp_file);
148 LOG(DEBUG) << "Patched: " << relative;
153 bool ApplyAddedFiles(const delta::DeltaInfo& info, const bf::path& app_dir,
154 const bf::path& patch_dir) {
155 for (auto& relative : info.added()) {
156 bf::path source = patch_dir / relative;
157 bf::path target = app_dir / relative;
158 bs::error_code error;
159 if (bf::is_directory(source)) {
160 bf::create_directories(target, error);
162 LOG(ERROR) << "Failed to add: " << relative;
166 if (!ci::Remove(target))
168 if (!bf::exists(target.parent_path())) {
169 bf::create_directories(target.parent_path(), error);
171 LOG(ERROR) << "Cannot create directory: " << target.parent_path();
175 if (!ci::MoveFile(source, target)) {
176 LOG(ERROR) << "Failed to move file: " << source << " to " << target;
180 LOG(DEBUG) << "Added: " << relative;
185 bool ApplyPatch(const delta::DeltaInfo& info, const bf::path& app_dir,
186 const bf::path& patch_dir, bool is_readonly, uid_t uid) {
187 if (!ApplyDeletedFiles(info, app_dir))
189 if (!ApplyModifiedFiles(info, app_dir, patch_dir, is_readonly, uid))
191 if (!ApplyAddedFiles(info, app_dir, patch_dir))
196 bool CopySkipMount(const bf::path& from, const bf::path& to) {
197 bs::error_code error;
198 bf::create_directory(to, error);
200 LOG(ERROR) << "Failed to create target directory";
203 for (bf::directory_iterator iter(from); iter != bf::directory_iterator();
205 if (iter->path().filename() == kExternalMemoryMountPoint)
208 if (bf::is_directory(iter->path())) {
209 if (!ci::CopyDir(iter->path(), to / iter->path().filename())) {
210 LOG(ERROR) << "Failed to create copy of: " << iter->path();
214 bs::error_code error;
215 bf::copy(iter->path(), to / iter->path().filename(), error);
217 LOG(ERROR) << "Failed to create copy of: " << iter->path();
227 namespace common_installer {
228 namespace filesystem {
230 StepDeltaPatch::StepDeltaPatch(InstallerContext* context,
231 const std::string& delta_root)
233 delta_root_(delta_root) {
236 Step::Status StepDeltaPatch::precheck() {
237 if (context_->unpacked_dir_path.get().empty()) {
238 LOG(ERROR) << "Unpacked dir is not set";
239 return Status::INVALID_VALUE;
241 if (context_->pkgid.get().empty()) {
242 LOG(ERROR) << "Package id is not set";
243 return Status::PACKAGE_NOT_FOUND;
248 Step::Status StepDeltaPatch::process() {
249 bf::path delta_file = context_->unpacked_dir_path.get() / kDeltaFile;
250 if (!bf::exists(delta_file)) {
251 LOG(ERROR) << "Delta file doesn't exist in package.";
252 return Status::DELTA_ERROR;
254 delta::DeltaParser parser;
255 if (!parser.ParseManifest(delta_file)) {
256 LOG(ERROR) << parser.GetErrorMessage();
257 return Status::DELTA_ERROR;
259 std::shared_ptr<const delta::DeltaInfo> delta_info =
260 std::static_pointer_cast<const delta::DeltaInfo>(
261 parser.GetManifestData(delta::kDeltaInfoKey));
263 LOG(ERROR) << "Failed to parse delta information";
264 return Status::DELTA_ERROR;
267 // additional validation
268 if (!ValidateDeltaInfo(*delta_info)) {
269 LOG(ERROR) << "Delta info is malformed";
270 return Status::DELTA_ERROR;
273 // create old content directory and patch directory
274 patch_dir_ = context_->unpacked_dir_path.get();
275 patch_dir_ += ".patch";
276 if (!MoveDir(context_->unpacked_dir_path.get(), patch_dir_)) {
277 LOG(ERROR) << "Failed to move content to patch directory";
278 return Status::DELTA_ERROR;
282 context_->root_application_path.get() / context_->pkgid.get()
284 context_->unpacked_dir_path.get())) {
285 LOG(ERROR) << "Failed to copy package files";
286 return Status::DELTA_ERROR;
289 // if there is no root set, that means we need to handle files added by
290 // installer itself (during installation) and files added during runtime
291 // they will be restored in process so just remove extra copy here, so that
292 // it doesn't interfere with installation process
293 if (delta_root_.empty()) {
294 RemoveBinarySymlinks(context_->unpacked_dir_path.get());
295 RemoveStorageDirectories(context_->unpacked_dir_path.get());
296 RemoveExtraIconFiles(
297 context_->unpacked_dir_path.get(),
298 context_->root_application_path.get() / context_->pkgid.get(),
299 context_->old_manifest_data.get());
302 // apply changes mentioned in delta
303 if (!ApplyPatch(*delta_info, context_->unpacked_dir_path.get(), patch_dir_,
304 context_->is_readonly_package.get(), context_->uid.get()))
305 return Status::DELTA_ERROR;
307 ci::RemoveAll(patch_dir_);
308 LOG(INFO) << "Delta patch applied successfully";
312 Step::Status StepDeltaPatch::undo() {
313 RemoveAll(patch_dir_);
317 } // namespace filesystem
318 } // namespace common_installer