|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 |
|
8 #include "nsCache.h" |
|
9 #include "nsDiskCache.h" |
|
10 #include "nsDiskCacheDevice.h" |
|
11 #include "nsDiskCacheStreams.h" |
|
12 #include "nsCacheService.h" |
|
13 #include "mozilla/FileUtils.h" |
|
14 #include "nsThreadUtils.h" |
|
15 #include "mozilla/MemoryReporting.h" |
|
16 #include "mozilla/Telemetry.h" |
|
17 #include "mozilla/TimeStamp.h" |
|
18 #include <algorithm> |
|
19 #include "mozilla/VisualEventTracer.h" |
|
20 |
|
21 // we pick 16k as the max buffer size because that is the threshold above which |
|
22 // we are unable to store the data in the cache block files |
|
23 // see nsDiskCacheMap.[cpp,h] |
|
24 #define kMaxBufferSize (16 * 1024) |
|
25 |
|
26 // Assumptions: |
|
27 // - cache descriptors live for life of streams |
|
28 // - streams will only be used by FileTransport, |
|
29 // they will not be directly accessible to clients |
|
30 // - overlapped I/O is NOT supported |
|
31 |
|
32 |
|
33 /****************************************************************************** |
|
34 * nsDiskCacheInputStream |
|
35 *****************************************************************************/ |
|
36 class nsDiskCacheInputStream : public nsIInputStream { |
|
37 |
|
38 public: |
|
39 |
|
40 nsDiskCacheInputStream( nsDiskCacheStreamIO * parent, |
|
41 PRFileDesc * fileDesc, |
|
42 const char * buffer, |
|
43 uint32_t endOfStream); |
|
44 |
|
45 virtual ~nsDiskCacheInputStream(); |
|
46 |
|
47 NS_DECL_THREADSAFE_ISUPPORTS |
|
48 NS_DECL_NSIINPUTSTREAM |
|
49 |
|
50 private: |
|
51 nsDiskCacheStreamIO * mStreamIO; // backpointer to parent |
|
52 PRFileDesc * mFD; |
|
53 const char * mBuffer; |
|
54 uint32_t mStreamEnd; |
|
55 uint32_t mPos; // stream position |
|
56 bool mClosed; |
|
57 }; |
|
58 |
|
59 |
|
60 NS_IMPL_ISUPPORTS(nsDiskCacheInputStream, nsIInputStream) |
|
61 |
|
62 |
|
63 nsDiskCacheInputStream::nsDiskCacheInputStream( nsDiskCacheStreamIO * parent, |
|
64 PRFileDesc * fileDesc, |
|
65 const char * buffer, |
|
66 uint32_t endOfStream) |
|
67 : mStreamIO(parent) |
|
68 , mFD(fileDesc) |
|
69 , mBuffer(buffer) |
|
70 , mStreamEnd(endOfStream) |
|
71 , mPos(0) |
|
72 , mClosed(false) |
|
73 { |
|
74 NS_ADDREF(mStreamIO); |
|
75 mStreamIO->IncrementInputStreamCount(); |
|
76 } |
|
77 |
|
78 |
|
79 nsDiskCacheInputStream::~nsDiskCacheInputStream() |
|
80 { |
|
81 Close(); |
|
82 mStreamIO->DecrementInputStreamCount(); |
|
83 NS_RELEASE(mStreamIO); |
|
84 } |
|
85 |
|
86 |
|
87 NS_IMETHODIMP |
|
88 nsDiskCacheInputStream::Close() |
|
89 { |
|
90 if (!mClosed) { |
|
91 if (mFD) { |
|
92 (void) PR_Close(mFD); |
|
93 mFD = nullptr; |
|
94 } |
|
95 mClosed = true; |
|
96 } |
|
97 return NS_OK; |
|
98 } |
|
99 |
|
100 |
|
101 NS_IMETHODIMP |
|
102 nsDiskCacheInputStream::Available(uint64_t * bytesAvailable) |
|
103 { |
|
104 if (mClosed) return NS_BASE_STREAM_CLOSED; |
|
105 if (mStreamEnd < mPos) return NS_ERROR_UNEXPECTED; |
|
106 |
|
107 *bytesAvailable = mStreamEnd - mPos; |
|
108 return NS_OK; |
|
109 } |
|
110 |
|
111 |
|
112 NS_IMETHODIMP |
|
113 nsDiskCacheInputStream::Read(char * buffer, uint32_t count, uint32_t * bytesRead) |
|
114 { |
|
115 *bytesRead = 0; |
|
116 |
|
117 if (mClosed) { |
|
118 CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read " |
|
119 "[stream=%p] stream was closed", |
|
120 this, buffer, count)); |
|
121 return NS_OK; |
|
122 } |
|
123 |
|
124 if (mPos == mStreamEnd) { |
|
125 CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read " |
|
126 "[stream=%p] stream at end of file", |
|
127 this, buffer, count)); |
|
128 return NS_OK; |
|
129 } |
|
130 if (mPos > mStreamEnd) { |
|
131 CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read " |
|
132 "[stream=%p] stream past end of file (!)", |
|
133 this, buffer, count)); |
|
134 return NS_ERROR_UNEXPECTED; |
|
135 } |
|
136 |
|
137 if (count > mStreamEnd - mPos) |
|
138 count = mStreamEnd - mPos; |
|
139 |
|
140 if (mFD) { |
|
141 // just read from file |
|
142 int32_t result = PR_Read(mFD, buffer, count); |
|
143 if (result < 0) { |
|
144 nsresult rv = NS_ErrorAccordingToNSPR(); |
|
145 CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read PR_Read failed" |
|
146 "[stream=%p, rv=%d, NSPR error %s", |
|
147 this, int(rv), PR_ErrorToName(PR_GetError()))); |
|
148 return rv; |
|
149 } |
|
150 |
|
151 mPos += (uint32_t)result; |
|
152 *bytesRead = (uint32_t)result; |
|
153 |
|
154 } else if (mBuffer) { |
|
155 // read data from mBuffer |
|
156 memcpy(buffer, mBuffer + mPos, count); |
|
157 mPos += count; |
|
158 *bytesRead = count; |
|
159 } else { |
|
160 // no data source for input stream |
|
161 } |
|
162 |
|
163 CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read " |
|
164 "[stream=%p, count=%ud, byteRead=%ud] ", |
|
165 this, unsigned(count), unsigned(*bytesRead))); |
|
166 return NS_OK; |
|
167 } |
|
168 |
|
169 |
|
170 NS_IMETHODIMP |
|
171 nsDiskCacheInputStream::ReadSegments(nsWriteSegmentFun writer, |
|
172 void * closure, |
|
173 uint32_t count, |
|
174 uint32_t * bytesRead) |
|
175 { |
|
176 return NS_ERROR_NOT_IMPLEMENTED; |
|
177 } |
|
178 |
|
179 |
|
180 NS_IMETHODIMP |
|
181 nsDiskCacheInputStream::IsNonBlocking(bool * nonBlocking) |
|
182 { |
|
183 *nonBlocking = false; |
|
184 return NS_OK; |
|
185 } |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 /****************************************************************************** |
|
191 * nsDiskCacheStreamIO |
|
192 *****************************************************************************/ |
|
193 NS_IMPL_ISUPPORTS(nsDiskCacheStreamIO, nsIOutputStream) |
|
194 |
|
195 nsDiskCacheStreamIO::nsDiskCacheStreamIO(nsDiskCacheBinding * binding) |
|
196 : mBinding(binding) |
|
197 , mInStreamCount(0) |
|
198 , mFD(nullptr) |
|
199 , mStreamEnd(0) |
|
200 , mBufSize(0) |
|
201 , mBuffer(nullptr) |
|
202 , mOutputStreamIsOpen(false) |
|
203 { |
|
204 mDevice = (nsDiskCacheDevice *)mBinding->mCacheEntry->CacheDevice(); |
|
205 |
|
206 // acquire "death grip" on cache service |
|
207 nsCacheService *service = nsCacheService::GlobalInstance(); |
|
208 NS_ADDREF(service); |
|
209 } |
|
210 |
|
211 |
|
212 nsDiskCacheStreamIO::~nsDiskCacheStreamIO() |
|
213 { |
|
214 nsCacheService::AssertOwnsLock(); |
|
215 |
|
216 // Close the outputstream |
|
217 if (mBinding && mOutputStreamIsOpen) { |
|
218 (void)CloseOutputStream(); |
|
219 } |
|
220 |
|
221 // release "death grip" on cache service |
|
222 nsCacheService *service = nsCacheService::GlobalInstance(); |
|
223 NS_RELEASE(service); |
|
224 |
|
225 // assert streams closed |
|
226 NS_ASSERTION(!mOutputStreamIsOpen, "output stream still open"); |
|
227 NS_ASSERTION(mInStreamCount == 0, "input stream still open"); |
|
228 NS_ASSERTION(!mFD, "file descriptor not closed"); |
|
229 |
|
230 DeleteBuffer(); |
|
231 } |
|
232 |
|
233 |
|
234 // NOTE: called with service lock held |
|
235 nsresult |
|
236 nsDiskCacheStreamIO::GetInputStream(uint32_t offset, nsIInputStream ** inputStream) |
|
237 { |
|
238 NS_ENSURE_ARG_POINTER(inputStream); |
|
239 NS_ENSURE_TRUE(offset == 0, NS_ERROR_NOT_IMPLEMENTED); |
|
240 |
|
241 *inputStream = nullptr; |
|
242 |
|
243 if (!mBinding) return NS_ERROR_NOT_AVAILABLE; |
|
244 |
|
245 if (mOutputStreamIsOpen) { |
|
246 NS_WARNING("already have an output stream open"); |
|
247 return NS_ERROR_NOT_AVAILABLE; |
|
248 } |
|
249 |
|
250 nsresult rv; |
|
251 PRFileDesc * fd = nullptr; |
|
252 |
|
253 mStreamEnd = mBinding->mCacheEntry->DataSize(); |
|
254 if (mStreamEnd == 0) { |
|
255 // there's no data to read |
|
256 NS_ASSERTION(!mBinding->mRecord.DataLocationInitialized(), "storage allocated for zero data size"); |
|
257 } else if (mBinding->mRecord.DataFile() == 0) { |
|
258 // open file desc for data |
|
259 rv = OpenCacheFile(PR_RDONLY, &fd); |
|
260 if (NS_FAILED(rv)) return rv; // unable to open file |
|
261 NS_ASSERTION(fd, "cache stream lacking open file."); |
|
262 |
|
263 } else if (!mBuffer) { |
|
264 // read block file for data |
|
265 rv = ReadCacheBlocks(mStreamEnd); |
|
266 if (NS_FAILED(rv)) return rv; |
|
267 } |
|
268 // else, mBuffer already contains all of the data (left over from a |
|
269 // previous block-file read or write). |
|
270 |
|
271 NS_ASSERTION(!(fd && mBuffer), "ambiguous data sources for input stream"); |
|
272 |
|
273 // create a new input stream |
|
274 nsDiskCacheInputStream * inStream = new nsDiskCacheInputStream(this, fd, mBuffer, mStreamEnd); |
|
275 if (!inStream) return NS_ERROR_OUT_OF_MEMORY; |
|
276 |
|
277 NS_ADDREF(*inputStream = inStream); |
|
278 return NS_OK; |
|
279 } |
|
280 |
|
281 |
|
282 // NOTE: called with service lock held |
|
283 nsresult |
|
284 nsDiskCacheStreamIO::GetOutputStream(uint32_t offset, nsIOutputStream ** outputStream) |
|
285 { |
|
286 NS_ENSURE_ARG_POINTER(outputStream); |
|
287 *outputStream = nullptr; |
|
288 |
|
289 if (!mBinding) return NS_ERROR_NOT_AVAILABLE; |
|
290 |
|
291 NS_ASSERTION(!mOutputStreamIsOpen, "already have an output stream open"); |
|
292 NS_ASSERTION(mInStreamCount == 0, "we already have input streams open"); |
|
293 if (mOutputStreamIsOpen || mInStreamCount) return NS_ERROR_NOT_AVAILABLE; |
|
294 |
|
295 mStreamEnd = mBinding->mCacheEntry->DataSize(); |
|
296 |
|
297 // Inits file or buffer and truncate at the desired offset |
|
298 nsresult rv = SeekAndTruncate(offset); |
|
299 if (NS_FAILED(rv)) return rv; |
|
300 |
|
301 mOutputStreamIsOpen = true; |
|
302 NS_ADDREF(*outputStream = this); |
|
303 return NS_OK; |
|
304 } |
|
305 |
|
306 nsresult |
|
307 nsDiskCacheStreamIO::ClearBinding() |
|
308 { |
|
309 nsresult rv = NS_OK; |
|
310 if (mBinding && mOutputStreamIsOpen) |
|
311 rv = CloseOutputStream(); |
|
312 mBinding = nullptr; |
|
313 return rv; |
|
314 } |
|
315 |
|
316 NS_IMETHODIMP |
|
317 nsDiskCacheStreamIO::Close() |
|
318 { |
|
319 if (!mOutputStreamIsOpen) return NS_OK; |
|
320 |
|
321 mozilla::TimeStamp start = mozilla::TimeStamp::Now(); |
|
322 |
|
323 // grab service lock |
|
324 nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHESTREAMIO_CLOSEOUTPUTSTREAM)); |
|
325 |
|
326 if (!mBinding) { // if we're severed, just clear member variables |
|
327 mOutputStreamIsOpen = false; |
|
328 return NS_ERROR_NOT_AVAILABLE; |
|
329 } |
|
330 |
|
331 nsresult rv = CloseOutputStream(); |
|
332 if (NS_FAILED(rv)) |
|
333 NS_WARNING("CloseOutputStream() failed"); |
|
334 |
|
335 mozilla::Telemetry::ID id; |
|
336 if (NS_IsMainThread()) |
|
337 id = mozilla::Telemetry::NETWORK_DISK_CACHE_STREAMIO_CLOSE_MAIN_THREAD; |
|
338 else |
|
339 id = mozilla::Telemetry::NETWORK_DISK_CACHE_STREAMIO_CLOSE; |
|
340 mozilla::Telemetry::AccumulateTimeDelta(id, start); |
|
341 |
|
342 return rv; |
|
343 } |
|
344 |
|
345 nsresult |
|
346 nsDiskCacheStreamIO::CloseOutputStream() |
|
347 { |
|
348 NS_ASSERTION(mBinding, "oops"); |
|
349 |
|
350 CACHE_LOG_DEBUG(("CACHE: CloseOutputStream [%x doomed=%u]\n", |
|
351 mBinding->mRecord.HashNumber(), mBinding->mDoomed)); |
|
352 |
|
353 // Mark outputstream as closed, even if saving the stream fails |
|
354 mOutputStreamIsOpen = false; |
|
355 |
|
356 // When writing to a file, just close the file |
|
357 if (mFD) { |
|
358 (void) PR_Close(mFD); |
|
359 mFD = nullptr; |
|
360 return NS_OK; |
|
361 } |
|
362 |
|
363 // write data to cache blocks, or flush mBuffer to file |
|
364 NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "stream is bigger than buffer"); |
|
365 |
|
366 nsDiskCacheMap *cacheMap = mDevice->CacheMap(); // get map reference |
|
367 nsDiskCacheRecord * record = &mBinding->mRecord; |
|
368 nsresult rv = NS_OK; |
|
369 |
|
370 // delete existing storage |
|
371 if (record->DataLocationInitialized()) { |
|
372 rv = cacheMap->DeleteStorage(record, nsDiskCache::kData); |
|
373 NS_ENSURE_SUCCESS(rv, rv); |
|
374 |
|
375 // Only call UpdateRecord when there is no data to write, |
|
376 // because WriteDataCacheBlocks / FlushBufferToFile calls it. |
|
377 if ((mStreamEnd == 0) && (!mBinding->mDoomed)) { |
|
378 rv = cacheMap->UpdateRecord(record); |
|
379 if (NS_FAILED(rv)) { |
|
380 NS_WARNING("cacheMap->UpdateRecord() failed."); |
|
381 return rv; // XXX doom cache entry |
|
382 } |
|
383 } |
|
384 } |
|
385 |
|
386 if (mStreamEnd == 0) return NS_OK; // nothing to write |
|
387 |
|
388 // try to write to the cache blocks |
|
389 rv = cacheMap->WriteDataCacheBlocks(mBinding, mBuffer, mStreamEnd); |
|
390 if (NS_FAILED(rv)) { |
|
391 NS_WARNING("WriteDataCacheBlocks() failed."); |
|
392 |
|
393 // failed to store in cacheblocks, save as separate file |
|
394 rv = FlushBufferToFile(); // initializes DataFileLocation() if necessary |
|
395 if (mFD) { |
|
396 UpdateFileSize(); |
|
397 (void) PR_Close(mFD); |
|
398 mFD = nullptr; |
|
399 } |
|
400 else |
|
401 NS_WARNING("no file descriptor"); |
|
402 } |
|
403 |
|
404 return rv; |
|
405 } |
|
406 |
|
407 |
|
408 // assumptions: |
|
409 // only one thread writing at a time |
|
410 // never have both output and input streams open |
|
411 // OnDataSizeChanged() will have already been called to update entry->DataSize() |
|
412 |
|
413 NS_IMETHODIMP |
|
414 nsDiskCacheStreamIO::Write( const char * buffer, |
|
415 uint32_t count, |
|
416 uint32_t * bytesWritten) |
|
417 { |
|
418 NS_ENSURE_ARG_POINTER(buffer); |
|
419 NS_ENSURE_ARG_POINTER(bytesWritten); |
|
420 if (!mOutputStreamIsOpen) return NS_BASE_STREAM_CLOSED; |
|
421 |
|
422 *bytesWritten = 0; // always initialize to zero in case of errors |
|
423 |
|
424 NS_ASSERTION(count, "Write called with count of zero"); |
|
425 if (count == 0) { |
|
426 return NS_OK; // nothing to write |
|
427 } |
|
428 |
|
429 // grab service lock |
|
430 nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHESTREAMIO_WRITE)); |
|
431 if (!mBinding) return NS_ERROR_NOT_AVAILABLE; |
|
432 |
|
433 if (mInStreamCount) { |
|
434 // we have open input streams already |
|
435 // this is an error until we support overlapped I/O |
|
436 NS_WARNING("Attempting to write to cache entry with open input streams.\n"); |
|
437 return NS_ERROR_NOT_AVAILABLE; |
|
438 } |
|
439 |
|
440 // Not writing to file, and it will fit in the cachedatablocks? |
|
441 if (!mFD && (mStreamEnd + count <= kMaxBufferSize)) { |
|
442 |
|
443 // We have more data than the current buffer size? |
|
444 if ((mStreamEnd + count > mBufSize) && (mBufSize < kMaxBufferSize)) { |
|
445 // Increase buffer to the maximum size. |
|
446 mBuffer = (char *) moz_xrealloc(mBuffer, kMaxBufferSize); |
|
447 mBufSize = kMaxBufferSize; |
|
448 } |
|
449 |
|
450 // Store in the buffer but only if it fits |
|
451 if (mStreamEnd + count <= mBufSize) { |
|
452 memcpy(mBuffer + mStreamEnd, buffer, count); |
|
453 mStreamEnd += count; |
|
454 *bytesWritten = count; |
|
455 return NS_OK; |
|
456 } |
|
457 } |
|
458 |
|
459 // There are more bytes than fit in the buffer/cacheblocks, switch to file |
|
460 if (!mFD) { |
|
461 // Opens a cache file and write the buffer to it |
|
462 nsresult rv = FlushBufferToFile(); |
|
463 if (NS_FAILED(rv)) { |
|
464 return rv; |
|
465 } |
|
466 } |
|
467 // Write directly to the file |
|
468 if (PR_Write(mFD, buffer, count) != (int32_t)count) { |
|
469 NS_WARNING("failed to write all data"); |
|
470 return NS_ERROR_UNEXPECTED; // NS_ErrorAccordingToNSPR() |
|
471 } |
|
472 mStreamEnd += count; |
|
473 *bytesWritten = count; |
|
474 |
|
475 UpdateFileSize(); |
|
476 NS_ASSERTION(mBinding->mCacheEntry->DataSize() == mStreamEnd, "bad stream"); |
|
477 |
|
478 return NS_OK; |
|
479 } |
|
480 |
|
481 |
|
482 void |
|
483 nsDiskCacheStreamIO::UpdateFileSize() |
|
484 { |
|
485 NS_ASSERTION(mFD, "nsDiskCacheStreamIO::UpdateFileSize should not have been called"); |
|
486 |
|
487 nsDiskCacheRecord * record = &mBinding->mRecord; |
|
488 const uint32_t oldSizeK = record->DataFileSize(); |
|
489 uint32_t newSizeK = (mStreamEnd + 0x03FF) >> 10; |
|
490 |
|
491 // make sure the size won't overflow (bug #651100) |
|
492 if (newSizeK > kMaxDataSizeK) |
|
493 newSizeK = kMaxDataSizeK; |
|
494 |
|
495 if (newSizeK == oldSizeK) return; |
|
496 |
|
497 record->SetDataFileSize(newSizeK); |
|
498 |
|
499 // update cache size totals |
|
500 nsDiskCacheMap * cacheMap = mDevice->CacheMap(); |
|
501 cacheMap->DecrementTotalSize(oldSizeK); // decrement old size |
|
502 cacheMap->IncrementTotalSize(newSizeK); // increment new size |
|
503 |
|
504 if (!mBinding->mDoomed) { |
|
505 nsresult rv = cacheMap->UpdateRecord(record); |
|
506 if (NS_FAILED(rv)) { |
|
507 NS_WARNING("cacheMap->UpdateRecord() failed."); |
|
508 // XXX doom cache entry? |
|
509 } |
|
510 } |
|
511 } |
|
512 |
|
513 |
|
514 nsresult |
|
515 nsDiskCacheStreamIO::OpenCacheFile(int flags, PRFileDesc ** fd) |
|
516 { |
|
517 NS_ENSURE_ARG_POINTER(fd); |
|
518 |
|
519 CACHE_LOG_DEBUG(("nsDiskCacheStreamIO::OpenCacheFile")); |
|
520 |
|
521 nsresult rv; |
|
522 nsDiskCacheMap * cacheMap = mDevice->CacheMap(); |
|
523 nsCOMPtr<nsIFile> localFile; |
|
524 |
|
525 rv = cacheMap->GetLocalFileForDiskCacheRecord(&mBinding->mRecord, |
|
526 nsDiskCache::kData, |
|
527 !!(flags & PR_CREATE_FILE), |
|
528 getter_AddRefs(localFile)); |
|
529 if (NS_FAILED(rv)) return rv; |
|
530 |
|
531 // create PRFileDesc for input stream - the 00600 is just for consistency |
|
532 return localFile->OpenNSPRFileDesc(flags, 00600, fd); |
|
533 } |
|
534 |
|
535 |
|
536 nsresult |
|
537 nsDiskCacheStreamIO::ReadCacheBlocks(uint32_t bufferSize) |
|
538 { |
|
539 mozilla::eventtracer::AutoEventTracer readCacheBlocks( |
|
540 mBinding->mCacheEntry, |
|
541 mozilla::eventtracer::eExec, |
|
542 mozilla::eventtracer::eDone, |
|
543 "net::cache::ReadCacheBlocks"); |
|
544 |
|
545 NS_ASSERTION(mStreamEnd == mBinding->mCacheEntry->DataSize(), "bad stream"); |
|
546 NS_ASSERTION(bufferSize <= kMaxBufferSize, "bufferSize too large for buffer"); |
|
547 NS_ASSERTION(mStreamEnd <= bufferSize, "data too large for buffer"); |
|
548 |
|
549 nsDiskCacheRecord * record = &mBinding->mRecord; |
|
550 if (!record->DataLocationInitialized()) return NS_OK; |
|
551 |
|
552 NS_ASSERTION(record->DataFile() != kSeparateFile, "attempt to read cache blocks on separate file"); |
|
553 |
|
554 if (!mBuffer) { |
|
555 mBuffer = (char *) moz_xmalloc(bufferSize); |
|
556 mBufSize = bufferSize; |
|
557 } |
|
558 |
|
559 // read data stored in cache block files |
|
560 nsDiskCacheMap *map = mDevice->CacheMap(); // get map reference |
|
561 return map->ReadDataCacheBlocks(mBinding, mBuffer, mStreamEnd); |
|
562 } |
|
563 |
|
564 |
|
565 nsresult |
|
566 nsDiskCacheStreamIO::FlushBufferToFile() |
|
567 { |
|
568 mozilla::eventtracer::AutoEventTracer flushBufferToFile( |
|
569 mBinding->mCacheEntry, |
|
570 mozilla::eventtracer::eExec, |
|
571 mozilla::eventtracer::eDone, |
|
572 "net::cache::FlushBufferToFile"); |
|
573 |
|
574 nsresult rv; |
|
575 nsDiskCacheRecord * record = &mBinding->mRecord; |
|
576 |
|
577 if (!mFD) { |
|
578 if (record->DataLocationInitialized() && (record->DataFile() > 0)) { |
|
579 // remove cache block storage |
|
580 nsDiskCacheMap * cacheMap = mDevice->CacheMap(); |
|
581 rv = cacheMap->DeleteStorage(record, nsDiskCache::kData); |
|
582 if (NS_FAILED(rv)) return rv; |
|
583 } |
|
584 record->SetDataFileGeneration(mBinding->mGeneration); |
|
585 |
|
586 // allocate file |
|
587 rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD); |
|
588 if (NS_FAILED(rv)) return rv; |
|
589 |
|
590 int64_t dataSize = mBinding->mCacheEntry->PredictedDataSize(); |
|
591 if (dataSize != -1) |
|
592 mozilla::fallocate(mFD, std::min<int64_t>(dataSize, kPreallocateLimit)); |
|
593 } |
|
594 |
|
595 // write buffer to the file when there is data in it |
|
596 if (mStreamEnd > 0) { |
|
597 if (!mBuffer) { |
|
598 NS_RUNTIMEABORT("Fix me!"); |
|
599 } |
|
600 if (PR_Write(mFD, mBuffer, mStreamEnd) != (int32_t)mStreamEnd) { |
|
601 NS_WARNING("failed to flush all data"); |
|
602 return NS_ERROR_UNEXPECTED; // NS_ErrorAccordingToNSPR() |
|
603 } |
|
604 } |
|
605 |
|
606 // buffer is no longer valid |
|
607 DeleteBuffer(); |
|
608 |
|
609 return NS_OK; |
|
610 } |
|
611 |
|
612 |
|
613 void |
|
614 nsDiskCacheStreamIO::DeleteBuffer() |
|
615 { |
|
616 if (mBuffer) { |
|
617 free(mBuffer); |
|
618 mBuffer = nullptr; |
|
619 mBufSize = 0; |
|
620 } |
|
621 } |
|
622 |
|
623 size_t |
|
624 nsDiskCacheStreamIO::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) |
|
625 { |
|
626 size_t usage = aMallocSizeOf(this); |
|
627 |
|
628 usage += aMallocSizeOf(mFD); |
|
629 usage += aMallocSizeOf(mBuffer); |
|
630 |
|
631 return usage; |
|
632 } |
|
633 |
|
634 nsresult |
|
635 nsDiskCacheStreamIO::SeekAndTruncate(uint32_t offset) |
|
636 { |
|
637 if (!mBinding) return NS_ERROR_NOT_AVAILABLE; |
|
638 |
|
639 if (uint32_t(offset) > mStreamEnd) return NS_ERROR_FAILURE; |
|
640 |
|
641 // Set the current end to the desired offset |
|
642 mStreamEnd = offset; |
|
643 |
|
644 // Currently stored in file? |
|
645 if (mBinding->mRecord.DataLocationInitialized() && |
|
646 (mBinding->mRecord.DataFile() == 0)) { |
|
647 if (!mFD) { |
|
648 // we need an mFD, we better open it now |
|
649 nsresult rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD); |
|
650 if (NS_FAILED(rv)) return rv; |
|
651 } |
|
652 if (offset) { |
|
653 if (PR_Seek(mFD, offset, PR_SEEK_SET) == -1) |
|
654 return NS_ErrorAccordingToNSPR(); |
|
655 } |
|
656 nsDiskCache::Truncate(mFD, offset); |
|
657 UpdateFileSize(); |
|
658 |
|
659 // When we starting at zero again, close file and start with buffer. |
|
660 // If offset is non-zero (and within buffer) an option would be |
|
661 // to read the file into the buffer, but chance is high that it is |
|
662 // rewritten to the file anyway. |
|
663 if (offset == 0) { |
|
664 // close file descriptor |
|
665 (void) PR_Close(mFD); |
|
666 mFD = nullptr; |
|
667 } |
|
668 return NS_OK; |
|
669 } |
|
670 |
|
671 // read data into mBuffer if not read yet. |
|
672 if (offset && !mBuffer) { |
|
673 nsresult rv = ReadCacheBlocks(kMaxBufferSize); |
|
674 if (NS_FAILED(rv)) return rv; |
|
675 } |
|
676 |
|
677 // stream buffer sanity check |
|
678 NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "bad stream"); |
|
679 return NS_OK; |
|
680 } |
|
681 |
|
682 |
|
683 NS_IMETHODIMP |
|
684 nsDiskCacheStreamIO::Flush() |
|
685 { |
|
686 if (!mOutputStreamIsOpen) return NS_BASE_STREAM_CLOSED; |
|
687 return NS_OK; |
|
688 } |
|
689 |
|
690 |
|
691 NS_IMETHODIMP |
|
692 nsDiskCacheStreamIO::WriteFrom(nsIInputStream *inStream, uint32_t count, uint32_t *bytesWritten) |
|
693 { |
|
694 NS_NOTREACHED("WriteFrom"); |
|
695 return NS_ERROR_NOT_IMPLEMENTED; |
|
696 } |
|
697 |
|
698 |
|
699 NS_IMETHODIMP |
|
700 nsDiskCacheStreamIO::WriteSegments( nsReadSegmentFun reader, |
|
701 void * closure, |
|
702 uint32_t count, |
|
703 uint32_t * bytesWritten) |
|
704 { |
|
705 NS_NOTREACHED("WriteSegments"); |
|
706 return NS_ERROR_NOT_IMPLEMENTED; |
|
707 } |
|
708 |
|
709 |
|
710 NS_IMETHODIMP |
|
711 nsDiskCacheStreamIO::IsNonBlocking(bool * nonBlocking) |
|
712 { |
|
713 *nonBlocking = false; |
|
714 return NS_OK; |
|
715 } |