Commit 42819820 authored by Kathleen Brade's avatar Kathleen Brade Committed by Matthew Finkel
Browse files

Bug 13252: Do not store data in the app bundle

When --enable-tor-browser-data-outside-app-dir is enabled,
all user data is stored in a directory named
TorBrowser-Data which is located next to the application directory.

Display an informative error message if the TorBrowser-Data
directory cannot be created due to an "access denied" or a
"read only volume" error.

On Mac OS, add support for the --invisible command line option which
is used by the meek-http-helper to avoid showing an icon for the
helper browser on the dock.
parent 2d241330
Loading
Loading
Loading
Loading
+63 −13
Original line number Diff line number Diff line
@@ -2373,6 +2373,8 @@ static nsresult ProfileMissingDialog(nsINativeAppSupport* aNative) {
  }
}

// If aUnlocker is NULL, it is also OK for the following arguments to be NULL:
//   aProfileDir, aProfileLocalDir, aResult.
static ReturnAbortOnError ProfileErrorDialog(nsIFile* aProfileDir,
                                             nsIFile* aProfileLocalDir,
                                             ProfileStatus aStatus,
@@ -2381,17 +2383,19 @@ static ReturnAbortOnError ProfileErrorDialog(nsIFile* aProfileDir,
                                             nsIProfileLock** aResult) {
  nsresult rv;

  if (aProfileDir) {
    bool exists;
    aProfileDir->Exists(&exists);
    if (!exists) {
      return ProfileMissingDialog(aNative);
    }
  }

  ScopedXPCOMStartup xpcom;
  rv = xpcom.Initialize();
  NS_ENSURE_SUCCESS(rv, rv);

  mozilla::Telemetry::WriteFailedProfileLock(aProfileDir);
  if (aProfileDir) mozilla::Telemetry::WriteFailedProfileLock(aProfileDir);

  rv = xpcom.SetWindowCreator(aNative);
  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
@@ -2481,7 +2485,8 @@ static ReturnAbortOnError ProfileErrorDialog(nsIFile* aProfileDir,
      }
    } else {
#ifdef MOZ_WIDGET_ANDROID
      if (java::GeckoAppShell::UnlockProfile()) {
      if (aProfileDir && aProfileLocalDir && aResult &&
          java::GeckoAppShell::UnlockProfile()) {
        return NS_LockProfilePath(aProfileDir, aProfileLocalDir, nullptr,
                                  aResult);
      }
@@ -2590,6 +2595,23 @@ static ReturnAbortOnError ShowProfileManager(
  return LaunchChild(false);
}

#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
static ProfileStatus CheckTorBrowserDataWriteAccess(nsIFile* aAppDir) {
  // Check whether we can write to the directory that will contain
  // TorBrowser-Data.
  nsCOMPtr<nsIFile> tbDataDir;
  RefPtr<nsXREDirProvider> dirProvider = nsXREDirProvider::GetSingleton();
  if (!dirProvider) return PROFILE_STATUS_OTHER_ERROR;
  nsresult rv =
      dirProvider->GetTorBrowserUserDataDir(getter_AddRefs(tbDataDir));
  NS_ENSURE_SUCCESS(rv, PROFILE_STATUS_OTHER_ERROR);
  nsCOMPtr<nsIFile> tbDataDirParent;
  rv = tbDataDir->GetParent(getter_AddRefs(tbDataDirParent));
  NS_ENSURE_SUCCESS(rv, PROFILE_STATUS_OTHER_ERROR);
  return nsToolkitProfileService::CheckProfileWriteAccess(tbDataDirParent);
}
#endif

static bool gDoMigration = false;
static bool gDoProfileReset = false;
static nsCOMPtr<nsIToolkitProfile> gResetOldProfile;
@@ -3614,6 +3636,14 @@ int XREMain::XRE_mainInit(bool* aExitFlag) {
  if (PR_GetEnv("XRE_MAIN_BREAK")) NS_BREAK();
#endif

#if defined(XP_MACOSX) && defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR)
  bool hideDockIcon = (CheckArg("invisible") == ARG_FOUND);
  if (hideDockIcon) {
    ProcessSerialNumber psn = {0, kCurrentProcess};
    TransformProcessType(&psn, kProcessTransformToBackgroundApplication);
  }
#endif

  IncreaseDescriptorLimits();

#ifdef USE_GLX_TEST
@@ -4466,7 +4496,34 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) {
    return 0;
  }

#if (defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)) || \
    defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR)
  nsCOMPtr<nsIFile> exeFile, exeDir;
  bool persistent;
  rv = mDirProvider.GetFile(XRE_EXECUTABLE_FILE, &persistent,
                            getter_AddRefs(exeFile));
  NS_ENSURE_SUCCESS(rv, 1);
  rv = exeFile->GetParent(getter_AddRefs(exeDir));
  NS_ENSURE_SUCCESS(rv, 1);
#endif

  rv = NS_NewToolkitProfileService(getter_AddRefs(mProfileSvc));
#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
  if (NS_FAILED(rv)) {
    // NS_NewToolkitProfileService() returns a generic NS_ERROR_FAILURE error
    // if creation of the TorBrowser-Data directory fails due to access denied
    // or because of a read-only disk volume. Do an extra check here to detect
    // these errors so we can display an informative error message.
    ProfileStatus status = CheckTorBrowserDataWriteAccess(exeDir);
    if ((PROFILE_STATUS_ACCESS_DENIED == status) ||
        (PROFILE_STATUS_READ_ONLY == status)) {
      ProfileErrorDialog(nullptr, nullptr, status, nullptr, mNativeApp,
                         nullptr);
      return 1;
    }
  }
#endif

  if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
    PR_fprintf(PR_STDERR,
               "Error: Access was denied while trying to open files in "
@@ -4535,7 +4592,6 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) {
#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
  // Check for and process any available updates
  nsCOMPtr<nsIFile> updRoot;
  bool persistent;
  rv = mDirProvider.GetFile(XRE_UPDATE_ROOT_DIR, &persistent,
                            getter_AddRefs(updRoot));
  // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed
@@ -4571,12 +4627,6 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) {
  if (CheckArg("test-process-updates")) {
    SaveToEnv("MOZ_TEST_PROCESS_UPDATES=1");
  }
  nsCOMPtr<nsIFile> exeFile, exeDir;
  rv = mDirProvider.GetFile(XRE_EXECUTABLE_FILE, &persistent,
                            getter_AddRefs(exeFile));
  NS_ENSURE_SUCCESS(rv, 1);
  rv = exeFile->GetParent(getter_AddRefs(exeDir));
  NS_ENSURE_SUCCESS(rv, 1);
  ProcessUpdates(mDirProvider.GetGREDir(), exeDir, updRoot, gRestartArgc,
                 gRestartArgv, mAppData->version);
  if (EnvHasValue("MOZ_TEST_PROCESS_UPDATES")) {
+19 −24
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@
#include "mozilla/XREAppData.h"
#include "nsPrintfCString.h"

#include "TorFileUtils.h"

#include <stdlib.h>

#ifdef XP_WIN
@@ -1401,34 +1403,18 @@ nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile,
    return gDataDirHome->Clone(aFile);
  }

  nsresult rv = GetAppDir()->Clone(getter_AddRefs(localDir));
  NS_ENSURE_SUCCESS(rv, rv);

  int levelsToRemove = 1;  // In FF21+, appDir points to browser subdirectory.
#if defined(XP_MACOSX)
  levelsToRemove += 2;
#endif
  while (localDir && (levelsToRemove > 0)) {
    // When crawling up the hierarchy, components named "." do not count.
    nsAutoCString removedName;
    rv = localDir->GetNativeLeafName(removedName);
  nsresult rv = GetTorBrowserUserDataDir(getter_AddRefs(localDir));
  NS_ENSURE_SUCCESS(rv, rv);
    bool didRemove = !removedName.Equals(".");

    // Remove a directory component.
    nsCOMPtr<nsIFile> parentDir;
    rv = localDir->GetParent(getter_AddRefs(parentDir));
    NS_ENSURE_SUCCESS(rv, rv);
    localDir = parentDir;
    if (didRemove) --levelsToRemove;
  }

  if (!localDir) return NS_ERROR_FAILURE;

  rv = localDir->AppendRelativeNativePath("TorBrowser" XPCOM_FILE_PATH_SEPARATOR
                                          "Data" XPCOM_FILE_PATH_SEPARATOR
#if !defined(ANDROID)
#  ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
  rv = localDir->AppendNative("Browser"_ns);
#  else
  rv = localDir->AppendRelativeNativePath("Data" XPCOM_FILE_PATH_SEPARATOR
                                          "Browser"_ns);
#  endif
  NS_ENSURE_SUCCESS(rv, rv);
#endif

  if (aLocal) {
    rv = localDir->AppendNative("Caches"_ns);
@@ -1534,6 +1520,15 @@ nsresult nsXREDirProvider::GetUserDataDirectory(nsIFile** aFile, bool aLocal) {
  return NS_OK;
}

nsresult nsXREDirProvider::GetTorBrowserUserDataDir(nsIFile** aFile) {
  NS_ENSURE_ARG_POINTER(aFile);
  nsCOMPtr<nsIFile> exeFile;
  bool per = false;
  nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(exeFile));
  NS_ENSURE_SUCCESS(rv, rv);
  return TorBrowser_GetUserDataDir(exeFile, aFile);
}

nsresult nsXREDirProvider::EnsureDirectoryExists(nsIFile* aDirectory) {
  nsresult rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0700);

+6 −0
Original line number Diff line number Diff line
@@ -113,6 +113,12 @@ class nsXREDirProvider final : public nsIDirectoryServiceProvider2,
   */
  nsresult GetProfileDir(nsIFile** aResult);

  /**
   * Get the TorBrowser user data directory by calling the
   * TorBrowser_GetUserDataDir() utility function.
   */
  nsresult GetTorBrowserUserDataDir(nsIFile** aFile);

 protected:
  nsresult GetFilesInternal(const char* aProperty,
                            nsISimpleEnumerator** aResult);
+133 −0
Original line number Diff line number Diff line
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "TorFileUtils.h"
#include "nsString.h"
#ifdef MOZ_WIDGET_COCOA
#  include <Carbon/Carbon.h>
#  include "nsILocalFileMac.h"
#endif

static nsresult GetAppRootDir(nsIFile* aExeFile, nsIFile** aFile);

//-----------------------------------------------------------------------------
nsresult TorBrowser_GetUserDataDir(nsIFile* aExeFile, nsIFile** aFile) {
  NS_ENSURE_ARG_POINTER(aFile);
  nsCOMPtr<nsIFile> tbDataDir;

#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
  nsAutoCString tbDataLeafName("TorBrowser-Data"_ns);
  nsCOMPtr<nsIFile> appRootDir;
  nsresult rv = GetAppRootDir(aExeFile, getter_AddRefs(appRootDir));
  NS_ENSURE_SUCCESS(rv, rv);
#  ifndef XP_MACOSX
  // On all platforms except Mac OS, we always operate in a "portable" mode
  // where the TorBrowser-Data directory is located next to the application.
  rv = appRootDir->GetParent(getter_AddRefs(tbDataDir));
  NS_ENSURE_SUCCESS(rv, rv);
  rv = tbDataDir->AppendNative(tbDataLeafName);
  NS_ENSURE_SUCCESS(rv, rv);
#  else
  // For Mac OS, determine whether we should store user data in the OS's
  // standard location (i.e., under ~/Library/Application Support). We use
  // the OS location if (1) the application is installed in a directory whose
  // path contains "/Applications" or (2) the TorBrowser-Data directory does
  // not exist and cannot be created (which probably means we lack write
  // permission to the directory that contains the application).
  nsAutoString appRootPath;
  rv = appRootDir->GetPath(appRootPath);
  NS_ENSURE_SUCCESS(rv, rv);
  bool useOSLocation =
      (appRootPath.Find("/Applications", true /* ignore case */) >= 0);
  if (!useOSLocation) {
    // We hope to use the portable (aka side-by-side) approach, but before we
    // commit to that, let's ensure that we can create the TorBrowser-Data
    // directory. If it already exists, we will try to use it; if not and we
    // fail to create it, we will switch to ~/Library/Application Support.
    rv = appRootDir->GetParent(getter_AddRefs(tbDataDir));
    NS_ENSURE_SUCCESS(rv, rv);
    rv = tbDataDir->AppendNative(tbDataLeafName);
    NS_ENSURE_SUCCESS(rv, rv);
    bool exists = false;
    rv = tbDataDir->Exists(&exists);
    if (NS_SUCCEEDED(rv) && !exists)
      rv = tbDataDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
    useOSLocation = NS_FAILED(rv);
  }

  if (useOSLocation) {
    // We are using ~/Library/Application Support/TorBrowser-Data. We do not
    // need to create that directory here because the code in nsXREDirProvider
    // will do so (and the user should always have write permission for
    // ~/Library/Application Support; if they do not we have no more options).
    FSRef fsRef;
    OSErr err = ::FSFindFolder(kUserDomain, kApplicationSupportFolderType,
                               kCreateFolder, &fsRef);
    NS_ENSURE_FALSE(err, NS_ERROR_FAILURE);
    // To convert the FSRef returned by FSFindFolder() into an nsIFile that
    // points to ~/Library/Application Support, we first create an empty
    // nsIFile object (no path) and then use InitWithFSRef() to set the
    // path.
    rv = NS_NewNativeLocalFile(""_ns, true, getter_AddRefs(tbDataDir));
    NS_ENSURE_SUCCESS(rv, rv);
    nsCOMPtr<nsILocalFileMac> dirFileMac = do_QueryInterface(tbDataDir);
    if (!dirFileMac) return NS_ERROR_UNEXPECTED;
    rv = dirFileMac->InitWithFSRef(&fsRef);
    NS_ENSURE_SUCCESS(rv, rv);
    rv = tbDataDir->AppendNative(tbDataLeafName);
    NS_ENSURE_SUCCESS(rv, rv);
  }
#  endif

#elif defined(ANDROID)
  // Tor Browser Android stores data in the app home directory.
  const char* homeDir = getenv("HOME");
  if (!homeDir || !*homeDir) return NS_ERROR_FAILURE;
  nsresult rv = NS_NewNativeLocalFile(nsDependentCString(homeDir), true,
                                      getter_AddRefs(tbDataDir));
#else
  // User data is embedded within the application directory (i.e.,
  // TOR_BROWSER_DATA_OUTSIDE_APP_DIR is not defined).
  nsresult rv = GetAppRootDir(aExeFile, getter_AddRefs(tbDataDir));
  NS_ENSURE_SUCCESS(rv, rv);
  rv = tbDataDir->AppendNative("TorBrowser"_ns);
  NS_ENSURE_SUCCESS(rv, rv);
#endif

  tbDataDir.forget(aFile);
  return NS_OK;
}

static nsresult GetAppRootDir(nsIFile* aExeFile, nsIFile** aFile) {
  NS_ENSURE_ARG_POINTER(aExeFile);
  NS_ENSURE_ARG_POINTER(aFile);
  nsCOMPtr<nsIFile> appRootDir = aExeFile;

  int levelsToRemove = 1;  // Remove firefox (the executable file).
#if defined(XP_MACOSX)
  levelsToRemove += 2;  // On Mac OS, we must also remove Contents/MacOS.
#endif
  while (appRootDir && (levelsToRemove > 0)) {
    // When crawling up the hierarchy, components named "." do not count.
    nsAutoCString removedName;
    nsresult rv = appRootDir->GetNativeLeafName(removedName);
    NS_ENSURE_SUCCESS(rv, rv);
    bool didRemove = !removedName.Equals(".");

    // Remove a directory component.
    nsCOMPtr<nsIFile> parentDir;
    rv = appRootDir->GetParent(getter_AddRefs(parentDir));
    NS_ENSURE_SUCCESS(rv, rv);
    appRootDir = parentDir;

    if (didRemove) --levelsToRemove;
  }

  if (!appRootDir) return NS_ERROR_FAILURE;

  appRootDir.forget(aFile);
  return NS_OK;
}
+32 −0
Original line number Diff line number Diff line
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */

#ifndef TorFileUtils_h__
#define TorFileUtils_h__

#include "nsIFile.h"

/**
 * TorBrowser_GetUserDataDir
 *
 * Retrieve the Tor Browser user data directory.
 * When built with --enable-tor-browser-data-outside-app-dir, the directory
 * is next to the application directory, except on Mac OS where it may be
 * there or it may be at ~/Library/Application Support/TorBrowser-Data (the
 * latter location is used if the .app bundle is in a directory whose path
 * contains /Applications or if we lack write access to the directory that
 * contains the .app).
 * When built without --enable-tor-browser-data-outside-app-dir, this
 * directory is TorBrowser.app/TorBrowser.
 *
 * @param aExeFile  The firefox executable.
 * @param aFile     Out parameter that is set to the Tor Browser user data
 *                  directory.
 * @return NS_OK on success.  Error otherwise.
 */
extern nsresult TorBrowser_GetUserDataDir(nsIFile* aExeFile, nsIFile** aFile);

#endif  // !TorFileUtils_h__
Loading