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.
5 #include "net/base/file_stream_context.h"
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"
16 #if defined(OS_ANDROID)
17 #include "base/android/content_uri_utils.h"
24 void CallInt64ToInt(const CompletionCallback
& callback
, int64 result
) {
25 callback
.Run(static_cast<int>(result
));
30 FileStream::Context::IOResult::IOResult()
35 FileStream::Context::IOResult::IOResult(int64 result
, int os_error
)
41 FileStream::Context::IOResult
FileStream::Context::IOResult::FromOSError(
43 return IOResult(MapSystemError(os_error
), os_error
);
46 // ---------------------------------------------------------------------
48 FileStream::Context::OpenResult::OpenResult() {
51 FileStream::Context::OpenResult::OpenResult(base::File file
,
54 error_code(error_code
) {
57 FileStream::Context::OpenResult::OpenResult(RValue other
)
58 : file(other
.object
->file
.Pass()),
59 error_code(other
.object
->error_code
) {
62 FileStream::Context::OpenResult
& FileStream::Context::OpenResult::operator=(
64 if (this != other
.object
) {
65 file
= other
.object
->file
.Pass();
66 error_code
= other
.object
->error_code
;
71 // ---------------------------------------------------------------------
73 void FileStream::Context::Orphan() {
78 if (!async_in_progress_
) {
80 } else if (file_
.IsValid()) {
81 CancelIo(file_
.GetPlatformFile());
85 void FileStream::Context::OpenAsync(const base::FilePath
& path
,
87 const CompletionCallback
& callback
) {
88 DCHECK(!async_in_progress_
);
90 bool posted
= base::PostTaskAndReplyWithResult(
94 &Context::OpenFileImpl
, base::Unretained(this), path
, open_flags
),
95 base::Bind(&Context::OnOpenCompleted
, base::Unretained(this), callback
));
98 async_in_progress_
= true;
101 void FileStream::Context::CloseAsync(const CompletionCallback
& callback
) {
102 DCHECK(!async_in_progress_
);
103 bool posted
= base::PostTaskAndReplyWithResult(
106 base::Bind(&Context::CloseFileImpl
, base::Unretained(this)),
107 base::Bind(&Context::OnAsyncCompleted
,
108 base::Unretained(this),
109 IntToInt64(callback
)));
112 async_in_progress_
= true;
115 void FileStream::Context::SeekAsync(Whence whence
,
117 const Int64CompletionCallback
& callback
) {
118 DCHECK(!async_in_progress_
);
120 bool posted
= base::PostTaskAndReplyWithResult(
124 &Context::SeekFileImpl
, base::Unretained(this), whence
, offset
),
125 base::Bind(&Context::OnAsyncCompleted
,
126 base::Unretained(this),
130 async_in_progress_
= true;
133 void FileStream::Context::FlushAsync(const CompletionCallback
& callback
) {
134 DCHECK(!async_in_progress_
);
136 bool posted
= base::PostTaskAndReplyWithResult(
139 base::Bind(&Context::FlushFileImpl
, base::Unretained(this)),
140 base::Bind(&Context::OnAsyncCompleted
,
141 base::Unretained(this),
142 IntToInt64(callback
)));
145 async_in_progress_
= true;
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
;
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
);
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)
173 #endif // defined(OS_ANDROID)
175 return OpenResult(base::File(), IOResult::FromOSError(GetLastErrno()));
177 return OpenResult(file
.Pass(), IOResult(OK
, 0));
180 FileStream::Context::IOResult
FileStream::Context::CloseFileImpl() {
182 return IOResult(OK
, 0);
185 void FileStream::Context::OnOpenCompleted(const CompletionCallback
& callback
,
186 OpenResult open_result
) {
187 file_
= open_result
.file
.Pass();
188 if (file_
.IsValid() && !orphaned_
)
191 OnAsyncCompleted(IntToInt64(callback
), open_result
.error_code
);
194 void FileStream::Context::CloseAndDelete() {
195 DCHECK(!async_in_progress_
);
197 if (file_
.IsValid()) {
198 bool posted
= task_runner_
.get()->PostTask(
200 base::Bind(base::IgnoreResult(&Context::CloseFileImpl
),
208 Int64CompletionCallback
FileStream::Context::IntToInt64(
209 const CompletionCallback
& callback
) {
210 return base::Bind(&CallInt64ToInt
, callback
);
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;
223 callback
.Run(result
.result
);