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 <delta/delta_handler.h>
8 #include <delta/delta_parser.h>
17 #include <system_error>
19 #include "common/utils/file_util.h"
20 #include "common/utils/glist_range.h"
22 namespace ci = common_installer;
23 namespace fs = std::filesystem;
27 const char kBinaryDir[] = "bin";
28 const char kCacheDir[] = "cache";
29 const char kDataDir[] = "data";
30 const char kSharedData[] = "shared/data";
31 const char kSharedTrusted[] = "shared/trusted";
33 const char kDeltaFile[] = "delta_info.xml";
34 const char kXDeltaBinary[] = "/usr/bin/xdelta3";
36 const char kExternalMemoryMountPoint[] = ".mmc";
38 bool ValidateDeltaInfo(const delta::DeltaInfo& info) {
39 for (auto& item : info.added()) {
40 if (ci::HasDirectoryClimbing(item))
43 for (auto& item : info.modified()) {
44 if (ci::HasDirectoryClimbing(item))
47 for (auto& item : info.removed()) {
48 if (ci::HasDirectoryClimbing(item))
54 void RemoveBinarySymlinks(const fs::path& dir) {
55 for (fs::directory_iterator iter(dir / kBinaryDir);
56 iter != fs::directory_iterator(); ++iter) {
57 if (fs::is_symlink(symlink_status(iter->path()))) {
58 // FIXME: note that this assumes that it is not possible to create
59 // explicitly symlinks in bin/ directory pointing to whatever
60 std::error_code error;
61 ci::Remove(iter->path());
66 void RemoveStorageDirectories(const fs::path& dir) {
67 ci::RemoveAll(dir / kDataDir);
68 ci::RemoveAll(dir / kCacheDir);
69 ci::RemoveAll(dir / kSharedData);
70 ci::RemoveAll(dir / kSharedTrusted);
73 void RemoveExtraIconFiles(const fs::path& dir, const fs::path& pkg_dir,
74 manifest_x* manifest) {
75 for (application_x* app : GListRange<application_x*>(manifest->application)) {
76 if (strcmp("webapp", app->type) != 0)
78 auto range = GListRange<icon_x*>(app->icon);
79 auto iter = range.begin();
80 if (iter != range.end()) {
81 std::error_code error;
82 std::string old_path((*iter)->text);
83 fs::path icon_copy = dir / old_path.substr(pkg_dir.string().size());
84 ci::Remove(icon_copy);
89 bool ApplyDeletedFiles(const delta::DeltaInfo& info, const fs::path& app_dir) {
90 for (auto& relative : info.removed()) {
91 if (!ci::Remove(app_dir / relative))
93 LOG(DEBUG) << "Deleted: " << relative;
98 bool ApplyModifiedFiles(const delta::DeltaInfo& info, const fs::path& app_dir,
99 const fs::path& patch_dir, bool is_readonly,
101 for (auto& relative : info.modified()) {
102 fs::path temp_file = ci::GenerateTemporaryPath(
103 fs::path(ci::GetRootAppPath(is_readonly, uid)) / "tmp_file");
104 fs::path patch_file = patch_dir / relative;
105 fs::path input = app_dir / relative;
106 if (!fs::is_regular_file(input)) {
107 LOG(ERROR) << "Cannot modify. Not a regular file: " << input;
112 const char* const argv[] = {
122 int ret = execv(argv[0], const_cast<char* const*>(argv));
124 // no other thing to -> do just quit
127 } else if (pid == -1) {
128 LOG(ERROR) << "Failed to fork with errno: " << errno;
132 waitpid(pid, &status, 0);
134 LOG(ERROR) << "xdelta3 failed with error code: " << status;
138 std::error_code error;
139 fs::copy_file(temp_file, input, fs::copy_options::overwrite_existing,
142 LOG(ERROR) << "Failed to copy from " << temp_file << " to " << input;
143 fs::remove(temp_file, error);
146 ci::Remove(temp_file);
147 LOG(DEBUG) << "Patched: " << relative;
152 bool ApplyAddedFiles(const delta::DeltaInfo& info, const fs::path& app_dir,
153 const fs::path& patch_dir) {
154 for (auto& relative : info.added()) {
155 fs::path source = patch_dir / relative;
156 fs::path target = app_dir / relative;
157 std::error_code error;
158 if (fs::is_directory(source)) {
159 fs::create_directories(target, error);
161 LOG(ERROR) << "Failed to add: " << relative;
165 if (!ci::Remove(target))
167 if (!fs::exists(target.parent_path())) {
168 fs::create_directories(target.parent_path(), error);
170 LOG(ERROR) << "Cannot create directory: " << target.parent_path();
174 if (!ci::MoveFile(source, target)) {
175 LOG(ERROR) << "Failed to move file: " << source << " to " << target;
179 LOG(DEBUG) << "Added: " << relative;
184 bool ApplyPatch(const delta::DeltaInfo& info, const fs::path& app_dir,
185 const fs::path& patch_dir, bool is_readonly, uid_t uid) {
186 if (!ApplyDeletedFiles(info, app_dir))
188 if (!ApplyModifiedFiles(info, app_dir, patch_dir, is_readonly, uid))
190 if (!ApplyAddedFiles(info, app_dir, patch_dir))
195 bool CopySkipMount(const fs::path& from, const fs::path& to) {
196 std::error_code error;
197 fs::create_directory(to, error);
199 LOG(ERROR) << "Failed to create target directory";
202 for (fs::directory_iterator iter(from); iter != fs::directory_iterator();
204 if (iter->path().filename() == kExternalMemoryMountPoint)
207 if (fs::is_directory(iter->path())) {
208 if (!ci::CopyDir(iter->path(), to / iter->path().filename())) {
209 LOG(ERROR) << "Failed to create copy of: " << iter->path();
213 fs::copy(iter->path(), to / iter->path().filename(), error);
215 LOG(ERROR) << "Failed to create copy of: " << iter->path();
225 namespace common_installer {
226 namespace filesystem {
228 StepDeltaPatch::StepDeltaPatch(InstallerContext* context,
229 const std::string& delta_root)
231 delta_root_(delta_root) {
234 Step::Status StepDeltaPatch::precheck() {
235 if (context_->unpacked_dir_path.get().empty()) {
236 LOG(ERROR) << "Unpacked dir is not set";
237 return Status::INVALID_VALUE;
239 if (context_->pkgid.get().empty()) {
240 LOG(ERROR) << "Package id is not set";
241 return Status::PACKAGE_NOT_FOUND;
246 Step::Status StepDeltaPatch::process() {
247 fs::path delta_file = context_->unpacked_dir_path.get() / kDeltaFile;
248 if (!fs::exists(delta_file)) {
249 LOG(ERROR) << "Delta file doesn't exist in package.";
250 return Status::DELTA_ERROR;
252 delta::DeltaParser parser;
253 if (!parser.ParseManifest(delta_file)) {
254 LOG(ERROR) << parser.GetErrorMessage();
255 return Status::DELTA_ERROR;
257 std::shared_ptr<const delta::DeltaInfo> delta_info =
258 std::static_pointer_cast<const delta::DeltaInfo>(
259 parser.GetManifestData(delta::kDeltaInfoKey));
261 LOG(ERROR) << "Failed to parse delta information";
262 return Status::DELTA_ERROR;
265 // additional validation
266 if (!ValidateDeltaInfo(*delta_info)) {
267 LOG(ERROR) << "Delta info is malformed";
268 return Status::DELTA_ERROR;
271 // create old content directory and patch directory
272 patch_dir_ = context_->unpacked_dir_path.get().string() + ".patch";
273 if (!MoveDir(context_->unpacked_dir_path.get(), patch_dir_)) {
274 LOG(ERROR) << "Failed to move content to patch directory";
275 return Status::DELTA_ERROR;
279 context_->root_application_path.get() / context_->pkgid.get()
281 context_->unpacked_dir_path.get())) {
282 LOG(ERROR) << "Failed to copy package files";
283 return Status::DELTA_ERROR;
286 // if there is no root set, that means we need to handle files added by
287 // installer itself (during installation) and files added during runtime
288 // they will be restored in process so just remove extra copy here, so that
289 // it doesn't interfere with installation process
290 if (delta_root_.empty()) {
291 RemoveBinarySymlinks(context_->unpacked_dir_path.get());
292 RemoveStorageDirectories(context_->unpacked_dir_path.get());
293 RemoveExtraIconFiles(
294 context_->unpacked_dir_path.get(),
295 context_->root_application_path.get() / context_->pkgid.get(),
296 context_->old_manifest_data.get());
299 // apply changes mentioned in delta
300 if (!ApplyPatch(*delta_info, context_->unpacked_dir_path.get(), patch_dir_,
301 context_->is_readonly_package.get(), context_->uid.get()))
302 return Status::DELTA_ERROR;
304 ci::RemoveAll(patch_dir_);
305 LOG(INFO) << "Delta patch applied successfully";
309 Step::Status StepDeltaPatch::undo() {
310 RemoveAll(patch_dir_);
314 } // namespace filesystem
315 } // namespace common_installer