Follow-on fix for bug 457825. Use sheet principal for agent and user sheets. r=dbaron...
[wine-gecko.git] / netwerk / cache / src / nsDiskCacheStreams.cpp
blobc4eacbe02c5b3552fd0a8fe1ce5b474ede5f5c09
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is nsDiskCacheStreams.cpp, released
17 * June 13, 2001.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 2001
22 * the Initial Developer. All Rights Reserved.
24 * Contributor(s):
25 * Gordon Sheridan <gordon@netscape.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 #include "nsDiskCache.h"
43 #include "nsDiskCacheDevice.h"
44 #include "nsDiskCacheStreams.h"
45 #include "nsCacheService.h"
49 // Assumptions:
50 // - cache descriptors live for life of streams
51 // - streams will only be used by FileTransport,
52 // they will not be directly accessible to clients
53 // - overlapped I/O is NOT supported
56 /******************************************************************************
57 * nsDiskCacheInputStream
58 *****************************************************************************/
59 #ifdef XP_MAC
60 #pragma mark nsDiskCacheInputStream
61 #endif
62 class nsDiskCacheInputStream : public nsIInputStream {
64 public:
66 nsDiskCacheInputStream( nsDiskCacheStreamIO * parent,
67 PRFileDesc * fileDesc,
68 const char * buffer,
69 PRUint32 endOfStream);
71 virtual ~nsDiskCacheInputStream();
73 NS_DECL_ISUPPORTS
74 NS_DECL_NSIINPUTSTREAM
76 private:
77 nsDiskCacheStreamIO * mStreamIO; // backpointer to parent
78 PRFileDesc * mFD;
79 const char * mBuffer;
80 PRUint32 mStreamEnd;
81 PRUint32 mPos; // stream position
82 PRBool mClosed;
86 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDiskCacheInputStream, nsIInputStream)
89 nsDiskCacheInputStream::nsDiskCacheInputStream( nsDiskCacheStreamIO * parent,
90 PRFileDesc * fileDesc,
91 const char * buffer,
92 PRUint32 endOfStream)
93 : mStreamIO(parent)
94 , mFD(fileDesc)
95 , mBuffer(buffer)
96 , mStreamEnd(endOfStream)
97 , mPos(0)
98 , mClosed(PR_FALSE)
100 NS_ADDREF(mStreamIO);
101 mStreamIO->IncrementInputStreamCount();
105 nsDiskCacheInputStream::~nsDiskCacheInputStream()
107 Close();
108 mStreamIO->DecrementInputStreamCount();
109 NS_RELEASE(mStreamIO);
113 NS_IMETHODIMP
114 nsDiskCacheInputStream::Close()
116 if (!mClosed) {
117 if (mFD) {
118 (void) PR_Close(mFD);
119 mFD = nsnull;
121 mClosed = PR_TRUE;
123 return NS_OK;
127 NS_IMETHODIMP
128 nsDiskCacheInputStream::Available(PRUint32 * bytesAvailable)
130 if (mClosed) return NS_BASE_STREAM_CLOSED;
131 if (mStreamEnd < mPos) return NS_ERROR_UNEXPECTED;
133 *bytesAvailable = mStreamEnd - mPos;
134 return NS_OK;
138 NS_IMETHODIMP
139 nsDiskCacheInputStream::Read(char * buffer, PRUint32 count, PRUint32 * bytesRead)
141 *bytesRead = 0;
143 if (mClosed)
144 return NS_OK;
146 if (mPos == mStreamEnd) return NS_OK;
147 if (mPos > mStreamEnd) return NS_ERROR_UNEXPECTED;
149 if (mFD) {
150 // just read from file
151 PRInt32 result = PR_Read(mFD, buffer, count);
152 if (result < 0) return NS_ErrorAccordingToNSPR();
154 mPos += (PRUint32)result;
155 *bytesRead = (PRUint32)result;
157 } else if (mBuffer) {
158 // read data from mBuffer
159 if (count > mStreamEnd - mPos)
160 count = mStreamEnd - mPos;
162 memcpy(buffer, mBuffer + mPos, count);
163 mPos += count;
164 *bytesRead = count;
165 } else {
166 // no data source for input stream
169 return NS_OK;
173 NS_IMETHODIMP
174 nsDiskCacheInputStream::ReadSegments(nsWriteSegmentFun writer,
175 void * closure,
176 PRUint32 count,
177 PRUint32 * bytesRead)
179 return NS_ERROR_NOT_IMPLEMENTED;
183 NS_IMETHODIMP
184 nsDiskCacheInputStream::IsNonBlocking(PRBool * nonBlocking)
186 *nonBlocking = PR_FALSE;
187 return NS_OK;
191 /******************************************************************************
192 * nsDiskCacheOutputStream
193 *****************************************************************************/
194 #ifdef XP_MAC
195 #pragma mark -
196 #pragma mark nsDiskCacheOutputStream
197 #endif
198 class nsDiskCacheOutputStream : public nsIOutputStream {
199 public:
200 nsDiskCacheOutputStream( nsDiskCacheStreamIO * parent);
201 virtual ~nsDiskCacheOutputStream();
203 NS_DECL_ISUPPORTS
204 NS_DECL_NSIOUTPUTSTREAM
206 void ReleaseStreamIO() { NS_IF_RELEASE(mStreamIO); }
208 private:
209 nsDiskCacheStreamIO * mStreamIO; // backpointer to parent
210 PRBool mClosed;
214 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDiskCacheOutputStream,
215 nsIOutputStream)
217 nsDiskCacheOutputStream::nsDiskCacheOutputStream( nsDiskCacheStreamIO * parent)
218 : mStreamIO(parent)
219 , mClosed(PR_FALSE)
221 NS_ADDREF(mStreamIO);
225 nsDiskCacheOutputStream::~nsDiskCacheOutputStream()
227 Close();
228 ReleaseStreamIO();
232 NS_IMETHODIMP
233 nsDiskCacheOutputStream::Close()
235 if (!mClosed) {
236 mClosed = PR_TRUE;
237 // tell parent streamIO we are closing
238 mStreamIO->CloseOutputStream(this);
240 return NS_OK;
244 NS_IMETHODIMP
245 nsDiskCacheOutputStream::Flush()
247 if (mClosed) return NS_BASE_STREAM_CLOSED;
248 // yeah, yeah, well get to it...eventually...
249 return NS_OK;
253 NS_IMETHODIMP
254 nsDiskCacheOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *bytesWritten)
256 if (mClosed) return NS_BASE_STREAM_CLOSED;
257 return mStreamIO->Write(buf, count, bytesWritten);
261 NS_IMETHODIMP
262 nsDiskCacheOutputStream::WriteFrom(nsIInputStream *inStream, PRUint32 count, PRUint32 *bytesWritten)
264 NS_NOTREACHED("WriteFrom");
265 return NS_ERROR_NOT_IMPLEMENTED;
269 NS_IMETHODIMP
270 nsDiskCacheOutputStream::WriteSegments( nsReadSegmentFun reader,
271 void * closure,
272 PRUint32 count,
273 PRUint32 * bytesWritten)
275 NS_NOTREACHED("WriteSegments");
276 return NS_ERROR_NOT_IMPLEMENTED;
280 NS_IMETHODIMP
281 nsDiskCacheOutputStream::IsNonBlocking(PRBool * nonBlocking)
283 *nonBlocking = PR_FALSE;
284 return NS_OK;
289 /******************************************************************************
290 * nsDiskCacheStreamIO
291 *****************************************************************************/
292 #ifdef XP_MAC
293 #pragma mark -
294 #pragma mark nsDiskCacheStreamIO
295 #endif
297 NS_IMPL_THREADSAFE_ISUPPORTS0(nsDiskCacheStreamIO)
299 // we pick 16k as the max buffer size because that is the threshold above which
300 // we are unable to store the data in the cache block files
301 // see nsDiskCacheMap.[cpp,h]
302 #define kMaxBufferSize (16 * 1024)
304 nsDiskCacheStreamIO::nsDiskCacheStreamIO(nsDiskCacheBinding * binding)
305 : mBinding(binding)
306 , mOutStream(nsnull)
307 , mInStreamCount(0)
308 , mFD(nsnull)
309 , mStreamPos(0)
310 , mStreamEnd(0)
311 , mBufPos(0)
312 , mBufEnd(0)
313 , mBufSize(0)
314 , mBufDirty(PR_FALSE)
315 , mBuffer(nsnull)
317 mDevice = (nsDiskCacheDevice *)mBinding->mCacheEntry->CacheDevice();
319 // acquire "death grip" on cache service
320 nsCacheService *service = nsCacheService::GlobalInstance();
321 NS_ADDREF(service);
325 nsDiskCacheStreamIO::~nsDiskCacheStreamIO()
327 Close();
329 // release "death grip" on cache service
330 nsCacheService *service = nsCacheService::GlobalInstance();
331 NS_RELEASE(service);
335 void
336 nsDiskCacheStreamIO::Close()
338 // this should only be called from our destructor
339 // no one is interested in us anymore, so we don't need to grab any locks
341 // assert streams closed
342 NS_ASSERTION(!mOutStream, "output stream still open");
343 NS_ASSERTION(mInStreamCount == 0, "input stream still open");
344 NS_ASSERTION(!mFD, "file descriptor not closed");
346 DeleteBuffer();
350 // NOTE: called with service lock held
351 nsresult
352 nsDiskCacheStreamIO::GetInputStream(PRUint32 offset, nsIInputStream ** inputStream)
354 NS_ENSURE_ARG_POINTER(inputStream);
355 NS_ENSURE_TRUE(offset == 0, NS_ERROR_NOT_IMPLEMENTED);
357 *inputStream = nsnull;
359 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
361 if (mOutStream) {
362 NS_WARNING("already have an output stream open");
363 return NS_ERROR_NOT_AVAILABLE;
366 nsresult rv;
367 PRFileDesc * fd = nsnull;
369 mStreamEnd = mBinding->mCacheEntry->DataSize();
370 if (mStreamEnd == 0) {
371 // there's no data to read
372 NS_ASSERTION(!mBinding->mRecord.DataLocationInitialized(), "storage allocated for zero data size");
373 } else if (mBinding->mRecord.DataFile() == 0) {
374 // open file desc for data
375 rv = OpenCacheFile(PR_RDONLY, &fd);
376 if (NS_FAILED(rv)) return rv; // unable to open file
377 NS_ASSERTION(fd, "cache stream lacking open file.");
379 } else if (!mBuffer) {
380 // read block file for data
381 rv = ReadCacheBlocks();
382 if (NS_FAILED(rv)) return rv;
384 // else, mBuffer already contains all of the data (left over from a
385 // previous block-file read or write).
387 NS_ASSERTION(!(fd && mBuffer), "ambiguous data sources for input stream");
389 // create a new input stream
390 nsDiskCacheInputStream * inStream = new nsDiskCacheInputStream(this, fd, mBuffer, mStreamEnd);
391 if (!inStream) return NS_ERROR_OUT_OF_MEMORY;
393 NS_ADDREF(*inputStream = inStream);
394 return NS_OK;
398 // NOTE: called with service lock held
399 nsresult
400 nsDiskCacheStreamIO::GetOutputStream(PRUint32 offset, nsIOutputStream ** outputStream)
402 NS_ENSURE_ARG_POINTER(outputStream);
403 *outputStream = nsnull;
405 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
407 NS_ASSERTION(!mOutStream, "already have an output stream open");
408 NS_ASSERTION(mInStreamCount == 0, "we already have input streams open");
409 if (mOutStream || mInStreamCount) return NS_ERROR_NOT_AVAILABLE;
411 // mBuffer lazily allocated, but might exist if a previous stream already
412 // created one.
413 mBufPos = 0;
414 mStreamPos = 0;
415 mStreamEnd = mBinding->mCacheEntry->DataSize();
417 nsresult rv;
418 if (offset) {
419 rv = Seek(PR_SEEK_SET, offset);
420 if (NS_FAILED(rv)) return rv;
422 rv = SetEOF();
423 if (NS_FAILED(rv)) return rv;
425 // create a new output stream
426 mOutStream = new nsDiskCacheOutputStream(this);
427 if (!mOutStream) return NS_ERROR_OUT_OF_MEMORY;
429 NS_ADDREF(*outputStream = mOutStream);
430 return NS_OK;
433 void
434 nsDiskCacheStreamIO::ClearBinding()
436 if (mBinding && mOutStream)
437 Flush();
438 mBinding = nsnull;
441 nsresult
442 nsDiskCacheStreamIO::CloseOutputStream(nsDiskCacheOutputStream * outputStream)
444 nsCacheServiceAutoLock lock; // grab service lock
445 nsresult rv;
447 if (outputStream != mOutStream) {
448 NS_WARNING("mismatched output streams");
449 return NS_ERROR_UNEXPECTED;
452 // output stream is closing
453 if (!mBinding) { // if we're severed, just clear member variables
454 NS_ASSERTION(!mBufDirty, "oops");
455 mOutStream = nsnull;
456 outputStream->ReleaseStreamIO();
457 return NS_ERROR_NOT_AVAILABLE;
460 rv = Flush();
461 NS_ASSERTION(NS_SUCCEEDED(rv), "Flush() failed");
463 mOutStream = nsnull;
464 return rv;
467 nsresult
468 nsDiskCacheStreamIO::Flush()
470 NS_ASSERTION(mBinding, "oops");
472 CACHE_LOG_DEBUG(("CACHE: Flush [%x doomed=%u]\n",
473 mBinding->mRecord.HashNumber(), mBinding->mDoomed));
475 if (!mBufDirty)
476 return NS_OK;
478 // write data to cache blocks, or flush mBuffer to file
479 nsDiskCacheMap *cacheMap = mDevice->CacheMap(); // get map reference
480 nsresult rv;
482 if ((mStreamEnd > kMaxBufferSize) ||
483 (mBinding->mCacheEntry->StoragePolicy() == nsICache::STORE_ON_DISK_AS_FILE)) {
484 // make sure we save as separate file
485 rv = FlushBufferToFile(); // will initialize DataFileLocation() if necessary
487 if (mFD) {
488 // Update the file size of the disk file in the cache
489 UpdateFileSize();
491 // close file descriptor
492 (void) PR_Close(mFD);
493 mFD = nsnull;
495 else
496 NS_WARNING("no file descriptor");
498 // close mFD first if possible before returning if FlushBufferToFile
499 // failed
500 NS_ENSURE_SUCCESS(rv, rv);
502 // since the data location is on disk as a single file, the only value
503 // in keeping mBuffer around is to avoid an extra malloc the next time
504 // we need to write to this file. reading will use a file descriptor.
505 // therefore, it's probably not worth optimizing for the subsequent
506 // write, so we unconditionally delete mBuffer here.
507 DeleteBuffer();
509 } else {
510 // store data (if any) in cache block files
512 // delete existing storage
513 nsDiskCacheRecord * record = &mBinding->mRecord;
514 if (record->DataLocationInitialized()) {
515 rv = cacheMap->DeleteStorage(record, nsDiskCache::kData);
516 if (NS_FAILED(rv)) {
517 NS_WARNING("cacheMap->DeleteStorage() failed.");
518 cacheMap->DeleteRecord(record);
519 return rv;
523 // flush buffer to block files
524 if (mStreamEnd > 0) {
525 rv = cacheMap->WriteDataCacheBlocks(mBinding, mBuffer, mBufEnd);
526 if (NS_FAILED(rv)) {
527 NS_WARNING("WriteDataCacheBlocks() failed.");
528 return rv; // XXX doom cache entry?
533 mBufDirty = PR_FALSE;
536 // XXX do we need this here? WriteDataCacheBlocks() calls UpdateRecord()
537 // update cache map if entry isn't doomed
538 if (!mBinding->mDoomed) {
539 rv = cacheMap->UpdateRecord(&mBinding->mRecord);
540 if (NS_FAILED(rv)) {
541 NS_WARNING("cacheMap->UpdateRecord() failed.");
542 return rv; // XXX doom cache entry
546 return NS_OK;
550 // assumptions:
551 // only one thread writing at a time
552 // never have both output and input streams open
553 // OnDataSizeChanged() will have already been called to update entry->DataSize()
555 nsresult
556 nsDiskCacheStreamIO::Write( const char * buffer,
557 PRUint32 count,
558 PRUint32 * bytesWritten)
560 nsresult rv = NS_OK;
561 nsCacheServiceAutoLock lock; // grab service lock
562 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
564 if (mInStreamCount) {
565 // we have open input streams already
566 // this is an error until we support overlapped I/O
567 NS_WARNING("Attempting to write to cache entry with open input streams.\n");
568 return NS_ERROR_NOT_AVAILABLE;
571 NS_ASSERTION(count, "Write called with count of zero");
572 NS_ASSERTION(mBufPos <= mBufEnd, "streamIO buffer corrupted");
574 PRUint32 bytesLeft = count;
575 PRBool flushed = PR_FALSE;
577 while (bytesLeft) {
578 if (mBufPos == mBufSize) {
579 if (mBufSize < kMaxBufferSize) {
580 mBufSize = kMaxBufferSize;
581 mBuffer = (char *) realloc(mBuffer, mBufSize);
582 if (!mBuffer) {
583 mBufSize = 0;
584 break;
586 } else {
587 nsresult rv = FlushBufferToFile();
588 if (NS_FAILED(rv)) break;
589 flushed = PR_TRUE;
593 PRUint32 chunkSize = bytesLeft;
594 if (chunkSize > (mBufSize - mBufPos))
595 chunkSize = mBufSize - mBufPos;
597 memcpy(mBuffer + mBufPos, buffer, chunkSize);
598 mBufDirty = PR_TRUE;
599 mBufPos += chunkSize;
600 bytesLeft -= chunkSize;
601 buffer += chunkSize;
603 if (mBufEnd < mBufPos)
604 mBufEnd = mBufPos;
606 if (bytesLeft) {
607 *bytesWritten = 0;
608 return NS_ERROR_FAILURE;
610 *bytesWritten = count;
612 // update mStreamPos, mStreamEnd
613 mStreamPos += count;
614 if (mStreamEnd < mStreamPos) {
615 mStreamEnd = mStreamPos;
616 NS_ASSERTION(mBinding->mCacheEntry->DataSize() == mStreamEnd, "bad stream");
618 // If we have flushed to a file, update the file size
619 if (flushed && mFD) {
620 UpdateFileSize();
624 return rv;
628 void
629 nsDiskCacheStreamIO::UpdateFileSize()
631 NS_ASSERTION(mFD, "nsDiskCacheStreamIO::UpdateFileSize should not have been called");
633 nsDiskCacheRecord * record = &mBinding->mRecord;
634 const PRUint32 oldSizeK = record->DataFileSize();
635 const PRUint32 newSizeK = (mStreamEnd + 0x03FF) >> 10;
637 if (newSizeK == oldSizeK) return;
639 record->SetDataFileSize(newSizeK);
641 // update cache size totals
642 nsDiskCacheMap * cacheMap = mDevice->CacheMap();
643 cacheMap->DecrementTotalSize(oldSizeK); // decrement old size
644 cacheMap->IncrementTotalSize(newSizeK); // increment new size
646 if (!mBinding->mDoomed) {
647 nsresult rv = cacheMap->UpdateRecord(record);
648 if (NS_FAILED(rv)) {
649 NS_WARNING("cacheMap->UpdateRecord() failed.");
650 // XXX doom cache entry?
656 nsresult
657 nsDiskCacheStreamIO::OpenCacheFile(PRIntn flags, PRFileDesc ** fd)
659 NS_ENSURE_ARG_POINTER(fd);
661 nsresult rv;
662 nsDiskCacheMap * cacheMap = mDevice->CacheMap();
664 rv = cacheMap->GetLocalFileForDiskCacheRecord(&mBinding->mRecord,
665 nsDiskCache::kData,
666 getter_AddRefs(mLocalFile));
667 if (NS_FAILED(rv)) return rv;
669 // create PRFileDesc for input stream - the 00600 is just for consistency
670 rv = mLocalFile->OpenNSPRFileDesc(flags, 00600, fd);
671 if (NS_FAILED(rv)) return rv; // unable to open file
673 return NS_OK;
677 nsresult
678 nsDiskCacheStreamIO::ReadCacheBlocks()
680 NS_ASSERTION(mStreamEnd == mBinding->mCacheEntry->DataSize(), "bad stream");
681 NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "data too large for buffer");
683 nsDiskCacheRecord * record = &mBinding->mRecord;
684 if (!record->DataLocationInitialized()) return NS_OK;
686 NS_ASSERTION(record->DataFile() != kSeparateFile, "attempt to read cache blocks on separate file");
688 if (!mBuffer) {
689 // allocate buffer
690 mBuffer = (char *) malloc(mStreamEnd);
691 if (!mBuffer) {
692 return NS_ERROR_OUT_OF_MEMORY;
694 mBufSize = mStreamEnd;
697 // read data stored in cache block files
698 nsDiskCacheMap *map = mDevice->CacheMap(); // get map reference
699 nsresult rv = map->ReadDataCacheBlocks(mBinding, mBuffer, mStreamEnd);
700 if (NS_FAILED(rv)) return rv;
702 // update streamIO variables
703 mBufPos = 0;
704 mBufEnd = mStreamEnd;
706 return NS_OK;
710 nsresult
711 nsDiskCacheStreamIO::FlushBufferToFile()
713 nsresult rv;
714 nsDiskCacheRecord * record = &mBinding->mRecord;
716 if (!mFD) {
717 if (record->DataLocationInitialized() && (record->DataFile() > 0)) {
718 // remove cache block storage
719 nsDiskCacheMap * cacheMap = mDevice->CacheMap();
720 rv = cacheMap->DeleteStorage(record, nsDiskCache::kData);
721 if (NS_FAILED(rv)) return rv;
723 record->SetDataFileGeneration(mBinding->mGeneration);
725 // allocate file
726 rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
727 if (NS_FAILED(rv)) return rv;
730 // write buffer
731 PRInt32 bytesWritten = PR_Write(mFD, mBuffer, mBufEnd);
732 if (PRUint32(bytesWritten) != mBufEnd) {
733 NS_WARNING("failed to flush all data");
734 return NS_ERROR_UNEXPECTED; // NS_ErrorAccordingToNSPR()
736 mBufDirty = PR_FALSE;
738 // reset buffer
739 mBufPos = 0;
740 mBufEnd = 0;
742 return NS_OK;
746 void
747 nsDiskCacheStreamIO::DeleteBuffer()
749 if (mBuffer) {
750 NS_ASSERTION(mBufDirty == PR_FALSE, "deleting dirty buffer");
751 free(mBuffer);
752 mBuffer = nsnull;
753 mBufPos = 0;
754 mBufEnd = 0;
755 mBufSize = 0;
760 // NOTE: called with service lock held
761 nsresult
762 nsDiskCacheStreamIO::Seek(PRInt32 whence, PRInt32 offset)
764 PRInt32 newPos;
765 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
767 if (PRUint32(offset) > mStreamEnd) return NS_ERROR_FAILURE;
769 if (mBinding->mRecord.DataLocationInitialized()) {
770 if (mBinding->mRecord.DataFile() == 0) {
771 if (!mFD) {
772 // we need an mFD, we better open it now
773 nsresult rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
774 if (NS_FAILED(rv)) return rv;
779 if (mFD) {
780 // do we have data in the buffer that needs to be flushed?
781 if (mBufDirty) {
782 // XXX optimization: are we just moving within the current buffer?
783 nsresult rv = FlushBufferToFile();
784 if (NS_FAILED(rv)) return rv;
787 newPos = PR_Seek(mFD, offset, (PRSeekWhence)whence);
788 if (newPos == -1)
789 return NS_ErrorAccordingToNSPR();
791 mStreamPos = (PRUint32) newPos;
792 mBufPos = 0;
793 mBufEnd = 0;
794 return NS_OK;
797 // else, seek in mBuffer
799 switch(whence) {
800 case PR_SEEK_SET:
801 newPos = offset;
802 break;
804 case PR_SEEK_CUR: // relative from current posistion
805 newPos = offset + (PRUint32)mStreamPos;
806 break;
808 case PR_SEEK_END: // relative from end
809 newPos = offset + (PRUint32)mBufEnd;
810 break;
812 default:
813 return NS_ERROR_INVALID_ARG;
816 // read data into mBuffer if not read yet.
817 if (mStreamEnd && !mBufEnd) {
818 if (newPos > 0) {
819 nsresult rv = ReadCacheBlocks();
820 if (NS_FAILED(rv)) return rv;
824 // stream buffer sanity checks
825 NS_ASSERTION(mBufEnd <= kMaxBufferSize, "bad stream");
826 NS_ASSERTION(mBufPos <= mBufEnd, "bad stream");
827 NS_ASSERTION(mStreamPos == mBufPos, "bad stream");
828 NS_ASSERTION(mStreamEnd == mBufEnd, "bad stream");
830 if ((newPos < 0) || (PRUint32(newPos) > mBufEnd)) {
831 NS_WARNING("seek offset out of range");
832 return NS_ERROR_INVALID_ARG;
835 mStreamPos = newPos;
836 mBufPos = newPos;
837 return NS_OK;
841 // called only from nsDiskCacheOutputStream::Tell
842 nsresult
843 nsDiskCacheStreamIO::Tell(PRUint32 * result)
845 NS_ENSURE_ARG_POINTER(result);
846 *result = mStreamPos;
847 return NS_OK;
851 // NOTE: called with service lock held
852 nsresult
853 nsDiskCacheStreamIO::SetEOF()
855 nsresult rv;
856 PRBool needToCloseFD = PR_FALSE;
858 NS_ASSERTION(mStreamPos <= mStreamEnd, "bad stream");
859 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
861 if (mBinding->mRecord.DataLocationInitialized()) {
862 if (mBinding->mRecord.DataFile() == 0) {
863 if (!mFD) {
864 // we need an mFD, we better open it now
865 rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
866 if (NS_FAILED(rv)) return rv;
867 needToCloseFD = PR_TRUE;
869 } else {
870 // data in cache block files
871 if ((mStreamPos != 0) && (mStreamPos != mBufPos)) {
872 // only read data if there will be some left after truncation
873 rv = ReadCacheBlocks();
874 if (NS_FAILED(rv)) return rv;
877 // We need to make sure we reflect this change in Flush().
878 // In particular, if mStreamPos is 0 and we never write to
879 // the buffer, we want the storage to be deleted.
880 mBufDirty = PR_TRUE;
884 if (mFD) {
885 rv = nsDiskCache::Truncate(mFD, mStreamPos);
886 #ifdef DEBUG
887 PRUint32 oldSizeK = (mStreamEnd + 0x03FF) >> 10;
888 NS_ASSERTION(mBinding->mRecord.DataFileSize() == oldSizeK, "bad disk cache entry size");
889 } else {
890 // data stored in buffer.
891 NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "buffer truncation inadequate");
892 NS_ASSERTION(mBufPos == mStreamPos, "bad stream");
893 NS_ASSERTION(mBuffer ? mBufEnd == mStreamEnd : PR_TRUE, "bad stream");
894 #endif
897 NS_ASSERTION(mStreamEnd == mBinding->mCacheEntry->DataSize(), "cache entry not updated");
898 // we expect nsCacheEntryDescriptor::TransportWrapper::OpenOutputStream()
899 // to eventually update the cache entry
901 mStreamEnd = mStreamPos;
902 mBufEnd = mBufPos;
904 if (mFD) {
905 UpdateFileSize();
906 if (needToCloseFD) {
907 (void) PR_Close(mFD);
908 mFD = nsnull;
912 return NS_OK;