LCOV - code coverage report
Current view: top level - netwerk/cache - nsDiskCacheBlockFile.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 168 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 11 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  *
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "nsCache.h"
       8             : #include "nsDiskCache.h"
       9             : #include "nsDiskCacheBlockFile.h"
      10             : #include "mozilla/FileUtils.h"
      11             : #include "mozilla/IntegerPrintfMacros.h"
      12             : #include "mozilla/MemoryReporting.h"
      13             : #include <algorithm>
      14             : 
      15             : using namespace mozilla;
      16             : 
      17             : /******************************************************************************
      18             :  * nsDiskCacheBlockFile -
      19             :  *****************************************************************************/
      20             : 
      21             : /******************************************************************************
      22             :  *  Open
      23             :  *****************************************************************************/
      24             : nsresult
      25           0 : nsDiskCacheBlockFile::Open(nsIFile * blockFile,
      26             :                            uint32_t  blockSize,
      27             :                            uint32_t  bitMapSize,
      28             :                            nsDiskCache::CorruptCacheInfo *  corruptInfo)
      29             : {
      30           0 :     NS_ENSURE_ARG_POINTER(corruptInfo);
      31           0 :     *corruptInfo = nsDiskCache::kUnexpectedError;
      32             : 
      33           0 :     if (bitMapSize % 32) {
      34           0 :         *corruptInfo = nsDiskCache::kInvalidArgPointer;
      35           0 :         return NS_ERROR_INVALID_ARG;
      36             :     }
      37             : 
      38           0 :     mBlockSize = blockSize;
      39           0 :     mBitMapWords = bitMapSize / 32;
      40           0 :     uint32_t bitMapBytes = mBitMapWords * 4;
      41             : 
      42             :     // open the file - restricted to user, the data could be confidential
      43           0 :     nsresult rv = blockFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE, 00600, &mFD);
      44           0 :     if (NS_FAILED(rv)) {
      45           0 :         *corruptInfo = nsDiskCache::kCouldNotCreateBlockFile;
      46           0 :         CACHE_LOG_DEBUG(("CACHE: nsDiskCacheBlockFile::Open "
      47             :                          "[this=%p] unable to open or create file: %" PRId32,
      48             :                          this, static_cast<uint32_t>(rv)));
      49           0 :         return rv;  // unable to open or create file
      50             :     }
      51             : 
      52             :     // allocate bit map buffer
      53           0 :     mBitMap = new uint32_t[mBitMapWords];
      54             : 
      55             :     // check if we just creating the file
      56           0 :     mFileSize = PR_Available(mFD);
      57           0 :     if (mFileSize < 0) {
      58             :         // XXX an error occurred. We could call PR_GetError(), but how would that help?
      59           0 :         *corruptInfo = nsDiskCache::kBlockFileSizeError;
      60           0 :         rv = NS_ERROR_UNEXPECTED;
      61           0 :         goto error_exit;
      62             :     }
      63           0 :     if (mFileSize == 0) {
      64             :         // initialize bit map and write it
      65           0 :         memset(mBitMap, 0, bitMapBytes);
      66           0 :         if (!Write(0, mBitMap, bitMapBytes)) {
      67           0 :             *corruptInfo = nsDiskCache::kBlockFileBitMapWriteError;
      68           0 :             goto error_exit;
      69             :         }
      70             : 
      71           0 :     } else if ((uint32_t)mFileSize < bitMapBytes) {
      72           0 :         *corruptInfo = nsDiskCache::kBlockFileSizeLessThanBitMap;
      73           0 :         rv = NS_ERROR_UNEXPECTED;  // XXX NS_ERROR_CACHE_INVALID;
      74           0 :         goto error_exit;
      75             : 
      76             :     } else {
      77             :         // read the bit map
      78           0 :         const int32_t bytesRead = PR_Read(mFD, mBitMap, bitMapBytes);
      79           0 :         if ((bytesRead < 0) || ((uint32_t)bytesRead < bitMapBytes)) {
      80           0 :             *corruptInfo = nsDiskCache::kBlockFileBitMapReadError;
      81           0 :             rv = NS_ERROR_UNEXPECTED;
      82           0 :             goto error_exit;
      83             :         }
      84             : #if defined(IS_LITTLE_ENDIAN)
      85             :         // Swap from network format
      86           0 :         for (unsigned int i = 0; i < mBitMapWords; ++i)
      87           0 :             mBitMap[i] = ntohl(mBitMap[i]);
      88             : #endif
      89             :         // validate block file size
      90             :         // Because not whole blocks are written, the size may be a
      91             :         // little bit smaller than used blocks times blocksize,
      92             :         // because the last block will generally not be 'whole'.
      93           0 :         const uint32_t  estimatedSize = CalcBlockFileSize();
      94           0 :         if ((uint32_t)mFileSize + blockSize < estimatedSize) {
      95           0 :             *corruptInfo = nsDiskCache::kBlockFileEstimatedSizeError;
      96           0 :             rv = NS_ERROR_UNEXPECTED;
      97           0 :             goto error_exit;
      98             :         }
      99             :     }
     100           0 :     CACHE_LOG_DEBUG(("CACHE: nsDiskCacheBlockFile::Open [this=%p] succeeded",
     101             :                       this));
     102           0 :     return NS_OK;
     103             : 
     104             : error_exit:
     105           0 :     CACHE_LOG_DEBUG(("CACHE: nsDiskCacheBlockFile::Open [this=%p] failed with "
     106             :                      "error %" PRId32, this, static_cast<uint32_t>(rv)));
     107           0 :     Close(false);
     108           0 :     return rv;
     109             : }
     110             : 
     111             : 
     112             : /******************************************************************************
     113             :  *  Close
     114             :  *****************************************************************************/
     115             : nsresult
     116           0 : nsDiskCacheBlockFile::Close(bool flush)
     117             : {
     118           0 :     nsresult rv = NS_OK;
     119             : 
     120           0 :     if (mFD) {
     121           0 :         if (flush)
     122           0 :             rv  = FlushBitMap();
     123           0 :         PRStatus err = PR_Close(mFD);
     124           0 :         if (NS_SUCCEEDED(rv) && (err != PR_SUCCESS))
     125           0 :             rv = NS_ERROR_UNEXPECTED;
     126           0 :         mFD = nullptr;
     127             :     }
     128             : 
     129           0 :      if (mBitMap) {
     130           0 :          delete [] mBitMap;
     131           0 :          mBitMap = nullptr;
     132             :      }
     133             : 
     134           0 :     return rv;
     135             : }
     136             : 
     137             : 
     138             : /******************************************************************************
     139             :  *  AllocateBlocks
     140             :  *
     141             :  *  Allocates 1-4 blocks, using a first fit strategy,
     142             :  *  so that no group of blocks spans a quad block boundary.
     143             :  *
     144             :  *  Returns block number of first block allocated or -1 on failure.
     145             :  *
     146             :  *****************************************************************************/
     147             : int32_t
     148           0 : nsDiskCacheBlockFile::AllocateBlocks(int32_t numBlocks)
     149             : {
     150           0 :     const int maxPos = 32 - numBlocks;
     151           0 :     const uint32_t mask = (0x01 << numBlocks) - 1;
     152           0 :     for (unsigned int i = 0; i < mBitMapWords; ++i) {
     153           0 :         uint32_t mapWord = ~mBitMap[i]; // flip bits so free bits are 1
     154           0 :         if (mapWord) {                  // At least one free bit
     155             :             // Binary search for first free bit in word
     156           0 :             int bit = 0;
     157           0 :             if ((mapWord & 0x0FFFF) == 0) { bit |= 16; mapWord >>= 16; }
     158           0 :             if ((mapWord & 0x000FF) == 0) { bit |= 8;  mapWord >>= 8;  }
     159           0 :             if ((mapWord & 0x0000F) == 0) { bit |= 4;  mapWord >>= 4;  }
     160           0 :             if ((mapWord & 0x00003) == 0) { bit |= 2;  mapWord >>= 2;  }
     161           0 :             if ((mapWord & 0x00001) == 0) { bit |= 1;  mapWord >>= 1;  }
     162             :             // Find first fit for mask
     163           0 :             for (; bit <= maxPos; ++bit) {
     164             :                 // all bits selected by mask are 1, so free
     165           0 :                 if ((mask & mapWord) == mask) {
     166           0 :                     mBitMap[i] |= mask << bit;
     167           0 :                     mBitMapDirty = true;
     168           0 :                     return (int32_t)i * 32 + bit;
     169             :                 }
     170             :             }
     171             :         }
     172             :     }
     173             : 
     174           0 :     return -1;
     175             : }
     176             : 
     177             : 
     178             : /******************************************************************************
     179             :  *  DeallocateBlocks
     180             :  *****************************************************************************/
     181             : nsresult
     182           0 : nsDiskCacheBlockFile::DeallocateBlocks( int32_t  startBlock, int32_t  numBlocks)
     183             : {
     184           0 :     if (!mFD)  return NS_ERROR_NOT_AVAILABLE;
     185             : 
     186           0 :     if ((startBlock < 0) || ((uint32_t)startBlock > mBitMapWords * 32 - 1) ||
     187           0 :         (numBlocks < 1)  || (numBlocks > 4))
     188           0 :        return NS_ERROR_ILLEGAL_VALUE;
     189             : 
     190           0 :     const int32_t startWord = startBlock >> 5;      // Divide by 32
     191           0 :     const uint32_t startBit = startBlock & 31;      // Modulo by 32
     192             : 
     193             :     // make sure requested deallocation doesn't span a word boundary
     194           0 :     if (startBit + numBlocks > 32)  return NS_ERROR_UNEXPECTED;
     195           0 :     uint32_t mask = ((0x01 << numBlocks) - 1) << startBit;
     196             : 
     197             :     // make sure requested deallocation is currently allocated
     198           0 :     if ((mBitMap[startWord] & mask) != mask)    return NS_ERROR_ABORT;
     199             : 
     200           0 :     mBitMap[startWord] ^= mask;    // flips the bits off;
     201           0 :     mBitMapDirty = true;
     202             :     // XXX rv = FlushBitMap();  // coherency vs. performance
     203           0 :     return NS_OK;
     204             : }
     205             : 
     206             : 
     207             : /******************************************************************************
     208             :  *  WriteBlocks
     209             :  *****************************************************************************/
     210             : nsresult
     211           0 : nsDiskCacheBlockFile::WriteBlocks( void *   buffer,
     212             :                                    uint32_t size,
     213             :                                    int32_t  numBlocks,
     214             :                                    int32_t * startBlock)
     215             : {
     216             :     // presume buffer != nullptr and startBlock != nullptr
     217           0 :     NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_AVAILABLE);
     218             : 
     219             :     // allocate some blocks in the cache block file
     220           0 :     *startBlock = AllocateBlocks(numBlocks);
     221           0 :     if (*startBlock < 0)
     222           0 :         return NS_ERROR_NOT_AVAILABLE;
     223             : 
     224             :     // seek to block position
     225           0 :     int32_t blockPos = mBitMapWords * 4 + *startBlock * mBlockSize;
     226             : 
     227             :     // write the blocks
     228           0 :     return Write(blockPos, buffer, size) ? NS_OK : NS_ERROR_FAILURE;
     229             : }
     230             : 
     231             : 
     232             : /******************************************************************************
     233             :  *  ReadBlocks
     234             :  *****************************************************************************/
     235             : nsresult
     236           0 : nsDiskCacheBlockFile::ReadBlocks( void *    buffer,
     237             :                                   int32_t   startBlock,
     238             :                                   int32_t   numBlocks,
     239             :                                   int32_t * bytesRead)
     240             : {
     241             :     // presume buffer != nullptr and bytesRead != bytesRead
     242             : 
     243           0 :     if (!mFD)  return NS_ERROR_NOT_AVAILABLE;
     244           0 :     nsresult rv = VerifyAllocation(startBlock, numBlocks);
     245           0 :     if (NS_FAILED(rv))  return rv;
     246             : 
     247             :     // seek to block position
     248           0 :     int32_t blockPos = mBitMapWords * 4 + startBlock * mBlockSize;
     249           0 :     int32_t filePos = PR_Seek(mFD, blockPos, PR_SEEK_SET);
     250           0 :     if (filePos != blockPos)  return NS_ERROR_UNEXPECTED;
     251             : 
     252             :     // read the blocks
     253           0 :     int32_t bytesToRead = *bytesRead;
     254           0 :     if ((bytesToRead <= 0) || ((uint32_t)bytesToRead > mBlockSize * numBlocks)) {
     255           0 :         bytesToRead = mBlockSize * numBlocks;
     256             :     }
     257           0 :     *bytesRead = PR_Read(mFD, buffer, bytesToRead);
     258             : 
     259           0 :     CACHE_LOG_DEBUG(("CACHE: nsDiskCacheBlockFile::Read [this=%p] "
     260             :                      "returned %d / %d bytes", this, *bytesRead, bytesToRead));
     261             : 
     262           0 :     return NS_OK;
     263             : }
     264             : 
     265             : 
     266             : /******************************************************************************
     267             :  *  FlushBitMap
     268             :  *****************************************************************************/
     269             : nsresult
     270           0 : nsDiskCacheBlockFile::FlushBitMap()
     271             : {
     272           0 :     if (!mBitMapDirty)  return NS_OK;
     273             : 
     274             : #if defined(IS_LITTLE_ENDIAN)
     275           0 :     uint32_t *bitmap = new uint32_t[mBitMapWords];
     276             :     // Copy and swap to network format
     277           0 :     uint32_t *p = bitmap;
     278           0 :     for (unsigned int i = 0; i < mBitMapWords; ++i, ++p)
     279           0 :       *p = htonl(mBitMap[i]);
     280             : #else
     281             :     uint32_t *bitmap = mBitMap;
     282             : #endif
     283             : 
     284             :     // write bitmap
     285           0 :     bool written = Write(0, bitmap, mBitMapWords * 4);
     286             : #if defined(IS_LITTLE_ENDIAN)
     287           0 :     delete [] bitmap;
     288             : #endif
     289           0 :     if (!written)
     290           0 :         return NS_ERROR_UNEXPECTED;
     291             : 
     292           0 :     PRStatus err = PR_Sync(mFD);
     293           0 :     if (err != PR_SUCCESS)  return NS_ERROR_UNEXPECTED;
     294             : 
     295           0 :     mBitMapDirty = false;
     296           0 :     return NS_OK;
     297             : }
     298             : 
     299             : 
     300             : /******************************************************************************
     301             :  *  VerifyAllocation
     302             :  *
     303             :  *  Return values:
     304             :  *      NS_OK if all bits are marked allocated
     305             :  *      NS_ERROR_ILLEGAL_VALUE if parameters don't obey constraints
     306             :  *      NS_ERROR_FAILURE if some or all the bits are marked unallocated
     307             :  *
     308             :  *****************************************************************************/
     309             : nsresult
     310           0 : nsDiskCacheBlockFile::VerifyAllocation( int32_t  startBlock, int32_t  numBlocks)
     311             : {
     312           0 :     if ((startBlock < 0) || ((uint32_t)startBlock > mBitMapWords * 32 - 1) ||
     313           0 :         (numBlocks < 1)  || (numBlocks > 4))
     314           0 :        return NS_ERROR_ILLEGAL_VALUE;
     315             : 
     316           0 :     const int32_t startWord = startBlock >> 5;      // Divide by 32
     317           0 :     const uint32_t startBit = startBlock & 31;      // Modulo by 32
     318             : 
     319             :     // make sure requested deallocation doesn't span a word boundary
     320           0 :     if (startBit + numBlocks > 32)  return NS_ERROR_ILLEGAL_VALUE;
     321           0 :     uint32_t mask = ((0x01 << numBlocks) - 1) << startBit;
     322             : 
     323             :     // check if all specified blocks are currently allocated
     324           0 :     if ((mBitMap[startWord] & mask) != mask)    return NS_ERROR_FAILURE;
     325             : 
     326           0 :     return NS_OK;
     327             : }
     328             : 
     329             : 
     330             : /******************************************************************************
     331             :  *  CalcBlockFileSize
     332             :  *
     333             :  *  Return size of the block file according to the bits set in mBitmap
     334             :  *
     335             :  *****************************************************************************/
     336             : uint32_t
     337           0 : nsDiskCacheBlockFile::CalcBlockFileSize()
     338             : {
     339             :     // search for last byte in mBitMap with allocated bits
     340           0 :     uint32_t  estimatedSize = mBitMapWords * 4;
     341           0 :     int32_t   i = mBitMapWords;
     342           0 :     while (--i >= 0) {
     343           0 :         if (mBitMap[i]) break;
     344             :     }
     345             : 
     346           0 :     if (i >= 0) {
     347             :         // binary search to find last allocated bit in byte
     348           0 :         uint32_t mapWord = mBitMap[i];
     349           0 :         uint32_t lastBit = 31;
     350           0 :         if ((mapWord & 0xFFFF0000) == 0) { lastBit ^= 16; mapWord <<= 16; }
     351           0 :         if ((mapWord & 0xFF000000) == 0) { lastBit ^= 8; mapWord <<= 8; }
     352           0 :         if ((mapWord & 0xF0000000) == 0) { lastBit ^= 4; mapWord <<= 4; }
     353           0 :         if ((mapWord & 0xC0000000) == 0) { lastBit ^= 2; mapWord <<= 2; }
     354           0 :         if ((mapWord & 0x80000000) == 0) { lastBit ^= 1; mapWord <<= 1; }
     355           0 :         estimatedSize +=  (i * 32 + lastBit + 1) * mBlockSize;
     356             :     }
     357             : 
     358           0 :     return estimatedSize;
     359             : }
     360             : 
     361             : /******************************************************************************
     362             :  *  Write
     363             :  *
     364             :  *  Wrapper around PR_Write that grows file in larger chunks to combat fragmentation
     365             :  *
     366             :  *****************************************************************************/
     367             : bool
     368           0 : nsDiskCacheBlockFile::Write(int32_t offset, const void *buf, int32_t amount)
     369             : {
     370             :     /* Grow the file to 4mb right away, then double it until the file grows to 20mb.
     371             :        20mb is a magic threshold because OSX stops autodefragging files bigger than that.
     372             :        Beyond 20mb grow in 4mb chunks.
     373             :      */
     374           0 :     const int32_t upTo = offset + amount;
     375             :     // Use a conservative definition of 20MB
     376           0 :     const int32_t minPreallocate = 4*1024*1024;
     377           0 :     const int32_t maxPreallocate = 20*1000*1000;
     378           0 :     if (mFileSize < upTo) {
     379             :         // maximal file size
     380           0 :         const int32_t maxFileSize = mBitMapWords * 4 * (mBlockSize * 8 + 1);
     381           0 :         if (upTo > maxPreallocate) {
     382             :             // grow the file as a multiple of minPreallocate
     383           0 :             mFileSize = ((upTo + minPreallocate - 1) / minPreallocate) * minPreallocate;
     384             :         } else {
     385             :             // Grow quickly between 1MB to 20MB
     386           0 :             if (mFileSize)
     387           0 :                 while(mFileSize < upTo)
     388           0 :                     mFileSize *= 2;
     389           0 :             mFileSize = clamped(mFileSize, minPreallocate, maxPreallocate);
     390             :         }
     391           0 :         mFileSize = std::min(mFileSize, maxFileSize);
     392             : #if !defined(XP_MACOSX)
     393           0 :         mozilla::fallocate(mFD, mFileSize);
     394             : #endif
     395             :     }
     396           0 :     if (PR_Seek(mFD, offset, PR_SEEK_SET) != offset)
     397           0 :         return false;
     398           0 :     return PR_Write(mFD, buf, amount) == amount;
     399             : }
     400             : 
     401             : size_t
     402           0 : nsDiskCacheBlockFile::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
     403             : {
     404           0 :     return aMallocSizeOf(mBitMap) + aMallocSizeOf(mFD);
     405             : }

Generated by: LCOV version 1.13