• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 // For 64-bit file access (off_t = off64_t, lseek64, etc).
6 #define _FILE_OFFSET_BITS 64
7 
8 #include "net/base/file_stream.h"
9 
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <errno.h>
15 
16 #include "base/basictypes.h"
17 #include "base/callback.h"
18 #include "base/eintr_wrapper.h"
19 #include "base/file_path.h"
20 #include "base/logging.h"
21 #include "base/message_loop.h"
22 #include "base/metrics/histogram.h"
23 #include "base/string_util.h"
24 #include "base/task.h"
25 #include "base/threading/thread_restrictions.h"
26 #include "base/threading/worker_pool.h"
27 #include "base/synchronization/waitable_event.h"
28 #include "net/base/net_errors.h"
29 
30 namespace net {
31 
32 // We cast back and forth, so make sure it's the size we're expecting.
33 #if defined(__BIONIC__) && defined(ANDROID)
34 COMPILE_ASSERT(sizeof(int32) == sizeof(off_t), off_t_32_bit);
35 #else
36 COMPILE_ASSERT(sizeof(int64) == sizeof(off_t), off_t_64_bit);
37 #endif
38 
39 // Make sure our Whence mappings match the system headers.
40 COMPILE_ASSERT(FROM_BEGIN   == SEEK_SET &&
41                FROM_CURRENT == SEEK_CUR &&
42                FROM_END     == SEEK_END, whence_matches_system);
43 
44 namespace {
45 
46 // Map from errno to net error codes.
MapErrorCode(int err)47 int64 MapErrorCode(int err) {
48   switch (err) {
49     case ENOENT:
50       return ERR_FILE_NOT_FOUND;
51     case EACCES:
52       return ERR_ACCESS_DENIED;
53     default:
54       LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
55       return ERR_FAILED;
56   }
57 }
58 
59 // ReadFile() is a simple wrapper around read() that handles EINTR signals and
60 // calls MapErrorCode() to map errno to net error codes.
ReadFile(base::PlatformFile file,char * buf,int buf_len)61 int ReadFile(base::PlatformFile file, char* buf, int buf_len) {
62   base::ThreadRestrictions::AssertIOAllowed();
63   // read(..., 0) returns 0 to indicate end-of-file.
64 
65   // Loop in the case of getting interrupted by a signal.
66   ssize_t res = HANDLE_EINTR(read(file, buf, static_cast<size_t>(buf_len)));
67   if (res == static_cast<ssize_t>(-1))
68     return MapErrorCode(errno);
69   return static_cast<int>(res);
70 }
71 
ReadFileTask(base::PlatformFile file,char * buf,int buf_len,CompletionCallback * callback)72 void ReadFileTask(base::PlatformFile file,
73                   char* buf,
74                   int buf_len,
75                   CompletionCallback* callback) {
76   callback->Run(ReadFile(file, buf, buf_len));
77 }
78 
79 // WriteFile() is a simple wrapper around write() that handles EINTR signals and
80 // calls MapErrorCode() to map errno to net error codes.  It tries to write to
81 // completion.
WriteFile(base::PlatformFile file,const char * buf,int buf_len)82 int WriteFile(base::PlatformFile file, const char* buf, int buf_len) {
83   base::ThreadRestrictions::AssertIOAllowed();
84   ssize_t res = HANDLE_EINTR(write(file, buf, buf_len));
85   if (res == -1)
86     return MapErrorCode(errno);
87   return res;
88 }
89 
WriteFileTask(base::PlatformFile file,const char * buf,int buf_len,CompletionCallback * callback)90 void WriteFileTask(base::PlatformFile file,
91                    const char* buf,
92                    int buf_len,
93                    CompletionCallback* callback) {
94   callback->Run(WriteFile(file, buf, buf_len));
95 }
96 
97 // FlushFile() is a simple wrapper around fsync() that handles EINTR signals and
98 // calls MapErrorCode() to map errno to net error codes.  It tries to flush to
99 // completion.
FlushFile(base::PlatformFile file)100 int FlushFile(base::PlatformFile file) {
101   base::ThreadRestrictions::AssertIOAllowed();
102   ssize_t res = HANDLE_EINTR(fsync(file));
103   if (res == -1)
104     return MapErrorCode(errno);
105   return res;
106 }
107 
108 }  // namespace
109 
110 // CancelableCallbackTask takes ownership of the Callback.  This task gets
111 // posted to the MessageLoopForIO instance.
112 class CancelableCallbackTask : public CancelableTask {
113  public:
CancelableCallbackTask(Callback0::Type * callback)114   explicit CancelableCallbackTask(Callback0::Type* callback)
115       : canceled_(false), callback_(callback) {}
116 
Run()117   virtual void Run() {
118     if (!canceled_)
119       callback_->Run();
120   }
121 
Cancel()122   virtual void Cancel() {
123     canceled_ = true;
124   }
125 
126  private:
127   bool canceled_;
128   scoped_ptr<Callback0::Type> callback_;
129 };
130 
131 // FileStream::AsyncContext ----------------------------------------------
132 
133 class FileStream::AsyncContext {
134  public:
135   AsyncContext();
136   ~AsyncContext();
137 
138   // These methods post synchronous read() and write() calls to a WorkerThread.
139   void InitiateAsyncRead(
140       base::PlatformFile file, char* buf, int buf_len,
141       CompletionCallback* callback);
142   void InitiateAsyncWrite(
143       base::PlatformFile file, const char* buf, int buf_len,
144       CompletionCallback* callback);
145 
callback() const146   CompletionCallback* callback() const { return callback_; }
147 
148   // Called by the WorkerPool thread executing the IO after the IO completes.
149   // This method queues RunAsynchronousCallback() on the MessageLoop and signals
150   // |background_io_completed_callback_|, in case the destructor is waiting.  In
151   // that case, the destructor will call RunAsynchronousCallback() instead, and
152   // cancel |message_loop_task_|.
153   // |result| is the result of the Read/Write task.
154   void OnBackgroundIOCompleted(int result);
155 
156  private:
157   // Always called on the IO thread, either directly by a task on the
158   // MessageLoop or by ~AsyncContext().
159   void RunAsynchronousCallback();
160 
161   // The MessageLoopForIO that this AsyncContext is running on.
162   MessageLoopForIO* const message_loop_;
163   CompletionCallback* callback_;  // The user provided callback.
164 
165   // A callback wrapper around OnBackgroundIOCompleted().  Run by the WorkerPool
166   // thread doing the background IO on our behalf.
167   CompletionCallbackImpl<AsyncContext> background_io_completed_callback_;
168 
169   // This is used to synchronize between the AsyncContext destructor (which runs
170   // on the IO thread and OnBackgroundIOCompleted() which runs on the WorkerPool
171   // thread.
172   base::WaitableEvent background_io_completed_;
173 
174   // These variables are only valid when background_io_completed is signaled.
175   int result_;
176   CancelableCallbackTask* message_loop_task_;
177 
178   bool is_closing_;
179 
180   DISALLOW_COPY_AND_ASSIGN(AsyncContext);
181 };
182 
AsyncContext()183 FileStream::AsyncContext::AsyncContext()
184     : message_loop_(MessageLoopForIO::current()),
185       callback_(NULL),
186       background_io_completed_callback_(
187           this, &AsyncContext::OnBackgroundIOCompleted),
188       background_io_completed_(true, false),
189       message_loop_task_(NULL),
190       is_closing_(false) {}
191 
~AsyncContext()192 FileStream::AsyncContext::~AsyncContext() {
193   is_closing_ = true;
194   if (callback_) {
195     // If |callback_| is non-NULL, that implies either the worker thread is
196     // still running the IO task, or the completion callback is queued up on the
197     // MessageLoopForIO, but AsyncContext() got deleted before then.
198     const bool need_to_wait = !background_io_completed_.IsSignaled();
199     base::TimeTicks start = base::TimeTicks::Now();
200     RunAsynchronousCallback();
201     if (need_to_wait) {
202       // We want to see if we block the message loop for too long.
203       UMA_HISTOGRAM_TIMES("AsyncIO.FileStreamClose",
204                           base::TimeTicks::Now() - start);
205     }
206   }
207 }
208 
InitiateAsyncRead(base::PlatformFile file,char * buf,int buf_len,CompletionCallback * callback)209 void FileStream::AsyncContext::InitiateAsyncRead(
210     base::PlatformFile file, char* buf, int buf_len,
211     CompletionCallback* callback) {
212   DCHECK(!callback_);
213   callback_ = callback;
214 
215   base::WorkerPool::PostTask(FROM_HERE,
216                              NewRunnableFunction(
217                                  &ReadFileTask,
218                                  file, buf, buf_len,
219                                  &background_io_completed_callback_),
220                              true /* task_is_slow */);
221 }
222 
InitiateAsyncWrite(base::PlatformFile file,const char * buf,int buf_len,CompletionCallback * callback)223 void FileStream::AsyncContext::InitiateAsyncWrite(
224     base::PlatformFile file, const char* buf, int buf_len,
225     CompletionCallback* callback) {
226   DCHECK(!callback_);
227   callback_ = callback;
228 
229   base::WorkerPool::PostTask(FROM_HERE,
230                              NewRunnableFunction(
231                                  &WriteFileTask,
232                                  file, buf, buf_len,
233                                  &background_io_completed_callback_),
234                              true /* task_is_slow */);
235 }
236 
OnBackgroundIOCompleted(int result)237 void FileStream::AsyncContext::OnBackgroundIOCompleted(int result) {
238   result_ = result;
239   message_loop_task_ = new CancelableCallbackTask(
240       NewCallback(this, &AsyncContext::RunAsynchronousCallback));
241   message_loop_->PostTask(FROM_HERE, message_loop_task_);
242   background_io_completed_.Signal();
243 }
244 
RunAsynchronousCallback()245 void FileStream::AsyncContext::RunAsynchronousCallback() {
246   // Wait() here ensures that all modifications from the WorkerPool thread are
247   // now visible.
248   background_io_completed_.Wait();
249 
250   // Either we're in the MessageLoop's task, in which case Cancel() doesn't do
251   // anything, or we're in ~AsyncContext(), in which case this prevents the call
252   // from happening again.  Must do it here after calling Wait().
253   message_loop_task_->Cancel();
254   message_loop_task_ = NULL;
255 
256   if (is_closing_) {
257     callback_ = NULL;
258     return;
259   }
260 
261   DCHECK(callback_);
262   CompletionCallback* temp = NULL;
263   std::swap(temp, callback_);
264   background_io_completed_.Reset();
265   temp->Run(result_);
266 }
267 
268 // FileStream ------------------------------------------------------------
269 
FileStream()270 FileStream::FileStream()
271     : file_(base::kInvalidPlatformFileValue),
272       open_flags_(0),
273       auto_closed_(true) {
274   DCHECK(!IsOpen());
275 }
276 
FileStream(base::PlatformFile file,int flags)277 FileStream::FileStream(base::PlatformFile file, int flags)
278     : file_(file),
279       open_flags_(flags),
280       auto_closed_(false) {
281   // If the file handle is opened with base::PLATFORM_FILE_ASYNC, we need to
282   // make sure we will perform asynchronous File IO to it.
283   if (flags & base::PLATFORM_FILE_ASYNC) {
284     async_context_.reset(new AsyncContext());
285   }
286 }
287 
~FileStream()288 FileStream::~FileStream() {
289   if (auto_closed_)
290     Close();
291 }
292 
Close()293 void FileStream::Close() {
294   // Abort any existing asynchronous operations.
295   async_context_.reset();
296 
297   if (file_ != base::kInvalidPlatformFileValue) {
298     if (close(file_) != 0) {
299       NOTREACHED();
300     }
301     file_ = base::kInvalidPlatformFileValue;
302   }
303 }
304 
Open(const FilePath & path,int open_flags)305 int FileStream::Open(const FilePath& path, int open_flags) {
306   if (IsOpen()) {
307     DLOG(FATAL) << "File is already open!";
308     return ERR_UNEXPECTED;
309   }
310 
311   open_flags_ = open_flags;
312   file_ = base::CreatePlatformFile(path, open_flags_, NULL, NULL);
313   if (file_ == base::kInvalidPlatformFileValue) {
314     return MapErrorCode(errno);
315   }
316 
317   if (open_flags_ & base::PLATFORM_FILE_ASYNC) {
318     async_context_.reset(new AsyncContext());
319   }
320 
321   return OK;
322 }
323 
IsOpen() const324 bool FileStream::IsOpen() const {
325   return file_ != base::kInvalidPlatformFileValue;
326 }
327 
Seek(Whence whence,int64 offset)328 int64 FileStream::Seek(Whence whence, int64 offset) {
329   base::ThreadRestrictions::AssertIOAllowed();
330 
331   if (!IsOpen())
332     return ERR_UNEXPECTED;
333 
334   // If we're in async, make sure we don't have a request in flight.
335   DCHECK(!async_context_.get() || !async_context_->callback());
336 
337   off_t res = lseek(file_, static_cast<off_t>(offset),
338                     static_cast<int>(whence));
339   if (res == static_cast<off_t>(-1))
340     return MapErrorCode(errno);
341 
342   return res;
343 }
344 
Available()345 int64 FileStream::Available() {
346   base::ThreadRestrictions::AssertIOAllowed();
347 
348   if (!IsOpen())
349     return ERR_UNEXPECTED;
350 
351   int64 cur_pos = Seek(FROM_CURRENT, 0);
352   if (cur_pos < 0)
353     return cur_pos;
354 
355   struct stat info;
356   if (fstat(file_, &info) != 0)
357     return MapErrorCode(errno);
358 
359   int64 size = static_cast<int64>(info.st_size);
360   DCHECK_GT(size, cur_pos);
361 
362   return size - cur_pos;
363 }
364 
Read(char * buf,int buf_len,CompletionCallback * callback)365 int FileStream::Read(
366     char* buf, int buf_len, CompletionCallback* callback) {
367   if (!IsOpen())
368     return ERR_UNEXPECTED;
369 
370   // read(..., 0) will return 0, which indicates end-of-file.
371   DCHECK(buf_len > 0);
372   DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
373 
374   if (async_context_.get()) {
375     DCHECK(open_flags_ & base::PLATFORM_FILE_ASYNC);
376     // If we're in async, make sure we don't have a request in flight.
377     DCHECK(!async_context_->callback());
378     async_context_->InitiateAsyncRead(file_, buf, buf_len, callback);
379     return ERR_IO_PENDING;
380   } else {
381     return ReadFile(file_, buf, buf_len);
382   }
383 }
384 
ReadUntilComplete(char * buf,int buf_len)385 int FileStream::ReadUntilComplete(char *buf, int buf_len) {
386   int to_read = buf_len;
387   int bytes_total = 0;
388 
389   do {
390     int bytes_read = Read(buf, to_read, NULL);
391     if (bytes_read <= 0) {
392       if (bytes_total == 0)
393         return bytes_read;
394 
395       return bytes_total;
396     }
397 
398     bytes_total += bytes_read;
399     buf += bytes_read;
400     to_read -= bytes_read;
401   } while (bytes_total < buf_len);
402 
403   return bytes_total;
404 }
405 
Write(const char * buf,int buf_len,CompletionCallback * callback)406 int FileStream::Write(
407     const char* buf, int buf_len, CompletionCallback* callback) {
408   // write(..., 0) will return 0, which indicates end-of-file.
409   DCHECK_GT(buf_len, 0);
410 
411   if (!IsOpen())
412     return ERR_UNEXPECTED;
413 
414   if (async_context_.get()) {
415     DCHECK(open_flags_ & base::PLATFORM_FILE_ASYNC);
416     // If we're in async, make sure we don't have a request in flight.
417     DCHECK(!async_context_->callback());
418     async_context_->InitiateAsyncWrite(file_, buf, buf_len, callback);
419     return ERR_IO_PENDING;
420   } else {
421     return WriteFile(file_, buf, buf_len);
422   }
423 }
424 
Truncate(int64 bytes)425 int64 FileStream::Truncate(int64 bytes) {
426   base::ThreadRestrictions::AssertIOAllowed();
427 
428   if (!IsOpen())
429     return ERR_UNEXPECTED;
430 
431   // We better be open for reading.
432   DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
433 
434   // Seek to the position to truncate from.
435   int64 seek_position = Seek(FROM_BEGIN, bytes);
436   if (seek_position != bytes)
437     return ERR_UNEXPECTED;
438 
439   // And truncate the file.
440   int result = ftruncate(file_, bytes);
441   return result == 0 ? seek_position : MapErrorCode(errno);
442 }
443 
Flush()444 int FileStream::Flush() {
445   if (!IsOpen())
446     return ERR_UNEXPECTED;
447 
448   return FlushFile(file_);
449 }
450 
451 }  // namespace net
452