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