1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/io/nsAnonymousTemporaryFile.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,252 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 + 1.10 +#include "nsAnonymousTemporaryFile.h" 1.11 +#include "nsDirectoryServiceUtils.h" 1.12 +#include "nsDirectoryServiceDefs.h" 1.13 +#include "nsXULAppAPI.h" 1.14 +#include "nsCOMPtr.h" 1.15 +#include "nsString.h" 1.16 +#include "nsAppDirectoryServiceDefs.h" 1.17 + 1.18 +#ifdef XP_WIN 1.19 +#include "nsIObserver.h" 1.20 +#include "nsIObserverService.h" 1.21 +#include "mozilla/Services.h" 1.22 +#include "nsIIdleService.h" 1.23 +#include "nsISimpleEnumerator.h" 1.24 +#include "nsIFile.h" 1.25 +#include "nsAutoPtr.h" 1.26 +#include "nsITimer.h" 1.27 +#include "nsCRT.h" 1.28 + 1.29 +using namespace mozilla; 1.30 +#endif 1.31 + 1.32 + 1.33 +// We store the temp files in the system temp dir. 1.34 +// 1.35 +// On Windows systems in particular we use a sub-directory of the temp 1.36 +// directory, because: 1.37 +// 1. DELETE_ON_CLOSE is unreliable on Windows, in particular if we power 1.38 +// cycle (and perhaps if we crash) the files are not deleted. We store 1.39 +// the temporary files in a known sub-dir so that we can find and delete 1.40 +// them easily and quickly. 1.41 +// 2. On Windows NT the system temp dir is in the user's $HomeDir/AppData, 1.42 +// so we can be sure the user always has write privileges to that directory; 1.43 +// if the sub-dir for our temp files was in some shared location and 1.44 +// was created by a privileged user, it's possible that other users 1.45 +// wouldn't have write access to that sub-dir. (Non-Windows systems 1.46 +// don't store their temp files in a sub-dir, so this isn't an issue on 1.47 +// those platforms). 1.48 +// 3. Content processes can access the system temp dir 1.49 +// (NS_GetSpecialDirectory fails on NS_APP_USER_PROFILE_LOCAL_50_DIR 1.50 +// for content process for example, which is where we previously stored 1.51 +// temp files on Windows). This argument applies to all platforms, not 1.52 +// just Windows. 1.53 +static nsresult 1.54 +GetTempDir(nsIFile** aTempDir) 1.55 +{ 1.56 + if (NS_WARN_IF(!aTempDir)) 1.57 + return NS_ERROR_INVALID_ARG; 1.58 + nsCOMPtr<nsIFile> tmpFile; 1.59 + nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile)); 1.60 + if (NS_WARN_IF(NS_FAILED(rv))) 1.61 + return rv; 1.62 + 1.63 +#ifdef XP_WIN 1.64 + // On windows DELETE_ON_CLOSE is unreliable, so we store temporary files 1.65 + // in a subdir of the temp dir and delete that in an idle service observer 1.66 + // to ensure it's been cleared. 1.67 + rv = tmpFile->AppendNative(nsDependentCString("mozilla-temp-files")); 1.68 + if (NS_WARN_IF(NS_FAILED(rv))) 1.69 + return rv; 1.70 + rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700); 1.71 + if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) 1.72 + return rv; 1.73 +#endif 1.74 + 1.75 + tmpFile.forget(aTempDir); 1.76 + 1.77 + return NS_OK; 1.78 +} 1.79 + 1.80 +nsresult 1.81 +NS_OpenAnonymousTemporaryFile(PRFileDesc** aOutFileDesc) 1.82 +{ 1.83 + if (NS_WARN_IF(!aOutFileDesc)) 1.84 + return NS_ERROR_INVALID_ARG; 1.85 + nsresult rv; 1.86 + 1.87 + nsCOMPtr<nsIFile> tmpFile; 1.88 + rv = GetTempDir(getter_AddRefs(tmpFile)); 1.89 + if (NS_WARN_IF(NS_FAILED(rv))) 1.90 + return rv; 1.91 + 1.92 + // Give the temp file a name with a random element. CreateUnique will also 1.93 + // append a counter to the name if it encounters a name collision. Adding 1.94 + // a random element to the name reduces the likelihood of a name collision, 1.95 + // so that CreateUnique() doesn't end up trying a lot of name variants in 1.96 + // its "try appending an incrementing counter" loop, as file IO can be 1.97 + // expensive on some mobile flash drives. 1.98 + nsAutoCString name("mozilla-temp-"); 1.99 + name.AppendInt(rand()); 1.100 + 1.101 + rv = tmpFile->AppendNative(name); 1.102 + if (NS_WARN_IF(NS_FAILED(rv))) 1.103 + return rv; 1.104 + 1.105 + rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700); 1.106 + if (NS_WARN_IF(NS_FAILED(rv))) 1.107 + return rv; 1.108 + 1.109 + rv = tmpFile->OpenNSPRFileDesc(PR_RDWR | nsIFile::DELETE_ON_CLOSE, 1.110 + PR_IRWXU, aOutFileDesc); 1.111 + 1.112 + return rv; 1.113 +} 1.114 + 1.115 +#ifdef XP_WIN 1.116 + 1.117 +// On Windows we have an idle service observer that runs some time after 1.118 +// startup and deletes any stray anonymous temporary files... 1.119 + 1.120 +// Duration of idle time before we'll get a callback whereupon we attempt to 1.121 +// remove any stray and unused anonymous temp files. 1.122 +#define TEMP_FILE_IDLE_TIME_S 30 1.123 + 1.124 +// The nsAnonTempFileRemover is created in a timer, which sets an idle observer. 1.125 +// This is expiration time (in ms) which initial timer is set for (3 minutes). 1.126 +#define SCHEDULE_TIMEOUT_MS 3 * 60 * 1000 1.127 + 1.128 +#define XPCOM_SHUTDOWN_TOPIC "xpcom-shutdown" 1.129 + 1.130 +// This class adds itself as an idle observer. When the application has 1.131 +// been idle for about 30 seconds we'll get a notification, whereupon we'll 1.132 +// attempt to delete ${TempDir}/mozilla-temp-files/. This is to ensure all 1.133 +// temp files that were supposed to be deleted on application exit were actually 1.134 +// deleted, as they may not be if we previously crashed. See bugs 572579 and 1.135 +// 785662. This is only needed on some versions of Windows, 1.136 +// nsIFile::DELETE_ON_CLOSE works on other platforms. 1.137 +// This class adds itself as a shutdown observer so that it can cancel the 1.138 +// idle observer and its timer on shutdown. Note: the observer and idle 1.139 +// services hold references to instances of this object, and those references 1.140 +// are what keep this object alive. 1.141 +class nsAnonTempFileRemover MOZ_FINAL : public nsIObserver { 1.142 +public: 1.143 + NS_DECL_ISUPPORTS 1.144 + 1.145 + nsAnonTempFileRemover() { 1.146 + MOZ_COUNT_CTOR(nsAnonTempFileRemover); 1.147 + } 1.148 + 1.149 + ~nsAnonTempFileRemover() { 1.150 + MOZ_COUNT_DTOR(nsAnonTempFileRemover); 1.151 + } 1.152 + 1.153 + nsresult Init() { 1.154 + // We add the idle observer in a timer, so that the app has enough 1.155 + // time to start up before we add the idle observer. If we register the 1.156 + // idle observer too early, it will be registered before the fake idle 1.157 + // service is installed when running in xpcshell, and this interferes with 1.158 + // the fake idle service, causing xpcshell-test failures. 1.159 + mTimer = do_CreateInstance(NS_TIMER_CONTRACTID); 1.160 + if (NS_WARN_IF(!mTimer)) 1.161 + return NS_ERROR_FAILURE; 1.162 + nsresult rv = mTimer->Init(this, 1.163 + SCHEDULE_TIMEOUT_MS, 1.164 + nsITimer::TYPE_ONE_SHOT); 1.165 + if (NS_WARN_IF(NS_FAILED(rv))) 1.166 + return rv; 1.167 + 1.168 + // Register shutdown observer so we can cancel the timer if we shutdown before 1.169 + // the timer runs. 1.170 + nsCOMPtr<nsIObserverService> obsSrv = services::GetObserverService(); 1.171 + if (NS_WARN_IF(!obsSrv)) 1.172 + return NS_ERROR_FAILURE; 1.173 + return obsSrv->AddObserver(this, XPCOM_SHUTDOWN_TOPIC, false); 1.174 + } 1.175 + 1.176 + void Cleanup() { 1.177 + // Cancel timer. 1.178 + if (mTimer) { 1.179 + mTimer->Cancel(); 1.180 + mTimer = nullptr; 1.181 + } 1.182 + // Remove idle service observer. 1.183 + nsCOMPtr<nsIIdleService> idleSvc = 1.184 + do_GetService("@mozilla.org/widget/idleservice;1"); 1.185 + if (idleSvc) { 1.186 + idleSvc->RemoveIdleObserver(this, TEMP_FILE_IDLE_TIME_S); 1.187 + } 1.188 + // Remove shutdown observer. 1.189 + nsCOMPtr<nsIObserverService> obsSrv = services::GetObserverService(); 1.190 + if (obsSrv) { 1.191 + obsSrv->RemoveObserver(this, XPCOM_SHUTDOWN_TOPIC); 1.192 + } 1.193 + } 1.194 + 1.195 + NS_IMETHODIMP Observe(nsISupports *aSubject, 1.196 + const char *aTopic, 1.197 + const char16_t *aData) 1.198 + { 1.199 + if (nsCRT::strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0 && 1.200 + NS_FAILED(RegisterIdleObserver())) { 1.201 + Cleanup(); 1.202 + } else if (nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE) == 0) { 1.203 + // The user has been idle for a while, clean up the temp files. 1.204 + // The idle service will drop its reference to this object after 1.205 + // we exit, destroying this object. 1.206 + RemoveAnonTempFileFiles(); 1.207 + Cleanup(); 1.208 + } else if (nsCRT::strcmp(aTopic, XPCOM_SHUTDOWN_TOPIC) == 0) { 1.209 + Cleanup(); 1.210 + } 1.211 + return NS_OK; 1.212 + } 1.213 + 1.214 + nsresult RegisterIdleObserver() { 1.215 + // Add this as an idle observer. When we've been idle for 1.216 + // TEMP_FILE_IDLE_TIME_S seconds, we'll get a notification, and we'll then 1.217 + // try to delete any stray temp files. 1.218 + nsCOMPtr<nsIIdleService> idleSvc = 1.219 + do_GetService("@mozilla.org/widget/idleservice;1"); 1.220 + if (!idleSvc) 1.221 + return NS_ERROR_FAILURE; 1.222 + return idleSvc->AddIdleObserver(this, TEMP_FILE_IDLE_TIME_S); 1.223 + } 1.224 + 1.225 + void RemoveAnonTempFileFiles() { 1.226 + nsCOMPtr<nsIFile> tmpDir; 1.227 + nsresult rv = GetTempDir(getter_AddRefs(tmpDir)); 1.228 + if (NS_WARN_IF(NS_FAILED(rv))) 1.229 + return; 1.230 + 1.231 + // Remove the directory recursively. 1.232 + tmpDir->Remove(true); 1.233 + } 1.234 + 1.235 +private: 1.236 + nsCOMPtr<nsITimer> mTimer; 1.237 +}; 1.238 + 1.239 +NS_IMPL_ISUPPORTS(nsAnonTempFileRemover, nsIObserver) 1.240 + 1.241 +nsresult CreateAnonTempFileRemover() { 1.242 + // Create a temp file remover. If Init() succeeds, the temp file remover is kept 1.243 + // alive by a reference held by the observer service, since the temp file remover 1.244 + // is a shutdown observer. We only create the temp file remover if we're running 1.245 + // in the main process; there's no point in doing the temp file removal multiple 1.246 + // times per startup. 1.247 + if (XRE_GetProcessType() != GeckoProcessType_Default) { 1.248 + return NS_OK; 1.249 + } 1.250 + nsRefPtr<nsAnonTempFileRemover> tempRemover = new nsAnonTempFileRemover(); 1.251 + return tempRemover->Init(); 1.252 +} 1.253 + 1.254 +#endif 1.255 +