Bug 458861. Validate TrueType headers before activating downloaded font. r=roc, sr...
[wine-gecko.git] / netwerk / cache / src / nsDiskCacheBlockFile.cpp
blob2f9a2fdb8ee6d5c52e856f9332da663a032568b6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is nsDiskCacheBlockFile.cpp, released
17 * April 12, 2001.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 2001
22 * the Initial Developer. All Rights Reserved.
24 * Contributor(s):
25 * Gordon Sheridan <gordon@netscape.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "nsDiskCache.h"
42 #include "nsDiskCacheBlockFile.h"
44 /******************************************************************************
45 * nsDiskCacheBlockFile -
46 *****************************************************************************/
48 const unsigned short kBitMapBytes = 4096;
49 const unsigned short kBitMapWords = (kBitMapBytes/4);
51 /******************************************************************************
52 * Open
53 *****************************************************************************/
54 nsresult
55 nsDiskCacheBlockFile::Open( nsILocalFile * blockFile, PRUint32 blockSize)
57 PRInt32 fileSize;
59 mBlockSize = blockSize;
61 // open the file - restricted to user, the data could be confidential
62 nsresult rv = blockFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE, 00600, &mFD);
63 if (NS_FAILED(rv)) return rv; // unable to open or create file
65 // allocate bit map buffer
66 mBitMap = new PRUint32[kBitMapWords];
67 if (!mBitMap) {
68 rv = NS_ERROR_OUT_OF_MEMORY;
69 goto error_exit;
72 // check if we just creating the file
73 fileSize = PR_Available(mFD);
74 if (fileSize < 0) {
75 // XXX an error occurred. We could call PR_GetError(), but how would that help?
76 rv = NS_ERROR_UNEXPECTED;
77 goto error_exit;
79 if (fileSize == 0) {
80 // initialize bit map and write it
81 memset(mBitMap, 0, kBitMapBytes);
82 PRInt32 bytesWritten = PR_Write(mFD, mBitMap, kBitMapBytes);
83 if (bytesWritten < kBitMapBytes)
84 goto error_exit;
86 } else if (fileSize < kBitMapBytes) {
87 rv = NS_ERROR_UNEXPECTED; // XXX NS_ERROR_CACHE_INVALID;
88 goto error_exit;
90 } else {
91 // read the bit map
92 const PRInt32 bytesRead = PR_Read(mFD, mBitMap, kBitMapBytes);
93 if (bytesRead < kBitMapBytes) {
94 rv = NS_ERROR_UNEXPECTED;
95 goto error_exit;
97 #if defined(IS_LITTLE_ENDIAN)
98 // Swap from network format
99 for (int i = 0; i < kBitMapWords; ++i)
100 mBitMap[i] = ntohl(mBitMap[i]);
101 #endif
102 // validate block file size
103 // Because not whole blocks are written, the size may be a
104 // little bit smaller than used blocks times blocksize,
105 // because the last block will generally not be 'whole'.
106 const PRUint32 estimatedSize = CalcBlockFileSize();
107 if ((PRUint32)fileSize + blockSize < estimatedSize) {
108 rv = NS_ERROR_UNEXPECTED;
109 goto error_exit;
112 return NS_OK;
114 error_exit:
115 Close(PR_FALSE);
116 return rv;
120 /******************************************************************************
121 * Close
122 *****************************************************************************/
123 nsresult
124 nsDiskCacheBlockFile::Close(PRBool flush)
126 nsresult rv = NS_OK;
128 if (mFD) {
129 if (flush)
130 rv = FlushBitMap();
131 PRStatus err = PR_Close(mFD);
132 if (NS_SUCCEEDED(rv) && (err != PR_SUCCESS))
133 rv = NS_ERROR_UNEXPECTED;
134 mFD = nsnull;
137 if (mBitMap) {
138 delete [] mBitMap;
139 mBitMap = nsnull;
142 return rv;
146 /******************************************************************************
147 * AllocateBlocks
149 * Allocates 1-4 blocks, using a first fit strategy,
150 * so that no group of blocks spans a quad block boundary.
152 * Returns block number of first block allocated or -1 on failure.
154 *****************************************************************************/
155 PRInt32
156 nsDiskCacheBlockFile::AllocateBlocks(PRInt32 numBlocks)
158 const int maxPos = 32 - numBlocks;
159 const PRUint32 mask = (0x01 << numBlocks) - 1;
160 for (int i = 0; i < kBitMapWords; ++i) {
161 PRUint32 mapWord = ~mBitMap[i]; // flip bits so free bits are 1
162 if (mapWord) { // At least one free bit
163 // Binary search for first free bit in word
164 int bit = 0;
165 if ((mapWord & 0x0FFFF) == 0) { bit |= 16; mapWord >>= 16; }
166 if ((mapWord & 0x000FF) == 0) { bit |= 8; mapWord >>= 8; }
167 if ((mapWord & 0x0000F) == 0) { bit |= 4; mapWord >>= 4; }
168 if ((mapWord & 0x00003) == 0) { bit |= 2; mapWord >>= 2; }
169 if ((mapWord & 0x00001) == 0) { bit |= 1; mapWord >>= 1; }
170 // Find first fit for mask
171 for (; bit <= maxPos; ++bit) {
172 // all bits selected by mask are 1, so free
173 if ((mask & mapWord) == mask) {
174 mBitMap[i] |= mask << bit;
175 mBitMapDirty = PR_TRUE;
176 return i * 32 + bit;
182 return -1;
186 /******************************************************************************
187 * DeallocateBlocks
188 *****************************************************************************/
189 nsresult
190 nsDiskCacheBlockFile::DeallocateBlocks( PRInt32 startBlock, PRInt32 numBlocks)
192 if (!mFD) return NS_ERROR_NOT_AVAILABLE;
194 if ((startBlock < 0) || (startBlock > kBitMapBytes * 8 - 1) ||
195 (numBlocks < 1) || (numBlocks > 4))
196 return NS_ERROR_ILLEGAL_VALUE;
198 const PRInt32 startWord = startBlock >> 5; // Divide by 32
199 const PRUint32 startBit = startBlock & 31; // Modulo by 32
201 // make sure requested deallocation doesn't span a word boundary
202 if (startBit + numBlocks > 32) return NS_ERROR_UNEXPECTED;
203 PRUint32 mask = ((0x01 << numBlocks) - 1) << startBit;
205 // make sure requested deallocation is currently allocated
206 if ((mBitMap[startWord] & mask) != mask) return NS_ERROR_ABORT;
208 mBitMap[startWord] ^= mask; // flips the bits off;
209 mBitMapDirty = PR_TRUE;
210 // XXX rv = FlushBitMap(); // coherency vs. performance
211 return NS_OK;
215 /******************************************************************************
216 * WriteBlocks
217 *****************************************************************************/
218 nsresult
219 nsDiskCacheBlockFile::WriteBlocks( void * buffer,
220 PRUint32 size,
221 PRInt32 numBlocks,
222 PRInt32 * startBlock)
224 // presume buffer != nsnull and startBlock != nsnull
225 NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_AVAILABLE);
227 // allocate some blocks in the cache block file
228 *startBlock = AllocateBlocks(numBlocks);
229 NS_ENSURE_STATE(*startBlock >= 0);
231 // seek to block position
232 PRInt32 blockPos = kBitMapBytes + *startBlock * mBlockSize;
233 PRInt32 filePos = PR_Seek(mFD, blockPos, PR_SEEK_SET);
234 NS_ENSURE_STATE(filePos == blockPos);
236 // write the blocks
237 PRInt32 bytesWritten = PR_Write(mFD, buffer, size);
238 NS_ENSURE_STATE(bytesWritten == size);
240 // write the bit map and flush the file
241 // XXX except we would take a severe performance hit
242 // XXX rv = FlushBitMap();
243 return NS_OK;
247 /******************************************************************************
248 * ReadBlocks
249 *****************************************************************************/
250 nsresult
251 nsDiskCacheBlockFile::ReadBlocks( void * buffer,
252 PRInt32 startBlock,
253 PRInt32 numBlocks,
254 PRInt32 * bytesRead)
256 // presume buffer != nsnull and bytesRead != bytesRead
258 if (!mFD) return NS_ERROR_NOT_AVAILABLE;
259 nsresult rv = VerifyAllocation(startBlock, numBlocks);
260 if (NS_FAILED(rv)) return rv;
262 // seek to block position
263 PRInt32 blockPos = kBitMapBytes + startBlock * mBlockSize;
264 PRInt32 filePos = PR_Seek(mFD, blockPos, PR_SEEK_SET);
265 if (filePos != blockPos) return NS_ERROR_UNEXPECTED;
267 // read the blocks
268 PRInt32 bytesToRead = *bytesRead;
269 if ((bytesToRead <= 0) || ((PRUint32)bytesToRead > mBlockSize * numBlocks)) {
270 bytesToRead = mBlockSize * numBlocks;
272 *bytesRead = PR_Read(mFD, buffer, bytesToRead);
274 return NS_OK;
278 /******************************************************************************
279 * FlushBitMap
280 *****************************************************************************/
281 nsresult
282 nsDiskCacheBlockFile::FlushBitMap()
284 if (!mBitMapDirty) return NS_OK;
286 // seek to bitmap
287 PRInt32 filePos = PR_Seek(mFD, 0, PR_SEEK_SET);
288 if (filePos != 0) return NS_ERROR_UNEXPECTED;
290 #if defined(IS_LITTLE_ENDIAN)
291 PRUint32 bitmap[kBitMapWords];
292 // Copy and swap to network format
293 PRUint32 *p = bitmap;
294 for (int i = 0; i < kBitMapWords; ++i, ++p)
295 *p = htonl(mBitMap[i]);
296 #else
297 PRUint32 *bitmap = mBitMap;
298 #endif
300 // write bitmap
301 PRInt32 bytesWritten = PR_Write(mFD, bitmap, kBitMapBytes);
302 if (bytesWritten < kBitMapBytes) return NS_ERROR_UNEXPECTED;
304 PRStatus err = PR_Sync(mFD);
305 if (err != PR_SUCCESS) return NS_ERROR_UNEXPECTED;
307 mBitMapDirty = PR_FALSE;
308 return NS_OK;
312 /******************************************************************************
313 * VerifyAllocation
315 * Return values:
316 * NS_OK if all bits are marked allocated
317 * NS_ERROR_ILLEGAL_VALUE if parameters don't obey constraints
318 * NS_ERROR_FAILURE if some or all the bits are marked unallocated
320 *****************************************************************************/
321 nsresult
322 nsDiskCacheBlockFile::VerifyAllocation( PRInt32 startBlock, PRInt32 numBlocks)
324 if ((startBlock < 0) || (startBlock > kBitMapBytes * 8 - 1) ||
325 (numBlocks < 1) || (numBlocks > 4))
326 return NS_ERROR_ILLEGAL_VALUE;
328 const PRInt32 startWord = startBlock >> 5; // Divide by 32
329 const PRUint32 startBit = startBlock & 31; // Modulo by 32
331 // make sure requested deallocation doesn't span a word boundary
332 if (startBit + numBlocks > 32) return NS_ERROR_ILLEGAL_VALUE;
333 PRUint32 mask = ((0x01 << numBlocks) - 1) << startBit;
335 // check if all specified blocks are currently allocated
336 if ((mBitMap[startWord] & mask) != mask) return NS_ERROR_FAILURE;
338 return NS_OK;
342 /******************************************************************************
343 * CalcBlockFileSize
345 * Return size of the block file according to the bits set in mBitmap
347 *****************************************************************************/
348 PRUint32
349 nsDiskCacheBlockFile::CalcBlockFileSize()
351 // search for last byte in mBitMap with allocated bits
352 PRUint32 estimatedSize = kBitMapBytes;
353 PRInt32 i = kBitMapWords;
354 while (--i >= 0) {
355 if (mBitMap[i]) break;
358 if (i >= 0) {
359 // binary search to find last allocated bit in byte
360 PRUint32 mapWord = mBitMap[i];
361 PRUint32 lastBit = 31;
362 if ((mapWord & 0xFFFF0000) == 0) { lastBit ^= 16; mapWord <<= 16; }
363 if ((mapWord & 0xFF000000) == 0) { lastBit ^= 8; mapWord <<= 8; }
364 if ((mapWord & 0xF0000000) == 0) { lastBit ^= 4; mapWord <<= 4; }
365 if ((mapWord & 0xC0000000) == 0) { lastBit ^= 2; mapWord <<= 2; }
366 if ((mapWord & 0x80000000) == 0) { lastBit ^= 1; mapWord <<= 1; }
367 estimatedSize += (i * 32 + lastBit + 1) * mBlockSize;
370 return estimatedSize;