LCOV - code coverage report
Current view: top level - xpcom/io - nsAnonymousTemporaryFile.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 28 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 2 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13