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/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"
16 void CallInt64ToInt(const net::CompletionCallback
& callback
, int64 result
) {
17 callback
.Run(static_cast<int>(result
));
24 FileStream::Context::IOResult::IOResult()
29 FileStream::Context::IOResult::IOResult(int64 result
, int os_error
)
35 FileStream::Context::IOResult
FileStream::Context::IOResult::FromOSError(
37 return IOResult(MapSystemError(os_error
), os_error
);
40 FileStream::Context::OpenResult::OpenResult()
41 : file(base::kInvalidPlatformFileValue
) {
44 FileStream::Context::OpenResult::OpenResult(base::PlatformFile file
,
47 error_code(error_code
) {
50 void FileStream::Context::Orphan() {
54 if (file_
!= base::kInvalidPlatformFileValue
)
55 bound_net_log_
.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN
);
57 if (!async_in_progress_
) {
59 } else if (file_
!= base::kInvalidPlatformFileValue
) {
64 void FileStream::Context::OpenAsync(const base::FilePath
& path
,
66 const CompletionCallback
& callback
) {
67 DCHECK(!async_in_progress_
);
71 const bool posted
= base::PostTaskAndReplyWithResult(
75 &Context::OpenFileImpl
, base::Unretained(this), path
, open_flags
),
76 base::Bind(&Context::OnOpenCompleted
, base::Unretained(this), callback
));
79 async_in_progress_
= true;
82 int FileStream::Context::OpenSync(const base::FilePath
& path
, int open_flags
) {
83 DCHECK(!async_in_progress_
);
86 OpenResult result
= OpenFileImpl(path
, open_flags
);
88 if (file_
== base::kInvalidPlatformFileValue
) {
89 ProcessOpenError(result
.error_code
);
91 // TODO(satorux): Remove this once all async clients are migrated to use
92 // Open(). crbug.com/114783
93 if (open_flags
& base::PLATFORM_FILE_ASYNC
)
96 return result
.error_code
.result
;
99 void FileStream::Context::CloseSync() {
100 DCHECK(!async_in_progress_
);
101 if (file_
!= base::kInvalidPlatformFileValue
) {
102 base::ClosePlatformFile(file_
);
103 file_
= base::kInvalidPlatformFileValue
;
104 bound_net_log_
.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN
);
108 void FileStream::Context::SeekAsync(Whence whence
,
110 const Int64CompletionCallback
& callback
) {
111 DCHECK(!async_in_progress_
);
113 const bool posted
= base::PostTaskAndReplyWithResult(
117 &Context::SeekFileImpl
, base::Unretained(this), whence
, offset
),
118 base::Bind(&Context::ProcessAsyncResult
,
119 base::Unretained(this),
121 FILE_ERROR_SOURCE_SEEK
));
124 async_in_progress_
= true;
127 int64
FileStream::Context::SeekSync(Whence whence
, int64 offset
) {
128 IOResult result
= SeekFileImpl(whence
, offset
);
129 RecordError(result
, FILE_ERROR_SOURCE_SEEK
);
130 return result
.result
;
133 void FileStream::Context::FlushAsync(const CompletionCallback
& callback
) {
134 DCHECK(!async_in_progress_
);
136 const bool posted
= base::PostTaskAndReplyWithResult(
139 base::Bind(&Context::FlushFileImpl
, base::Unretained(this)),
140 base::Bind(&Context::ProcessAsyncResult
,
141 base::Unretained(this),
142 IntToInt64(callback
),
143 FILE_ERROR_SOURCE_FLUSH
));
146 async_in_progress_
= true;
149 int FileStream::Context::FlushSync() {
150 IOResult result
= FlushFileImpl();
151 RecordError(result
, FILE_ERROR_SOURCE_FLUSH
);
152 return result
.result
;
155 void FileStream::Context::RecordError(const IOResult
& result
,
156 FileErrorSource source
) const {
157 if (result
.result
>= 0) {
158 // |result| is not an error.
162 // The following check is against incorrect use or bug. File descriptor
163 // shouldn't ever be closed outside of FileStream while it still tries to do
164 // something with it.
165 DCHECK_NE(result
.result
, ERR_INVALID_HANDLE
);
168 bound_net_log_
.AddEvent(
169 NetLog::TYPE_FILE_STREAM_ERROR
,
170 base::Bind(&NetLogFileStreamErrorCallback
,
171 source
, result
.os_error
,
172 static_cast<net::Error
>(result
.result
)));
175 RecordFileError(result
.os_error
, source
, record_uma_
);
178 void FileStream::Context::BeginOpenEvent(const base::FilePath
& path
) {
179 std::string file_name
= path
.AsUTF8Unsafe();
180 bound_net_log_
.BeginEvent(NetLog::TYPE_FILE_STREAM_OPEN
,
181 NetLog::StringCallback("file_name", &file_name
));
184 FileStream::Context::OpenResult
FileStream::Context::OpenFileImpl(
185 const base::FilePath
& path
, int open_flags
) {
186 // FileStream::Context actually closes the file asynchronously, independently
187 // from FileStream's destructor. It can cause problems for users wanting to
188 // delete the file right after FileStream deletion. Thus we are always
189 // adding SHARE_DELETE flag to accommodate such use case.
190 open_flags
|= base::PLATFORM_FILE_SHARE_DELETE
;
191 base::PlatformFile file
=
192 base::CreatePlatformFile(path
, open_flags
, NULL
, NULL
);
193 if (file
== base::kInvalidPlatformFileValue
)
194 return OpenResult(file
, IOResult::FromOSError(GetLastErrno()));
196 return OpenResult(file
, IOResult(OK
, 0));
199 void FileStream::Context::ProcessOpenError(const IOResult
& error_code
) {
200 bound_net_log_
.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN
);
201 RecordError(error_code
, FILE_ERROR_SOURCE_OPEN
);
204 void FileStream::Context::OnOpenCompleted(const CompletionCallback
& callback
,
205 OpenResult open_result
) {
206 file_
= open_result
.file
;
207 if (file_
== base::kInvalidPlatformFileValue
)
208 ProcessOpenError(open_result
.error_code
);
211 OnAsyncCompleted(IntToInt64(callback
), open_result
.error_code
.result
);
214 void FileStream::Context::CloseAndDelete() {
215 DCHECK(!async_in_progress_
);
217 if (file_
== base::kInvalidPlatformFileValue
) {
220 const bool posted
= task_runner_
->PostTaskAndReply(
222 base::Bind(base::IgnoreResult(&base::ClosePlatformFile
), file_
),
223 base::Bind(&Context::OnCloseCompleted
, base::Unretained(this)));
225 file_
= base::kInvalidPlatformFileValue
;
229 void FileStream::Context::OnCloseCompleted() {
233 Int64CompletionCallback
FileStream::Context::IntToInt64(
234 const CompletionCallback
& callback
) {
235 return base::Bind(&CallInt64ToInt
, callback
);
238 void FileStream::Context::ProcessAsyncResult(
239 const Int64CompletionCallback
& callback
,
240 FileErrorSource source
,
241 const IOResult
& result
) {
242 RecordError(result
, source
);
243 OnAsyncCompleted(callback
, result
.result
);
246 void FileStream::Context::OnAsyncCompleted(
247 const Int64CompletionCallback
& callback
,
249 // Reset this before Run() as Run() may issue a new async operation. Also it
250 // should be reset before CloseAsync() because it shouldn't run if any async
251 // operation is in progress.
252 async_in_progress_
= false;
256 callback
.Run(result
);