[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
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index 24460a4..b9264c5 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -104,12 +104,15 @@
}
struct stat sb{.st_mtime = -1};
- 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
- // always have to return true.
- LOG(WARNING) << "Failed to stat file '" << path << "': "
- << base::SystemErrorCodeToString(errno);
+ // 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
+ // always have to return true.
+ LOG(WARNING) << "Failed to stat file '" << path << "': "
+ << base::SystemErrorCodeToString(errno);
+ }
}
return std::unique_ptr<ZipAssetsProvider>(
@@ -136,12 +139,15 @@
}
struct stat sb{.st_mtime = -1};
- 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);
+ // 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);
+ }
}
return std::unique_ptr<ZipAssetsProvider>(
@@ -278,6 +284,9 @@
}
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 @@
: 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 @@
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 @@
}
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.
@@ -434,4 +447,4 @@
return true;
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/androidfw/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h
index 5a5a0e2..d40d24e 100644
--- a/libs/androidfw/include/androidfw/misc.h
+++ b/libs/androidfw/include/androidfw/misc.h
@@ -44,6 +44,10 @@
/* 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
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index 5285420..7af5066 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -21,12 +21,17 @@
//
#include <androidfw/misc.h>
-#include <sys/stat.h>
-#include <cstring>
-#include <errno.h>
-#include <cstdio>
+#include "android-base/logging.h"
-using namespace android;
+#ifndef _WIN32
+#include <sys/statvfs.h>
+#include <sys/vfs.h>
+#endif // _WIN32
+
+#include <cstring>
+#include <cstdio>
+#include <errno.h>
+#include <sys/stat.h>
namespace android {
@@ -41,8 +46,7 @@
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 @@
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