• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/base/file_stream_context.h"
6 
7 #include <windows.h>
8 
9 #include "base/files/file_path.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/task_runner.h"
13 #include "net/base/io_buffer.h"
14 #include "net/base/net_errors.h"
15 
16 namespace net {
17 
18 // Ensure that we can just use our Whence values directly.
19 COMPILE_ASSERT(FROM_BEGIN == FILE_BEGIN, bad_whence_begin);
20 COMPILE_ASSERT(FROM_CURRENT == FILE_CURRENT, bad_whence_current);
21 COMPILE_ASSERT(FROM_END == FILE_END, bad_whence_end);
22 
23 namespace {
24 
SetOffset(OVERLAPPED * overlapped,const LARGE_INTEGER & offset)25 void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) {
26   overlapped->Offset = offset.LowPart;
27   overlapped->OffsetHigh = offset.HighPart;
28 }
29 
IncrementOffset(OVERLAPPED * overlapped,DWORD count)30 void IncrementOffset(OVERLAPPED* overlapped, DWORD count) {
31   LARGE_INTEGER offset;
32   offset.LowPart = overlapped->Offset;
33   offset.HighPart = overlapped->OffsetHigh;
34   offset.QuadPart += static_cast<LONGLONG>(count);
35   SetOffset(overlapped, offset);
36 }
37 
38 }  // namespace
39 
Context(const scoped_refptr<base::TaskRunner> & task_runner)40 FileStream::Context::Context(const scoped_refptr<base::TaskRunner>& task_runner)
41     : io_context_(),
42       async_in_progress_(false),
43       orphaned_(false),
44       task_runner_(task_runner) {
45   io_context_.handler = this;
46   memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped));
47 }
48 
Context(base::File file,const scoped_refptr<base::TaskRunner> & task_runner)49 FileStream::Context::Context(base::File file,
50                              const scoped_refptr<base::TaskRunner>& task_runner)
51     : io_context_(),
52       file_(file.Pass()),
53       async_in_progress_(false),
54       orphaned_(false),
55       task_runner_(task_runner) {
56   io_context_.handler = this;
57   memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped));
58   if (file_.IsValid()) {
59     // TODO(hashimoto): Check that file_ is async.
60     OnAsyncFileOpened();
61   }
62 }
63 
~Context()64 FileStream::Context::~Context() {
65 }
66 
ReadAsync(IOBuffer * buf,int buf_len,const CompletionCallback & callback)67 int FileStream::Context::ReadAsync(IOBuffer* buf,
68                                    int buf_len,
69                                    const CompletionCallback& callback) {
70   DCHECK(!async_in_progress_);
71 
72   DWORD bytes_read;
73   if (!ReadFile(file_.GetPlatformFile(), buf->data(), buf_len,
74                 &bytes_read, &io_context_.overlapped)) {
75     IOResult error = IOResult::FromOSError(GetLastError());
76     if (error.os_error == ERROR_IO_PENDING) {
77       IOCompletionIsPending(callback, buf);
78     } else if (error.os_error == ERROR_HANDLE_EOF) {
79       return 0;  // Report EOF by returning 0 bytes read.
80     } else {
81       LOG(WARNING) << "ReadFile failed: " << error.os_error;
82     }
83     return error.result;
84   }
85 
86   IOCompletionIsPending(callback, buf);
87   return ERR_IO_PENDING;
88 }
89 
WriteAsync(IOBuffer * buf,int buf_len,const CompletionCallback & callback)90 int FileStream::Context::WriteAsync(IOBuffer* buf,
91                                     int buf_len,
92                                     const CompletionCallback& callback) {
93   DWORD bytes_written = 0;
94   if (!WriteFile(file_.GetPlatformFile(), buf->data(), buf_len,
95                  &bytes_written, &io_context_.overlapped)) {
96     IOResult error = IOResult::FromOSError(GetLastError());
97     if (error.os_error == ERROR_IO_PENDING) {
98       IOCompletionIsPending(callback, buf);
99     } else {
100       LOG(WARNING) << "WriteFile failed: " << error.os_error;
101     }
102     return error.result;
103   }
104 
105   IOCompletionIsPending(callback, buf);
106   return ERR_IO_PENDING;
107 }
108 
OnAsyncFileOpened()109 void FileStream::Context::OnAsyncFileOpened() {
110   base::MessageLoopForIO::current()->RegisterIOHandler(file_.GetPlatformFile(),
111                                                        this);
112 }
113 
SeekFileImpl(Whence whence,int64 offset)114 FileStream::Context::IOResult FileStream::Context::SeekFileImpl(Whence whence,
115                                                                 int64 offset) {
116   LARGE_INTEGER distance, res;
117   distance.QuadPart = offset;
118   DWORD move_method = static_cast<DWORD>(whence);
119   if (SetFilePointerEx(file_.GetPlatformFile(), distance, &res, move_method)) {
120     SetOffset(&io_context_.overlapped, res);
121     return IOResult(res.QuadPart, 0);
122   }
123 
124   return IOResult::FromOSError(GetLastError());
125 }
126 
FlushFileImpl()127 FileStream::Context::IOResult FileStream::Context::FlushFileImpl() {
128   if (FlushFileBuffers(file_.GetPlatformFile()))
129     return IOResult(OK, 0);
130 
131   return IOResult::FromOSError(GetLastError());
132 }
133 
IOCompletionIsPending(const CompletionCallback & callback,IOBuffer * buf)134 void FileStream::Context::IOCompletionIsPending(
135     const CompletionCallback& callback,
136     IOBuffer* buf) {
137   DCHECK(callback_.is_null());
138   callback_ = callback;
139   in_flight_buf_ = buf;  // Hold until the async operation ends.
140   async_in_progress_ = true;
141 }
142 
OnIOCompleted(base::MessageLoopForIO::IOContext * context,DWORD bytes_read,DWORD error)143 void FileStream::Context::OnIOCompleted(
144     base::MessageLoopForIO::IOContext* context,
145     DWORD bytes_read,
146     DWORD error) {
147   DCHECK_EQ(&io_context_, context);
148   DCHECK(!callback_.is_null());
149   DCHECK(async_in_progress_);
150 
151   async_in_progress_ = false;
152   if (orphaned_) {
153     callback_.Reset();
154     in_flight_buf_ = NULL;
155     CloseAndDelete();
156     return;
157   }
158 
159   int result;
160   if (error == ERROR_HANDLE_EOF) {
161     result = 0;
162   } else if (error) {
163     IOResult error_result = IOResult::FromOSError(error);
164     result = error_result.result;
165   } else {
166     result = bytes_read;
167     IncrementOffset(&io_context_.overlapped, bytes_read);
168   }
169 
170   CompletionCallback temp_callback = callback_;
171   callback_.Reset();
172   scoped_refptr<IOBuffer> temp_buf = in_flight_buf_;
173   in_flight_buf_ = NULL;
174   temp_callback.Run(result);
175 }
176 
177 }  // namespace net
178