diff options
author | Yurii Zubrytskyi <zyy@google.com> | 2021-03-23 00:16:11 -0700 |
---|---|---|
committer | Yurii Zubrytskyi <zyy@google.com> | 2021-03-23 00:17:45 -0700 |
commit | e5b624a7cf9fb4433112b04cb78d3751e2f7058c (patch) | |
tree | 3b1d428c0a401bcccb269d1b4b3f4884d7f96782 | |
parent | 3723fd96ad71257683fdbcb2d1062b5e3d530ba5 (diff) | |
download | incremental_delivery-e5b624a7cf9fb4433112b04cb78d3751e2f7058c.tar.gz |
[incfs] New API to iterate overall files
v2 also supports iterating over incomplete files on the mount
Bug: 183435580
Test: atest libincfs-test
Change-Id: I12c34a7c1b2894622dfdabcd2c376133f19b35ac
-rw-r--r-- | incfs/incfs.cpp | 96 | ||||
-rw-r--r-- | incfs/include/incfs.h | 6 | ||||
-rw-r--r-- | incfs/include/incfs_inline.h | 24 | ||||
-rw-r--r-- | incfs/include/incfs_ndk.h | 11 | ||||
-rw-r--r-- | incfs/tests/incfs_test.cpp | 49 |
5 files changed, 154 insertions, 32 deletions
diff --git a/incfs/incfs.cpp b/incfs/incfs.cpp index 46a206d..ea90dd9 100644 --- a/incfs/incfs.cpp +++ b/incfs/incfs.cpp @@ -152,6 +152,29 @@ static std::pair<bool, std::string_view> parseProperty(std::string_view property return {false, {}}; } +template <class Callback> +static IncFsErrorCode forEachFileIn(std::string_view dirPath, Callback cb) { + auto dir = path::openDir(details::c_str(dirPath)); + if (!dir) { + return -EINVAL; + } + + int res = 0; + while (auto entry = (errno = 0, ::readdir(dir.get()))) { + if (entry->d_type != DT_REG) { + continue; + } + ++res; + if (!cb(entry->d_name)) { + break; + } + } + if (errno) { + return -errno; + } + return res; +} + namespace { class IncFsInit { @@ -245,7 +268,7 @@ static Features readIncFsFeatures() { } } - PLOG(INFO) << "IncFs_Features: " << ((res & Features::v2) ? "v2" : "v1"); + LOG(INFO) << "IncFs_Features: " << ((res & Features::v2) ? "v2" : "v1"); return Features(res); } @@ -1550,19 +1573,8 @@ static IncFsErrorCode isEverythingLoadedV2(const IncFsControl* control) { if (root.empty()) { return -EINVAL; } - const auto dirPath = path::join(root, INCFS_INCOMPLETE_NAME); - const auto dir = path::openDir(dirPath.c_str()); - if (!dir) { - return -EINVAL; - } - while (const auto entry = ::readdir(dir.get())) { - if (entry->d_type != DT_REG) { - continue; - } - // any file in this directory has to be incomplete - return -ENODATA; - } - return 0; + auto res = forEachFileIn(path::join(root, INCFS_INCOMPLETE_NAME), [](auto) { return false; }); + return res < 0 ? res : res > 0 ? -ENODATA : 0; } static IncFsErrorCode isEverythingLoadedSlow(const IncFsControl* control) { @@ -1738,28 +1750,52 @@ IncFsErrorCode IncFs_ListIncompleteFiles(const IncFsControl* control, IncFsFileI if (root.empty()) { return -EINVAL; } - auto dirPath = path::join(root, INCFS_INCOMPLETE_NAME); - auto dir = path::openDir(dirPath.c_str()); - if (!dir) { - return -EINVAL; - } - - int res = 0; size_t index = 0; - while (auto entry = ::readdir(dir.get())) { - if (entry->d_type != DT_REG) { - continue; - } + int error = 0; + const auto res = forEachFileIn(path::join(root, INCFS_INCOMPLETE_NAME), [&](const char* name) { if (index >= *bufferSize) { - res = -E2BIG; + error = -E2BIG; } else { - ids[index] = IncFs_FileIdFromString(entry->d_name); + ids[index] = IncFs_FileIdFromString(name); } ++index; + return true; + }); + if (res < 0) { + return res; } - *bufferSize = index; - return res; + return error ? error : 0; +} + +IncFsErrorCode IncFs_ForEachFile(const IncFsControl* control, void* context, FileCallback cb) { + if (!control || !cb) { + return -EINVAL; + } + const auto root = rootForCmd(control->cmd); + if (root.empty()) { + return -EINVAL; + } + return forEachFileIn(path::join(root, INCFS_INDEX_NAME), [&](const char* name) { + return cb(context, control, IncFs_FileIdFromString(name)); + }); +} + +IncFsErrorCode IncFs_ForEachIncompleteFile(const IncFsControl* control, void* context, + FileCallback cb) { + if (!control || !cb) { + return -EINVAL; + } + if (!(features() & Features::v2)) { + return -ENOTSUP; + } + const auto root = rootForCmd(control->cmd); + if (root.empty()) { + return -EINVAL; + } + return forEachFileIn(path::join(root, INCFS_INCOMPLETE_NAME), [&](const char* name) { + return cb(context, control, IncFs_FileIdFromString(name)); + }); } IncFsErrorCode IncFs_WaitForLoadingComplete(const IncFsControl* control, int32_t timeoutMs) { @@ -1915,7 +1951,7 @@ IncFsErrorCode IncFs_ReserveSpace(const IncFsControl* control, const char* path, // for fixed overhead. // hash tree is ~33 bytes / page, and blockmap is 10 bytes / page // no need to round to a page size as filesystems already do that. - const auto backingSize = IncFsSize(size * 1.015) + INCFS_DATA_FILE_BLOCK_SIZE * 8; + const auto backingSize = IncFsSize(size * 1.015) + INCFS_DATA_FILE_BLOCK_SIZE * 4; if (backingSize < st.st_size) { return -EPERM; } diff --git a/incfs/include/incfs.h b/incfs/include/incfs.h index 817613e..8a08c11 100644 --- a/incfs/include/incfs.h +++ b/incfs/include/incfs.h @@ -259,6 +259,12 @@ std::optional<BlockCounts> getBlockCount(const Control& control, FileId fileId); std::optional<BlockCounts> getBlockCount(const Control& control, std::string_view path); std::optional<std::vector<FileId>> listIncompleteFiles(const Control& control); + +template <class Callback> +ErrorCode forEachFile(const Control& control, Callback&& cb); +template <class Callback> +ErrorCode forEachIncompleteFile(const Control& control, Callback&& cb); + WaitResult waitForLoadingComplete(const Control& control, std::chrono::milliseconds timeout); enum class LoadingState { Full, MissingBlocks }; diff --git a/incfs/include/incfs_inline.h b/incfs/include/incfs_inline.h index 56cea91..0d21677 100644 --- a/incfs/include/incfs_inline.h +++ b/incfs/include/incfs_inline.h @@ -376,6 +376,30 @@ inline std::optional<std::vector<FileId>> listIncompleteFiles(const Control& con return std::move(ids); } +template <class Callback> +inline ErrorCode forEachFile(const Control& control, const Callback& cb) { + struct Context { + const Control& c; + const Callback& cb; + } context = {control, cb}; + return IncFs_ForEachFile(control, &context, [](void* pcontext, const IncFsControl*, FileId id) { + const auto context = (Context*)pcontext; + return context->cb(context->control, id); + }); +} +template <class Callback> +inline ErrorCode forEachIncompleteFile(const Control& control, const Callback& cb) { + struct Context { + const Control& c; + const Callback& cb; + } context = {control, cb}; + return IncFs_ForEachIncompleteFile(control, &context, + [](void* pcontext, const IncFsControl*, FileId id) { + const auto context = (Context*)pcontext; + return context->cb(context->control, id); + }); +} + inline WaitResult waitForLoadingComplete(const Control& control, std::chrono::milliseconds timeout) { const auto res = IncFs_WaitForLoadingComplete(control, timeout.count()); diff --git a/incfs/include/incfs_ndk.h b/incfs/include/incfs_ndk.h index ea2974f..6c00cc6 100644 --- a/incfs/include/incfs_ndk.h +++ b/incfs/include/incfs_ndk.h @@ -258,6 +258,17 @@ IncFsErrorCode IncFs_GetFileBlockCountById(const IncFsControl* control, IncFsFil IncFsErrorCode IncFs_ListIncompleteFiles(const IncFsControl* control, IncFsFileId ids[], size_t* bufferSize); +// Calls a passed callback for each file on the mounted filesystem, or, in the second case, +// for each incomplete file (only for v2 IncFS). +// Callback can stop the iteration early by returning |false|. +// Return codes: +// >=0 - number of files iterated, +// <0 - -errno +typedef bool (*FileCallback)(void* context, const IncFsControl* control, IncFsFileId fileId); +IncFsErrorCode IncFs_ForEachFile(const IncFsControl* control, void* context, FileCallback cb); +IncFsErrorCode IncFs_ForEachIncompleteFile(const IncFsControl* control, void* context, + FileCallback cb); + IncFsErrorCode IncFs_WaitForLoadingComplete(const IncFsControl* control, int32_t timeoutMs); // Gets a collection of filled ranges in the file from IncFS. Uses the |outBuffer| memory, it has diff --git a/incfs/tests/incfs_test.cpp b/incfs/tests/incfs_test.cpp index 7b197ff..6b866d0 100644 --- a/incfs/tests/incfs_test.cpp +++ b/incfs/tests/incfs_test.cpp @@ -939,8 +939,6 @@ TEST_F(IncFsTest, CompletionWait) { return; } - GTEST_SKIP() << "broken: b/175323815"; - ASSERT_EQ(0, makeFile(control_, mountPath("test1"), 0555, fileId(1), {.size = INCFS_DATA_FILE_BLOCK_SIZE})); @@ -1103,4 +1101,51 @@ TEST_F(IncFsTest, ReserveSpace) { IncFs_ReserveSpace(control_, mountPath(test_file_name_).c_str(), kTrimReservedSpace)); EXPECT_EQ(0, IncFs_ReserveSpace(control_, mountPath(test_file_name_).c_str(), kTrimReservedSpace)); +} + +TEST_F(IncFsTest, ForEachFile) { + const auto incompleteSupported = (features() & Features::v2) != 0; + EXPECT_EQ(-EINVAL, IncFs_ForEachFile(nullptr, nullptr, nullptr)); + EXPECT_EQ(-EINVAL, IncFs_ForEachIncompleteFile(nullptr, nullptr, nullptr)); + EXPECT_EQ(-EINVAL, IncFs_ForEachFile(control_, nullptr, nullptr)); + EXPECT_EQ(-EINVAL, IncFs_ForEachIncompleteFile(control_, nullptr, nullptr)); + EXPECT_EQ(0, IncFs_ForEachFile(control_, nullptr, [](auto, auto, auto) { return true; })); + EXPECT_EQ(incompleteSupported ? 0 : -ENOTSUP, + IncFs_ForEachIncompleteFile(control_, nullptr, + [](auto, auto, auto) { return true; })); + EXPECT_EQ(0, IncFs_ForEachFile(control_, this, [](auto, auto, auto) { return true; })); + EXPECT_EQ(incompleteSupported ? 0 : -ENOTSUP, + IncFs_ForEachIncompleteFile(control_, this, [](auto, auto, auto) { return true; })); + + int res = makeFile(control_, mountPath("incomplete.txt"), 0555, fileId(1), + {.metadata = metadata("md")}); + ASSERT_EQ(res, 0); + + EXPECT_EQ(1, IncFs_ForEachFile(control_, this, [](auto, auto context, auto id) { + auto self = (IncFsTest*)context; + EXPECT_EQ(self->fileId(1), id); + return true; + })); + EXPECT_EQ(incompleteSupported ? 0 : -ENOTSUP, + IncFs_ForEachIncompleteFile(control_, this, [](auto, auto, auto) { return true; })); + + auto size = makeFileWithHash(2); + ASSERT_GT(size, 0); + + EXPECT_EQ(1, IncFs_ForEachFile(control_, this, [](auto, auto context, auto id) { + auto self = (IncFsTest*)context; + EXPECT_TRUE(id == self->fileId(1) || id == self->fileId(2)); + return false; + })); + EXPECT_EQ(2, IncFs_ForEachFile(control_, this, [](auto, auto context, auto id) { + auto self = (IncFsTest*)context; + EXPECT_TRUE(id == self->fileId(1) || id == self->fileId(2)); + return true; + })); + EXPECT_EQ(incompleteSupported ? 1 : -ENOTSUP, + IncFs_ForEachIncompleteFile(control_, this, [](auto, auto context, auto id) { + auto self = (IncFsTest*)context; + EXPECT_EQ(self->fileId(2), id); + return true; + })); }
\ No newline at end of file |