summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYurii Zubrytskyi <zyy@google.com>2021-03-23 00:16:11 -0700
committerYurii Zubrytskyi <zyy@google.com>2021-03-23 00:17:45 -0700
commite5b624a7cf9fb4433112b04cb78d3751e2f7058c (patch)
tree3b1d428c0a401bcccb269d1b4b3f4884d7f96782
parent3723fd96ad71257683fdbcb2d1062b5e3d530ba5 (diff)
downloadincremental_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.cpp96
-rw-r--r--incfs/include/incfs.h6
-rw-r--r--incfs/include/incfs_inline.h24
-rw-r--r--incfs/include/incfs_ndk.h11
-rw-r--r--incfs/tests/incfs_test.cpp49
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