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