Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b3e2571 authored by Tianjie Xu's avatar Tianjie Xu Committed by Gerrit Code Review
Browse files

Merge "Remove the old log files if cache space is insufficient for OTA"

parents ea8a6a9a d5fbcc1b
Loading
Loading
Loading
Loading
+7 −7
Original line number Diff line number Diff line
@@ -436,10 +436,10 @@ static size_t FileSink(const unsigned char* data, size_t len, int fd) {

// Return the amount of free space (in bytes) on the filesystem
// containing filename.  filename must exist.  Return -1 on error.
size_t FreeSpaceForFile(const char* filename) {
size_t FreeSpaceForFile(const std::string& filename) {
  struct statfs sf;
    if (statfs(filename, &sf) != 0) {
        printf("failed to statfs %s: %s\n", filename, strerror(errno));
  if (statfs(filename.c_str(), &sf) != 0) {
    printf("failed to statfs %s: %s\n", filename.c_str(), strerror(errno));
    return -1;
  }
  return sf.f_bsize * sf.f_bavail;
+117 −51
Original line number Diff line number Diff line
@@ -14,7 +14,10 @@
 * limitations under the License.
 */

#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <error.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
@@ -22,20 +25,22 @@
#include <sys/stat.h>
#include <sys/statfs.h>
#include <unistd.h>
#include <dirent.h>
#include <ctype.h>

#include <algorithm>
#include <limits>
#include <memory>
#include <set>
#include <string>

#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>

#include "applypatch/applypatch.h"
#include "otautil/cache_location.h"

static int EliminateOpenFiles(std::set<std::string>* files) {
static int EliminateOpenFiles(const std::string& dirname, std::set<std::string>* files) {
  std::unique_ptr<DIR, decltype(&closedir)> d(opendir("/proc"), closedir);
  if (!d) {
    printf("error opening /proc: %s\n", strerror(errno));
@@ -62,7 +67,7 @@ static int EliminateOpenFiles(std::set<std::string>* files) {
      int count = readlink(fd_path.c_str(), link, sizeof(link)-1);
      if (count >= 0) {
        link[count] = '\0';
        if (strncmp(link, "/cache/", 7) == 0) {
        if (android::base::StartsWith(link, dirname)) {
          if (files->erase(link) > 0) {
            printf("%s is open by %s\n", link, de->d_name);
          }
@@ -73,23 +78,20 @@ static int EliminateOpenFiles(std::set<std::string>* files) {
  return 0;
}

static std::set<std::string> FindExpendableFiles() {
static std::vector<std::string> FindExpendableFiles(
    const std::string& dirname, const std::function<bool(const std::string&)>& name_filter) {
  std::set<std::string> files;
  // We're allowed to delete unopened regular files in any of these
  // directories.
  const char* dirs[2] = {"/cache", "/cache/recovery/otatest"};

  for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); ++i) {
    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dirs[i]), closedir);
  std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dirname.c_str()), closedir);
  if (!d) {
      printf("error opening %s: %s\n", dirs[i], strerror(errno));
      continue;
    printf("error opening %s: %s\n", dirname.c_str(), strerror(errno));
    return {};
  }

  // Look for regular files in the directory (not in any subdirectories).
  struct dirent* de;
  while ((de = readdir(d.get())) != 0) {
      std::string path = std::string(dirs[i]) + "/" + de->d_name;
    std::string path = dirname + "/" + de->d_name;

    // We can't delete cache_temp_source; if it's there we might have restarted during
    // installation and could be depending on it to be there.
@@ -97,53 +99,117 @@ static std::set<std::string> FindExpendableFiles() {
      continue;
    }

    // Do not delete the file if it doesn't have the expected format.
    if (name_filter != nullptr && !name_filter(de->d_name)) {
      continue;
    }

    struct stat st;
    if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
      files.insert(path);
    }
  }

  printf("%zu regular files in deletable directory\n", files.size());
  if (EliminateOpenFiles(dirname, &files) < 0) {
    return {};
  }

  printf("%zu regular files in deletable directories\n", files.size());
  if (EliminateOpenFiles(&files) < 0) {
    return std::set<std::string>();
  return std::vector<std::string>(files.begin(), files.end());
}
  return files;

// Parses the index of given log file, e.g. 3 for last_log.3; returns max number if the log name
// doesn't have the expected format so that we'll delete these ones first.
static unsigned int GetLogIndex(const std::string& log_name) {
  if (log_name == "last_log" || log_name == "last_kmsg") {
    return 0;
  }

  unsigned int index;
  if (sscanf(log_name.c_str(), "last_log.%u", &index) == 1 ||
      sscanf(log_name.c_str(), "last_kmsg.%u", &index) == 1) {
    return index;
  }

  return std::numeric_limits<unsigned int>::max();
}

int MakeFreeSpaceOnCache(size_t bytes_needed) {
#ifndef __ANDROID__
  // TODO (xunchang) implement a heuristic cache size check during host simulation.
  printf("Skip making (%zu) bytes free space on cache; program is running on host\n", bytes_needed);
  printf("Skip making (%zu) bytes free space on /cache; program is running on host\n",
         bytes_needed);
  return 0;
#endif

  size_t free_now = FreeSpaceForFile("/cache");
  printf("%zu bytes free on /cache (%zu needed)\n", free_now, bytes_needed);

  if (free_now >= bytes_needed) {
  std::vector<std::string> dirs = { "/cache", CacheLocation::location().cache_log_directory() };
  for (const auto& dirname : dirs) {
    if (RemoveFilesInDirectory(bytes_needed, dirname, FreeSpaceForFile)) {
      return 0;
    }
  std::set<std::string> files = FindExpendableFiles();
  if (files.empty()) {
    // nothing we can delete to free up space!
    printf("no files can be deleted to free space on /cache\n");
  }

  return -1;
}

  // We could try to be smarter about which files to delete:  the
  // biggest ones?  the smallest ones that will free up enough space?
  // the oldest?  the newest?
  //
  // Instead, we'll be dumb.
bool RemoveFilesInDirectory(size_t bytes_needed, const std::string& dirname,
                            const std::function<size_t(const std::string&)>& space_checker) {
  struct stat st;
  if (stat(dirname.c_str(), &st) != 0) {
    error(0, errno, "Unable to free space on %s", dirname.c_str());
    return false;
  }
  if (!S_ISDIR(st.st_mode)) {
    printf("%s is not a directory\n", dirname.c_str());
    return false;
  }

  size_t free_now = space_checker(dirname);
  printf("%zu bytes free on %s (%zu needed)\n", free_now, dirname.c_str(), bytes_needed);

  if (free_now >= bytes_needed) {
    return true;
  }

  std::vector<std::string> files;
  if (dirname == CacheLocation::location().cache_log_directory()) {
    // Deletes the log files only.
    auto log_filter = [](const std::string& file_name) {
      return android::base::StartsWith(file_name, "last_log") ||
             android::base::StartsWith(file_name, "last_kmsg");
    };

    files = FindExpendableFiles(dirname, log_filter);

    // Older logs will come to the top of the queue.
    auto comparator = [](const std::string& name1, const std::string& name2) -> bool {
      unsigned int index1 = GetLogIndex(android::base::Basename(name1));
      unsigned int index2 = GetLogIndex(android::base::Basename(name2));
      if (index1 == index2) {
        return name1 < name2;
      }

      return index1 > index2;
    };

    std::sort(files.begin(), files.end(), comparator);
  } else {
    // We're allowed to delete unopened regular files in the directory.
    files = FindExpendableFiles(dirname, nullptr);
  }

  for (const auto& file : files) {
    unlink(file.c_str());
    free_now = FreeSpaceForFile("/cache");
    if (unlink(file.c_str()) == -1) {
      error(0, errno, "Failed to delete %s", file.c_str());
      continue;
    }

    free_now = space_checker(dirname);
    printf("deleted %s; now %zu bytes free\n", file.c_str(), free_now);
    if (free_now < bytes_needed) {
        break;
    if (free_now >= bytes_needed) {
      return true;
    }
  }
  return (free_now >= bytes_needed) ? 0 : -1;

  return false;
}
+5 −1
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ using SinkFn = std::function<size_t(const unsigned char*, size_t)>;
// applypatch.cpp

int ShowLicenses();
size_t FreeSpaceForFile(const char* filename);
size_t FreeSpaceForFile(const std::string& filename);
int CacheSizeCheck(size_t bytes);
int ParseSha1(const char* str, uint8_t* digest);

@@ -79,5 +79,9 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value&
// freecache.cpp

int MakeFreeSpaceOnCache(size_t bytes_needed);
// Removes the files in |dirname| until we have at least |bytes_needed| bytes of free space on
// the partition. The size of the free space is returned by calling |space_checker|.
bool RemoveFilesInDirectory(size_t bytes_needed, const std::string& dirname,
                            const std::function<size_t(const std::string&)>& space_checker);

#endif
+3 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
constexpr const char kDefaultCacheTempSource[] = "/cache/saved.file";
constexpr const char kDefaultLastCommandFile[] = "/cache/recovery/last_command";
constexpr const char kDefaultStashDirectoryBase[] = "/cache/recovery";
constexpr const char kDefaultCacheLogDirectory[] = "/cache/recovery";

CacheLocation& CacheLocation::location() {
  static CacheLocation cache_location;
@@ -28,4 +29,5 @@ CacheLocation& CacheLocation::location() {
CacheLocation::CacheLocation()
    : cache_temp_source_(kDefaultCacheTempSource),
      last_command_file_(kDefaultLastCommandFile),
      stash_directory_base_(kDefaultStashDirectoryBase) {}
      stash_directory_base_(kDefaultStashDirectoryBase),
      cache_log_directory_(kDefaultCacheLogDirectory) {}
+10 −0
Original line number Diff line number Diff line
@@ -49,6 +49,13 @@ class CacheLocation {
    stash_directory_base_ = base;
  }

  std::string cache_log_directory() const {
    return cache_log_directory_;
  }
  void set_cache_log_directory(const std::string& log_dir) {
    cache_log_directory_ = log_dir;
  }

 private:
  CacheLocation();
  DISALLOW_COPY_AND_ASSIGN(CacheLocation);
@@ -64,6 +71,9 @@ class CacheLocation {

  // The base directory to write stashes during update.
  std::string stash_directory_base_;

  // The location of last_log & last_kmsg.
  std::string cache_log_directory_;
};

#endif  // _OTAUTIL_OTAUTIL_CACHE_LOCATION_H_
Loading