Cross platform low level file IO wrappers

Rename fd_io to file_io, and ReadFD to ReadFile, etc.

file_io.cc is the higher level versions that call the basic ReadFile/WriteFile
and then file_io_posix.cc and file_io_win.cc are the implementations of
those functions.

The Windows path is as yet untested, lacking the ability to link the test binary.

R=cpu@chromium.org, mark@chromium.org
BUG=crashpad:1

Review URL: https://codereview.chromium.org/811823003
This commit is contained in:
Scott Graham 2014-12-17 14:35:18 -08:00
parent 3d84a738d0
commit 10165ce449
18 changed files with 398 additions and 278 deletions

View File

@ -30,7 +30,7 @@
#include "client/simple_string_dictionary.h" #include "client/simple_string_dictionary.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "snapshot/mac/process_reader.h" #include "snapshot/mac/process_reader.h"
#include "util/file/fd_io.h" #include "util/file/file_io.h"
#include "util/mac/mac_util.h" #include "util/mac/mac_util.h"
#include "util/mach/exc_server_variants.h" #include "util/mach/exc_server_variants.h"
#include "util/mach/exception_ports.h" #include "util/mach/exception_ports.h"
@ -193,7 +193,7 @@ class TestMachOImageAnnotationsReader final
// Wait for the child process to indicate that its done setting up its // Wait for the child process to indicate that its done setting up its
// annotations via the CrashpadInfo interface. // annotations via the CrashpadInfo interface.
char c; char c;
CheckedReadFD(ReadPipeFD(), &c, sizeof(c)); CheckedReadFile(ReadPipeFD(), &c, sizeof(c));
// Verify the “simple map” annotations set via the CrashpadInfo interface. // Verify the “simple map” annotations set via the CrashpadInfo interface.
const std::vector<ProcessReader::Module>& modules = const std::vector<ProcessReader::Module>& modules =
@ -216,7 +216,7 @@ class TestMachOImageAnnotationsReader final
EXPECT_EQ("", all_annotations_simple_map["#TEST# empty_value"]); EXPECT_EQ("", all_annotations_simple_map["#TEST# empty_value"]);
// Tell the child process that its permitted to crash. // Tell the child process that its permitted to crash.
CheckedWriteFD(WritePipeFD(), &c, sizeof(c)); CheckedWriteFile(WritePipeFD(), &c, sizeof(c));
if (test_type_ != kDontCrash) { if (test_type_ != kDontCrash) {
// Handle the childs crash. Further validation will be done in // Handle the childs crash. Further validation will be done in
@ -268,10 +268,10 @@ class TestMachOImageAnnotationsReader final
// Tell the parent that the environment has been set up. // Tell the parent that the environment has been set up.
char c = '\0'; char c = '\0';
CheckedWriteFD(WritePipeFD(), &c, sizeof(c)); CheckedWriteFile(WritePipeFD(), &c, sizeof(c));
// Wait for the parent to indicate that its safe to crash. // Wait for the parent to indicate that its safe to crash.
CheckedReadFD(ReadPipeFD(), &c, sizeof(c)); CheckedReadFile(ReadPipeFD(), &c, sizeof(c));
// Direct an exception message to the exception server running in the // Direct an exception message to the exception server running in the
// parent. // parent.

View File

@ -31,7 +31,7 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "snapshot/mac/mach_o_image_reader.h" #include "snapshot/mac/mach_o_image_reader.h"
#include "util/file/fd_io.h" #include "util/file/file_io.h"
#include "util/mach/mach_extensions.h" #include "util/mach/mach_extensions.h"
#include "util/stdlib/pointer_container.h" #include "util/stdlib/pointer_container.h"
#include "util/synchronization/semaphore.h" #include "util/synchronization/semaphore.h"
@ -91,7 +91,7 @@ class ProcessReaderChild final : public MachMultiprocess {
int read_fd = ReadPipeFD(); int read_fd = ReadPipeFD();
mach_vm_address_t address; mach_vm_address_t address;
CheckedReadFD(read_fd, &address, sizeof(address)); CheckedReadFile(read_fd, &address, sizeof(address));
std::string read_string; std::string read_string;
ASSERT_TRUE(process_reader.Memory()->ReadCString(address, &read_string)); ASSERT_TRUE(process_reader.Memory()->ReadCString(address, &read_string));
@ -103,11 +103,11 @@ class ProcessReaderChild final : public MachMultiprocess {
mach_vm_address_t address = mach_vm_address_t address =
reinterpret_cast<mach_vm_address_t>(kTestMemory); reinterpret_cast<mach_vm_address_t>(kTestMemory);
CheckedWriteFD(write_fd, &address, sizeof(address)); CheckedWriteFile(write_fd, &address, sizeof(address));
// Wait for the parent to signal that its OK to exit by closing its end of // Wait for the parent to signal that its OK to exit by closing its end of
// the pipe. // the pipe.
CheckedReadFDAtEOF(ReadPipeFD()); CheckedReadFileAtEOF(ReadPipeFD());
} }
DISALLOW_COPY_AND_ASSIGN(ProcessReaderChild); DISALLOW_COPY_AND_ASSIGN(ProcessReaderChild);
@ -434,13 +434,13 @@ class ProcessReaderThreadedChild final : public MachMultiprocess {
thread_index < thread_count_ + 1; thread_index < thread_count_ + 1;
++thread_index) { ++thread_index) {
uint64_t thread_id; uint64_t thread_id;
CheckedReadFD(read_fd, &thread_id, sizeof(thread_id)); CheckedReadFile(read_fd, &thread_id, sizeof(thread_id));
TestThreadPool::ThreadExpectation expectation; TestThreadPool::ThreadExpectation expectation;
CheckedReadFD(read_fd, CheckedReadFile(read_fd,
&expectation.stack_address, &expectation.stack_address,
sizeof(expectation.stack_address)); sizeof(expectation.stack_address));
CheckedReadFD(read_fd, CheckedReadFile(read_fd,
&expectation.suspend_count, &expectation.suspend_count,
sizeof(expectation.suspend_count)); sizeof(expectation.suspend_count));
@ -467,16 +467,16 @@ class ProcessReaderThreadedChild final : public MachMultiprocess {
// to inspect it. Write an entry for it. // to inspect it. Write an entry for it.
uint64_t thread_id = PthreadToThreadID(pthread_self()); uint64_t thread_id = PthreadToThreadID(pthread_self());
CheckedWriteFD(write_fd, &thread_id, sizeof(thread_id)); CheckedWriteFile(write_fd, &thread_id, sizeof(thread_id));
TestThreadPool::ThreadExpectation expectation; TestThreadPool::ThreadExpectation expectation;
expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_id); expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_id);
expectation.suspend_count = 0; expectation.suspend_count = 0;
CheckedWriteFD(write_fd, CheckedWriteFile(write_fd,
&expectation.stack_address, &expectation.stack_address,
sizeof(expectation.stack_address)); sizeof(expectation.stack_address));
CheckedWriteFD(write_fd, CheckedWriteFile(write_fd,
&expectation.suspend_count, &expectation.suspend_count,
sizeof(expectation.suspend_count)); sizeof(expectation.suspend_count));
@ -487,18 +487,18 @@ class ProcessReaderThreadedChild final : public MachMultiprocess {
uint64_t thread_id = uint64_t thread_id =
thread_pool.GetThreadInfo(thread_index, &expectation); thread_pool.GetThreadInfo(thread_index, &expectation);
CheckedWriteFD(write_fd, &thread_id, sizeof(thread_id)); CheckedWriteFile(write_fd, &thread_id, sizeof(thread_id));
CheckedWriteFD(write_fd, CheckedWriteFile(write_fd,
&expectation.stack_address, &expectation.stack_address,
sizeof(expectation.stack_address)); sizeof(expectation.stack_address));
CheckedWriteFD(write_fd, CheckedWriteFile(write_fd,
&expectation.suspend_count, &expectation.suspend_count,
sizeof(expectation.suspend_count)); sizeof(expectation.suspend_count));
} }
// Wait for the parent to signal that its OK to exit by closing its end of // Wait for the parent to signal that its OK to exit by closing its end of
// the pipe. // the pipe.
CheckedReadFDAtEOF(ReadPipeFD()); CheckedReadFileAtEOF(ReadPipeFD());
} }
size_t thread_count_; size_t thread_count_;
@ -597,7 +597,7 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
int read_fd = ReadPipeFD(); int read_fd = ReadPipeFD();
uint32_t expect_modules; uint32_t expect_modules;
CheckedReadFD(read_fd, &expect_modules, sizeof(expect_modules)); CheckedReadFile(read_fd, &expect_modules, sizeof(expect_modules));
ASSERT_EQ(expect_modules, modules.size()); ASSERT_EQ(expect_modules, modules.size());
@ -606,16 +606,16 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
"index %zu, name %s", index, modules[index].name.c_str())); "index %zu, name %s", index, modules[index].name.c_str()));
uint32_t expect_name_length; uint32_t expect_name_length;
CheckedReadFD( CheckedReadFile(
read_fd, &expect_name_length, sizeof(expect_name_length)); read_fd, &expect_name_length, sizeof(expect_name_length));
// The NUL terminator is not read. // The NUL terminator is not read.
std::string expect_name(expect_name_length, '\0'); std::string expect_name(expect_name_length, '\0');
CheckedReadFD(read_fd, &expect_name[0], expect_name_length); CheckedReadFile(read_fd, &expect_name[0], expect_name_length);
EXPECT_EQ(expect_name, modules[index].name); EXPECT_EQ(expect_name, modules[index].name);
mach_vm_address_t expect_address; mach_vm_address_t expect_address;
CheckedReadFD(read_fd, &expect_address, sizeof(expect_address)); CheckedReadFile(read_fd, &expect_address, sizeof(expect_address));
EXPECT_EQ(expect_address, modules[index].reader->Address()); EXPECT_EQ(expect_address, modules[index].reader->Address());
if (index == 0 || index == modules.size() - 1) { if (index == 0 || index == modules.size() - 1) {
@ -648,7 +648,7 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
++write_image_count; ++write_image_count;
} }
CheckedWriteFD(write_fd, &write_image_count, sizeof(write_image_count)); CheckedWriteFile(write_fd, &write_image_count, sizeof(write_image_count));
for (size_t index = 0; index < write_image_count; ++index) { for (size_t index = 0; index < write_image_count; ++index) {
const char* dyld_image_name; const char* dyld_image_name;
@ -665,18 +665,19 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
} }
uint32_t dyld_image_name_length = strlen(dyld_image_name); uint32_t dyld_image_name_length = strlen(dyld_image_name);
CheckedWriteFD( CheckedWriteFile(
write_fd, &dyld_image_name_length, sizeof(dyld_image_name_length)); write_fd, &dyld_image_name_length, sizeof(dyld_image_name_length));
// The NUL terminator is not written. // The NUL terminator is not written.
CheckedWriteFD(write_fd, dyld_image_name, dyld_image_name_length); CheckedWriteFile(write_fd, dyld_image_name, dyld_image_name_length);
CheckedWriteFD(write_fd, &dyld_image_address, sizeof(dyld_image_address)); CheckedWriteFile(
write_fd, &dyld_image_address, sizeof(dyld_image_address));
} }
// Wait for the parent to signal that its OK to exit by closing its end of // Wait for the parent to signal that its OK to exit by closing its end of
// the pipe. // the pipe.
CheckedReadFDAtEOF(ReadPipeFD()); CheckedReadFileAtEOF(ReadPipeFD());
} }
DISALLOW_COPY_AND_ASSIGN(ProcessReaderModulesChild); DISALLOW_COPY_AND_ASSIGN(ProcessReaderModulesChild);

View File

@ -1,125 +0,0 @@
// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_UTIL_FILE_FD_IO_H_
#define CRASHPAD_UTIL_FILE_FD_IO_H_
#include <sys/types.h>
namespace crashpad {
//! \brief Wraps `read()`, retrying when interrupted or following a short read.
//!
//! This function reads into \a buffer, stopping only when \a size bytes have
//! been read or when `read()` returns 0, indicating that end-of-file has been
//! reached.
//!
//! \return The number of bytes read and placed into \a buffer, or `-1` on
//! error, with `errno` set appropriately. On error, a portion of \a fd may
//! have been read into \a buffer.
//!
//! \sa WriteFD
//! \sa LoggingReadFD
//! \sa CheckedReadFD
//! \sa CheckedReadFDAtEOF
ssize_t ReadFD(int fd, void* buffer, size_t size);
//! \brief Wraps `write()`, retrying when interrupted or following a short
//! write.
//!
//! This function writes to \a fd, stopping only when \a size bytes have been
//! written.
//!
//! \return The number of bytes written from \a buffer, or `-1` on error, with
//! `errno` set appropriately. On error, a portion of \a buffer may have
//! been written to \a fd.
//!
//! \sa ReadFD
//! \sa LoggingWriteFD
//! \sa CheckedWriteFD
ssize_t WriteFD(int fd, const void* buffer, size_t size);
//! \brief Wraps ReadFD(), ensuring that exactly \a size bytes are read.
//!
//! \return `true` on success. If \a size is out of the range of possible
//! `read()` return values, if the underlying ReadFD() fails, or if other
//! than \a size bytes were read, this function logs a message and returns
//! `false`.
//!
//! \sa LoggingWriteFD
//! \sa ReadFD
//! \sa CheckedReadFD
//! \sa CheckedReadFDAtEOF
bool LoggingReadFD(int fd, void* buffer, size_t size);
//! \brief Wraps WriteFD(), ensuring that exactly \a size bytes are written.
//!
//! \return `true` on success. If \a size is out of the range of possible
//! `write()` return values, if the underlying WriteFD() fails, or if other
//! than \a size bytes were written, this function logs a message and
//! returns `false`.
//!
//! \sa LoggingReadFD
//! \sa WriteFD
//! \sa CheckedWriteFD
bool LoggingWriteFD(int fd, const void* buffer, size_t size);
//! \brief Wraps ReadFD(), ensuring that exactly \a size bytes are read.
//!
//! If \a size is out of the range of possible `read()` return values, if the
//! underlying ReadFD() fails, or if other than \a size bytes were read, this
//! function causes execution to terminate without returning.
//!
//! \sa CheckedWriteFD
//! \sa ReadFD
//! \sa LoggingReadFD
//! \sa CheckedReadFDAtEOF
void CheckedReadFD(int fd, void* buffer, size_t size);
//! \brief Wraps WriteFD(), ensuring that exactly \a size bytes are written.
//!
//! If \a size is out of the range of possible `write()` return values, if the
//! underlying WriteFD() fails, or if other than \a size bytes were written,
//! this function causes execution to terminate without returning.
//!
//! \sa CheckedReadFD
//! \sa WriteFD
//! \sa LoggingWriteFD
void CheckedWriteFD(int fd, const void* buffer, size_t size);
//! \brief Wraps ReadFD(), ensuring that it indicates end-of-file.
//!
//! Attempts to read a single byte from \a fd, expecting no data to be read. If
//! the underlying ReadFD() fails, or if a byte actually is read, this function
//! causes execution to terminate without returning.
//!
//! \sa CheckedReadFD
//! \sa ReadFD
void CheckedReadFDAtEOF(int fd);
//! \brief Wraps `close()`, logging an error if the operation fails.
//!
//! \return On success, `true` is returned. On failure, an error is logged and
//! `false` is returned.
bool LoggingCloseFD(int fd);
//! \brief Wraps `close()`, ensuring that it succeeds.
//!
//! If `close()` fails, this function causes execution to terminate without
//! returning.
void CheckedCloseFD(int fd);
} // namespace crashpad
#endif // CRASHPAD_UTIL_FILE_FD_IO_H_

74
util/file/file_io.cc Normal file
View File

@ -0,0 +1,74 @@
// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/file/file_io.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
namespace crashpad {
bool LoggingReadFile(FileHandle file, void* buffer, size_t size) {
ssize_t expect = base::checked_cast<ssize_t>(size);
ssize_t rv = ReadFile(file, buffer, size);
if (rv < 0) {
PLOG(ERROR) << "read";
return false;
}
if (rv != expect) {
LOG(ERROR) << "read: expected " << expect << ", observed " << rv;
return false;
}
return true;
}
bool LoggingWriteFile(FileHandle file, const void* buffer, size_t size) {
ssize_t expect = base::checked_cast<ssize_t>(size);
ssize_t rv = WriteFile(file, buffer, size);
if (rv < 0) {
PLOG(ERROR) << "write";
return false;
}
if (rv != expect) {
LOG(ERROR) << "write: expected " << expect << ", observed " << rv;
return false;
}
return true;
}
void CheckedReadFile(FileHandle file, void* buffer, size_t size) {
CHECK(LoggingReadFile(file, buffer, size));
}
void CheckedWriteFile(FileHandle file, const void* buffer, size_t size) {
CHECK(LoggingWriteFile(file, buffer, size));
}
void CheckedReadFileAtEOF(FileHandle file) {
char c;
ssize_t rv = ReadFile(file, &c, 1);
if (rv < 0) {
PCHECK(rv == 0) << "read";
} else {
CHECK_EQ(rv, 0) << "read";
}
}
void CheckedCloseFile(FileHandle file) {
CHECK(LoggingCloseFile(file));
}
} // namespace crashpad

139
util/file/file_io.h Normal file
View File

@ -0,0 +1,139 @@
// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_UTIL_FILE_FILE_IO_H_
#define CRASHPAD_UTIL_FILE_FILE_IO_H_
#include <sys/types.h>
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#endif
namespace crashpad {
#if defined(OS_POSIX)
using FileHandle = int;
#elif defined(OS_WIN)
using FileHandle = HANDLE;
#endif
//! \brief Reads from a file, retrying when interrupted on POSIX or following a
//! short read.
//!
//! This function reads into \a buffer, stopping only when \a size bytes have
//! been read or when end-of-file has been reached. On Windows, reading from
//! sockets is not currently supported.
//!
//! \return The number of bytes read and placed into \a buffer, or `-1` on
//! error, with `errno` or `GetLastError()` set appropriately. On error, a
//! portion of \a file may have been read into \a buffer.
//!
//! \sa WriteFile
//! \sa LoggingReadFile
//! \sa CheckedReadFile
//! \sa CheckedReadFileAtEOF
ssize_t ReadFile(FileHandle file, void* buffer, size_t size);
//! \brief Writes to a file, retrying when interrupted or following a short
//! write on POSIX.
//!
//! This function writes to \a file, stopping only when \a size bytes have been
//! written.
//!
//! \return The number of bytes written from \a buffer, or `-1` on error, with
//! `errno` or `GetLastError()` set appropriately. On error, a portion of
//! \a buffer may have been written to \a file.
//!
//! \sa ReadFile
//! \sa LoggingWriteFile
//! \sa CheckedWriteFile
ssize_t WriteFile(FileHandle file, const void* buffer, size_t size);
//! \brief Wraps ReadFile(), ensuring that exactly \a size bytes are read.
//!
//! \return `true` on success. If \a size is out of the range of possible
//! ReadFile() return values, if the underlying ReadFile() fails, or if
//! other than \a size bytes were read, this function logs a message and
//! returns `false`.
//!
//! \sa LoggingWriteFile
//! \sa ReadFile
//! \sa CheckedReadFile
//! \sa CheckedReadFileAtEOF
bool LoggingReadFile(FileHandle file, void* buffer, size_t size);
//! \brief Wraps WriteFile(), ensuring that exactly \a size bytes are written.
//!
//! \return `true` on success. If \a size is out of the range of possible
//! WriteFile() return values, if the underlying WriteFile() fails, or if
//! other than \a size bytes were written, this function logs a message and
//! returns `false`.
//!
//! \sa LoggingReadFile
//! \sa WriteFile
//! \sa CheckedWriteFile
bool LoggingWriteFile(FileHandle file, const void* buffer, size_t size);
//! \brief Wraps ReadFile(), ensuring that exactly \a size bytes are read.
//!
//! If \a size is out of the range of possible ReadFile() return values, if the
//! underlying ReadFile() fails, or if other than \a size bytes were read, this
//! function causes execution to terminate without returning.
//!
//! \sa CheckedWriteFile
//! \sa ReadFile
//! \sa LoggingReadFile
//! \sa CheckedReadFileAtEOF
void CheckedReadFile(FileHandle file, void* buffer, size_t size);
//! \brief Wraps WriteFile(), ensuring that exactly \a size bytes are written.
//!
//! If \a size is out of the range of possible WriteFile() return values, if the
//! underlying WriteFile() fails, or if other than \a size bytes were written,
//! this function causes execution to terminate without returning.
//!
//! \sa CheckedReadFile
//! \sa WriteFile
//! \sa LoggingWriteFile
void CheckedWriteFile(FileHandle file, const void* buffer, size_t size);
//! \brief Wraps ReadFile(), ensuring that it indicates end-of-file.
//!
//! Attempts to read a single byte from \a file, expecting no data to be read.
//! If the underlying ReadFile() fails, or if a byte actually is read, this
//! function causes execution to terminate without returning.
//!
//! \sa CheckedReadFile
//! \sa ReadFile
void CheckedReadFileAtEOF(FileHandle file);
//! \brief Wraps `close()` or `CloseHandle()`, logging an error if the operation
//! fails.
//!
//! \return On success, `true` is returned. On failure, an error is logged and
//! `false` is returned.
bool LoggingCloseFile(FileHandle file);
//! \brief Wraps `close()` or `CloseHandle()`, ensuring that it succeeds.
//!
//! If the underlying function fails, this function causes execution to
//! terminate without returning.
void CheckedCloseFile(FileHandle file);
} // namespace crashpad
#endif // CRASHPAD_UTIL_FILE_FILE_IO_H_

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "util/file/fd_io.h" #include "util/file/file_io.h"
#include <unistd.h> #include <unistd.h>
@ -66,70 +66,18 @@ ssize_t ReadOrWrite(int fd,
namespace crashpad { namespace crashpad {
ssize_t ReadFD(int fd, void* buffer, size_t size) { ssize_t ReadFile(FileHandle file, void* buffer, size_t size) {
return ReadOrWrite<ReadTraits>(fd, buffer, size); return ReadOrWrite<ReadTraits>(file, buffer, size);
} }
ssize_t WriteFD(int fd, const void* buffer, size_t size) { ssize_t WriteFile(FileHandle file, const void* buffer, size_t size) {
return ReadOrWrite<WriteTraits>(fd, buffer, size); return ReadOrWrite<WriteTraits>(file, buffer, size);
} }
bool LoggingReadFD(int fd, void* buffer, size_t size) { bool LoggingCloseFile(FileHandle file) {
ssize_t expect = base::checked_cast<ssize_t>(size); int rv = IGNORE_EINTR(close(file));
ssize_t rv = ReadFD(fd, buffer, size);
if (rv < 0) {
PLOG(ERROR) << "read";
return false;
}
if (rv != expect) {
LOG(ERROR) << "read: expected " << expect << ", observed " << rv;
return false;
}
return true;
}
bool LoggingWriteFD(int fd, const void* buffer, size_t size) {
ssize_t expect = base::checked_cast<ssize_t>(size);
ssize_t rv = WriteFD(fd, buffer, size);
if (rv < 0) {
PLOG(ERROR) << "write";
return false;
}
if (rv != expect) {
LOG(ERROR) << "write: expected " << expect << ", observed " << rv;
return false;
}
return true;
}
void CheckedReadFD(int fd, void* buffer, size_t size) {
CHECK(LoggingReadFD(fd, buffer, size));
}
void CheckedWriteFD(int fd, const void* buffer, size_t size) {
CHECK(LoggingWriteFD(fd, buffer, size));
}
void CheckedReadFDAtEOF(int fd) {
char c;
ssize_t rv = ReadFD(fd, &c, 1);
if (rv < 0) {
PCHECK(rv == 0) << "read";
} else {
CHECK_EQ(rv, 0) << "read";
}
}
bool LoggingCloseFD(int fd) {
int rv = IGNORE_EINTR(close(fd));
PLOG_IF(ERROR, rv != 0) << "close"; PLOG_IF(ERROR, rv != 0) << "close";
return rv == 0; return rv == 0;
} }
void CheckedCloseFD(int fd) {
CHECK(LoggingCloseFD(fd));
}
} // namespace crashpad } // namespace crashpad

81
util/file/file_io_win.cc Normal file
View File

@ -0,0 +1,81 @@
// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/file/file_io.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
namespace {
bool IsSocketHandle(HANDLE file) {
if (GetFileType(file) == FILE_TYPE_PIPE) {
// FILE_TYPE_PIPE means that it's a socket, a named pipe, or an anonymous
// pipe. If we are unable to retrieve the pipe information, we know it's a
// socket.
return !GetNamedPipeInfo(file, NULL, NULL, NULL, NULL);
}
return false;
}
} // namespace
namespace crashpad {
// TODO(scottmg): Handle > DWORD sized writes if necessary.
ssize_t ReadFile(FileHandle file, void* buffer, size_t size) {
DCHECK(!IsSocketHandle(file));
DWORD size_dword = base::checked_cast<DWORD>(size);
DWORD total_read = 0;
char* buffer_c = reinterpret_cast<char*>(buffer);
while (size_dword > 0) {
DWORD bytes_read;
BOOL success = ::ReadFile(file, buffer_c, size_dword, &bytes_read, nullptr);
if (!success && GetLastError() != ERROR_MORE_DATA) {
return -1;
} else if (success && bytes_read == 0 &&
GetFileType(file) != FILE_TYPE_PIPE) {
// Zero bytes read for a file indicates reaching EOF. Zero bytes read from
// a pipe indicates only that there was a zero byte WriteFile issued on
// the other end, so continue reading.
break;
}
buffer_c += bytes_read;
size_dword -= bytes_read;
total_read += bytes_read;
}
return total_read;
}
ssize_t WriteFile(FileHandle file, const void* buffer, size_t size) {
// TODO(scottmg): This might need to handle the limit for pipes across a
// network in the future.
DWORD size_dword = base::checked_cast<DWORD>(size);
DWORD bytes_written;
BOOL rv = ::WriteFile(file, buffer, size_dword, &bytes_written, nullptr);
if (!rv)
return -1;
CHECK_EQ(bytes_written, size_dword);
return bytes_written;
}
bool LoggingCloseFile(FileHandle file) {
BOOL rv = CloseHandle(file);
PLOG_IF(ERROR, !rv) << "CloseHandle";
return rv;
}
} // namespace crashpad

View File

@ -20,7 +20,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"
#include "util/file/fd_io.h" #include "util/file/file_io.h"
namespace crashpad { namespace crashpad {
@ -61,7 +61,7 @@ bool FileWriter::Write(const void* data, size_t size) {
DCHECK(fd_.is_valid()); DCHECK(fd_.is_valid());
// TODO(mark): Write no more than SSIZE_MAX bytes in a single call. // TODO(mark): Write no more than SSIZE_MAX bytes in a single call.
ssize_t written = WriteFD(fd_.get(), data, size); ssize_t written = WriteFile(fd_.get(), data, size);
if (written < 0) { if (written < 0) {
PLOG(ERROR) << "write"; PLOG(ERROR) << "write";
return false; return false;

View File

@ -31,7 +31,7 @@
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"
#include "base/rand_util.h" #include "base/rand_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "util/file/fd_io.h" #include "util/file/file_io.h"
#include "util/mach/child_port.h" #include "util/mach/child_port.h"
#include "util/mach/mach_extensions.h" #include "util/mach/mach_extensions.h"
#include "util/mach/mach_message.h" #include "util/mach/mach_message.h"
@ -85,7 +85,7 @@ mach_port_t ChildPortHandshake::RunServer() {
// Initialize the token and share it with the client via the pipe. // Initialize the token and share it with the client via the pipe.
token_ = base::RandUint64(); token_ = base::RandUint64();
int pipe_write = pipe_write_owner.get(); int pipe_write = pipe_write_owner.get();
if (!LoggingWriteFD(pipe_write, &token_, sizeof(token_))) { if (!LoggingWriteFile(pipe_write, &token_, sizeof(token_))) {
LOG(WARNING) << "no client check-in"; LOG(WARNING) << "no client check-in";
return MACH_PORT_NULL; return MACH_PORT_NULL;
} }
@ -113,13 +113,14 @@ mach_port_t ChildPortHandshake::RunServer() {
// Share the service name with the client via the pipe. // Share the service name with the client via the pipe.
uint32_t service_name_length = service_name.size(); uint32_t service_name_length = service_name.size();
if (!LoggingWriteFD( if (!LoggingWriteFile(
pipe_write, &service_name_length, sizeof(service_name_length))) { pipe_write, &service_name_length, sizeof(service_name_length))) {
LOG(WARNING) << "no client check-in"; LOG(WARNING) << "no client check-in";
return MACH_PORT_NULL; return MACH_PORT_NULL;
} }
if (!LoggingWriteFD(pipe_write, service_name.c_str(), service_name_length)) { if (!LoggingWriteFile(
pipe_write, service_name.c_str(), service_name_length)) {
LOG(WARNING) << "no client check-in"; LOG(WARNING) << "no client check-in";
return MACH_PORT_NULL; return MACH_PORT_NULL;
} }
@ -310,17 +311,17 @@ void ChildPortHandshake::RunClientInternal_ReadPipe(int pipe_read,
child_port_token_t* token, child_port_token_t* token,
std::string* service_name) { std::string* service_name) {
// Read the token from the pipe. // Read the token from the pipe.
CheckedReadFD(pipe_read, token, sizeof(*token)); CheckedReadFile(pipe_read, token, sizeof(*token));
// Read the service name from the pipe. // Read the service name from the pipe.
uint32_t service_name_length; uint32_t service_name_length;
CheckedReadFD(pipe_read, &service_name_length, sizeof(service_name_length)); CheckedReadFile(pipe_read, &service_name_length, sizeof(service_name_length));
DCHECK_LT(service_name_length, DCHECK_LT(service_name_length,
implicit_cast<uint32_t>(BOOTSTRAP_MAX_NAME_LEN)); implicit_cast<uint32_t>(BOOTSTRAP_MAX_NAME_LEN));
if (service_name_length > 0) { if (service_name_length > 0) {
service_name->resize(service_name_length); service_name->resize(service_name_length);
CheckedReadFD(pipe_read, &(*service_name)[0], service_name_length); CheckedReadFile(pipe_read, &(*service_name)[0], service_name_length);
} }
} }

View File

@ -25,7 +25,7 @@
#include "base/mac/scoped_mach_port.h" #include "base/mac/scoped_mach_port.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "util/file/fd_io.h" #include "util/file/file_io.h"
#include "util/mach/exc_server_variants.h" #include "util/mach/exc_server_variants.h"
#include "util/mach/mach_extensions.h" #include "util/mach/mach_extensions.h"
#include "util/mach/mach_message.h" #include "util/mach/mach_message.h"
@ -242,10 +242,10 @@ class TestExceptionPorts : public MachMultiprocess,
// Tell the parent process that everything is set up. // Tell the parent process that everything is set up.
char c = '\0'; char c = '\0';
CheckedWriteFD(test_exception_ports_->WritePipeFD(), &c, 1); CheckedWriteFile(test_exception_ports_->WritePipeFD(), &c, 1);
// Wait for the parent process to say that its end is set up. // Wait for the parent process to say that its end is set up.
CheckedReadFD(test_exception_ports_->ReadPipeFD(), &c, 1); CheckedReadFile(test_exception_ports_->ReadPipeFD(), &c, 1);
EXPECT_EQ('\0', c); EXPECT_EQ('\0', c);
// Regardless of where ExceptionPorts::SetExceptionPort() ran, // Regardless of where ExceptionPorts::SetExceptionPort() ran,
@ -359,7 +359,7 @@ class TestExceptionPorts : public MachMultiprocess,
// Wait for the child process to be ready. It needs to have all of its // Wait for the child process to be ready. It needs to have all of its
// threads set up before proceeding if in kSetOutOfProcess mode. // threads set up before proceeding if in kSetOutOfProcess mode.
char c; char c;
CheckedReadFD(ReadPipeFD(), &c, 1); CheckedReadFile(ReadPipeFD(), &c, 1);
EXPECT_EQ('\0', c); EXPECT_EQ('\0', c);
mach_port_t local_port = LocalPort(); mach_port_t local_port = LocalPort();
@ -442,7 +442,7 @@ class TestExceptionPorts : public MachMultiprocess,
// Let the child process know that everything in the parent process is set // Let the child process know that everything in the parent process is set
// up. // up.
c = '\0'; c = '\0';
CheckedWriteFD(WritePipeFD(), &c, 1); CheckedWriteFile(WritePipeFD(), &c, 1);
if (who_crashes_ != kNobodyCrashes) { if (who_crashes_ != kNobodyCrashes) {
UniversalMachExcServer universal_mach_exc_server(this); UniversalMachExcServer universal_mach_exc_server(this);
@ -463,7 +463,7 @@ class TestExceptionPorts : public MachMultiprocess,
// Wait for the child process to exit or terminate, as indicated by it // Wait for the child process to exit or terminate, as indicated by it
// closing its pipe. This keeps LocalPort() alive in the child as // closing its pipe. This keeps LocalPort() alive in the child as
// RemotePort(), for the childs use in its TestGetExceptionPorts(). // RemotePort(), for the childs use in its TestGetExceptionPorts().
CheckedReadFDAtEOF(ReadPipeFD()); CheckedReadFileAtEOF(ReadPipeFD());
} }
void MachMultiprocessChild() override { void MachMultiprocessChild() override {

View File

@ -22,7 +22,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/mac/scoped_mach_port.h" #include "base/mac/scoped_mach_port.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "util/file/fd_io.h" #include "util/file/file_io.h"
#include "util/mach/mach_extensions.h" #include "util/mach/mach_extensions.h"
#include "util/mach/mach_message.h" #include "util/mach/mach_message.h"
#include "util/test/mac/mach_errors.h" #include "util/test/mac/mach_errors.h"
@ -335,13 +335,13 @@ class TestMachMessageServer : public MachMessageServer::Interface,
if (options_.child_wait_for_parent_pipe_early) { if (options_.child_wait_for_parent_pipe_early) {
// Tell the child to begin sending messages. // Tell the child to begin sending messages.
char c = '\0'; char c = '\0';
CheckedWriteFD(WritePipeFD(), &c, 1); CheckedWriteFile(WritePipeFD(), &c, 1);
} }
if (options_.parent_wait_for_child_pipe) { if (options_.parent_wait_for_child_pipe) {
// Wait until the child is done sending what its going to send. // Wait until the child is done sending what its going to send.
char c; char c;
CheckedReadFD(ReadPipeFD(), &c, 1); CheckedReadFile(ReadPipeFD(), &c, 1);
EXPECT_EQ('\0', c); EXPECT_EQ('\0', c);
} }
@ -386,7 +386,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
if (options_.child_wait_for_parent_pipe_late) { if (options_.child_wait_for_parent_pipe_late) {
// Let the child know its safe to exit. // Let the child know its safe to exit.
char c = '\0'; char c = '\0';
CheckedWriteFD(WritePipeFD(), &c, 1); CheckedWriteFile(WritePipeFD(), &c, 1);
} }
} }
@ -394,7 +394,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
if (options_.child_wait_for_parent_pipe_early) { if (options_.child_wait_for_parent_pipe_early) {
// Wait until the parent is done setting things up on its end. // Wait until the parent is done setting things up on its end.
char c; char c;
CheckedReadFD(ReadPipeFD(), &c, 1); CheckedReadFile(ReadPipeFD(), &c, 1);
EXPECT_EQ('\0', c); EXPECT_EQ('\0', c);
} }
@ -428,7 +428,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
if (options_.child_wait_for_parent_pipe_late) { if (options_.child_wait_for_parent_pipe_late) {
char c; char c;
CheckedReadFD(ReadPipeFD(), &c, 1); CheckedReadFile(ReadPipeFD(), &c, 1);
ASSERT_EQ('\0', c); ASSERT_EQ('\0', c);
} }
} }
@ -550,7 +550,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
// running MachMessageServer() once its received. // running MachMessageServer() once its received.
void ChildNotifyParentViaPipe() { void ChildNotifyParentViaPipe() {
char c = '\0'; char c = '\0';
CheckedWriteFD(WritePipeFD(), &c, 1); CheckedWriteFile(WritePipeFD(), &c, 1);
} }
// In the child process, sends a request message to the server and then // In the child process, sends a request message to the server and then

View File

@ -17,7 +17,6 @@
#include <mach/mach.h> #include <mach/mach.h>
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "util/file/fd_io.h"
#include "util/test/mac/mach_errors.h" #include "util/test/mac/mach_errors.h"
#include "util/test/mac/mach_multiprocess.h" #include "util/test/mac/mach_multiprocess.h"

View File

@ -24,7 +24,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "util/file/fd_io.h" #include "util/file/file_io.h"
namespace crashpad { namespace crashpad {
@ -55,7 +55,7 @@ FileHTTPBodyStream::FileHTTPBodyStream(const base::FilePath& path)
FileHTTPBodyStream::~FileHTTPBodyStream() { FileHTTPBodyStream::~FileHTTPBodyStream() {
if (fd_ >= 0) { if (fd_ >= 0) {
LoggingCloseFD(fd_); LoggingCloseFile(fd_);
} }
} }
@ -77,9 +77,9 @@ ssize_t FileHTTPBodyStream::GetBytesBuffer(uint8_t* buffer, size_t max_len) {
break; break;
} }
ssize_t rv = ReadFD(fd_, buffer, max_len); ssize_t rv = ReadFile(fd_, buffer, max_len);
if (rv == 0) { if (rv == 0) {
LoggingCloseFD(fd_); LoggingCloseFile(fd_);
fd_ = kClosedAtEOF; fd_ = kClosedAtEOF;
} else if (rv < 0) { } else if (rv < 0) {
PLOG(ERROR) << "read"; PLOG(ERROR) << "read";

View File

@ -24,7 +24,7 @@
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "util/file/fd_io.h" #include "util/file/file_io.h"
#include "util/net/http_body.h" #include "util/net/http_body.h"
#include "util/net/http_headers.h" #include "util/net/http_headers.h"
#include "util/net/http_multipart_builder.h" #include "util/net/http_multipart_builder.h"
@ -63,12 +63,12 @@ class HTTPTransportTestFixture : public MultiprocessExec {
// The child will write the HTTP server port number as a packed unsigned // The child will write the HTTP server port number as a packed unsigned
// short to stdout. // short to stdout.
uint16_t port; uint16_t port;
ASSERT_TRUE(LoggingReadFD(ReadPipeFD(), &port, sizeof(port))); ASSERT_TRUE(LoggingReadFile(ReadPipeFD(), &port, sizeof(port)));
// Then the parent will tell the web server what response code to send // Then the parent will tell the web server what response code to send
// for the HTTP request. // for the HTTP request.
ASSERT_TRUE( ASSERT_TRUE(LoggingWriteFile(
LoggingWriteFD(WritePipeFD(), &response_code_, sizeof(response_code_))); WritePipeFD(), &response_code_, sizeof(response_code_)));
// Now execute the HTTP request. // Now execute the HTTP request.
scoped_ptr<HTTPTransport> transport(HTTPTransport::Create()); scoped_ptr<HTTPTransport> transport(HTTPTransport::Create());
@ -85,7 +85,7 @@ class HTTPTransportTestFixture : public MultiprocessExec {
std::string request; std::string request;
char buf[32]; char buf[32];
ssize_t bytes_read; ssize_t bytes_read;
while ((bytes_read = ReadFD(ReadPipeFD(), buf, sizeof(buf))) != 0) { while ((bytes_read = ReadFile(ReadPipeFD(), buf, sizeof(buf))) != 0) {
ASSERT_GE(bytes_read, 0); ASSERT_GE(bytes_read, 0);
request.append(buf, bytes_read); request.append(buf, bytes_read);
} }

View File

@ -26,7 +26,7 @@
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/rand_util.h" #include "base/rand_util.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "util/file/fd_io.h" #include "util/file/file_io.h"
#include "util/mach/mach_extensions.h" #include "util/mach/mach_extensions.h"
#include "util/misc/scoped_forbid_return.h" #include "util/misc/scoped_forbid_return.h"
#include "util/test/errors.h" #include "util/test/errors.h"
@ -262,7 +262,7 @@ void MachMultiprocess::MultiprocessChild() {
// Wait for the parent process to close its end of the pipe. The child process // Wait for the parent process to close its end of the pipe. The child process
// needs to remain alive until then because the parent process will attempt to // needs to remain alive until then because the parent process will attempt to
// verify it using the task port it has access to via ChildTask(). // verify it using the task port it has access to via ChildTask().
CheckedReadFDAtEOF(ReadPipeFD()); CheckedReadFileAtEOF(ReadPipeFD());
if (testing::Test::HasFailure()) { if (testing::Test::HasFailure()) {
// Trigger the ScopedForbidReturn destructor. // Trigger the ScopedForbidReturn destructor.

View File

@ -18,7 +18,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "util/file/fd_io.h" #include "util/file/file_io.h"
#include "util/test/executable_path.h" #include "util/test/executable_path.h"
namespace crashpad { namespace crashpad {
@ -37,9 +37,9 @@ class TestMultiprocessExec final : public MultiprocessExec {
// gracefully with a gtest assertion if the child does not execute properly. // gracefully with a gtest assertion if the child does not execute properly.
char c = 'z'; char c = 'z';
ASSERT_TRUE(LoggingWriteFD(WritePipeFD(), &c, 1)); ASSERT_TRUE(LoggingWriteFile(WritePipeFD(), &c, 1));
ASSERT_TRUE(LoggingReadFD(ReadPipeFD(), &c, 1)); ASSERT_TRUE(LoggingReadFile(ReadPipeFD(), &c, 1));
EXPECT_EQ('Z', c); EXPECT_EQ('Z', c);
} }

View File

@ -20,7 +20,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "util/file/fd_io.h" #include "util/file/file_io.h"
namespace crashpad { namespace crashpad {
namespace test { namespace test {
@ -38,31 +38,31 @@ class TestMultiprocess final : public Multiprocess {
void MultiprocessParent() override { void MultiprocessParent() override {
int read_fd = ReadPipeFD(); int read_fd = ReadPipeFD();
char c; char c;
CheckedReadFD(read_fd, &c, 1); CheckedReadFile(read_fd, &c, 1);
EXPECT_EQ('M', c); EXPECT_EQ('M', c);
pid_t pid; pid_t pid;
CheckedReadFD(read_fd, &pid, sizeof(pid)); CheckedReadFile(read_fd, &pid, sizeof(pid));
EXPECT_EQ(pid, ChildPID()); EXPECT_EQ(pid, ChildPID());
c = 'm'; c = 'm';
CheckedWriteFD(WritePipeFD(), &c, 1); CheckedWriteFile(WritePipeFD(), &c, 1);
// The child will close its end of the pipe and exit. Make sure that the // The child will close its end of the pipe and exit. Make sure that the
// parent sees EOF. // parent sees EOF.
CheckedReadFDAtEOF(read_fd); CheckedReadFileAtEOF(read_fd);
} }
void MultiprocessChild() override { void MultiprocessChild() override {
int write_fd = WritePipeFD(); int write_fd = WritePipeFD();
char c = 'M'; char c = 'M';
CheckedWriteFD(write_fd, &c, 1); CheckedWriteFile(write_fd, &c, 1);
pid_t pid = getpid(); pid_t pid = getpid();
CheckedWriteFD(write_fd, &pid, sizeof(pid)); CheckedWriteFile(write_fd, &pid, sizeof(pid));
CheckedReadFD(ReadPipeFD(), &c, 1); CheckedReadFile(ReadPipeFD(), &c, 1);
EXPECT_EQ('m', c); EXPECT_EQ('m', c);
} }
@ -178,18 +178,18 @@ class TestMultiprocessClosePipe final : public Multiprocess {
// the read pipe in this process to show end-of-file. // the read pipe in this process to show end-of-file.
void VerifyPartner() { void VerifyPartner() {
if (what_closes_ == kWriteCloses) { if (what_closes_ == kWriteCloses) {
CheckedReadFDAtEOF(ReadPipeFD()); CheckedReadFileAtEOF(ReadPipeFD());
} else if (what_closes_ == kReadAndWriteClose) { } else if (what_closes_ == kReadAndWriteClose) {
CheckedReadFDAtEOF(ReadPipeFD()); CheckedReadFileAtEOF(ReadPipeFD());
char c = '\0'; char c = '\0';
// This will raise SIGPIPE. If fatal (the normal case), that will cause // This will raise SIGPIPE. If fatal (the normal case), that will cause
// process termination. If SIGPIPE is being handled somewhere, the write // process termination. If SIGPIPE is being handled somewhere, the write
// will still fail and set errno to EPIPE, and CheckedWriteFD() will abort // will still fail and set errno to EPIPE, and CheckedWriteFile() will
// execution. Regardless of how SIGPIPE is handled, the process will be // abort execution. Regardless of how SIGPIPE is handled, the process will
// terminated. Because the actual termination mechanism is not known, no // be terminated. Because the actual termination mechanism is not known,
// regex can be specified. // no regex can be specified.
EXPECT_DEATH(CheckedWriteFD(WritePipeFD(), &c, 1), ""); EXPECT_DEATH(CheckedWriteFile(WritePipeFD(), &c, 1), "");
} }
} }

View File

@ -26,8 +26,10 @@
'<(INTERMEDIATE_DIR)', '<(INTERMEDIATE_DIR)',
], ],
'sources': [ 'sources': [
'file/fd_io.cc', 'file/file_io.cc',
'file/fd_io.h', 'file/file_io.h',
'file/file_io_posix.cc',
'file/file_io_win.cc',
'file/file_writer.cc', 'file/file_writer.cc',
'file/file_writer.h', 'file/file_writer.h',
'file/string_file_writer.cc', 'file/string_file_writer.cc',