// Copyright (c) 2006-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/disk_cache/file.h" #include "base/message_loop.h" #include "base/singleton.h" #include "net/disk_cache/disk_cache.h" namespace { // Structure used for asynchronous operations. struct MyOverlapped { MyOverlapped(disk_cache::File* file, size_t offset, disk_cache::FileIOCallback* callback); ~MyOverlapped(); OVERLAPPED* overlapped() { return &context_.overlapped; } MessageLoopForIO::IOContext context_; scoped_refptr file_; disk_cache::FileIOCallback* callback_; const void* buffer_; bool delete_buffer_; // Delete the user buffer at completion. }; COMPILE_ASSERT(!offsetof(MyOverlapped, context_), starts_with_overlapped); // Helper class to handle the IO completion notifications from the message loop. class CompletionHandler : public MessageLoopForIO::IOHandler { virtual void OnIOCompleted(MessageLoopForIO::IOContext* context, DWORD actual_bytes, DWORD error); }; void CompletionHandler::OnIOCompleted(MessageLoopForIO::IOContext* context, DWORD actual_bytes, DWORD error) { MyOverlapped* data = reinterpret_cast(context); if (error) { DCHECK(!actual_bytes); actual_bytes = static_cast(-1); NOTREACHED(); } if (data->callback_) data->callback_->OnFileIOComplete(static_cast(actual_bytes)); delete data; } MyOverlapped::MyOverlapped(disk_cache::File* file, size_t offset, disk_cache::FileIOCallback* callback) { memset(this, 0, sizeof(*this)); context_.handler = Singleton::get(); context_.overlapped.Offset = static_cast(offset); file_ = file; callback_ = callback; } MyOverlapped::~MyOverlapped() { if (delete_buffer_) { DCHECK(!callback_); delete buffer_; } } } // namespace namespace disk_cache { // Used from WaitForPendingIO() when the cache is being destroyed. MessageLoopForIO::IOHandler* GetFileIOHandler() { return Singleton::get(); } File::File(base::PlatformFile file) : init_(true), platform_file_(INVALID_HANDLE_VALUE), sync_platform_file_(file) { } bool File::Init(const std::wstring& name) { DCHECK(!init_); if (init_) return false; platform_file_ = CreateFile(name.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (INVALID_HANDLE_VALUE == platform_file_) return false; MessageLoopForIO::current()->RegisterIOHandler( platform_file_, Singleton::get()); init_ = true; sync_platform_file_ = CreateFile(name.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == sync_platform_file_) return false; return true; } File::~File() { if (!init_) return; if (INVALID_HANDLE_VALUE != platform_file_) CloseHandle(platform_file_); if (INVALID_HANDLE_VALUE != sync_platform_file_) CloseHandle(sync_platform_file_); } base::PlatformFile File::platform_file() const { DCHECK(init_); return (INVALID_HANDLE_VALUE == platform_file_) ? sync_platform_file_ : platform_file_; } bool File::IsValid() const { if (!init_) return false; return (INVALID_HANDLE_VALUE != platform_file_ || INVALID_HANDLE_VALUE != sync_platform_file_); } bool File::Read(void* buffer, size_t buffer_len, size_t offset) { DCHECK(init_); if (buffer_len > ULONG_MAX || offset > LONG_MAX) return false; DWORD ret = SetFilePointer(sync_platform_file_, static_cast(offset), NULL, FILE_BEGIN); if (INVALID_SET_FILE_POINTER == ret) return false; DWORD actual; DWORD size = static_cast(buffer_len); if (!ReadFile(sync_platform_file_, buffer, size, &actual, NULL)) return false; return actual == size; } bool File::Write(const void* buffer, size_t buffer_len, size_t offset) { DCHECK(init_); if (buffer_len > ULONG_MAX || offset > ULONG_MAX) return false; DWORD ret = SetFilePointer(sync_platform_file_, static_cast(offset), NULL, FILE_BEGIN); if (INVALID_SET_FILE_POINTER == ret) return false; DWORD actual; DWORD size = static_cast(buffer_len); if (!WriteFile(sync_platform_file_, buffer, size, &actual, NULL)) return false; return actual == size; } // We have to increase the ref counter of the file before performing the IO to // prevent the completion to happen with an invalid handle (if the file is // closed while the IO is in flight). bool File::Read(void* buffer, size_t buffer_len, size_t offset, FileIOCallback* callback, bool* completed) { DCHECK(init_); if (!callback) return Read(buffer, buffer_len, offset); if (buffer_len > ULONG_MAX || offset > ULONG_MAX) return false; MyOverlapped* data = new MyOverlapped(this, offset, callback); DWORD size = static_cast(buffer_len); DWORD actual; if (!ReadFile(platform_file_, buffer, size, &actual, data->overlapped())) { *completed = false; if (GetLastError() == ERROR_IO_PENDING) return true; delete data; return false; } // The operation completed already. We'll be called back anyway. *completed = (actual == size); DCHECK(actual == size); data->callback_ = NULL; data->file_ = NULL; // There is no reason to hold on to this anymore. return *completed; } bool File::Write(const void* buffer, size_t buffer_len, size_t offset, FileIOCallback* callback, bool* completed) { DCHECK(init_); if (!callback) return Write(buffer, buffer_len, offset); return AsyncWrite(buffer, buffer_len, offset, true, callback, completed); } bool File::PostWrite(const void* buffer, size_t buffer_len, size_t offset) { DCHECK(init_); return AsyncWrite(buffer, buffer_len, offset, false, NULL, NULL); } bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset, bool notify, FileIOCallback* callback, bool* completed) { DCHECK(init_); if (buffer_len > ULONG_MAX || offset > ULONG_MAX) return false; MyOverlapped* data = new MyOverlapped(this, offset, callback); bool dummy_completed; if (!callback) { DCHECK(!notify); data->delete_buffer_ = true; data->buffer_ = buffer; completed = &dummy_completed; } DWORD size = static_cast(buffer_len); DWORD actual; if (!WriteFile(platform_file_, buffer, size, &actual, data->overlapped())) { *completed = false; if (GetLastError() == ERROR_IO_PENDING) return true; delete data; return false; } // The operation completed already. We'll be called back anyway. *completed = (actual == size); DCHECK(actual == size); data->callback_ = NULL; data->file_ = NULL; // There is no reason to hold on to this anymore. return *completed; } bool File::SetLength(size_t length) { DCHECK(init_); if (length > ULONG_MAX) return false; DWORD size = static_cast(length); HANDLE file = platform_file(); if (INVALID_SET_FILE_POINTER == SetFilePointer(file, size, NULL, FILE_BEGIN)) return false; return TRUE == SetEndOfFile(file); } size_t File::GetLength() { DCHECK(init_); LARGE_INTEGER size; HANDLE file = platform_file(); if (!GetFileSizeEx(file, &size)) return 0; if (size.HighPart) return ULONG_MAX; return static_cast(size.LowPart); } } // namespace disk_cache