Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
017797b
Add fstat to OsSysCalls
ravenblackx Dec 1, 2022
9e8d1b4
Add test comments for readability
ravenblackx Dec 1, 2022
f29cf99
Merge remote-tracking branch 'upstream/main' into fstat
ravenblackx Dec 1, 2022
ec3358f
Satisfy the spelling demon
ravenblackx Dec 1, 2022
9bd5d0a
Include fcntl.h for O_CREATE etc on windows
ravenblackx Dec 2, 2022
13d2137
Implement for windows, and test-cover pread and pwrite
ravenblackx Dec 2, 2022
660c8db
no pread and pwrite for Windows after all; make the test not use them
ravenblackx Dec 2, 2022
46afa09
Catch failure of write in the windows test path
ravenblackx Dec 2, 2022
64a6ab8
spell function names with backticks
ravenblackx Dec 5, 2022
4ece430
Explain fcntl.h, and make it only included on Win32
ravenblackx Dec 5, 2022
5458415
check_format insists on adding this newline
ravenblackx Dec 5, 2022
6dfddbb
Make Win32 OsSysCalls::close work on file descriptors, not just sockets
ravenblackx Dec 5, 2022
8a8b5c9
Backtick the spelling demon again
ravenblackx Dec 5, 2022
37d6ca0
Remove accidental ifndef
ravenblackx Dec 6, 2022
bdbf2ea
Return code no longer const
ravenblackx Dec 6, 2022
3efd132
Merge remote-tracking branch 'upstream/main' into fstat
ravenblackx Dec 6, 2022
46fc40b
Add stat for AsyncFileHandle
ravenblackx Dec 7, 2022
77700aa
Merge remote-tracking branch 'upstream/main' into async_fstat
ravenblackx Dec 12, 2022
a3ab997
Add stat by filename
ravenblackx Dec 12, 2022
4fba965
header order
ravenblackx Dec 12, 2022
6c5bc8a
Add stat to mock manager
ravenblackx Dec 12, 2022
5bfdcff
Merge remote-tracking branch 'upstream/main' into async_fstat
ravenblackx Dec 12, 2022
d3bf6eb
Mock manager stat should enqueue
ravenblackx Dec 13, 2022
19e11aa
Merge remote-tracking branch 'upstream/main' into async_fstat
ravenblackx Dec 15, 2022
b4ee407
Merge remote-tracking branch 'upstream/main' into async_fstat
ravenblackx Dec 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ template <typename T> class AsyncFileActionThreadPool : public AsyncFileActionWi
AsyncFileHandle handle_;
};

class ActionStat : public AsyncFileActionThreadPool<absl::StatusOr<struct stat>> {
public:
ActionStat(AsyncFileHandle handle, std::function<void(absl::StatusOr<struct stat>)> on_complete)
: AsyncFileActionThreadPool<absl::StatusOr<struct stat>>(handle, on_complete) {}

absl::StatusOr<struct stat> executeImpl() override {
ASSERT(fileDescriptor() != -1);
struct stat stat_result;
auto result = posix().fstat(fileDescriptor(), &stat_result);
if (result.return_value_ != 0) {
return statusAfterFileError(result);
}
return stat_result;
}
};

class ActionCreateHardLink : public AsyncFileActionThreadPool<absl::Status> {
public:
ActionCreateHardLink(AsyncFileHandle handle, absl::string_view filename,
Expand Down Expand Up @@ -171,6 +187,11 @@ class ActionDuplicateFile : public AsyncFileActionThreadPool<absl::StatusOr<Asyn

} // namespace

absl::StatusOr<CancelFunction>
AsyncFileContextThreadPool::stat(std::function<void(absl::StatusOr<struct stat>)> on_complete) {
return checkFileAndEnqueue(std::make_shared<ActionStat>(handle(), std::move(on_complete)));
}

absl::StatusOr<CancelFunction>
AsyncFileContextThreadPool::createHardLink(absl::string_view filename,
std::function<void(absl::Status)> on_complete) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class AsyncFileContextThreadPool final : public AsyncFileContextBase {
public:
explicit AsyncFileContextThreadPool(AsyncFileManager& manager, int fd);

absl::StatusOr<CancelFunction>
stat(std::function<void(absl::StatusOr<struct stat>)> on_complete) override;
absl::StatusOr<CancelFunction>
createHardLink(absl::string_view filename,
std::function<void(absl::Status)> on_complete) override;
Expand Down
4 changes: 4 additions & 0 deletions source/extensions/common/async_files/async_file_handle.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ namespace AsyncFiles {
// Instantiated from an AsyncFileManager.
class AsyncFileContext : public std::enable_shared_from_this<AsyncFileContext> {
public:
// Gets a stat struct for the file.
virtual absl::StatusOr<CancelFunction>
stat(std::function<void(absl::StatusOr<struct stat>)> on_complete) PURE;

// Action to hard link the file that is currently open. Typically for use in tandem with
// createAnonymousFile to turn that file into a named file after finishing writing its contents.
//
Expand Down
8 changes: 8 additions & 0 deletions source/extensions/common/async_files/async_file_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ class AsyncFileManager {
openExistingFile(absl::string_view filename, Mode mode,
std::function<void(absl::StatusOr<AsyncFileHandle>)> on_complete) PURE;

// Action to stat a file.
// on_complete receives a stat structure on success, or an error on failure.
//
// Returns a cancellation function, which aborts the operation
// unless the callback has already been called.
virtual CancelFunction stat(absl::string_view filename,
std::function<void(absl::StatusOr<struct stat>)> on_complete) PURE;

// Action to delete a named file.
// on_complete receives OK on success, or an error on failure.
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,26 @@ class ActionOpenExistingFile : public ActionWithFileResult {
const AsyncFileManager::Mode mode_;
};

class ActionStat : public AsyncFileActionWithResult<absl::StatusOr<struct stat>> {
public:
ActionStat(Api::OsSysCalls& posix, absl::string_view filename,
std::function<void(absl::StatusOr<struct stat>)> on_complete)
: AsyncFileActionWithResult(on_complete), posix_(posix), filename_(filename) {}

absl::StatusOr<struct stat> executeImpl() override {
struct stat ret;
Api::SysCallIntResult stat_result = posix_.stat(filename_.c_str(), &ret);
if (stat_result.return_value_ == -1) {
return statusAfterFileError(stat_result);
}
return ret;
}

private:
Api::OsSysCalls& posix_;
const std::string filename_;
};

class ActionUnlink : public AsyncFileActionWithResult<absl::Status> {
public:
ActionUnlink(Api::OsSysCalls& posix, absl::string_view filename,
Expand Down Expand Up @@ -246,6 +266,12 @@ CancelFunction AsyncFileManagerThreadPool::openExistingFile(
return enqueue(std::make_shared<ActionOpenExistingFile>(*this, filename, mode, on_complete));
}

CancelFunction
AsyncFileManagerThreadPool::stat(absl::string_view filename,
std::function<void(absl::StatusOr<struct stat>)> on_complete) {
return enqueue(std::make_shared<ActionStat>(posix(), filename, on_complete));
}

CancelFunction AsyncFileManagerThreadPool::unlink(absl::string_view filename,
std::function<void(absl::Status)> on_complete) {
return enqueue(std::make_shared<ActionUnlink>(posix(), filename, on_complete));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class AsyncFileManagerThreadPool : public AsyncFileManager,
CancelFunction
openExistingFile(absl::string_view filename, Mode mode,
std::function<void(absl::StatusOr<AsyncFileHandle>)> on_complete) override;
CancelFunction stat(absl::string_view filename,
std::function<void(absl::StatusOr<struct stat>)> on_complete) override;
CancelFunction unlink(absl::string_view filename,
std::function<void(absl::Status)> on_complete) override;
std::string describe() const override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace Common {
namespace AsyncFiles {

using StatusHelpers::IsOkAndHolds;
using StatusHelpers::StatusIs;
using ::testing::_;
using ::testing::Eq;
using ::testing::Return;
Expand Down Expand Up @@ -206,7 +207,7 @@ class TestTmpFile {
posix.close(fd_);
posix.unlink(template_);
}
std::string name() { return std::string(template_); }
std::string name() { return {template_}; }

private:
int fd_;
Expand Down Expand Up @@ -420,6 +421,36 @@ TEST_F(AsyncFileHandleWithMockPosixTest, CancellingFailedCreateHardLinkInProgres
close(handle);
}

TEST_F(AsyncFileHandleWithMockPosixTest, StatSuccessReturnsPopulatedStatStruct) {
auto handle = createAnonymousFile();
struct stat expected_stat = {};
expected_stat.st_size = 9876;
EXPECT_CALL(mock_posix_file_operations_, fstat(_, _)).WillOnce([&](int, struct stat* buffer) {
*buffer = expected_stat;
return Api::SysCallIntResult{0, 0};
});
std::promise<absl::StatusOr<struct stat>> fstat_status_promise;
EXPECT_OK(handle->stat([&](absl::StatusOr<struct stat> status) {
fstat_status_promise.set_value(std::move(status));
}));
auto fstat_status = fstat_status_promise.get_future().get();
EXPECT_THAT(fstat_status, IsOkAndHolds(testing::Field(&stat::st_size, expected_stat.st_size)));
close(handle);
}

TEST_F(AsyncFileHandleWithMockPosixTest, StatFailureReportsError) {
auto handle = createAnonymousFile();
EXPECT_CALL(mock_posix_file_operations_, fstat(_, _))
.WillOnce(Return(Api::SysCallIntResult{-1, EBADF}));
std::promise<absl::StatusOr<struct stat>> fstat_status_promise;
EXPECT_OK(handle->stat([&](absl::StatusOr<struct stat> status) {
fstat_status_promise.set_value(std::move(status));
}));
auto fstat_status = fstat_status_promise.get_future().get();
EXPECT_THAT(fstat_status, StatusIs(absl::StatusCode::kFailedPrecondition));
close(handle);
}

TEST_F(AsyncFileHandleWithMockPosixTest, CloseFailureReportsError) {
auto handle = createAnonymousFile();
EXPECT_CALL(mock_posix_file_operations_, close(1))
Expand All @@ -438,8 +469,7 @@ TEST_F(AsyncFileHandleWithMockPosixTest, DuplicateFailureReportsError) {
EXPECT_OK(handle->duplicate(
[&](absl::StatusOr<AsyncFileHandle> status) { dup_status_promise.set_value(status); }));
auto dup_status = dup_status_promise.get_future().get();
EXPECT_EQ(absl::StatusCode::kFailedPrecondition, dup_status.status().code())
<< dup_status.status();
EXPECT_THAT(dup_status, StatusIs(absl::StatusCode::kFailedPrecondition));
close(handle);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ namespace Extensions {
namespace Common {
namespace AsyncFiles {

using StatusHelpers::HasStatusCode;

enum class BlockerState {
Start,
BlockingDuringExecution,
Expand Down Expand Up @@ -268,7 +270,7 @@ TEST_F(AsyncFileManagerSingleThreadTest, CreateAnonymousFileWorks) {
EXPECT_OK(status);
}

TEST_F(AsyncFileManagerSingleThreadTest, OpenExistingFileAndUnlinkWork) {
TEST_F(AsyncFileManagerSingleThreadTest, OpenExistingFileStatAndUnlinkWork) {
char filename[1024];
snprintf(filename, sizeof(filename), "%s/async_file.XXXXXX", tmpdir_.c_str());
Api::OsSysCalls& posix = Api::OsSysCallsSingleton().get();
Expand All @@ -282,6 +284,11 @@ TEST_F(AsyncFileManagerSingleThreadTest, OpenExistingFileAndUnlinkWork) {
EXPECT_OK(handle->close(close_blocker.callback()));
absl::Status status = close_blocker.getResult();
EXPECT_OK(status);
WaitForResult<absl::StatusOr<struct stat>> stat_blocker;
manager_->stat(filename, stat_blocker.callback());
absl::StatusOr<struct stat> stat_result = stat_blocker.getResult();
EXPECT_OK(stat_result);
EXPECT_EQ(0, stat_result.value().st_size);
WaitForResult<absl::Status> unlink_blocker;
manager_->unlink(filename, unlink_blocker.callback());
status = unlink_blocker.getResult();
Expand All @@ -295,14 +302,21 @@ TEST_F(AsyncFileManagerSingleThreadTest, OpenExistingFileFailsForNonexistent) {
manager_->openExistingFile(absl::StrCat(tmpdir_, "/nonexistent_file"),
AsyncFileManager::Mode::ReadWrite, handle_blocker.callback());
absl::Status status = handle_blocker.getResult().status();
EXPECT_EQ(absl::StatusCode::kNotFound, status.code()) << status;
EXPECT_THAT(status, HasStatusCode(absl::StatusCode::kNotFound));
}

TEST_F(AsyncFileManagerSingleThreadTest, StatFailsForNonexistent) {
WaitForResult<absl::StatusOr<struct stat>> stat_blocker;
manager_->stat(absl::StrCat(tmpdir_, "/nonexistent_file"), stat_blocker.callback());
absl::StatusOr<struct stat> result = stat_blocker.getResult();
EXPECT_THAT(result, HasStatusCode(absl::StatusCode::kNotFound));
}

TEST_F(AsyncFileManagerSingleThreadTest, UnlinkFailsForNonexistent) {
WaitForResult<absl::StatusOr<AsyncFileHandle>> handle_blocker;
WaitForResult<absl::Status> handle_blocker;
manager_->unlink(absl::StrCat(tmpdir_, "/nonexistent_file"), handle_blocker.callback());
absl::Status status = handle_blocker.getResult().status();
EXPECT_EQ(absl::StatusCode::kNotFound, status.code()) << status;
absl::Status status = handle_blocker.getResult();
EXPECT_THAT(status, HasStatusCode(absl::StatusCode::kNotFound));
}

} // namespace AsyncFiles
Expand Down
11 changes: 11 additions & 0 deletions test/extensions/common/async_files/mocks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ using ::testing::_;

MockAsyncFileContext::MockAsyncFileContext(std::shared_ptr<MockAsyncFileManager> manager)
: manager_(manager) {
ON_CALL(*this, stat(_))
.WillByDefault([this](std::function<void(absl::StatusOr<struct stat>)> on_complete) {
return manager_->enqueue(
std::shared_ptr<MockAsyncFileAction>(new TypedMockAsyncFileAction(on_complete)));
});
ON_CALL(*this, createHardLink(_, _))
.WillByDefault([this](absl::string_view, std::function<void(absl::Status)> on_complete) {
return manager_->enqueue(
Expand Down Expand Up @@ -47,6 +52,12 @@ MockAsyncFileManager::MockAsyncFileManager() {
queue_.push_back(std::dynamic_pointer_cast<MockAsyncFileAction>(action));
return [this]() { mockCancel(); };
});
ON_CALL(*this, stat(_, _))
.WillByDefault(
[this](absl::string_view, std::function<void(absl::StatusOr<struct stat>)> on_complete) {
return enqueue(
std::shared_ptr<MockAsyncFileAction>(new TypedMockAsyncFileAction(on_complete)));
});
ON_CALL(*this, createAnonymousFile(_, _))
.WillByDefault([this](absl::string_view,
std::function<void(absl::StatusOr<AsyncFileHandle>)> on_complete) {
Expand Down
5 changes: 5 additions & 0 deletions test/extensions/common/async_files/mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class MockAsyncFileContext : public Extensions::Common::AsyncFiles::AsyncFileCon
// appropriate MockAsyncFileAction on the MockAsyncFileManager (if one was provided).
// These can be consumed by calling MockAsyncFileManager::nextActionCompletes
// with the desired parameter to the on_complete callback.
MOCK_METHOD(absl::StatusOr<CancelFunction>, stat,
(std::function<void(absl::StatusOr<struct stat>)> on_complete));
MOCK_METHOD(absl::StatusOr<CancelFunction>, createHardLink,
(absl::string_view filename, std::function<void(absl::Status)> on_complete));
MOCK_METHOD(absl::Status, close, (std::function<void(absl::Status)> on_complete));
Expand Down Expand Up @@ -61,6 +63,9 @@ class MockAsyncFileManager : public Extensions::Common::AsyncFiles::AsyncFileMan
MOCK_METHOD(CancelFunction, openExistingFile,
(absl::string_view filename, Mode mode,
std::function<void(absl::StatusOr<AsyncFileHandle>)> on_complete));
MOCK_METHOD(CancelFunction, stat,
(absl::string_view filename,
std::function<void(absl::StatusOr<struct stat>)> on_complete));
MOCK_METHOD(CancelFunction, unlink,
(absl::string_view filename, std::function<void(absl::Status)> on_complete));
MOCK_METHOD(std::string, describe, (), (const));
Expand Down