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