• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2010 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.h"
6 
7 #include <windows.h>
8 
9 #include "base/file_path.h"
10 #include "base/logging.h"
11 #include "base/message_loop.h"
12 #include "base/metrics/histogram.h"
13 #include "base/threading/thread_restrictions.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 
SetOffset(OVERLAPPED * overlapped,const LARGE_INTEGER & offset)23 static void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) {
24   overlapped->Offset = offset.LowPart;
25   overlapped->OffsetHigh = offset.HighPart;
26 }
27 
IncrementOffset(OVERLAPPED * overlapped,DWORD count)28 static void IncrementOffset(OVERLAPPED* overlapped, DWORD count) {
29   LARGE_INTEGER offset;
30   offset.LowPart = overlapped->Offset;
31   offset.HighPart = overlapped->OffsetHigh;
32   offset.QuadPart += static_cast<LONGLONG>(count);
33   SetOffset(overlapped, offset);
34 }
35 
MapErrorCode(DWORD err)36 static int MapErrorCode(DWORD err) {
37   switch (err) {
38     case ERROR_FILE_NOT_FOUND:
39     case ERROR_PATH_NOT_FOUND:
40       return ERR_FILE_NOT_FOUND;
41     case ERROR_ACCESS_DENIED:
42       return ERR_ACCESS_DENIED;
43     case ERROR_SUCCESS:
44       return OK;
45     default:
46       LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
47       return ERR_FAILED;
48   }
49 }
50 
51 // FileStream::AsyncContext ----------------------------------------------
52 
53 class FileStream::AsyncContext : public MessageLoopForIO::IOHandler {
54  public:
AsyncContext(FileStream * owner)55   AsyncContext(FileStream* owner)
56       : owner_(owner), context_(), callback_(NULL), is_closing_(false) {
57     context_.handler = this;
58   }
59   ~AsyncContext();
60 
61   void IOCompletionIsPending(CompletionCallback* callback);
62 
overlapped()63   OVERLAPPED* overlapped() { return &context_.overlapped; }
callback() const64   CompletionCallback* callback() const { return callback_; }
65 
66  private:
67   virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
68                              DWORD bytes_read, DWORD error);
69 
70   FileStream* owner_;
71   MessageLoopForIO::IOContext context_;
72   CompletionCallback* callback_;
73   bool is_closing_;
74 };
75 
~AsyncContext()76 FileStream::AsyncContext::~AsyncContext() {
77   is_closing_ = true;
78   bool waited = false;
79   base::TimeTicks start = base::TimeTicks::Now();
80   while (callback_) {
81     waited = true;
82     MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
83   }
84   if (waited) {
85     // We want to see if we block the message loop for too long.
86     UMA_HISTOGRAM_TIMES("AsyncIO.FileStreamClose",
87                         base::TimeTicks::Now() - start);
88   }
89 }
90 
IOCompletionIsPending(CompletionCallback * callback)91 void FileStream::AsyncContext::IOCompletionIsPending(
92     CompletionCallback* callback) {
93   DCHECK(!callback_);
94   callback_ = callback;
95 }
96 
OnIOCompleted(MessageLoopForIO::IOContext * context,DWORD bytes_read,DWORD error)97 void FileStream::AsyncContext::OnIOCompleted(
98     MessageLoopForIO::IOContext* context, DWORD bytes_read, DWORD error) {
99   DCHECK(&context_ == context);
100   DCHECK(callback_);
101 
102   if (is_closing_) {
103     callback_ = NULL;
104     return;
105   }
106 
107   int result = static_cast<int>(bytes_read);
108   if (error && error != ERROR_HANDLE_EOF)
109     result = MapErrorCode(error);
110 
111   if (bytes_read)
112     IncrementOffset(&context->overlapped, bytes_read);
113 
114   CompletionCallback* temp = NULL;
115   std::swap(temp, callback_);
116   temp->Run(result);
117 }
118 
119 // FileStream ------------------------------------------------------------
120 
FileStream()121 FileStream::FileStream()
122     : file_(INVALID_HANDLE_VALUE),
123       open_flags_(0),
124       auto_closed_(true) {
125 }
126 
FileStream(base::PlatformFile file,int flags)127 FileStream::FileStream(base::PlatformFile file, int flags)
128     : file_(file),
129       open_flags_(flags),
130       auto_closed_(false) {
131   // If the file handle is opened with base::PLATFORM_FILE_ASYNC, we need to
132   // make sure we will perform asynchronous File IO to it.
133   if (flags & base::PLATFORM_FILE_ASYNC) {
134     async_context_.reset(new AsyncContext(this));
135     MessageLoopForIO::current()->RegisterIOHandler(file_,
136                                                    async_context_.get());
137   }
138 }
139 
~FileStream()140 FileStream::~FileStream() {
141   if (auto_closed_)
142     Close();
143 }
144 
Close()145 void FileStream::Close() {
146   if (file_ != INVALID_HANDLE_VALUE)
147     CancelIo(file_);
148 
149   async_context_.reset();
150   if (file_ != INVALID_HANDLE_VALUE) {
151     CloseHandle(file_);
152     file_ = INVALID_HANDLE_VALUE;
153   }
154 }
155 
Open(const FilePath & path,int open_flags)156 int FileStream::Open(const FilePath& path, int open_flags) {
157   if (IsOpen()) {
158     DLOG(FATAL) << "File is already open!";
159     return ERR_UNEXPECTED;
160   }
161 
162   open_flags_ = open_flags;
163   file_ = base::CreatePlatformFile(path, open_flags_, NULL, NULL);
164   if (file_ == INVALID_HANDLE_VALUE) {
165     DWORD error = GetLastError();
166     LOG(WARNING) << "Failed to open file: " << error;
167     return MapErrorCode(error);
168   }
169 
170   if (open_flags_ & base::PLATFORM_FILE_ASYNC) {
171     async_context_.reset(new AsyncContext(this));
172     MessageLoopForIO::current()->RegisterIOHandler(file_,
173                                                    async_context_.get());
174   }
175 
176   return OK;
177 }
178 
IsOpen() const179 bool FileStream::IsOpen() const {
180   return file_ != INVALID_HANDLE_VALUE;
181 }
182 
Seek(Whence whence,int64 offset)183 int64 FileStream::Seek(Whence whence, int64 offset) {
184   if (!IsOpen())
185     return ERR_UNEXPECTED;
186   DCHECK(!async_context_.get() || !async_context_->callback());
187 
188   LARGE_INTEGER distance, result;
189   distance.QuadPart = offset;
190   DWORD move_method = static_cast<DWORD>(whence);
191   if (!SetFilePointerEx(file_, distance, &result, move_method)) {
192     DWORD error = GetLastError();
193     LOG(WARNING) << "SetFilePointerEx failed: " << error;
194     return MapErrorCode(error);
195   }
196   if (async_context_.get())
197     SetOffset(async_context_->overlapped(), result);
198   return result.QuadPart;
199 }
200 
Available()201 int64 FileStream::Available() {
202   base::ThreadRestrictions::AssertIOAllowed();
203 
204   if (!IsOpen())
205     return ERR_UNEXPECTED;
206 
207   int64 cur_pos = Seek(FROM_CURRENT, 0);
208   if (cur_pos < 0)
209     return cur_pos;
210 
211   LARGE_INTEGER file_size;
212   if (!GetFileSizeEx(file_, &file_size)) {
213     DWORD error = GetLastError();
214     LOG(WARNING) << "GetFileSizeEx failed: " << error;
215     return MapErrorCode(error);
216   }
217 
218   return file_size.QuadPart - cur_pos;
219 }
220 
Read(char * buf,int buf_len,CompletionCallback * callback)221 int FileStream::Read(
222     char* buf, int buf_len, CompletionCallback* callback) {
223   if (!IsOpen())
224     return ERR_UNEXPECTED;
225   DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
226 
227   OVERLAPPED* overlapped = NULL;
228   if (async_context_.get()) {
229     DCHECK(callback);
230     DCHECK(!async_context_->callback());
231     overlapped = async_context_->overlapped();
232   } else {
233     DCHECK(!callback);
234     base::ThreadRestrictions::AssertIOAllowed();
235   }
236 
237   int rv;
238 
239   DWORD bytes_read;
240   if (!ReadFile(file_, buf, buf_len, &bytes_read, overlapped)) {
241     DWORD error = GetLastError();
242     if (async_context_.get() && error == ERROR_IO_PENDING) {
243       async_context_->IOCompletionIsPending(callback);
244       rv = ERR_IO_PENDING;
245     } else if (error == ERROR_HANDLE_EOF) {
246       rv = 0;  // Report EOF by returning 0 bytes read.
247     } else {
248       LOG(WARNING) << "ReadFile failed: " << error;
249       rv = MapErrorCode(error);
250     }
251   } else if (overlapped) {
252     async_context_->IOCompletionIsPending(callback);
253     rv = ERR_IO_PENDING;
254   } else {
255     rv = static_cast<int>(bytes_read);
256   }
257   return rv;
258 }
259 
ReadUntilComplete(char * buf,int buf_len)260 int FileStream::ReadUntilComplete(char *buf, int buf_len) {
261   int to_read = buf_len;
262   int bytes_total = 0;
263 
264   do {
265     int bytes_read = Read(buf, to_read, NULL);
266     if (bytes_read <= 0) {
267       if (bytes_total == 0)
268         return bytes_read;
269 
270       return bytes_total;
271     }
272 
273     bytes_total += bytes_read;
274     buf += bytes_read;
275     to_read -= bytes_read;
276   } while (bytes_total < buf_len);
277 
278   return bytes_total;
279 }
280 
Write(const char * buf,int buf_len,CompletionCallback * callback)281 int FileStream::Write(
282     const char* buf, int buf_len, CompletionCallback* callback) {
283   if (!IsOpen())
284     return ERR_UNEXPECTED;
285   DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
286 
287   OVERLAPPED* overlapped = NULL;
288   if (async_context_.get()) {
289     DCHECK(callback);
290     DCHECK(!async_context_->callback());
291     overlapped = async_context_->overlapped();
292   } else {
293     DCHECK(!callback);
294     base::ThreadRestrictions::AssertIOAllowed();
295   }
296 
297   int rv;
298   DWORD bytes_written;
299   if (!WriteFile(file_, buf, buf_len, &bytes_written, overlapped)) {
300     DWORD error = GetLastError();
301     if (async_context_.get() && error == ERROR_IO_PENDING) {
302       async_context_->IOCompletionIsPending(callback);
303       rv = ERR_IO_PENDING;
304     } else {
305       LOG(WARNING) << "WriteFile failed: " << error;
306       rv = MapErrorCode(error);
307     }
308   } else if (overlapped) {
309     async_context_->IOCompletionIsPending(callback);
310     rv = ERR_IO_PENDING;
311   } else {
312     rv = static_cast<int>(bytes_written);
313   }
314   return rv;
315 }
316 
Flush()317 int FileStream::Flush() {
318   base::ThreadRestrictions::AssertIOAllowed();
319 
320   if (!IsOpen())
321     return ERR_UNEXPECTED;
322 
323   DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
324   if (FlushFileBuffers(file_)) {
325     return OK;
326   }
327 
328   int rv;
329   DWORD error = GetLastError();
330   rv = MapErrorCode(error);
331   return rv;
332 }
333 
Truncate(int64 bytes)334 int64 FileStream::Truncate(int64 bytes) {
335   base::ThreadRestrictions::AssertIOAllowed();
336 
337   if (!IsOpen())
338     return ERR_UNEXPECTED;
339 
340   // We better be open for reading.
341   DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
342 
343   // Seek to the position to truncate from.
344   int64 seek_position = Seek(FROM_BEGIN, bytes);
345   if (seek_position != bytes)
346     return ERR_UNEXPECTED;
347 
348   // And truncate the file.
349   BOOL result = SetEndOfFile(file_);
350   if (!result) {
351     DWORD error = GetLastError();
352     LOG(WARNING) << "SetEndOfFile failed: " << error;
353     return MapErrorCode(error);
354   }
355 
356   // Success.
357   return seek_position;
358 }
359 
360 }  // namespace net
361