Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/cache/nsDiskCacheMap.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* vim:set ts=4 sw=4 sts=4 cin et: */
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
#ifndef _nsDiskCacheMap_h_
8
#define _nsDiskCacheMap_h_
9
10
#include "mozilla/MemoryReporting.h"
11
#include <limits.h>
12
13
#include "prnetdb.h"
14
#include "nsDebug.h"
15
#include "nsError.h"
16
#include "nsIFile.h"
17
#include "nsITimer.h"
18
19
#include "nsDiskCache.h"
20
#include "nsDiskCacheBlockFile.h"
21
22
23
class nsDiskCacheBinding;
24
struct nsDiskCacheEntry;
25
26
/******************************************************************************
27
 *  nsDiskCacheRecord
28
 *
29
 *   Cache Location Format
30
 *
31
 *    1000 0000 0000 0000 0000 0000 0000 0000 : initialized bit
32
 *
33
 *    0011 0000 0000 0000 0000 0000 0000 0000 : File Selector (0 = separate file)
34
 *    0000 0011 0000 0000 0000 0000 0000 0000 : number of extra contiguous blocks 1-4
35
 *    0100 1100 0000 0000 0000 0000 0000 0000 : reserved bits
36
 *    0000 0000 1111 1111 1111 1111 1111 1111 : block#  0-16777216 (2^24)
37
 *
38
 *    0000 0000 1111 1111 1111 1111 0000 0000 : eFileSizeMask (size of file in k: see note)
39
 *    0000 0000 0000 0000 0000 0000 1111 1111 : eFileGenerationMask
40
 *
41
 *  File Selector:
42
 *      0 = separate file on disk
43
 *      1 = 256 byte block file
44
 *      2 = 1k block file
45
 *      3 = 4k block file
46
 *
47
 *  eFileSizeMask note:  Files larger than 65535 KiB have this limit stored in
48
 *                       the location.  The file itself must be examined to
49
 *                       determine its actual size if necessary.
50
 *
51
 *****************************************************************************/
52
53
/*
54
  We have 3 block files with roughly the same max size (32MB)
55
    1 - block size 256B, number of blocks 131072
56
    2 - block size  1kB, number of blocks  32768
57
    3 - block size  4kB, number of blocks   8192
58
*/
59
0
#define kNumBlockFiles             3
60
0
#define SIZE_SHIFT(idx)            (2 * ((idx) - 1))
61
0
#define BLOCK_SIZE_FOR_INDEX(idx)  ((idx) ? (256    << SIZE_SHIFT(idx)) : 0)
62
0
#define BITMAP_SIZE_FOR_INDEX(idx) ((idx) ? (131072 >> SIZE_SHIFT(idx)) : 0)
63
64
// Min and max values for the number of records in the DiskCachemap
65
0
#define kMinRecordCount    512
66
67
#define kSeparateFile      0
68
0
#define kBuckets           (1 << 5)    // must be a power of 2!
69
70
// Maximum size in K which can be stored in the location (see eFileSizeMask).
71
// Both data and metadata can be larger, but only up to kMaxDataSizeK can be
72
// counted into total cache size. I.e. if there are entries where either data or
73
// metadata is larger than kMaxDataSizeK, the total cache size will be
74
// inaccurate (smaller) than the actual cache size. The alternative is to stat
75
// the files to find the real size, which was decided against for performance
76
// reasons. See bug #651100 comment #21.
77
0
#define kMaxDataSizeK      0xFFFF
78
79
// preallocate up to 1MB of separate cache file
80
0
#define kPreallocateLimit  1 * 1024 * 1024
81
82
// The minimum amount of milliseconds to wait before re-attempting to
83
// revalidate the cache.
84
0
#define kRevalidateCacheTimeout 3000
85
0
#define kRevalidateCacheTimeoutTolerance 10
86
0
#define kRevalidateCacheErrorTimeout 1000
87
88
class nsDiskCacheRecord {
89
90
private:
91
    uint32_t    mHashNumber;
92
    uint32_t    mEvictionRank;
93
    uint32_t    mDataLocation;
94
    uint32_t    mMetaLocation;
95
96
    enum {
97
        eLocationInitializedMask = 0x80000000,
98
99
        eLocationSelectorMask    = 0x30000000,
100
        eLocationSelectorOffset  = 28,
101
102
        eExtraBlocksMask         = 0x03000000,
103
        eExtraBlocksOffset       = 24,
104
105
        eReservedMask            = 0x4C000000,
106
107
        eBlockNumberMask         = 0x00FFFFFF,
108
109
        eFileSizeMask            = 0x00FFFF00,
110
        eFileSizeOffset          = 8,
111
        eFileGenerationMask      = 0x000000FF,
112
        eFileReservedMask        = 0x4F000000
113
114
    };
115
116
public:
117
    nsDiskCacheRecord()
118
        :   mHashNumber(0), mEvictionRank(0), mDataLocation(0), mMetaLocation(0)
119
0
    {
120
0
    }
121
122
    bool    ValidRecord()
123
0
    {
124
0
        if ((mDataLocation & eReservedMask) || (mMetaLocation & eReservedMask))
125
0
            return false;
126
0
        return true;
127
0
    }
128
129
    // HashNumber accessors
130
0
    uint32_t  HashNumber() const                  { return mHashNumber; }
131
0
    void      SetHashNumber( uint32_t hashNumber) { mHashNumber = hashNumber; }
132
133
    // EvictionRank accessors
134
0
    uint32_t  EvictionRank() const              { return mEvictionRank; }
135
0
    void      SetEvictionRank( uint32_t rank)   { mEvictionRank = rank ? rank : 1; }
136
137
    // DataLocation accessors
138
0
    bool      DataLocationInitialized() const { return 0 != (mDataLocation & eLocationInitializedMask); }
139
0
    void      ClearDataLocation()       { mDataLocation = 0; }
140
141
    uint32_t  DataFile() const
142
0
    {
143
0
        return (uint32_t)(mDataLocation & eLocationSelectorMask) >> eLocationSelectorOffset;
144
0
    }
145
146
    void      SetDataBlocks( uint32_t index, uint32_t startBlock, uint32_t blockCount)
147
0
    {
148
0
        // clear everything
149
0
        mDataLocation = 0;
150
0
151
0
        // set file index
152
0
        NS_ASSERTION( index < (kNumBlockFiles + 1), "invalid location index");
153
0
        NS_ASSERTION( index > 0,"invalid location index");
154
0
        mDataLocation |= (index << eLocationSelectorOffset) & eLocationSelectorMask;
155
0
156
0
        // set startBlock
157
0
        NS_ASSERTION(startBlock == (startBlock & eBlockNumberMask), "invalid block number");
158
0
        mDataLocation |= startBlock & eBlockNumberMask;
159
0
160
0
        // set blockCount
161
0
        NS_ASSERTION( (blockCount>=1) && (blockCount<=4),"invalid block count");
162
0
        --blockCount;
163
0
        mDataLocation |= (blockCount << eExtraBlocksOffset) & eExtraBlocksMask;
164
0
165
0
        mDataLocation |= eLocationInitializedMask;
166
0
    }
167
168
    uint32_t   DataBlockCount() const
169
0
    {
170
0
        return (uint32_t)((mDataLocation & eExtraBlocksMask) >> eExtraBlocksOffset) + 1;
171
0
    }
172
173
    uint32_t   DataStartBlock() const
174
0
    {
175
0
        return (mDataLocation & eBlockNumberMask);
176
0
    }
177
178
    uint32_t   DataBlockSize() const
179
0
    {
180
0
        return BLOCK_SIZE_FOR_INDEX(DataFile());
181
0
    }
182
183
0
    uint32_t   DataFileSize() const  { return (mDataLocation & eFileSizeMask) >> eFileSizeOffset; }
184
    void       SetDataFileSize(uint32_t  size)
185
0
    {
186
0
        NS_ASSERTION((mDataLocation & eFileReservedMask) == 0, "bad location");
187
0
        mDataLocation &= ~eFileSizeMask;    // clear eFileSizeMask
188
0
        mDataLocation |= (size << eFileSizeOffset) & eFileSizeMask;
189
0
    }
190
191
    uint8_t   DataFileGeneration() const
192
0
    {
193
0
        return (mDataLocation & eFileGenerationMask);
194
0
    }
195
196
    void       SetDataFileGeneration( uint8_t generation)
197
0
    {
198
0
        // clear everything, (separate file index = 0)
199
0
        mDataLocation = 0;
200
0
        mDataLocation |= generation & eFileGenerationMask;
201
0
        mDataLocation |= eLocationInitializedMask;
202
0
    }
203
204
    // MetaLocation accessors
205
0
    bool      MetaLocationInitialized() const { return 0 != (mMetaLocation & eLocationInitializedMask); }
206
0
    void      ClearMetaLocation()             { mMetaLocation = 0; }
207
0
    uint32_t  MetaLocation() const            { return mMetaLocation; }
208
209
    uint32_t  MetaFile() const
210
0
    {
211
0
        return (uint32_t)(mMetaLocation & eLocationSelectorMask) >> eLocationSelectorOffset;
212
0
    }
213
214
    void      SetMetaBlocks( uint32_t index, uint32_t startBlock, uint32_t blockCount)
215
0
    {
216
0
        // clear everything
217
0
        mMetaLocation = 0;
218
0
219
0
        // set file index
220
0
        NS_ASSERTION( index < (kNumBlockFiles + 1), "invalid location index");
221
0
        NS_ASSERTION( index > 0, "invalid location index");
222
0
        mMetaLocation |= (index << eLocationSelectorOffset) & eLocationSelectorMask;
223
0
224
0
        // set startBlock
225
0
        NS_ASSERTION(startBlock == (startBlock & eBlockNumberMask), "invalid block number");
226
0
        mMetaLocation |= startBlock & eBlockNumberMask;
227
0
228
0
        // set blockCount
229
0
        NS_ASSERTION( (blockCount>=1) && (blockCount<=4),"invalid block count");
230
0
        --blockCount;
231
0
        mMetaLocation |= (blockCount << eExtraBlocksOffset) & eExtraBlocksMask;
232
0
233
0
        mMetaLocation |= eLocationInitializedMask;
234
0
    }
235
236
    uint32_t   MetaBlockCount() const
237
0
    {
238
0
        return (uint32_t)((mMetaLocation & eExtraBlocksMask) >> eExtraBlocksOffset) + 1;
239
0
    }
240
241
    uint32_t   MetaStartBlock() const
242
0
    {
243
0
        return (mMetaLocation & eBlockNumberMask);
244
0
    }
245
246
    uint32_t   MetaBlockSize() const
247
0
    {
248
0
        return BLOCK_SIZE_FOR_INDEX(MetaFile());
249
0
    }
250
251
0
    uint32_t   MetaFileSize() const  { return (mMetaLocation & eFileSizeMask) >> eFileSizeOffset; }
252
    void       SetMetaFileSize(uint32_t  size)
253
0
    {
254
0
        mMetaLocation &= ~eFileSizeMask;    // clear eFileSizeMask
255
0
        mMetaLocation |= (size << eFileSizeOffset) & eFileSizeMask;
256
0
    }
257
258
    uint8_t   MetaFileGeneration() const
259
0
    {
260
0
        return (mMetaLocation & eFileGenerationMask);
261
0
    }
262
263
    void       SetMetaFileGeneration( uint8_t generation)
264
0
    {
265
0
        // clear everything, (separate file index = 0)
266
0
        mMetaLocation = 0;
267
0
        mMetaLocation |= generation & eFileGenerationMask;
268
0
        mMetaLocation |= eLocationInitializedMask;
269
0
    }
270
271
    uint8_t   Generation() const
272
0
    {
273
0
        if ((mDataLocation & eLocationInitializedMask)  &&
274
0
            (DataFile() == 0))
275
0
            return DataFileGeneration();
276
0
277
0
        if ((mMetaLocation & eLocationInitializedMask)  &&
278
0
            (MetaFile() == 0))
279
0
            return MetaFileGeneration();
280
0
281
0
        return 0;  // no generation
282
0
    }
283
284
#if defined(IS_LITTLE_ENDIAN)
285
    void        Swap()
286
0
    {
287
0
        mHashNumber   = htonl(mHashNumber);
288
0
        mEvictionRank = htonl(mEvictionRank);
289
0
        mDataLocation = htonl(mDataLocation);
290
0
        mMetaLocation = htonl(mMetaLocation);
291
0
    }
292
#endif
293
294
#if defined(IS_LITTLE_ENDIAN)
295
    void        Unswap()
296
0
    {
297
0
        mHashNumber   = ntohl(mHashNumber);
298
0
        mEvictionRank = ntohl(mEvictionRank);
299
0
        mDataLocation = ntohl(mDataLocation);
300
0
        mMetaLocation = ntohl(mMetaLocation);
301
0
    }
302
#endif
303
304
};
305
306
307
/******************************************************************************
308
 *  nsDiskCacheRecordVisitor
309
 *****************************************************************************/
310
311
enum {  kDeleteRecordAndContinue = -1,
312
        kStopVisitingRecords     =  0,
313
        kVisitNextRecord         =  1
314
};
315
316
class nsDiskCacheRecordVisitor {
317
    public:
318
319
    virtual int32_t  VisitRecord( nsDiskCacheRecord *  mapRecord) = 0;
320
};
321
322
323
/******************************************************************************
324
 *  nsDiskCacheHeader
325
 *****************************************************************************/
326
327
struct nsDiskCacheHeader {
328
    uint32_t    mVersion;                           // cache version.
329
    uint32_t    mDataSize;                          // size of cache in units of 1024bytes.
330
    int32_t     mEntryCount;                        // number of entries stored in cache.
331
    uint32_t    mIsDirty;                           // dirty flag.
332
    int32_t     mRecordCount;                       // Number of records
333
    uint32_t    mEvictionRank[kBuckets];            // Highest EvictionRank of the bucket
334
    uint32_t    mBucketUsage[kBuckets];             // Number of used entries in the bucket
335
336
    nsDiskCacheHeader()
337
        : mVersion(nsDiskCache::kCurrentVersion)
338
        , mDataSize(0)
339
        , mEntryCount(0)
340
        , mIsDirty(true)
341
        , mRecordCount(0)
342
0
    {}
343
344
    void        Swap()
345
0
    {
346
0
#if defined(IS_LITTLE_ENDIAN)
347
0
        mVersion     = htonl(mVersion);
348
0
        mDataSize    = htonl(mDataSize);
349
0
        mEntryCount  = htonl(mEntryCount);
350
0
        mIsDirty     = htonl(mIsDirty);
351
0
        mRecordCount = htonl(mRecordCount);
352
0
353
0
        for (uint32_t i = 0; i < kBuckets ; i++) {
354
0
            mEvictionRank[i] = htonl(mEvictionRank[i]);
355
0
            mBucketUsage[i]  = htonl(mBucketUsage[i]);
356
0
        }
357
0
#endif
358
0
    }
359
360
    void        Unswap()
361
0
    {
362
0
#if defined(IS_LITTLE_ENDIAN)
363
0
        mVersion     = ntohl(mVersion);
364
0
        mDataSize    = ntohl(mDataSize);
365
0
        mEntryCount  = ntohl(mEntryCount);
366
0
        mIsDirty     = ntohl(mIsDirty);
367
0
        mRecordCount = ntohl(mRecordCount);
368
0
369
0
        for (uint32_t i = 0; i < kBuckets ; i++) {
370
0
            mEvictionRank[i] = ntohl(mEvictionRank[i]);
371
0
            mBucketUsage[i]  = ntohl(mBucketUsage[i]);
372
0
        }
373
0
#endif
374
0
    }
375
};
376
377
378
/******************************************************************************
379
 *  nsDiskCacheMap
380
 *****************************************************************************/
381
382
class nsDiskCacheMap {
383
public:
384
385
     nsDiskCacheMap() :
386
        mCacheDirectory(nullptr),
387
        mMapFD(nullptr),
388
        mCleanFD(nullptr),
389
        mRecordArray(nullptr),
390
        mBufferSize(0),
391
        mBuffer(nullptr),
392
        mMaxRecordCount(16384), // this default value won't matter
393
        mIsDirtyCacheFlushed(false),
394
        mLastInvalidateTime(0)
395
0
    { }
396
397
    ~nsDiskCacheMap()
398
0
    {
399
0
        (void) Close(true);
400
0
    }
401
402
/**
403
 *  File Operations
404
 *
405
 *  Open
406
 *
407
 *  Creates a new cache map file if one doesn't exist.
408
 *  Returns error if it detects change in format or cache wasn't closed.
409
 */
410
    nsresult  Open( nsIFile *  cacheDirectory,
411
                    nsDiskCache::CorruptCacheInfo *  corruptInfo);
412
    nsresult  Close(bool flush);
413
    nsresult  Trim();
414
415
    nsresult  FlushHeader();
416
    nsresult  FlushRecords( bool unswap);
417
418
    void      NotifyCapacityChange(uint32_t capacity);
419
420
/**
421
 *  Record operations
422
 */
423
    nsresult AddRecord( nsDiskCacheRecord *  mapRecord, nsDiskCacheRecord * oldRecord);
424
    nsresult UpdateRecord( nsDiskCacheRecord *  mapRecord);
425
    nsresult FindRecord( uint32_t  hashNumber, nsDiskCacheRecord *  mapRecord);
426
    nsresult DeleteRecord( nsDiskCacheRecord *  mapRecord);
427
    nsresult VisitRecords( nsDiskCacheRecordVisitor * visitor);
428
    nsresult EvictRecords( nsDiskCacheRecordVisitor * visitor);
429
430
/**
431
 *  Disk Entry operations
432
 */
433
    nsresult    DeleteStorage( nsDiskCacheRecord *  record);
434
435
    nsresult    GetFileForDiskCacheRecord( nsDiskCacheRecord * record,
436
                                           bool                meta,
437
                                           bool                createPath,
438
                                           nsIFile **          result);
439
440
    nsresult    GetLocalFileForDiskCacheRecord( nsDiskCacheRecord *  record,
441
                                                bool                 meta,
442
                                                bool                 createPath,
443
                                                nsIFile **           result);
444
445
    // On success, this returns the buffer owned by nsDiskCacheMap,
446
    // so it must not be deleted by the caller.
447
    nsDiskCacheEntry * ReadDiskCacheEntry( nsDiskCacheRecord *  record);
448
449
    nsresult    WriteDiskCacheEntry( nsDiskCacheBinding *  binding);
450
451
    nsresult    ReadDataCacheBlocks(nsDiskCacheBinding * binding, char * buffer, uint32_t size);
452
    nsresult    WriteDataCacheBlocks(nsDiskCacheBinding * binding, char * buffer, uint32_t size);
453
    nsresult    DeleteStorage( nsDiskCacheRecord * record, bool metaData);
454
455
    /**
456
     *  Statistical Operations
457
     */
458
    void     IncrementTotalSize( uint32_t  delta)
459
0
             {
460
0
                mHeader.mDataSize += delta;
461
0
                mHeader.mIsDirty   = true;
462
0
             }
463
464
    void     DecrementTotalSize( uint32_t  delta)
465
0
             {
466
0
                NS_ASSERTION(mHeader.mDataSize >= delta, "disk cache size negative?");
467
0
                mHeader.mDataSize  = mHeader.mDataSize > delta ? mHeader.mDataSize - delta : 0;
468
0
                mHeader.mIsDirty   = true;
469
0
             }
470
471
    inline void IncrementTotalSize( uint32_t  blocks, uint32_t blockSize)
472
0
             {
473
0
                // Round up to nearest K
474
0
                IncrementTotalSize(((blocks*blockSize) + 0x03FF) >> 10);
475
0
             }
476
477
    inline void DecrementTotalSize( uint32_t  blocks, uint32_t blockSize)
478
0
             {
479
0
                // Round up to nearest K
480
0
                DecrementTotalSize(((blocks*blockSize) + 0x03FF) >> 10);
481
0
             }
482
483
0
    uint32_t TotalSize()   { return mHeader.mDataSize; }
484
485
0
    int32_t  EntryCount()  { return mHeader.mEntryCount; }
486
487
    size_t  SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
488
489
490
private:
491
492
    /**
493
     *  Private methods
494
     */
495
    nsresult    OpenBlockFiles(nsDiskCache::CorruptCacheInfo *  corruptInfo);
496
    nsresult    CloseBlockFiles(bool flush);
497
    bool        CacheFilesExist();
498
499
    nsresult    CreateCacheSubDirectories();
500
501
    uint32_t    CalculateFileIndex(uint32_t size);
502
503
    nsresult    GetBlockFileForIndex( uint32_t index, nsIFile ** result);
504
0
    uint32_t    GetBlockSizeForIndex( uint32_t index) const {
505
0
        return BLOCK_SIZE_FOR_INDEX(index);
506
0
    }
507
0
    uint32_t    GetBitMapSizeForIndex( uint32_t index) const {
508
0
        return BITMAP_SIZE_FOR_INDEX(index);
509
0
    }
510
511
    // returns the bucket number
512
0
    uint32_t GetBucketIndex( uint32_t hashNumber) const {
513
0
        return (hashNumber & (kBuckets - 1));
514
0
    }
515
516
    // Gets the size of the bucket (in number of records)
517
0
    uint32_t GetRecordsPerBucket() const {
518
0
        return mHeader.mRecordCount / kBuckets;
519
0
    }
520
521
    // Gets the first record in the bucket
522
0
    nsDiskCacheRecord *GetFirstRecordInBucket(uint32_t bucket) const {
523
0
        return mRecordArray + bucket * GetRecordsPerBucket();
524
0
    }
525
526
    uint32_t GetBucketRank(uint32_t bucketIndex, uint32_t targetRank);
527
528
    int32_t  VisitEachRecord(uint32_t                    bucketIndex,
529
                             nsDiskCacheRecordVisitor *  visitor,
530
                             uint32_t                    evictionRank);
531
532
    nsresult GrowRecords();
533
    nsresult ShrinkRecords();
534
535
    nsresult EnsureBuffer(uint32_t bufSize);
536
537
    // The returned structure will point to the buffer owned by nsDiskCacheMap,
538
    // so it must not be deleted by the caller.
539
    nsDiskCacheEntry *  CreateDiskCacheEntry(nsDiskCacheBinding *  binding,
540
                                             uint32_t * size);
541
542
    // Initializes the _CACHE_CLEAN_ related functionality
543
    nsresult InitCacheClean(nsIFile *  cacheDirectory,
544
                            nsDiskCache::CorruptCacheInfo *  corruptInfo);
545
    // Writes out a value of '0' or '1' in the _CACHE_CLEAN_ file
546
    nsresult WriteCacheClean(bool clean);
547
    // Resets the timout for revalidating the cache
548
    nsresult ResetCacheTimer(int32_t timeout = kRevalidateCacheTimeout);
549
    // Invalidates the cache, calls WriteCacheClean and ResetCacheTimer
550
    nsresult InvalidateCache();
551
    // Determines if the cache is in a safe state
552
    bool IsCacheInSafeState();
553
    // Revalidates the cache by writting out the header, records, and finally
554
    // by calling WriteCacheClean(true).
555
    nsresult RevalidateCache();
556
    // Timer which revalidates the cache
557
    static void RevalidateTimerCallback(nsITimer *aTimer, void *arg);
558
559
/**
560
 *  data members
561
 */
562
private:
563
    nsCOMPtr<nsITimer>      mCleanCacheTimer;
564
    nsCOMPtr<nsIFile>       mCacheDirectory;
565
    PRFileDesc *            mMapFD;
566
    PRFileDesc *            mCleanFD;
567
    nsDiskCacheRecord *     mRecordArray;
568
    nsDiskCacheBlockFile    mBlockFile[kNumBlockFiles];
569
    uint32_t                mBufferSize;
570
    char *                  mBuffer;
571
    nsDiskCacheHeader       mHeader;
572
    int32_t                 mMaxRecordCount;
573
    bool                    mIsDirtyCacheFlushed;
574
    PRIntervalTime          mLastInvalidateTime;
575
};
576
577
#endif // _nsDiskCacheMap_h_