OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/base/file_stream.h" | 5 #include "net/base/file_stream.h" |
6 | 6 |
7 #include "base/message_loop.h" | |
8 #include "base/task_runner_util.h" | |
9 #include "base/threading/thread_restrictions.h" | |
10 #include "base/threading/worker_pool.h" | |
11 #include "net/base/file_stream_net_log_parameters.h" | |
12 #include "net/base/net_errors.h" | |
13 | |
14 #if defined(OS_WIN) | |
15 #include "net/base/file_stream_win.h" | |
16 #elif defined(OS_POSIX) | |
17 #include "net/base/file_stream_posix.h" | |
18 #endif | |
19 | |
7 namespace net { | 20 namespace net { |
8 | 21 |
22 void FileStream::Context::Orphan() { | |
willchan no longer on Chromium
2012/09/05 23:20:31
DCHECK(!orphaned_);
We shouldn't call this twice.
| |
23 orphaned_ = true; | |
24 if (async_in_progress_) { | |
25 // Do we need this here? It can race with CloseAsync() and result in | |
26 // calling CancelIo() on the file handle that was already closed and | |
27 // reopened again. | |
28 //if (file_ != base::kInvalidPlatformFileValue) | |
29 // CancelIo(file_); | |
30 } else { | |
31 CloseAsync(CompletionCallback()); | |
32 } | |
33 } | |
34 | |
35 void FileStream::Context::OpenAsync(const FilePath& path, | |
36 int open_flags, | |
37 const CompletionCallback& callback) { | |
38 DCHECK(!async_in_progress_); | |
39 | |
40 BeginOpenEvent(path); | |
41 | |
42 const bool posted = base::PostTaskAndReplyWithResult( | |
43 base::WorkerPool::GetTaskRunner(true /* task_is_slow */), | |
44 FROM_HERE, | |
45 base::Bind(&Context::OpenFileImpl, | |
46 base::Unretained(this), path, open_flags), | |
47 base::Bind(&Context::OnOpenCompleted, | |
48 base::Unretained(this), callback)); | |
49 DCHECK(posted); | |
50 | |
51 async_in_progress_ = true; | |
52 } | |
53 | |
54 int FileStream::Context::OpenSync(const FilePath& path, int open_flags) { | |
willchan no longer on Chromium
2012/09/05 23:20:31
DCHECK(!async_in_progress_);
| |
55 BeginOpenEvent(path); | |
56 int result = OpenFileImpl(path, open_flags); | |
57 CheckForOpenError(&result); | |
58 // TODO(satorux): Remove this once all async clients are migrated to use | |
59 // Open(). crbug.com/114783 | |
60 if (open_flags & base::PLATFORM_FILE_ASYNC) | |
61 RegisterInMessageLoop(); | |
62 return result; | |
63 } | |
64 | |
65 void FileStream::Context::CloseAsync(const CompletionCallback& callback) { | |
66 DCHECK(!async_in_progress_); | |
67 | |
68 bound_net_log_.AddEvent(net::NetLog::TYPE_FILE_STREAM_CLOSE); | |
69 | |
70 if (file_ == base::kInvalidPlatformFileValue) { | |
71 MessageLoop::current()->PostTask( | |
willchan no longer on Chromium
2012/09/05 23:20:31
base::MessageLoopProxy::current()->PostTask(
| |
72 FROM_HERE, | |
73 base::Bind(&Context::OnCloseCompleted, | |
74 base::Unretained(this), callback)); | |
75 } else { | |
76 const bool posted = base::WorkerPool::PostTaskAndReply( | |
77 FROM_HERE, | |
78 base::Bind(&Context::CloseFileImpl, | |
79 base::Unretained(this)), | |
80 base::Bind(&Context::OnCloseCompleted, | |
81 base::Unretained(this), callback), | |
82 true /* task_is_slow */); | |
83 DCHECK(posted); | |
84 | |
85 async_in_progress_ = true; | |
86 } | |
87 } | |
88 | |
89 void FileStream::Context::CloseSync() { | |
90 DCHECK(!async_in_progress_); | |
91 bound_net_log_.AddEvent(net::NetLog::TYPE_FILE_STREAM_CLOSE); | |
92 if (file_ != base::kInvalidPlatformFileValue) { | |
93 CloseFileImpl(); | |
94 bound_net_log_.EndEvent(net::NetLog::TYPE_FILE_STREAM_OPEN); | |
95 } | |
96 } | |
97 | |
98 int FileStream::Context::RecordAndMapError(int error, | |
99 FileErrorSource source) const { | |
100 // The following check is against incorrect use or bug. File descriptor | |
101 // shouldn't ever be closed outside of FileStream while it still tries to do | |
102 // something with it. | |
103 DCHECK(error != kErrorBadFile); | |
104 net::Error net_error = MapSystemError(error); | |
105 | |
106 if (!orphaned_) { | |
107 bound_net_log_.AddEvent(net::NetLog::TYPE_FILE_STREAM_ERROR, | |
108 base::Bind(&NetLogFileStreamErrorCallback, | |
109 source, error, net_error)); | |
110 } | |
111 RecordFileError(error, source, record_uma_); | |
112 return net_error; | |
113 } | |
114 | |
115 void FileStream::Context::BeginOpenEvent(const FilePath& path) { | |
116 std::string file_name = path.AsUTF8Unsafe(); | |
117 bound_net_log_.BeginEvent(net::NetLog::TYPE_FILE_STREAM_OPEN, | |
118 NetLog::StringCallback("file_name", &file_name)); | |
119 } | |
120 | |
121 int FileStream::Context::OpenFileImpl(const FilePath& path, int open_flags) { | |
122 file_ = base::CreatePlatformFile(path, open_flags, NULL, NULL); | |
123 if (file_ == base::kInvalidPlatformFileValue) | |
124 return GetLastErrno(); | |
125 | |
126 return OK; | |
127 } | |
128 | |
129 void FileStream::Context::CheckForOpenError(int* result) { | |
willchan no longer on Chromium
2012/09/05 23:20:31
What do you think about, instead of having a singl
| |
130 if (file_ == base::kInvalidPlatformFileValue) { | |
131 bound_net_log_.EndEvent(net::NetLog::TYPE_FILE_STREAM_OPEN); | |
132 *result = RecordAndMapError(*result, FILE_ERROR_SOURCE_OPEN); | |
133 } | |
134 } | |
135 | |
136 void FileStream::Context::OnOpenCompleted(const CompletionCallback& callback, | |
137 int result) { | |
138 CheckForOpenError(&result); | |
139 if (!orphaned_) | |
140 RegisterInMessageLoop(); | |
willchan no longer on Chromium
2012/09/05 23:20:31
Can we name this differently? Perhaps something li
| |
141 OnAsyncCompleted(callback, result); | |
willchan no longer on Chromium
2012/09/05 23:20:31
Why isn't OnAsyncCompleted defined here in file_st
| |
142 } | |
143 | |
144 void FileStream::Context::CloseFileImpl() { | |
willchan no longer on Chromium
2012/09/05 23:20:31
How would you feel about getting rid of CloseFileI
| |
145 if (!base::ClosePlatformFile(file_)) | |
146 NOTREACHED(); | |
147 file_ = base::kInvalidPlatformFileValue; | |
148 } | |
149 | |
150 void FileStream::Context::OnCloseCompleted(const CompletionCallback& callback) { | |
151 if (!orphaned_) { | |
152 bound_net_log_.EndEvent(net::NetLog::TYPE_FILE_STREAM_OPEN); | |
153 // Reset this before Run() as Run() may issue a new async operation. | |
154 async_in_progress_ = false; | |
155 callback.Run(OK); | |
156 } else { | |
157 delete this; | |
158 } | |
159 } | |
160 | |
9 FileStream::FileStream(net::NetLog* net_log) | 161 FileStream::FileStream(net::NetLog* net_log) |
10 : impl_(net_log) { | 162 : open_flags_(0), |
11 } | 163 bound_net_log_(net::BoundNetLog::Make(net_log, |
12 | 164 net::NetLog::SOURCE_FILESTREAM)), |
13 FileStream::FileStream( | 165 context_(new Context(bound_net_log_)) { |
14 base::PlatformFile file, int flags, net::NetLog* net_log) | 166 bound_net_log_.BeginEvent(net::NetLog::TYPE_FILE_STREAM_ALIVE); |
15 : impl_(file, flags, net_log) { | 167 } |
168 | |
169 FileStream::FileStream(base::PlatformFile file, int flags, net::NetLog* net_log) | |
170 : open_flags_(flags), | |
171 bound_net_log_(net::BoundNetLog::Make(net_log, | |
172 net::NetLog::SOURCE_FILESTREAM)), | |
173 context_(new Context(file, bound_net_log_, open_flags_)) { | |
174 bound_net_log_.BeginEvent(net::NetLog::TYPE_FILE_STREAM_ALIVE); | |
16 } | 175 } |
17 | 176 |
18 FileStream::~FileStream() { | 177 FileStream::~FileStream() { |
178 if (IsOpen() && !is_async()) { | |
179 CloseSync(); | |
180 context_.reset(); | |
181 } else { | |
182 context_.release()->Orphan(); | |
183 } | |
184 | |
185 bound_net_log_.EndEvent(net::NetLog::TYPE_FILE_STREAM_ALIVE); | |
19 } | 186 } |
20 | 187 |
21 void FileStream::Close(const CompletionCallback& callback) { | 188 void FileStream::Close(const CompletionCallback& callback) { |
22 impl_.Close(callback); | 189 DCHECK(is_async()); |
190 context_->CloseAsync(callback); | |
23 } | 191 } |
24 | 192 |
25 void FileStream::CloseSync() { | 193 void FileStream::CloseSync() { |
26 impl_.CloseSync(); | 194 base::ThreadRestrictions::AssertIOAllowed(); |
195 | |
196 // TODO(satorux): Replace the following async stuff with a | |
197 // DCHECK(is_async()) once all async clients are migrated to | |
198 // use Close(). crbug.com/114783 | |
199 if (!context_->async_in_progress()) { | |
200 context_->CloseSync(); | |
201 } else { | |
202 Context* old_context = context_.release(); | |
203 context_.reset(new Context(bound_net_log_)); | |
204 context_->set_record_uma(old_context->record_uma()); | |
205 old_context->Orphan(); | |
206 } | |
27 } | 207 } |
28 | 208 |
29 int FileStream::Open(const FilePath& path, int open_flags, | 209 int FileStream::Open(const FilePath& path, int open_flags, |
30 const CompletionCallback& callback) { | 210 const CompletionCallback& callback) { |
31 return impl_.Open(path, open_flags, callback); | 211 if (IsOpen()) { |
212 DLOG(FATAL) << "File is already open!"; | |
213 return ERR_UNEXPECTED; | |
214 } | |
215 | |
216 open_flags_ = open_flags; | |
217 DCHECK(is_async()); | |
218 context_->OpenAsync(path, open_flags, callback); | |
219 return ERR_IO_PENDING; | |
32 } | 220 } |
33 | 221 |
34 int FileStream::OpenSync(const FilePath& path, int open_flags) { | 222 int FileStream::OpenSync(const FilePath& path, int open_flags) { |
35 return impl_.OpenSync(path, open_flags); | 223 base::ThreadRestrictions::AssertIOAllowed(); |
224 | |
225 if (IsOpen()) { | |
226 DLOG(FATAL) << "File is already open!"; | |
227 return ERR_UNEXPECTED; | |
228 } | |
229 | |
230 open_flags_ = open_flags; | |
231 // TODO(satorux): Put a DCHECK once all async clients are migrated | |
232 // to use Open(). crbug.com/114783 | |
233 // | |
234 // DCHECK(!is_async()); | |
235 return context_->OpenSync(path, open_flags_); | |
36 } | 236 } |
37 | 237 |
38 bool FileStream::IsOpen() const { | 238 bool FileStream::IsOpen() const { |
39 return impl_.IsOpen(); | 239 return context_->file() != base::kInvalidPlatformFileValue; |
40 } | 240 } |
41 | 241 |
42 int FileStream::Seek(Whence whence, int64 offset, | 242 int FileStream::Seek(Whence whence, |
243 int64 offset, | |
43 const Int64CompletionCallback& callback) { | 244 const Int64CompletionCallback& callback) { |
44 return impl_.Seek(whence, offset, callback); | 245 if (!IsOpen()) |
246 return ERR_UNEXPECTED; | |
247 | |
248 // Make sure we're async. | |
249 DCHECK(is_async()); | |
250 context_->SeekAsync(whence, offset, callback); | |
251 return ERR_IO_PENDING; | |
45 } | 252 } |
46 | 253 |
47 int64 FileStream::SeekSync(Whence whence, int64 offset) { | 254 int64 FileStream::SeekSync(Whence whence, int64 offset) { |
48 return impl_.SeekSync(whence, offset); | 255 base::ThreadRestrictions::AssertIOAllowed(); |
256 | |
257 if (!IsOpen()) | |
258 return ERR_UNEXPECTED; | |
259 | |
260 // If we're in async, make sure we don't have a request in flight. | |
261 DCHECK(!is_async() || !context_->async_in_progress()); | |
262 return context_->SeekSync(whence, offset); | |
49 } | 263 } |
50 | 264 |
51 int64 FileStream::Available() { | 265 int64 FileStream::Available() { |
52 return impl_.Available(); | 266 base::ThreadRestrictions::AssertIOAllowed(); |
53 } | 267 |
54 | 268 if (!IsOpen()) |
55 int FileStream::Read( | 269 return ERR_UNEXPECTED; |
56 IOBuffer* in_buf, int buf_len, const CompletionCallback& callback) { | 270 |
57 return impl_.Read(in_buf, buf_len, callback); | 271 int64 cur_pos = SeekSync(FROM_CURRENT, 0); |
272 if (cur_pos < 0) | |
273 return cur_pos; | |
274 | |
275 int64 size = context_->GetFileSize(); | |
276 if (size < 0) | |
277 return size; | |
278 | |
279 DCHECK_GT(size, cur_pos); | |
280 return size - cur_pos; | |
281 } | |
282 | |
283 int FileStream::Read(IOBuffer* buf, | |
284 int buf_len, | |
285 const CompletionCallback& callback) { | |
286 if (!IsOpen()) | |
287 return ERR_UNEXPECTED; | |
288 | |
289 // read(..., 0) will return 0, which indicates end-of-file. | |
290 DCHECK_GT(buf_len, 0); | |
291 DCHECK(open_flags_ & base::PLATFORM_FILE_READ); | |
292 DCHECK(is_async()); | |
293 | |
294 return context_->ReadAsync(buf, buf_len, callback); | |
58 } | 295 } |
59 | 296 |
60 int FileStream::ReadSync(char* buf, int buf_len) { | 297 int FileStream::ReadSync(char* buf, int buf_len) { |
61 return impl_.ReadSync(buf, buf_len); | 298 base::ThreadRestrictions::AssertIOAllowed(); |
299 | |
300 if (!IsOpen()) | |
301 return ERR_UNEXPECTED; | |
302 | |
303 DCHECK(!is_async()); | |
304 // read(..., 0) will return 0, which indicates end-of-file. | |
305 DCHECK_GT(buf_len, 0); | |
306 DCHECK(open_flags_ & base::PLATFORM_FILE_READ); | |
307 | |
308 return context_->ReadSync(buf, buf_len); | |
62 } | 309 } |
63 | 310 |
64 int FileStream::ReadUntilComplete(char *buf, int buf_len) { | 311 int FileStream::ReadUntilComplete(char *buf, int buf_len) { |
65 return impl_.ReadUntilComplete(buf, buf_len); | 312 base::ThreadRestrictions::AssertIOAllowed(); |
66 } | 313 |
67 | 314 int to_read = buf_len; |
68 int FileStream::Write( | 315 int bytes_total = 0; |
69 IOBuffer* buf, int buf_len, const CompletionCallback& callback) { | 316 |
70 return impl_.Write(buf, buf_len, callback); | 317 do { |
318 int bytes_read = ReadSync(buf, to_read); | |
319 if (bytes_read <= 0) { | |
320 if (bytes_total == 0) | |
321 return bytes_read; | |
322 | |
323 return bytes_total; | |
324 } | |
325 | |
326 bytes_total += bytes_read; | |
327 buf += bytes_read; | |
328 to_read -= bytes_read; | |
329 } while (bytes_total < buf_len); | |
330 | |
331 return bytes_total; | |
332 } | |
333 | |
334 int FileStream::Write(IOBuffer* buf, | |
335 int buf_len, | |
336 const CompletionCallback& callback) { | |
337 if (!IsOpen()) | |
338 return ERR_UNEXPECTED; | |
339 | |
340 DCHECK(is_async()); | |
341 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); | |
342 // write(..., 0) will return 0, which indicates end-of-file. | |
343 DCHECK_GT(buf_len, 0); | |
344 | |
345 return context_->WriteAsync(buf, buf_len, callback); | |
71 } | 346 } |
72 | 347 |
73 int FileStream::WriteSync(const char* buf, int buf_len) { | 348 int FileStream::WriteSync(const char* buf, int buf_len) { |
74 return impl_.WriteSync(buf, buf_len); | 349 base::ThreadRestrictions::AssertIOAllowed(); |
350 | |
351 if (!IsOpen()) | |
352 return ERR_UNEXPECTED; | |
353 | |
354 DCHECK(!is_async()); | |
355 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); | |
356 // write(..., 0) will return 0, which indicates end-of-file. | |
357 DCHECK_GT(buf_len, 0); | |
358 | |
359 return context_->WriteSync(buf, buf_len); | |
75 } | 360 } |
76 | 361 |
77 int64 FileStream::Truncate(int64 bytes) { | 362 int64 FileStream::Truncate(int64 bytes) { |
78 return impl_.Truncate(bytes); | 363 base::ThreadRestrictions::AssertIOAllowed(); |
364 | |
365 if (!IsOpen()) | |
366 return ERR_UNEXPECTED; | |
367 | |
368 // We'd better be open for writing. | |
369 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); | |
370 | |
371 // Seek to the position to truncate from. | |
372 int64 seek_position = SeekSync(FROM_BEGIN, bytes); | |
373 if (seek_position != bytes) | |
374 return ERR_UNEXPECTED; | |
375 | |
376 // And truncate the file. | |
377 return context_->Truncate(bytes); | |
79 } | 378 } |
80 | 379 |
81 int FileStream::Flush() { | 380 int FileStream::Flush() { |
82 return impl_.Flush(); | 381 base::ThreadRestrictions::AssertIOAllowed(); |
382 | |
383 if (!IsOpen()) | |
384 return ERR_UNEXPECTED; | |
385 | |
386 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); | |
387 return context_->Flush(); | |
83 } | 388 } |
84 | 389 |
85 void FileStream::EnableErrorStatistics() { | 390 void FileStream::EnableErrorStatistics() { |
86 impl_.EnableErrorStatistics(); | 391 context_->set_record_uma(true); |
87 } | 392 } |
88 | 393 |
89 void FileStream::SetBoundNetLogSource( | 394 void FileStream::SetBoundNetLogSource( |
90 const net::BoundNetLog& owner_bound_net_log) { | 395 const net::BoundNetLog& owner_bound_net_log) { |
91 impl_.SetBoundNetLogSource(owner_bound_net_log); | 396 if ((owner_bound_net_log.source().id == net::NetLog::Source::kInvalidId) && |
397 (bound_net_log_.source().id == net::NetLog::Source::kInvalidId)) { | |
398 // Both |BoundNetLog|s are invalid. | |
399 return; | |
400 } | |
401 | |
402 // Should never connect to itself. | |
403 DCHECK_NE(bound_net_log_.source().id, owner_bound_net_log.source().id); | |
404 | |
405 bound_net_log_.AddEvent( | |
406 net::NetLog::TYPE_FILE_STREAM_BOUND_TO_OWNER, | |
407 owner_bound_net_log.source().ToEventParametersCallback()); | |
408 | |
409 owner_bound_net_log.AddEvent( | |
410 net::NetLog::TYPE_FILE_STREAM_SOURCE, | |
411 bound_net_log_.source().ToEventParametersCallback()); | |
92 } | 412 } |
93 | 413 |
94 base::PlatformFile FileStream::GetPlatformFileForTesting() { | 414 base::PlatformFile FileStream::GetPlatformFileForTesting() { |
95 return impl_.GetPlatformFileForTesting(); | 415 return context_->file(); |
96 } | 416 } |
97 | 417 |
98 } // namespace net | 418 } // namespace net |
OLD | NEW |