• 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 "base/files/file_path.h"
8 #include "base/location.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/task_runner.h"
11 #include "base/task_runner_util.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "base/values.h"
14 #include "net/base/net_errors.h"
15 
16 #if defined(OS_ANDROID)
17 #include "base/android/content_uri_utils.h"
18 #endif
19 
20 namespace net {
21 
22 namespace {
23 
CallInt64ToInt(const CompletionCallback & callback,int64 result)24 void CallInt64ToInt(const CompletionCallback& callback, int64 result) {
25   callback.Run(static_cast<int>(result));
26 }
27 
28 }  // namespace
29 
IOResult()30 FileStream::Context::IOResult::IOResult()
31     : result(OK),
32       os_error(0) {
33 }
34 
IOResult(int64 result,int os_error)35 FileStream::Context::IOResult::IOResult(int64 result, int os_error)
36     : result(result),
37       os_error(os_error) {
38 }
39 
40 // static
FromOSError(int64 os_error)41 FileStream::Context::IOResult FileStream::Context::IOResult::FromOSError(
42     int64 os_error) {
43   return IOResult(MapSystemError(os_error), os_error);
44 }
45 
46 // ---------------------------------------------------------------------
47 
OpenResult()48 FileStream::Context::OpenResult::OpenResult() {
49 }
50 
OpenResult(base::File file,IOResult error_code)51 FileStream::Context::OpenResult::OpenResult(base::File file,
52                                             IOResult error_code)
53     : file(file.Pass()),
54       error_code(error_code) {
55 }
56 
OpenResult(RValue other)57 FileStream::Context::OpenResult::OpenResult(RValue other)
58     : file(other.object->file.Pass()),
59       error_code(other.object->error_code) {
60 }
61 
operator =(RValue other)62 FileStream::Context::OpenResult& FileStream::Context::OpenResult::operator=(
63     RValue other) {
64   if (this != other.object) {
65     file = other.object->file.Pass();
66     error_code = other.object->error_code;
67   }
68   return *this;
69 }
70 
71 // ---------------------------------------------------------------------
72 
Orphan()73 void FileStream::Context::Orphan() {
74   DCHECK(!orphaned_);
75 
76   orphaned_ = true;
77 
78   if (!async_in_progress_) {
79     CloseAndDelete();
80   } else if (file_.IsValid()) {
81     CancelIo(file_.GetPlatformFile());
82   }
83 }
84 
OpenAsync(const base::FilePath & path,int open_flags,const CompletionCallback & callback)85 void FileStream::Context::OpenAsync(const base::FilePath& path,
86                                     int open_flags,
87                                     const CompletionCallback& callback) {
88   DCHECK(!async_in_progress_);
89 
90   bool posted = base::PostTaskAndReplyWithResult(
91       task_runner_.get(),
92       FROM_HERE,
93       base::Bind(
94           &Context::OpenFileImpl, base::Unretained(this), path, open_flags),
95       base::Bind(&Context::OnOpenCompleted, base::Unretained(this), callback));
96   DCHECK(posted);
97 
98   async_in_progress_ = true;
99 }
100 
CloseAsync(const CompletionCallback & callback)101 void FileStream::Context::CloseAsync(const CompletionCallback& callback) {
102   DCHECK(!async_in_progress_);
103   bool posted = base::PostTaskAndReplyWithResult(
104       task_runner_.get(),
105       FROM_HERE,
106       base::Bind(&Context::CloseFileImpl, base::Unretained(this)),
107       base::Bind(&Context::OnAsyncCompleted,
108                  base::Unretained(this),
109                  IntToInt64(callback)));
110   DCHECK(posted);
111 
112   async_in_progress_ = true;
113 }
114 
SeekAsync(Whence whence,int64 offset,const Int64CompletionCallback & callback)115 void FileStream::Context::SeekAsync(Whence whence,
116                                     int64 offset,
117                                     const Int64CompletionCallback& callback) {
118   DCHECK(!async_in_progress_);
119 
120   bool posted = base::PostTaskAndReplyWithResult(
121       task_runner_.get(),
122       FROM_HERE,
123       base::Bind(
124           &Context::SeekFileImpl, base::Unretained(this), whence, offset),
125       base::Bind(&Context::OnAsyncCompleted,
126                  base::Unretained(this),
127                  callback));
128   DCHECK(posted);
129 
130   async_in_progress_ = true;
131 }
132 
FlushAsync(const CompletionCallback & callback)133 void FileStream::Context::FlushAsync(const CompletionCallback& callback) {
134   DCHECK(!async_in_progress_);
135 
136   bool posted = base::PostTaskAndReplyWithResult(
137       task_runner_.get(),
138       FROM_HERE,
139       base::Bind(&Context::FlushFileImpl, base::Unretained(this)),
140       base::Bind(&Context::OnAsyncCompleted,
141                  base::Unretained(this),
142                  IntToInt64(callback)));
143   DCHECK(posted);
144 
145   async_in_progress_ = true;
146 }
147 
OpenFileImpl(const base::FilePath & path,int open_flags)148 FileStream::Context::OpenResult FileStream::Context::OpenFileImpl(
149     const base::FilePath& path, int open_flags) {
150 #if defined(OS_POSIX)
151   // Always use blocking IO.
152   open_flags &= ~base::File::FLAG_ASYNC;
153 #endif
154   base::File file;
155 #if defined(OS_ANDROID)
156   if (path.IsContentUri()) {
157     // Check that only Read flags are set.
158     DCHECK_EQ(open_flags & ~base::File::FLAG_ASYNC,
159               base::File::FLAG_OPEN | base::File::FLAG_READ);
160     file = base::OpenContentUriForRead(path);
161   } else {
162 #endif  // defined(OS_ANDROID)
163     // FileStream::Context actually closes the file asynchronously,
164     // independently from FileStream's destructor. It can cause problems for
165     // users wanting to delete the file right after FileStream deletion. Thus
166     // we are always adding SHARE_DELETE flag to accommodate such use case.
167     // TODO(rvargas): This sounds like a bug, as deleting the file would
168     // presumably happen on the wrong thread. There should be an async delete.
169     open_flags |= base::File::FLAG_SHARE_DELETE;
170     file.Initialize(path, open_flags);
171 #if defined(OS_ANDROID)
172   }
173 #endif  // defined(OS_ANDROID)
174   if (!file.IsValid())
175     return OpenResult(base::File(), IOResult::FromOSError(GetLastErrno()));
176 
177   return OpenResult(file.Pass(), IOResult(OK, 0));
178 }
179 
CloseFileImpl()180 FileStream::Context::IOResult FileStream::Context::CloseFileImpl() {
181   file_.Close();
182   return IOResult(OK, 0);
183 }
184 
OnOpenCompleted(const CompletionCallback & callback,OpenResult open_result)185 void FileStream::Context::OnOpenCompleted(const CompletionCallback& callback,
186                                           OpenResult open_result) {
187   file_ = open_result.file.Pass();
188   if (file_.IsValid() && !orphaned_)
189     OnAsyncFileOpened();
190 
191   OnAsyncCompleted(IntToInt64(callback), open_result.error_code);
192 }
193 
CloseAndDelete()194 void FileStream::Context::CloseAndDelete() {
195   DCHECK(!async_in_progress_);
196 
197   if (file_.IsValid()) {
198     bool posted = task_runner_.get()->PostTask(
199         FROM_HERE,
200         base::Bind(base::IgnoreResult(&Context::CloseFileImpl),
201                    base::Owned(this)));
202     DCHECK(posted);
203   } else {
204     delete this;
205   }
206 }
207 
IntToInt64(const CompletionCallback & callback)208 Int64CompletionCallback FileStream::Context::IntToInt64(
209     const CompletionCallback& callback) {
210   return base::Bind(&CallInt64ToInt, callback);
211 }
212 
OnAsyncCompleted(const Int64CompletionCallback & callback,const IOResult & result)213 void FileStream::Context::OnAsyncCompleted(
214     const Int64CompletionCallback& callback,
215     const IOResult& result) {
216   // Reset this before Run() as Run() may issue a new async operation. Also it
217   // should be reset before CloseAsync() because it shouldn't run if any async
218   // operation is in progress.
219   async_in_progress_ = false;
220   if (orphaned_)
221     CloseAndDelete();
222   else
223     callback.Run(result.result);
224 }
225 
226 }  // namespace net
227