summaryrefslogtreecommitdiff
path: root/security/manager/ssl/DataStorage.h
blob: 13e3688254e848fee4d667948cde7c62d7943cb7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_DataStorage_h
#define mozilla_DataStorage_h

#include "mozilla/Monitor.h"
#include "mozilla/Mutex.h"
#include "mozilla/StaticPtr.h"
#include "nsCOMPtr.h"
#include "nsDataHashtable.h"
#include "nsIObserver.h"
#include "nsIThread.h"
#include "nsITimer.h"
#include "nsRefPtrHashtable.h"
#include "nsString.h"

namespace mozilla {

namespace dom {
class DataStorageItem;
}

/**
 * DataStorage is a threadsafe, generic, narrow string-based hash map that
 * persists data on disk and additionally handles temporary and private data.
 * However, if used in a context where there is no profile directory, data
 * will not be persisted.
 *
 * Its lifecycle is as follows:
 * - Allocate with a filename (this is or will eventually be a file in the
 *   profile directory, if the profile exists).
 * - Call Init() from the main thread. This spins off an asynchronous read
 *   of the backing file.
 * - Eventually observers of the topic "data-storage-ready" will be notified
 *   with the backing filename as the data in the notification when this
 *   has completed.
 * - Should the profile directory not be available, (e.g. in xpcshell),
 *   DataStorage will not initially read any persistent data. The
 *   "data-storage-ready" event will still be emitted. This follows semantics
 *   similar to the permission manager and allows tests that test
 *   unrelated components to proceed without a profile.
 * - When any persistent data changes, a timer is initialized that will
 *   eventually asynchronously write all persistent data to the backing file.
 *   When this happens, observers will be notified with the topic
 *   "data-storage-written" and the backing filename as the data.
 *   It is possible to receive a "data-storage-written" event while there exist
 *   pending persistent data changes. However, those changes will cause the
 *   timer to be reinitialized and another "data-storage-written" event will
 *   be sent.
 * - When DataStorage observes the topic "profile-before-change" in
 *   anticipation of shutdown, all persistent data is synchronously written to
 *   the backing file. The worker thread responsible for these writes is then
 *   disabled to prevent further writes to that file (the delayed-write timer
 *   is cancelled when this happens).
 * - For testing purposes, the preference "test.datastorage.write_timer_ms" can
 *   be set to cause the asynchronous writing of data to happen more quickly.
 * - To prevent unbounded memory and disk use, the number of entries in each
 *   table is limited to 1024. Evictions are handled in by a modified LRU scheme
 *   (see implementation comments).
 * - NB: Instances of DataStorage have long lifetimes because they are strong
 *   observers of events and won't go away until the observer service does.
 *
 * For each key/value:
 * - The key must be a non-empty string containing no instances of '\t' or '\n'
 *   (this is a limitation of how the data is stored and will be addressed in
 *   the future).
 * - The key must have a length no more than 256.
 * - The value must not contain '\n' and must have a length no more than 1024.
 *   (the length limits are to prevent unbounded disk and memory usage)
 */

/**
 * Data that is DataStorage_Persistent is saved on disk. DataStorage_Temporary
 * and DataStorage_Private are not saved. DataStorage_Private is meant to
 * only be set and accessed from private contexts. It will be cleared upon
 * observing the event "last-pb-context-exited".
 */
enum DataStorageType {
  DataStorage_Persistent,
  DataStorage_Temporary,
  DataStorage_Private
};

class DataStorage : public nsIObserver
{
  typedef dom::DataStorageItem DataStorageItem;

public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSIOBSERVER

  // If there is a profile directory, there is or will eventually be a file
  // by the name specified by aFilename there.
  static already_AddRefed<DataStorage> Get(const nsString& aFilename);
  static already_AddRefed<DataStorage> GetIfExists(const nsString& aFilename);

  // Initializes the DataStorage. Must be called before using.
  // aDataWillPersist returns whether or not data can be persistently saved.
  nsresult Init(/*out*/bool& aDataWillPersist);
  // Given a key and a type of data, returns a value. Returns an empty string if
  // the key is not present for that type of data. If Get is called before the
  // "data-storage-ready" event is observed, it will block. NB: It is not
  // currently possible to differentiate between missing data and data that is
  // the empty string.
  nsCString Get(const nsCString& aKey, DataStorageType aType);
  // Give a key, value, and type of data, adds an entry as appropriate.
  // Updates existing entries.
  nsresult Put(const nsCString& aKey, const nsCString& aValue,
               DataStorageType aType);
  // Given a key and type of data, removes an entry if present.
  void Remove(const nsCString& aKey, DataStorageType aType);
  // Removes all entries of all types of data.
  nsresult Clear();

  // Read all of the data items.
  void GetAll(InfallibleTArray<DataStorageItem>* aItems);

private:
  explicit DataStorage(const nsString& aFilename);
  virtual ~DataStorage();

  class Writer;
  class Reader;

  class Entry
  {
  public:
    Entry();
    bool UpdateScore();

    uint32_t mScore;
    int32_t mLastAccessed; // the last accessed time in days since the epoch
    nsCString mValue;
  };

  // Utility class for scanning tables for an entry to evict.
  class KeyAndEntry
  {
  public:
    nsCString mKey;
    Entry mEntry;
  };

  typedef nsDataHashtable<nsCStringHashKey, Entry> DataStorageTable;
  typedef nsRefPtrHashtable<nsStringHashKey, DataStorage> DataStorages;

  void WaitForReady();
  nsresult AsyncWriteData(const MutexAutoLock& aProofOfLock);
  nsresult AsyncReadData(bool& aHaveProfileDir,
                         const MutexAutoLock& aProofOfLock);
  nsresult AsyncSetTimer(const MutexAutoLock& aProofOfLock);
  nsresult DispatchShutdownTimer(const MutexAutoLock& aProofOfLock);

  static nsresult ValidateKeyAndValue(const nsCString& aKey,
                                      const nsCString& aValue);
  static void TimerCallback(nsITimer* aTimer, void* aClosure);
  void SetTimer();
  void ShutdownTimer();
  void NotifyObservers(const char* aTopic);

  bool GetInternal(const nsCString& aKey, Entry* aEntry, DataStorageType aType,
                   const MutexAutoLock& aProofOfLock);
  nsresult PutInternal(const nsCString& aKey, Entry& aEntry,
                       DataStorageType aType,
                       const MutexAutoLock& aProofOfLock);
  void MaybeEvictOneEntry(DataStorageType aType,
                          const MutexAutoLock& aProofOfLock);
  DataStorageTable& GetTableForType(DataStorageType aType,
                                    const MutexAutoLock& aProofOfLock);

  void ReadAllFromTable(DataStorageType aType,
                        InfallibleTArray<DataStorageItem>* aItems,
                        const MutexAutoLock& aProofOfLock);

  Mutex mMutex; // This mutex protects access to the following members:
  DataStorageTable  mPersistentDataTable;
  DataStorageTable  mTemporaryDataTable;
  DataStorageTable  mPrivateDataTable;
  nsCOMPtr<nsIThread> mWorkerThread;
  nsCOMPtr<nsIFile> mBackingFile;
  nsCOMPtr<nsITimer> mTimer; // All uses after init must be on the worker thread
  uint32_t mTimerDelay; // in milliseconds
  bool mPendingWrite; // true if a write is needed but hasn't been dispatched
  bool mShuttingDown;
  bool mInitCalled; // Indicates that Init() has been called.
  // (End list of members protected by mMutex)

  Monitor mReadyMonitor; // Do not acquire this at the same time as mMutex.
  bool mReady; // Indicates that saved data has been read and Get can proceed.

  const nsString mFilename;

  static StaticAutoPtr<DataStorages> sDataStorages;
};

} // namespace mozilla

#endif // mozilla_DataStorage_h