diff --git a/core/lib/Drupal/Core/Updater/Module.php b/core/lib/Drupal/Core/Updater/Module.php
index acc3028..078f991 100644
--- a/core/lib/Drupal/Core/Updater/Module.php
+++ b/core/lib/Drupal/Core/Updater/Module.php
@@ -31,6 +31,14 @@ class Module extends Updater implements UpdaterInterface {
   public function getInstallDirectory() {
     if ($relative_path = drupal_get_path('module', $this->name)) {
       $relative_path = dirname($relative_path);
+      $project_name = $this->getProjectName($relative_path);
+      do {
+        // Some projects keep their .info file in a subdirectory of their root
+        // directory, e.g., modules/MODULE/MODULE/MODULE.info. To find
+        // the appropriate directory to install the module in, we repeatedly
+        // ascend to the parent directory until the project name differs.
+          $relative_path = dirname($relative_path);
+      } while ($this->getProjectName($relative_path) == $project_name);
     }
     else {
       $relative_path = 'modules';
diff --git a/core/lib/Drupal/Core/Updater/Theme.php b/core/lib/Drupal/Core/Updater/Theme.php
index 4bd716a..58ff830 100644
--- a/core/lib/Drupal/Core/Updater/Theme.php
+++ b/core/lib/Drupal/Core/Updater/Theme.php
@@ -31,6 +31,14 @@ class Theme extends Updater implements UpdaterInterface {
   public function getInstallDirectory() {
     if ($relative_path = drupal_get_path('theme', $this->name)) {
       $relative_path = dirname($relative_path);
+      $project_name = $this->getProjectName($relative_path);
+      do {
+        // Some projects keep their .info file in a subdirectory of their root
+        // directory, e.g., themes/THEME/THEME/THEME.info. To find
+        // the appropriate directory to install the theme in, we repeatedly
+        // ascend to the parent directory until the project name differs.
+          $relative_path = dirname($relative_path);
+      } while ($this->getProjectName($relative_path) == $project_name);
     }
     else {
       $relative_path = 'themes';
diff --git a/core/modules/update/lib/Drupal/update/Tests/UpdateContribTest.php b/core/modules/update/lib/Drupal/update/Tests/UpdateContribTest.php
index a184092..7962e5e 100644
--- a/core/modules/update/lib/Drupal/update/Tests/UpdateContribTest.php
+++ b/core/modules/update/lib/Drupal/update/Tests/UpdateContribTest.php
@@ -19,7 +19,7 @@ class UpdateContribTest extends UpdateTestBase {
    *
    * @var array
    */
-  public static $modules = array('update_test', 'update', 'aaa_update_test', 'bbb_update_test', 'ccc_update_test');
+  public static $modules = array('update_test', 'update', 'aaa_update_test', 'bbb_update_test', 'ccc_update_test', 'ddd_update_test', 'ddd_ui_update_test');
 
   public static function getInfo() {
     return array(
@@ -135,6 +135,20 @@ function testUpdateContribOrder() {
         'version' => '8.x-1.0',
         'hidden' => FALSE,
       ),
+
+      // This should be its own project, and listed third on the report.
+      'ddd_update_test' => array(
+        'project' => 'ddd_update_test',
+        'version' => '8.x-1.0',
+        'hidden' => FALSE,
+      ),
+
+      // This should be part of the "DDD Update test" project
+      'ddd_ui_update_test' => array(
+        'project' => 'ddd_update_test',
+        'version' => '8.x-1.0',
+        'hidden' => FALSE,
+      ),
     );
     \Drupal::config('update_test.settings')->set('system_info', $system_info)->save();
     $this->refreshUpdateStatus(array('drupal' => '0', '#all' => '1_0'));
@@ -142,17 +156,21 @@ function testUpdateContribOrder() {
     // We're expecting the report to say all projects are up to date.
     $this->assertText(t('Up to date'));
     $this->assertNoText(t('Update available'));
-    // We want to see all 3 module names listed, since they'll show up either
+    // We want to see all 5 module names listed, since they'll show up either
     // as project names or as modules under the "Includes" listing.
     $this->assertText(t('AAA Update test'));
     $this->assertText(t('BBB Update test'));
     $this->assertText(t('CCC Update test'));
+    $this->assertText(t('DDD Update test'));
+    $this->assertText(t('DDD UI Update test'));
     // We want aaa_update_test included in the ccc_update_test project, not as
     // its own project on the report.
     $this->assertNoRaw(l(t('AAA Update test'), 'http://example.com/project/aaa_update_test'), 'Link to aaa_update_test project does not appear.');
-    // The other two should be listed as projects.
+    $this->assertNoRaw(l(t('DDD UI Update test'), 'http://example.com/project/ddd_ui_update_test'), 'Link to ddd_ui_update_test project does not appear.');
+    // The other three should be listed as projects.
     $this->assertRaw(l(t('BBB Update test'), 'http://example.com/project/bbb_update_test'), 'Link to bbb_update_test project appears.');
     $this->assertRaw(l(t('CCC Update test'), 'http://example.com/project/ccc_update_test'), 'Link to bbb_update_test project appears.');
+    $this->assertRaw(l(t('DDD Update test'), 'http://example.com/project/ddd_update_test'), 'Link to ddd_update_test project appears.');
 
     // We want to make sure we see the BBB project before the CCC project.
     // Instead of just searching for 'BBB Update test' or something, we want
@@ -318,6 +336,16 @@ function testUpdateBrokenFetchURL() {
         'version' => '8.x-1.0',
         'hidden' => FALSE,
       ),
+      'ddd_update_test' => array(
+        'project' => 'ddd_update_test',
+        'version' => '8.x-1.0',
+        'hidden' => FALSE,
+      ),
+      'ddd_ui_update_test' => array(
+        'project' => 'ddd_update_test',
+        'version' => '8.x-1.0',
+        'hidden' => FALSE,
+      ),
     );
     \Drupal::config('update_test.settings')->set('system_info', $system_info)->save();
 
@@ -326,6 +354,8 @@ function testUpdateBrokenFetchURL() {
       'aaa_update_test' => '1_0',
       'bbb_update_test' => 'does-not-exist',
       'ccc_update_test' => '1_0',
+      'ddd_update_test' => '1_0',
+      'ddd_ui_update_test' => '1_0',
     );
     $this->refreshUpdateStatus($xml_mapping);
 
@@ -342,13 +372,14 @@ function testUpdateBrokenFetchURL() {
     $this->assertRaw('<div class="version-status">' . t('Failed to get available update data'));
 
     // We should see the output messages from fetching manually.
-    $this->assertUniqueText(t('Checked available update data for 3 projects.'));
+    $this->assertUniqueText(t('Checked available update data for 4 projects.'));
     $this->assertUniqueText(t('Failed to get available update data for one project.'));
 
-    // The other two should be listed as projects.
+    // The other three should be listed as projects.
     $this->assertRaw(l(t('AAA Update test'), 'http://example.com/project/aaa_update_test'), 'Link to aaa_update_test project appears.');
     $this->assertNoRaw(l(t('BBB Update test'), 'http://example.com/project/bbb_update_test'), 'Link to bbb_update_test project does not appear.');
     $this->assertRaw(l(t('CCC Update test'), 'http://example.com/project/ccc_update_test'), 'Link to bbb_update_test project appears.');
+    $this->assertRaw(l(t('DDD Update test'), 'http://example.com/project/ddd_update_test'), 'Link to ddd_update_test project appears.');
   }
 
   /**
