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