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-06-02 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             557 :     void ReleaseStreamIO() { NS_IF_RELEASE(mStreamIO); }
     208                 : 
     209                 : private:
     210                 :     nsDiskCacheStreamIO *           mStreamIO;  // backpointer to parent
     211                 :     bool                            mClosed;
     212                 : };
     213                 : 
     214                 : 
     215            3937 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsDiskCacheOutputStream,
     216                 :                               nsIOutputStream,
     217                 :                               nsIDiskCacheStreamInternal)
     218                 : 
     219             557 : nsDiskCacheOutputStream::nsDiskCacheOutputStream( nsDiskCacheStreamIO * parent)
     220                 :     : mStreamIO(parent)
     221             557 :     , mClosed(false)
     222                 : {
     223             557 :     NS_ADDREF(mStreamIO);
     224             557 : }
     225                 : 
     226                 : 
     227            1114 : nsDiskCacheOutputStream::~nsDiskCacheOutputStream()
     228                 : {
     229             557 :     Close();
     230             557 :     ReleaseStreamIO();
     231            2228 : }
     232                 : 
     233                 : 
     234                 : NS_IMETHODIMP
     235            1124 : nsDiskCacheOutputStream::Close()
     236                 : {
     237            1124 :     nsresult rv = NS_OK;
     238            1124 :     mozilla::TimeStamp start = mozilla::TimeStamp::Now();
     239                 : 
     240            1124 :     if (!mClosed) {
     241             555 :         mClosed = true;
     242                 :         // tell parent streamIO we are closing
     243             555 :         rv = mStreamIO->CloseOutputStream(this);
     244                 :     }
     245                 : 
     246                 :     mozilla::Telemetry::ID id;
     247            1124 :     if (NS_IsMainThread())
     248              38 :         id = mozilla::Telemetry::NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_MAIN_THREAD;
     249                 :     else
     250            1086 :         id = mozilla::Telemetry::NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE;
     251                 : 
     252            1124 :     mozilla::Telemetry::AccumulateTimeDelta(id, start);
     253                 : 
     254            1124 :     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            2878 : 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             710 : 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             710 :     , mBuffer(nsnull)
     347                 : {
     348             710 :     mDevice = (nsDiskCacheDevice *)mBinding->mCacheEntry->CacheDevice();
     349                 : 
     350                 :     // acquire "death grip" on cache service
     351             710 :     nsCacheService *service = nsCacheService::GlobalInstance();
     352             710 :     NS_ADDREF(service);
     353             710 : }
     354                 : 
     355                 : 
     356            2130 : nsDiskCacheStreamIO::~nsDiskCacheStreamIO()
     357                 : {
     358             710 :     Close();
     359                 : 
     360                 :     // release "death grip" on cache service
     361             710 :     nsCacheService *service = nsCacheService::GlobalInstance();
     362             710 :     NS_RELEASE(service);
     363            2840 : }
     364                 : 
     365                 : 
     366                 : void
     367             710 : 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             710 :     NS_ASSERTION(!mOutStream, "output stream still open");
     374             710 :     NS_ASSERTION(mInStreamCount == 0, "input stream still open");
     375             710 :     NS_ASSERTION(!mFD, "file descriptor not closed");
     376                 : 
     377             710 :     DeleteBuffer();
     378             710 : }
     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             158 :         rv = ReadCacheBlocks();
     413             158 :         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             557 : nsDiskCacheStreamIO::GetOutputStream(PRUint32 offset, nsIOutputStream ** outputStream)
     432                 : {
     433             557 :     NS_ENSURE_ARG_POINTER(outputStream);
     434             557 :     *outputStream = nsnull;
     435                 : 
     436             557 :     if (!mBinding)  return NS_ERROR_NOT_AVAILABLE;
     437                 :         
     438             557 :     NS_ASSERTION(!mOutStream, "already have an output stream open");
     439             557 :     NS_ASSERTION(mInStreamCount == 0, "we already have input streams open");
     440             557 :     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             557 :     mBufPos    = 0;
     445             557 :     mStreamPos = 0;
     446             557 :     mStreamEnd = mBinding->mCacheEntry->DataSize();
     447                 : 
     448                 :     nsresult rv;
     449             557 :     if (offset) {
     450               6 :         rv = Seek(PR_SEEK_SET, offset);
     451               6 :         if (NS_FAILED(rv)) return rv;
     452                 :     }
     453             557 :     rv = SetEOF();
     454             557 :     if (NS_FAILED(rv)) return rv;
     455                 : 
     456                 :     // create a new output stream
     457             557 :     mOutStream = new nsDiskCacheOutputStream(this);
     458             557 :     if (!mOutStream)  return NS_ERROR_OUT_OF_MEMORY;
     459                 :     
     460             557 :     NS_ADDREF(*outputStream = mOutStream);
     461             557 :     return NS_OK;
     462                 : }
     463                 : 
     464                 : nsresult
     465             710 : nsDiskCacheStreamIO::ClearBinding()
     466                 : {
     467             710 :     nsresult rv = NS_OK;
     468             710 :     if (mBinding && mOutStream)
     469               0 :         rv = Flush();
     470             710 :     mBinding = nsnull;
     471             710 :     return rv;
     472                 : }
     473                 : 
     474                 : nsresult
     475             555 : nsDiskCacheStreamIO::CloseOutputStream(nsDiskCacheOutputStream *  outputStream)
     476                 : {
     477            1110 :     nsCacheServiceAutoLock lock; // grab service lock
     478             555 :     return CloseOutputStreamInternal(outputStream);
     479                 : }
     480                 : 
     481                 : nsresult
     482             557 : nsDiskCacheStreamIO::CloseOutputStreamInternal(
     483                 :     nsDiskCacheOutputStream * outputStream)
     484                 : {
     485                 :     nsresult   rv;
     486                 : 
     487             557 :     if (outputStream != mOutStream) {
     488               0 :         NS_WARNING("mismatched output streams");
     489               0 :         return NS_ERROR_UNEXPECTED;
     490                 :     }
     491                 :     
     492                 :     // output stream is closing
     493             557 :     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             557 :     rv = Flush();
     501             557 :     if (NS_FAILED(rv))
     502               0 :         NS_WARNING("Flush() failed");
     503                 : 
     504             557 :     mOutStream = nsnull;
     505             557 :     return rv;
     506                 : }
     507                 : 
     508                 : nsresult
     509             557 : nsDiskCacheStreamIO::Flush()
     510                 : {
     511             557 :     NS_ASSERTION(mBinding, "oops");
     512                 : 
     513             557 :     CACHE_LOG_DEBUG(("CACHE: Flush [%x doomed=%u]\n",
     514                 :         mBinding->mRecord.HashNumber(), mBinding->mDoomed));
     515                 : 
     516             557 :     if (!mBufDirty) {
     517              43 :         if (mFD) {
     518               3 :             (void) PR_Close(mFD);
     519               3 :             mFD = nsnull;
     520                 :         }
     521              43 :         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             160 : nsDiskCacheStreamIO::ReadCacheBlocks()
     735                 : {
     736             160 :     NS_ASSERTION(mStreamEnd == mBinding->mCacheEntry->DataSize(), "bad stream");
     737             160 :     NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "data too large for buffer");
     738                 : 
     739             160 :     nsDiskCacheRecord * record = &mBinding->mRecord;
     740             160 :     if (!record->DataLocationInitialized()) return NS_OK;
     741                 : 
     742             160 :     NS_ASSERTION(record->DataFile() != kSeparateFile, "attempt to read cache blocks on separate file");
     743                 : 
     744             160 :     if (!mBuffer) {
     745                 :         // allocate buffer
     746             160 :         mBuffer = (char *) malloc(mStreamEnd);
     747             160 :         if (!mBuffer) {
     748               0 :             return NS_ERROR_OUT_OF_MEMORY;
     749                 :         }
     750             160 :         mBufSize = mStreamEnd;
     751                 :     }
     752                 :     
     753                 :     // read data stored in cache block files
     754             160 :     nsDiskCacheMap *map = mDevice->CacheMap();  // get map reference
     755             160 :     nsresult rv = map->ReadDataCacheBlocks(mBinding, mBuffer, mStreamEnd);
     756             160 :     if (NS_FAILED(rv)) return rv;
     757                 : 
     758                 :     // update streamIO variables
     759             160 :     mBufPos = 0;
     760             160 :     mBufEnd = mStreamEnd;
     761                 :     
     762             160 :     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             748 : nsDiskCacheStreamIO::DeleteBuffer()
     808                 : {
     809             748 :     if (mBuffer) {
     810             668 :         NS_ASSERTION(!mBufDirty, "deleting dirty buffer");
     811             668 :         free(mBuffer);
     812             668 :         mBuffer = nsnull;
     813             668 :         mBufPos = 0;
     814             668 :         mBufEnd = 0;
     815             668 :         mBufSize = 0;
     816                 :     }
     817             748 : }
     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             557 : nsDiskCacheStreamIO::SetEOF()
     914                 : {
     915                 :     nsresult    rv;
     916             557 :     bool        needToCloseFD = false;
     917                 : 
     918             557 :     NS_ASSERTION(mStreamPos <= mStreamEnd, "bad stream");
     919             557 :     if (!mBinding)  return NS_ERROR_NOT_AVAILABLE;
     920                 :     
     921             557 :     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             557 :     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             553 :         NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "buffer truncation inadequate");
     952             553 :         NS_ASSERTION(mBufPos == mStreamPos, "bad stream");
     953             553 :         NS_ASSERTION(mBuffer ? mBufEnd == mStreamEnd : true, "bad stream");
     954                 : #endif
     955                 :     }
     956                 : 
     957             557 :     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             557 :     mStreamEnd  = mStreamPos;
     962             557 :     mBufEnd     = mBufPos;
     963                 :     
     964             557 :     if (mFD) {
     965               4 :         UpdateFileSize();
     966               4 :         if (needToCloseFD) {
     967               0 :             (void) PR_Close(mFD);
     968               0 :             mFD = nsnull;
     969                 :         } 
     970                 :     }
     971                 : 
     972             557 :     return  NS_OK;
     973                 : }

Generated by: LCOV version 1.7