summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Buynytskyy <alexbuy@google.com>2021-08-30 14:02:13 -0700
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-09-20 22:33:04 +0000
commit777df0107f858402794214dd6a7dec4dd36334c9 (patch)
tree7664caf564aa324a52b94d5de25335f279188b1e
parent012f638b12fede33417e7ae4c99059958fbb41c7 (diff)
downloadincremental_delivery-777df0107f858402794214dd6a7dec4dd36334c9.tar.gz
Fix for loading progress for mapped files.
- fix the error code clash inside isFullyLoaded, - work around buggy INCFS_IOC_GET_BLOCK_COUNT ioctl, - additional feature flag. Bug: 194143197 Test: atest IncFsTest Change-Id: I1e2617842854db6335599b19b9b297ec9f677cf1 (cherry picked from commit 6405bab34a375002a569379068ff6102a6ff1a02)
-rw-r--r--incfs/incfs.cpp20
-rw-r--r--incfs/include/incfs.h1
-rw-r--r--incfs/include/incfs_ndk.h6
-rw-r--r--incfs/tests/incfs_test.cpp127
-rw-r--r--incfs/tests/include/IncFsTestBase.h1
5 files changed, 142 insertions, 13 deletions
diff --git a/incfs/incfs.cpp b/incfs/incfs.cpp
index 490b907..842e381 100644
--- a/incfs/incfs.cpp
+++ b/incfs/incfs.cpp
@@ -252,14 +252,15 @@ bool IncFs_IsEnabled() {
static Features readIncFsFeatures() {
init().enabledAndReady();
+ int res = Features::none | Features::mappingFilesProgressFixed;
+
static const char kSysfsFeaturesDir[] = "/sys/fs/" INCFS_NAME "/features";
const auto dir = path::openDir(kSysfsFeaturesDir);
if (!dir) {
PLOG(ERROR) << "IncFs_Features: failed to open features dir, assuming v1/none.";
- return Features::none;
+ return Features(res);
}
- int res = Features::none;
while (auto entry = ::readdir(dir.get())) {
if (entry->d_type != DT_REG) {
continue;
@@ -1555,7 +1556,7 @@ IncFsErrorCode IncFs_IsFullyLoadedByPath(const IncFsControl* control, const char
if (features() & Features::v2) {
const auto id = getId(::getxattr, path);
if (id == kIncFsInvalidFileId) {
- return -errno;
+ return -ENOTSUP;
}
return isFullyLoadedV2(root, id);
}
@@ -1692,7 +1693,20 @@ IncFsErrorCode IncFs_GetUidReadTimeouts(const IncFsControl* control,
return 0;
}
+// Trying to detect if this is a mapped file.
+// Not the best way as it might return true for other system files.
+// TODO: remove after IncFS returns ENOTSUP for such files.
+static bool isMapped(int fd) {
+ char buffer[kIncFsFileIdStringLength];
+ const auto res = ::fgetxattr(fd, kIdAttrName, buffer, sizeof(buffer));
+ return res != sizeof(buffer);
+}
+
static IncFsErrorCode getFileBlockCount(int fd, IncFsBlockCounts* blockCount) {
+ if (isMapped(fd)) {
+ return -ENOTSUP;
+ }
+
incfs_get_block_count_args args = {};
auto res = ::ioctl(fd, INCFS_IOC_GET_BLOCK_COUNT, &args);
if (res < 0) {
diff --git a/incfs/include/incfs.h b/incfs/include/incfs.h
index 76619c2..5dfe060 100644
--- a/incfs/include/incfs.h
+++ b/incfs/include/incfs.h
@@ -41,6 +41,7 @@ enum Features {
none = INCFS_FEATURE_NONE,
core = INCFS_FEATURE_CORE,
v2 = INCFS_FEATURE_V2,
+ mappingFilesProgressFixed = INCFS_FEATURE_MAPPING_FILES_PROGRESS_FIXED,
};
enum class HashAlgorithm {
diff --git a/incfs/include/incfs_ndk.h b/incfs/include/incfs_ndk.h
index 2a538bc..4862f39 100644
--- a/incfs/include/incfs_ndk.h
+++ b/incfs/include/incfs_ndk.h
@@ -43,8 +43,10 @@ static const int kIncFsFileIdStringLength = sizeof(IncFsFileId) * 2;
typedef enum {
INCFS_FEATURE_NONE = 0,
- INCFS_FEATURE_CORE = 1,
- INCFS_FEATURE_V2 = 2,
+ INCFS_FEATURE_CORE = 1 << 0,
+ INCFS_FEATURE_V2 = 1 << 1,
+ INCFS_FEATURE_MAPPING_FILES_PROGRESS_FIXED = 1 << 2,
+
} IncFsFeatures;
typedef int IncFsErrorCode;
diff --git a/incfs/tests/incfs_test.cpp b/incfs/tests/incfs_test.cpp
index e651f2e..94bbdcd 100644
--- a/incfs/tests/incfs_test.cpp
+++ b/incfs/tests/incfs_test.cpp
@@ -105,25 +105,29 @@ protected:
ASSERT_EQ((int)std::size(blocks), writeBlocks({blocks, std::size(blocks)}));
}
- template <class ReadStruct>
- void testWriteBlockAndPageRead() {
- const auto id = fileId(1);
- ASSERT_TRUE(control_.logs() >= 0);
- ASSERT_EQ(0,
- makeFile(control_, mountPath(test_file_name_), 0555, id,
- {.size = test_file_size_}));
+ void writeBlock(int pageIndex) {
auto fd = openForSpecialOps(control_, fileId(1));
ASSERT_GE(fd.get(), 0);
std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
auto block = DataBlock{
.fileFd = fd.get(),
- .pageIndex = 0,
+ .pageIndex = pageIndex,
.compression = INCFS_COMPRESSION_KIND_NONE,
.dataSize = (uint32_t)data.size(),
.data = data.data(),
};
ASSERT_EQ(1, writeBlocks({&block, 1}));
+ }
+
+ template <class ReadStruct>
+ void testWriteBlockAndPageRead() {
+ const auto id = fileId(1);
+ ASSERT_TRUE(control_.logs() >= 0);
+ ASSERT_EQ(0,
+ makeFile(control_, mountPath(test_file_name_), 0555, id,
+ {.size = test_file_size_}));
+ writeBlock(/*pageIndex=*/0);
std::thread wait_page_read_thread([&]() {
std::vector<ReadStruct> reads;
@@ -149,6 +153,7 @@ protected:
ASSERT_TRUE(android::base::ReadFully(readFd, buf, sizeof(buf)));
wait_page_read_thread.join();
}
+
template <class PendingRead>
void testWaitForPendingReads() {
const auto id = fileId(1);
@@ -366,6 +371,39 @@ TEST_F(IncFsTest, MakeFile0) {
ASSERT_EQ(0, (int)s.st_size);
}
+TEST_F(IncFsTest, MakeMappedFile) {
+ ASSERT_EQ(0, makeDir(control_, mountPath(test_dir_name_)));
+
+ constexpr auto file_size = INCFS_DATA_FILE_BLOCK_SIZE * 2;
+ constexpr auto mapped_file_offset = file_size / 2;
+ constexpr auto mapped_file_size = file_size / 3;
+
+ const auto file_path = mountPath(test_dir_name_, test_file_name_);
+ ASSERT_FALSE(exists(file_path));
+ ASSERT_EQ(0,
+ makeFile(control_, file_path, 0111, fileId(1),
+ {.size = file_size, .metadata = metadata("md")}));
+ struct stat s = {};
+ ASSERT_EQ(0, stat(file_path.c_str(), &s));
+ ASSERT_EQ(file_size, (int)s.st_size);
+
+ const auto mapped_file_path = mountPath(test_dir_name_, test_mapped_file_name_);
+ ASSERT_FALSE(exists(mapped_file_path));
+ ASSERT_EQ(0,
+ makeMappedFile(control_, mapped_file_path, 0111,
+ {.sourceId = fileId(1),
+ .sourceOffset = mapped_file_offset,
+ .size = mapped_file_size}));
+ s = {};
+ ASSERT_EQ(0, stat(mapped_file_path.c_str(), &s));
+ ASSERT_EQ(mapped_file_size, (int)s.st_size);
+
+ // Check fileId for the source file.
+ ASSERT_EQ(fileId(1), getFileId(control_, file_path));
+ // Check that there is no fileId for the mapped file.
+ ASSERT_EQ(kIncFsInvalidFileId, getFileId(control_, mapped_file_path));
+}
+
TEST_F(IncFsTest, GetFileId) {
auto id = fileId(1);
ASSERT_EQ(0,
@@ -1384,3 +1422,76 @@ TEST_F(IncFsGetMetricsTest, MetricsWithReadsDelayedPerUidTimeout) {
EXPECT_EQ(0, (int)incfsMetrics.readsFailedOther);
EXPECT_EQ(0, (int)incfsMetrics.readsFailedTimedOut);
}
+
+inline bool operator==(const BlockCounts& lhs, const BlockCounts& rhs) {
+ return lhs.totalDataBlocks == rhs.totalDataBlocks &&
+ lhs.filledDataBlocks == rhs.filledDataBlocks &&
+ lhs.totalHashBlocks == rhs.totalHashBlocks &&
+ lhs.filledHashBlocks == rhs.filledHashBlocks;
+}
+
+TEST_F(IncFsTest, LoadingProgress) {
+ ASSERT_EQ(0, makeDir(control_, mountPath(test_dir_name_)));
+
+ constexpr auto file_size = INCFS_DATA_FILE_BLOCK_SIZE * 2;
+ constexpr auto mapped_file_offset = file_size / 2;
+ constexpr auto mapped_file_size = file_size / 3;
+
+ const auto file_id = fileId(1);
+
+ const auto file_path = mountPath(test_dir_name_, test_file_name_);
+ ASSERT_FALSE(exists(file_path));
+ ASSERT_EQ(0,
+ makeFile(control_, file_path, 0111, file_id,
+ {.size = file_size, .metadata = metadata("md")}));
+ struct stat s = {};
+ ASSERT_EQ(0, stat(file_path.c_str(), &s));
+ ASSERT_EQ(file_size, (int)s.st_size);
+
+ const auto mapped_file_path = mountPath(test_dir_name_, test_mapped_file_name_);
+ ASSERT_FALSE(exists(mapped_file_path));
+ ASSERT_EQ(0,
+ makeMappedFile(control_, mapped_file_path, 0111,
+ {.sourceId = file_id,
+ .sourceOffset = mapped_file_offset,
+ .size = mapped_file_size}));
+ s = {};
+ ASSERT_EQ(0, stat(mapped_file_path.c_str(), &s));
+ ASSERT_EQ(mapped_file_size, (int)s.st_size);
+
+ // Check fully loaded first.
+ ASSERT_EQ(LoadingState::MissingBlocks, isFullyLoaded(control_, file_path));
+ ASSERT_EQ(LoadingState::MissingBlocks, isFullyLoaded(control_, file_id));
+ ASSERT_EQ((LoadingState)-ENOTSUP, isFullyLoaded(control_, mapped_file_path));
+
+ // Next is loading progress.
+ ASSERT_EQ(BlockCounts{.totalDataBlocks = 2}, *getBlockCount(control_, file_path));
+ ASSERT_EQ(BlockCounts{.totalDataBlocks = 2}, *getBlockCount(control_, file_id));
+ ASSERT_FALSE(getBlockCount(control_, mapped_file_path));
+
+ // Now write a page #0.
+ ASSERT_NO_FATAL_FAILURE(writeBlock(0));
+
+ // Recheck everything.
+ ASSERT_EQ(LoadingState::MissingBlocks, isFullyLoaded(control_, file_path));
+ ASSERT_EQ(LoadingState::MissingBlocks, isFullyLoaded(control_, file_id));
+ ASSERT_EQ((LoadingState)-ENOTSUP, isFullyLoaded(control_, mapped_file_path));
+
+ BlockCounts onePage{.totalDataBlocks = 2, .filledDataBlocks = 1};
+ ASSERT_EQ(onePage, *getBlockCount(control_, file_path));
+ ASSERT_EQ(onePage, *getBlockCount(control_, file_id));
+ ASSERT_FALSE(getBlockCount(control_, mapped_file_path));
+
+ // Now write a page #1.
+ ASSERT_NO_FATAL_FAILURE(writeBlock(1));
+
+ // Check for fully loaded.
+ ASSERT_EQ(LoadingState::Full, isFullyLoaded(control_, file_path));
+ ASSERT_EQ(LoadingState::Full, isFullyLoaded(control_, file_id));
+ ASSERT_EQ((LoadingState)-ENOTSUP, isFullyLoaded(control_, mapped_file_path));
+
+ BlockCounts twoPages{.totalDataBlocks = 2, .filledDataBlocks = 2};
+ ASSERT_EQ(twoPages, *getBlockCount(control_, file_path));
+ ASSERT_EQ(twoPages, *getBlockCount(control_, file_id));
+ ASSERT_FALSE(getBlockCount(control_, mapped_file_path));
+}
diff --git a/incfs/tests/include/IncFsTestBase.h b/incfs/tests/include/IncFsTestBase.h
index 5f8c311..8c61d64 100644
--- a/incfs/tests/include/IncFsTestBase.h
+++ b/incfs/tests/include/IncFsTestBase.h
@@ -117,6 +117,7 @@ protected:
std::string image_dir_path_;
std::optional<TemporaryDir> tmp_dir_for_image_;
inline static const std::string_view test_file_name_ = "test.txt";
+ inline static const std::string_view test_mapped_file_name_ = "mapped.txt";
inline static const std::string_view test_dir_name_ = "test_dir";
std::string metrics_key_;
Control control_;