From 21da6eb1f8a9740de03cb1435bf935f5a3609a37 Mon Sep 17 00:00:00 2001 From: "erikkay@google.com" Date: Mon, 3 Nov 2008 17:18:14 +0000 Subject: * Add write and read/write support to FileStream (renamed from FileInputStream). * Moved net/disk_cache/os_file to base/platform_file. Review URL: http://codereview.chromium.org/8843 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@4454 0039d316-1c4b-4281-b951-d872f2087c98 --- net/base/file_input_stream.h | 92 -------- net/base/file_input_stream_posix.cc | 143 ------------- net/base/file_input_stream_unittest.cc | 205 ------------------ net/base/file_input_stream_win.cc | 230 -------------------- net/base/file_stream.h | 109 ++++++++++ net/base/file_stream_posix.cc | 177 ++++++++++++++++ net/base/file_stream_unittest.cc | 369 +++++++++++++++++++++++++++++++++ net/base/file_stream_win.cc | 254 +++++++++++++++++++++++ net/base/upload_data_stream.cc | 4 +- net/base/upload_data_stream.h | 4 +- 10 files changed, 914 insertions(+), 673 deletions(-) delete mode 100644 net/base/file_input_stream.h delete mode 100644 net/base/file_input_stream_posix.cc delete mode 100644 net/base/file_input_stream_unittest.cc delete mode 100644 net/base/file_input_stream_win.cc create mode 100644 net/base/file_stream.h create mode 100644 net/base/file_stream_posix.cc create mode 100644 net/base/file_stream_unittest.cc create mode 100644 net/base/file_stream_win.cc (limited to 'net/base') diff --git a/net/base/file_input_stream.h b/net/base/file_input_stream.h deleted file mode 100644 index eb4b2ae..0000000 --- a/net/base/file_input_stream.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this -// source code is governed by a BSD-style license that can be found in the -// LICENSE file. - -// This file defines FileInputStream, a basic interface for reading files -// synchronously or asynchronously with support for seeking to an offset. - -#ifndef NET_BASE_FILE_INPUT_STREAM_H_ -#define NET_BASE_FILE_INPUT_STREAM_H_ - -#include "net/base/completion_callback.h" - -#if defined(OS_WIN) -typedef void* HANDLE; -#endif - -namespace net { - -// TODO(darin): Move this to a more generic location. -// This explicit mapping matches both FILE_ on Windows and SEEK_ on Linux. -enum Whence { - FROM_BEGIN = 0, - FROM_CURRENT = 1, - FROM_END = 2 -}; - -class FileInputStream { - public: - FileInputStream(); - ~FileInputStream(); - - // Call this method to close the FileInputStream. It is OK to call Close - // multiple times. Redundant calls are ignored. - void Close(); - - // Call this method to open the FileInputStream. The remaining methods - // cannot be used unless this method returns OK. If the file cannot be - // opened then an error code is returned. - // NOTE: The underlying file is opened with non-exclusive access. - int Open(const std::wstring& path, bool asynchronous_mode); - - // Returns true if Open succeeded and Close has not been called. - bool IsOpen() const; - - // Adjust the position from where data is read. Upon success, the stream - // position relative to the start of the file is returned. Otherwise, an - // error code is returned. It is not valid to call Seek while a Read call - // has a pending completion. - int64 Seek(Whence whence, int64 offset); - - // Returns the number of bytes available to read from the current stream - // position until the end of the file. Otherwise, an error code is returned. - int64 Available(); - - // Call this method to read data from the current stream position. Up to - // buf_len bytes will be copied into buf. (In other words, partial reads are - // allowed.) Returns the number of bytes copied, 0 if at end-of-file, or an - // error code if the operation could not be performed. - // - // If opened with |asynchronous_mode| set to true, then a non-null callback - // must be passed to this method. In asynchronous mode, if the read could - // not complete synchronously, then ERR_IO_PENDING is returned, and the - // callback will be notified on the current thread (via the MessageLoop) when - // the read has completed. - // - // In the case of an asychronous read, the memory pointed to by |buf| must - // remain valid until the callback is notified. However, it is valid to - // destroy or close the file stream while there is an asynchronous read in - // progress. That will cancel the read and allow the buffer to be freed. - // - int Read(char* buf, int buf_len, CompletionCallback* callback); - - private: - class AsyncContext; - friend class AsyncContext; - - // This member is used to support asynchronous reads. It is non-null when - // the FileInputStream was opened with asynchronous_mode set to true. - scoped_ptr async_context_; - -#if defined(OS_WIN) - HANDLE handle_; -#elif defined(OS_POSIX) - int fd_; -#endif - - DISALLOW_COPY_AND_ASSIGN(FileInputStream); -}; - -} // namespace net - -#endif // NET_BASE_FILE_INPUT_STREAM_H_ diff --git a/net/base/file_input_stream_posix.cc b/net/base/file_input_stream_posix.cc deleted file mode 100644 index 4f2eb937..0000000 --- a/net/base/file_input_stream_posix.cc +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this -// source code is governed by a BSD-style license that can be found in the -// LICENSE file. - -// For 64-bit file access (off_t = off64_t, lseek64, etc). -#define _FILE_OFFSET_BITS 64 - -#include "net/base/file_input_stream.h" - -#include -#include -#include -#include -#include - -#include "base/basictypes.h" -#include "base/logging.h" -#include "base/message_loop.h" -#include "base/string_util.h" -#include "net/base/net_errors.h" - -// We cast back and forth, so make sure it's the size we're expecting. -COMPILE_ASSERT(sizeof(int64) == sizeof(off_t), off_t_64_bit); - -// Make sure our Whence mappings match the system headers. -COMPILE_ASSERT(net::FROM_BEGIN == SEEK_SET && - net::FROM_CURRENT == SEEK_CUR && - net::FROM_END == SEEK_END, whence_matches_system); - -namespace net { - -// FileInputStream::AsyncContext ---------------------------------------------- - -// TODO(deanm): Figure out how to best do async IO. -class FileInputStream::AsyncContext { - public: - - CompletionCallback* callback() const { return NULL; } - private: - - DISALLOW_COPY_AND_ASSIGN(AsyncContext); -}; - -// FileInputStream ------------------------------------------------------------ - -FileInputStream::FileInputStream() : fd_(-1) { - DCHECK(!IsOpen()); -} - -FileInputStream::~FileInputStream() { - Close(); -} - -void FileInputStream::Close() { - if (fd_ != -1) { - if (close(fd_) != 0) { - NOTREACHED(); - } - fd_ = -1; - } - async_context_.reset(); -} - -// Map from errno to net error codes. -static int64 MapErrorCode(int err) { - switch(err) { - case ENOENT: - return ERR_FILE_NOT_FOUND; - case EACCES: - return ERR_ACCESS_DENIED; - default: - LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; - return ERR_FAILED; - } -} - -int FileInputStream::Open(const std::wstring& path, bool asynchronous_mode) { - // We don't need O_LARGEFILE here since we set the 64-bit off_t feature. - fd_ = open(WideToUTF8(path).c_str(), 0, O_RDONLY); - if (fd_ == -1) - return MapErrorCode(errno); - - return OK; -} - -bool FileInputStream::IsOpen() const { - return fd_ != -1; -} - -int64 FileInputStream::Seek(Whence whence, int64 offset) { - if (!IsOpen()) - return ERR_UNEXPECTED; - - // If we're in async, make sure we don't have a request in flight. - DCHECK(!async_context_.get() || !async_context_->callback()); - - off_t res = lseek(fd_, static_cast(offset), static_cast(whence)); - if (res == static_cast(-1)) - return MapErrorCode(errno); - - return res; -} - -int64 FileInputStream::Available() { - if (!IsOpen()) - return ERR_UNEXPECTED; - - int64 cur_pos = Seek(FROM_CURRENT, 0); - if (cur_pos < 0) - return cur_pos; - - struct stat info; - if (fstat(fd_, &info) != 0) - return MapErrorCode(errno); - - int64 size = static_cast(info.st_size); - DCHECK(size >= cur_pos); - - return size - cur_pos; -} - -// TODO(deanm): async. -int FileInputStream::Read( - char* buf, int buf_len, CompletionCallback* callback) { - // read(..., 0) will return 0, which indicates end-of-file. - DCHECK(buf_len > 0 && buf_len <= SSIZE_MAX); - - if (!IsOpen()) - return ERR_UNEXPECTED; - - // Loop in the case of getting interrupted by a signal. - for (;;) { - ssize_t res = read(fd_, buf, static_cast(buf_len)); - if (res == static_cast(-1)) { - if (errno == EINTR) - continue; - return MapErrorCode(errno); - } - return static_cast(res); - } -} - -} // namespace net diff --git a/net/base/file_input_stream_unittest.cc b/net/base/file_input_stream_unittest.cc deleted file mode 100644 index b259de8..0000000 --- a/net/base/file_input_stream_unittest.cc +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this -// source code is governed by a BSD-style license that can be found in the -// LICENSE file. - -#include "base/file_util.h" -#include "base/path_service.h" -#include "base/platform_test.h" -#include "net/base/file_input_stream.h" -#include "net/base/net_errors.h" -#include "net/base/test_completion_callback.h" -#include "testing/gtest/include/gtest/gtest.h" - -static const char kTestData[] = "0123456789"; - -class FileInputStreamTest : public PlatformTest { - public: - virtual void SetUp() { - PlatformTest::SetUp(); - - file_util::CreateTemporaryFileName(&temp_file_path_); - file_util::WriteFile(temp_file_path_, kTestData, arraysize(kTestData)-1); - } - virtual void TearDown() { - file_util::Delete(temp_file_path_, false); - - PlatformTest::TearDown(); - } - const std::wstring temp_file_path() const { return temp_file_path_; } - private: - std::wstring temp_file_path_; -}; - -TEST_F(FileInputStreamTest, BasicOpenClose) { - net::FileInputStream stream; - int rv = stream.Open(temp_file_path(), false); - EXPECT_EQ(net::OK, rv); -} - -TEST_F(FileInputStreamTest, UseClosedStream) { - net::FileInputStream stream; - - EXPECT_FALSE(stream.IsOpen()); - - // Try seeking... - int64 new_offset = stream.Seek(net::FROM_BEGIN, 5); - EXPECT_EQ(net::ERR_UNEXPECTED, new_offset); - - // Try available... - int64 avail = stream.Available(); - EXPECT_EQ(net::ERR_UNEXPECTED, avail); - - // Try reading... - char buf[10]; - int rv = stream.Read(buf, sizeof(buf), NULL); - EXPECT_EQ(net::ERR_UNEXPECTED, rv); -} - -TEST_F(FileInputStreamTest, BasicRead) { - int64 file_size; - bool ok = file_util::GetFileSize(temp_file_path(), &file_size); - EXPECT_TRUE(ok); - - net::FileInputStream stream; - int rv = stream.Open(temp_file_path(), false); - EXPECT_EQ(net::OK, rv); - - int64 total_bytes_avail = stream.Available(); - EXPECT_EQ(file_size, total_bytes_avail); - - int64 total_bytes_read = 0; - - std::string data_read; - for (;;) { - char buf[4]; - rv = stream.Read(buf, sizeof(buf), NULL); - EXPECT_LE(0, rv); - if (rv <= 0) - break; - total_bytes_read += rv; - data_read.append(buf, rv); - } - EXPECT_EQ(file_size, total_bytes_read); - EXPECT_TRUE(data_read == kTestData); -} - -TEST_F(FileInputStreamTest, AsyncRead) { - int64 file_size; - bool ok = file_util::GetFileSize(temp_file_path(), &file_size); - EXPECT_TRUE(ok); - - net::FileInputStream stream; - int rv = stream.Open(temp_file_path(), true); - EXPECT_EQ(net::OK, rv); - - int64 total_bytes_avail = stream.Available(); - EXPECT_EQ(file_size, total_bytes_avail); - - TestCompletionCallback callback; - - int64 total_bytes_read = 0; - - std::string data_read; - for (;;) { - char buf[4]; - rv = stream.Read(buf, sizeof(buf), &callback); - if (rv == net::ERR_IO_PENDING) - rv = callback.WaitForResult(); - EXPECT_LE(0, rv); - if (rv <= 0) - break; - total_bytes_read += rv; - data_read.append(buf, rv); - } - EXPECT_EQ(file_size, total_bytes_read); - EXPECT_TRUE(data_read == kTestData); -} - -TEST_F(FileInputStreamTest, BasicRead_FromOffset) { - int64 file_size; - bool ok = file_util::GetFileSize(temp_file_path(), &file_size); - EXPECT_TRUE(ok); - - net::FileInputStream stream; - int rv = stream.Open(temp_file_path(), false); - EXPECT_EQ(net::OK, rv); - - const int64 kOffset = 3; - int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); - EXPECT_EQ(kOffset, new_offset); - - int64 total_bytes_avail = stream.Available(); - EXPECT_EQ(file_size - kOffset, total_bytes_avail); - - int64 total_bytes_read = 0; - - std::string data_read; - for (;;) { - char buf[4]; - rv = stream.Read(buf, sizeof(buf), NULL); - EXPECT_LE(0, rv); - if (rv <= 0) - break; - total_bytes_read += rv; - data_read.append(buf, rv); - } - EXPECT_EQ(file_size - kOffset, total_bytes_read); - EXPECT_TRUE(data_read == kTestData + kOffset); -} - -TEST_F(FileInputStreamTest, AsyncRead_FromOffset) { - int64 file_size; - bool ok = file_util::GetFileSize(temp_file_path(), &file_size); - EXPECT_TRUE(ok); - - net::FileInputStream stream; - int rv = stream.Open(temp_file_path(), true); - EXPECT_EQ(net::OK, rv); - - const int64 kOffset = 3; - int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); - EXPECT_EQ(kOffset, new_offset); - - int64 total_bytes_avail = stream.Available(); - EXPECT_EQ(file_size - kOffset, total_bytes_avail); - - TestCompletionCallback callback; - - int64 total_bytes_read = 0; - - std::string data_read; - for (;;) { - char buf[4]; - rv = stream.Read(buf, sizeof(buf), &callback); - if (rv == net::ERR_IO_PENDING) - rv = callback.WaitForResult(); - EXPECT_LE(0, rv); - if (rv <= 0) - break; - total_bytes_read += rv; - data_read.append(buf, rv); - } - EXPECT_EQ(file_size - kOffset, total_bytes_read); - EXPECT_TRUE(data_read == kTestData + kOffset); -} - -TEST_F(FileInputStreamTest, SeekAround) { - net::FileInputStream stream; - int rv = stream.Open(temp_file_path(), true); - EXPECT_EQ(net::OK, rv); - - const int64 kOffset = 3; - int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); - EXPECT_EQ(kOffset, new_offset); - - new_offset = stream.Seek(net::FROM_CURRENT, kOffset); - EXPECT_EQ(2 * kOffset, new_offset); - - new_offset = stream.Seek(net::FROM_CURRENT, -kOffset); - EXPECT_EQ(kOffset, new_offset); - - const int kTestDataLen = arraysize(kTestData) - 1; - - new_offset = stream.Seek(net::FROM_END, -kTestDataLen); - EXPECT_EQ(0, new_offset); -} diff --git a/net/base/file_input_stream_win.cc b/net/base/file_input_stream_win.cc deleted file mode 100644 index f964362..0000000 --- a/net/base/file_input_stream_win.cc +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this -// source code is governed by a BSD-style license that can be found in the -// LICENSE file. - -#include "net/base/file_input_stream.h" - -#include - -#include "base/logging.h" -#include "base/message_loop.h" -#include "net/base/net_errors.h" - -namespace net { - -// Ensure that we can just use our Whence values directly. -COMPILE_ASSERT(FROM_BEGIN == FILE_BEGIN, bad_whence_begin); -COMPILE_ASSERT(FROM_CURRENT == FILE_CURRENT, bad_whence_current); -COMPILE_ASSERT(FROM_END == FILE_END, bad_whence_end); - -static void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) { - overlapped->Offset = offset.LowPart; - overlapped->OffsetHigh = offset.HighPart; -} - -static void IncrementOffset(OVERLAPPED* overlapped, DWORD count) { - LARGE_INTEGER offset; - offset.LowPart = overlapped->Offset; - offset.HighPart = overlapped->OffsetHigh; - offset.QuadPart += static_cast(count); - SetOffset(overlapped, offset); -} - -static int MapErrorCode(DWORD err) { - switch (err) { - case ERROR_FILE_NOT_FOUND: - case ERROR_PATH_NOT_FOUND: - return ERR_FILE_NOT_FOUND; - case ERROR_ACCESS_DENIED: - return ERR_ACCESS_DENIED; - case ERROR_SUCCESS: - return OK; - default: - LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; - return ERR_FAILED; - } -} - -// FileInputStream::AsyncContext ---------------------------------------------- - -class FileInputStream::AsyncContext : public MessageLoopForIO::IOHandler { - public: - AsyncContext(FileInputStream* owner) - : owner_(owner), overlapped_(), callback_(NULL) { - overlapped_.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - } - - ~AsyncContext() { - if (callback_) - MessageLoopForIO::current()->RegisterIOContext(&overlapped_, NULL); - CloseHandle(overlapped_.hEvent); - } - - void IOCompletionIsPending(CompletionCallback* callback); - - OVERLAPPED* overlapped() { return &overlapped_; } - CompletionCallback* callback() const { return callback_; } - - private: - // MessageLoopForIO::IOHandler implementation: - virtual void OnIOCompleted(OVERLAPPED* context, DWORD bytes_read, - DWORD error); - - FileInputStream* owner_; - OVERLAPPED overlapped_; - CompletionCallback* callback_; -}; - -void FileInputStream::AsyncContext::IOCompletionIsPending( - CompletionCallback* callback) { - DCHECK(!callback_); - callback_ = callback; - - MessageLoopForIO::current()->RegisterIOContext(&overlapped_, this); -} - -void FileInputStream::AsyncContext::OnIOCompleted(OVERLAPPED* context, - DWORD bytes_read, - DWORD error) { - DCHECK(&overlapped_ == context); - DCHECK(callback_); - - MessageLoopForIO::current()->RegisterIOContext(&overlapped_, NULL); - - HANDLE handle = owner_->handle_; - - int result = static_cast(bytes_read); - if (error && error != ERROR_HANDLE_EOF) - result = MapErrorCode(error); - - if (bytes_read) - IncrementOffset(&overlapped_, bytes_read); - - CompletionCallback* temp = NULL; - std::swap(temp, callback_); - temp->Run(result); -} - -// FileInputStream ------------------------------------------------------------ - -FileInputStream::FileInputStream() : handle_(INVALID_HANDLE_VALUE) { -} - -FileInputStream::~FileInputStream() { - Close(); -} - -void FileInputStream::Close() { - if (handle_ != INVALID_HANDLE_VALUE) { - CloseHandle(handle_); - handle_ = INVALID_HANDLE_VALUE; - } - async_context_.reset(); -} - -int FileInputStream::Open(const std::wstring& path, bool asynchronous_mode) { - if (IsOpen()) { - DLOG(FATAL) << "File is already open!"; - return ERR_UNEXPECTED; - } - - // Optimize for streaming, not seeking. If someone does a lot of random - // access operations, then we should consider revising this. - DWORD create_file_flags = FILE_FLAG_SEQUENTIAL_SCAN; - - if (asynchronous_mode) - create_file_flags |= FILE_FLAG_OVERLAPPED; - - handle_ = - CreateFile(path.c_str(), GENERIC_READ | SYNCHRONIZE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, OPEN_EXISTING, create_file_flags, NULL); - if (handle_ == INVALID_HANDLE_VALUE) { - DWORD error = GetLastError(); - LOG(WARNING) << "Failed to open file: " << error; - return MapErrorCode(error); - } - - if (asynchronous_mode) { - async_context_.reset(new AsyncContext(this)); - MessageLoopForIO::current()->RegisterIOHandler(handle_, - async_context_.get()); - } - - return OK; -} - -bool FileInputStream::IsOpen() const { - return handle_ != INVALID_HANDLE_VALUE; -} - -int64 FileInputStream::Seek(Whence whence, int64 offset) { - if (!IsOpen()) - return ERR_UNEXPECTED; - DCHECK(!async_context_.get() || !async_context_->callback()); - - LARGE_INTEGER distance, result; - distance.QuadPart = offset; - DWORD move_method = static_cast(whence); - if (!SetFilePointerEx(handle_, distance, &result, move_method)) { - DWORD error = GetLastError(); - LOG(WARNING) << "SetFilePointerEx failed: " << error; - return MapErrorCode(error); - } - if (async_context_.get()) - SetOffset(async_context_->overlapped(), result); - return result.QuadPart; -} - -int64 FileInputStream::Available() { - if (!IsOpen()) - return ERR_UNEXPECTED; - - int64 cur_pos = Seek(FROM_CURRENT, 0); - if (cur_pos < 0) - return cur_pos; - - LARGE_INTEGER file_size; - if (!GetFileSizeEx(handle_, &file_size)) { - DWORD error = GetLastError(); - LOG(WARNING) << "GetFileSizeEx failed: " << error; - return MapErrorCode(error); - } - - return file_size.QuadPart - cur_pos; -} - -int FileInputStream::Read( - char* buf, int buf_len, CompletionCallback* callback) { - if (!IsOpen()) - return ERR_UNEXPECTED; - - OVERLAPPED* overlapped = NULL; - if (async_context_.get()) { - DCHECK(!async_context_->callback()); - overlapped = async_context_->overlapped(); - } - - int rv; - - DWORD bytes_read; - if (!ReadFile(handle_, buf, buf_len, &bytes_read, overlapped)) { - DWORD error = GetLastError(); - if (async_context_.get() && error == ERROR_IO_PENDING) { - async_context_->IOCompletionIsPending(callback); - rv = ERR_IO_PENDING; - } else if (error == ERROR_HANDLE_EOF) { - rv = 0; // Report EOF by returning 0 bytes read. - } else { - LOG(WARNING) << "ReadFile failed: " << error; - rv = MapErrorCode(error); - } - } else { - if (overlapped) - IncrementOffset(overlapped, bytes_read); - rv = static_cast(bytes_read); - } - return rv; -} - -} // namespace net diff --git a/net/base/file_stream.h b/net/base/file_stream.h new file mode 100644 index 0000000..b98cb15 --- /dev/null +++ b/net/base/file_stream.h @@ -0,0 +1,109 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +// This file defines FileStream, a basic interface for reading and writing files +// synchronously or asynchronously with support for seeking to an offset. +// Note that even when used asynchronously, only one operation is supported at +// a time. + +#ifndef NET_BASE_FILE_STREAM_H_ +#define NET_BASE_FILE_STREAM_H_ + +#include "base/platform_file.h" +#include "net/base/completion_callback.h" + +namespace net { + +// TODO(darin): Move this to a more generic location. +// This explicit mapping matches both FILE_ on Windows and SEEK_ on Linux. +enum Whence { + FROM_BEGIN = 0, + FROM_CURRENT = 1, + FROM_END = 2 +}; + +class FileStream { + public: + FileStream(); + ~FileStream(); + + // Call this method to close the FileStream. It is OK to call Close + // multiple times. Redundant calls are ignored. + // Note that if there are any pending async operations, they'll be aborted. + void Close(); + + // Call this method to open the FileStream. The remaining methods + // cannot be used unless this method returns OK. If the file cannot be + // opened then an error code is returned. + // open_flags is a bitfield of base::PlatformFileFlags + int Open(const std::wstring& path, int open_flags); + + // Returns true if Open succeeded and Close has not been called. + bool IsOpen() const; + + // Adjust the position from where data is read. Upon success, the stream + // position relative to the start of the file is returned. Otherwise, an + // error code is returned. It is not valid to call Seek while a Read call + // has a pending completion. + int64 Seek(Whence whence, int64 offset); + + // Returns the number of bytes available to read from the current stream + // position until the end of the file. Otherwise, an error code is returned. + int64 Available(); + + // Call this method to read data from the current stream position. Up to + // buf_len bytes will be copied into buf. (In other words, partial reads are + // allowed.) Returns the number of bytes copied, 0 if at end-of-file, or an + // error code if the operation could not be performed. + // + // If opened with PLATFORM_FILE_ASYNC, then a non-null callback + // must be passed to this method. In asynchronous mode, if the read could + // not complete synchronously, then ERR_IO_PENDING is returned, and the + // callback will be notified on the current thread (via the MessageLoop) when + // the read has completed. + // + // In the case of an asychronous read, the memory pointed to by |buf| must + // remain valid until the callback is notified. However, it is valid to + // destroy or close the file stream while there is an asynchronous read in + // progress. That will cancel the read and allow the buffer to be freed. + // + // This method should not be called if the stream was opened WRITE_ONLY. + int Read(char* buf, int buf_len, CompletionCallback* callback); + + // Call this method to write data at the current stream position. Up to + // buf_len bytes will be written from buf. (In other words, partial writes are + // allowed.) Returns the number of bytes written, or an error code if the + // operation could not be performed. + // + // If opened with PLATFORM_FILE_ASYNC, then a non-null callback + // must be passed to this method. In asynchronous mode, if the write could + // not complete synchronously, then ERR_IO_PENDING is returned, and the + // callback will be notified on the current thread (via the MessageLoop) when + // the write has completed. + // + // In the case of an asychronous write, the memory pointed to by |buf| must + // remain valid until the callback is notified. However, it is valid to + // destroy or close the file stream while there is an asynchronous write in + // progress. That will cancel the write and allow the buffer to be freed. + // + // This method should not be called if the stream was opened READ_ONLY. + int Write(const char* buf, int buf_len, CompletionCallback* callback); + + private: + class AsyncContext; + friend class AsyncContext; + + // This member is used to support asynchronous reads. It is non-null when + // the FileStream was opened with PLATFORM_FILE_ASYNC. + scoped_ptr async_context_; + + base::PlatformFile file_; + int open_flags_; + + DISALLOW_COPY_AND_ASSIGN(FileStream); +}; + +} // namespace net + +#endif // NET_BASE_FILE_STREAM_H_ diff --git a/net/base/file_stream_posix.cc b/net/base/file_stream_posix.cc new file mode 100644 index 0000000..53589166 --- /dev/null +++ b/net/base/file_stream_posix.cc @@ -0,0 +1,177 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +// For 64-bit file access (off_t = off64_t, lseek64, etc). +#define _FILE_OFFSET_BITS 64 + +#include "net/base/file_stream.h" + +#include +#include +#include +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "net/base/net_errors.h" + +// We cast back and forth, so make sure it's the size we're expecting. +COMPILE_ASSERT(sizeof(int64) == sizeof(off_t), off_t_64_bit); + +// Make sure our Whence mappings match the system headers. +COMPILE_ASSERT(net::FROM_BEGIN == SEEK_SET && + net::FROM_CURRENT == SEEK_CUR && + net::FROM_END == SEEK_END, whence_matches_system); + +namespace net { + +// FileStream::AsyncContext ---------------------------------------------- + +// TODO(deanm): Figure out how to best do async IO. +class FileStream::AsyncContext { + public: + + CompletionCallback* callback() const { return NULL; } + private: + + DISALLOW_COPY_AND_ASSIGN(AsyncContext); +}; + +// FileStream ------------------------------------------------------------ + +FileStream::FileStream() : file_(base::kInvalidPlatformFileValue) { + DCHECK(!IsOpen()); +} + +FileStream::~FileStream() { + Close(); +} + +void FileStream::Close() { + if (file_ != base::kInvalidPlatformFileValue) { + if (close(file_) != 0) { + NOTREACHED(); + } + file_ = base::kInvalidPlatformFileValue; + } + async_context_.reset(); +} + +// Map from errno to net error codes. +static int64 MapErrorCode(int err) { + switch(err) { + case ENOENT: + return ERR_FILE_NOT_FOUND; + case EACCES: + return ERR_ACCESS_DENIED; + default: + LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; + return ERR_FAILED; + } +} + +int FileStream::Open(const std::wstring& path, int open_flags) { + if (IsOpen()) { + DLOG(FATAL) << "File is already open!"; + return ERR_UNEXPECTED; + } + + open_flags_ = open_flags; + file_ = base::CreatePlatformFile(path, open_flags_, NULL); + if (file_ == base::kInvalidPlatformFileValue) { + LOG(WARNING) << "Failed to open file: " << errno; + return MapErrorCode(errno); + } + + return OK; +} + +bool FileStream::IsOpen() const { + return file_ != base::kInvalidPlatformFileValue; +} + +int64 FileStream::Seek(Whence whence, int64 offset) { + if (!IsOpen()) + return ERR_UNEXPECTED; + + // If we're in async, make sure we don't have a request in flight. + DCHECK(!async_context_.get() || !async_context_->callback()); + + off_t res = lseek(file_, static_cast(offset), + static_cast(whence)); + if (res == static_cast(-1)) + return MapErrorCode(errno); + + return res; +} + +int64 FileStream::Available() { + if (!IsOpen()) + return ERR_UNEXPECTED; + + int64 cur_pos = Seek(FROM_CURRENT, 0); + if (cur_pos < 0) + return cur_pos; + + struct stat info; + if (fstat(file_, &info) != 0) + return MapErrorCode(errno); + + int64 size = static_cast(info.st_size); + DCHECK(size >= cur_pos); + + return size - cur_pos; +} + +// TODO(deanm): async. +int FileStream::Read( + char* buf, int buf_len, CompletionCallback* callback) { + // read(..., 0) will return 0, which indicates end-of-file. + DCHECK(buf_len > 0 && buf_len <= SSIZE_MAX); + + if (!IsOpen()) + return ERR_UNEXPECTED; + + // Loop in the case of getting interrupted by a signal. + for (;;) { + ssize_t res = read(file_, buf, static_cast(buf_len)); + if (res == static_cast(-1)) { + if (errno == EINTR) + continue; + return MapErrorCode(errno); + } + return static_cast(res); + } +} + +// TODO(deanm): async. +int FileStream::Write( + const char* buf, int buf_len, CompletionCallback* callback) { + + // read(..., 0) will return 0, which indicates end-of-file. + DCHECK(buf_len > 0 && buf_len <= SSIZE_MAX); + + if (!IsOpen()) + return ERR_UNEXPECTED; + + int total_bytes_written = 0; + size_t len = static_cast(buf_len); + while (total_bytes_written < buf_len) { + ssize_t res = write(file_, buf, len); + if (res == static_cast(-1)) { + if (errno == EINTR) + continue; + return MapErrorCode(errno); + } + total_bytes_written += res; + buf += res; + len -= res; + } + return total_bytes_written; +} + +} // namespace net diff --git a/net/base/file_stream_unittest.cc b/net/base/file_stream_unittest.cc new file mode 100644 index 0000000..f3f208b --- /dev/null +++ b/net/base/file_stream_unittest.cc @@ -0,0 +1,369 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/platform_test.h" +#include "net/base/file_stream.h" +#include "net/base/net_errors.h" +#include "net/base/test_completion_callback.h" +#include "testing/gtest/include/gtest/gtest.h" + +static const char kTestData[] = "0123456789"; +static const int kTestDataSize = arraysize(kTestData) - 1; + +class FileStreamTest : public PlatformTest { + public: + virtual void SetUp() { + PlatformTest::SetUp(); + + file_util::CreateTemporaryFileName(&temp_file_path_); + file_util::WriteFile(temp_file_path_, kTestData, kTestDataSize); + } + virtual void TearDown() { + file_util::Delete(temp_file_path_, false); + + PlatformTest::TearDown(); + } + const std::wstring temp_file_path() const { return temp_file_path_; } + private: + std::wstring temp_file_path_; +}; + +TEST_F(FileStreamTest, BasicOpenClose) { + net::FileStream stream; + int rv = stream.Open(temp_file_path(), + base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ); + EXPECT_EQ(net::OK, rv); +} + +TEST_F(FileStreamTest, UseClosedStream) { + net::FileStream stream; + + EXPECT_FALSE(stream.IsOpen()); + + // Try seeking... + int64 new_offset = stream.Seek(net::FROM_BEGIN, 5); + EXPECT_EQ(net::ERR_UNEXPECTED, new_offset); + + // Try available... + int64 avail = stream.Available(); + EXPECT_EQ(net::ERR_UNEXPECTED, avail); + + // Try reading... + char buf[10]; + int rv = stream.Read(buf, sizeof(buf), NULL); + EXPECT_EQ(net::ERR_UNEXPECTED, rv); +} + +TEST_F(FileStreamTest, BasicRead) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size, total_bytes_avail); + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), NULL); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size, total_bytes_read); + EXPECT_TRUE(data_read == kTestData); +} + +TEST_F(FileStreamTest, AsyncRead) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_ASYNC; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size, total_bytes_avail); + + TestCompletionCallback callback; + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), &callback); + if (rv == net::ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size, total_bytes_read); + EXPECT_TRUE(data_read == kTestData); +} + +TEST_F(FileStreamTest, BasicRead_FromOffset) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + const int64 kOffset = 3; + int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); + EXPECT_EQ(kOffset, new_offset); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size - kOffset, total_bytes_avail); + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), NULL); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size - kOffset, total_bytes_read); + EXPECT_TRUE(data_read == kTestData + kOffset); +} + +TEST_F(FileStreamTest, AsyncRead_FromOffset) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_ASYNC; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + const int64 kOffset = 3; + int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); + EXPECT_EQ(kOffset, new_offset); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size - kOffset, total_bytes_avail); + + TestCompletionCallback callback; + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), &callback); + if (rv == net::ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size - kOffset, total_bytes_read); + EXPECT_TRUE(data_read == kTestData + kOffset); +} + +TEST_F(FileStreamTest, SeekAround) { + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + const int64 kOffset = 3; + int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); + EXPECT_EQ(kOffset, new_offset); + + new_offset = stream.Seek(net::FROM_CURRENT, kOffset); + EXPECT_EQ(2 * kOffset, new_offset); + + new_offset = stream.Seek(net::FROM_CURRENT, -kOffset); + EXPECT_EQ(kOffset, new_offset); + + const int kTestDataLen = arraysize(kTestData) - 1; + + new_offset = stream.Seek(net::FROM_END, -kTestDataLen); + EXPECT_EQ(0, new_offset); +} + +TEST_F(FileStreamTest, BasicWrite) { + net::FileStream stream; + int flags = base::PLATFORM_FILE_CREATE_ALWAYS | + base::PLATFORM_FILE_WRITE; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(0, file_size); + + rv = stream.Write(kTestData, kTestDataSize, NULL); + EXPECT_EQ(kTestDataSize, rv); + stream.Close(); + + ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(kTestDataSize, file_size); +} + +TEST_F(FileStreamTest, AsyncWrite) { + net::FileStream stream; + int flags = base::PLATFORM_FILE_CREATE_ALWAYS | + base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_ASYNC; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(0, file_size); + + TestCompletionCallback callback; + int64 total_bytes_written = 0; + + while (total_bytes_written != kTestDataSize) { + rv = stream.Write(kTestData, kTestDataSize, &callback); + if (rv == net::ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_LT(0, rv); + if (rv <= 0) + break; + total_bytes_written += rv; + } + ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(file_size, total_bytes_written); +} + +TEST_F(FileStreamTest, BasicWrite_FromOffset) { + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_WRITE; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(kTestDataSize, file_size); + + const int64 kOffset = 0; + int64 new_offset = stream.Seek(net::FROM_END, kOffset); + EXPECT_EQ(kTestDataSize, new_offset); + + rv = stream.Write(kTestData, kTestDataSize, NULL); + EXPECT_EQ(kTestDataSize, rv); + stream.Close(); + + ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(kTestDataSize * 2, file_size); +} + +TEST_F(FileStreamTest, AsyncWrite_FromOffset) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_ASYNC; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + const int64 kOffset = 0; + int64 new_offset = stream.Seek(net::FROM_END, kOffset); + EXPECT_EQ(kTestDataSize, new_offset); + + TestCompletionCallback callback; + int64 total_bytes_written = 0; + + while (total_bytes_written != kTestDataSize) { + rv = stream.Write(kTestData, kTestDataSize, &callback); + if (rv == net::ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_LT(0, rv); + if (rv <= 0) + break; + total_bytes_written += rv; + } + ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(file_size, kTestDataSize * 2); +} + +TEST_F(FileStreamTest, BasicReadWrite) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileStream stream; + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_WRITE; + int rv = stream.Open(temp_file_path(), flags); + EXPECT_EQ(net::OK, rv); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size, total_bytes_avail); + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), NULL); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size, total_bytes_read); + EXPECT_TRUE(data_read == kTestData); + + rv = stream.Write(kTestData, kTestDataSize, NULL); + EXPECT_EQ(kTestDataSize, rv); + stream.Close(); + + ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + EXPECT_EQ(kTestDataSize * 2, file_size); +} + +// TODO(erikkay): more READ_WRITE tests? diff --git a/net/base/file_stream_win.cc b/net/base/file_stream_win.cc new file mode 100644 index 0000000..e59ef0d --- /dev/null +++ b/net/base/file_stream_win.cc @@ -0,0 +1,254 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +#include "net/base/file_stream.h" + +#include + +#include "base/logging.h" +#include "base/message_loop.h" +#include "net/base/net_errors.h" + +namespace net { + +// Ensure that we can just use our Whence values directly. +COMPILE_ASSERT(FROM_BEGIN == FILE_BEGIN, bad_whence_begin); +COMPILE_ASSERT(FROM_CURRENT == FILE_CURRENT, bad_whence_current); +COMPILE_ASSERT(FROM_END == FILE_END, bad_whence_end); + +static void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) { + overlapped->Offset = offset.LowPart; + overlapped->OffsetHigh = offset.HighPart; +} + +static void IncrementOffset(OVERLAPPED* overlapped, DWORD count) { + LARGE_INTEGER offset; + offset.LowPart = overlapped->Offset; + offset.HighPart = overlapped->OffsetHigh; + offset.QuadPart += static_cast(count); + SetOffset(overlapped, offset); +} + +static int MapErrorCode(DWORD err) { + switch (err) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + return ERR_FILE_NOT_FOUND; + case ERROR_ACCESS_DENIED: + return ERR_ACCESS_DENIED; + case ERROR_SUCCESS: + return OK; + default: + LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; + return ERR_FAILED; + } +} + +// FileStream::AsyncContext ---------------------------------------------- + +class FileStream::AsyncContext : public MessageLoopForIO::IOHandler { + public: + AsyncContext(FileStream* owner) + : owner_(owner), overlapped_(), callback_(NULL) { + overlapped_.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + } + + ~AsyncContext() { + if (callback_) + MessageLoopForIO::current()->RegisterIOContext(&overlapped_, NULL); + CloseHandle(overlapped_.hEvent); + } + + void IOCompletionIsPending(CompletionCallback* callback); + + OVERLAPPED* overlapped() { return &overlapped_; } + CompletionCallback* callback() const { return callback_; } + + private: + // MessageLoopForIO::IOHandler implementation: + virtual void OnIOCompleted(OVERLAPPED* context, DWORD num_bytes, + DWORD error); + + FileStream* owner_; + OVERLAPPED overlapped_; + CompletionCallback* callback_; +}; + +void FileStream::AsyncContext::IOCompletionIsPending( + CompletionCallback* callback) { + DCHECK(!callback_); + callback_ = callback; + + MessageLoopForIO::current()->RegisterIOContext(&overlapped_, this); +} + +void FileStream::AsyncContext::OnIOCompleted(OVERLAPPED* context, + DWORD num_bytes, + DWORD error) { + DCHECK(&overlapped_ == context); + DCHECK(callback_); + + MessageLoopForIO::current()->RegisterIOContext(&overlapped_, NULL); + + HANDLE handle = owner_->file_; + + int result = static_cast(num_bytes); + if (error && error != ERROR_HANDLE_EOF) + result = MapErrorCode(error); + + if (num_bytes) + IncrementOffset(&overlapped_, num_bytes); + + CompletionCallback* temp = NULL; + std::swap(temp, callback_); + temp->Run(result); +} + +// FileStream ------------------------------------------------------------ + +FileStream::FileStream() : file_(INVALID_HANDLE_VALUE) { +} + +FileStream::~FileStream() { + Close(); +} + +void FileStream::Close() { + if (file_ != INVALID_HANDLE_VALUE) { + CloseHandle(file_); + file_ = INVALID_HANDLE_VALUE; + } + async_context_.reset(); +} + +int FileStream::Open(const std::wstring& path, int open_flags) { + if (IsOpen()) { + DLOG(FATAL) << "File is already open!"; + return ERR_UNEXPECTED; + } + + open_flags_ = open_flags; + file_ = base::CreatePlatformFile(path, open_flags_, NULL); + if (file_ == INVALID_HANDLE_VALUE) { + DWORD error = GetLastError(); + LOG(WARNING) << "Failed to open file: " << error; + return MapErrorCode(error); + } + + if (open_flags_ & base::PLATFORM_FILE_ASYNC) { + async_context_.reset(new AsyncContext(this)); + MessageLoopForIO::current()->RegisterIOHandler(file_, + async_context_.get()); + } + + return OK; +} + +bool FileStream::IsOpen() const { + return file_ != INVALID_HANDLE_VALUE; +} + +int64 FileStream::Seek(Whence whence, int64 offset) { + if (!IsOpen()) + return ERR_UNEXPECTED; + DCHECK(!async_context_.get() || !async_context_->callback()); + + LARGE_INTEGER distance, result; + distance.QuadPart = offset; + DWORD move_method = static_cast(whence); + if (!SetFilePointerEx(file_, distance, &result, move_method)) { + DWORD error = GetLastError(); + LOG(WARNING) << "SetFilePointerEx failed: " << error; + return MapErrorCode(error); + } + if (async_context_.get()) + SetOffset(async_context_->overlapped(), result); + return result.QuadPart; +} + +int64 FileStream::Available() { + if (!IsOpen()) + return ERR_UNEXPECTED; + + int64 cur_pos = Seek(FROM_CURRENT, 0); + if (cur_pos < 0) + return cur_pos; + + LARGE_INTEGER file_size; + if (!GetFileSizeEx(file_, &file_size)) { + DWORD error = GetLastError(); + LOG(WARNING) << "GetFileSizeEx failed: " << error; + return MapErrorCode(error); + } + + return file_size.QuadPart - cur_pos; +} + +int FileStream::Read( + char* buf, int buf_len, CompletionCallback* callback) { + if (!IsOpen()) + return ERR_UNEXPECTED; + DCHECK(open_flags_ & base::PLATFORM_FILE_READ); + + OVERLAPPED* overlapped = NULL; + if (async_context_.get()) { + DCHECK(!async_context_->callback()); + overlapped = async_context_->overlapped(); + } + + int rv; + + DWORD bytes_read; + if (!ReadFile(file_, buf, buf_len, &bytes_read, overlapped)) { + DWORD error = GetLastError(); + if (async_context_.get() && error == ERROR_IO_PENDING) { + async_context_->IOCompletionIsPending(callback); + rv = ERR_IO_PENDING; + } else if (error == ERROR_HANDLE_EOF) { + rv = 0; // Report EOF by returning 0 bytes read. + } else { + LOG(WARNING) << "ReadFile failed: " << error; + rv = MapErrorCode(error); + } + } else { + if (overlapped) + IncrementOffset(overlapped, bytes_read); + rv = static_cast(bytes_read); + } + return rv; +} + +int FileStream::Write( + const char* buf, int buf_len, CompletionCallback* callback) { + if (!IsOpen()) + return ERR_UNEXPECTED; + DCHECK(open_flags_ & base::PLATFORM_FILE_READ); + + OVERLAPPED* overlapped = NULL; + if (async_context_.get()) { + DCHECK(!async_context_->callback()); + overlapped = async_context_->overlapped(); + } + + int rv; + DWORD bytes_written; + if (!WriteFile(file_, buf, buf_len, &bytes_written, overlapped)) { + DWORD error = GetLastError(); + if (async_context_.get() && error == ERROR_IO_PENDING) { + async_context_->IOCompletionIsPending(callback); + rv = ERR_IO_PENDING; + } else { + LOG(WARNING) << "WriteFile failed: " << error; + rv = MapErrorCode(error); + } + } else { + if (overlapped) + IncrementOffset(overlapped, bytes_written); + rv = static_cast(bytes_written); + } + return rv; +} + +} // namespace net + diff --git a/net/base/upload_data_stream.cc b/net/base/upload_data_stream.cc index df5ef0f..e874439 100644 --- a/net/base/upload_data_stream.cc +++ b/net/base/upload_data_stream.cc @@ -68,7 +68,9 @@ void UploadDataStream::FillBuf() { DCHECK(element.type() == UploadData::TYPE_FILE); if (!next_element_stream_.IsOpen()) { - int rv = next_element_stream_.Open(element.file_path(), false); + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ; + int rv = next_element_stream_.Open(element.file_path(), flags); // If the file does not exist, that's technically okay.. we'll just // upload an empty file. This is for consistency with Mozilla. DLOG_IF(WARNING, rv != OK) << "Failed to open \"" << diff --git a/net/base/upload_data_stream.h b/net/base/upload_data_stream.h index 9d22d2e..7c2bb6d 100644 --- a/net/base/upload_data_stream.h +++ b/net/base/upload_data_stream.h @@ -5,7 +5,7 @@ #ifndef NET_BASE_UPLOAD_DATA_STREAM_H_ #define NET_BASE_UPLOAD_DATA_STREAM_H_ -#include "net/base/file_input_stream.h" +#include "net/base/file_stream.h" #include "net/base/upload_data.h" namespace net { @@ -53,7 +53,7 @@ class UploadDataStream { // A stream to the currently open file, for next_element_ if the next element // is a TYPE_FILE element. - FileInputStream next_element_stream_; + FileStream next_element_stream_; // The number of bytes remaining to be read from the currently open file // if the next element is of TYPE_FILE. -- cgit v1.1