summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/DataStorage.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /security/manager/ssl/DataStorage.h
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/manager/ssl/DataStorage.h')
-rw-r--r--security/manager/ssl/DataStorage.h223
1 files changed, 223 insertions, 0 deletions
diff --git a/security/manager/ssl/DataStorage.h b/security/manager/ssl/DataStorage.h
new file mode 100644
index 0000000000..f4d536f21c
--- /dev/null
+++ b/security/manager/ssl/DataStorage.h
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/Atomics.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/StaticPtr.h"
+#include "nsCOMPtr.h"
+#include "nsTHashMap.h"
+#include "nsIObserver.h"
+#include "nsITimer.h"
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+
+class psm_DataStorageTest;
+
+namespace mozilla {
+class DataStorageMemoryReporter;
+class TaskQueue;
+
+/**
+ * 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.
+ * - A timer periodically fires on a background thread that checks if any
+ * persistent data has changed, and if so writes 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 eventually be
+ * written when the timer fires again, and eventually another
+ * "data-storage-written" event will be sent.
+ * - When a DataStorage instance observes the topic "profile-before-change" in
+ * anticipation of shutdown, all persistent data for that DataStorage is
+ * written to the backing file (this blocks the main thread). In the process
+ * of doing this, the background serial event target responsible for these
+ * writes is then shut down to prevent further writes to that file (the
+ * background timer is also cancelled when this happens).
+ * If "profile-before-change" is not observed, this happens upon observing
+ * "xpcom-shutdown-threads".
+ * - 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
+};
+
+struct DataStorageItem final {
+ nsCString key;
+ nsCString value;
+ DataStorageType type;
+};
+
+enum class DataStorageClass {
+#define DATA_STORAGE(_) _,
+#include "mozilla/DataStorageList.h"
+#undef DATA_STORAGE
+};
+
+class DataStorage : public nsIObserver {
+ 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(DataStorageClass aFilename);
+
+ // Initializes the DataStorage. Must be called before using.
+ nsresult Init();
+
+ // 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(nsTArray<DataStorageItem>* aItems);
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+
+ // Return true if this data storage is ready to be used.
+ bool IsReady();
+
+ void ShutdownTimer();
+
+ private:
+ explicit DataStorage(const nsString& aFilename);
+ virtual ~DataStorage() = default;
+
+ static already_AddRefed<DataStorage> GetFromRawFileName(
+ const nsString& aFilename);
+
+ friend class ::psm_DataStorageTest;
+ friend class mozilla::DataStorageMemoryReporter;
+
+ 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 nsTHashMap<nsCStringHashKey, Entry> DataStorageTable;
+ typedef nsRefPtrHashtable<nsStringHashKey, DataStorage> DataStorages;
+
+ void WaitForReady();
+ nsresult AsyncWriteData(const MutexAutoLock& aProofOfLock);
+ nsresult AsyncReadData(const MutexAutoLock& aProofOfLock);
+
+ static nsresult ValidateKeyAndValue(const nsCString& aKey,
+ const nsCString& aValue);
+ static void TimerCallback(nsITimer* aTimer, void* aClosure);
+ 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,
+ nsTArray<DataStorageItem>* aItems,
+ const MutexAutoLock& aProofOfLock);
+
+ Mutex mMutex; // This mutex protects access to the following members:
+ DataStorageTable mPersistentDataTable MOZ_GUARDED_BY(mMutex);
+ DataStorageTable mTemporaryDataTable MOZ_GUARDED_BY(mMutex);
+ DataStorageTable mPrivateDataTable MOZ_GUARDED_BY(mMutex);
+ nsCOMPtr<nsIFile> mBackingFile MOZ_GUARDED_BY(mMutex);
+ bool mPendingWrite MOZ_GUARDED_BY(
+ mMutex); // true if a write is needed but hasn't been dispatched
+ bool mShuttingDown MOZ_GUARDED_BY(mMutex);
+ RefPtr<TaskQueue> mBackgroundTaskQueue MOZ_GUARDED_BY(mMutex);
+ // (End list of members protected by mMutex)
+
+ nsCOMPtr<nsITimer> mTimer; // Must only be accessed on the main thread
+
+ mozilla::Atomic<bool> mInitCalled; // Indicates that Init() has been called.
+
+ Monitor mReadyMonitor; // Do not acquire this at the same time as mMutex.
+ bool mReady MOZ_GUARDED_BY(mReadyMonitor); // Indicates that saved data has
+ // been read and Get can proceed.
+
+ const nsString mFilename;
+
+ static StaticAutoPtr<DataStorages> sDataStorages;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_DataStorage_h