Commit d798079d authored by Robbie McElrath's avatar Robbie McElrath Committed by Commit Bot

Add an async flag to mojo_base.mojo.File

Currently the async flag in base::File isn't serialized when passing
Files between processes. There are assertions and checks on the value
of this flag in several places, so it needs to be preserved during
serialization/deserialization. On Windows we can't check whether an
existing file handle is async or not, so we have to explicitly pass
the flag around.

Bug: 845612
Change-Id: I8847ecd9451c13f7419e96db465caa417f4c543d
Reviewed-on: https://chromium-review.googlesource.com/1096480
Commit-Queue: Robbie McElrath <rmcelrath@chromium.org>
Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568662}
parent 8647b9a2
...@@ -36,11 +36,13 @@ File::File(const FilePath& path, uint32_t flags) ...@@ -36,11 +36,13 @@ File::File(const FilePath& path, uint32_t flags)
} }
#endif #endif
File::File(PlatformFile platform_file) File::File(PlatformFile platform_file) : File(platform_file, false) {}
File::File(PlatformFile platform_file, bool async)
: file_(platform_file), : file_(platform_file),
error_details_(FILE_OK), error_details_(FILE_OK),
created_(false), created_(false),
async_(false) { async_(async) {
#if defined(OS_POSIX) || defined(OS_FUCHSIA) #if defined(OS_POSIX) || defined(OS_FUCHSIA)
DCHECK_GE(platform_file, -1); DCHECK_GE(platform_file, -1);
#endif #endif
...@@ -64,15 +66,6 @@ File::~File() { ...@@ -64,15 +66,6 @@ File::~File() {
Close(); Close();
} }
// static
File File::CreateForAsyncHandle(PlatformFile platform_file) {
File file(platform_file);
// It would be nice if we could validate that |platform_file| was opened with
// FILE_FLAG_OVERLAPPED on Windows but this doesn't appear to be possible.
file.async_ = true;
return file;
}
File& File::operator=(File&& other) { File& File::operator=(File&& other) {
Close(); Close();
SetPlatformFile(other.TakePlatformFile()); SetPlatformFile(other.TakePlatformFile());
......
...@@ -153,9 +153,14 @@ class BASE_EXPORT File { ...@@ -153,9 +153,14 @@ class BASE_EXPORT File {
// |path| contains path traversal ('..') components. // |path| contains path traversal ('..') components.
File(const FilePath& path, uint32_t flags); File(const FilePath& path, uint32_t flags);
// Takes ownership of |platform_file|. // Takes ownership of |platform_file| and sets async to false.
explicit File(PlatformFile platform_file); explicit File(PlatformFile platform_file);
// Takes ownership of |platform_file| and sets async to the given value.
// This constructor exists because on Windows you can't check if platform_file
// is async or not.
File(PlatformFile platform_file, bool async);
// Creates an object with a specific error_details code. // Creates an object with a specific error_details code.
explicit File(Error error_details); explicit File(Error error_details);
...@@ -163,9 +168,6 @@ class BASE_EXPORT File { ...@@ -163,9 +168,6 @@ class BASE_EXPORT File {
~File(); ~File();
// Takes ownership of |platform_file|.
static File CreateForAsyncHandle(PlatformFile platform_file);
File& operator=(File&& other); File& operator=(File&& other);
// Creates or opens the given file. // Creates or opens the given file.
......
...@@ -395,10 +395,7 @@ File File::Duplicate() const { ...@@ -395,10 +395,7 @@ File File::Duplicate() const {
if (other_fd == -1) if (other_fd == -1)
return File(File::GetLastFileError()); return File(File::GetLastFileError());
File other(other_fd); return File(other_fd, async());
if (async())
other.async_ = true;
return other;
} }
// Static. // Static.
......
...@@ -745,4 +745,18 @@ TEST(FileTest, NoDeleteOnCloseWithMappedFile) { ...@@ -745,4 +745,18 @@ TEST(FileTest, NoDeleteOnCloseWithMappedFile) {
file.Close(); file.Close();
ASSERT_TRUE(base::PathExists(file_path)); ASSERT_TRUE(base::PathExists(file_path));
} }
// Check that we handle the async bit being set incorrectly in a sane way.
TEST(FileTest, UseSyncApiWithAsyncFile) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath file_path = temp_dir.GetPath().AppendASCII("file");
File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE |
base::File::FLAG_ASYNC);
File lying_file(file.TakePlatformFile(), false /* async */);
ASSERT_TRUE(lying_file.IsValid());
ASSERT_EQ(lying_file.WriteAtCurrentPos("12345", 5), -1);
}
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
...@@ -269,10 +269,7 @@ File File::Duplicate() const { ...@@ -269,10 +269,7 @@ File File::Duplicate() const {
return File(GetLastFileError()); return File(GetLastFileError());
} }
File other(other_handle); return File(other_handle, async());
if (async())
other.async_ = true;
return other;
} }
bool File::DeleteOnClose(bool delete_on_close) { bool File::DeleteOnClose(bool delete_on_close) {
......
...@@ -215,15 +215,17 @@ TEST_F(NativeMessagingTest, SingleSendMessageWrite) { ...@@ -215,15 +215,17 @@ TEST_F(NativeMessagingTest, SingleSendMessageWrite) {
#if defined(OS_WIN) #if defined(OS_WIN)
base::string16 pipe_name = base::StringPrintf( base::string16 pipe_name = base::StringPrintf(
L"\\\\.\\pipe\\chrome.nativeMessaging.out.%llx", base::RandUint64()); L"\\\\.\\pipe\\chrome.nativeMessaging.out.%llx", base::RandUint64());
base::File write_handle = base::File::CreateForAsyncHandle( base::File write_handle =
CreateNamedPipeW(pipe_name.c_str(), base::File(CreateNamedPipeW(pipe_name.c_str(),
PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED | PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED |
FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_FIRST_PIPE_INSTANCE,
PIPE_TYPE_BYTE, 1, 0, 0, 5000, NULL)); PIPE_TYPE_BYTE, 1, 0, 0, 5000, NULL),
true /* async */);
ASSERT_TRUE(write_handle.IsValid()); ASSERT_TRUE(write_handle.IsValid());
base::File read_handle = base::File::CreateForAsyncHandle( base::File read_handle = base::File(
CreateFileW(pipe_name.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, CreateFileW(pipe_name.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL)); FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL),
true /* async */);
ASSERT_TRUE(read_handle.IsValid()); ASSERT_TRUE(read_handle.IsValid());
read_file = std::move(read_handle); read_file = std::move(read_handle);
......
...@@ -171,8 +171,8 @@ bool NativeProcessLauncher::LaunchNativeProcess( ...@@ -171,8 +171,8 @@ bool NativeProcessLauncher::LaunchNativeProcess(
} }
*process = std::move(cmd_process); *process = std::move(cmd_process);
*read_file = base::File::CreateForAsyncHandle(stdout_pipe.Take()); *read_file = base::File(stdout_pipe.Take(), true /* async */);
*write_file = base::File::CreateForAsyncHandle(stdin_pipe.Take()); *write_file = base::File(stdin_pipe.Take(), true /* async */);
return true; return true;
} }
......
...@@ -23,7 +23,7 @@ bool StructTraits<mojo_base::mojom::FileDataView, base::File>::Read( ...@@ -23,7 +23,7 @@ bool StructTraits<mojo_base::mojom::FileDataView, base::File>::Read(
MOJO_RESULT_OK) { MOJO_RESULT_OK) {
return false; return false;
} }
*file = base::File(platform_handle); *file = base::File(platform_handle, data.async());
return true; return true;
} }
......
...@@ -19,6 +19,7 @@ struct COMPONENT_EXPORT(MOJO_BASE_MOJOM) ...@@ -19,6 +19,7 @@ struct COMPONENT_EXPORT(MOJO_BASE_MOJOM)
static void SetToNull(base::File* file) { *file = base::File(); } static void SetToNull(base::File* file) { *file = base::File(); }
static mojo::ScopedHandle fd(base::File& file); static mojo::ScopedHandle fd(base::File& file);
static bool async(base::File& file) { return file.async(); }
static bool Read(mojo_base::mojom::FileDataView data, base::File* file); static bool Read(mojo_base::mojom::FileDataView data, base::File* file);
}; };
......
...@@ -28,6 +28,7 @@ TEST(FileTest, File) { ...@@ -28,6 +28,7 @@ TEST(FileTest, File) {
mojo::test::SerializeAndDeserialize<mojom::File>(&file, &file_out)); mojo::test::SerializeAndDeserialize<mojom::File>(&file, &file_out));
std::vector<char> content(test_content.size()); std::vector<char> content(test_content.size());
ASSERT_TRUE(file_out.IsValid()); ASSERT_TRUE(file_out.IsValid());
ASSERT_FALSE(file_out.async());
ASSERT_EQ(static_cast<int>(test_content.size()), ASSERT_EQ(static_cast<int>(test_content.size()),
file_out.Read(0, content.data(), file_out.Read(0, content.data(),
base::checked_cast<int>(test_content.size()))); base::checked_cast<int>(test_content.size())));
...@@ -35,6 +36,25 @@ TEST(FileTest, File) { ...@@ -35,6 +36,25 @@ TEST(FileTest, File) {
base::StringPiece(content.data(), test_content.size())); base::StringPiece(content.data(), test_content.size()));
} }
TEST(FileTest, AsyncFile) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath path = temp_dir.GetPath().AppendASCII("async_test_file.txt");
base::File write_file(path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
const base::StringPiece test_content = "test string";
write_file.WriteAtCurrentPos(test_content.data(),
base::checked_cast<int>(test_content.size()));
write_file.Close();
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_ASYNC);
base::File file_out;
ASSERT_TRUE(
mojo::test::SerializeAndDeserialize<mojom::File>(&file, &file_out));
ASSERT_TRUE(file_out.async());
}
TEST(FileTest, InvalidFile) { TEST(FileTest, InvalidFile) {
base::ScopedTempDir temp_dir; base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
......
...@@ -7,4 +7,5 @@ module mojo_base.mojom; ...@@ -7,4 +7,5 @@ module mojo_base.mojom;
// Corresponds to |base::File| in base/files/file.h // Corresponds to |base::File| in base/files/file.h
struct File { struct File {
handle fd; handle fd;
bool async;
}; };
...@@ -814,8 +814,7 @@ TEST_F(FileStreamTest, AsyncFlagMismatch) { ...@@ -814,8 +814,7 @@ TEST_F(FileStreamTest, AsyncFlagMismatch) {
// handle but with the async flag set to true. // handle but with the async flag set to true.
uint32_t flags = base::File::FLAG_OPEN | base::File::FLAG_READ; uint32_t flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
base::File file(temp_file_path(), flags); base::File file(temp_file_path(), flags);
base::File lying_file = base::File lying_file(file.TakePlatformFile(), true);
base::File::CreateForAsyncHandle(file.TakePlatformFile());
ASSERT_TRUE(lying_file.IsValid()); ASSERT_TRUE(lying_file.IsValid());
FileStream stream(std::move(lying_file), base::ThreadTaskRunnerHandle::Get()); FileStream stream(std::move(lying_file), base::ThreadTaskRunnerHandle::Get());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment