Merge cherrypicks of [15831545, 15831526, 15831634, 15831684, 15831293, 15831546, 15831847, 15831808, 15831638, 15831639, 15831558, 15831793, 15831794, 15831795, 15831796, 15831797, 15831798, 15831799, 15831800, 15831801, 15831802, 15831803, 15831804, 15831640, 15831849, 15831810, 15831811, 15831850, 15831641, 15831805, 15831812, 15831642, 15831813, 15831851, 15831852, 15831887, 15831888, 15831559, 15831814, 15831815, 15831853, 15831854, 15831855, 15831315] into sc-release

Change-Id: I46390af748803f430d26d626ae9d368291bcd7b1
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 @@
 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 @@
     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 @@
     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 @@
     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 @@
 
 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,6 +105,21 @@
         ASSERT_EQ((int)std::size(blocks), writeBlocks({blocks, std::size(blocks)}));
     }
 
+    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 = 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);
@@ -112,18 +127,7 @@
         ASSERT_EQ(0,
                   makeFile(control_, mountPath(test_file_name_), 0555, id,
                            {.size = test_file_size_}));
-        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,
-                .compression = INCFS_COMPRESSION_KIND_NONE,
-                .dataSize = (uint32_t)data.size(),
-                .data = data.data(),
-        };
-        ASSERT_EQ(1, writeBlocks({&block, 1}));
+        writeBlock(/*pageIndex=*/0);
 
         std::thread wait_page_read_thread([&]() {
             std::vector<ReadStruct> reads;
@@ -149,6 +153,7 @@
         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 @@
     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 @@
     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 @@
     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_;