Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/cache2/CacheFileIOManager.h
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#ifndef CacheFileIOManager__h__
6
#define CacheFileIOManager__h__
7
8
#include "CacheIOThread.h"
9
#include "CacheStorageService.h"
10
#include "CacheHashUtils.h"
11
#include "nsIEventTarget.h"
12
#include "nsINamed.h"
13
#include "nsITimer.h"
14
#include "nsCOMPtr.h"
15
#include "mozilla/Atomics.h"
16
#include "mozilla/SHA1.h"
17
#include "mozilla/StaticPtr.h"
18
#include "mozilla/TimeStamp.h"
19
#include "nsTArray.h"
20
#include "nsString.h"
21
#include "nsTHashtable.h"
22
#include "prio.h"
23
24
//#define DEBUG_HANDLES 1
25
26
class nsIFile;
27
class nsITimer;
28
class nsIDirectoryEnumerator;
29
class nsILoadContextInfo;
30
31
namespace mozilla {
32
namespace net {
33
34
class CacheFile;
35
class CacheFileIOListener;
36
37
#ifdef DEBUG_HANDLES
38
class CacheFileHandlesEntry;
39
#endif
40
41
0
#define ENTRIES_DIR "entries"
42
0
#define DOOMED_DIR  "doomed"
43
0
#define TRASH_DIR   "trash"
44
45
46
class CacheFileHandle final : public nsISupports
47
{
48
public:
49
  enum class PinningStatus : uint32_t {
50
    UNKNOWN,
51
    NON_PINNED,
52
    PINNED
53
  };
54
55
  NS_DECL_THREADSAFE_ISUPPORTS
56
  bool DispatchRelease();
57
58
  CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority, PinningStatus aPinning);
59
  CacheFileHandle(const nsACString &aKey, bool aPriority, PinningStatus aPinning);
60
  void Log();
61
0
  bool IsDoomed() const { return mIsDoomed; }
62
0
  const SHA1Sum::Hash *Hash() const { return mHash; }
63
0
  int64_t FileSize() const { return mFileSize; }
64
  uint32_t FileSizeInK() const;
65
0
  bool IsPriority() const { return mPriority; }
66
0
  bool FileExists() const { return mFileExists; }
67
0
  bool IsClosed() const { return mClosed; }
68
0
  bool IsSpecialFile() const { return mSpecialFile; }
69
0
  nsCString & Key() { return mKey; }
70
71
  // Returns false when this handle has been doomed based on the pinning state update.
72
  bool SetPinned(bool aPinned);
73
0
  void SetInvalid() { mInvalid = true; }
74
75
  // Memory reporting
76
  size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
77
  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
78
79
private:
80
  friend class CacheFileIOManager;
81
  friend class CacheFileHandles;
82
  friend class ReleaseNSPRHandleEvent;
83
84
  virtual ~CacheFileHandle();
85
86
  const SHA1Sum::Hash *mHash;
87
  mozilla::Atomic<bool, ReleaseAcquire> mIsDoomed;
88
  mozilla::Atomic<bool, ReleaseAcquire> mClosed;
89
90
  // mPriority and mSpecialFile are plain "bool", not "bool:1", so as to
91
  // avoid bitfield races with the byte containing mInvalid et al.  See
92
  // bug 1278502.
93
  bool const           mPriority;
94
  bool const           mSpecialFile;
95
96
  mozilla::Atomic<bool, Relaxed> mInvalid;
97
98
  // These bit flags are all accessed only on the IO thread
99
  bool                 mFileExists : 1; // This means that the file should exists,
100
                                        // but it can be still deleted by OS/user
101
                                        // and then a subsequent OpenNSPRFileDesc()
102
                                        // will fail.
103
104
  // Both initially false.  Can be raised to true only when this handle is to be doomed
105
  // during the period when the pinning status is unknown.  After the pinning status
106
  // determination we check these flags and possibly doom.
107
  // These flags are only accessed on the IO thread.
108
  bool                 mDoomWhenFoundPinned : 1;
109
  bool                 mDoomWhenFoundNonPinned : 1;
110
  // Set when after shutdown AND:
111
  // - when writing: writing data (not metadata) OR the physical file handle is not currently open
112
  // - when truncating: the physical file handle is not currently open
113
  // When set it prevents any further writes or truncates on such handles to happen immediately
114
  // after shutdown and gives a chance to write metadata of already open files quickly as possible
115
  // (only that renders them actually usable by the cache.)
116
  bool                 mKilled : 1;
117
  // For existing files this is always pre-set to UNKNOWN.  The status is udpated accordingly
118
  // after the matadata has been parsed.
119
  // For new files the flag is set according to which storage kind is opening
120
  // the cache entry and remains so for the handle's lifetime.
121
  // The status can only change from UNKNOWN (if set so initially) to one of PINNED or NON_PINNED
122
  // and it stays unchanged afterwards.
123
  // This status is only accessed on the IO thread.
124
  PinningStatus        mPinning;
125
126
  nsCOMPtr<nsIFile>    mFile;
127
  int64_t              mFileSize;
128
  PRFileDesc          *mFD;  // if null then the file doesn't exists on the disk
129
  nsCString            mKey;
130
};
131
132
class CacheFileHandles {
133
public:
134
  CacheFileHandles();
135
  ~CacheFileHandles();
136
137
  nsresult GetHandle(const SHA1Sum::Hash *aHash, CacheFileHandle **_retval);
138
  nsresult NewHandle(const SHA1Sum::Hash *aHash, bool aPriority,
139
                     CacheFileHandle::PinningStatus aPinning, CacheFileHandle **_retval);
140
  void     RemoveHandle(CacheFileHandle *aHandlle);
141
  void     GetAllHandles(nsTArray<RefPtr<CacheFileHandle> > *_retval);
142
  void     GetActiveHandles(nsTArray<RefPtr<CacheFileHandle> > *_retval);
143
  void     ClearAll();
144
  uint32_t HandleCount();
145
146
#ifdef DEBUG_HANDLES
147
  void     Log(CacheFileHandlesEntry *entry);
148
#endif
149
150
  // Memory reporting
151
  size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
152
  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
153
154
  class HandleHashKey : public PLDHashEntryHdr
155
  {
156
  public:
157
    typedef const SHA1Sum::Hash& KeyType;
158
    typedef const SHA1Sum::Hash* KeyTypePointer;
159
160
    explicit HandleHashKey(KeyTypePointer aKey)
161
0
    {
162
0
      MOZ_COUNT_CTOR(HandleHashKey);
163
0
      mHash = MakeUnique<uint8_t[]>(SHA1Sum::kHashSize);
164
0
      memcpy(mHash.get(), aKey, sizeof(SHA1Sum::Hash));
165
0
    }
166
    HandleHashKey(const HandleHashKey& aOther)
167
0
    {
168
0
      MOZ_ASSERT_UNREACHABLE("HandleHashKey copy constructor is forbidden!");
169
0
    }
170
    ~HandleHashKey()
171
0
    {
172
0
      MOZ_COUNT_DTOR(HandleHashKey);
173
0
    }
174
175
    bool KeyEquals(KeyTypePointer aKey) const
176
0
    {
177
0
      return memcmp(mHash.get(), aKey, sizeof(SHA1Sum::Hash)) == 0;
178
0
    }
179
    static KeyTypePointer KeyToPointer(KeyType aKey)
180
0
    {
181
0
      return &aKey;
182
0
    }
183
    static PLDHashNumber HashKey(KeyTypePointer aKey)
184
0
    {
185
0
      return (reinterpret_cast<const uint32_t *>(aKey))[0];
186
0
    }
187
188
    void AddHandle(CacheFileHandle* aHandle);
189
    void RemoveHandle(CacheFileHandle* aHandle);
190
    already_AddRefed<CacheFileHandle> GetNewestHandle();
191
    void GetHandles(nsTArray<RefPtr<CacheFileHandle> > &aResult);
192
193
    SHA1Sum::Hash *Hash() const
194
0
    {
195
0
      return reinterpret_cast<SHA1Sum::Hash*>(mHash.get());
196
0
    }
197
0
    bool IsEmpty() const { return mHandles.Length() == 0; }
198
199
    enum { ALLOW_MEMMOVE = true };
200
201
#ifdef DEBUG
202
    void AssertHandlesState();
203
#endif
204
205
    // Memory reporting
206
    size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
207
    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
208
209
  private:
210
    // We can't make this UniquePtr<SHA1Sum::Hash>, because you can't have
211
    // UniquePtrs with known bounds.  So we settle for this representation
212
    // and using appropriate casts when we need to access it as a
213
    // SHA1Sum::Hash.
214
    UniquePtr<uint8_t[]> mHash;
215
    // Use weak pointers since the hash table access is on a single thread
216
    // only and CacheFileHandle removes itself from this table in its dtor
217
    // that may only be called on the same thread as we work with the hashtable
218
    // since we dispatch its Release() to this thread.
219
    nsTArray<CacheFileHandle*> mHandles;
220
  };
221
222
private:
223
  nsTHashtable<HandleHashKey> mTable;
224
};
225
226
////////////////////////////////////////////////////////////////////////////////
227
228
class OpenFileEvent;
229
class ReadEvent;
230
class WriteEvent;
231
class MetadataWriteScheduleEvent;
232
class CacheFileContextEvictor;
233
234
#define CACHEFILEIOLISTENER_IID \
235
{ /* dcaf2ddc-17cf-4242-bca1-8c86936375a5 */       \
236
  0xdcaf2ddc,                                      \
237
  0x17cf,                                          \
238
  0x4242,                                          \
239
  {0xbc, 0xa1, 0x8c, 0x86, 0x93, 0x63, 0x75, 0xa5} \
240
}
241
242
class CacheFileIOListener : public nsISupports
243
{
244
public:
245
  NS_DECLARE_STATIC_IID_ACCESSOR(CACHEFILEIOLISTENER_IID)
246
247
  NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) = 0;
248
  NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
249
                           nsresult aResult) = 0;
250
  NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf,
251
                        nsresult aResult) = 0;
252
  NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) = 0;
253
  NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) = 0;
254
  NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) = 0;
255
256
0
  virtual bool IsKilled() { return false; }
257
};
258
259
NS_DEFINE_STATIC_IID_ACCESSOR(CacheFileIOListener, CACHEFILEIOLISTENER_IID)
260
261
262
class CacheFileIOManager final
263
  : public nsITimerCallback
264
  , public nsINamed
265
{
266
public:
267
  NS_DECL_THREADSAFE_ISUPPORTS
268
  NS_DECL_NSITIMERCALLBACK
269
  NS_DECL_NSINAMED
270
271
  enum {
272
    OPEN         =  0U,
273
    CREATE       =  1U,
274
    CREATE_NEW   =  2U,
275
    PRIORITY     =  4U,
276
    SPECIAL_FILE =  8U,
277
    PINNED       = 16U
278
  };
279
280
  CacheFileIOManager();
281
282
  static nsresult Init();
283
  static nsresult Shutdown();
284
  static nsresult OnProfile();
285
  static already_AddRefed<nsIEventTarget> IOTarget();
286
  static already_AddRefed<CacheIOThread> IOThread();
287
  static bool IsOnIOThread();
288
  static bool IsOnIOThreadOrCeased();
289
  static bool IsShutdown();
290
291
  // Make aFile's WriteMetadataIfNeeded be called automatically after
292
  // a short interval.
293
  static nsresult ScheduleMetadataWrite(CacheFile * aFile);
294
  // Remove aFile from the scheduling registry array.
295
  // WriteMetadataIfNeeded will not be automatically called.
296
  static nsresult UnscheduleMetadataWrite(CacheFile * aFile);
297
  // Shuts the scheduling off and flushes all pending metadata writes.
298
  static nsresult ShutdownMetadataWriteScheduling();
299
300
  static nsresult OpenFile(const nsACString &aKey,
301
                           uint32_t aFlags, CacheFileIOListener *aCallback);
302
  static nsresult Read(CacheFileHandle *aHandle, int64_t aOffset,
303
                       char *aBuf, int32_t aCount,
304
                       CacheFileIOListener *aCallback);
305
  static nsresult Write(CacheFileHandle *aHandle, int64_t aOffset,
306
                        const char *aBuf, int32_t aCount, bool aValidate,
307
                        bool aTruncate, CacheFileIOListener *aCallback);
308
  // PinningDoomRestriction:
309
  // NO_RESTRICTION
310
  //    no restriction is checked, the file is simply always doomed
311
  // DOOM_WHEN_(NON)_PINNED, we branch based on the pinning status of the handle:
312
  //   UNKNOWN: the handle is marked to be doomed when later found (non)pinned
313
  //   PINNED/NON_PINNED: doom only when the restriction matches the pin status
314
  //      and the handle has not yet been required to doom during the UNKNOWN
315
  //      period
316
  enum PinningDoomRestriction {
317
    NO_RESTRICTION,
318
    DOOM_WHEN_NON_PINNED,
319
    DOOM_WHEN_PINNED
320
  };
321
  static nsresult DoomFile(CacheFileHandle *aHandle,
322
                           CacheFileIOListener *aCallback);
323
  static nsresult DoomFileByKey(const nsACString &aKey,
324
                                CacheFileIOListener *aCallback);
325
  static nsresult ReleaseNSPRHandle(CacheFileHandle *aHandle);
326
  static nsresult TruncateSeekSetEOF(CacheFileHandle *aHandle,
327
                                     int64_t aTruncatePos, int64_t aEOFPos,
328
                                     CacheFileIOListener *aCallback);
329
  static nsresult RenameFile(CacheFileHandle *aHandle,
330
                             const nsACString &aNewName,
331
                             CacheFileIOListener *aCallback);
332
  static nsresult EvictIfOverLimit();
333
  static nsresult EvictAll();
334
  static nsresult EvictByContext(nsILoadContextInfo *aLoadContextInfo,
335
                                 bool aPinning,
336
                                 const nsAString& aOrigin);
337
338
  static nsresult InitIndexEntry(CacheFileHandle *aHandle,
339
                                 OriginAttrsHash  aOriginAttrsHash,
340
                                 bool             aAnonymous,
341
                                 bool             aPinning);
342
  static nsresult UpdateIndexEntry(CacheFileHandle *aHandle,
343
                                   const uint32_t  *aFrecency,
344
                                   const uint32_t  *aExpirationTime,
345
                                   const bool      *aHasAltData,
346
                                   const uint16_t  *aOnStartTime,
347
                                   const uint16_t  *aOnStopTime);
348
349
  static nsresult UpdateIndexEntry();
350
351
  enum EEnumerateMode {
352
    ENTRIES,
353
    DOOMED
354
  };
355
356
  static void GetCacheDirectory(nsIFile** result);
357
#if defined(MOZ_WIDGET_ANDROID)
358
  static void GetProfilelessCacheDirectory(nsIFile** result);
359
#endif
360
361
  // Calls synchronously OnEntryInfo for an entry with the given hash.
362
  // Tries to find an existing entry in the service hashtables first, if not
363
  // found, loads synchronously from disk file.
364
  // Callable on the IO thread only.
365
  static nsresult GetEntryInfo(const SHA1Sum::Hash *aHash,
366
                               CacheStorageService::EntryInfoCallback *aCallback);
367
368
  // Memory reporting
369
  static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
370
  static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
371
372
private:
373
  friend class CacheFileHandle;
374
  friend class CacheFileChunk;
375
  friend class CacheFile;
376
  friend class ShutdownEvent;
377
  friend class OpenFileEvent;
378
  friend class CloseHandleEvent;
379
  friend class ReadEvent;
380
  friend class WriteEvent;
381
  friend class DoomFileEvent;
382
  friend class DoomFileByKeyEvent;
383
  friend class ReleaseNSPRHandleEvent;
384
  friend class TruncateSeekSetEOFEvent;
385
  friend class RenameFileEvent;
386
  friend class CacheIndex;
387
  friend class MetadataWriteScheduleEvent;
388
  friend class CacheFileContextEvictor;
389
390
  virtual ~CacheFileIOManager();
391
392
  nsresult InitInternal();
393
  nsresult ShutdownInternal();
394
395
  nsresult OpenFileInternal(const SHA1Sum::Hash *aHash,
396
                            const nsACString &aKey,
397
                            uint32_t aFlags,
398
                            CacheFileHandle **_retval);
399
  nsresult OpenSpecialFileInternal(const nsACString &aKey,
400
                                   uint32_t aFlags,
401
                                   CacheFileHandle **_retval);
402
  nsresult CloseHandleInternal(CacheFileHandle *aHandle);
403
  nsresult ReadInternal(CacheFileHandle *aHandle, int64_t aOffset,
404
                        char *aBuf, int32_t aCount);
405
  nsresult WriteInternal(CacheFileHandle *aHandle, int64_t aOffset,
406
                         const char *aBuf, int32_t aCount, bool aValidate,
407
                         bool aTruncate);
408
  nsresult DoomFileInternal(CacheFileHandle *aHandle,
409
                            PinningDoomRestriction aPinningStatusRestriction = NO_RESTRICTION);
410
  nsresult DoomFileByKeyInternal(const SHA1Sum::Hash *aHash);
411
  nsresult MaybeReleaseNSPRHandleInternal(CacheFileHandle *aHandle,
412
                                     bool aIgnoreShutdownLag = false);
413
  nsresult TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
414
                                      int64_t aTruncatePos, int64_t aEOFPos);
415
  nsresult RenameFileInternal(CacheFileHandle *aHandle,
416
                              const nsACString &aNewName);
417
  nsresult EvictIfOverLimitInternal();
418
  nsresult OverLimitEvictionInternal();
419
  nsresult EvictAllInternal();
420
  nsresult EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo,
421
                                  bool aPinning, const nsAString& aOrigin);
422
423
  nsresult TrashDirectory(nsIFile *aFile);
424
  static void OnTrashTimer(nsITimer *aTimer, void *aClosure);
425
  nsresult StartRemovingTrash();
426
  nsresult RemoveTrashInternal();
427
  nsresult FindTrashDirToRemove();
428
429
  nsresult CreateFile(CacheFileHandle *aHandle);
430
  static void HashToStr(const SHA1Sum::Hash *aHash, nsACString &_retval);
431
  static nsresult StrToHash(const nsACString &aHash, SHA1Sum::Hash *_retval);
432
  nsresult GetFile(const SHA1Sum::Hash *aHash, nsIFile **_retval);
433
  nsresult GetSpecialFile(const nsACString &aKey, nsIFile **_retval);
434
  nsresult GetDoomedFile(nsIFile **_retval);
435
  nsresult IsEmptyDirectory(nsIFile *aFile, bool *_retval);
436
  nsresult CheckAndCreateDir(nsIFile *aFile, const char *aDir,
437
                             bool aEnsureEmptyDir);
438
  nsresult CreateCacheTree();
439
  nsresult OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate = false);
440
  void     NSPRHandleUsed(CacheFileHandle *aHandle);
441
442
  // Removing all cache files during shutdown
443
  nsresult SyncRemoveDir(nsIFile *aFile, const char *aDir);
444
  void     SyncRemoveAllCacheFiles();
445
446
  nsresult ScheduleMetadataWriteInternal(CacheFile * aFile);
447
  nsresult UnscheduleMetadataWriteInternal(CacheFile * aFile);
448
  nsresult ShutdownMetadataWriteSchedulingInternal();
449
450
  static nsresult CacheIndexStateChanged();
451
  nsresult CacheIndexStateChangedInternal();
452
453
  // Smart size calculation. UpdateSmartCacheSize() must be called on IO thread.
454
  // It is called in EvictIfOverLimitInternal() just before we decide whether to
455
  // start overlimit eviction or not and also in OverLimitEvictionInternal()
456
  // before we start an eviction loop.
457
  nsresult UpdateSmartCacheSize(int64_t aFreeSpace);
458
459
  // Memory reporting (private part)
460
  size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const;
461
462
  static StaticRefPtr<CacheFileIOManager> gInstance;
463
464
  TimeStamp                            mStartTime;
465
  // Set true on the IO thread, CLOSE level as part of the internal shutdown
466
  // procedure.
467
  bool                                 mShuttingDown;
468
  RefPtr<CacheIOThread>                mIOThread;
469
  nsCOMPtr<nsIFile>                    mCacheDirectory;
470
#if defined(MOZ_WIDGET_ANDROID)
471
  // On Android we add the active profile directory name between the path
472
  // and the 'cache2' leaf name.  However, to delete any leftover data from
473
  // times before we were doing it, we still need to access the directory
474
  // w/o the profile name in the path.  Here it is stored.
475
  nsCOMPtr<nsIFile>                    mCacheProfilelessDirectory;
476
#endif
477
  bool                                 mTreeCreated;
478
  bool                                 mTreeCreationFailed;
479
  CacheFileHandles                     mHandles;
480
  nsTArray<CacheFileHandle *>          mHandlesByLastUsed;
481
  nsTArray<CacheFileHandle *>          mSpecialHandles;
482
  nsTArray<RefPtr<CacheFile> >         mScheduledMetadataWrites;
483
  nsCOMPtr<nsITimer>                   mMetadataWritesTimer;
484
  bool                                 mOverLimitEvicting;
485
  // When overlimit eviction is too slow and cache size reaches 105% of the
486
  // limit, this flag is set and no other content is cached to prevent
487
  // uncontrolled cache growing.
488
  bool                                 mCacheSizeOnHardLimit;
489
  bool                                 mRemovingTrashDirs;
490
  nsCOMPtr<nsITimer>                   mTrashTimer;
491
  nsCOMPtr<nsIFile>                    mTrashDir;
492
  nsCOMPtr<nsIDirectoryEnumerator>     mTrashDirEnumerator;
493
  nsTArray<nsCString>                  mFailedTrashDirs;
494
  RefPtr<CacheFileContextEvictor>      mContextEvictor;
495
  TimeStamp                            mLastSmartSizeTime;
496
};
497
498
} // namespace net
499
} // namespace mozilla
500
501
#endif