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
FileStream(NetLog * net_log,const scoped_refptr<base::TaskRunner> & task_runner)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
FileStream(NetLog * net_log)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
FileStream(base::PlatformFile file,int flags,NetLog * net_log,const scoped_refptr<base::TaskRunner> & task_runner)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
FileStream(base::PlatformFile file,int flags,NetLog * net_log)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
~FileStream()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
Open(const base::FilePath & path,int open_flags,const CompletionCallback & callback)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
OpenSync(const base::FilePath & path,int open_flags)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
Close(const CompletionCallback & callback)96 int FileStream::Close(const CompletionCallback& callback) {
97 DCHECK(is_async());
98 context_->CloseAsync(callback);
99 return ERR_IO_PENDING;
100 }
101
CloseSync()102 int FileStream::CloseSync() {
103 DCHECK(!is_async());
104 base::ThreadRestrictions::AssertIOAllowed();
105 context_->CloseSync();
106 return OK;
107 }
108
IsOpen() const109 bool FileStream::IsOpen() const {
110 return context_->file() != base::kInvalidPlatformFileValue;
111 }
112
Seek(Whence whence,int64 offset,const Int64CompletionCallback & callback)113 int FileStream::Seek(Whence whence,
114 int64 offset,
115 const Int64CompletionCallback& callback) {
116 if (!IsOpen())
117 return ERR_UNEXPECTED;
118
119 // Make sure we're async.
120 DCHECK(is_async());
121 context_->SeekAsync(whence, offset, callback);
122 return ERR_IO_PENDING;
123 }
124
SeekSync(Whence whence,int64 offset)125 int64 FileStream::SeekSync(Whence whence, int64 offset) {
126 base::ThreadRestrictions::AssertIOAllowed();
127
128 if (!IsOpen())
129 return ERR_UNEXPECTED;
130
131 // If we're in async, make sure we don't have a request in flight.
132 DCHECK(!is_async() || !context_->async_in_progress());
133 return context_->SeekSync(whence, offset);
134 }
135
Available()136 int64 FileStream::Available() {
137 base::ThreadRestrictions::AssertIOAllowed();
138
139 if (!IsOpen())
140 return ERR_UNEXPECTED;
141
142 int64 cur_pos = SeekSync(FROM_CURRENT, 0);
143 if (cur_pos < 0)
144 return cur_pos;
145
146 int64 size = context_->GetFileSize();
147 if (size < 0)
148 return size;
149
150 DCHECK_GE(size, cur_pos);
151 return size - cur_pos;
152 }
153
Read(IOBuffer * buf,int buf_len,const CompletionCallback & callback)154 int FileStream::Read(IOBuffer* buf,
155 int buf_len,
156 const CompletionCallback& callback) {
157 if (!IsOpen())
158 return ERR_UNEXPECTED;
159
160 // read(..., 0) will return 0, which indicates end-of-file.
161 DCHECK_GT(buf_len, 0);
162 DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
163 DCHECK(is_async());
164
165 return context_->ReadAsync(buf, buf_len, callback);
166 }
167
ReadSync(char * buf,int buf_len)168 int FileStream::ReadSync(char* buf, int buf_len) {
169 base::ThreadRestrictions::AssertIOAllowed();
170
171 if (!IsOpen())
172 return ERR_UNEXPECTED;
173
174 DCHECK(!is_async());
175 // read(..., 0) will return 0, which indicates end-of-file.
176 DCHECK_GT(buf_len, 0);
177 DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
178
179 return context_->ReadSync(buf, buf_len);
180 }
181
ReadUntilComplete(char * buf,int buf_len)182 int FileStream::ReadUntilComplete(char *buf, int buf_len) {
183 base::ThreadRestrictions::AssertIOAllowed();
184
185 int to_read = buf_len;
186 int bytes_total = 0;
187
188 do {
189 int bytes_read = ReadSync(buf, to_read);
190 if (bytes_read <= 0) {
191 if (bytes_total == 0)
192 return bytes_read;
193
194 return bytes_total;
195 }
196
197 bytes_total += bytes_read;
198 buf += bytes_read;
199 to_read -= bytes_read;
200 } while (bytes_total < buf_len);
201
202 return bytes_total;
203 }
204
Write(IOBuffer * buf,int buf_len,const CompletionCallback & callback)205 int FileStream::Write(IOBuffer* buf,
206 int buf_len,
207 const CompletionCallback& callback) {
208 if (!IsOpen())
209 return ERR_UNEXPECTED;
210
211 DCHECK(is_async());
212 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
213 // write(..., 0) will return 0, which indicates end-of-file.
214 DCHECK_GT(buf_len, 0);
215
216 return context_->WriteAsync(buf, buf_len, callback);
217 }
218
WriteSync(const char * buf,int buf_len)219 int FileStream::WriteSync(const char* buf, int buf_len) {
220 base::ThreadRestrictions::AssertIOAllowed();
221
222 if (!IsOpen())
223 return ERR_UNEXPECTED;
224
225 DCHECK(!is_async());
226 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
227 // write(..., 0) will return 0, which indicates end-of-file.
228 DCHECK_GT(buf_len, 0);
229
230 return context_->WriteSync(buf, buf_len);
231 }
232
Truncate(int64 bytes)233 int64 FileStream::Truncate(int64 bytes) {
234 base::ThreadRestrictions::AssertIOAllowed();
235
236 if (!IsOpen())
237 return ERR_UNEXPECTED;
238
239 // We'd better be open for writing.
240 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
241
242 // Seek to the position to truncate from.
243 int64 seek_position = SeekSync(FROM_BEGIN, bytes);
244 if (seek_position != bytes)
245 return ERR_UNEXPECTED;
246
247 // And truncate the file.
248 return context_->Truncate(bytes);
249 }
250
Flush(const CompletionCallback & callback)251 int FileStream::Flush(const CompletionCallback& callback) {
252 if (!IsOpen())
253 return ERR_UNEXPECTED;
254
255 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
256 // Make sure we're async.
257 DCHECK(is_async());
258
259 context_->FlushAsync(callback);
260 return ERR_IO_PENDING;
261 }
262
FlushSync()263 int FileStream::FlushSync() {
264 base::ThreadRestrictions::AssertIOAllowed();
265
266 if (!IsOpen())
267 return ERR_UNEXPECTED;
268
269 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
270 return context_->FlushSync();
271 }
272
EnableErrorStatistics()273 void FileStream::EnableErrorStatistics() {
274 context_->set_record_uma(true);
275 }
276
SetBoundNetLogSource(const BoundNetLog & owner_bound_net_log)277 void FileStream::SetBoundNetLogSource(const BoundNetLog& owner_bound_net_log) {
278 if ((owner_bound_net_log.source().id == NetLog::Source::kInvalidId) &&
279 (bound_net_log_.source().id == NetLog::Source::kInvalidId)) {
280 // Both |BoundNetLog|s are invalid.
281 return;
282 }
283
284 // Should never connect to itself.
285 DCHECK_NE(bound_net_log_.source().id, owner_bound_net_log.source().id);
286
287 bound_net_log_.AddEvent(NetLog::TYPE_FILE_STREAM_BOUND_TO_OWNER,
288 owner_bound_net_log.source().ToEventParametersCallback());
289
290 owner_bound_net_log.AddEvent(NetLog::TYPE_FILE_STREAM_SOURCE,
291 bound_net_log_.source().ToEventParametersCallback());
292 }
293
GetPlatformFileForTesting()294 base::PlatformFile FileStream::GetPlatformFileForTesting() {
295 return context_->file();
296 }
297
298 } // namespace net
299