LCOV - app.info - netwerk/cache/nsDiskCacheStreams.cpp

LCOV - code coverage report
| Current view: | directory - netwerk/cache - nsDiskCacheStreams.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 408 | 336 | 82.4 %
| Date: | 2012-04-21 | Functions: | 47 | 39 | 83.0 %

           1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
           2                 :  *
           3                 :  * ***** BEGIN LICENSE BLOCK *****
           4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           5                 :  *
           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/
          10                 :  *
          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.
          15                 :  *
          16                 :  * The Original Code is nsDiskCacheStreams.cpp, released
          17                 :  * June 13, 2001.
          18                 :  *
          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.
          23                 :  *
          24                 :  * Contributor(s):
          25                 :  *   Gordon Sheridan <gordon@netscape.com>
          26                 :  *
          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.
          38                 :  *
          39                 :  * ***** END LICENSE BLOCK ***** */
          40                 :
          41                 :
          42                 : #include "nsDiskCache.h"
          43                 : #include "nsDiskCacheDevice.h"
          44                 : #include "nsDiskCacheStreams.h"
          45                 : #include "nsCacheService.h"
          46                 : #include "mozilla/FileUtils.h"
          47                 : #include "nsIDiskCacheStreamInternal.h"
          48                 : #include "nsThreadUtils.h"
          49                 : #include "mozilla/Telemetry.h"
          50                 : #include "mozilla/TimeStamp.h"
          51                 :
          52                 :
          53                 :
          54                 : // Assumptions:
          55                 : //      - cache descriptors live for life of streams
          56                 : //      - streams will only be used by FileTransport,
          57                 : //         they will not be directly accessible to clients
          58                 : //      - overlapped I/O is NOT supported
          59                 :
          60                 :
          61                 : /******************************************************************************
          62                 :  *  nsDiskCacheInputStream
          63                 :  *****************************************************************************/
          64                 : class nsDiskCacheInputStream : public nsIInputStream {
          65                 :
          66                 : public:
          67                 :
          68                 :     nsDiskCacheInputStream( nsDiskCacheStreamIO * parent,
          69                 :                             PRFileDesc *          fileDesc,
          70                 :                             const char *          buffer,
          71                 :                             PRUint32              endOfStream);
          72                 :
          73                 :     virtual ~nsDiskCacheInputStream();
          74                 :
          75                 :     NS_DECL_ISUPPORTS
          76                 :     NS_DECL_NSIINPUTSTREAM
          77                 :
          78                 : private:
          79                 :     nsDiskCacheStreamIO *           mStreamIO;  // backpointer to parent
          80                 :     PRFileDesc *                    mFD;
          81                 :     const char *                    mBuffer;
          82                 :     PRUint32                        mStreamEnd;
          83                 :     PRUint32                        mPos;       // stream position
          84                 :     bool                            mClosed;
          85                 : };
          86                 :
          87                 :
          88             860 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsDiskCacheInputStream, nsIInputStream)
          89                 :
          90                 :
          91             172 : nsDiskCacheInputStream::nsDiskCacheInputStream( nsDiskCacheStreamIO * parent,
          92                 :                                                 PRFileDesc *          fileDesc,
          93                 :                                                 const char *          buffer,
          94                 :                                                 PRUint32              endOfStream)
          95                 :     : mStreamIO(parent)
          96                 :     , mFD(fileDesc)
          97                 :     , mBuffer(buffer)
          98                 :     , mStreamEnd(endOfStream)
          99                 :     , mPos(0)
         100             172 :     , mClosed(false)
         101                 : {
         102             172 :     NS_ADDREF(mStreamIO);
         103             172 :     mStreamIO->IncrementInputStreamCount();
         104             172 : }
         105                 :
         106                 :
         107             344 : nsDiskCacheInputStream::~nsDiskCacheInputStream()
         108                 : {
         109             172 :     Close();
         110             172 :     mStreamIO->DecrementInputStreamCount();
         111             172 :     NS_RELEASE(mStreamIO);
         112             688 : }
         113                 :
         114                 :
         115                 : NS_IMETHODIMP
         116             344 : nsDiskCacheInputStream::Close()
         117                 : {
         118             344 :     if (!mClosed) {
         119             172 :         if (mFD) {
         120               1 :             (void) PR_Close(mFD);
         121               1 :             mFD = nsnull;
         122                 :         }
         123             172 :         mClosed = true;
         124                 :     }
         125             344 :     return NS_OK;
         126                 : }
         127                 :
         128                 :
         129                 : NS_IMETHODIMP
         130              10 : nsDiskCacheInputStream::Available(PRUint32 * bytesAvailable)
         131                 : {
         132              10 :     if (mClosed)  return NS_BASE_STREAM_CLOSED;
         133              10 :     if (mStreamEnd < mPos)  return NS_ERROR_UNEXPECTED;
         134                 :
         135              10 :     *bytesAvailable = mStreamEnd - mPos;
         136              10 :     return NS_OK;
         137                 : }
         138                 :
         139                 :
         140                 : NS_IMETHODIMP
         141             339 : nsDiskCacheInputStream::Read(char * buffer, PRUint32 count, PRUint32 * bytesRead)
         142                 : {
         143             339 :     *bytesRead = 0;
         144                 :
         145             339 :     if (mClosed)
         146               0 :         return NS_OK;
         147                 :
         148             339 :     if (mPos == mStreamEnd)  return NS_OK;
         149             172 :     if (mPos > mStreamEnd)   return NS_ERROR_UNEXPECTED;
         150                 :
         151             172 :     if (count > mStreamEnd - mPos)
         152             167 :         count = mStreamEnd - mPos;
         153                 :
         154             172 :     if (mFD) {
         155                 :         // just read from file
         156               1 :         PRInt32  result = PR_Read(mFD, buffer, count);
         157               1 :         if (result < 0)  return  NS_ErrorAccordingToNSPR();
         158                 :
         159               1 :         mPos += (PRUint32)result;
         160               1 :         *bytesRead = (PRUint32)result;
         161                 :
         162             171 :     } else if (mBuffer) {
         163                 :         // read data from mBuffer
         164             171 :         memcpy(buffer, mBuffer + mPos, count);
         165             171 :         mPos += count;
         166             171 :         *bytesRead = count;
         167                 :     } else {
         168                 :         // no data source for input stream
         169                 :     }
         170                 :
         171             172 :     return NS_OK;
         172                 : }
         173                 :
         174                 :
         175                 : NS_IMETHODIMP
         176               0 : nsDiskCacheInputStream::ReadSegments(nsWriteSegmentFun writer,
         177                 :                                      void *            closure,
         178                 :                                      PRUint32          count,
         179                 :                                      PRUint32 *        bytesRead)
         180                 : {
         181               0 :     return NS_ERROR_NOT_IMPLEMENTED;
         182                 : }
         183                 :
         184                 :
         185                 : NS_IMETHODIMP
         186               0 : nsDiskCacheInputStream::IsNonBlocking(bool * nonBlocking)
         187                 : {
         188               0 :     *nonBlocking = false;
         189               0 :     return NS_OK;
         190                 : }
         191                 :
         192                 :
         193                 : /******************************************************************************
         194                 :  *  nsDiskCacheOutputStream
         195                 :  *****************************************************************************/
         196                 : class nsDiskCacheOutputStream : public nsIOutputStream
         197                 :                               , public nsIDiskCacheStreamInternal
         198                 : {
         199                 : public:
         200                 :     nsDiskCacheOutputStream( nsDiskCacheStreamIO * parent);
         201                 :     virtual ~nsDiskCacheOutputStream();
         202                 :
         203                 :     NS_DECL_ISUPPORTS
         204                 :     NS_DECL_NSIOUTPUTSTREAM
         205                 :     NS_DECL_NSIDISKCACHESTREAMINTERNAL
         206                 :
         207             556 :     void ReleaseStreamIO() { NS_IF_RELEASE(mStreamIO); }
         208                 :
         209                 : private:
         210                 :     nsDiskCacheStreamIO *           mStreamIO;  // backpointer to parent
         211                 :     bool                            mClosed;
         212                 : };
         213                 :
         214                 :
         215            3930 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsDiskCacheOutputStream,
         216                 :                               nsIOutputStream,
         217                 :                               nsIDiskCacheStreamInternal)
         218                 :
         219             556 : nsDiskCacheOutputStream::nsDiskCacheOutputStream( nsDiskCacheStreamIO * parent)
         220                 :     : mStreamIO(parent)
         221             556 :     , mClosed(false)
         222                 : {
         223             556 :     NS_ADDREF(mStreamIO);
         224             556 : }
         225                 :
         226                 :
         227            1112 : nsDiskCacheOutputStream::~nsDiskCacheOutputStream()
         228                 : {
         229             556 :     Close();
         230             556 :     ReleaseStreamIO();
         231            2224 : }
         232                 :
         233                 :
         234                 : NS_IMETHODIMP
         235            1122 : nsDiskCacheOutputStream::Close()
         236                 : {
         237            1122 :     nsresult rv = NS_OK;
         238            1122 :     mozilla::TimeStamp start = mozilla::TimeStamp::Now();
         239                 :
         240            1122 :     if (!mClosed) {
         241             554 :         mClosed = true;
         242                 :         // tell parent streamIO we are closing
         243             554 :         rv = mStreamIO->CloseOutputStream(this);
         244                 :     }
         245                 :
         246                 :     mozilla::Telemetry::ID id;
         247            1122 :     if (NS_IsMainThread())
         248              38 :         id = mozilla::Telemetry::NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_MAIN_THREAD;
         249                 :     else
         250            1084 :         id = mozilla::Telemetry::NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE;
         251                 :
         252            1122 :     mozilla::Telemetry::AccumulateTimeDelta(id, start);
         253                 :
         254            1122 :     return rv;
         255                 : }
         256                 :
         257                 : NS_IMETHODIMP
         258              14 : nsDiskCacheOutputStream::CloseInternal()
         259                 : {
         260              14 :     nsresult rv = NS_OK;
         261              14 :     mozilla::TimeStamp start = mozilla::TimeStamp::Now();
         262                 :
         263              14 :     if (!mClosed) {
         264               2 :         mClosed = true;
         265                 :         // tell parent streamIO we are closing
         266               2 :         rv = mStreamIO->CloseOutputStreamInternal(this);
         267                 :     }
         268                 :
         269                 :     mozilla::Telemetry::ID id;
         270              14 :     if (NS_IsMainThread())
         271              14 :         id = mozilla::Telemetry::NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_INTERNAL_MAIN_THREAD;
         272                 :     else
         273               0 :         id = mozilla::Telemetry::NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_INTERNAL;
         274                 :
         275              14 :     mozilla::Telemetry::AccumulateTimeDelta(id, start);
         276                 :
         277              14 :     return rv;
         278                 : }
         279                 :
         280                 : NS_IMETHODIMP
         281               0 : nsDiskCacheOutputStream::Flush()
         282                 : {
         283               0 :     if (mClosed)  return NS_BASE_STREAM_CLOSED;
         284                 :     // yeah, yeah, well get to it...eventually...
         285               0 :     return NS_OK;
         286                 : }
         287                 :
         288                 :
         289                 : NS_IMETHODIMP
         290             758 : nsDiskCacheOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *bytesWritten)
         291                 : {
         292             758 :     if (mClosed)  return NS_BASE_STREAM_CLOSED;
         293             758 :     return mStreamIO->Write(buf, count, bytesWritten);
         294                 : }
         295                 :
         296                 :
         297                 : NS_IMETHODIMP
         298               0 : nsDiskCacheOutputStream::WriteFrom(nsIInputStream *inStream, PRUint32 count, PRUint32 *bytesWritten)
         299                 : {
         300               0 :     NS_NOTREACHED("WriteFrom");
         301               0 :     return NS_ERROR_NOT_IMPLEMENTED;
         302                 : }
         303                 :
         304                 :
         305                 : NS_IMETHODIMP
         306               0 : nsDiskCacheOutputStream::WriteSegments( nsReadSegmentFun reader,
         307                 :                                         void *           closure,
         308                 :                                         PRUint32         count,
         309                 :                                         PRUint32 *       bytesWritten)
         310                 : {
         311               0 :     NS_NOTREACHED("WriteSegments");
         312               0 :     return NS_ERROR_NOT_IMPLEMENTED;
         313                 : }
         314                 :
         315                 :
         316                 : NS_IMETHODIMP
         317               0 : nsDiskCacheOutputStream::IsNonBlocking(bool * nonBlocking)
         318                 : {
         319               0 :     *nonBlocking = false;
         320               0 :     return NS_OK;
         321                 : }
         322                 :
         323                 :
         324                 :
         325                 : /******************************************************************************
         326                 :  *  nsDiskCacheStreamIO
         327                 :  *****************************************************************************/
         328            2872 : NS_IMPL_THREADSAFE_ISUPPORTS0(nsDiskCacheStreamIO)
         329                 :
         330                 : // we pick 16k as the max buffer size because that is the threshold above which
         331                 : //      we are unable to store the data in the cache block files
         332                 : //      see nsDiskCacheMap.[cpp,h]
         333                 : #define kMaxBufferSize      (16 * 1024)
         334                 :
         335             708 : nsDiskCacheStreamIO::nsDiskCacheStreamIO(nsDiskCacheBinding *   binding)
         336                 :     : mBinding(binding)
         337                 :     , mOutStream(nsnull)
         338                 :     , mInStreamCount(0)
         339                 :     , mFD(nsnull)
         340                 :     , mStreamPos(0)
         341                 :     , mStreamEnd(0)
         342                 :     , mBufPos(0)
         343                 :     , mBufEnd(0)
         344                 :     , mBufSize(0)
         345                 :     , mBufDirty(false)
         346             708 :     , mBuffer(nsnull)
         347                 : {
         348             708 :     mDevice = (nsDiskCacheDevice *)mBinding->mCacheEntry->CacheDevice();
         349                 :
         350                 :     // acquire "death grip" on cache service
         351             708 :     nsCacheService *service = nsCacheService::GlobalInstance();
         352             708 :     NS_ADDREF(service);
         353             708 : }
         354                 :
         355                 :
         356            2124 : nsDiskCacheStreamIO::~nsDiskCacheStreamIO()
         357                 : {
         358             708 :     Close();
         359                 :
         360                 :     // release "death grip" on cache service
         361             708 :     nsCacheService *service = nsCacheService::GlobalInstance();
         362             708 :     NS_RELEASE(service);
         363            2832 : }
         364                 :
         365                 :
         366                 : void
         367             708 : nsDiskCacheStreamIO::Close()
         368                 : {
         369                 :     // this should only be called from our destructor
         370                 :     // no one is interested in us anymore, so we don't need to grab any locks
         371                 :
         372                 :     // assert streams closed
         373             708 :     NS_ASSERTION(!mOutStream, "output stream still open");
         374             708 :     NS_ASSERTION(mInStreamCount == 0, "input stream still open");
         375             708 :     NS_ASSERTION(!mFD, "file descriptor not closed");
         376                 :
         377             708 :     DeleteBuffer();
         378             708 : }
         379                 :
         380                 :
         381                 : // NOTE: called with service lock held
         382                 : nsresult
         383             172 : nsDiskCacheStreamIO::GetInputStream(PRUint32 offset, nsIInputStream ** inputStream)
         384                 : {
         385             172 :     NS_ENSURE_ARG_POINTER(inputStream);
         386             172 :     NS_ENSURE_TRUE(offset == 0, NS_ERROR_NOT_IMPLEMENTED);
         387                 :
         388             172 :     *inputStream = nsnull;
         389                 :
         390             172 :     if (!mBinding)  return NS_ERROR_NOT_AVAILABLE;
         391                 :
         392             172 :     if (mOutStream) {
         393               0 :         NS_WARNING("already have an output stream open");
         394               0 :         return NS_ERROR_NOT_AVAILABLE;
         395                 :     }
         396                 :
         397                 :     nsresult            rv;
         398             172 :     PRFileDesc *        fd = nsnull;
         399                 :
         400             172 :     mStreamEnd = mBinding->mCacheEntry->DataSize();
         401             172 :     if (mStreamEnd == 0) {
         402                 :         // there's no data to read
         403               0 :         NS_ASSERTION(!mBinding->mRecord.DataLocationInitialized(), "storage allocated for zero data size");
         404             172 :     } else if (mBinding->mRecord.DataFile() == 0) {
         405                 :         // open file desc for data
         406               1 :         rv = OpenCacheFile(PR_RDONLY, &fd);
         407               1 :         if (NS_FAILED(rv))  return rv;  // unable to open file
         408               1 :         NS_ASSERTION(fd, "cache stream lacking open file.");
         409                 :
         410             171 :     } else if (!mBuffer) {
         411                 :         // read block file for data
         412             157 :         rv = ReadCacheBlocks();
         413             157 :         if (NS_FAILED(rv))  return rv;
         414                 :     }
         415                 :     // else, mBuffer already contains all of the data (left over from a
         416                 :     // previous block-file read or write).
         417                 :
         418             172 :     NS_ASSERTION(!(fd && mBuffer), "ambiguous data sources for input stream");
         419                 :
         420                 :     // create a new input stream
         421             172 :     nsDiskCacheInputStream * inStream = new nsDiskCacheInputStream(this, fd, mBuffer, mStreamEnd);
         422             172 :     if (!inStream)  return NS_ERROR_OUT_OF_MEMORY;
         423                 :
         424             172 :     NS_ADDREF(*inputStream = inStream);
         425             172 :     return NS_OK;
         426                 : }
         427                 :
         428                 :
         429                 : // NOTE: called with service lock held
         430                 : nsresult
         431             556 : nsDiskCacheStreamIO::GetOutputStream(PRUint32 offset, nsIOutputStream ** outputStream)
         432                 : {
         433             556 :     NS_ENSURE_ARG_POINTER(outputStream);
         434             556 :     *outputStream = nsnull;
         435                 :
         436             556 :     if (!mBinding)  return NS_ERROR_NOT_AVAILABLE;
         437                 :
         438             556 :     NS_ASSERTION(!mOutStream, "already have an output stream open");
         439             556 :     NS_ASSERTION(mInStreamCount == 0, "we already have input streams open");
         440             556 :     if (mOutStream || mInStreamCount)  return NS_ERROR_NOT_AVAILABLE;
         441                 :
         442                 :     // mBuffer lazily allocated, but might exist if a previous stream already
         443                 :     // created one.
         444             556 :     mBufPos    = 0;
         445             556 :     mStreamPos = 0;
         446             556 :     mStreamEnd = mBinding->mCacheEntry->DataSize();
         447                 :
         448                 :     nsresult rv;
         449             556 :     if (offset) {
         450               6 :         rv = Seek(PR_SEEK_SET, offset);
         451               6 :         if (NS_FAILED(rv)) return rv;
         452                 :     }
         453             556 :     rv = SetEOF();
         454             556 :     if (NS_FAILED(rv)) return rv;
         455                 :
         456                 :     // create a new output stream
         457             556 :     mOutStream = new nsDiskCacheOutputStream(this);
         458             556 :     if (!mOutStream)  return NS_ERROR_OUT_OF_MEMORY;
         459                 :
         460             556 :     NS_ADDREF(*outputStream = mOutStream);
         461             556 :     return NS_OK;
         462                 : }
         463                 :
         464                 : nsresult
         465             708 : nsDiskCacheStreamIO::ClearBinding()
         466                 : {
         467             708 :     nsresult rv = NS_OK;
         468             708 :     if (mBinding && mOutStream)
         469               0 :         rv = Flush();
         470             708 :     mBinding = nsnull;
         471             708 :     return rv;
         472                 : }
         473                 :
         474                 : nsresult
         475             554 : nsDiskCacheStreamIO::CloseOutputStream(nsDiskCacheOutputStream *  outputStream)
         476                 : {
         477            1108 :     nsCacheServiceAutoLock lock; // grab service lock
         478             554 :     return CloseOutputStreamInternal(outputStream);
         479                 : }
         480                 :
         481                 : nsresult
         482             556 : nsDiskCacheStreamIO::CloseOutputStreamInternal(
         483                 :     nsDiskCacheOutputStream * outputStream)
         484                 : {
         485                 :     nsresult   rv;
         486                 :
         487             556 :     if (outputStream != mOutStream) {
         488               0 :         NS_WARNING("mismatched output streams");
         489               0 :         return NS_ERROR_UNEXPECTED;
         490                 :     }
         491                 :
         492                 :     // output stream is closing
         493             556 :     if (!mBinding) {    // if we're severed, just clear member variables
         494               0 :         NS_ASSERTION(!mBufDirty, "oops");
         495               0 :         mOutStream = nsnull;
         496               0 :         outputStream->ReleaseStreamIO();
         497               0 :         return NS_ERROR_NOT_AVAILABLE;
         498                 :     }
         499                 :
         500             556 :     rv = Flush();
         501             556 :     if (NS_FAILED(rv))
         502               0 :         NS_WARNING("Flush() failed");
         503                 :
         504             556 :     mOutStream = nsnull;
         505             556 :     return rv;
         506                 : }
         507                 :
         508                 : nsresult
         509             556 : nsDiskCacheStreamIO::Flush()
         510                 : {
         511             556 :     NS_ASSERTION(mBinding, "oops");
         512                 :
         513             556 :     CACHE_LOG_DEBUG(("CACHE: Flush [%x doomed=%u]\n",
         514                 :         mBinding->mRecord.HashNumber(), mBinding->mDoomed));
         515                 :
         516             556 :     if (!mBufDirty) {
         517              42 :         if (mFD) {
         518               3 :             (void) PR_Close(mFD);
         519               3 :             mFD = nsnull;
         520                 :         }
         521              42 :         return NS_OK;
         522                 :     }
         523                 :
         524                 :     // write data to cache blocks, or flush mBuffer to file
         525             514 :     nsDiskCacheMap *cacheMap = mDevice->CacheMap();  // get map reference
         526                 :     nsresult rv;
         527                 :
         528             514 :     bool written = false;
         529                 :
         530             990 :     if ((mStreamEnd <= kMaxBufferSize) &&
         531             476 :         (mBinding->mCacheEntry->StoragePolicy() != nsICache::STORE_ON_DISK_AS_FILE)) {
         532                 :         // store data (if any) in cache block files
         533                 :
         534             476 :         mBufDirty = false;
         535                 :
         536                 :         // delete existing storage
         537             476 :         nsDiskCacheRecord * record = &mBinding->mRecord;
         538             476 :         if (record->DataLocationInitialized()) {
         539               8 :             rv = cacheMap->DeleteStorage(record, nsDiskCache::kData);
         540               8 :             if (NS_FAILED(rv)) {
         541               0 :                 NS_WARNING("cacheMap->DeleteStorage() failed.");
         542               0 :                 return rv;
         543                 :             }
         544                 :         }
         545                 :
         546                 :         // flush buffer to block files
         547             476 :         written = true;
         548             476 :         if (mStreamEnd > 0) {
         549             476 :             rv = cacheMap->WriteDataCacheBlocks(mBinding, mBuffer, mBufEnd);
         550             476 :             if (NS_FAILED(rv)) {
         551               0 :                 NS_WARNING("WriteDataCacheBlocks() failed.");
         552               0 :                 written = false;
         553                 :             }
         554                 :         }
         555                 :     }
         556                 :
         557             514 :     if (!written) {
         558                 :         // make sure we save as separate file
         559              38 :         rv = FlushBufferToFile(); // initializes DataFileLocation() if necessary
         560                 :
         561              38 :         if (mFD) {
         562                 :           // Update the file size of the disk file in the cache
         563              38 :           UpdateFileSize();
         564                 :
         565                 :           // close file descriptor
         566              38 :           (void) PR_Close(mFD);
         567              38 :           mFD = nsnull;
         568                 :         }
         569                 :         else
         570               0 :           NS_WARNING("no file descriptor");
         571                 :
         572                 :         // close mFD first if possible before returning if FlushBufferToFile
         573                 :         // failed
         574              38 :         NS_ENSURE_SUCCESS(rv, rv);
         575                 :
         576                 :         // since the data location is on disk as a single file, the only value
         577                 :         // in keeping mBuffer around is to avoid an extra malloc the next time
         578                 :         // we need to write to this file.  reading will use a file descriptor.
         579                 :         // therefore, it's probably not worth optimizing for the subsequent
         580                 :         // write, so we unconditionally delete mBuffer here.
         581              38 :         DeleteBuffer();
         582                 :     }
         583                 :
         584                 :     // XXX do we need this here?  WriteDataCacheBlocks() calls UpdateRecord()
         585                 :     // update cache map if entry isn't doomed
         586             514 :     if (!mBinding->mDoomed) {
         587             512 :         rv = cacheMap->UpdateRecord(&mBinding->mRecord);
         588             512 :         if (NS_FAILED(rv)) {
         589               0 :             NS_WARNING("cacheMap->UpdateRecord() failed.");
         590               0 :             return rv;   // XXX doom cache entry
         591                 :         }
         592                 :     }
         593                 :
         594             514 :     return NS_OK;
         595                 : }
         596                 :
         597                 :
         598                 : // assumptions:
         599                 : //      only one thread writing at a time
         600                 : //      never have both output and input streams open
         601                 : //      OnDataSizeChanged() will have already been called to update entry->DataSize()
         602                 :
         603                 : nsresult
         604             758 : nsDiskCacheStreamIO::Write( const char * buffer,
         605                 :                             PRUint32     count,
         606                 :                             PRUint32 *   bytesWritten)
         607                 : {
         608             758 :     nsresult    rv = NS_OK;
         609            1516 :     nsCacheServiceAutoLock lock; // grab service lock
         610             758 :     if (!mBinding)  return NS_ERROR_NOT_AVAILABLE;
         611                 :
         612             758 :     if (mInStreamCount) {
         613                 :         // we have open input streams already
         614                 :         // this is an error until we support overlapped I/O
         615               0 :         NS_WARNING("Attempting to write to cache entry with open input streams.\n");
         616               0 :         return NS_ERROR_NOT_AVAILABLE;
         617                 :     }
         618                 :
         619             758 :     NS_ASSERTION(count, "Write called with count of zero");
         620             758 :     NS_ASSERTION(mBufPos <= mBufEnd, "streamIO buffer corrupted");
         621                 :
         622             758 :     PRUint32 bytesLeft = count;
         623             758 :     bool     flushed = false;
         624                 :
         625            9834 :     while (bytesLeft) {
         626            8318 :         if (mBufPos == mBufSize) {
         627            8216 :             if (mBufSize < kMaxBufferSize) {
         628             510 :                 mBufSize = kMaxBufferSize;
         629             510 :                 char *buffer = mBuffer;
         630                 :
         631             510 :                 mBuffer  = (char *) realloc(mBuffer, mBufSize);
         632             510 :                 if (!mBuffer) {
         633               0 :                     free(buffer);
         634               0 :                     mBufSize = 0;
         635               0 :                     break;
         636                 :                 }
         637                 :             } else {
         638            7706 :                 nsresult rv = FlushBufferToFile();
         639            7706 :                 if (NS_FAILED(rv))  break;
         640            7706 :                 flushed = true;
         641                 :             }
         642                 :         }
         643                 :
         644            8318 :         PRUint32 chunkSize = bytesLeft;
         645            8318 :         if (chunkSize > (mBufSize - mBufPos))
         646            7560 :             chunkSize =  mBufSize - mBufPos;
         647                 :
         648            8318 :         memcpy(mBuffer + mBufPos, buffer, chunkSize);
         649            8318 :         mBufDirty = true;
         650            8318 :         mBufPos += chunkSize;
         651            8318 :         bytesLeft -= chunkSize;
         652            8318 :         buffer += chunkSize;
         653                 :
         654            8318 :         if (mBufEnd < mBufPos)
         655            8318 :             mBufEnd = mBufPos;
         656                 :     }
         657             758 :     if (bytesLeft) {
         658               0 :         *bytesWritten = 0;
         659               0 :         return NS_ERROR_FAILURE;
         660                 :     }
         661             758 :     *bytesWritten = count;
         662                 :
         663                 :     // update mStreamPos, mStreamEnd
         664             758 :     mStreamPos += count;
         665             758 :     if (mStreamEnd < mStreamPos) {
         666             758 :         mStreamEnd = mStreamPos;
         667             758 :         NS_ASSERTION(mBinding->mCacheEntry->DataSize() == mStreamEnd, "bad stream");
         668                 :
         669                 :         // If we have flushed to a file, update the file size
         670             758 :         if (flushed && mFD) {
         671             152 :             UpdateFileSize();
         672                 :         }
         673                 :     }
         674                 :
         675             758 :     return rv;
         676                 : }
         677                 :
         678                 :
         679                 : void
         680             194 : nsDiskCacheStreamIO::UpdateFileSize()
         681                 : {
         682             194 :     NS_ASSERTION(mFD, "nsDiskCacheStreamIO::UpdateFileSize should not have been called");
         683                 :
         684             194 :     nsDiskCacheRecord * record = &mBinding->mRecord;
         685             194 :     const PRUint32      oldSizeK  = record->DataFileSize();
         686             194 :     PRUint32            newSizeK  = (mStreamEnd + 0x03FF) >> 10;
         687                 :
         688                 :     // make sure the size won't overflow (bug #651100)
         689             194 :     if (newSizeK > kMaxDataSizeK)
         690               3 :         newSizeK = kMaxDataSizeK;
         691                 :
         692             194 :     if (newSizeK == oldSizeK)  return;
         693                 :
         694             155 :     record->SetDataFileSize(newSizeK);
         695                 :
         696                 :     // update cache size totals
         697             155 :     nsDiskCacheMap * cacheMap = mDevice->CacheMap();
         698             155 :     cacheMap->DecrementTotalSize(oldSizeK);       // decrement old size
         699             155 :     cacheMap->IncrementTotalSize(newSizeK);       // increment new size
         700                 :
         701             155 :     if (!mBinding->mDoomed) {
         702             138 :         nsresult rv = cacheMap->UpdateRecord(record);
         703             138 :         if (NS_FAILED(rv)) {
         704               0 :             NS_WARNING("cacheMap->UpdateRecord() failed.");
         705                 :             // XXX doom cache entry?
         706                 :         }
         707                 :     }
         708                 : }
         709                 :
         710                 :
         711                 : nsresult
         712              42 : nsDiskCacheStreamIO::OpenCacheFile(PRIntn flags, PRFileDesc ** fd)
         713                 : {
         714              42 :     NS_ENSURE_ARG_POINTER(fd);
         715                 :
         716                 :     nsresult         rv;
         717              42 :     nsDiskCacheMap * cacheMap = mDevice->CacheMap();
         718                 :
         719                 :     rv = cacheMap->GetLocalFileForDiskCacheRecord(&mBinding->mRecord,
         720                 :                                                   nsDiskCache::kData,
         721                 :                                                   !!(flags & PR_CREATE_FILE),
         722              42 :                                                   getter_AddRefs(mLocalFile));
         723              42 :     if (NS_FAILED(rv))  return rv;
         724                 :
         725                 :     // create PRFileDesc for input stream - the 00600 is just for consistency
         726              42 :     rv = mLocalFile->OpenNSPRFileDesc(flags, 00600, fd);
         727              42 :     if (NS_FAILED(rv))  return rv;  // unable to open file
         728                 :
         729              42 :     return NS_OK;
         730                 : }
         731                 :
         732                 :
         733                 : nsresult
         734             159 : nsDiskCacheStreamIO::ReadCacheBlocks()
         735                 : {
         736             159 :     NS_ASSERTION(mStreamEnd == mBinding->mCacheEntry->DataSize(), "bad stream");
         737             159 :     NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "data too large for buffer");
         738                 :
         739             159 :     nsDiskCacheRecord * record = &mBinding->mRecord;
         740             159 :     if (!record->DataLocationInitialized()) return NS_OK;
         741                 :
         742             159 :     NS_ASSERTION(record->DataFile() != kSeparateFile, "attempt to read cache blocks on separate file");
         743                 :
         744             159 :     if (!mBuffer) {
         745                 :         // allocate buffer
         746             159 :         mBuffer = (char *) malloc(mStreamEnd);
         747             159 :         if (!mBuffer) {
         748               0 :             return NS_ERROR_OUT_OF_MEMORY;
         749                 :         }
         750             159 :         mBufSize = mStreamEnd;
         751                 :     }
         752                 :
         753                 :     // read data stored in cache block files
         754             159 :     nsDiskCacheMap *map = mDevice->CacheMap();  // get map reference
         755             159 :     nsresult rv = map->ReadDataCacheBlocks(mBinding, mBuffer, mStreamEnd);
         756             159 :     if (NS_FAILED(rv)) return rv;
         757                 :
         758                 :     // update streamIO variables
         759             159 :     mBufPos = 0;
         760             159 :     mBufEnd = mStreamEnd;
         761                 :
         762             159 :     return NS_OK;
         763                 : }
         764                 :
         765                 :
         766                 : nsresult
         767            7744 : nsDiskCacheStreamIO::FlushBufferToFile()
         768                 : {
         769                 :     nsresult  rv;
         770            7744 :     nsDiskCacheRecord * record = &mBinding->mRecord;
         771                 :
         772            7744 :     if (!mFD) {
         773              37 :         if (record->DataLocationInitialized() && (record->DataFile() > 0)) {
         774                 :             // remove cache block storage
         775               0 :             nsDiskCacheMap * cacheMap = mDevice->CacheMap();
         776               0 :             rv = cacheMap->DeleteStorage(record, nsDiskCache::kData);
         777               0 :             if (NS_FAILED(rv))  return rv;
         778                 :         }
         779              37 :         record->SetDataFileGeneration(mBinding->mGeneration);
         780                 :
         781                 :         // allocate file
         782              37 :         rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
         783              37 :         if (NS_FAILED(rv))  return rv;
         784                 :
         785              37 :         PRInt64 dataSize = mBinding->mCacheEntry->PredictedDataSize();
         786              37 :         if (dataSize != -1)
         787              32 :             mozilla::fallocate(mFD, NS_MIN<PRInt64>(dataSize, kPreallocateLimit));
         788                 :     }
         789                 :
         790                 :     // write buffer
         791            7744 :     PRInt32 bytesWritten = PR_Write(mFD, mBuffer, mBufEnd);
         792            7744 :     if (PRUint32(bytesWritten) != mBufEnd) {
         793               0 :         NS_WARNING("failed to flush all data");
         794               0 :         return NS_ERROR_UNEXPECTED;     // NS_ErrorAccordingToNSPR()
         795                 :     }
         796            7744 :     mBufDirty = false;
         797                 :
         798                 :     // reset buffer
         799            7744 :     mBufPos = 0;
         800            7744 :     mBufEnd = 0;
         801                 :
         802            7744 :     return NS_OK;
         803                 : }
         804                 :
         805                 :
         806                 : void
         807             746 : nsDiskCacheStreamIO::DeleteBuffer()
         808                 : {
         809             746 :     if (mBuffer) {
         810             667 :         NS_ASSERTION(!mBufDirty, "deleting dirty buffer");
         811             667 :         free(mBuffer);
         812             667 :         mBuffer = nsnull;
         813             667 :         mBufPos = 0;
         814             667 :         mBufEnd = 0;
         815             667 :         mBufSize = 0;
         816                 :     }
         817             746 : }
         818                 :
         819                 :
         820                 : // NOTE: called with service lock held
         821                 : nsresult
         822               6 : nsDiskCacheStreamIO::Seek(PRInt32 whence, PRInt32 offset)
         823                 : {
         824                 :     PRInt32  newPos;
         825               6 :     if (!mBinding)  return NS_ERROR_NOT_AVAILABLE;
         826                 :
         827               6 :     if (PRUint32(offset) > mStreamEnd)  return NS_ERROR_FAILURE;
         828                 :
         829               6 :     if (mBinding->mRecord.DataLocationInitialized()) {
         830               6 :         if (mBinding->mRecord.DataFile() == 0) {
         831               4 :             if (!mFD) {
         832                 :                 // we need an mFD, we better open it now
         833               4 :                 nsresult rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
         834               4 :                 if (NS_FAILED(rv))  return rv;
         835                 :             }
         836                 :         }
         837                 :     }
         838                 :
         839               6 :     if (mFD) {
         840                 :         // do we have data in the buffer that needs to be flushed?
         841               4 :         if (mBufDirty) {
         842                 :             // XXX optimization: are we just moving within the current buffer?
         843               0 :             nsresult rv = FlushBufferToFile();
         844               0 :             if (NS_FAILED(rv))  return rv;
         845                 :         }
         846                 :
         847               4 :         newPos = PR_Seek(mFD, offset, (PRSeekWhence)whence);
         848               4 :         if (newPos == -1)
         849               0 :             return NS_ErrorAccordingToNSPR();
         850                 :
         851               4 :         mStreamPos = (PRUint32) newPos;
         852               4 :         mBufPos = 0;
         853               4 :         mBufEnd = 0;
         854               4 :         return NS_OK;
         855                 :     }
         856                 :
         857                 :     // else, seek in mBuffer
         858                 :
         859               2 :     switch(whence) {
         860                 :         case PR_SEEK_SET:
         861               2 :             newPos = offset;
         862               2 :             break;
         863                 :
         864                 :         case PR_SEEK_CUR:   // relative from current posistion
         865               0 :             newPos = offset + (PRUint32)mStreamPos;
         866               0 :             break;
         867                 :
         868                 :         case PR_SEEK_END:   // relative from end
         869               0 :             newPos = offset + (PRUint32)mBufEnd;
         870               0 :             break;
         871                 :
         872                 :         default:
         873               0 :             return NS_ERROR_INVALID_ARG;
         874                 :     }
         875                 :
         876                 :     // read data into mBuffer if not read yet.
         877               2 :     if (mStreamEnd && !mBufEnd) {
         878               2 :         if (newPos > 0) {
         879               2 :             nsresult rv = ReadCacheBlocks();
         880               2 :             if (NS_FAILED(rv))  return rv;
         881                 :         }
         882                 :     }
         883                 :
         884                 :     // stream buffer sanity checks
         885               2 :     NS_ASSERTION(mBufEnd <= kMaxBufferSize, "bad stream");
         886               2 :     NS_ASSERTION(mBufPos <= mBufEnd,     "bad stream");
         887               2 :     NS_ASSERTION(mStreamPos == mBufPos,  "bad stream");
         888               2 :     NS_ASSERTION(mStreamEnd == mBufEnd,  "bad stream");
         889                 :
         890               2 :     if ((newPos < 0) || (PRUint32(newPos) > mBufEnd)) {
         891               0 :         NS_WARNING("seek offset out of range");
         892               0 :         return NS_ERROR_INVALID_ARG;
         893                 :     }
         894                 :
         895               2 :     mStreamPos = newPos;
         896               2 :     mBufPos    = newPos;
         897               2 :     return NS_OK;
         898                 : }
         899                 :
         900                 :
         901                 : // called only from nsDiskCacheOutputStream::Tell
         902                 : nsresult
         903               0 : nsDiskCacheStreamIO::Tell(PRUint32 * result)
         904                 : {
         905               0 :     NS_ENSURE_ARG_POINTER(result);
         906               0 :     *result = mStreamPos;
         907               0 :     return NS_OK;
         908                 : }
         909                 :
         910                 :
         911                 : // NOTE: called with service lock held
         912                 : nsresult
         913             556 : nsDiskCacheStreamIO::SetEOF()
         914                 : {
         915                 :     nsresult    rv;
         916             556 :     bool        needToCloseFD = false;
         917                 :
         918             556 :     NS_ASSERTION(mStreamPos <= mStreamEnd, "bad stream");
         919             556 :     if (!mBinding)  return NS_ERROR_NOT_AVAILABLE;
         920                 :
         921             556 :     if (mBinding->mRecord.DataLocationInitialized()) {
         922              12 :         if (mBinding->mRecord.DataFile() == 0) {
         923               4 :             if (!mFD) {
         924                 :                 // we need an mFD, we better open it now
         925               0 :                 rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
         926               0 :                 if (NS_FAILED(rv))  return rv;
         927               0 :                 needToCloseFD = true;
         928                 :             }
         929                 :         } else {
         930                 :             // data in cache block files
         931               8 :             if ((mStreamPos != 0) && (mStreamPos != mBufPos)) {
         932                 :                 // only read data if there will be some left after truncation
         933               0 :                 rv = ReadCacheBlocks();
         934               0 :                 if (NS_FAILED(rv))  return rv;
         935                 :             }
         936                 :
         937                 :             // We need to make sure we reflect this change in Flush().
         938                 :             // In particular, if mStreamPos is 0 and we never write to
         939                 :             // the buffer, we want the storage to be deleted.
         940               8 :             mBufDirty = true;
         941                 :         }
         942                 :     }
         943                 :
         944             556 :     if (mFD) {
         945               4 :         rv = nsDiskCache::Truncate(mFD, mStreamPos);
         946                 : #ifdef DEBUG
         947               4 :         PRUint32 oldSizeK = (mStreamEnd + 0x03FF) >> 10;
         948               4 :         NS_ASSERTION(mBinding->mRecord.DataFileSize() == oldSizeK, "bad disk cache entry size");
         949                 :     } else {
         950                 :         // data stored in buffer.
         951             552 :         NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "buffer truncation inadequate");
         952             552 :         NS_ASSERTION(mBufPos == mStreamPos, "bad stream");
         953             552 :         NS_ASSERTION(mBuffer ? mBufEnd == mStreamEnd : true, "bad stream");
         954                 : #endif
         955                 :     }
         956                 :
         957             556 :     NS_ASSERTION(mStreamEnd == mBinding->mCacheEntry->DataSize(), "cache entry not updated");
         958                 :     // we expect nsCacheEntryDescriptor::TransportWrapper::OpenOutputStream()
         959                 :     // to eventually update the cache entry
         960                 :
         961             556 :     mStreamEnd  = mStreamPos;
         962             556 :     mBufEnd     = mBufPos;
         963                 :
         964             556 :     if (mFD) {
         965               4 :         UpdateFileSize();
         966               4 :         if (needToCloseFD) {
         967               0 :             (void) PR_Close(mFD);
         968               0 :             mFD = nsnull;
         969                 :         }
         970                 :     }
         971                 :
         972             556 :     return  NS_OK;
         973                 : }

Generated by: LCOV version 1.7