diff --git includes/file.inc includes/file.inc
index a768ded..3bd370f 100644
--- includes/file.inc
+++ includes/file.inc
@@ -1154,12 +1154,7 @@ function file_save_upload($source, $validators = array(), $destination = FALSE,
   }
 
   // Build the list of non-munged extensions.
-  // @todo: this should not be here. we need to figure out the right place.
-  $extensions = '';
-  foreach ($user->roles as $rid => $name) {
-    $extensions .= ' ' . variable_get("upload_extensions_$rid",
-    variable_get('upload_extensions_default', 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp'));
-  }
+  $extensions = file_permitted_extensions($user);
 
   // Begin building file object.
   $file = new stdClass();
diff --git modules/file/file.admin.inc modules/file/file.admin.inc
new file mode 100644
index 0000000..9afe0ad
--- /dev/null
+++ modules/file/file.admin.inc
@@ -0,0 +1,36 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Admin page callback file for the file module.
+ */
+
+/**
+ * Menu callback for the file settings form.
+ */
+function file_admin_settings() {
+  $upload_extensions_default = variable_get('upload_extensions_default', 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp');
+
+  $form['upload_extensions_default'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Default permitted file extensions'),
+    '#default_value' => $upload_extensions_default,
+    '#maxlength' => 255,
+    '#description' => t('Default extensions that users can upload. Separate extensions with a space and do not include the leading dot.'),
+  );
+
+  $roles = user_roles(FALSE, 'upload files');
+  foreach ($roles as $rid => $role) {
+    $form['upload_extensions_' . $rid] = array(
+      '#type' => 'textfield',
+      '#title' => t('Permitted file extensions for @role', array('@role' => $role)),
+      '#default_value' => variable_get('upload_extensions_' . $rid, $upload_extensions_default),
+      '#maxlength' => 255,
+      '#description' => t('Extensions that users in this role can upload. Separate extensions with a space and do not include the leading dot.'),
+    );
+  }
+
+  return system_settings_form($form, TRUE);
+}
+
diff --git modules/file/file.info modules/file/file.info
index 675c26b..8ada6d4 100644
--- modules/file/file.info
+++ modules/file/file.info
@@ -5,6 +5,7 @@ package = Core
 version = VERSION
 core = 7.x
 files[] = file.module
+files[] = file.admin.inc
 files[] = file.field.inc
 files[] = file.install
 files[] = tests/file.test
diff --git modules/file/file.module modules/file/file.module
index c8b1fd0..e284ac2 100644
--- modules/file/file.module
+++ modules/file/file.module
@@ -49,11 +49,52 @@ function file_menu() {
     'access arguments' => array('access content'),
     'type' => MENU_CALLBACK,
   );
+  $items['admin/config/media/uploads'] = array(
+    'title' => 'File uploads',
+    'description' => 'Control which files may be uploaded.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('file_admin_settings'),
+    'access arguments' => array('administer site configuration'),
+    'file' => 'file.admin.inc',
+  );
 
   return $items;
 }
 
 /**
+ * Implements hook_permission().
+ */
+function file_permission() {
+  return array(
+    'upload files' => array(
+      'title' => 'Upload files',
+      'restrict access' => TRUE,
+    ),
+  );
+}
+
+/**
+ * Determine the extensions that the user can upload.
+ *
+ * @param $account
+ *   The account to check.
+ * @return
+ *   A space-separated list of extensions.
+ */
+function file_permitted_extensions($account) {
+  $upload_extensions_default = variable_get('upload_extensions_default', 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp');
+
+  $upload_extensions = '';
+  foreach ($account->roles as $rid => $role) {
+    $upload_extensions .= ' ' . variable_get('upload_extensions_' . $rid, $upload_extensions_default);
+  }
+  if (user_access('administer software updates', $account)) {
+    $upload_extensions .= ' tar tgz tar.gz tar.bz2';
+  }
+  return $upload_extensions;
+}
+
+/**
  * Implements hook_element_info().
  *
  * The managed file element may be used independently anywhere in Drupal.
diff --git modules/simpletest/tests/filetransfer.test modules/simpletest/tests/filetransfer.test
index b9a79f7..aa1bbed 100644
--- modules/simpletest/tests/filetransfer.test
+++ modules/simpletest/tests/filetransfer.test
@@ -12,7 +12,7 @@ class FileTranferTest extends DrupalWebTestCase {
     return array(
       'name' => 'FileTransfer unit tests',
       'description' => 'Test that the jail is respected and that protocols using recursive file move operations work.',
-      'group' => 'System'
+      'group' => 'System',
     );
   }
 
@@ -89,6 +89,54 @@ class FileTranferTest extends DrupalWebTestCase {
   }
 }
 
+class FileTransferUploadTest extends DrupalWebTestCase {
+  function getInfo() {
+    return array(
+      'name' => 'FileTransfer upload tests',
+      'description' => 'Tests that the upload interface can install modules or themes.',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('update');
+    $this->admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer software updates', 'administer modules'));
+    $this->drupalLogin($this->admin_user);
+  }
+
+  function testTarballUpload() {
+    // Make a really small test module.
+    $info = "name = File transfer test\ncore = 7.x\nfiles[] = filetransfer_test.module";
+    $module = '<?php
+      function filetransfer_test_enable() {
+        variable_set("filetransfer_test_success", TRUE);
+      }
+    ';
+    $path = 'temporary://filetransfer_test/';
+
+    // Write the module to files.
+    drupal_mkdir($path);
+    $info_file = file_unmanaged_save_data($info, $path .'filetransfer_test.info', FILE_EXISTS_REPLACE);
+    $module_file = file_unmanaged_save_data($module, $path .'filetransfer_test.module', FILE_EXISTS_REPLACE);
+
+    // Roll a tarball of the module.
+    $tar = new Archive_Tar(file_directory_path('temporary') .'/filetransfer_test.tar.gz', 'gz');
+    $tar->createModify($path, '', 'temporary://');
+
+    // Attempt to upload the module.
+    $this->drupalPost('admin/reports/updates/install', array('files[project_upload]' => $tar->_tarname), t('Install'));
+    $this->clickLink(t('Enable newly added modules in !project', array('!project' => 'File transfer test')));
+    $this->drupalPost('admin/modules', array('modules[Other][filetransfer_test][enable]' => 1), t('Save configuration'));
+    $this->assertTrue(variable_get('filetransfer_test_success', FALSE), t('The module was installed.'));
+  }
+
+  function tearDown() {
+    // Delete the module as it as placed where simpletest does not clean up.
+    file_unmanaged_delete_recursive(drupal_get_path('module', 'filetransfer_test'));
+    parent::tearDown();
+  }
+}
+
 /**
  * Mock FileTransfer object for test case.
  */
diff --git modules/system/system.api.php modules/system/system.api.php
index 3158fee..c28c337 100644
--- modules/system/system.api.php
+++ modules/system/system.api.php
@@ -2768,7 +2768,7 @@ function hook_archiver_info() {
   return array(
     'tar' => array(
       'class' => 'ArchiverTar',
-      'extensions' => array('tar', 'tar.gz', 'tar.bz2'),
+      'extensions' => array('tar', 'tgz', 'tar.gz', 'tar.bz2'),
     ),
   );
 }
