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

Commit 2ab4447a authored by Yurii Zubrytskyi's avatar Yurii Zubrytskyi
Browse files

[res] Don't stat asset providers on RO filesystems

IsUpToDate() is one of the most often called functions, let's
make sure it only performs a syscall if it makes any sense and
the underlying file can really change.

Bug: 237583012
Test: build + boot + UTs
Change-Id: Ie5999ddadf10b56f35354d00ad3402b229ffa2c3
parent 9d22537e
Loading
Loading
Loading
Loading
+30 −17
Original line number Diff line number Diff line
@@ -104,6 +104,8 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path,
  }

  struct stat sb{.st_mtime = -1};
  // Skip all up-to-date checks if the file won't ever change.
  if (!isReadonlyFilesystem(path.c_str())) {
    if ((released_fd < 0 ? stat(path.c_str(), &sb) : fstat(released_fd, &sb)) < 0) {
      // Stat requires execute permissions on all directories path to the file. If the process does
      // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
@@ -111,6 +113,7 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path,
      LOG(WARNING) << "Failed to stat file '" << path << "': "
                   << base::SystemErrorCodeToString(errno);
    }
  }

  return std::unique_ptr<ZipAssetsProvider>(
      new ZipAssetsProvider(handle, PathOrDebugName{std::move(path),
@@ -136,12 +139,15 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
  }

  struct stat sb{.st_mtime = -1};
  // Skip all up-to-date checks if the file won't ever change.
  if (!isReadonlyFilesystem(released_fd)) {
    if (fstat(released_fd, &sb) < 0) {
      // Stat requires execute permissions on all directories path to the file. If the process does
      // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
      // always have to return true.
    LOG(WARNING) << "Failed to fstat file '" << friendly_name << "': "
                 << base::SystemErrorCodeToString(errno);
      LOG(WARNING) << "Failed to fstat file '" << friendly_name
                   << "': " << base::SystemErrorCodeToString(errno);
    }
  }

  return std::unique_ptr<ZipAssetsProvider>(
@@ -278,6 +284,9 @@ const std::string& ZipAssetsProvider::GetDebugName() const {
}

bool ZipAssetsProvider::IsUpToDate() const {
  if (last_mod_time_ == -1) {
    return true;
  }
  struct stat sb{};
  if (fstat(GetFileDescriptor(zip_handle_.get()), &sb) < 0) {
    // If fstat fails on the zip archive, return true so the zip archive the resource system does
@@ -291,7 +300,7 @@ DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last
    : dir_(std::forward<std::string>(path)), last_mod_time_(last_mod_time) {}

std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::string path) {
  struct stat sb{};
  struct stat sb;
  const int result = stat(path.c_str(), &sb);
  if (result == -1) {
    LOG(ERROR) << "Failed to find directory '" << path << "'.";
@@ -307,8 +316,9 @@ std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::st
    path += OS_PATH_SEPARATOR;
  }

  return std::unique_ptr<DirectoryAssetsProvider>(new DirectoryAssetsProvider(std::move(path),
                                                                              sb.st_mtime));
  const bool isReadonly = isReadonlyFilesystem(path.c_str());
  return std::unique_ptr<DirectoryAssetsProvider>(
      new DirectoryAssetsProvider(std::move(path), isReadonly ? -1 : sb.st_mtime));
}

std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string& path,
@@ -338,7 +348,10 @@ const std::string& DirectoryAssetsProvider::GetDebugName() const {
}

bool DirectoryAssetsProvider::IsUpToDate() const {
  struct stat sb{};
  if (last_mod_time_ == -1) {
    return true;
  }
  struct stat sb;
  if (stat(dir_.c_str(), &sb) < 0) {
    // If stat fails on the zip archive, return true so the zip archive the resource system does
    // attempt to refresh the ApkAsset.
+4 −0
Original line number Diff line number Diff line
@@ -44,6 +44,10 @@ FileType getFileType(const char* fileName);
/* get the file's modification date; returns -1 w/errno set on failure */
time_t getFileModDate(const char* fileName);

// Check if |path| or |fd| resides on a readonly filesystem.
bool isReadonlyFilesystem(const char* path);
bool isReadonlyFilesystem(int fd);

}; // namespace android

#endif // _LIBS_ANDROID_FW_MISC_H
+38 −6
Original line number Diff line number Diff line
@@ -21,12 +21,17 @@
//
#include <androidfw/misc.h>

#include <sys/stat.h>
#include "android-base/logging.h"

#ifndef _WIN32
#include <sys/statvfs.h>
#include <sys/vfs.h>
#endif  // _WIN32

#include <cstring>
#include <errno.h>
#include <cstdio>

using namespace android;
#include <errno.h>
#include <sys/stat.h>

namespace android {

@@ -41,8 +46,7 @@ FileType getFileType(const char* fileName)
        if (errno == ENOENT || errno == ENOTDIR)
            return kFileTypeNonexistent;
        else {
            fprintf(stderr, "getFileType got errno=%d on '%s'\n",
                errno, fileName);
            PLOG(ERROR) << "getFileType(): stat(" << fileName << ") failed";
            return kFileTypeUnknown;
        }
    } else {
@@ -82,4 +86,32 @@ time_t getFileModDate(const char* fileName)
    return sb.st_mtime;
}

#ifdef _WIN32
// No need to implement these for Windows, the functions only matter on a device.
bool isReadonlyFilesystem(const char*) {
    return false;
}
bool isReadonlyFilesystem(int) {
    return false;
}
#else   // _WIN32
bool isReadonlyFilesystem(const char* path) {
    struct statfs sfs;
    if (::statfs(path, &sfs)) {
        PLOG(ERROR) << "isReadonlyFilesystem(): statfs(" << path << ") failed";
        return false;
    }
    return (sfs.f_flags & ST_RDONLY) != 0;
}

bool isReadonlyFilesystem(int fd) {
    struct statfs sfs;
    if (::fstatfs(fd, &sfs)) {
        PLOG(ERROR) << "isReadonlyFilesystem(): fstatfs(" << fd << ") failed";
        return false;
    }
    return (sfs.f_flags & ST_RDONLY) != 0;
}
#endif  // _WIN32

}; // namespace android