DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (5b81998bb7ab)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "nsAnonymousTemporaryFile.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsXULAppAPI.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsAppDirectoryServiceDefs.h"

#ifdef XP_WIN
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "mozilla/Services.h"
#include "nsIIdleService.h"
#include "nsISimpleEnumerator.h"
#include "nsIFile.h"
#include "nsAutoPtr.h"
#include "nsITimer.h"
#include "nsCRT.h"

using namespace mozilla;
#endif


// We store the temp files in the system temp dir.
//
// On Windows systems in particular we use a sub-directory of the temp
// directory, because:
//   1. DELETE_ON_CLOSE is unreliable on Windows, in particular if we power
//      cycle (and perhaps if we crash) the files are not deleted. We store
//      the temporary files in a known sub-dir so that we can find and delete
//      them easily and quickly.
//   2. On Windows NT the system temp dir is in the user's $HomeDir/AppData,
//      so we can be sure the user always has write privileges to that directory;
//      if the sub-dir for our temp files was in some shared location and
//      was created by a privileged user, it's possible that other users
//      wouldn't have write access to that sub-dir. (Non-Windows systems
//      don't store their temp files in a sub-dir, so this isn't an issue on
//      those platforms).
//   3. Content processes can access the system temp dir
//      (NS_GetSpecialDirectory fails on NS_APP_USER_PROFILE_LOCAL_50_DIR
//      for content process for example, which is where we previously stored
//      temp files on Windows). This argument applies to all platforms, not
//      just Windows.
static nsresult
GetTempDir(nsIFile** aTempDir)
{
  NS_ENSURE_ARG(aTempDir);
  nsCOMPtr<nsIFile> tmpFile;
  nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile));
  NS_ENSURE_SUCCESS(rv,rv);

#ifdef XP_WIN
  // On windows DELETE_ON_CLOSE is unreliable, so we store temporary files
  // in a subdir of the temp dir and delete that in an idle service observer
  // to ensure it's been cleared.
  rv = tmpFile->AppendNative(nsDependentCString("mozilla-temp-files"));
  NS_ENSURE_SUCCESS(rv,rv);    
  rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
  NS_ENSURE_TRUE(rv == NS_ERROR_FILE_ALREADY_EXISTS || NS_SUCCEEDED(rv), rv);
#endif
 
  tmpFile.forget(aTempDir);

  return NS_OK;
}

nsresult
NS_OpenAnonymousTemporaryFile(PRFileDesc** aOutFileDesc)
{
  NS_ENSURE_ARG(aOutFileDesc);
  nsresult rv;

  nsCOMPtr<nsIFile> tmpFile;
  rv = GetTempDir(getter_AddRefs(tmpFile));
  NS_ENSURE_SUCCESS(rv,rv);

  // Give the temp file a name with a random element. CreateUnique will also
  // append a counter to the name if it encounters a name collision. Adding
  // a random element to the name reduces the likelihood of a name collision,
  // so that CreateUnique() doesn't end up trying a lot of name variants in
  // its "try appending an incrementing counter" loop, as file IO can be
  // expensive on some mobile flash drives.
  nsAutoCString name("mozilla-temp-");
  name.AppendInt(rand());

  rv = tmpFile->AppendNative(name);
  NS_ENSURE_SUCCESS(rv,rv);

  rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700);
  NS_ENSURE_SUCCESS(rv,rv);

  rv = tmpFile->OpenNSPRFileDesc(PR_RDWR | nsIFile::DELETE_ON_CLOSE,
                                 PR_IRWXU, aOutFileDesc);

  return rv;    
}

#ifdef XP_WIN

// On Windows we have an idle service observer that runs some time after
// startup and deletes any stray anonymous temporary files...

// Duration of idle time before we'll get a callback whereupon we attempt to
// remove any stray and unused anonymous temp files.
#define TEMP_FILE_IDLE_TIME_S 30

// The nsAnonTempFileRemover is created in a timer, which sets an idle observer.
// This is expiration time (in ms) which initial timer is set for (3 minutes).
#define SCHEDULE_TIMEOUT_MS 3 * 60 * 1000

#define XPCOM_SHUTDOWN_TOPIC "xpcom-shutdown"

// This class adds itself as an idle observer. When the application has
// been idle for about 30 seconds we'll get a notification, whereupon we'll
// attempt to delete ${TempDir}/mozilla-temp-files/. This is to ensure all
// temp files that were supposed to be deleted on application exit were actually
// deleted, as they may not be if we previously crashed. See bugs 572579 and
// 785662. This is only needed on some versions of Windows,
// nsIFile::DELETE_ON_CLOSE works on other platforms.
// This class adds itself as a shutdown observer so that it can cancel the
// idle observer and its timer on shutdown. Note: the observer and idle
// services hold references to instances of this object, and those references
// are what keep this object alive.
class nsAnonTempFileRemover MOZ_FINAL : public nsIObserver {
public:
  NS_DECL_ISUPPORTS

  nsAnonTempFileRemover() {
    MOZ_COUNT_CTOR(nsAnonTempFileRemover);
  }

  ~nsAnonTempFileRemover() {
    MOZ_COUNT_DTOR(nsAnonTempFileRemover);
  }

  nsresult Init() {
    // We add the idle observer in a timer, so that the app has enough
    // time to start up before we add the idle observer. If we register the
    // idle observer too early, it will be registered before the fake idle
    // service is installed when running in xpcshell, and this interferes with
    // the fake idle service, causing xpcshell-test failures.
    mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
    NS_ENSURE_TRUE(mTimer != nullptr, NS_ERROR_FAILURE);
    nsresult rv = mTimer->Init(this,
                         SCHEDULE_TIMEOUT_MS,
                         nsITimer::TYPE_ONE_SHOT);
    NS_ENSURE_SUCCESS(rv, rv);

    // Register shutdown observer so we can cancel the timer if we shutdown before
    // the timer runs.
    nsCOMPtr<nsIObserverService> obsSrv = services::GetObserverService();
    NS_ENSURE_TRUE(obsSrv != nullptr, NS_ERROR_FAILURE);
    return obsSrv->AddObserver(this, XPCOM_SHUTDOWN_TOPIC, false);
  }

  void Cleanup() {
    // Cancel timer.
    if (mTimer) {
      mTimer->Cancel();
      mTimer = nullptr;
    }    
    // Remove idle service observer.
    nsCOMPtr<nsIIdleService> idleSvc =
      do_GetService("@mozilla.org/widget/idleservice;1");
    if (idleSvc) {
      idleSvc->RemoveIdleObserver(this, TEMP_FILE_IDLE_TIME_S);    
    }
    // Remove shutdown observer.
    nsCOMPtr<nsIObserverService> obsSrv = services::GetObserverService();
    if (obsSrv) {
      obsSrv->RemoveObserver(this, XPCOM_SHUTDOWN_TOPIC);
    }
  }
  
  NS_IMETHODIMP Observe(nsISupports *aSubject,
                        const char *aTopic,
                        const PRUnichar *aData)
  {
    if (nsCRT::strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0 &&
        NS_FAILED(RegisterIdleObserver())) {
      Cleanup();
    } else if (nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE) == 0) {
      // The user has been idle for a while, clean up the temp files.
      // The idle service will drop its reference to this object after
      // we exit, destroying this object.
      RemoveAnonTempFileFiles();
      Cleanup();
    } else if (nsCRT::strcmp(aTopic, XPCOM_SHUTDOWN_TOPIC) == 0) {
      Cleanup();
    }
    return NS_OK;
  }

  nsresult RegisterIdleObserver() {
    // Add this as an idle observer. When we've been idle for 
    // TEMP_FILE_IDLE_TIME_S seconds, we'll get a notification, and we'll then
    // try to delete any stray temp files.
    nsCOMPtr<nsIIdleService> idleSvc =
      do_GetService("@mozilla.org/widget/idleservice;1");
    if (!idleSvc)
      return NS_ERROR_FAILURE;
    return idleSvc->AddIdleObserver(this, TEMP_FILE_IDLE_TIME_S);
  }

  void RemoveAnonTempFileFiles() {
    nsCOMPtr<nsIFile> tmpDir;
    nsresult rv = GetTempDir(getter_AddRefs(tmpDir));
    NS_ENSURE_SUCCESS_VOID(rv);

    // Remove the directory recursively.
    tmpDir->Remove(true);
  }

private:
  nsCOMPtr<nsITimer> mTimer;
};

NS_IMPL_ISUPPORTS1(nsAnonTempFileRemover, nsIObserver)

nsresult CreateAnonTempFileRemover() {
  // Create a temp file remover. If Init() succeeds, the temp file remover is kept
  // alive by a reference held by the observer service, since the temp file remover
  // is a shutdown observer. We only create the temp file remover if we're running
  // in the main process; there's no point in doing the temp file removal multiple
  // times per startup.
  if (XRE_GetProcessType() != GeckoProcessType_Default) {
    return NS_OK;
  }
  nsRefPtr<nsAnonTempFileRemover> tempRemover = new nsAnonTempFileRemover();
  return tempRemover->Init();
}

#endif