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.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 "base/threading/worker_pool.h" 12 #include "net/base/file_stream_context.h" 13 #include "net/base/file_stream_net_log_parameters.h" 14 #include "net/base/net_errors.h" 15 16 namespace net { 17 18 FileStream::FileStream(NetLog* net_log, 19 const scoped_refptr<base::TaskRunner>& task_runner) 20 /* To allow never opened stream to be destroyed on any thread we set flags 21 as if stream was opened asynchronously. */ 22 : open_flags_(base::PLATFORM_FILE_ASYNC), 23 bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)), 24 context_(new Context(bound_net_log_, task_runner)) { 25 bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE); 26 } 27 28 FileStream::FileStream(NetLog* net_log) 29 /* To allow never opened stream to be destroyed on any thread we set flags 30 as if stream was opened asynchronously. */ 31 : open_flags_(base::PLATFORM_FILE_ASYNC), 32 bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)), 33 context_(new Context(bound_net_log_, 34 base::WorkerPool::GetTaskRunner(true /* slow */))) { 35 bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE); 36 } 37 38 FileStream::FileStream(base::PlatformFile file, 39 int flags, 40 NetLog* net_log, 41 const scoped_refptr<base::TaskRunner>& task_runner) 42 : open_flags_(flags), 43 bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)), 44 context_(new Context(file, bound_net_log_, open_flags_, task_runner)) { 45 bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE); 46 } 47 48 FileStream::FileStream(base::PlatformFile file, int flags, NetLog* net_log) 49 : open_flags_(flags), 50 bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)), 51 context_(new Context(file, 52 bound_net_log_, 53 open_flags_, 54 base::WorkerPool::GetTaskRunner(true /* slow */))) { 55 bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE); 56 } 57 58 FileStream::~FileStream() { 59 if (!is_async()) { 60 base::ThreadRestrictions::AssertIOAllowed(); 61 context_->CloseSync(); 62 context_.reset(); 63 } else { 64 context_.release()->Orphan(); 65 } 66 67 bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_ALIVE); 68 } 69 70 int FileStream::Open(const base::FilePath& path, int open_flags, 71 const CompletionCallback& callback) { 72 if (IsOpen()) { 73 DLOG(FATAL) << "File is already open!"; 74 return ERR_UNEXPECTED; 75 } 76 77 open_flags_ = open_flags; 78 DCHECK(is_async()); 79 context_->OpenAsync(path, open_flags, callback); 80 return ERR_IO_PENDING; 81 } 82 83 int FileStream::OpenSync(const base::FilePath& path, int open_flags) { 84 base::ThreadRestrictions::AssertIOAllowed(); 85 86 if (IsOpen()) { 87 DLOG(FATAL) << "File is already open!"; 88 return ERR_UNEXPECTED; 89 } 90 91 open_flags_ = open_flags; 92 DCHECK(!is_async()); 93 return context_->OpenSync(path, open_flags_); 94 } 95 96 bool FileStream::IsOpen() const { 97 return context_->file() != base::kInvalidPlatformFileValue; 98 } 99 100 int FileStream::Seek(Whence whence, 101 int64 offset, 102 const Int64CompletionCallback& callback) { 103 if (!IsOpen()) 104 return ERR_UNEXPECTED; 105 106 // Make sure we're async. 107 DCHECK(is_async()); 108 context_->SeekAsync(whence, offset, callback); 109 return ERR_IO_PENDING; 110 } 111 112 int64 FileStream::SeekSync(Whence whence, int64 offset) { 113 base::ThreadRestrictions::AssertIOAllowed(); 114 115 if (!IsOpen()) 116 return ERR_UNEXPECTED; 117 118 // If we're in async, make sure we don't have a request in flight. 119 DCHECK(!is_async() || !context_->async_in_progress()); 120 return context_->SeekSync(whence, offset); 121 } 122 123 int64 FileStream::Available() { 124 base::ThreadRestrictions::AssertIOAllowed(); 125 126 if (!IsOpen()) 127 return ERR_UNEXPECTED; 128 129 int64 cur_pos = SeekSync(FROM_CURRENT, 0); 130 if (cur_pos < 0) 131 return cur_pos; 132 133 int64 size = context_->GetFileSize(); 134 if (size < 0) 135 return size; 136 137 DCHECK_GE(size, cur_pos); 138 return size - cur_pos; 139 } 140 141 int FileStream::Read(IOBuffer* buf, 142 int buf_len, 143 const CompletionCallback& callback) { 144 if (!IsOpen()) 145 return ERR_UNEXPECTED; 146 147 // read(..., 0) will return 0, which indicates end-of-file. 148 DCHECK_GT(buf_len, 0); 149 DCHECK(open_flags_ & base::PLATFORM_FILE_READ); 150 DCHECK(is_async()); 151 152 return context_->ReadAsync(buf, buf_len, callback); 153 } 154 155 int FileStream::ReadSync(char* buf, int buf_len) { 156 base::ThreadRestrictions::AssertIOAllowed(); 157 158 if (!IsOpen()) 159 return ERR_UNEXPECTED; 160 161 DCHECK(!is_async()); 162 // read(..., 0) will return 0, which indicates end-of-file. 163 DCHECK_GT(buf_len, 0); 164 DCHECK(open_flags_ & base::PLATFORM_FILE_READ); 165 166 return context_->ReadSync(buf, buf_len); 167 } 168 169 int FileStream::ReadUntilComplete(char *buf, int buf_len) { 170 base::ThreadRestrictions::AssertIOAllowed(); 171 172 int to_read = buf_len; 173 int bytes_total = 0; 174 175 do { 176 int bytes_read = ReadSync(buf, to_read); 177 if (bytes_read <= 0) { 178 if (bytes_total == 0) 179 return bytes_read; 180 181 return bytes_total; 182 } 183 184 bytes_total += bytes_read; 185 buf += bytes_read; 186 to_read -= bytes_read; 187 } while (bytes_total < buf_len); 188 189 return bytes_total; 190 } 191 192 int FileStream::Write(IOBuffer* buf, 193 int buf_len, 194 const CompletionCallback& callback) { 195 if (!IsOpen()) 196 return ERR_UNEXPECTED; 197 198 DCHECK(is_async()); 199 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); 200 // write(..., 0) will return 0, which indicates end-of-file. 201 DCHECK_GT(buf_len, 0); 202 203 return context_->WriteAsync(buf, buf_len, callback); 204 } 205 206 int FileStream::WriteSync(const char* buf, int buf_len) { 207 base::ThreadRestrictions::AssertIOAllowed(); 208 209 if (!IsOpen()) 210 return ERR_UNEXPECTED; 211 212 DCHECK(!is_async()); 213 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); 214 // write(..., 0) will return 0, which indicates end-of-file. 215 DCHECK_GT(buf_len, 0); 216 217 return context_->WriteSync(buf, buf_len); 218 } 219 220 int64 FileStream::Truncate(int64 bytes) { 221 base::ThreadRestrictions::AssertIOAllowed(); 222 223 if (!IsOpen()) 224 return ERR_UNEXPECTED; 225 226 // We'd better be open for writing. 227 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); 228 229 // Seek to the position to truncate from. 230 int64 seek_position = SeekSync(FROM_BEGIN, bytes); 231 if (seek_position != bytes) 232 return ERR_UNEXPECTED; 233 234 // And truncate the file. 235 return context_->Truncate(bytes); 236 } 237 238 int FileStream::Flush(const CompletionCallback& callback) { 239 if (!IsOpen()) 240 return ERR_UNEXPECTED; 241 242 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); 243 // Make sure we're async. 244 DCHECK(is_async()); 245 246 context_->FlushAsync(callback); 247 return ERR_IO_PENDING; 248 } 249 250 int FileStream::FlushSync() { 251 base::ThreadRestrictions::AssertIOAllowed(); 252 253 if (!IsOpen()) 254 return ERR_UNEXPECTED; 255 256 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); 257 return context_->FlushSync(); 258 } 259 260 void FileStream::EnableErrorStatistics() { 261 context_->set_record_uma(true); 262 } 263 264 void FileStream::SetBoundNetLogSource(const BoundNetLog& owner_bound_net_log) { 265 if ((owner_bound_net_log.source().id == NetLog::Source::kInvalidId) && 266 (bound_net_log_.source().id == NetLog::Source::kInvalidId)) { 267 // Both |BoundNetLog|s are invalid. 268 return; 269 } 270 271 // Should never connect to itself. 272 DCHECK_NE(bound_net_log_.source().id, owner_bound_net_log.source().id); 273 274 bound_net_log_.AddEvent(NetLog::TYPE_FILE_STREAM_BOUND_TO_OWNER, 275 owner_bound_net_log.source().ToEventParametersCallback()); 276 277 owner_bound_net_log.AddEvent(NetLog::TYPE_FILE_STREAM_SOURCE, 278 bound_net_log_.source().ToEventParametersCallback()); 279 } 280 281 base::PlatformFile FileStream::GetPlatformFileForTesting() { 282 return context_->file(); 283 } 284 285 } // namespace net 286