Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / net / base / file_stream_context.cc
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/location.h"
8 #include "base/message_loop/message_loop_proxy.h"
9 #include "base/task_runner_util.h"
10 #include "base/threading/thread_restrictions.h"
11 #include "net/base/file_stream_net_log_parameters.h"
12 #include "net/base/net_errors.h"
13
14 #if defined(OS_ANDROID)
15 #include "base/android/content_uri_utils.h"
16 #endif
17
18 namespace {
19
20 void CallInt64ToInt(const net::CompletionCallback& callback, int64 result) {
21   callback.Run(static_cast<int>(result));
22 }
23
24 }
25
26 namespace net {
27
28 FileStream::Context::IOResult::IOResult()
29     : result(OK),
30       os_error(0) {
31 }
32
33 FileStream::Context::IOResult::IOResult(int64 result, int os_error)
34     : result(result),
35       os_error(os_error) {
36 }
37
38 // static
39 FileStream::Context::IOResult FileStream::Context::IOResult::FromOSError(
40     int64 os_error) {
41   return IOResult(MapSystemError(os_error), os_error);
42 }
43
44 // ---------------------------------------------------------------------
45
46 FileStream::Context::OpenResult::OpenResult() {
47 }
48
49 FileStream::Context::OpenResult::OpenResult(base::File file,
50                                             IOResult error_code)
51     : file(file.Pass()),
52       error_code(error_code) {
53 }
54
55 FileStream::Context::OpenResult::OpenResult(RValue other)
56     : file(other.object->file.Pass()),
57       error_code(other.object->error_code) {
58 }
59
60 FileStream::Context::OpenResult& FileStream::Context::OpenResult::operator=(
61     RValue other) {
62   if (this != other.object) {
63     file = other.object->file.Pass();
64     error_code = other.object->error_code;
65   }
66   return *this;
67 }
68
69 // ---------------------------------------------------------------------
70
71 void FileStream::Context::Orphan() {
72   DCHECK(!orphaned_);
73
74   orphaned_ = true;
75   if (file_.IsValid())
76     bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN);
77
78   if (!async_in_progress_) {
79     CloseAndDelete();
80   } else if (file_.IsValid()) {
81     CancelIo(file_.GetPlatformFile());
82   }
83 }
84
85 void FileStream::Context::OpenAsync(const base::FilePath& path,
86                                     int open_flags,
87                                     const CompletionCallback& callback) {
88   DCHECK(!async_in_progress_);
89   BeginOpenEvent(path);
90
91   bool posted = base::PostTaskAndReplyWithResult(
92       task_runner_.get(),
93       FROM_HERE,
94       base::Bind(
95           &Context::OpenFileImpl, base::Unretained(this), path, open_flags),
96       base::Bind(&Context::OnOpenCompleted, base::Unretained(this), callback));
97   DCHECK(posted);
98
99   async_in_progress_ = true;
100
101   // TODO(rvargas): Figure out what to do here. For POSIX, async IO is
102   // implemented by doing blocking IO on another thread, so file_ is not really
103   // async, but this code has sync and async paths so it has random checks to
104   // figure out what mode to use. We should probably make this class async only,
105   // and make consumers of sync IO use base::File.
106   async_ = true;
107 }
108
109 int FileStream::Context::OpenSync(const base::FilePath& path, int open_flags) {
110   DCHECK(!async_in_progress_);
111
112   BeginOpenEvent(path);
113   OpenResult result = OpenFileImpl(path, open_flags);
114   if (result.file.IsValid()) {
115     file_ = result.file.Pass();
116     // TODO(satorux): Remove this once all async clients are migrated to use
117     // Open(). crbug.com/114783
118     if (open_flags & base::File::FLAG_ASYNC)
119       OnAsyncFileOpened();
120   } else {
121     ProcessOpenError(result.error_code);
122   }
123   return result.error_code.result;
124 }
125
126 void FileStream::Context::CloseSync() {
127   DCHECK(!async_in_progress_);
128   if (file_.IsValid()) {
129     file_.Close();
130     bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN);
131   }
132 }
133
134 void FileStream::Context::CloseAsync(const CompletionCallback& callback) {
135   DCHECK(!async_in_progress_);
136   bool posted = base::PostTaskAndReplyWithResult(
137       task_runner_.get(),
138       FROM_HERE,
139       base::Bind(&Context::CloseFileImpl, base::Unretained(this)),
140       base::Bind(&Context::ProcessAsyncResult,
141                  base::Unretained(this),
142                  IntToInt64(callback),
143                  FILE_ERROR_SOURCE_CLOSE));
144   DCHECK(posted);
145
146   async_in_progress_ = true;
147 }
148
149 void FileStream::Context::SeekAsync(Whence whence,
150                                     int64 offset,
151                                     const Int64CompletionCallback& callback) {
152   DCHECK(!async_in_progress_);
153
154   bool posted = base::PostTaskAndReplyWithResult(
155       task_runner_.get(),
156       FROM_HERE,
157       base::Bind(
158           &Context::SeekFileImpl, base::Unretained(this), whence, offset),
159       base::Bind(&Context::ProcessAsyncResult,
160                  base::Unretained(this),
161                  callback,
162                  FILE_ERROR_SOURCE_SEEK));
163   DCHECK(posted);
164
165   async_in_progress_ = true;
166 }
167
168 int64 FileStream::Context::SeekSync(Whence whence, int64 offset) {
169   IOResult result = SeekFileImpl(whence, offset);
170   RecordError(result, FILE_ERROR_SOURCE_SEEK);
171   return result.result;
172 }
173
174 void FileStream::Context::FlushAsync(const CompletionCallback& callback) {
175   DCHECK(!async_in_progress_);
176
177   bool posted = base::PostTaskAndReplyWithResult(
178       task_runner_.get(),
179       FROM_HERE,
180       base::Bind(&Context::FlushFileImpl, base::Unretained(this)),
181       base::Bind(&Context::ProcessAsyncResult,
182                  base::Unretained(this),
183                  IntToInt64(callback),
184                  FILE_ERROR_SOURCE_FLUSH));
185   DCHECK(posted);
186
187   async_in_progress_ = true;
188 }
189
190 int FileStream::Context::FlushSync() {
191   IOResult result = FlushFileImpl();
192   RecordError(result, FILE_ERROR_SOURCE_FLUSH);
193   return result.result;
194 }
195
196 void FileStream::Context::RecordError(const IOResult& result,
197                                       FileErrorSource source) const {
198   if (result.result >= 0) {
199     // |result| is not an error.
200     return;
201   }
202
203   if (!orphaned_) {
204     bound_net_log_.AddEvent(
205         NetLog::TYPE_FILE_STREAM_ERROR,
206         base::Bind(&NetLogFileStreamErrorCallback,
207                    source, result.os_error,
208                    static_cast<net::Error>(result.result)));
209   }
210
211   RecordFileError(result.os_error, source, record_uma_);
212 }
213
214 void FileStream::Context::BeginOpenEvent(const base::FilePath& path) {
215   std::string file_name = path.AsUTF8Unsafe();
216   bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_OPEN,
217                             NetLog::StringCallback("file_name", &file_name));
218 }
219
220 FileStream::Context::OpenResult FileStream::Context::OpenFileImpl(
221     const base::FilePath& path, int open_flags) {
222 #if defined(OS_POSIX)
223   // Always use blocking IO.
224   open_flags &= ~base::File::FLAG_ASYNC;
225 #endif
226   base::File file;
227 #if defined(OS_ANDROID)
228   if (path.IsContentUri()) {
229     // Check that only Read flags are set.
230     DCHECK_EQ(open_flags & ~base::File::FLAG_ASYNC,
231               base::File::FLAG_OPEN | base::File::FLAG_READ);
232     file = base::OpenContentUriForRead(path);
233   } else {
234 #endif  // defined(OS_ANDROID)
235     // FileStream::Context actually closes the file asynchronously,
236     // independently from FileStream's destructor. It can cause problems for
237     // users wanting to delete the file right after FileStream deletion. Thus
238     // we are always adding SHARE_DELETE flag to accommodate such use case.
239     // TODO(rvargas): This sounds like a bug, as deleting the file would
240     // presumably happen on the wrong thread. There should be an async delete.
241     open_flags |= base::File::FLAG_SHARE_DELETE;
242     file.Initialize(path, open_flags);
243 #if defined(OS_ANDROID)
244   }
245 #endif  // defined(OS_ANDROID)
246   if (!file.IsValid())
247     return OpenResult(base::File(), IOResult::FromOSError(GetLastErrno()));
248
249   return OpenResult(file.Pass(), IOResult(OK, 0));
250 }
251
252 FileStream::Context::IOResult FileStream::Context::CloseFileImpl() {
253   file_.Close();
254   return IOResult(OK, 0);
255 }
256
257 void FileStream::Context::ProcessOpenError(const IOResult& error_code) {
258   bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN);
259   RecordError(error_code, FILE_ERROR_SOURCE_OPEN);
260 }
261
262 void FileStream::Context::OnOpenCompleted(const CompletionCallback& callback,
263                                           OpenResult open_result) {
264   if (!open_result.file.IsValid()) {
265     ProcessOpenError(open_result.error_code);
266   } else if (!orphaned_) {
267     file_ = open_result.file.Pass();
268     OnAsyncFileOpened();
269   }
270   OnAsyncCompleted(IntToInt64(callback), open_result.error_code.result);
271 }
272
273 void FileStream::Context::CloseAndDelete() {
274   DCHECK(!async_in_progress_);
275
276   if (file_.IsValid()) {
277     bool posted = task_runner_.get()->PostTaskAndReply(
278         FROM_HERE,
279         base::Bind(base::IgnoreResult(&Context::CloseFileImpl),
280                    base::Unretained(this)),
281         base::Bind(&Context::OnCloseCompleted, base::Unretained(this)));
282     DCHECK(posted);
283   } else {
284     delete this;
285   }
286 }
287
288 void FileStream::Context::OnCloseCompleted() {
289   delete this;
290 }
291
292 Int64CompletionCallback FileStream::Context::IntToInt64(
293     const CompletionCallback& callback) {
294   return base::Bind(&CallInt64ToInt, callback);
295 }
296
297 void FileStream::Context::ProcessAsyncResult(
298     const Int64CompletionCallback& callback,
299     FileErrorSource source,
300     const IOResult& result) {
301   RecordError(result, source);
302   OnAsyncCompleted(callback, result.result);
303 }
304
305 void FileStream::Context::OnAsyncCompleted(
306     const Int64CompletionCallback& callback,
307     int64 result) {
308   // Reset this before Run() as Run() may issue a new async operation. Also it
309   // should be reset before CloseAsync() because it shouldn't run if any async
310   // operation is in progress.
311   async_in_progress_ = false;
312   if (orphaned_)
313     CloseAndDelete();
314   else
315     callback.Run(result);
316 }
317
318 }  // namespace net
319