External installation 80/61680/23
authorTomasz Iwanek <t.iwanek@samsung.com>
Fri, 2 Oct 2015 09:01:32 +0000 (11:01 +0200)
committerjongmyeong ko <jongmyeong.ko@samsung.com>
Wed, 1 Jun 2016 00:47:06 +0000 (17:47 -0700)
This patch implements installation of package in external sd card storage.
Tpk and wgt applications may declare preference of being installed on external
storage. If so and space requirement is satisified then their resource directory
is installed in sd card with use of app2sd API.

Following patchsets should be submitted together:
 - https://review.tizen.org/gerrit/61678
 - https://review.tizen.org/gerrit/61679
 - https://review.tizen.org/gerrit/61680

Verify by:
 - running smoke tests,
 - running installation, update, deinstallation for package that prefers external
   installation (SD card must be inserted).

Requires:
 - https://review.tizen.org/gerrit/#/c/64796/

Change-Id: I3c49eaa3c65ce318b2e1ce6ccf049d285a83e58b
Signed-off-by: jongmyeongko <jongmyeong.ko@samsung.com>
21 files changed:
CMakeLists.txt
packaging/app-installers.spec
src/common/CMakeLists.txt
src/common/app-installers.pc.in
src/common/external_storage.cc [new file with mode: 0644]
src/common/external_storage.h [new file with mode: 0644]
src/common/installer_context.h
src/common/pkgmgr_registration.cc
src/common/pkgmgr_registration.h
src/common/step/backup/step_copy_backup.cc
src/common/step/backup/step_copy_backup.h
src/common/step/configuration/step_parse_manifest.cc
src/common/step/filesystem/step_acquire_external_storage.cc [new file with mode: 0644]
src/common/step/filesystem/step_acquire_external_storage.h [new file with mode: 0644]
src/common/step/filesystem/step_copy.cc
src/common/step/filesystem/step_copy.h
src/common/step/filesystem/step_recover_external_storage.cc [new file with mode: 0644]
src/common/step/filesystem/step_recover_external_storage.h [new file with mode: 0644]
src/common/step/filesystem/step_remove_files.cc
src/common/utils/file_util.cc
src/common/utils/file_util.h

index ab9e6fc..e33e5ef 100644 (file)
@@ -56,6 +56,7 @@ PKG_CHECK_MODULES(TPK_MANIFEST_HANDLERS_DEPS REQUIRED tpk-manifest-handlers)
 PKG_CHECK_MODULES(DBUS_DEPS REQUIRED dbus-1)
 PKG_CHECK_MODULES(GDBUS_DEPS REQUIRED glib-2.0 gio-2.0)
 PKG_CHECK_MODULES(GUM_DEPS REQUIRED libgum)
+PKG_CHECK_MODULES(APP2SD_DEPS REQUIRED app2sd)
 
 FIND_PACKAGE(Boost REQUIRED COMPONENTS system filesystem regex program_options)
 FIND_PACKAGE(GTest REQUIRED)
index 9fdd323..658b442 100644 (file)
@@ -35,6 +35,7 @@ BuildRequires:  pkgconfig(tpk-manifest-handlers)
 BuildRequires:  pkgconfig(dbus-1)
 BuildRequires:  pkgconfig(aul)
 BuildRequires:  pkgconfig(libgum)
+BuildRequires:  pkgconfig(app2sd)
 
 Requires: ca-certificates-tizen
 Requires: libtzplatform-config
index ae6658d..b433bd3 100644 (file)
@@ -3,8 +3,8 @@ SET(SRCS
   app_installer.cc
   backup_paths.cc
   certificate_validation.cc
+  external_storage.cc
   installer_context.cc
-  shared_dirs.cc
   plugins/plugin_factory.cc
   plugins/plugin_manager.cc
   plugins/plugin_list_parser.cc
@@ -21,6 +21,7 @@ SET(SRCS
   recovery_file.cc
   request.cc
   security_registration.cc
+  shared_dirs.cc
   step/backup/step_backup_icons.cc
   step/backup/step_backup_manifest.cc
   step/backup/step_copy_backup.cc
@@ -29,6 +30,7 @@ SET(SRCS
   step/configuration/step_configure.cc
   step/configuration/step_fail.cc
   step/configuration/step_parse_manifest.cc
+  step/filesystem/step_acquire_external_storage.cc
   step/filesystem/step_clear_data.cc
   step/filesystem/step_copy.cc
   step/filesystem/step_copy_storage_directories.cc
@@ -40,6 +42,7 @@ SET(SRCS
   step/filesystem/step_recover_files.cc
   step/filesystem/step_recover_icons.cc
   step/filesystem/step_recover_manifest.cc
+  step/filesystem/step_recover_external_storage.cc
   step/filesystem/step_recover_storage_directories.cc
   step/filesystem/step_remove_files.cc
   step/filesystem/step_remove_icons.cc
@@ -101,6 +104,7 @@ APPLY_PKG_CONFIG(${TARGET_LIBNAME_COMMON} PUBLIC
   TPK_MANIFEST_HANDLERS_DEPS
   GDBUS_DEPS
   GUM_DEPS
+  APP2SD_DEPS
   Boost
 )
 
index db03b61..ea5f218 100644 (file)
@@ -6,6 +6,6 @@ includedir=@INCLUDEDIR@
 Name: app-installers
 Description: Common library for pkgmgr backends
 Version: @VERSION@
-Requires: pkgmgr pkgmgr-installer minizip zlib libtzplatform-config security-manager manifest-parser-utils delta-manifest-handlers cert-svc-vcore pkgmgr-parser pkgmgr-info libxml-2.0 security-privilege-manager
+Requires: pkgmgr pkgmgr-installer minizip zlib libtzplatform-config security-manager manifest-parser-utils delta-manifest-handlers cert-svc-vcore pkgmgr-parser pkgmgr-info libxml-2.0 security-privilege-manager app2sd
 Libs: -L${libdir} -lapp-installers
 Cflags: -I${includedir}/app-installers/
diff --git a/src/common/external_storage.cc b/src/common/external_storage.cc
new file mode 100644 (file)
index 0000000..3df4a69
--- /dev/null
@@ -0,0 +1,181 @@
+// Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache-2.0 license that can be
+// found in the LICENSE file.
+
+#include "common/external_storage.h"
+
+#include <glib.h>
+
+#include <manifest_parser/utils/logging.h>
+
+#include "common/utils/byte_size_literals.h"
+#include "common/utils/file_util.h"
+
+namespace bf = boost::filesystem;
+
+namespace {
+
+const char kWgtType[] = "wgt";
+const char kExternalDirForWgt[] = "res";
+
+const std::vector<std::string> kExternalDirsForTpk = {
+  "bin",
+  "lib",
+  "res"
+};
+
+int64_t SizeInMB(int64_t size) {
+  return (size + 1_MB - 1) / 1_MB;
+}
+
+}  // namespace
+
+namespace common_installer {
+
+ExternalStorage::ExternalStorage(RequestType type,
+    const std::string& pkgid, const std::string& package_type,
+    const boost::filesystem::path& application_root, uid_t uid)
+    : type_(type),
+      pkgid_(pkgid),
+      package_type_(package_type),
+      application_root_(application_root),
+      uid_(uid),
+      handle_(nullptr) {
+  if (package_type_ == kWgtType) {
+    external_dirs_.push_back(kExternalDirForWgt);
+  } else {
+    external_dirs_ = kExternalDirsForTpk;
+  }
+}
+
+ExternalStorage::~ExternalStorage() {
+  if (handle_)
+    app2ext_deinit(handle_);
+}
+
+bool ExternalStorage::Finalize(bool success) {
+  int ret = APP2EXT_STATUS_SUCCESS;
+  switch (type_) {
+  case RequestType::Install: {
+    ret = handle_->interface.client_usr_post_install(pkgid_.c_str(),
+        success ? APP2EXT_STATUS_SUCCESS : APP2EXT_STATUS_FAILED, uid_);
+    break;
+  }
+  case RequestType::Update: {
+    ret = handle_->interface.client_usr_post_upgrade(pkgid_.c_str(),
+        success ? APP2EXT_STATUS_SUCCESS : APP2EXT_STATUS_FAILED, uid_);
+    break;
+  }
+  case RequestType::Uninstall: {
+    ret = handle_->interface.client_usr_post_uninstall(pkgid_.c_str(), uid_);
+    break;
+  }
+  default:
+    assert(false && "Not supported installation mode");
+  }
+  return ret == APP2EXT_STATUS_SUCCESS;
+}
+
+bool ExternalStorage::Commit() {
+  return Finalize(true);
+}
+
+bool ExternalStorage::Abort() {
+  return Finalize(false);
+}
+
+const std::vector<std::string>& ExternalStorage::external_dirs() const {
+  return external_dirs_;
+}
+
+bool ExternalStorage::Initialize(
+    const boost::filesystem::path& space_requirement) {
+  // external size in MB, set any-non zero size as default
+  int external_size = 1;
+
+  if (!space_requirement.empty()) {
+    if (package_type_ == kWgtType) {
+      for (auto& dir : kExternalDirsForTpk) {
+        bf::path requirement = space_requirement / dir;
+        if (!bf::exists(requirement))
+          continue;
+        external_size +=
+            SizeInMB(GetDirectorySize(requirement));
+      }
+    } else {
+      // for wgt whole content of package goes to res/
+      external_size =
+          SizeInMB(GetDirectorySize(space_requirement));
+    }
+  }
+
+  if (external_size == 0)
+    external_size = 1;
+
+  handle_ = app2ext_init(APP2EXT_SD_CARD);
+  if (!handle_) {
+    LOG(ERROR) << "app2ext_init() failed";
+    return false;
+  }
+
+  GList* glist = nullptr;
+  for (auto& dir : external_dirs_) {
+    app2ext_dir_details* dir_detail = reinterpret_cast<app2ext_dir_details*>(
+        calloc(1, sizeof(app2ext_dir_details)));
+    dir_detail->name = strdup(dir.c_str());
+    dir_detail->type = APP2EXT_DIR_RO;
+    glist = g_list_append(glist, dir_detail);
+  }
+
+  int ret = 0;
+  switch (type_) {
+  case RequestType::Install:
+    ret = handle_->interface.client_usr_pre_install(pkgid_.c_str(), glist,
+                                                    external_size, uid_);
+    break;
+  case RequestType::Update:
+  case RequestType::Delta:
+    ret = handle_->interface.client_usr_pre_upgrade(pkgid_.c_str(), glist,
+                                                    external_size, uid_);
+    break;
+  case RequestType::Uninstall:
+    ret = handle_->interface.client_usr_pre_uninstall(pkgid_.c_str(), uid_);
+    break;
+  case RequestType::Clear:
+  case RequestType::Reinstall:
+  case RequestType::Recovery:
+  case RequestType::ManifestDirectInstall:
+  case RequestType::ManifestDirectUpdate:
+  case RequestType::MountInstall:
+  case RequestType::MountUpdate:
+    LOG(ERROR) << "Installation type is not supported by external installation";
+    ret = -1;
+    break;
+  default:
+    assert(false && "Invalid installation mode");
+  }
+
+  g_list_free_full(glist, [](gpointer data) {
+      app2ext_dir_details* dir_detail =
+          reinterpret_cast<app2ext_dir_details*>(data);
+      free(dir_detail->name);
+      free(dir_detail);
+    });
+  return ret == 0;
+}
+
+std::unique_ptr<ExternalStorage> ExternalStorage::AcquireExternalStorage(
+    RequestType type, const boost::filesystem::path& application_root,
+    const std::string& pkgid, const std::string& package_type,
+    const boost::filesystem::path& space_requirement,
+    uid_t uid) {
+  std::unique_ptr<ExternalStorage> external_storage(
+      new ExternalStorage(type, pkgid, package_type, application_root, uid));
+  if (!external_storage->Initialize(space_requirement)) {
+    LOG(WARNING) << "Cannot initialize external storage for request";
+    return nullptr;
+  }
+  return external_storage;
+}
+
+}  // namespace common_installer
diff --git a/src/common/external_storage.h b/src/common/external_storage.h
new file mode 100644 (file)
index 0000000..dca7a3d
--- /dev/null
@@ -0,0 +1,52 @@
+// Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache-2.0 license that can be
+// found in the LICENSE file.
+
+#ifndef COMMON_EXTERNAL_STORAGE_H_
+#define COMMON_EXTERNAL_STORAGE_H_
+
+#include <boost/filesystem/path.hpp>
+
+#include <app2ext_interface.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "common/request.h"
+
+namespace common_installer {
+
+class ExternalStorage final {
+ public:
+  static std::unique_ptr<ExternalStorage> AcquireExternalStorage(
+      RequestType type, const boost::filesystem::path& application_root,
+      const std::string& pkgid, const std::string& package_type,
+      const boost::filesystem::path& space_requirement, uid_t uid);
+
+  ExternalStorage(RequestType type, const std::string& pkgid,
+                  const std::string& package_type,
+                  const boost::filesystem::path& application_root,  uid_t uid);
+  ~ExternalStorage();
+
+  bool Commit();
+  bool Abort();
+
+  const std::vector<std::string>& external_dirs() const;
+
+ private:
+  bool Initialize(const boost::filesystem::path& space_requirement);
+  bool Finalize(bool success);
+
+  RequestType type_;
+  std::string pkgid_;
+  std::string package_type_;
+  boost::filesystem::path application_root_;
+  uid_t uid_;
+  app2ext_handle* handle_;
+  std::vector<std::string> external_dirs_;
+};
+
+}  // namespace common_installer
+
+#endif  // COMMON_EXTERNAL_STORAGE_H_
index 7b1e5b6..d018a96 100644 (file)
@@ -19,6 +19,7 @@
 #include <utility>
 #include <vector>
 
+#include "common/external_storage.h"
 #include "common/pkgmgr_interface.h"
 #include "common/recovery_file.h"
 #include "common/request.h"
@@ -314,6 +315,11 @@ class InstallerContext {
   * \brief Property of vector of files to delete
   */
   Property<std::vector<std::string>> files_to_delete;
+
+  /**
+   * @brief External Storage object if installing in external
+   */
+  std::unique_ptr<ExternalStorage> external_storage;
 };
 
 }  // namespace common_installer
index 0fd6ab3..ca51a99 100644 (file)
@@ -150,7 +150,6 @@ int PkgmgrForeachPrivilegeCallback(const char* privilege_name,
 // "removable" : this package can be removed.
 // "readonly" : this package exists in readonly location.
 bool AssignPackageTags(manifest_x* manifest,
-                       common_installer::RequestMode,
                        bool is_update) {
   if (!strcmp(manifest->preload, "true")) {
     manifest->removable = strdup("false");
@@ -166,8 +165,6 @@ bool AssignPackageTags(manifest_x* manifest,
     manifest->system = strdup("false");
     manifest->update = strdup("false");
   }
-  // external installation should alter this flag
-  manifest->installed_storage = strdup("installed_internal");
 
   return true;
 }
@@ -187,7 +184,7 @@ bool RegisterAppInPkgmgr(manifest_x* manifest,
   if (!tep_path.empty())
     manifest->tep_name = strdup(tep_path.c_str());
 
-  if (!AssignPackageTags(manifest, request_mode, false))
+  if (!AssignPackageTags(manifest, false))
     return false;
 
   int ret = request_mode != RequestMode::GLOBAL ?
@@ -214,7 +211,7 @@ bool UpgradeAppInPkgmgr(manifest_x* manifest,
                         const CertificateInfo& cert_info,
                         uid_t uid,
                         RequestMode request_mode) {
-  if (!AssignPackageTags(manifest, request_mode, true))
+  if (!AssignPackageTags(manifest, true))
     return false;
 
   int ret = request_mode != RequestMode::GLOBAL ?
@@ -355,6 +352,32 @@ bool QueryPrivilegesForPkgId(const std::string& pkg_id, uid_t uid,
   return ret;
 }
 
+std::string QueryStorageForPkgId(const std::string& pkg_id, uid_t uid) {
+  pkgmgrinfo_pkginfo_h package_info;
+  if (pkgmgrinfo_pkginfo_get_usr_pkginfo(pkg_id.c_str(), uid, &package_info)
+      != PMINFO_R_OK) {
+    return false;
+  }
+
+  pkgmgrinfo_installed_storage storage;
+  bool ok = pkgmgrinfo_pkginfo_get_installed_storage(package_info,
+      &storage) == PMINFO_R_OK;
+  pkgmgrinfo_pkginfo_destroy_pkginfo(package_info);
+
+  if (!ok)
+    return "";
+
+  // TODO(t.iwanek): enum is used in pkgmgr API, whereas here we assign internal
+  // values known to pkgmgr
+  if (storage == PMINFO_INTERNAL_STORAGE) {
+    return "installed_internal";
+  } else if (storage == PMINFO_EXTERNAL_STORAGE) {
+    return "installed_external";
+  } else {
+    return "";
+  }
+}
+
 bool IsPackageInstalled(const std::string& pkg_id, RequestMode request_mode) {
   pkgmgrinfo_pkginfo_h handle;
   int ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkg_id.c_str(), getuid(),
index 21403bf..7e6b58c 100644 (file)
@@ -131,6 +131,17 @@ bool QueryPrivilegesForPkgId(const std::string& pkg_id, uid_t uid,
                              std::vector<std::string>* result);
 
 /**
+ * \brief Adapter interface for external PkgMgr module used for getting
+ *        storage for given package
+ *
+ * \param pkg_id id of package
+ * \param uid user id
+ *
+ * \return storage name of empty if not installed
+ */
+std::string QueryStorageForPkgId(const std::string& pkg_id, uid_t uid);
+
+/**
  * \brief Adapter interface for external PkgMgr module used for checking
  *        if given package is installed/registered
  *
index 35eb5c4..aafb0f0 100644 (file)
 #include "common/backup_paths.h"
 #include "common/utils/file_util.h"
 
+namespace {
+
+const char kExternalMemoryMountPoint[] = ".mmc";
+
+}
+
 namespace common_installer {
 namespace backup {
 
@@ -55,6 +61,9 @@ Step::Status StepCopyBackup::clean() {
   }
   LOG(DEBUG) << "Applications files backup directory removed";
 
+  if (context_->external_storage)
+    context_->external_storage->Commit();
+
   return Status::OK;
 }
 
@@ -64,6 +73,9 @@ Step::Status StepCopyBackup::undo() {
   LOG(DEBUG) << "Remove tmp dir: " << context_->unpacked_dir_path.get();
   bf::remove_all(context_->unpacked_dir_path.get(), error);  // ignore error
 
+  if (context_->external_storage)
+    context_->external_storage->Abort();
+
   // if backup was created then restore files
   if (bf::exists(backup_path_)) {
     if (!RollbackApplicationDirectory()) {
@@ -76,14 +88,72 @@ Step::Status StepCopyBackup::undo() {
 }
 
 bool StepCopyBackup::Backup() {
-  if (!MoveDir(context_->pkg_path.get(), backup_path_)) {
-    LOG(ERROR) << "Fail to backup package directory";
-    return false;
+  // create backup directory
+  bs::error_code error;
+  bf::create_directories(backup_path_, error);
+
+  // create copy of old package content skipping the external memory mount point
+  for (bf::directory_iterator iter(context_->pkg_path.get());
+       iter != bf::directory_iterator(); ++iter) {
+    if (iter->path().filename() == kExternalMemoryMountPoint)
+      continue;
+
+    // external storage directories are mounted
+    // therefore move only content
+    if (context_->external_storage) {
+      auto& ext_dirs = context_->external_storage->external_dirs();
+      auto found = std::find(ext_dirs.begin(), ext_dirs.end(),
+                             iter->path().filename());
+      if (found != ext_dirs.end()) {
+        bool done = MoveMountPointContent(iter->path(),
+            backup_path_ / iter->path().filename());
+        if (!done) {
+          LOG(ERROR) << "Failed to move: " << iter->path();
+          return false;
+        }
+        continue;
+      }
+    }
+
+    if (bf::is_directory(iter->path())) {
+      if (!MoveDir(iter->path(), backup_path_ / iter->path().filename())) {
+        LOG(ERROR) << "Fail to backup package directory of: " << iter->path();
+        return false;
+      }
+    } else {
+      if (!MoveFile(iter->path(), backup_path_ / iter->path().filename())) {
+        LOG(ERROR) << "Fail to backup package file of: " << iter->path();
+        return false;
+      }
+    }
   }
   LOG(INFO) << "Old package context saved to: " << backup_path_;
   return true;
 }
 
+
+bool StepCopyBackup::MoveMountPointContent(const boost::filesystem::path& from,
+                                           const boost::filesystem::path& to) {
+  bs::error_code error;
+  bf::create_directories(to, error);
+
+  for (bf::directory_iterator iter(from);
+       iter != bf::directory_iterator(); ++iter) {
+    if (bf::is_directory(iter->path())) {
+      if (!MoveDir(iter->path(), to / iter->path().filename())) {
+        LOG(ERROR) << "Fail to backup package directory of: " << iter->path();
+        return false;
+      }
+    } else {
+      if (!MoveFile(iter->path(), to / iter->path().filename())) {
+        LOG(ERROR) << "Fail to backup package file of: " << iter->path();
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 bool StepCopyBackup::NewContent() {
   bs::error_code error;
   bf::create_directories(install_path_.parent_path(), error);
@@ -91,7 +161,8 @@ bool StepCopyBackup::NewContent() {
     LOG(ERROR) << "Cannot create package directory";
     return false;
   }
-  if (!MoveDir(context_->unpacked_dir_path.get(), install_path_)) {
+  if (!MoveDir(context_->unpacked_dir_path.get(), install_path_,
+               FSFlag::FS_MERGE_DIRECTORIES)) {
     LOG(ERROR) << "Fail to copy tmp dir: " << context_->unpacked_dir_path.get()
                << " to dst dir: " << install_path_;
     return false;
index 96954b8..fea81c0 100644 (file)
@@ -57,6 +57,8 @@ class StepCopyBackup : public Step {
   bool NewContent();
   bool CleanBackupDirectory();
   bool RollbackApplicationDirectory();
+  bool MoveMountPointContent(const boost::filesystem::path& from,
+                   const boost::filesystem::path& to);
 
   boost::filesystem::path install_path_;
   boost::filesystem::path backup_path_;
index f6c44a3..a2ee835 100644 (file)
@@ -88,10 +88,16 @@ bool StepParseManifest::LocateConfigFile() {
       bf::path backup_path = common_installer::GetBackupPathForPackagePath(
           context_->pkg_path.get()) / kManifestFileName;
       bf::path in_package_path = context_->pkg_path.get() / kManifestFileName;
+      bf::path install_path =
+          bf::path(getUserManifestPath(context_->uid.get(), false))
+              / bf::path(context_->pkgid.get());
+      install_path += ".xml";
       if (bf::exists(backup_path))
         manifest = backup_path;
       else if (bf::exists(in_package_path))
         manifest = in_package_path;
+      else if (bf::exists(install_path))
+        manifest = install_path;
       break;
     }
     case ManifestLocation::INSTALLED: {
@@ -209,6 +215,17 @@ bool StepParseManifest::FillPackageInfo(manifest_x* manifest) {
     }
   }
 
+  // set installed_storage if package is installed
+  // this is internal field in package manager but after reading configuration
+  // we must know it
+  if (manifest_location_ == ManifestLocation::INSTALLED ||
+      manifest_location_ == ManifestLocation::RECOVERY) {
+    std::string storage = QueryStorageForPkgId(manifest->package,
+                                               context_->uid.get());
+    if (!storage.empty())
+      manifest->installed_storage = strdup(storage.c_str());
+  }
+
   if (ui_application_list) {
     manifest->mainapp_id =
         strdup(ui_application_list->items[0].app_info.appid().c_str());
diff --git a/src/common/step/filesystem/step_acquire_external_storage.cc b/src/common/step/filesystem/step_acquire_external_storage.cc
new file mode 100644 (file)
index 0000000..1d2e2aa
--- /dev/null
@@ -0,0 +1,76 @@
+// Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache-2.0 license that can be
+// found in the LICENSE file.
+
+#include "common/step/filesystem/step_acquire_external_storage.h"
+
+#include "common/external_storage.h"
+
+namespace {
+
+const char kInstalledExternally[] = "installed_external";
+const char kPreferExternal[] = "prefer-external";
+
+enum class Storage {
+  NONE,
+  INTERNAL,
+  EXTERNAL
+};
+
+}  // namespace
+
+namespace common_installer {
+namespace filesystem {
+
+Step::Status StepAcquireExternalStorage::precheck() {
+  if (!context_->manifest_data.get()) {
+    LOG(ERROR) << "Manifest data is empty";
+    return Status::ERROR;
+  }
+  return Status::OK;
+}
+
+Step::Status StepAcquireExternalStorage::process() {
+  manifest_x* manifest = context_->manifest_data.get();
+  manifest_x* old_manifest = context_->old_manifest_data.get();
+
+  Storage storage = Storage::NONE;
+  if (old_manifest) {
+    storage = Storage::INTERNAL;
+    if (old_manifest->installed_storage)
+      if (!strcmp(old_manifest->installed_storage, kInstalledExternally))
+        storage = Storage::EXTERNAL;
+  }
+
+  if (storage == Storage::EXTERNAL ||
+      (!strcmp(manifest->installlocation, kPreferExternal) &&
+      storage == Storage::NONE)) {
+    context_->external_storage =
+        ExternalStorage::AcquireExternalStorage(context_->request_type.get(),
+            context_->root_application_path.get(),
+            context_->pkgid.get(),
+            context_->pkg_type.get(),
+            context_->unpacked_dir_path.get(),
+            context_->uid.get());
+  }
+
+  if (storage == Storage::EXTERNAL && !context_->external_storage) {
+    LOG(ERROR) << "Cannot initialize external storage for installed package";
+    return Status::APP_DIR_ERROR;
+  }
+
+  if (context_->external_storage) {
+    // This may be allocated by step parse unfortunatelly to indicate storage
+    // in case of recovery and update.
+    free(const_cast<char*>(manifest->installed_storage));
+    manifest->installed_storage = strdup(kInstalledExternally);
+    LOG(INFO) << "Package storage: external";
+  } else {
+    LOG(INFO) << "Package storage: internal";
+  }
+  return Status::OK;
+}
+
+}  // namespace filesystem
+}  // namespace common_installer
+
diff --git a/src/common/step/filesystem/step_acquire_external_storage.h b/src/common/step/filesystem/step_acquire_external_storage.h
new file mode 100644 (file)
index 0000000..e1e1789
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache-2.0 license that can be
+// found in the LICENSE file.
+
+#ifndef COMMON_STEP_FILESYSTEM_STEP_ACQUIRE_EXTERNAL_STORAGE_H_
+#define COMMON_STEP_FILESYSTEM_STEP_ACQUIRE_EXTERNAL_STORAGE_H_
+
+#include "common/installer_context.h"
+#include "common/step/step.h"
+
+namespace common_installer {
+namespace filesystem {
+
+/**
+ * \brief Creates package storage according to:
+ *        1) package installation
+ *        2) manifest file
+ */
+class StepAcquireExternalStorage : public Step {
+ public:
+  using Step::Step;
+
+  Status process() override;
+
+  Status clean() override { return Status::OK; }
+  Status undo() override { return Status::OK; }
+  Status precheck() override;
+
+  SCOPE_LOG_TAG(AcquireExternalStorage)
+};
+
+}  // namespace filesystem
+}  // namespace common_installer
+
+#endif  // COMMON_STEP_FILESYSTEM_STEP_ACQUIRE_EXTERNAL_STORAGE_H_
index 930c8d7..89e292c 100644 (file)
@@ -78,7 +78,16 @@ Step::Status StepCopy::process() {
   return Status::OK;
 }
 
+Step::Status StepCopy::clean() {
+  if (context_->external_storage)
+    context_->external_storage->Commit();
+  return Status::OK;
+}
+
 Step::Status StepCopy::undo() {
+  if (context_->external_storage)
+    context_->external_storage->Abort();
+
   if (bf::exists(context_->pkg_path.get()))
     bf::remove_all(context_->pkg_path.get());
   return Status::OK;
index f98e6a2..598b5c9 100644 (file)
@@ -31,7 +31,7 @@ class StepCopy : public Step {
    */
   Status process() override;
 
-  Status clean() override { return Status::OK; }
+  Status clean() override;
 
   /**
    * \brief removes files from final package destination
diff --git a/src/common/step/filesystem/step_recover_external_storage.cc b/src/common/step/filesystem/step_recover_external_storage.cc
new file mode 100644 (file)
index 0000000..f330335
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache-2.0 license that can be
+// found in the LICENSE file.
+
+#include "common/step/filesystem/step_recover_external_storage.h"
+
+namespace common_installer {
+namespace filesystem {
+
+Step::Status StepRecoverExternalStorage::process() {
+  (void) StepAcquireExternalStorage::process();
+  return Status::OK;
+}
+
+}  // namespace filesystem
+}  // namespace common_installer
+
diff --git a/src/common/step/filesystem/step_recover_external_storage.h b/src/common/step/filesystem/step_recover_external_storage.h
new file mode 100644 (file)
index 0000000..bc65cec
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache-2.0 license that can be
+// found in the LICENSE file.
+
+#ifndef COMMON_STEP_FILESYSTEM_STEP_RECOVER_EXTERNAL_STORAGE_H_
+#define COMMON_STEP_FILESYSTEM_STEP_RECOVER_EXTERNAL_STORAGE_H_
+
+#include "common/installer_context.h"
+#include "common/step/filesystem/step_acquire_external_storage.h"
+
+namespace common_installer {
+namespace filesystem {
+
+/**
+ * \brief Creates external storage according to:
+ *        1) package installation
+ *        2) manifest file
+ */
+class StepRecoverExternalStorage : public StepAcquireExternalStorage {
+ public:
+  using StepAcquireExternalStorage::StepAcquireExternalStorage;
+
+  Status process() override;
+
+  SCOPE_LOG_TAG(RecoverExternalStorage)
+};
+
+}  // namespace filesystem
+}  // namespace common_installer
+
+#endif  // COMMON_STEP_FILESYSTEM_STEP_RECOVER_EXTERNAL_STORAGE_H_
index b714054..f3d2145 100644 (file)
@@ -54,6 +54,11 @@ Step::Status StepRemoveFiles::process() {
   bs::error_code error;
   bf::path pkg_path(context_->pkg_path.get());
 
+  // We need to unmount external storage before removing package directory
+  // because mount point is inside
+  if (context_->external_storage)
+    context_->external_storage->Commit();
+
   if (IsPackageInstalled(context_->pkgid.get(), GLOBAL_USER)) {
     for (bf::directory_iterator itr(pkg_path); itr != bf::directory_iterator();
         ++itr) {
index 6d5dac4..d271750 100644 (file)
@@ -284,6 +284,28 @@ int64_t GetUnpackedPackageSize(const bf::path& path) {
   return size;
 }
 
+int64_t GetDirectorySize(const boost::filesystem::path& path) {
+  int64_t block_size = GetBlockSizeForPath(path);
+
+  // if failed to stat path
+  if (block_size == -1)
+    return -1;
+
+  int64_t size = 0;
+  for (bf::recursive_directory_iterator iter(path);
+      iter != bf::recursive_directory_iterator(); ++iter) {
+      struct stat buf;
+      if (lstat(iter->path().c_str(), &buf) == -1) {
+        LOG(ERROR) << "lstat() failed for: " << iter->path();
+        return -1;
+      }
+      size += RoundUpToBlockSizeOf(buf.st_size, block_size);
+  }
+
+  // FIXME: block size for external device may differ...
+  return size;
+}
+
 boost::filesystem::path GenerateTmpDir(const bf::path &app_path) {
   boost::filesystem::path install_tmp_dir;
   boost::filesystem::path tmp_dir(app_path);
index 9eb8d98..4effe71 100644 (file)
@@ -34,6 +34,8 @@ bool SetDirPermissions(const boost::filesystem::path& path,
 
 int64_t GetUnpackedPackageSize(const boost::filesystem::path& path);
 
+int64_t GetDirectorySize(const boost::filesystem::path& path);
+
 boost::filesystem::path GenerateTmpDir(const boost::filesystem::path& app_path);
 
 boost::filesystem::path GenerateTemporaryPath(