xpcom/io/nsAnonymousTemporaryFile.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     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"
    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"
    26 using namespace mozilla;
    27 #endif
    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;
    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
    72   tmpFile.forget(aTempDir);
    74   return NS_OK;
    75 }
    77 nsresult
    78 NS_OpenAnonymousTemporaryFile(PRFileDesc** aOutFileDesc)
    79 {
    80   if (NS_WARN_IF(!aOutFileDesc))
    81     return NS_ERROR_INVALID_ARG;
    82   nsresult rv;
    84   nsCOMPtr<nsIFile> tmpFile;
    85   rv = GetTempDir(getter_AddRefs(tmpFile));
    86   if (NS_WARN_IF(NS_FAILED(rv)))
    87     return rv;
    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());
    98   rv = tmpFile->AppendNative(name);
    99   if (NS_WARN_IF(NS_FAILED(rv)))
   100     return rv;
   102   rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700);
   103   if (NS_WARN_IF(NS_FAILED(rv)))
   104     return rv;
   106   rv = tmpFile->OpenNSPRFileDesc(PR_RDWR | nsIFile::DELETE_ON_CLOSE,
   107                                  PR_IRWXU, aOutFileDesc);
   109   return rv;    
   110 }
   112 #ifdef XP_WIN
   114 // On Windows we have an idle service observer that runs some time after
   115 // startup and deletes any stray anonymous temporary files...
   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
   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
   125 #define XPCOM_SHUTDOWN_TOPIC "xpcom-shutdown"
   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
   142   nsAnonTempFileRemover() {
   143     MOZ_COUNT_CTOR(nsAnonTempFileRemover);
   144   }
   146   ~nsAnonTempFileRemover() {
   147     MOZ_COUNT_DTOR(nsAnonTempFileRemover);
   148   }
   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;
   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   }
   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   }
   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   }
   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   }
   222   void RemoveAnonTempFileFiles() {
   223     nsCOMPtr<nsIFile> tmpDir;
   224     nsresult rv = GetTempDir(getter_AddRefs(tmpDir));
   225     if (NS_WARN_IF(NS_FAILED(rv)))
   226       return;
   228     // Remove the directory recursively.
   229     tmpDir->Remove(true);
   230   }
   232 private:
   233   nsCOMPtr<nsITimer> mTimer;
   234 };
   236 NS_IMPL_ISUPPORTS(nsAnonTempFileRemover, nsIObserver)
   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 }
   251 #endif

mercurial