xpcom/io/nsAnonymousTemporaryFile.cpp

changeset 0
6474c204b198
     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 +

mercurial