diff --git a/core/INSTALL.txt b/core/INSTALL.txt
index e9430d4..3f47a41 100644
--- a/core/INSTALL.txt
+++ b/core/INSTALL.txt
@@ -151,12 +151,12 @@ INSTALLATION
       which is normally in the directory sites/default (to avoid problems when
       upgrading, Drupal is not packaged with this file). If auto-creation fails,
       you will need to create this file yourself, using the file
-      sites/default/default.settings.php as a template.
+      core/default.settings.php as a template.
 
       For example, on a Unix/Linux command line, you can make a copy of the
       default.settings.php file with the command:
 
-        cp sites/default/default.settings.php sites/default/settings.php
+        cp core/default.settings.php sites/default/settings.php
 
       Next, grant write privileges to the file to everyone (including the web
       server) with the command:
diff --git a/core/UPGRADE.txt b/core/UPGRADE.txt
index f035b6c..57021fb 100644
--- a/core/UPGRADE.txt
+++ b/core/UPGRADE.txt
@@ -163,10 +163,6 @@ following the instructions in the INTRODUCTION section at the top of this file:
    no longer need their data, then you can uninstall them under the Uninstall
    tab after disabling them.
 
-8. On the command line or in your FTP client, remove the file
-
-     sites/default/default.settings.php
-
 9. Remove all old core files and directories, except for the 'sites' directory
    and any custom files you added elsewhere.
 
diff --git a/sites/default/default.settings.php b/core/default.settings.php
similarity index 91%
rename from sites/default/default.settings.php
rename to core/default.settings.php
index 5642dc5..7bf3ec7 100644
--- a/sites/default/default.settings.php
+++ b/core/default.settings.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Drupal site-specific configuration file.
+ * Drupal configuration file.
  *
  * IMPORTANT NOTE:
  * This file may have been set to read-only by the Drupal installation program.
@@ -10,10 +10,9 @@
  * your modifications. Failure to remove write permissions to this file is a
  * security risk.
  *
- * In order to use the selection rules below the multisite aliasing file named
- * sites/sites.php must be present. Its optional settings will be loaded, and
- * the aliases in the array $sites will override the default directory rules
- * below. See sites/example.sites.php for more information about aliases.
+ * The configuration file to be loaded is based upon the rules below. However
+ * if the multisite aliasing is enabled, the aliases in the $sites array will
+ * override the default directory rules below.
  *
  * The configuration directory will be discovered by stripping the website's
  * hostname from left to right and pathname from right to left. The first
@@ -46,10 +45,61 @@
  * hostname with that number. For example,
  * http://www.drupal.org:8080/mysite/test/ could be loaded from
  * sites/8080.www.drupal.org.mysite.test/.
+ */
+
+/**
+ * Multi-site functionality and aliases.
+ *
+ * Uncomment the $sites variable below to enable Drupal's multi-site
+ * functionality, which allows to serve multiple different sites from the same
+ * code-base.
+ *
+ * An empty $sites variable just enables the sites directory discovery process.
+ * You can additionally define aliases that map hostnames, ports, and path names
+ * to specific site directories. These aliases are applied prior to scanning for
+ * directories and exempt from the discovery rules.
+ *
+ * Aliases are useful on development servers, where the domain name may not be
+ * the same as the domain of the live server. Since Drupal stores file paths in
+ * the database (files, system table, etc.) this will ensure the paths are
+ * correct when the site is deployed to a live server.
+ *
+ * Aliases are defined in an associative array named $sites. The array is
+ * written in the format: '<port>.<domain>.<path>' => 'directory'. As an
+ * example, to map http://www.drupal.org:8080/mysite/test to the configuration
+ * directory sites/example.com, the array should be defined as:
+ * @code
+ * $sites = array(
+ *   '8080.www.drupal.org.mysite.test' => 'example.com',
+ * );
+ * @endcode
+ * The URL, http://www.drupal.org:8080/mysite/test/, could be a symbolic link or
+ * an Apache Alias directive that points to the Drupal root containing
+ * index.php. An alias could also be created for a subdomain. See the
+ * @link http://drupal.org/documentation/install online Drupal installation guide @endlink
+ * for more information on setting up domains, subdomains, and subdirectories.
+ *
+ * The following examples look for a site configuration in sites/example.com:
+ * @code
+ * URL: http://dev.drupal.org
+ * $sites['dev.drupal.org'] = 'example.com';
+ *
+ * URL: http://localhost/example
+ * $sites['localhost.example'] = 'example.com';
+ *
+ * URL: http://localhost:8080/example
+ * $sites['8080.localhost.example'] = 'example.com';
+ *
+ * URL: http://www.drupal.org:8080/mysite/test/
+ * $sites['8080.www.drupal.org.mysite.test'] = 'example.com';
+ * @endcode
  *
- * @see example.sites.php
- * @see conf_path()
+ * @see default.settings.php
+ * @see \Drupal\Core\Site\Site::getPath()
+ * @see http://drupal.org/documentation/install/multi-site
  */
+# $sites = array();
+# $sites['localhost.example'] = 'example.com';
 
 /**
  * Database settings:
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index fd24e01..e82f6c7 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -10,6 +10,7 @@
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Site\Site;
 use Drupal\Core\Utility\Title;
 use Drupal\Core\Utility\Error;
 use Symfony\Component\ClassLoader\ApcClassLoader;
@@ -218,116 +219,6 @@
 define('DRUPAL_ROOT', dirname(dirname(__DIR__)));
 
 /**
- * Returns the appropriate configuration directory.
- *
- * Returns the configuration path based on the site's hostname, port, and
- * pathname. Uses find_conf_path() to find the current configuration directory.
- * See default.settings.php for examples on how the URL is converted to a
- * directory.
- *
- * @param bool $require_settings
- *   Only configuration directories with an existing settings.php file
- *   will be recognized. Defaults to TRUE. During initial installation,
- *   this is set to FALSE so that Drupal can detect a matching directory,
- *   then create a new settings.php file in it.
- * @param bool $reset
- *   Force a full search for matching directories even if one had been
- *   found previously. Defaults to FALSE.
- *
- * @return
- *   The path of the matching directory.
- *
- * @see default.settings.php
- */
-function conf_path($require_settings = TRUE, $reset = FALSE) {
-  static $conf_path;
-
-  if (isset($conf_path) && !$reset) {
-    return $conf_path;
-  }
-
-  // Check for a simpletest override.
-  if ($test_prefix = drupal_valid_test_ua()) {
-    $conf_path = 'sites/simpletest/' . substr($test_prefix, 10);
-    return $conf_path;
-  }
-
-  // Otherwise, use the normal $conf_path.
-  $script_name = $_SERVER['SCRIPT_NAME'];
-  if (!$script_name) {
-    $script_name = $_SERVER['SCRIPT_FILENAME'];
-  }
-  $http_host = $_SERVER['HTTP_HOST'];
-  $conf_path = find_conf_path($http_host, $script_name, $require_settings);
-  return $conf_path;
-}
-
-/**
- * Finds the appropriate configuration directory for a given host and path.
- *
- * Finds a matching configuration directory file by stripping the website's
- * hostname from left to right and pathname from right to left. By default,
- * the directory must contain a 'settings.php' file for it to match. If the
- * parameter $require_settings is set to FALSE, then a directory without a
- * 'settings.php' file will match as well. The first configuration
- * file found will be used and the remaining ones will be ignored. If no
- * configuration file is found, returns a default value '$confdir/default'. See
- * default.settings.php for examples on how the URL is converted to a directory.
- *
- * If a file named sites.php is present in the $confdir, it will be loaded
- * prior to scanning for directories. That file can define aliases in an
- * associative array named $sites. The array is written in the format
- * '<port>.<domain>.<path>' => 'directory'. As an example, to create a
- * directory alias for http://www.drupal.org:8080/mysite/test whose configuration
- * file is in sites/example.com, the array should be defined as:
- * @code
- * $sites = array(
- *   '8080.www.drupal.org.mysite.test' => 'example.com',
- * );
- * @endcode
- *
- * @param $http_host
- *   The hostname and optional port number, e.g. "www.example.com" or
- *   "www.example.com:8080".
- * @param $script_name
- *   The part of the URL following the hostname, including the leading slash.
- * @param $require_settings
- *   Defaults to TRUE. If TRUE, then only match directories with a
- *   'settings.php' file. Otherwise match any directory.
- *
- * @return
- *   The path of the matching configuration directory.
- *
- * @see default.settings.php
- * @see example.sites.php
- * @see conf_path()
- */
-function find_conf_path($http_host, $script_name, $require_settings = TRUE) {
-  // Determine whether multi-site functionality is enabled.
-  if (!file_exists(DRUPAL_ROOT . '/sites/sites.php')) {
-    return 'sites/default';
-  }
-
-  $sites = array();
-  include DRUPAL_ROOT . '/sites/sites.php';
-
-  $uri = explode('/', $script_name);
-  $server = explode('.', implode('.', array_reverse(explode(':', rtrim($http_host, '.')))));
-  for ($i = count($uri) - 1; $i > 0; $i--) {
-    for ($j = count($server); $j > 0; $j--) {
-      $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
-      if (isset($sites[$dir]) && file_exists(DRUPAL_ROOT . '/sites/' . $sites[$dir])) {
-        $dir = $sites[$dir];
-      }
-      if (file_exists(DRUPAL_ROOT . '/sites/' . $dir . '/settings.php') || (!$require_settings && file_exists(DRUPAL_ROOT . '/sites/' . $dir))) {
-        return "sites/$dir";
-      }
-    }
-  }
-  return 'sites/default';
-}
-
-/**
  * Returns the path of a configuration directory.
  *
  * @param string $type
@@ -378,7 +269,7 @@ function config_get_config_directory($type = CONFIG_ACTIVE_DIRECTORY) {
  *   excluding any GET request but including the script name
  *   (e.g., http://www.example.com/mysite/index.php).
  *
- * @see conf_path()
+ * @see \Drupal\Core\Site\Site::getPath()
  * @see request_uri()
  * @see \Symfony\Component\HttpFoundation\Request::getClientIP()
  */
@@ -411,7 +302,7 @@ function drupal_override_server_variables($variables = array()) {
   // Replace elements of the $_SERVER array, as appropriate.
   $request->server->replace($variables + $server_vars + $defaults);
 
-  // @todo remove once conf_path() no longer uses $_SERVER.
+  // @todo remove once Site::determinePath() no longer uses $_SERVER.
   $_SERVER = $request->server->all();
 }
 
@@ -450,7 +341,7 @@ function drupal_environment_initialize() {
   error_reporting(E_STRICT | E_ALL | error_reporting());
 
   // Override PHP settings required for Drupal to work properly.
-  // sites/default/default.settings.php contains more runtime settings.
+  // settings.php contains more runtime settings.
   // The .htaccess file contains settings that cannot be changed at runtime.
 
   // Deny execution with enabled "magic quotes" (both GPC and runtime).
@@ -495,9 +386,20 @@ function drupal_settings_initialize() {
   $settings = array();
   $config = array();
 
-  // Make conf_path() available as local variable in settings.php.
-  $conf_path = conf_path();
-  if (is_readable(DRUPAL_ROOT . '/' . $conf_path . '/settings.php')) {
+  // Read the global /settings.php file.
+  // Exclude it for test requests to prevent settings of the test runner from
+  // leaking into the test environment.
+  if (!drupal_valid_test_ua() && is_readable(DRUPAL_ROOT . '/settings.php')) {
+    require DRUPAL_ROOT . '/settings.php';
+  }
+
+  // Discover the site directory.
+  Site::init(DRUPAL_ROOT, isset($sites) ? $sites : NULL, isset($conf_path) ? $conf_path : NULL);
+
+  // Make $conf_path available as local variable in settings.php.
+  // Concatenation is safe here, since $conf_path is known to be not empty.
+  $conf_path = Site::getPath();
+  if ($conf_path !== '' && is_readable(DRUPAL_ROOT . '/' . $conf_path . '/settings.php')) {
     require DRUPAL_ROOT . '/' . $conf_path . '/settings.php';
   }
   // Initialize Settings.
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 68fc46e..18bed0f 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -12,6 +12,7 @@
 use Drupal\Core\Database\Install\TaskException;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Site\Site;
 use Drupal\Core\StringTranslation\Translator\FileTranslation;
 
 use Drupal\Core\DependencyInjection\ContainerBuilder;
@@ -270,12 +271,6 @@ function install_begin_request(&$install_state) {
     drupal_override_server_variables($install_state['server']);
   }
 
-  // Initialize conf_path().
-  // This primes the site path to be used during installation. By not requiring
-  // settings.php, a bare site folder can be prepared in the /sites directory,
-  // which will be used for installing Drupal.
-  conf_path(FALSE);
-
   // If the hash salt leaks, it becomes possible to forge a valid testing user
   // agent, install a new copy of Drupal, and take over the original site.
   // The user agent header is used to pass a database prefix in the request when
@@ -286,6 +281,12 @@ function install_begin_request(&$install_state) {
     exit;
   }
 
+  // Initialize the Site directory.
+  // This primes the site path to be used during installation, to allow a bare
+  // site folder to be prepared in the /sites directory, which will be used for
+  // installing Drupal.
+  Site::initInstaller(DRUPAL_ROOT);
+
   drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
 
   // Ensure that procedural dependencies are loaded as early as possible,
@@ -1079,8 +1080,7 @@ function install_verify_database_settings() {
   global $databases;
   if (!empty($databases)) {
     $database = $databases['default']['default'];
-    $settings_file = './' . conf_path(FALSE) . '/settings.php';
-    $errors = install_database_errors($database, $settings_file);
+    $errors = install_database_errors($database);
     if (empty($errors)) {
       return TRUE;
     }
@@ -1101,8 +1101,6 @@ function install_verify_database_settings() {
 function install_settings_form($form, &$form_state, &$install_state) {
   global $databases;
 
-  $conf_path = './' . conf_path(FALSE);
-  $settings_file = $conf_path . '/settings.php';
 
   drupal_set_title(t('Database configuration'));
 
@@ -1172,7 +1170,6 @@ function install_settings_form($form, &$form_state, &$install_state) {
   );
 
   $form['errors'] = array();
-  $form['settings_file'] = array('#type' => 'value', '#value' => $settings_file);
 
   return $form;
 }
@@ -1193,7 +1190,7 @@ function install_settings_form_validate($form, &$form_state) {
   $database['driver'] = $driver;
 
   $form_state['storage']['database'] = $database;
-  $errors = install_database_errors($database, $form_state['values']['settings_file']);
+  $errors = install_database_errors($database);
   foreach ($errors as $name => $message) {
     form_set_error($name, $form_state, $message);
   }
@@ -1202,7 +1199,7 @@ function install_settings_form_validate($form, &$form_state) {
 /**
  * Checks a database connection and returns any errors.
  */
-function install_database_errors($database, $settings_file) {
+function install_database_errors($database) {
   global $databases;
   $errors = array();
 
@@ -1210,7 +1207,11 @@ function install_database_errors($database, $settings_file) {
   $database_types = drupal_get_database_types();
   $driver = $database['driver'];
   if (!isset($database_types[$driver])) {
-    $errors['driver'] = t("In your %settings_file file you have configured @drupal to use a %driver server, however your PHP installation currently does not support this database type.", array('%settings_file' => $settings_file, '@drupal' => drupal_install_profile_distribution_name(), '%driver' => $driver));
+    $errors['driver'] = t("In your %settings_file file you have configured @drupal to use a %driver server, however your PHP installation currently does not support this database type.", array(
+      '%settings_file' => Site::getPath('settings.php'),
+      '@drupal' => drupal_install_profile_distribution_name(),
+      '%driver' => $driver,
+    ));
   }
   else {
     // Run driver specific validation
@@ -1489,7 +1490,7 @@ function install_translations_directory() {
     $directory = $GLOBALS['conf']['locale.settings']['translation.path'];
   }
   else {
-    $directory = conf_path() . '/files/translations';
+    $directory = Site::getPath('files/translations');
   }
   return $directory;
 }
@@ -2066,8 +2067,8 @@ function install_configure_form($form, &$form_state, &$install_state) {
   drupal_set_title(t('Configure site'));
 
   // Warn about settings.php permissions risk
-  $settings_dir = conf_path();
-  $settings_file = $settings_dir . '/settings.php';
+  $settings_dir = Site::getPath();
+  $settings_file = Site::getPath('settings.php');
   // Check that $_POST is empty so we only show this message when the form is
   // first displayed, not on the next page after it is submitted. (We do not
   // want to repeat it multiple times because it is a general warning that is
@@ -2076,7 +2077,7 @@ function install_configure_form($form, &$form_state, &$install_state) {
   // distract from the message that the Drupal installation has completed
   // successfully.)
   $post_params = \Drupal::request()->request->all();
-  if (empty($post_params) && (!drupal_verify_install_file(DRUPAL_ROOT . '/' . $settings_file, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE) || !drupal_verify_install_file(DRUPAL_ROOT . '/' . $settings_dir, FILE_NOT_WRITABLE, 'dir'))) {
+  if (empty($post_params) && (!drupal_verify_install_file(Site::getAbsolutePath('settings.php'), FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE) || !drupal_verify_install_file(Site::getAbsolutePath(), FILE_NOT_WRITABLE, 'dir'))) {
     drupal_set_message(t('All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, consult the <a href="@handbook_url">online handbook</a>.', array('%dir' => $settings_dir, '%file' => $settings_file, '@handbook_url' => 'http://drupal.org/server-permissions')), 'warning');
   }
 
@@ -2222,8 +2223,8 @@ function install_check_translations($install_state) {
   $readable = FALSE;
   $writable = FALSE;
   // @todo: Make this configurable.
-  $files_directory = conf_path() . '/files';
-  $translations_directory = conf_path() . '/files/translations';
+  $files_directory = Site::getPath('files');
+  $translations_directory = Site::getPath('files/translations');
   $translations_directory_exists = FALSE;
   $translation_available = FALSE;
   $online = FALSE;
@@ -2376,15 +2377,13 @@ function install_check_requirements($install_state) {
   if (!$install_state['settings_verified']) {
     $readable = FALSE;
     $writable = FALSE;
-    $conf_path = './' . conf_path(FALSE);
-    $settings_file = $conf_path . '/settings.php';
-    $default_settings_file = './sites/default/default.settings.php';
-    $file = $conf_path;
+    $settings_file = Site::getAbsolutePath('settings.php');
+    $default_settings_file = './core/default.settings.php';
+    $file = Site::getPath('settings.php');
     $exists = FALSE;
     // Verify that the directory exists.
-    if (drupal_verify_install_file($conf_path, FILE_EXIST, 'dir')) {
+    if (drupal_verify_install_file(Site::getAbsolutePath(), FILE_EXIST, 'dir')) {
       // Check if a settings.php file already exists.
-      $file = $settings_file;
       if (drupal_verify_install_file($settings_file, FILE_EXIST)) {
         // If it does, make sure it is writable.
         $readable = drupal_verify_install_file($settings_file, FILE_READABLE);
@@ -2393,20 +2392,11 @@ function install_check_requirements($install_state) {
       }
     }
 
-    // If default.settings.php does not exist, or is not readable, throw an
-    // error.
-    if (!drupal_verify_install_file($default_settings_file, FILE_EXIST|FILE_READABLE)) {
-      $requirements['default settings file exists'] = array(
-        'title'       => t('Default settings file'),
-        'value'       => t('The default settings file does not exist.'),
-        'severity'    => REQUIREMENT_ERROR,
-        'description' => t('The @drupal installer requires that the %default-file file not be modified in any way from the original download.', array('@drupal' => drupal_install_profile_distribution_name(), '%default-file' => $default_settings_file)),
-      );
-    }
     // Otherwise, if settings.php does not exist yet, we can try to copy
     // default.settings.php to create it.
-    elseif (!$exists) {
-      $copied = drupal_verify_install_file($conf_path, FILE_EXIST|FILE_WRITABLE, 'dir') && @copy($default_settings_file, $settings_file);
+    if (!$exists) {
+      $copied = drupal_verify_install_file(Site::getAbsolutePath(), FILE_EXIST|FILE_WRITABLE, 'dir');
+      $copied = $copied && @copy($default_settings_file, $settings_file);
       if ($copied) {
         // If the new settings file has the same owner as default.settings.php,
         // this means default.settings.php is owned by the webserver user.
diff --git a/core/includes/install.inc b/core/includes/install.inc
index 895f068..85189b2 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -9,6 +9,7 @@
 use Drupal\Component\Utility\Crypt;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DrupalKernel;
+use Drupal\Core\Site\Site;
 
 /**
  * Requirement severity -- Informational message only.
@@ -195,7 +196,7 @@ function drupal_get_database_types() {
  */
 function drupal_rewrite_settings($settings = array(), $settings_file = NULL) {
   if (!isset($settings_file)) {
-    $settings_file = conf_path(FALSE) . '/settings.php';
+    $settings_file = Site::getPath('settings.php');
   }
   // Build list of setting names and insert the values into the global namespace.
   $variable_names = array();
@@ -445,11 +446,11 @@ function drupal_install_config_directories() {
     $config_directories_hash = Crypt::randomStringHashed(55);
     $settings['config_directories'] = array(
       CONFIG_ACTIVE_DIRECTORY => (object) array(
-        'value' => conf_path() . '/files/config_' . $config_directories_hash . '/active',
+        'value' => Site::getPath('files/config_' . $config_directories_hash . '/active'),
         'required' => TRUE,
       ),
       CONFIG_STAGING_DIRECTORY => (object) array(
-        'value' => conf_path() . '/files/config_' . $config_directories_hash . '/staging',
+        'value' => Site::getPath('files/config_' . $config_directories_hash . '/staging'),
         'required' => TRUE,
       ),
     );
diff --git a/core/includes/update.inc b/core/includes/update.inc
index c3bc07a..038c6d5 100644
--- a/core/includes/update.inc
+++ b/core/includes/update.inc
@@ -14,6 +14,7 @@
 use Drupal\Core\Config\FileStorage;
 use Drupal\Core\Config\ConfigException;
 use Drupal\Core\DrupalKernel;
+use Drupal\Core\Site\Site;
 use Drupal\Core\Utility\Error;
 use Drupal\Component\Uuid\Uuid;
 use Drupal\Component\Utility\NestedArray;
@@ -81,7 +82,7 @@ function update_settings_file_requirements() {
   $requirements = array();
 
   // Check whether settings.php needs to be rewritten.
-  $settings_file = conf_path() . '/settings.php';
+  $settings_file = Site::getPath('settings.php');
   $writable = drupal_verify_install_file($settings_file, FILE_EXIST | FILE_READABLE | FILE_WRITABLE);
   $requirements['settings file']['title'] = 'Settings file';
   if ($writable) {
diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php
index 97eab54..995f0e6 100644
--- a/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Database\Driver\sqlite\Connection;
 use Drupal\Core\Database\DatabaseNotFoundException;
 use Drupal\Core\Database\Install\Tasks as InstallTasks;
+use Drupal\Core\Site\Site;
 
 /**
  * Specifies installation tasks for SQLite databases.
@@ -49,7 +50,7 @@ public function getFormOptions(array $database) {
     // Make the text more accurate for SQLite.
     $form['database']['#title'] = t('Database file');
     $form['database']['#description'] = t('The absolute path to the file where @drupal data will be stored. This must be writable by the web server and should exist outside of the web root.', array('@drupal' => drupal_install_profile_distribution_name()));
-    $default_database = conf_path(FALSE) . '/files/.ht.sqlite';
+    $default_database = Site::getPath('files/.ht.sqlite');
     $form['database']['#default_value'] = empty($database['database']) ? $default_database : $database['database'];
     return $form;
   }
diff --git a/core/lib/Drupal/Core/Site/Site.php b/core/lib/Drupal/Core/Site/Site.php
new file mode 100644
index 0000000..572400e
--- /dev/null
+++ b/core/lib/Drupal/Core/Site/Site.php
@@ -0,0 +1,341 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Site\Site.
+ */
+
+namespace Drupal\Core\Site;
+
+/**
+ * A utility class for easy access to the site path.
+ */
+class Site {
+
+  /**
+   * The absolute path to the Drupal root directory.
+   *
+   * @var string
+   */
+  private $root;
+
+  /**
+   * The relative path to the site directory.
+   *
+   * May be an empty string, in case the site directory is the root directory.
+   *
+   * @var string
+   */
+  private $path;
+
+  /**
+   * Whether the Site singleton was instantiated by the installer.
+   *
+   * @var bool
+   */
+  private $isInstaller;
+
+  /**
+   * The original Site instance of the test runner during test execution.
+   *
+   * @see \Drupal\Core\Site\Site::setUpTest()
+   * @see \Drupal\Core\Site\Site::tearDownTest()
+   *
+   * @var \Drupal\Core\Site\Site
+   */
+  private static $original;
+
+  /**
+   * The Site singleton instance.
+   *
+   * @var \Drupal\Core\Site\Site
+   */
+  private static $instance;
+
+  /**
+   * Initializes the Site singleton.
+   *
+   * @param string $root_directory
+   *   The root directory to use for absolute paths; i.e., DRUPAL_ROOT.
+   * @param array $sites
+   *   (optional) A multi-site mapping, as defined in settings.php.
+   * @param string $custom_path
+   *   (optional) An explicit site path to set; skipping site negotiation.
+   *   This can be defined as $conf_path in the root /settings.php file.
+   *
+   * @see drupal_settings_initialize()
+   */
+  public static function init($root_directory, array $sites = NULL, $custom_path = NULL) {
+    // Only the installer environment is allowed instantiate the Site singleton
+    // prior to drupal_settings_initialize().
+    // @see initInstaller()
+    if (isset(self::$instance)) {
+      if (!self::$instance->isInstaller()) {
+        throw new \BadMethodCallException('Site path is initialized already.');
+      }
+      else {
+        // Disable the $isInstaller flag to prevent init() from being invoked
+        // more than once.
+        self::$instance->isInstaller = FALSE;
+      }
+    }
+    else {
+      new self($root_directory);
+    }
+    self::$instance->initializePath($sites, $custom_path);
+  }
+
+  /**
+   * Initializes the Site singleton for the early installer environment.
+   *
+   * The installer uses this function to prime the site directory path very
+   * early in the installer environmnt. This allows the application to be
+   * installed into a new and empty site directory, which does not contain a
+   * settings.php yet.
+   *
+   * @param string $root_directory
+   *   The root directory to use for absolute paths; i.e., DRUPAL_ROOT.
+   *
+   * @see install_begin_request()
+   */
+  public static function initInstaller($root_directory) {
+    if (isset(self::$instance)) {
+      throw new \BadMethodCallException('Site path is initialized already.');
+    }
+    // Set a global state flag to denote that we are operating in the special
+    // installer environment.
+    new self($root_directory, TRUE);
+    self::$instance->initializePath();
+  }
+
+  /**
+   * Constructs the Site singleton.
+   */
+  private function __construct($root_directory, $is_installer = FALSE) {
+    if (isset(self::$instance)) {
+      throw new \BadMethodCallException('Site path is initialized already.');
+    }
+    $this->root = $root_directory;
+    $this->isInstaller = $is_installer;
+    self::$instance = $this;
+  }
+
+  /**
+   * Re-initializes (resets) the Site singleton for a test run.
+   *
+   * @see \Drupal\simpletest\TestBase::prepareEnvironment()
+   */
+  public static function setUpTest() {
+    if (!isset(self::$instance)) {
+      throw new \RuntimeException('No original Site to backup. Missing invocation of Site::init()?');
+    }
+    if (!drupal_valid_test_ua()) {
+      throw new \BadMethodCallException('Site is not executing a test.');
+    }
+    self::$original = clone self::$instance;
+    self::$instance = NULL;
+  }
+
+  /**
+   * Reverts the Site singleton to the original after a test run.
+   *
+   * @see \Drupal\simpletest\TestBase::restoreEnvironment()
+   */
+  public static function tearDownTest() {
+    if (!isset(self::$original)) {
+      throw new \RuntimeException('No original Site to revert to. Missing invocation of Site::setUpTest()?');
+    }
+    // Do not allow to restore original Site singleton in a test environment,
+    // unless we are testing the test environment setup and teardown itself.
+    // @see \Drupal\simpletest\Tests\BrokenSetUpTest
+    if (drupal_valid_test_ua() && !DRUPAL_TEST_IN_CHILD_SITE) {
+      throw new \BadMethodCallException('Unable to revert Site: A test is still being executed.');
+    }
+    self::$instance = clone self::$original;
+    self::$original = NULL;
+  }
+
+  /**
+   * Returns whether the Site singleton was instantiated for the installer.
+   *
+   * @todo Leverage this to eliminate drupal_installation_attempted()?
+   */
+  private function isInstaller() {
+    return $this->isInstaller;
+  }
+
+  /**
+   * Initializes the site path.
+   *
+   * @param array $sites
+   *   (optional) A multi-site mapping, as defined in settings.php.
+   * @param string $custom_path
+   *   (optional) An explicit site path to set; skipping site negotiation.
+   */
+  private function initializePath(array $sites = NULL, $custom_path = NULL) {
+    // Force-override the site directory in tests.
+    if ($test_prefix = drupal_valid_test_ua()) {
+      $custom_path = 'sites/simpletest/' . substr($test_prefix, 10);
+    }
+
+    // An explicitly defined $conf_path in /settings.php takes precedence.
+    if (isset($custom_path)) {
+      $this->path = $custom_path;
+    }
+    // If the multi-site functionality was enabled in /settings.php, discover
+    // the path for the current site.
+    // $sites just needs to be defined; an explicit mapping is not required.
+    elseif (isset($sites)) {
+      $this->path = $this->determinePath($sites, !$this->isInstaller());
+    }
+    // If the multi-site functionality is not enabled, the Drupal root
+    // directory is the site directory.
+    else {
+      $this->path = '';
+    }
+  }
+
+  /**
+   * Finds the appropriate configuration directory for a given host and path.
+   *
+   * Finds a matching configuration directory file by stripping the website's
+   * hostname from left to right and pathname from right to left. By default,
+   * the directory must contain a 'settings.php' file for it to match. If the
+   * parameter $require_settings is set to FALSE, then a directory without a
+   * 'settings.php' file will match as well. The first configuration
+   * file found will be used and the remaining ones will be ignored.
+   *
+   * The settings.php file can define aliases in an associative array named
+   * $sites. For example, to create a directory alias for
+   * http://www.drupal.org:8080/mysite/test whose configuration file is in
+   * sites/example.com, the array should be defined as:
+   * @code
+   * $sites = array(
+   *   '8080.www.drupal.org.mysite.test' => 'example.com',
+   * );
+   * @endcode
+   *
+   * @see default.settings.php
+   *
+   * @param array $sites
+   *   A multi-site mapping, as defined in settings.php.
+   * @param bool $require_settings
+   *   Only configuration directories with an existing settings.php file
+   *   will be recognized. Defaults to TRUE. During initial installation,
+   *   this is set to FALSE so that Drupal can detect a matching directory,
+   *   then create a new settings.php file in it.
+   *
+   * @return string
+   *   The path of the matching configuration directory. May be an empty string,
+   *   in case the site configuration directory is the root directory.
+   *
+   * @todo Inject a Request object in instead of relying on globals?
+   */
+  private function determinePath(array $sites, $require_settings) {
+    // The hostname and optional port number, e.g. "www.example.com" or
+    // "www.example.com:8080".
+    $http_host = $_SERVER['HTTP_HOST'];
+    // The part of the URL following the hostname, including the leading slash.
+    $script_name = $_SERVER['SCRIPT_NAME'] ?: $_SERVER['SCRIPT_FILENAME'];
+
+    $uri = explode('/', $script_name);
+    $server = explode('.', implode('.', array_reverse(explode(':', rtrim($http_host, '.')))));
+    for ($i = count($uri) - 1; $i > 0; $i--) {
+      for ($j = count($server); $j > 0; $j--) {
+        $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
+        // Check for an alias in $sites.
+        if (isset($sites[$dir])) {
+          $dir = $sites[$dir];
+          // A defined site alias from /settings.php should be valid.
+          // @todo Even skip the settings.php check?
+          if (!$require_settings) {
+            return "sites/$dir";
+          }
+        }
+        if ($require_settings) {
+          if (file_exists($this->root . '/sites/' . $dir . '/settings.php')) {
+            return "sites/$dir";
+          }
+        }
+        elseif (file_exists($this->root . '/sites/' . $dir)) {
+          return "sites/$dir";
+        }
+      }
+    }
+    return '';
+  }
+
+  /**
+   * Prefixes a given filepath with the site directory, if any.
+   *
+   * Ensures that a given filepath does not result in an absolute filesystem
+   * path in case of a string concatenation like the following:
+   * @code
+   * // If $site_path is empty (Drupal's root directory), then the resulting
+   * // filesystem path would become absolute; e.g.: "/some/file"
+   * unlink($site_path . '/' . $some_file);
+   * @endcode
+   *
+   * @param string $filepath
+   *   The filepath to prefix.
+   *
+   * @return string
+   *   The prefixed filepath.
+   */
+  private function resolvePath($filepath) {
+    if ($filepath !== '' && $filepath[0] === '/') {
+      $filepath = substr($filepath, 1);
+    }
+    if ($this->path !== '') {
+      if ($filepath !== '') {
+        $filepath = $this->path . '/' . $filepath;
+      }
+      else {
+        $filepath = $this->path;
+      }
+    }
+    return $filepath;
+  }
+
+  /**
+   * Returns a given path as relative path to the site directory.
+   *
+   * Use this function instead of appending strings to the site path manually,
+   * because the site directory may be the root directory and thus the resulting
+   * path would be an absolute filesystem path.
+   *
+   * @param string $filepath
+   *   (optional) A relative filepath to append to the site path.
+   *
+   * @return string
+   *   The given $filepath, potentially prefixed with the site path.
+   *
+   * @see \Drupal\Core\Site\Site::getAbsolutePath()
+   */
+  public static function getPath($filepath = '') {
+    return self::$instance->resolvePath($filepath);
+  }
+
+  /**
+   * Returns a given path as absolute path in the site directory.
+   *
+   * @param string $filepath
+   *   (optional) A relative filepath to append to the site path.
+   *
+   * @return string
+   *   The given $filepath, potentially prefixed with the site path, as an
+   *   absolute filesystem path.
+   *
+   * @see \Drupal\Core\Site\Site::getPath()
+   */
+  public static function getAbsolutePath($filepath = '') {
+    $filepath = self::$instance->resolvePath($filepath);
+    if ($filepath !== '') {
+      return self::$instance->root . '/' . $filepath;
+    }
+    else {
+      return self::$instance->root;
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php
index 760a1f8..a2239ff 100644
--- a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php
+++ b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\Core\StreamWrapper;
 
+use Drupal\Core\Site\Site;
+
 /**
  * Defines a Drupal public (public://) stream wrapper class.
  *
@@ -37,7 +39,7 @@ public function getExternalUrl() {
    *   The base path for public:// typically sites/default/files.
    */
   public static function basePath() {
-    $base_path = settings()->get('file_public_path', conf_path() . '/files');
+    $base_path = settings()->get('file_public_path', Site::getPath('files'));
     return $base_path;
   }
 
diff --git a/core/lib/Drupal/Core/SystemListing.php b/core/lib/Drupal/Core/SystemListing.php
index 0a82cd2..e9aee89 100644
--- a/core/lib/Drupal/Core/SystemListing.php
+++ b/core/lib/Drupal/Core/SystemListing.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core;
 
 use Drupal\Component\Utility\Settings;
+use Drupal\Core\Site\Site;
 
 /**
  * Returns information about system object files (modules, themes, etc.).
@@ -87,7 +88,6 @@ function scan($mask, $directory, $key = 'name') {
     if (!in_array($key, array('uri', 'filename', 'name'))) {
       $key = 'uri';
     }
-    $config = conf_path();
 
     // Search for the directory in core.
     $searchdir = array('core/' . $directory);
@@ -112,9 +112,13 @@ function scan($mask, $directory, $key = 'name') {
     if ($parent_site = Settings::getSingleton()->get('test_parent_site')) {
       $searchdir[] = $parent_site;
     }
-    if (file_exists("$config/$directory")) {
-      $searchdir[] = "$config/$directory";
+
+    if (Site::getPath() !== '') {
+      if (file_exists(Site::getPath($directory))) {
+        $searchdir[] = Site::getPath($directory);
+      }
     }
+
     // @todo Find a way to skip ./config directories (but not modules/config).
     $nomask = '/^(CVS|lib|templates|css|js)$/';
     $files = array();
diff --git a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php b/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php
index d230bc5..8617566 100644
--- a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php
+++ b/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\file_test;
 
+use Drupal\Core\Site\Site;
 use Drupal\Core\StreamWrapper\LocalReadOnlyStream;
 
 /**
@@ -16,7 +17,7 @@
  */
 class DummyReadOnlyStreamWrapper extends LocalReadOnlyStream {
   function getDirectoryPath() {
-    return conf_path() . '/files';
+    return Site::getPath('files');
   }
 
   /**
diff --git a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyStreamWrapper.php b/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyStreamWrapper.php
index cbea40f..dd919c8 100644
--- a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyStreamWrapper.php
+++ b/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyStreamWrapper.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\file_test;
 
+use Drupal\Core\Site\Site;
 use Drupal\Core\StreamWrapper\LocalStream;
 
 /**
@@ -16,7 +17,7 @@
  */
 class DummyStreamWrapper extends LocalStream {
   function getDirectoryPath() {
-    return conf_path() . '/files';
+    return Site::getPath('files');
   }
 
   /**
diff --git a/core/modules/locale/locale.install b/core/modules/locale/locale.install
index c62c842..665873f 100644
--- a/core/modules/locale/locale.install
+++ b/core/modules/locale/locale.install
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Language\Language;
+use Drupal\Core\Site\Site;
 use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
 
 /**
@@ -14,7 +15,7 @@
 function locale_install() {
   // Create the interface translations directory and ensure it's writable.
   if (!$directory = \Drupal::config('locale.settings')->get('translation.path')) {
-    $directory = conf_path() . '/files/translations';
+    $directory = Site::getPath('files/translations');
     \Drupal::config('locale.settings')->set('translation.path', $directory)->save();
   }
   file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index cf162fd..d55089b 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -18,6 +18,7 @@
 use Drupal\Core\Config\StorageInterface;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Site\Site;
 use Drupal\Core\StreamWrapper\PublicStream;
 use Drupal\Core\Utility\Error;
 use Symfony\Component\HttpFoundation\Request;
@@ -907,6 +908,10 @@ private function prepareDatabasePrefix() {
       $this->databasePrefix = 'simpletest' . $suffix;
     } while (is_dir(DRUPAL_ROOT . '/' . $this->siteDirectory));
 
+    // Immediately create the test directory to ensure that the above uniqueness
+    // check works.
+    file_prepare_directory($this->siteDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+
     // As soon as the database prefix is set, the test might start to execute.
     // All assertions as well as the SimpleTest batch operations are associated
     // with the testId, so the database prefix has to be associated with it.
@@ -982,7 +987,7 @@ private function prepareEnvironment() {
     }
 
     // Backup current in-memory configuration.
-    $this->originalSite = conf_path();
+    $this->originalSite = Site::getPath();
     $this->originalSettings = settings()->getAll();
     $this->originalConfig = $GLOBALS['config'];
 
@@ -998,7 +1003,7 @@ private function prepareEnvironment() {
     // Save further contextual information.
     // Use the original files directory to avoid nesting it within an existing
     // simpletest directory if a test is executed within a test.
-    $this->originalFileDirectory = settings()->get('file_public_path', conf_path() . '/files');
+    $this->originalFileDirectory = settings()->get('file_public_path', Site::getPath('files'));
     $this->originalProfile = drupal_get_profile();
     $this->originalUser = isset($user) ? clone $user : NULL;
 
@@ -1017,10 +1022,6 @@ private function prepareEnvironment() {
     $this->originalShutdownCallbacks = $callbacks;
     $callbacks = array();
 
-    // Create test directory ahead of installation so fatal errors and debug
-    // information can be logged during installation process.
-    file_prepare_directory($this->siteDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
-
     // Prepare filesystem directory paths.
     $this->public_files_directory = $this->siteDirectory . '/files';
     $this->private_files_directory = $this->siteDirectory . '/private';
@@ -1091,8 +1092,11 @@ private function prepareEnvironment() {
 
     // After preparing the environment and changing the database prefix, we are
     // in a valid test environment.
+    if (!is_dir(DRUPAL_ROOT . '/' . $this->siteDirectory)) {
+      throw new \RuntimeException("Test site directory '$this->siteDirectory' does not exist.");
+    }
     drupal_valid_test_ua($this->databasePrefix);
-    conf_path(FALSE, TRUE);
+    Site::setUpTest();
 
     drupal_set_time_limit($this->timeLimit);
   }
@@ -1223,7 +1227,7 @@ private function restoreEnvironment() {
     else {
       drupal_valid_test_ua(FALSE);
     }
-    conf_path(TRUE, TRUE);
+    Site::tearDownTest();
 
     // Restore original shutdown callbacks.
     $callbacks = &drupal_register_shutdown_function();
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php
index 643f652..6aa1ad8 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Database\Database;
 use Drupal\Core\Database\ConnectionNotDefinedException;
+use Drupal\Core\Site\Site;
 
 /**
  * Base test case class for unit tests.
@@ -36,6 +37,9 @@ function __construct($test_id = NULL) {
    * setUp() method.
    */
   protected function setUp() {
+    // Initialize the test Site singleton, so that Site::getPath() works.
+    Site::init(DRUPAL_ROOT);
+
     file_prepare_directory($this->public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
     $this->settingsSet('file_public_path', $this->public_files_directory);
   }
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index af66791..17b6fa6 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\Settings;
 use Drupal\Component\Utility\String;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Database\Database;
@@ -774,7 +775,7 @@ protected function setUp() {
     // Copy and prepare an actual settings.php, so as to resemble a regular
     // installation.
     // Not using File API; a potential error must trigger a PHP warning.
-    copy(DRUPAL_ROOT . '/sites/default/default.settings.php', DRUPAL_ROOT . '/' . $this->siteDirectory . '/settings.php');
+    copy(DRUPAL_ROOT . '/core/default.settings.php', DRUPAL_ROOT . '/' . $this->siteDirectory . '/settings.php');
 
     // All file system paths are created by System module during installation.
     // @see system_requirements()
@@ -793,7 +794,7 @@ protected function setUp() {
     );
     // Add the parent profile's search path to the child site's search paths.
     // @see drupal_system_listing()
-    $settings['conf']['simpletest.settings']['parent_profile'] = (object) array(
+    $settings['config']['simpletest.settings']['parent_profile'] = (object) array(
       'value' => $this->originalProfile,
       'required' => TRUE,
     );
@@ -802,13 +803,16 @@ protected function setUp() {
     // Since Drupal is bootstrapped already, install_begin_request() will not
     // bootstrap into DRUPAL_BOOTSTRAP_CONFIGURATION (again). Hence, we have to
     // reload the newly written custom settings.php manually.
-    drupal_settings_initialize();
+    require DRUPAL_ROOT . '/' . $this->siteDirectory . '/settings.php';
+    new Settings($settings);
+    $GLOBALS['config'] = $config;
 
     // Execute the non-interactive installer.
     require_once DRUPAL_ROOT . '/core/includes/install.core.inc';
     install_drupal($parameters);
 
     // Import new settings.php written by the installer.
+    // @todo Fix non-interactive installer; this should happen automatically.
     drupal_settings_initialize();
     foreach ($GLOBALS['config_directories'] as $type => $path) {
       $this->configDirectories[$type] = $path;
diff --git a/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php
index 26b1b76..91908fc 100644
--- a/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\system\Tests\DrupalKernel;
 
 use Drupal\Core\DrupalKernel;
+use Drupal\Core\Site\Site;
 use Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage;
 use Drupal\Component\PhpStorage\FileReadOnlyStorage;
 use Drupal\simpletest\DrupalUnitTestBase;
@@ -26,6 +27,9 @@ public static function getInfo() {
   }
 
   function setUp() {
+    // Initialize the test Site singleton, so that Site::getPath() works.
+    Site::init(DRUPAL_ROOT);
+
     // DrupalKernel relies on global $config_directories and requires those
     // directories to exist. Therefore, create the directories, but do not
     // invoke DrupalUnitTestBase::setUp(), since that would set up further
diff --git a/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php b/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php
index 8c5634e..de5a165 100644
--- a/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\system\Tests\File;
 
+use Drupal\Core\Site\Site;
+
 /**
  * Directory related tests.
  */
@@ -23,7 +25,7 @@ public static function getInfo() {
    * Test local directory handling functions.
    */
   function testFileCheckLocalDirectoryHandling() {
-    $directory = conf_path() . '/files';
+    $directory = Site::getPath('files');
 
     // Check a new recursively created local directory for correct file system
     // permissions.
diff --git a/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php b/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php
index e02cd07..e62cf1a 100644
--- a/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\system\Tests\File;
 
+use Drupal\Core\Site\Site;
+
 /**
  * Tests that files can not be written using ReadOnlyStreamWrapper functions.
  */
@@ -40,7 +42,7 @@ public static function getInfo() {
   function testWriteFunctions() {
     // Generate a test file
     $filename = $this->randomName();
-    $filepath = conf_path() . '/files/' . $filename;
+    $filepath = Site::getPath('files/' . $filename);
     file_put_contents($filepath, $filename);
 
     // Generate a read-only stream wrapper instance
@@ -83,7 +85,7 @@ function testWriteFunctions() {
 
     // Test the mkdir() function by attempting to create a directory.
     $dirname = $this->randomName();
-    $dir = conf_path() . '/files/' . $dirname;
+    $dir = Site::getPath('files/' . $dirname);
     $readonlydir = $this->scheme . '://' . $dirname;
     $this->assertFalse(@drupal_mkdir($readonlydir, 0775, 0), 'Unable to create directory with read-only stream wrapper.');
     // Create a temporary directory for testing purposes
diff --git a/core/modules/system/lib/Drupal/system/Tests/System/SettingsRewriteTest.php b/core/modules/system/lib/Drupal/system/Tests/System/SettingsRewriteTest.php
index 74c080d..6096a41 100644
--- a/core/modules/system/lib/Drupal/system/Tests/System/SettingsRewriteTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/System/SettingsRewriteTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\System;
 
+use Drupal\Core\Site\Site;
 use Drupal\simpletest\UnitTestBase;
 
 /**
@@ -104,7 +105,7 @@ function testDrupalRewriteSettings() {
       ),
     );
     foreach ($tests as $test) {
-      $filename = settings()->get('file_public_path', conf_path() . '/files') . '/mock_settings.php';
+      $filename = settings()->get('file_public_path', Site::getPath('files')) . '/mock_settings.php';
       file_put_contents(DRUPAL_ROOT . '/' . $filename, "<?php\n" . $test['original'] . "\n");
       drupal_rewrite_settings($test['settings'], $filename);
       $this->assertEqual(file_get_contents(DRUPAL_ROOT . '/' . $filename), "<?php\n" . $test['expected'] . "\n");
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 5e32a6b..978a46a 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -9,6 +9,7 @@
 use Drupal\Core\Database\Database;
 use Drupal\Core\Language\Language;
 use Drupal\Core\StreamWrapper\PublicStream;
+use Drupal\Core\Site\Site;
 
 /**
  * Implements hook_requirements().
@@ -229,14 +230,17 @@ function system_requirements($phase) {
   // Test configuration files and directory for writability.
   if ($phase == 'runtime') {
     $conf_errors = array();
-    $conf_path = conf_path();
-    if (!drupal_verify_install_file($conf_path, FILE_NOT_WRITABLE, 'dir')) {
-      $conf_errors[] = t("The directory %file is not protected from modifications and poses a security risk. You must change the directory's permissions to be non-writable.", array('%file' => $conf_path));
+    if (!drupal_verify_install_file(Site::getAbsolutePath(), FILE_NOT_WRITABLE, 'dir')) {
+      $conf_errors[] = t("The directory %file is not protected from modifications and poses a security risk. You must change the directory's permissions to be non-writable.", array(
+        '%file' => Site::getPath(),
+      ));
     }
     foreach (array('settings.php', 'settings.local.php') as $conf_file) {
-      $full_path = $conf_path . '/' . $conf_file;
+      $full_path = Site::getAbsolutePath($conf_file);
       if (file_exists($full_path) && !drupal_verify_install_file($full_path, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE)) {
-        $conf_errors[] = t("The file %file is not protected from modifications and poses a security risk. You must change the file's permissions to be non-writable.", array('%file' => $full_path));
+        $conf_errors[] = t("The file %file is not protected from modifications and poses a security risk. You must change the file's permissions to be non-writable.", array(
+          '%file' => Site::getPath($conf_file),
+        ));
       }
     }
     if (!empty($conf_errors)) {
@@ -327,7 +331,7 @@ function system_requirements($phase) {
     else {
       // If we are installing Drupal, the settings.php file might not exist yet
       // in the intended site directory, so don't require it.
-      $directories[] = conf_path(FALSE) . '/files';
+      $directories[] = Site::getPath('files');
     }
     if (!empty($GLOBALS['config']['system.file']['path']['private'])) {
       $directories[] = $GLOBALS['config']['system.file']['path']['private'];
@@ -353,7 +357,7 @@ function system_requirements($phase) {
     $requirements['config directories'] = array(
       'title' => t('Configuration directories'),
       'value' => t('Not present'),
-      'description' => t('Your %file file must define the $config_directories variable as an array containing the name of a directories in which configuration files can be written.', array('%file' => conf_path() . '/settings.php')),
+      'description' => t('Your %file file must define the $config_directories variable as an array containing the name of a directories in which configuration files can be written.', array('%file' => Site::getPath('settings.php'))),
       'severity' => REQUIREMENT_ERROR,
     );
   }
diff --git a/core/modules/update/update.manager.inc b/core/modules/update/update.manager.inc
index 19c0dc1..7fa9194 100644
--- a/core/modules/update/update.manager.inc
+++ b/core/modules/update/update.manager.inc
@@ -38,6 +38,7 @@
 
 use Drupal\Core\Updater\Updater;
 use Drupal\Core\FileTransfer\Local;
+use Drupal\Core\Site\Site;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 
 /**
@@ -470,7 +471,7 @@ function update_manager_update_ready_form_submit($form, &$form_state) {
     // trying to install the code, there's no need to prompt for FTP/SSH
     // credentials. Instead, we instantiate a Drupal\Core\FileTransfer\Local and
     // invoke update_authorize_run_update() directly.
-    if (fileowner($project_real_location) == fileowner(conf_path())) {
+    if (fileowner($project_real_location) == fileowner(Site::getAbsolutePath())) {
       module_load_include('inc', 'update', 'update.authorize');
       $filetransfer = new Local(DRUPAL_ROOT);
       update_authorize_run_update($filetransfer, $updates);
@@ -748,7 +749,7 @@ function update_manager_install_form_submit($form, &$form_state) {
   // trying to install the code, there's no need to prompt for FTP/SSH
   // credentials. Instead, we instantiate a Drupal\Core\FileTransfer\Local and
   // invoke update_authorize_run_install() directly.
-  if (fileowner($project_real_location) == fileowner(conf_path())) {
+  if (fileowner($project_real_location) == fileowner(Site::getAbsolutePath())) {
     module_load_include('inc', 'update', 'update.authorize');
     $filetransfer = new Local(DRUPAL_ROOT);
     call_user_func_array('update_authorize_run_install', array_merge(array($filetransfer), $arguments));
@@ -947,7 +948,7 @@ function update_manager_local_transfers_allowed() {
   // the configuration directory to determine if local transfers will be
   // allowed.
   $temporary_file = drupal_tempnam('temporary://', 'update_');
-  $local_transfers_allowed = fileowner($temporary_file) === fileowner(conf_path());
+  $local_transfers_allowed = fileowner($temporary_file) === fileowner(Site::getAbsolutePath());
 
   // Clean up. If this fails, we can ignore it (since this is just a temporary
   // file anyway).
diff --git a/sites/example.sites.php b/sites/example.sites.php
deleted file mode 100644
index 2a813dd..0000000
--- a/sites/example.sites.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-/**
- * @file
- * Configuration file for multi-site support and directory aliasing feature.
- *
- * This file is required for multi-site support and also allows you to define a
- * set of aliases that map hostnames, ports, and pathnames to configuration
- * directories in the sites directory. These aliases are loaded prior to
- * scanning for directories, and they are exempt from the normal discovery
- * rules. See default.settings.php to view how Drupal discovers the
- * configuration directory when no alias is found.
- *
- * Aliases are useful on development servers, where the domain name may not be
- * the same as the domain of the live server. Since Drupal stores file paths in
- * the database (files, system table, etc.) this will ensure the paths are
- * correct when the site is deployed to a live server.
- *
- * To activate this feature, copy and rename it such that its path plus
- * filename is 'sites/sites.php'.
- *
- * Aliases are defined in an associative array named $sites. The array is
- * written in the format: '<port>.<domain>.<path>' => 'directory'. As an
- * example, to map http://www.drupal.org:8080/mysite/test to the configuration
- * directory sites/example.com, the array should be defined as:
- * @code
- * $sites = array(
- *   '8080.www.drupal.org.mysite.test' => 'example.com',
- * );
- * @endcode
- * The URL, http://www.drupal.org:8080/mysite/test/, could be a symbolic link or
- * an Apache Alias directive that points to the Drupal root containing
- * index.php. An alias could also be created for a subdomain. See the
- * @link http://drupal.org/documentation/install online Drupal installation guide @endlink
- * for more information on setting up domains, subdomains, and subdirectories.
- *
- * The following examples look for a site configuration in sites/example.com:
- * @code
- * URL: http://dev.drupal.org
- * $sites['dev.drupal.org'] = 'example.com';
- *
- * URL: http://localhost/example
- * $sites['localhost.example'] = 'example.com';
- *
- * URL: http://localhost:8080/example
- * $sites['8080.localhost.example'] = 'example.com';
- *
- * URL: http://www.drupal.org:8080/mysite/test/
- * $sites['8080.www.drupal.org.mysite.test'] = 'example.com';
- * @endcode
- *
- * @see default.settings.php
- * @see conf_path()
- * @see http://drupal.org/documentation/install/multi-site
- */
