diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index 847bfc3..385965f 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -835,7 +835,10 @@ function drupal_settings_initialize() {
 function drupal_get_filename($type, $name, $filename = NULL) {
   // The location of files will not change during the request, so do not use
   // drupal_static().
-  static $files = array(), $dirs = array();
+  // The $files static variable will hold the locations of all requested files,
+  // and any file that gets added during the request will have been checked
+  // with a file_exists() at some point.
+  static $files = array();
 
   // Profiles are a special case: they have a fixed location and naming.
   if ($type == 'profile') {
@@ -847,68 +850,225 @@ function drupal_get_filename($type, $name, $filename = NULL) {
   }
 
   if (!empty($filename) && file_exists($filename)) {
+    // Prime the static cache with the provided filename.
     $files[$type][$name] = $filename;
   }
   elseif (isset($files[$type][$name])) {
-    // nothing
+    // This item had already been found earlier in the request, either in this
+    // same function or by the static cache having been primed elsewhere, such
+    // as in system_list(). Do nothing.
   }
-  // Verify that we have an active database connection, before querying
-  // the database. This is required because this function is called both
-  // before we have a database connection (i.e. during installation) and
-  // when a database connection fails.
   else {
-    try {
-      if (function_exists('db_query')) {
-        $file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField();
-        if ($file !== FALSE && file_exists(DRUPAL_ROOT . '/' . $file)) {
-          $files[$type][$name] = $file;
+    $system_filepaths = &drupal_static('system_filepaths');
+    if (!isset($system_filepaths[$type][$name])) {
+      // Look for the file name listed in the {system} table.
+      // Verify that we have an active database connection, before querying
+      // the database. This is required because this function is called both
+      // before we have a database connection (i.e. during installation) and
+      // when a database connection fails.
+      try {
+        if (function_exists('db_query')) {
+          $file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(
+            ':name' => $name,
+            ':type' => $type
+          ))->fetchField();
+          if ($file !== FALSE) {
+            $system_filepaths[$type][$name] = $file;
+            if (file_exists(DRUPAL_ROOT . '/' . $file)) {
+              $files[$type][$name] = $file;
+            }
+          }
         }
       }
-    }
-    catch (Exception $e) {
-      // The database table may not exist because Drupal is not yet installed,
-      // or the database might be down. We have a fallback for this case so we
-      // hide the error completely.
+      catch (Exception $e) {
+        // The database table may not exist because Drupal is not yet installed,
+        // or the database might be down. We have a fallback for this case so we
+        // hide the error completely.
+      }
     }
     // Fallback to searching the filesystem if the database could not find the
-    // file or the file returned by the database is not found.
+    // file or the filename returned by the database is not found.
     if (!isset($files[$type][$name])) {
-      // We have a consistent directory naming: modules, themes...
-      $dir = $type . 's';
-      if ($type == 'theme_engine') {
-        $dir = 'themes/engines';
-        $extension = 'engine';
-      }
-      elseif ($type == 'theme') {
-        $extension = 'info';
-      }
-      else {
-        $extension = $type;
-      }
+      $files[$type][$name] = _drupal_get_filename_fallback($type, $name);
+    }
+  }
 
-      if (!isset($dirs[$dir][$extension])) {
-        $dirs[$dir][$extension] = TRUE;
-        if (!function_exists('drupal_system_listing')) {
-          require_once DRUPAL_ROOT . '/includes/common.inc';
-        }
-        // Scan the appropriate directories for all files with the requested
-        // extension, not just the file we are currently looking for. This
-        // prevents unnecessary scans from being repeated when this function is
-        // called more than once in the same page request.
-        $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
-        foreach ($matches as $matched_name => $file) {
-          $files[$type][$matched_name] = $file->uri;
+  if (isset($files[$type][$name])) {
+    // We found a file.
+    // Clean up the item from the file scan cache, in case the item had been
+    // marked as missing or moved.
+    $file_scans = &drupal_static('_drupal_get_filename_fallback');
+    if (isset($file_scans[$type][$name])) {
+      $file_scans[$type][$name] = NULL;
+      $file_scans['#write_cache'] = TRUE;
+    }
+    return $files[$type][$name];
+  }
+}
+
+/**
+ * Helper function for drupal_get_filename().
+ *
+ * This triggers a file scan, caching any missing or moved files. If an item
+ * had been marked as missing or moved in a previous file scan, no new file
+ * scans will be performed.
+ *
+ * @param string $type
+ *   The type of the item (theme, theme_engine, module, profile).
+ * @param string $name
+ *   The name of the item for which the filename is requested.
+ *
+ * @return string
+ *   The filename of the requested item or NULL if the item is not found.
+ */
+function _drupal_get_filename_fallback($type, $name) {
+  // This static variable will hold all missing and moved files, in order
+  // to prevent unnecessary file scans. It is an associative array with as
+  // keys the file type and name, and as value the boolean FALSE if the
+  // module is missing, or a string with location found in a file scan if
+  // the module has moved from the location listed in the {system} table.
+  $file_scans = &drupal_static('_drupal_get_filename_fallback');
+  if (!isset($file_scans)) {
+    $file_scans = array();
+  }
+  // Get the missing and moved files from persistent cache, if available.
+  if (!isset($file_scans['#cache_merge_done'])) {
+    try {
+      if (function_exists('cache_get')) {
+        $cache = cache_get('_drupal_get_filename_fallback', 'cache_bootstrap');
+        if (!empty($cache->data)) {
+          // Merge the changes already done in the current request
+          // (including the setting of missing records to NULL) into the
+          // values saved in persistent cache.
+          $file_scans = drupal_array_merge_deep($cache->data, $file_scans);
+          // Set a flag so we know that we've already done a merge with
+          // the values in persistent cache.
+          $file_scans['#cache_merge_done'] = TRUE;
         }
       }
     }
+    catch (Exception $e) {
+      // Hide the error.
+    }
   }
 
+  // Check whether this file had previously moved from its location in the
+  // {system} table.
+  if (isset($file_scans[$type][$name]) && is_string($file_scans[$type][$name]) && file_exists($file_scans[$type][$name])) {
+    // We found the file at the cached location.
+    return $file_scans[$type][$name];
+  }
+
+  // Unless the module had been marked as missing, perform a file scan.
+  if (!(isset($file_scans[$type][$name]) && $file_scans[$type][$name] === FALSE)) {
+    $filename_from_file_scan = _drupal_get_filename_perform_file_scan($type, $name);
+  }
+  if (isset($filename_from_file_scan)) {
+    // Check whether the current file was listed as being in another
+    // location in the {system} table.
+    $system_filepaths = &drupal_static('system_filepaths');
+    if (isset($system_filepaths[$type][$name]) && $system_filepaths[$type][$name] != $filename_from_file_scan) {
+      // This file has moved. Cache its new location into the missing and
+      // moved files list.
+      $file_scans[$type][$name] = $filename_from_file_scan[$type][$name];
+      // Make sure our change to the missing and moved files list will be
+      // written to persistent cache.
+      $file_scans['#write_cache'] = TRUE;
+      trigger_error(format_string('The following %type has moved on the file system: %name. In order to fix this, clear caches or put the file back in its original location. For more information, see <a href="@documentation">the documentation page</a>.', array('%type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
+    }
+    return $filename_from_file_scan;
+  }
+  else {
+    trigger_error(format_string('The following %type is missing from the file system: %name. In order to fix this, put the file back in its original location or uninstall the module. For more information, see <a href="@documentation">the documentation page</a>.', array('%type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
+    // Make sure our change to the missing files list will be written to
+    // persistent cache.
+    if (!isset($file_scans[$type][$name])) {
+      $file_scans['#write_cache'] = TRUE;
+    }
+    // Mark the file as missing.
+    $file_scans[$type][$name] = FALSE;
+  }
+}
+
+/**
+ * Helper function for drupal_get_filename().
+ *
+ * Performs a file scan.
+ *
+ * @param $type
+ *   The type of the item (theme, theme_engine, module, profile).
+ * @param $name
+ *   The name of the item for which the filename is requested.
+ *
+ * @return
+ *   The filename of the requested item or NULL if the item is not found.
+ */
+function _drupal_get_filename_perform_file_scan($type, $name) {
+  static $dirs = array(), $files = array();
+
+  // We have a consistent directory naming: modules, themes...
+  $dir = $type . 's';
+  if ($type == 'theme_engine') {
+    $dir = 'themes/engines';
+    $extension = 'engine';
+  }
+  elseif ($type == 'theme') {
+    $extension = 'info';
+  }
+  else {
+    $extension = $type;
+  }
+
+  // Check if we had already scanned this directory/extension combination.
+  if (!isset($dirs[$dir][$extension])) {
+    // Log that we have now scanned this directory/extension combination
+    // into a static variable to prevent unnecessary scans.
+    $dirs[$dir][$extension] = TRUE;
+    if (!function_exists('drupal_system_listing')) {
+      require_once DRUPAL_ROOT . '/includes/common.inc';
+    }
+    // Scan the appropriate directories for all files with the requested
+    // extension, not just the file we are currently looking for. This
+    // prevents unnecessary scans from being repeated when this function is
+    // called more than once in the same page request.
+    $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
+    foreach ($matches as $matched_name => $file) {
+      // Log the locations found in the file scan into a static variable.
+      $files[$type][$matched_name] = $file->uri;
+    }
+  }
+
+  // If current file had been found in a file scan earlier on in this
+  // request, use the new location that had been found in the file scan.
   if (isset($files[$type][$name])) {
     return $files[$type][$name];
   }
 }
 
 /**
+ * Writes the missing and moved files to persistent cache.
+ */
+function drupal_file_scan_write_cache() {
+  // Only write to cache if we are fully bootstrapped.
+  if (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL) {
+    return;
+  }
+  $file_scans = &drupal_static('_drupal_get_filename_fallback');
+  if (isset($file_scans['#write_cache'])) {
+    // Merge the newly found out missing and moved file data with
+    // the previously existing data, if we hadn't done so yet.
+    if (!isset($file_scans['#cache_merge_done'])) {
+      $cache = cache_get('_drupal_get_filename_fallback', 'cache_bootstrap');
+      if (isset($cache->data)) {
+        $file_scans = drupal_array_merge_deep($cache->data, $file_scans);
+      }
+    }
+    $file_scans['#write_cache'] = NULL;
+    cache_set('_drupal_get_filename_fallback', $file_scans, 'cache_bootstrap');
+  }
+}
+
+/**
  * Loads the persistent variable table.
  *
  * The variable table is composed of values that have been saved in the table
diff --git a/includes/common.inc b/includes/common.inc
index cd30145..b323f5f 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -2751,6 +2751,7 @@ function drupal_page_footer() {
   _registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
   drupal_cache_system_paths();
   module_implements_write_cache();
+  drupal_file_scan_write_cache();
   system_run_automated_cron();
 }
 
diff --git a/includes/module.inc b/includes/module.inc
index 494924f..61c356d 100644
--- a/includes/module.inc
+++ b/includes/module.inc
@@ -121,6 +121,7 @@ function module_list($refresh = FALSE, $bootstrap_refresh = FALSE, $sort = FALSE
  */
 function system_list($type) {
   $lists = &drupal_static(__FUNCTION__);
+  $system_filepaths = &drupal_static('system_filepaths');
 
   // For bootstrap modules, attempt to fetch the list from cache if possible.
   // if not fetch only the required information to fire bootstrap hooks
@@ -208,9 +209,13 @@ function system_list($type) {
       }
       cache_set('system_list', $lists, 'cache_bootstrap');
     }
-    // To avoid a separate database lookup for the filepath, prime the
-    // drupal_get_filename() static cache with all enabled modules and themes.
     foreach ($lists['filepaths'] as $item) {
+      // Save the file paths from the database into a static variable so we
+      // can determine later if the files have moved.
+      $system_filepaths[$item['type']][$item['name']] = $item['filepath'];
+      // To avoid a separate database lookup for the filepath, prime the
+      // drupal_get_filename() static cache with all enabled modules and all
+      // themes.
       drupal_get_filename($item['type'], $item['name'], $item['filepath']);
     }
   }
@@ -227,6 +232,11 @@ function system_list_reset() {
   drupal_static_reset('list_themes');
   cache_clear_all('bootstrap_modules', 'cache_bootstrap');
   cache_clear_all('system_list', 'cache_bootstrap');
+
+  // Clean up the bootstrap file scan cache.
+  drupal_static_reset('_drupal_get_filename_fallback');
+  drupal_static_reset('system_filepaths');
+  cache_clear_all('_drupal_get_filename_fallback', 'cache_bootstrap');
 }
 
 /**
diff --git a/includes/update.inc b/includes/update.inc
index a17161c..c23519d 100644
--- a/includes/update.inc
+++ b/includes/update.inc
@@ -795,6 +795,14 @@ function update_fix_d7_requirements() {
 function update_fix_d7_install_profile() {
   $profile = drupal_get_profile();
 
+  // 'Default' profile has been renamed to 'Standard' in D7.
+  // We change the profile here to prevent a broken record in the system table.
+  // @see system_update_7049()
+  if ($profile == 'default') {
+    $profile = 'standard';
+    variable_set('install_profile', $profile);
+  }
+
   $results = db_select('system', 's')
     ->fields('s', array('name', 'schema_version'))
     ->condition('name', $profile)
diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module
index 91f0f90..97b32ed 100644
--- a/modules/simpletest/simpletest.module
+++ b/modules/simpletest/simpletest.module
@@ -371,7 +371,17 @@ function simpletest_test_get_all() {
           // If this test class requires a non-existing module, skip it.
           if (!empty($info['dependencies'])) {
             foreach ($info['dependencies'] as $module) {
-              if (!drupal_get_filename('module', $module)) {
+              // Searching for an item that does not exist triggers a PHP
+              // error. Set a custom error handler so we can ignore the file
+              // not found error.
+              set_error_handler('_simpletest_file_not_found_error_handler');
+
+              $found_module = drupal_get_filename('module', $module);
+
+              // Restore the original error handler.
+              restore_error_handler();
+
+              if (!$found_module) {
                 continue 2;
               }
             }
@@ -395,6 +405,16 @@ function simpletest_test_get_all() {
   return $groups;
 }
 
+/**
+ * Skips handling of "file not found" errors.
+ */
+function _simpletest_file_not_found_error_handler($error_level, $message, $filename, $line, $context) {
+   if (strstr($message, 'is missing from the file system:')) {
+     return;
+   }
+   _drupal_error_handler($error_level, $message, $filename, $line, $context);
+}
+
 /*
  * Register a simple class loader that can find D8-style PSR-0 test classes.
  *
diff --git a/modules/simpletest/tests/bootstrap.test b/modules/simpletest/tests/bootstrap.test
index ece1cd9..b9d619d 100644
--- a/modules/simpletest/tests/bootstrap.test
+++ b/modules/simpletest/tests/bootstrap.test
@@ -382,6 +382,15 @@ class BootstrapGetFilenameTestCase extends DrupalUnitTestCase {
   }
 
   /**
+   * Whether the filename test triggered the right error.
+   *
+   * Used by BootstrapGetFilenameTestCase::testDrupalGetFilename().
+   *
+   * @var boolean
+   */
+  protected $getFilenameTestTriggeredError = FALSE;
+
+  /**
    * Test that drupal_get_filename() works correctly when the file is not found in the database.
    */
   function testDrupalGetFilename() {
@@ -410,6 +419,39 @@ class BootstrapGetFilenameTestCase extends DrupalUnitTestCase {
     // automatically check there for 'script' files, just as it does for (e.g.)
     // 'module' files in modules.
     $this->assertIdentical(drupal_get_filename('script', 'test'), 'scripts/test.script', t('Retrieve test script location.'));
+
+    // Generate a non-existing module name.
+    $non_existing_module = $this->randomName();
+
+    // Searching for an item that does not exist returns NULL.
+    // Set a custom error handler so we can ignore the file not found error.
+    set_error_handler(array($this, 'fileNotFoundErrorHandler'));
+    $this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for an item that does not exist returns NULL.');
+    $this->assertTrue($this->getFilenameTestTriggeredError, 'Searching for an item that does not exist triggers an error.');
+    // Restore the original error handler.
+    restore_error_handler();
+
+    // Get the missing and moved files static variable.
+    $file_scans = &drupal_static('_drupal_get_filename_fallback');
+
+    // Searching for an item that does not exist creates a record in the static variable.
+    $this->assertTrue($file_scans['module'][$non_existing_module] === FALSE, 'Searching for an item that does not exist creates a record in the missing and moved files static variable.');
+    drupal_install_schema('system');
+    drupal_file_scan_write_cache();
+    $cache = cache_get('_drupal_get_filename_fallback', 'cache_bootstrap');
+    $this->assertTrue($cache->data['module'][$non_existing_module] === FALSE, 'File scan results are correctly saved in persistent cache.');
+  }
+
+  /**
+   * Skips handling of "file not found" errors.
+   */
+  public function fileNotFoundErrorHandler($error_level, $message, $filename, $line, $context) {
+    // Skip error handling if this is a "file not found" error.
+    if (strstr($message, 'is missing from the file system:')) {
+      $this->getFilenameTestTriggeredError = TRUE;
+      return;
+    }
+    _drupal_error_handler($error_level, $message, $filename, $line, $context);
   }
 }
 
