xpcom/io/nsAnonymousTemporaryFile.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6
michael@0 7 #include "nsAnonymousTemporaryFile.h"
michael@0 8 #include "nsDirectoryServiceUtils.h"
michael@0 9 #include "nsDirectoryServiceDefs.h"
michael@0 10 #include "nsXULAppAPI.h"
michael@0 11 #include "nsCOMPtr.h"
michael@0 12 #include "nsString.h"
michael@0 13 #include "nsAppDirectoryServiceDefs.h"
michael@0 14
michael@0 15 #ifdef XP_WIN
michael@0 16 #include "nsIObserver.h"
michael@0 17 #include "nsIObserverService.h"
michael@0 18 #include "mozilla/Services.h"
michael@0 19 #include "nsIIdleService.h"
michael@0 20 #include "nsISimpleEnumerator.h"
michael@0 21 #include "nsIFile.h"
michael@0 22 #include "nsAutoPtr.h"
michael@0 23 #include "nsITimer.h"
michael@0 24 #include "nsCRT.h"
michael@0 25
michael@0 26 using namespace mozilla;
michael@0 27 #endif
michael@0 28
michael@0 29
michael@0 30 // We store the temp files in the system temp dir.
michael@0 31 //
michael@0 32 // On Windows systems in particular we use a sub-directory of the temp
michael@0 33 // directory, because:
michael@0 34 // 1. DELETE_ON_CLOSE is unreliable on Windows, in particular if we power
michael@0 35 // cycle (and perhaps if we crash) the files are not deleted. We store
michael@0 36 // the temporary files in a known sub-dir so that we can find and delete
michael@0 37 // them easily and quickly.
michael@0 38 // 2. On Windows NT the system temp dir is in the user's $HomeDir/AppData,
michael@0 39 // so we can be sure the user always has write privileges to that directory;
michael@0 40 // if the sub-dir for our temp files was in some shared location and
michael@0 41 // was created by a privileged user, it's possible that other users
michael@0 42 // wouldn't have write access to that sub-dir. (Non-Windows systems
michael@0 43 // don't store their temp files in a sub-dir, so this isn't an issue on
michael@0 44 // those platforms).
michael@0 45 // 3. Content processes can access the system temp dir
michael@0 46 // (NS_GetSpecialDirectory fails on NS_APP_USER_PROFILE_LOCAL_50_DIR
michael@0 47 // for content process for example, which is where we previously stored
michael@0 48 // temp files on Windows). This argument applies to all platforms, not
michael@0 49 // just Windows.
michael@0 50 static nsresult
michael@0 51 GetTempDir(nsIFile** aTempDir)
michael@0 52 {
michael@0 53 if (NS_WARN_IF(!aTempDir))
michael@0 54 return NS_ERROR_INVALID_ARG;
michael@0 55 nsCOMPtr<nsIFile> tmpFile;
michael@0 56 nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile));
michael@0 57 if (NS_WARN_IF(NS_FAILED(rv)))
michael@0 58 return rv;
michael@0 59
michael@0 60 #ifdef XP_WIN
michael@0 61 // On windows DELETE_ON_CLOSE is unreliable, so we store temporary files
michael@0 62 // in a subdir of the temp dir and delete that in an idle service observer
michael@0 63 // to ensure it's been cleared.
michael@0 64 rv = tmpFile->AppendNative(nsDependentCString("mozilla-temp-files"));
michael@0 65 if (NS_WARN_IF(NS_FAILED(rv)))
michael@0 66 return rv;
michael@0 67 rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
michael@0 68 if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv)))
michael@0 69 return rv;
michael@0 70 #endif
michael@0 71
michael@0 72 tmpFile.forget(aTempDir);
michael@0 73
michael@0 74 return NS_OK;
michael@0 75 }
michael@0 76
michael@0 77 nsresult
michael@0 78 NS_OpenAnonymousTemporaryFile(PRFileDesc** aOutFileDesc)
michael@0 79 {
michael@0 80 if (NS_WARN_IF(!aOutFileDesc))
michael@0 81 return NS_ERROR_INVALID_ARG;
michael@0 82 nsresult rv;
michael@0 83
michael@0 84 nsCOMPtr<nsIFile> tmpFile;
michael@0 85 rv = GetTempDir(getter_AddRefs(tmpFile));
michael@0 86 if (NS_WARN_IF(NS_FAILED(rv)))
michael@0 87 return rv;
michael@0 88
michael@0 89 // Give the temp file a name with a random element. CreateUnique will also
michael@0 90 // append a counter to the name if it encounters a name collision. Adding
michael@0 91 // a random element to the name reduces the likelihood of a name collision,
michael@0 92 // so that CreateUnique() doesn't end up trying a lot of name variants in
michael@0 93 // its "try appending an incrementing counter" loop, as file IO can be
michael@0 94 // expensive on some mobile flash drives.
michael@0 95 nsAutoCString name("mozilla-temp-");
michael@0 96 name.AppendInt(rand());
michael@0 97
michael@0 98 rv = tmpFile->AppendNative(name);
michael@0 99 if (NS_WARN_IF(NS_FAILED(rv)))
michael@0 100 return rv;
michael@0 101
michael@0 102 rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700);
michael@0 103 if (NS_WARN_IF(NS_FAILED(rv)))
michael@0 104 return rv;
michael@0 105
michael@0 106 rv = tmpFile->OpenNSPRFileDesc(PR_RDWR | nsIFile::DELETE_ON_CLOSE,
michael@0 107 PR_IRWXU, aOutFileDesc);
michael@0 108
michael@0 109 return rv;
michael@0 110 }
michael@0 111
michael@0 112 #ifdef XP_WIN
michael@0 113
michael@0 114 // On Windows we have an idle service observer that runs some time after
michael@0 115 // startup and deletes any stray anonymous temporary files...
michael@0 116
michael@0 117 // Duration of idle time before we'll get a callback whereupon we attempt to
michael@0 118 // remove any stray and unused anonymous temp files.
michael@0 119 #define TEMP_FILE_IDLE_TIME_S 30
michael@0 120
michael@0 121 // The nsAnonTempFileRemover is created in a timer, which sets an idle observer.
michael@0 122 // This is expiration time (in ms) which initial timer is set for (3 minutes).
michael@0 123 #define SCHEDULE_TIMEOUT_MS 3 * 60 * 1000
michael@0 124
michael@0 125 #define XPCOM_SHUTDOWN_TOPIC "xpcom-shutdown"
michael@0 126
michael@0 127 // This class adds itself as an idle observer. When the application has
michael@0 128 // been idle for about 30 seconds we'll get a notification, whereupon we'll
michael@0 129 // attempt to delete ${TempDir}/mozilla-temp-files/. This is to ensure all
michael@0 130 // temp files that were supposed to be deleted on application exit were actually
michael@0 131 // deleted, as they may not be if we previously crashed. See bugs 572579 and
michael@0 132 // 785662. This is only needed on some versions of Windows,
michael@0 133 // nsIFile::DELETE_ON_CLOSE works on other platforms.
michael@0 134 // This class adds itself as a shutdown observer so that it can cancel the
michael@0 135 // idle observer and its timer on shutdown. Note: the observer and idle
michael@0 136 // services hold references to instances of this object, and those references
michael@0 137 // are what keep this object alive.
michael@0 138 class nsAnonTempFileRemover MOZ_FINAL : public nsIObserver {
michael@0 139 public:
michael@0 140 NS_DECL_ISUPPORTS
michael@0 141
michael@0 142 nsAnonTempFileRemover() {
michael@0 143 MOZ_COUNT_CTOR(nsAnonTempFileRemover);
michael@0 144 }
michael@0 145
michael@0 146 ~nsAnonTempFileRemover() {
michael@0 147 MOZ_COUNT_DTOR(nsAnonTempFileRemover);
michael@0 148 }
michael@0 149
michael@0 150 nsresult Init() {
michael@0 151 // We add the idle observer in a timer, so that the app has enough
michael@0 152 // time to start up before we add the idle observer. If we register the
michael@0 153 // idle observer too early, it will be registered before the fake idle
michael@0 154 // service is installed when running in xpcshell, and this interferes with
michael@0 155 // the fake idle service, causing xpcshell-test failures.
michael@0 156 mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
michael@0 157 if (NS_WARN_IF(!mTimer))
michael@0 158 return NS_ERROR_FAILURE;
michael@0 159 nsresult rv = mTimer->Init(this,
michael@0 160 SCHEDULE_TIMEOUT_MS,
michael@0 161 nsITimer::TYPE_ONE_SHOT);
michael@0 162 if (NS_WARN_IF(NS_FAILED(rv)))
michael@0 163 return rv;
michael@0 164
michael@0 165 // Register shutdown observer so we can cancel the timer if we shutdown before
michael@0 166 // the timer runs.
michael@0 167 nsCOMPtr<nsIObserverService> obsSrv = services::GetObserverService();
michael@0 168 if (NS_WARN_IF(!obsSrv))
michael@0 169 return NS_ERROR_FAILURE;
michael@0 170 return obsSrv->AddObserver(this, XPCOM_SHUTDOWN_TOPIC, false);
michael@0 171 }
michael@0 172
michael@0 173 void Cleanup() {
michael@0 174 // Cancel timer.
michael@0 175 if (mTimer) {
michael@0 176 mTimer->Cancel();
michael@0 177 mTimer = nullptr;
michael@0 178 }
michael@0 179 // Remove idle service observer.
michael@0 180 nsCOMPtr<nsIIdleService> idleSvc =
michael@0 181 do_GetService("@mozilla.org/widget/idleservice;1");
michael@0 182 if (idleSvc) {
michael@0 183 idleSvc->RemoveIdleObserver(this, TEMP_FILE_IDLE_TIME_S);
michael@0 184 }
michael@0 185 // Remove shutdown observer.
michael@0 186 nsCOMPtr<nsIObserverService> obsSrv = services::GetObserverService();
michael@0 187 if (obsSrv) {
michael@0 188 obsSrv->RemoveObserver(this, XPCOM_SHUTDOWN_TOPIC);
michael@0 189 }
michael@0 190 }
michael@0 191
michael@0 192 NS_IMETHODIMP Observe(nsISupports *aSubject,
michael@0 193 const char *aTopic,
michael@0 194 const char16_t *aData)
michael@0 195 {
michael@0 196 if (nsCRT::strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0 &&
michael@0 197 NS_FAILED(RegisterIdleObserver())) {
michael@0 198 Cleanup();
michael@0 199 } else if (nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE) == 0) {
michael@0 200 // The user has been idle for a while, clean up the temp files.
michael@0 201 // The idle service will drop its reference to this object after
michael@0 202 // we exit, destroying this object.
michael@0 203 RemoveAnonTempFileFiles();
michael@0 204 Cleanup();
michael@0 205 } else if (nsCRT::strcmp(aTopic, XPCOM_SHUTDOWN_TOPIC) == 0) {
michael@0 206 Cleanup();
michael@0 207 }
michael@0 208 return NS_OK;
michael@0 209 }
michael@0 210
michael@0 211 nsresult RegisterIdleObserver() {
michael@0 212 // Add this as an idle observer. When we've been idle for
michael@0 213 // TEMP_FILE_IDLE_TIME_S seconds, we'll get a notification, and we'll then
michael@0 214 // try to delete any stray temp files.
michael@0 215 nsCOMPtr<nsIIdleService> idleSvc =
michael@0 216 do_GetService("@mozilla.org/widget/idleservice;1");
michael@0 217 if (!idleSvc)
michael@0 218 return NS_ERROR_FAILURE;
michael@0 219 return idleSvc->AddIdleObserver(this, TEMP_FILE_IDLE_TIME_S);
michael@0 220 }
michael@0 221
michael@0 222 void RemoveAnonTempFileFiles() {
michael@0 223 nsCOMPtr<nsIFile> tmpDir;
michael@0 224 nsresult rv = GetTempDir(getter_AddRefs(tmpDir));
michael@0 225 if (NS_WARN_IF(NS_FAILED(rv)))
michael@0 226 return;
michael@0 227
michael@0 228 // Remove the directory recursively.
michael@0 229 tmpDir->Remove(true);
michael@0 230 }
michael@0 231
michael@0 232 private:
michael@0 233 nsCOMPtr<nsITimer> mTimer;
michael@0 234 };
michael@0 235
michael@0 236 NS_IMPL_ISUPPORTS(nsAnonTempFileRemover, nsIObserver)
michael@0 237
michael@0 238 nsresult CreateAnonTempFileRemover() {
michael@0 239 // Create a temp file remover. If Init() succeeds, the temp file remover is kept
michael@0 240 // alive by a reference held by the observer service, since the temp file remover
michael@0 241 // is a shutdown observer. We only create the temp file remover if we're running
michael@0 242 // in the main process; there's no point in doing the temp file removal multiple
michael@0 243 // times per startup.
michael@0 244 if (XRE_GetProcessType() != GeckoProcessType_Default) {
michael@0 245 return NS_OK;
michael@0 246 }
michael@0 247 nsRefPtr<nsAnonTempFileRemover> tempRemover = new nsAnonTempFileRemover();
michael@0 248 return tempRemover->Init();
michael@0 249 }
michael@0 250
michael@0 251 #endif
michael@0 252

mercurial