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