Available Storage Check 56/37256/3
authorTomasz Iwanek <t.iwanek@samsung.com>
Mon, 16 Mar 2015 13:38:57 +0000 (14:38 +0100)
committerTomasz Iwanek <t.iwanek@samsung.com>
Thu, 26 Mar 2015 14:32:24 +0000 (15:32 +0100)
Solution calculates space needed for files.
This will ignore space for directories/icons/manifests but
in overall will prevent unneccesary unzipping the archive
in most cases.

Change-Id: Ib8e81c4cb56e771347a128021ece53e4cf2c9fce

src/common/step/step.h
src/common/step/step_unzip.cc

index 5173792..73abb31 100644 (file)
@@ -27,6 +27,7 @@ namespace common_installer {
 class Step {
  public:
   enum class Status {
+    OUT_OF_SPACE = -3,      /**< Out of disc space */
     INVALID_VALUE = -2,     /**< Invalid argument */
     ERROR = -1,             /**< General error */
     OK = 0                  /**< General success */
index e775b51..b34e132 100644 (file)
@@ -13,6 +13,7 @@
 #include <cassert>
 #include <cstdio>
 #include <cstdlib>
+#include <cstdint>
 #include <climits>
 #include <cstring>
 #include <string>
 #define ZIPBUFSIZE 8192
 #define ZIPMAXPATH 256
 
+namespace bf = boost::filesystem;
+namespace bs = boost::system;
+
+namespace {
+
+int64_t GetBlockSizeForPath(const bf::path& path_in_partition) {
+  struct stat stats;
+  if (stat(path_in_partition.string().c_str(), &stats)) {
+    LOG(ERROR) << "stat(" << path_in_partition.string()
+               << ") failed - error code: " << errno;
+    return -1;
+  }
+  return stats.st_blksize;
+}
+
+int64_t RoundUpToBlockSizeOf(int64_t size, int64_t block_size) {
+  return ((size + block_size - 1) / block_size) * block_size;
+}
+
+int64_t GetUnpackedPackageSize(const bf::path& path) {
+  int64_t size = 0;
+  int64_t block_size = GetBlockSizeForPath(path);
+
+  // if failed to stat path
+  if (block_size == -1)
+    return -1;
+
+  unz_global_info info;
+  unz_file_info64 raw_file_info;
+  char raw_file_name_in_zip[ZIPMAXPATH];
+
+  unzFile* zip_file = static_cast<unzFile*>(unzOpen(path.string().c_str()));
+  if (zip_file == nullptr) {
+    LOG(ERROR) << "Failed to open the source dir: " << path.string();
+    return -1;
+  }
+
+  if (unzGetGlobalInfo(zip_file, &info) != UNZ_OK) {
+    LOG(ERROR) << "Failed to read global info";
+    unzClose(zip_file);
+    return -1;
+  }
+
+  for (uLong i = 0; i < info.number_entry; i++) {
+    if (unzGetCurrentFileInfo64(zip_file, &raw_file_info, raw_file_name_in_zip,
+        sizeof(raw_file_name_in_zip), NULL, 0, NULL, 0) != UNZ_OK) {
+      LOG(ERROR) << "Failed to read file info";
+      return -1;
+    }
+    size += RoundUpToBlockSizeOf(raw_file_info.uncompressed_size, block_size);
+  }
+
+  // FIXME: calculate space needed for directories
+  unzClose(zip_file);
+  return size;
+}
+
+bool CheckFreeSpaceAtPath(int64_t required_size,
+    const boost::filesystem::path& target_location) {
+  bs::error_code error;
+  boost::filesystem::path root = target_location;
+  while (!bf::exists(root) && root != root.root_path()) {
+    root = root.parent_path();
+  }
+  if (!bf::exists(root)) {
+    LOG(ERROR) << "No mount point for path: " << target_location;
+    return false;
+  }
+  bf::space_info space_info = bf::space(root, error);
+  if (error) {
+    LOG(ERROR) << "Failed to get space_info: " << error.message();
+    return false;
+  }
+
+  return (space_info.free >= required_size);
+}
+
+}  // namespace
+
 namespace common_installer {
 namespace unzip {
 
@@ -46,7 +126,7 @@ boost::filesystem::path StepUnzip::GenerateTmpDir(const char* app_path) {
 }
 
 Step::Status StepUnzip::ExtractToTmpDir(const char* src,
-    const boost::filesystem::path& tmp_dir) {
+    const bf::path& tmp_dir) {
   if (is_extracted_) {
     LOG(ERROR) << src << " is already extracted";
     return Status::OK;
@@ -82,7 +162,7 @@ Step::Status StepUnzip::ExtractToTmpDir(const char* src,
     if (raw_file_name_in_zip[0] == '\0')
       return Step::Status::ERROR;
 
-    boost::filesystem::path filename_in_zip_path(raw_file_name_in_zip);
+    bf::path filename_in_zip_path(raw_file_name_in_zip);
     if (!filename_in_zip_path.parent_path().empty()) {
       if (!utils::CreateDir(filename_in_zip_path.parent_path())) {
         LOG(ERROR) << "Failed to create directory: "
@@ -138,7 +218,7 @@ Step::Status StepUnzip::process() {
   assert(!context_->file_path().empty());
   assert(!access(context_->file_path().c_str(), F_OK));
 
-  boost::filesystem::path tmp_dir =
+  bf::path tmp_dir =
       GenerateTmpDir(context_->GetRootApplicationPath());
 
   if (!utils::CreateDir(tmp_dir)) {
@@ -146,6 +226,28 @@ Step::Status StepUnzip::process() {
     return Step::Status::ERROR;
   }
 
+  int64_t required_size =
+      GetUnpackedPackageSize(bf::path(context_->file_path()));
+
+  if (required_size == -1) {
+    LOG(ERROR) << "Couldn't get uncompressed size for package: "
+               << context_->file_path();
+    return Step::Status::ERROR;
+  }
+
+  LOG(DEBUG) << "Required size for application: " << required_size << "B";
+
+  if (!CheckFreeSpaceAtPath(required_size, tmp_dir)) {
+    LOG(ERROR) << "There is not enough space to unpack application files";
+    return Step::Status::OUT_OF_SPACE;
+  }
+
+  if (!CheckFreeSpaceAtPath(required_size,
+      bf::path(context_->GetRootApplicationPath()))) {
+    LOG(ERROR) << "There is not enough space to install application files";
+    return Step::Status::OUT_OF_SPACE;
+  }
+
   if (ExtractToTmpDir(context_->file_path().c_str(), tmp_dir)
       != Step::Status::OK) {
     LOG(ERROR) << "Failed to process unpack step";
@@ -160,7 +262,7 @@ Step::Status StepUnzip::process() {
 
 Step::Status StepUnzip::undo() {
   if (access(context_->unpacked_dir_path().c_str(), F_OK) == 0) {
-    boost::filesystem::remove_all(context_->unpacked_dir_path());
+    bf::remove_all(context_->unpacked_dir_path());
     LOG(DEBUG) << "remove temp dir: " << context_->unpacked_dir_path();
   }
   return Status::OK;