diff --git a/core/includes/file.inc b/core/includes/file.inc
index ca18a2b..7dcfcba 100644
--- a/core/includes/file.inc
+++ b/core/includes/file.inc
@@ -1023,14 +1023,14 @@ function file_unmanaged_delete_recursive($path, $callback = NULL) {
 }
 
 /**
- * Saves a file upload to a new location.
+ * Saves file uploads to a new location.
  *
- * The file will be added to the {file_managed} table as a temporary file.
+ * The files will be added to the {file_managed} table as temporary files.
  * Temporary files are periodically cleaned. Use file_usage()->add() to register
  * the usage of the file which will automatically mark it as permanent.
  *
  * @param $source
- *   A string specifying the filepath or URI of the uploaded file to save.
+ *   A string specifying the filepath or URI of the uploaded files to save.
  * @param $validators
  *   An optional, associative array of callback functions used to validate the
  *   file. See file_validate() for a full discussion of the array format.
@@ -1044,6 +1044,8 @@ function file_unmanaged_delete_recursive($path, $callback = NULL) {
  *   A string containing the URI $source should be copied to.
  *   This must be a stream wrapper URI. If this value is omitted, Drupal's
  *   temporary files scheme will be used ("temporary://").
+ * @param $delta
+ *   Delta of the file to save or NULL to save all files. Defaults to NULL.
  * @param $replace
  *   Replace behavior when the destination file already exists:
  *   - FILE_EXISTS_REPLACE: Replace the existing file.
@@ -1052,181 +1054,203 @@ function file_unmanaged_delete_recursive($path, $callback = NULL) {
  *   - FILE_EXISTS_ERROR: Do nothing and return FALSE.
  *
  * @return
- *   An object containing the file information if the upload succeeded, FALSE
- *   in the event of an error, or NULL if no file was uploaded. The
- *   documentation for the "File interface" group, which you can find under
+ *   Function returns array of files or a single file object if $delta
+ *   != NULL. Each file object contains the file information if the
+ *   upload succeeded or FALSE in the event of an error. Function
+ *   returns NULL if no file was uploaded.
+ *
+ *   The documentation for the "File interface" group, which you can find under
  *   Related topics, or the header at the top of this file, documents the
  *   components of a file entity. In addition to the standard components,
  *   this function adds:
  *   - source: Path to the file before it is moved.
  *   - destination: Path to the file after it is moved (same as 'uri').
  */
-function file_save_upload($source, $validators = array(), $destination = FALSE, $replace = FILE_EXISTS_RENAME) {
+function file_save_upload($source, $validators = array(), $destination = FALSE, $delta = NULL, $replace = FILE_EXISTS_RENAME) {
   global $user;
   static $upload_cache;
 
   // Return cached objects without processing since the file will have
-  // already been processed and the paths in _FILES will be invalid.
+  // already been processed and the paths in $_FILES will be invalid.
   if (isset($upload_cache[$source])) {
-    return $upload_cache[$source];
+    if (isset($delta)) {
+      return $upload_cache[$source][$delta];
+    }
+    else {
+      return $upload_cache[$source];
+    }
   }
 
   // Make sure there's an upload to process.
-  if (empty($_FILES['files']['name'][$source])) {
+  if (empty($_FILES['files']['name'][$source][0])) {
     return NULL;
   }
 
-  // Check for file upload errors and return FALSE if a lower level system
-  // error occurred. For a complete list of errors:
-  // See http://php.net/manual/features.file-upload.errors.php.
-  switch ($_FILES['files']['error'][$source]) {
-    case UPLOAD_ERR_INI_SIZE:
-    case UPLOAD_ERR_FORM_SIZE:
-      drupal_set_message(t('The file %file could not be saved because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $_FILES['files']['name'][$source], '%maxsize' => format_size(file_upload_max_size()))), 'error');
-      return FALSE;
+  $files = array();
+  foreach ($_FILES['files']['name'][$source] as $i => $name) {
+    // Check for file upload errors and return FALSE for this file if a lower
+    // level system error occurred. For a complete list of errors:
+    // See http://php.net/manual/features.file-upload.errors.php.
+    switch ($_FILES['files']['error'][$source][$i]) {
+      case UPLOAD_ERR_INI_SIZE:
+      case UPLOAD_ERR_FORM_SIZE:
+        drupal_set_message(t('The file %file could not be saved because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $name, '%maxsize' => format_size(file_upload_max_size()))), 'error');
+        $files[$i] = FALSE;
+        continue;
 
-    case UPLOAD_ERR_PARTIAL:
-    case UPLOAD_ERR_NO_FILE:
-      drupal_set_message(t('The file %file could not be saved because the upload did not complete.', array('%file' => $_FILES['files']['name'][$source])), 'error');
-      return FALSE;
+      case UPLOAD_ERR_PARTIAL:
+      case UPLOAD_ERR_NO_FILE:
+        drupal_set_message(t('The file %file could not be saved because the upload did not complete.', array('%file' => $name)), 'error');
+        $files[$i] = FALSE;
+        continue;
 
-    case UPLOAD_ERR_OK:
-      // Final check that this is a valid upload, if it isn't, use the
-      // default error handler.
-      if (is_uploaded_file($_FILES['files']['tmp_name'][$source])) {
-        break;
-      }
+      case UPLOAD_ERR_OK:
+        // Final check that this is a valid upload, if it isn't, use the
+        // default error handler.
+        if (is_uploaded_file($_FILES['files']['tmp_name'][$source][$i])) {
+          break;
+        }
 
-    // Unknown error
-    default:
-      drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $_FILES['files']['name'][$source])), 'error');
-      return FALSE;
-  }
-  // Begin building file entity.
-  $values = array(
-    'uid' => $user->uid,
-    'status' => 0,
-    'filename' => trim(drupal_basename($_FILES['files']['name'][$source]), '.'),
-    'uri' => $_FILES['files']['tmp_name'][$source],
-    'filesize' => $_FILES['files']['size'][$source],
-  );
-  $values['filemime'] = file_get_mimetype($values['filename']);
-  $file = entity_create('file', $values);
-
-  $extensions = '';
-  if (isset($validators['file_validate_extensions'])) {
-    if (isset($validators['file_validate_extensions'][0])) {
-      // Build the list of non-munged extensions if the caller provided them.
-      $extensions = $validators['file_validate_extensions'][0];
+        // Unknown error
+      default:
+        drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $name)), 'error');
+        $files[$i] = FALSE;
+        continue;
+
+    }
+    // Begin building file entity.
+    $values = array(
+      'uid' => $user->uid,
+      'status' => 0,
+      'filename' => trim(drupal_basename($name, '.')),
+      'uri' => $_FILES['files']['tmp_name'][$source][$i],
+      'filesize' => $_FILES['files']['size'][$source][$i],
+    );
+    $values['filemime'] = file_get_mimetype($values['filename']);
+    $file = entity_create('file', $values);
+
+    $extensions = '';
+    if (isset($validators['file_validate_extensions'])) {
+      if (isset($validators['file_validate_extensions'][0])) {
+        // Build the list of non-munged extensions if the caller provided them.
+        $extensions = $validators['file_validate_extensions'][0];
+      }
+      else {
+        // If 'file_validate_extensions' is set and the list is empty then the
+        // caller wants to allow any extension. In this case we have to remove the
+        // validator or else it will reject all extensions.
+        unset($validators['file_validate_extensions']);
+      }
     }
     else {
-      // If 'file_validate_extensions' is set and the list is empty then the
-      // caller wants to allow any extension. In this case we have to remove the
-      // validator or else it will reject all extensions.
-      unset($validators['file_validate_extensions']);
+      // No validator was provided, so add one using the default list.
+      // Build a default non-munged safe list for file_munge_filename().
+      $extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp';
+      $validators['file_validate_extensions'] = array();
+      $validators['file_validate_extensions'][0] = $extensions;
     }
-  }
-  else {
-    // No validator was provided, so add one using the default list.
-    // Build a default non-munged safe list for file_munge_filename().
-    $extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp';
-    $validators['file_validate_extensions'] = array();
-    $validators['file_validate_extensions'][0] = $extensions;
-  }
-
-  if (!empty($extensions)) {
-    // Munge the filename to protect against possible malicious extension hiding
-    // within an unknown file type (ie: filename.html.foo).
-    $file->filename = file_munge_filename($file->filename, $extensions);
-  }
-
-  // Rename potentially executable files, to help prevent exploits (i.e. will
-  // rename filename.php.foo and filename.php to filename.php.foo.txt and
-  // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
-  // evaluates to TRUE.
-  if (!config('system.file')->get('allow_insecure_uploads') && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->filename) && (substr($file->filename, -4) != '.txt')) {
-    $file->filemime = 'text/plain';
-    $file->uri .= '.txt';
-    $file->filename .= '.txt';
-    // The .txt extension may not be in the allowed list of extensions. We have
-    // to add it here or else the file upload will fail.
+
     if (!empty($extensions)) {
-      $validators['file_validate_extensions'][0] .= ' txt';
-      drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $file->filename)));
+      // Munge the filename to protect against possible malicious extension hiding
+      // within an unknown file type (ie: filename.html.foo).
+      $file->filename = file_munge_filename($file->filename, $extensions);
     }
-  }
 
-  // If the destination is not provided, use the temporary directory.
-  if (empty($destination)) {
-    $destination = 'temporary://';
-  }
+    // Rename potentially executable files, to help prevent exploits (i.e. will
+    // rename filename.php.foo and filename.php to filename.php.foo.txt and
+    // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
+    // evaluates to TRUE.
+    if (!config('system.file')->get('allow_insecure_uploads') && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->filename) && (substr($file->filename, -4) != '.txt')) {
+      $file->filemime = 'text/plain';
+      $file->uri .= '.txt';
+      $file->filename .= '.txt';
+      // The .txt extension may not be in the allowed list of extensions. We have
+      // to add it here or else the file upload will fail.
+      if (!empty($extensions)) {
+        $validators['file_validate_extensions'][0] .= ' txt';
+        drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $file->filename)));
+      }
+    }
 
-  // Assert that the destination contains a valid stream.
-  $destination_scheme = file_uri_scheme($destination);
-  if (!file_stream_wrapper_valid_scheme($destination_scheme)) {
-    drupal_set_message(t('The file could not be uploaded because the destination %destination is invalid.', array('%destination' => $destination)), 'error');
-    return FALSE;
-  }
+    // If the destination is not provided, use the temporary directory.
+    if (empty($destination)) {
+      $destination = 'temporary://';
+    }
 
-  $file->source = $source;
-  // A URI may already have a trailing slash or look like "public://".
-  if (substr($destination, -1) != '/') {
-    $destination .= '/';
-  }
-  $file->destination = file_destination($destination . $file->filename, $replace);
-  // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and
-  // there's an existing file so we need to bail.
-  if ($file->destination === FALSE) {
-    drupal_set_message(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', array('%source' => $source, '%directory' => $destination)), 'error');
-    return FALSE;
-  }
+    // Assert that the destination contains a valid stream.
+    $destination_scheme = file_uri_scheme($destination);
+    if (!file_stream_wrapper_valid_scheme($destination_scheme)) {
+      drupal_set_message(t('The file could not be uploaded because the destination %destination is invalid.', array('%destination' => $destination)), 'error');
+      $files[$i] = FALSE;
+      continue;
+    }
+
+    $file->source = $source;
+    // A URI may already have a trailing slash or look like "public://".
+    if (substr($destination, -1) != '/') {
+      $destination .= '/';
+    }
+    $file->destination = file_destination($destination . $file->filename, $replace);
+    // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and
+    // there's an existing file so we need to bail.
+    if ($file->destination === FALSE) {
+      drupal_set_message(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', array('%source' => $source, '%directory' => $destination)), 'error');
+      $files[$i] = FALSE;
+      continue;
+    }
 
-  // Add in our check of the the file name length.
-  $validators['file_validate_name_length'] = array();
+    // Add in our check of the the file name length.
+    $validators['file_validate_name_length'] = array();
 
-  // Call the validation functions specified by this function's caller.
-  $errors = file_validate($file, $validators);
+    // Call the validation functions specified by this function's caller.
+    $errors = file_validate($file, $validators);
 
-  // Check for errors.
-  if (!empty($errors)) {
-    $message = t('The specified file %name could not be uploaded.', array('%name' => $file->filename));
-    if (count($errors) > 1) {
-      $message .= theme('item_list', array('items' => $errors));
+    // Check for errors.
+    if (!empty($errors)) {
+      $message = t('The specified file %name could not be uploaded.', array('%name' => $file->filename));
+      if (count($errors) > 1) {
+        $message .= theme('item_list', array('items' => $errors));
+      }
+      else {
+        $message .= ' ' . array_pop($errors);
+      }
+      form_set_error($source, $message);
+      $files[$i] = FALSE;
+      continue;
     }
-    else {
-      $message .= ' ' . array_pop($errors);
+
+    // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary
+    // directory. This overcomes open_basedir restrictions for future file
+    // operations.
+    $file->uri = $file->destination;
+    if (!drupal_move_uploaded_file($_FILES['files']['tmp_name'][$source][$i], $file->uri)) {
+      form_set_error($source, t('File upload error. Could not move uploaded file.'));
+      watchdog('file', 'Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->filename, '%destination' => $file->uri));
+      $files[$i] = FALSE;
+      continue;
     }
-    form_set_error($source, $message);
-    return FALSE;
-  }
 
-  // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary
-  // directory. This overcomes open_basedir restrictions for future file
-  // operations.
-  $file->uri = $file->destination;
-  if (!drupal_move_uploaded_file($_FILES['files']['tmp_name'][$source], $file->uri)) {
-    form_set_error($source, t('File upload error. Could not move uploaded file.'));
-    watchdog('file', 'Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->filename, '%destination' => $file->uri));
-    return FALSE;
-  }
+    // Set the permissions on the new file.
+    drupal_chmod($file->uri);
 
-  // Set the permissions on the new file.
-  drupal_chmod($file->uri);
-
-  // If we are replacing an existing file re-use its database record.
-  if ($replace == FILE_EXISTS_REPLACE) {
-    $existing_files = entity_load_multiple_by_properties('file', array('uri' => $file->uri));
-    if (count($existing_files)) {
-      $existing = reset($existing_files);
-      $file->fid = $existing->fid;
+    // If we are replacing an existing file re-use its database record.
+    if ($replace == FILE_EXISTS_REPLACE) {
+      $existing_files = entity_load_multiple_by_properties('file', array('uri' => $file->uri));
+      if (count($existing_files)) {
+        $existing = reset($existing_files);
+        $file->fid = $existing->fid;
+      }
     }
+
+    // If we made it this far it's safe to record this file in the database.
+    $file->save();
+    $files[$i] = $file;
   }
 
-  // If we made it this far it's safe to record this file in the database.
-  $file->save();
-  // Add file to the cache.
-  $upload_cache[$source] = $file;
-  return $file;
+  // Add files to the cache.
+  $upload_cache[$source] = $files;
+
+  return isset($delta) ? $files[$delta] : $files;
 }
 
 /**
diff --git a/core/includes/form.inc b/core/includes/form.inc
index 95470f9..48cd70c 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -4688,6 +4688,17 @@ function form_pre_render_file($element) {
 }
 
 /**
+ * Processes a file upload element, make use of #multiple if present.
+ */
+function form_process_file($element) {
+  if ($element['#multiple'] == TRUE) {
+    $element['#attributes'] = array('multiple' => 'multiple');
+  }
+  $element['#name'] .= '[]';
+  return $element;
+}
+
+/**
  * Returns HTML for a form element.
  *
  * Each form element is wrapped in a DIV container having the following CSS
diff --git a/core/modules/aggregator/aggregator.admin.inc b/core/modules/aggregator/aggregator.admin.inc
index 62cfe83..c6eb0a0 100644
--- a/core/modules/aggregator/aggregator.admin.inc
+++ b/core/modules/aggregator/aggregator.admin.inc
@@ -218,7 +218,7 @@ function aggregator_form_opml_validate($form, &$form_state) {
 function aggregator_form_opml_submit($form, &$form_state) {
   $data = '';
   $validators = array('file_validate_extensions' => array('opml xml'));
-  if ($file = file_save_upload('upload', $validators)) {
+  if ($file = file_save_upload('upload', $validators, FALSE, 0)) {
     $data = file_get_contents($file->uri);
   }
   else {
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/ImportOpmlTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/ImportOpmlTest.php
index 7a9f463..631bc6e 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/ImportOpmlTest.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/ImportOpmlTest.php
@@ -53,7 +53,7 @@ function openImportForm() {
 
     $this->drupalGet('admin/config/services/aggregator/add/opml');
     $this->assertText('A single OPML document may contain a collection of many feeds.', 'Found OPML help text.');
-    $this->assertField('files[upload]', 'Found file upload field.');
+    $this->assertField('files[upload][]', 'Found file upload field.');
     $this->assertField('remote', 'Found Remote URL field.');
     $this->assertField('refresh', '', 'Found Refresh field.');
     $this->assertFieldByName("category[$cid]", $cid, 'Found category field.');
@@ -71,7 +71,7 @@ function validateImportFormFields() {
 
     $path = $this->getEmptyOpml();
     $edit = array(
-      'files[upload]' => $path,
+      'files[upload][]' => $path,
       'remote' => file_create_url($path),
     );
     $this->drupalPost('admin/config/services/aggregator/add/opml', $edit, t('Import'));
@@ -91,7 +91,7 @@ function validateImportFormFields() {
   function submitImportForm() {
     $before = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
 
-    $form['files[upload]'] = $this->getInvalidOpml();
+    $form['files[upload][]'] = $this->getInvalidOpml();
     $this->drupalPost('admin/config/services/aggregator/add/opml', $form, t('Import'));
     $this->assertText(t('No new feed has been added.'), 'Attempting to upload invalid XML.');
 
@@ -119,7 +119,7 @@ function submitImportForm() {
     $feeds[1] = $this->getFeedEditArray();
     $feeds[2] = $this->getFeedEditArray();
     $edit = array(
-      'files[upload]' => $this->getValidOpml($feeds),
+      'files[upload][]' => $this->getValidOpml($feeds),
       'refresh'       => '900',
       'category[1]'   => $category,
     );
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentPreviewTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentPreviewTest.php
index 13817df..d7ec0b4 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentPreviewTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentPreviewTest.php
@@ -48,7 +48,7 @@ function testCommentPreview() {
     $edit['signature[value]'] = '<a href="http://example.com/">' . $test_signature. '</a>';
     $edit['signature[format]'] = 'filtered_html';
     $image = current($this->drupalGetTestFiles('image'));
-    $edit['files[user_picture_und_0]'] = drupal_realpath($image->uri);
+    $edit['files[user_picture_und_0][]'] = drupal_realpath($image->uri);
     $this->drupalPost('user/' . $this->web_user->uid . '/edit', $edit, t('Save'));
 
     // As the web user, fill in the comment form and preview the comment.
diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc
index 9d58d1d..239e814 100644
--- a/core/modules/file/file.field.inc
+++ b/core/modules/file/file.field.inc
@@ -387,7 +387,7 @@ function file_field_widget_value($element, $input = FALSE, $form_state) {
 
   // Ensure that all the required properties are returned even if empty.
   $return += array(
-    'fid' => 0,
+    'fids' => array(),
     'display' => 1,
     'description' => '',
   );
@@ -396,6 +396,39 @@ function file_field_widget_value($element, $input = FALSE, $form_state) {
 }
 
 /**
+ * Validation callback for upload element on file widget. Checks if
+ * user has uploaded more files than allowed.
+ *
+ * This validator is used only when cardinality not set to 1 or unlimited.
+ */
+function file_field_widget_multiple_count_validate($element, &$form_state, $form) {
+  $parents = $element['#array_parents'];
+  $values = NestedArray::getValue($form_state['values'], $parents);
+
+  array_pop($parents);
+  $current = count(element_children(NestedArray::getValue($form, $parents))) - 1;
+
+  $field = field_info_field($element['#field_name']);
+  $uploaded = count($values['fids']);
+  $count = $uploaded + $current;
+  if ($count > $field['cardinality']) {
+    $keep = $uploaded - $count + $field['cardinality'];
+    drupal_set_message(
+      t(
+        'Field %field can hold only @max values. Only @count values were uploaded.',
+        array(
+          '%field' => $field['field_name'],
+          '@max' => $field['cardinality'],
+          '@count' => $keep)
+      ),
+      'warning'
+    );
+    $values['fids'] = array_slice($values['fids'], 0, $keep);
+    NestedArray::setValue($form_state['values'], $element['#array_parents'], $values);
+  }
+}
+
+/**
  * Render API callback: Processes a file_generic field element.
  *
  * Expands the file_generic type to include the description and display fields.
@@ -404,7 +437,7 @@ function file_field_widget_value($element, $input = FALSE, $form_state) {
  */
 function file_field_widget_process($element, &$form_state, $form) {
   $item = $element['#value'];
-  $item['fid'] = $element['fid']['#value'];
+  $item['fids'] = $element['fids']['#value'];
 
   $field = field_widget_field($element, $form_state);
   $instance = field_widget_instance($element, $form_state);
@@ -413,9 +446,9 @@ function file_field_widget_process($element, &$form_state, $form) {
   $element['#theme'] = 'file_widget';
 
   // Add the display field if enabled.
-  if (!empty($field['settings']['display_field']) && $item['fid']) {
+  if (!empty($field['settings']['display_field']) && $item['fids']) {
     $element['display'] = array(
-      '#type' => empty($item['fid']) ? 'hidden' : 'checkbox',
+      '#type' => empty($item['fids']) ? 'hidden' : 'checkbox',
       '#title' => t('Include file in display'),
       '#value' => isset($item['display']) ? $item['display'] : $field['settings']['display_default'],
       '#attributes' => array('class' => array('file-display')),
@@ -429,7 +462,7 @@ function file_field_widget_process($element, &$form_state, $form) {
   }
 
   // Add the description field if enabled.
-  if (!empty($instance['settings']['description_field']) && $item['fid']) {
+  if (!empty($instance['settings']['description_field']) && $item['fids']) {
     $config = config('file.settings');
     $element['description'] = array(
       '#type' => $config->get('description.type'),
@@ -561,13 +594,24 @@ function file_field_widget_submit($form, &$form_state) {
 
   $submitted_values = NestedArray::getValue($form_state['values'], array_slice($button['#array_parents'], 0, -2));
   foreach ($submitted_values as $delta => $submitted_value) {
-    if (!$submitted_value['fid']) {
+    if (empty($submitted_value['fids'])) {
       unset($submitted_values[$delta]);
     }
   }
 
+  // If there are more files uploaded via same widget, we have to
+  // separate them, as we display each file in it's own widget.
+  $new_values = array();
+  foreach ($submitted_values as $delta => $submitted_value) {
+    foreach ($submitted_value['fids'] as $fid) {
+      $new_value = $submitted_value;
+      $new_value['fids'] = array($fid);
+      $new_values[] = $new_value;
+    }
+  }
+
   // Re-index deltas after removing empty items.
-  $submitted_values = array_values($submitted_values);
+  $submitted_values = array_values($new_values);
 
   // Update form_state values.
   NestedArray::setValue($form_state['values'], array_slice($button['#array_parents'], 0, -2), $submitted_values);
@@ -593,9 +637,10 @@ function theme_file_widget($variables) {
 
   // The "form-managed-file" class is required for proper Ajax functionality.
   $output .= '<div class="file-widget form-managed-file clearfix">';
-  if ($element['fid']['#value'] != 0) {
+  if (!empty($element['fids']['#value'])) {
     // Add the file size after the file name.
-    $element['filename']['#markup'] .= ' <span class="file-size">(' . format_size($element['#file']->filesize) . ')</span> ';
+    $file = reset($element['#files']);
+    $element['file_' . $file->fid]['filename']['#markup'] .= ' <span class="file-size">(' . format_size($file->filesize) . ')</span> ';
   }
   $output .= drupal_render_children($element);
   $output .= '</div>';
@@ -642,7 +687,7 @@ function theme_file_widget_multiple($variables) {
   $rows = array();
   foreach ($widgets as $key => &$widget) {
     // Save the uploading row for last.
-    if ($widget['#file'] == FALSE) {
+    if (empty($widget['#files'])) {
       $widget['#title'] = $element['#file_upload_title'];
       $widget['#description'] = $element['#file_upload_description'];
       continue;
diff --git a/core/modules/file/file.install b/core/modules/file/file.install
index 27a2438..1b9ee60 100644
--- a/core/modules/file/file.install
+++ b/core/modules/file/file.install
@@ -245,3 +245,35 @@ function file_update_8000() {
     'file_icon_directory'=>'icon.directory',
   ));
 }
+
+/**
+* Comvert image field's default image configuration to the new format.
+*/
+function file_update_8001() {
+  $fields = field_info_fields();
+  foreach ($fields as $field) {
+    if ($field['type'] == 'image') {
+      if (!empty($field['settings']['default_image'])) {
+        $field['settings']['default_image'] = array($field['settings']['default_image']);
+      }
+      else {
+        $field['settings']['default_image'] = array();
+      }
+      field_update_field($field);
+
+      foreach ($field['bundles'] as $etype => $bundles) {
+        foreach ($bundles as $bundle) {
+          if ($instance = field_read_instance($etype, $field['field_name'], $bundle)) {
+            if (!empty($instance['settings']['default_image'])) {
+              $instance['settings']['default_image'] = array($instance['settings']['default_image']);
+            }
+            else {
+              $instance['settings']['default_image'] = array();
+            }
+            field_update_instance($instance);
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 913f75a..c21aa1c 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -81,6 +81,7 @@ function file_element_info() {
     '#upload_validators' => array(),
     '#upload_location' => NULL,
     '#size' => 22,
+    '#multiple' => FALSE,
     '#extended' => FALSE,
     '#attached' => array(
       'library' => array(array('file','drupal.file')),
@@ -866,11 +867,15 @@ function file_managed_file_process($element, &$form_state, $form) {
   // Append the '-upload' to the #id so the field label's 'for' attribute
   // corresponds with the file element.
   $element['#id'] .= '-upload';
-  $fid = isset($element['#value']['fid']) ? $element['#value']['fid'] : 0;
+
+  // This is used some times so let's implode it just once.
+  $parents_prefix = implode('_', $element['#parents']);
+
+  $fids = isset($element['#value']['fids']) ? $element['#value']['fids'] : array();
 
   // Set some default element properties.
   $element['#progress_indicator'] = empty($element['#progress_indicator']) ? 'none' : $element['#progress_indicator'];
-  $element['#file'] = $fid ? file_load($fid) : FALSE;
+  $element['#files'] = !empty($fids) ? file_load_multiple($fids) : FALSE;
   $element['#tree'] = TRUE;
 
   $ajax_settings = array(
@@ -885,7 +890,7 @@ function file_managed_file_process($element, &$form_state, $form) {
 
   // Set up the buttons first since we need to check if they were clicked.
   $element['upload_button'] = array(
-    '#name' => implode('_', $element['#parents']) . '_upload_button',
+    '#name' => $parents_prefix . '_upload_button',
     '#type' => 'submit',
     '#value' => t('Upload'),
     '#validate' => array(),
@@ -894,26 +899,26 @@ function file_managed_file_process($element, &$form_state, $form) {
     '#ajax' => $ajax_settings,
     '#weight' => -5,
   );
-
-  // Force the progress indicator for the remove button to be either 'none' or
-  // 'throbber', even if the upload button is using something else.
-  $ajax_settings['progress']['type'] = ($element['#progress_indicator'] == 'none') ? 'none' : 'throbber';
-  $ajax_settings['progress']['message'] = NULL;
-  $ajax_settings['effect'] = 'none';
   $element['remove_button'] = array(
-    '#name' => implode('_', $element['#parents']) . '_remove_button',
+    '#name' => $parents_prefix . '_remove_button',
     '#type' => 'submit',
-    '#value' => t('Remove'),
+    '#value' => $element['#multiple'] ? t('Remove selected') : t('Remove'),
     '#validate' => array(),
     '#submit' => array('file_managed_file_submit'),
     '#limit_validation_errors' => array($element['#parents']),
     '#ajax' => $ajax_settings,
-    '#weight' => -5,
+    '#weight' => 1,
   );
 
-  $element['fid'] = array(
+  // Force the progress indicator for the remove button to be either 'none' or
+  // 'throbber', even if the upload button is using something else.
+  $ajax_settings['progress']['type'] = ($element['#progress_indicator'] == 'none') ? 'none' : 'throbber';
+  $ajax_settings['progress']['message'] = NULL;
+  $ajax_settings['effect'] = 'none';
+
+  $element['fids'] = array(
     '#type' => 'hidden',
-    '#value' => $fid,
+    '#value' => $fids,
   );
 
   // Add progress bar support to the upload if possible.
@@ -947,21 +952,32 @@ function file_managed_file_process($element, &$form_state, $form) {
 
   // The file upload field itself.
   $element['upload'] = array(
-    '#name' => 'files[' . implode('_', $element['#parents']) . ']',
+    '#name' => 'files[' . $parents_prefix . ']',
     '#type' => 'file',
     '#title' => t('Choose a file'),
     '#title_display' => 'invisible',
     '#size' => $element['#size'],
+    '#multiple' => $element['#multiple'],
     '#theme_wrappers' => array(),
     '#weight' => -10,
   );
 
-  if ($fid && $element['#file']) {
-    $element['filename'] = array(
-      '#type' => 'markup',
-      '#markup' => theme('file_link', array('file' => $element['#file'])) . ' ',
-      '#weight' => -10,
-    );
+  if (!empty($fids) && $element['#files']) {
+    foreach ($element['#files'] as $delta => $file) {
+      if ($element['#multiple']) {
+        $element['file_' . $delta]['selected'] = array(
+          '#type' => 'checkbox',
+          '#title' => theme('file_link', array('file' => $file)) . ' ',
+        );
+      }
+      else {
+        $element['file_' . $delta]['filename'] = array(
+          '#type' => 'markup',
+          '#markup' => theme('file_link', array('file' => $file)) . ' ',
+          '#weight' => -10,
+        );
+      }
+    }
   }
 
   // Add the extension list to the page as JavaScript settings.
@@ -988,28 +1004,30 @@ function file_managed_file_process($element, &$form_state, $form) {
  * This function is assigned as a #value_callback in file_element_info().
  */
 function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL) {
-  $fid = 0;
-
-  // Find the current value of this field from the form state.
-  $form_state_fid = $form_state['values'];
-  foreach ($element['#parents'] as $parent) {
-    $form_state_fid = isset($form_state_fid[$parent]) ? $form_state_fid[$parent] : 0;
-  }
-
-  if ($element['#extended'] && isset($form_state_fid['fid'])) {
-    $fid = $form_state_fid['fid'];
-  }
-  elseif (is_numeric($form_state_fid)) {
-    $fid = $form_state_fid;
-  }
+  $fids = array();
+
+  // Find the current value of this field.
+  $fids = !empty($input['fids']) ? explode(' ', $input['fids']) : array();
+  $fids = array_map(
+    function($item) {
+      return (int) $item;
+    },
+    $fids
+  );
 
   // Process any input and save new uploads.
   if ($input !== FALSE) {
+    $input['fids'] = $fids;
     $return = $input;
 
     // Uploads take priority over all other values.
-    if ($file = file_managed_file_save_upload($element)) {
-      $fid = $file->fid;
+    if ($files = file_managed_file_save_upload($element)) {
+      if ($element['#multiple']) {
+        $fids = array_merge($fids, array_keys($files));
+      }
+      else {
+        $fids = array_keys($files);
+      }
     }
     else {
       // Check for #filefield_value_callback values.
@@ -1021,9 +1039,15 @@ function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL)
           $callback($element, $input, $form_state);
         }
       }
-      // Load file if the FID has changed to confirm it exists.
-      if (isset($input['fid']) && $file = file_load($input['fid'])) {
-        $fid = $file->fid;
+
+      // Load files if the FIDs have changed to confirm they exist.
+      if (!empty($input['fids'])) {
+        $fids = array();
+        foreach ($input['fids'] as $key => $fid) {
+          if ($file = file_load($fid)) {
+            $fids[] = $file->fid;
+          }
+        }
       }
     }
   }
@@ -1031,22 +1055,26 @@ function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL)
   // If there is no input, set the default value.
   else {
     if ($element['#extended']) {
-      $default_fid = isset($element['#default_value']['fid']) ? $element['#default_value']['fid'] : 0;
-      $return = isset($element['#default_value']) ? $element['#default_value'] : array('fid' => 0);
+      $default_fids = isset($element['#default_value']['fids']) ? $element['#default_value']['fids'] : array();
+      $return = isset($element['#default_value']) ? $element['#default_value'] : array('fids' => array());
     }
     else {
-      $default_fid = isset($element['#default_value']) ? $element['#default_value'] : 0;
-      $return = array('fid' => 0);
+      $default_fids = isset($element['#default_value']) ? $element['#default_value'] : array();
+      $return = array('fids' => array());
     }
 
     // Confirm that the file exists when used as a default value.
-    if ($default_fid && $file = file_load($default_fid)) {
-      $fid = $file->fid;
+    if (!empty($default_fids)) {
+      $fids = array();
+      foreach ($default_fids as $key => $fid) {
+        if ($file = file_load($fid)) {
+          $fids[] = $file->fid;
+        }
+      }
     }
   }
 
-  $return['fid'] = $fid;
-
+  $return['fids'] = $fids;
   return $return;
 }
 
@@ -1061,28 +1089,41 @@ function file_managed_file_validate(&$element, &$form_state) {
   // references. This prevents unmanaged files from being deleted if this
   // item were to be deleted.
   $clicked_button = end($form_state['triggering_element']['#parents']);
-  if ($clicked_button != 'remove_button' && !empty($element['fid']['#value'])) {
-    if ($file = file_load($element['fid']['#value'])) {
-      if ($file->status == FILE_STATUS_PERMANENT) {
-        $references = file_usage()->listUsage($file);
-        if (empty($references)) {
-          form_error($element, t('The file used in the !name field may not be referenced.', array('!name' => $element['#title'])));
+  if ($clicked_button != 'remove_button' && !empty($element['fids']['#value'])) {
+    // Was here but I do not see any sense in it anymore. Leaving here for reference if any
+    // strange bugs occur during development of the patch.
+    //$fids = is_array($element['fids']['#value']) ? $element['fids']['#value'] : explode(' ', $element['fids']['#value']);
+    $fids = $element['fids']['#value'];
+    foreach ($fids as $fid) {
+      if ($file = file_load($fid)) {
+        if ($file->status == FILE_STATUS_PERMANENT) {
+          $references = file_usage()->listUsage($file);
+          if (empty($references)) {
+            form_error($element, t('The file used in the !name field may not be referenced.', array('!name' => $element['#title'])));
+          }
         }
       }
-    }
-    else {
-      form_error($element, t('The file referenced by the !name field does not exist.', array('!name' => $element['#title'])));
+      else {
+        form_error($element, t('The file referenced by the !name field does not exist.', array('!name' => $element['#title'])));
+      }
     }
   }
 
   // Check required property based on the FID.
-  if ($element['#required'] && empty($element['fid']['#value']) && !in_array($clicked_button, array('upload_button', 'remove_button'))) {
+  if ($element['#required'] && empty($element['fids']['#value']) && !in_array($clicked_button, array('upload_button', 'remove_button'))) {
     form_error($element['upload'], t('!name field is required.', array('!name' => $element['#title'])));
   }
 
-  // Consolidate the array value of this field to a single FID.
+  // Save entire values to storage.
+  $values = NestedArray::getValue($form_state['values'], $element['#array_parents']);
+  if (!isset($form_state['storage']['managed_file_values'])) {
+    $form_state['storage']['managed_file_values'] = array();
+  }
+  NestedArray::setValue($form_state['storage']['managed_file_values'], $element['#array_parents'], $values);
+
+  // Consolidate the array value of this field to array of fids.
   if (!$element['#extended']) {
-    form_set_value($element, $element['fid']['#value'], $form_state);
+    form_set_value($element, $element['fids']['#value'], $form_state);
   }
 }
 
@@ -1103,11 +1144,33 @@ function file_managed_file_submit($form, &$form_state) {
   // button was clicked. Action is needed here for the remove button, because we
   // only remove a file in response to its remove button being clicked.
   if ($button_key == 'remove_button') {
-    // If it's a temporary file we can safely remove it immediately, otherwise
-    // it's up to the implementing module to remove usages of files to have them
-    // removed.
-    if ($element['#file'] && $element['#file']->status == 0) {
-      file_delete($element['#file']->fid);
+    // Get files that need to be removed from list.
+    $fids = array();
+    $values = NestedArray::getValue($form_state['storage']['managed_file_values'], $parents);
+    if (!is_array($values)) {
+      $values = array('fids' => array());
+    }
+
+    if ($element['#multiple']) {
+      foreach ($values as $name => $value) {
+        if (strpos($name, 'file_') === 0 && $value['selected']) {
+          $fids[] = (int) substr($name, 5);
+        }
+      }
+    }
+    else {
+      $fids = $values['fids'];
+    }
+
+    $values['fids'] = array_diff($values['fids'], $fids);
+
+    foreach ($fids as $fid) {
+      // If it's a temporary file we can safely remove it immediately, otherwise
+      // it's up to the implementing module to remove usages of files to have them
+      // removed.
+      if ($element['#files'][$fid] && $element['#files'][$fid]->status == 0) {
+        file_delete($element['#files'][$fid]->fid);
+      }
     }
     // Update both $form_state['values'] and $form_state['input'] to reflect
     // that the file has been removed, so that the form is rebuilt correctly.
@@ -1116,9 +1179,9 @@ function file_managed_file_submit($form, &$form_state) {
     // when the managed_file element is part of a field widget.
     // $form_state['input'] must be updated so that file_managed_file_value()
     // has correct information during the rebuild.
-    $values_element = $element['#extended'] ? $element['fid'] : $element;
-    form_set_value($values_element, NULL, $form_state);
-    NestedArray::setValue($form_state['input'], $values_element['#parents'], NULL);
+    $values_element = $element['#extended'] ? $element['fids'] : $element['fids'];
+    form_set_value($values_element, $values['fids'], $form_state);
+    NestedArray::setValue($form_state['input'], $values_element['#parents'], implode(' ', $values['fids']));
   }
 
   // Set the form to rebuild so that $form is correctly updated in response to
@@ -1153,13 +1216,22 @@ function file_managed_file_save_upload($element) {
     return FALSE;
   }
 
-  if (!$file = file_save_upload($upload_name, $element['#upload_validators'], $destination)) {
-    watchdog('file', 'The file upload failed. %upload', array('%upload' => $upload_name));
-    form_set_error($upload_name, t('The file in the !name field was unable to be uploaded.', array('!name' => $element['#title'])));
-    return FALSE;
+  // Save attached files to the database.
+  if(count(array_filter($_FILES['files']['name'][$upload_name])) > 0) {
+    if (!$files = file_save_upload($upload_name, $element['#upload_validators'], $destination)) {
+      watchdog('file', 'The file upload failed. %upload', array('%upload' => $upload_name));
+      form_set_error($upload_name, t('Files in the !name field were unable to be uploaded.', array('!name' => $element['#title'])));
+      return array();
+    }
+
+    // Value callback expects FIDs to be keys.
+    $files = array_filter($files);
+    $fids = array_map(function($file) {return $file->fid;}, $files);
+
+    return empty($files) ? array() : array_combine($fids, $files);
   }
 
-  return $file;
+  return array();
 }
 
 /**
@@ -1214,9 +1286,11 @@ function theme_file_managed_file($variables) {
  */
 function file_managed_file_pre_render($element) {
   // If we already have a file, we don't want to show the upload controls.
-  if (!empty($element['#value']['fid'])) {
-    $element['upload']['#access'] = FALSE;
-    $element['upload_button']['#access'] = FALSE;
+  if (!empty($element['#value']['fids'])) {
+    if (!$element['#multiple']) {
+      $element['upload']['#access'] = FALSE;
+      $element['upload_button']['#access'] = FALSE;
+    }
   }
   // If we don't already have a file, there is nothing to remove.
   else {
diff --git a/core/modules/file/lib/Drupal/file/Plugin/field/widget/FileWidget.php b/core/modules/file/lib/Drupal/file/Plugin/field/widget/FileWidget.php
index 98a7a96..f05ef6c 100644
--- a/core/modules/file/lib/Drupal/file/Plugin/field/widget/FileWidget.php
+++ b/core/modules/file/lib/Drupal/file/Plugin/field/widget/FileWidget.php
@@ -166,7 +166,7 @@ protected function formMultipleElements(EntityInterface $entity, array $items, $
    */
   public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
     $defaults = array(
-      'fid' => 0,
+      'fids' => array(),
       'display' => !empty($this->field['settings']['display_default']),
       'description' => '',
     );
@@ -186,13 +186,45 @@ public function formElement(array $items, $delta, array $element, $langcode, arr
     );
 
     $element['#weight'] = $delta;
+
+    // If we just loaded from DB we have to translate value to multivalue file
+    // widgets.
+    if (!isset($items[$delta]['fids']) && isset($items[$delta]['fid'])) {
+      $items[$delta]['fids'][0] = $items[$delta]['fid'];
+    }
     $element['#default_value'] = !empty($items[$delta]) ? $items[$delta] : $defaults;
 
-    if (empty($element['#default_value']['fid'])) {
+    $default_fids = $element['#extended'] ? $element['#default_value']['fids'] : $element['#default_value'];
+    if (empty($default_fids)) {
+      $cardinality = isset($this->field['cardinality']) ? $this->field['cardinality'] : 1;
       $element['#description'] = theme('file_upload_help', array('description' => $element['#description'], 'upload_validators' => $element['#upload_validators']));
+      $element['#multiple'] = $cardinality != 1 ? TRUE : FALSE;
+      if ($cardinality != 1 && $cardinality != -1) {
+        $element['#element_validate'] = array('file_field_widget_multiple_count_validate');
+      }
     }
 
     return $element;
   }
 
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::massageFormValues().
+   */
+  public function massageFormValues(array $values, array $form, array &$form_state) {
+    // Since file upload widget now supports uploads of more than one file at
+    // the time it always returns array of fids. We have to translate this to
+    // a single fid, as field expects single value.
+    $new_values = array();
+    foreach ($values as &$value) {
+      foreach ($value['fids'] as $fid) {
+        $new_value = $value;
+        $new_value['fid'] = $fid;
+        unset($new_value['fids']);
+        $new_values[] = $new_value;
+      }
+    }
+
+    return $new_values;
+  }
+
 }
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php
index 52df6f2..6defa77 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php
@@ -147,7 +147,7 @@ function uploadNodeFile($file, $field_name, $nid_or_type, $new_revision = TRUE,
     }
 
     // Attach a file to the node.
-    $edit['files[' . $field_name . '_' . $langcode . '_0]'] = drupal_realpath($file->uri);
+    $edit['files[' . $field_name . '_' . $langcode . '_0][]'] = drupal_realpath($file->uri);
     $this->drupalPost("node/$nid/edit", $edit, t('Save and keep published'));
 
     return $nid;
@@ -172,7 +172,7 @@ function removeNodeFile($nid, $new_revision = TRUE) {
    */
   function replaceNodeFile($file, $field_name, $nid, $new_revision = TRUE) {
     $edit = array(
-      'files[' . $field_name . '_' . LANGUAGE_NOT_SPECIFIED . '_0]' => drupal_realpath($file->uri),
+      'files[' . $field_name . '_' . LANGUAGE_NOT_SPECIFIED . '_0][]' => drupal_realpath($file->uri),
       'revision' => (string) (int) $new_revision,
     );
 
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php
index 28a5d5d..ae72138 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php
@@ -113,7 +113,7 @@ function testMultiValuedWidget() {
       $this->drupalGet("node/add/$type_name");
       foreach (array($field_name2, $field_name) as $each_field_name) {
         for ($delta = 0; $delta < 3; $delta++) {
-          $edit = array('files[' . $each_field_name . '_' . LANGUAGE_NOT_SPECIFIED . '_' . $delta . ']' => drupal_realpath($test_file->uri));
+          $edit = array('files[' . $each_field_name . '_' . LANGUAGE_NOT_SPECIFIED . '_' . $delta . '][]' => drupal_realpath($test_file->uri));
           // If the Upload button doesn't exist, drupalPost() will automatically
           // fail with an assertion message.
           $this->drupalPost(NULL, $edit, t('Upload'));
@@ -272,7 +272,7 @@ function testPrivateFileComment() {
     // Add a comment with a file.
     $text_file = $this->getTestFile('text');
     $edit = array(
-      'files[field_' . $name . '_' . LANGUAGE_NOT_SPECIFIED . '_' . 0 . ']' => drupal_realpath($text_file->uri),
+      'files[field_' . $name . '_' . LANGUAGE_NOT_SPECIFIED . '_' . 0 . '][]' => drupal_realpath($text_file->uri),
       'comment_body[' . LANGUAGE_NOT_SPECIFIED . '][0][value]' => $comment_body = $this->randomName(),
     );
     $this->drupalPost(NULL, $edit, t('Save'));
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileManagedFileElementTest.php b/core/modules/file/lib/Drupal/file/Tests/FileManagedFileElementTest.php
index 9865314..5b4469c 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileManagedFileElementTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileManagedFileElementTest.php
@@ -28,7 +28,7 @@ public static function getInfo() {
   function testManagedFile() {
     // Check that $element['#size'] is passed to the child upload element.
     $this->drupalGet('file/test');
-    $this->assertFieldByXpath('//input[@name="files[nested_file]" and @size="13"]', NULL, 'The custom #size attribute is passed to the child upload element.');
+    $this->assertFieldByXpath('//input[@name="files[nested_file][]" and @size="13"]', NULL, 'The custom #size attribute is passed to the child upload element.');
 
     // Perform the tests with all permutations of $form['#tree'] and
     // $element['#extended'].
@@ -40,26 +40,26 @@ function testManagedFile() {
 
         // Submit without a file.
         $this->drupalPost($path, array(), t('Save'));
-        $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submitted without a file.'));
+        $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array()))), t('Submitted without a file.'));
 
         // Submit a new file, without using the Upload button.
         $last_fid_prior = $this->getLastFileId();
-        $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
+        $edit = array('files[' . $input_base_name . '][]' => drupal_realpath($test_file->uri));
         $this->drupalPost($path, $edit, t('Save'));
         $last_fid = $this->getLastFileId();
         $this->assertTrue($last_fid > $last_fid_prior, t('New file got saved.'));
-        $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Submit handler has correct file info.'));
+        $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array($last_fid)))), t('Submit handler has correct file info.'));
 
         // Submit no new input, but with a default file.
         $this->drupalPost($path . '/' . $last_fid, array(), t('Save'));
-        $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Empty submission did not change an existing file.'));
+        $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array($last_fid)))), t('Empty submission did not change an existing file.'));
 
         // Now, test the Upload and Remove buttons, with and without Ajax.
         foreach (array(FALSE, TRUE) as $ajax) {
           // Upload, then Submit.
           $last_fid_prior = $this->getLastFileId();
           $this->drupalGet($path);
-          $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
+          $edit = array('files[' . $input_base_name . '][]' => drupal_realpath($test_file->uri));
           if ($ajax) {
             $this->drupalPostAJAX(NULL, $edit, $input_base_name . '_upload_button');
           }
@@ -69,7 +69,7 @@ function testManagedFile() {
           $last_fid = $this->getLastFileId();
           $this->assertTrue($last_fid > $last_fid_prior, t('New file got uploaded.'));
           $this->drupalPost(NULL, array(), t('Save'));
-          $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Submit handler has correct file info.'));
+          $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array($last_fid)))), t('Submit handler has correct file info.'));
 
           // Remove, then Submit.
           $this->drupalGet($path . '/' . $last_fid);
@@ -80,11 +80,11 @@ function testManagedFile() {
             $this->drupalPost(NULL, array(), t('Remove'));
           }
           $this->drupalPost(NULL, array(), t('Save'));
-          $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submission after file removal was successful.'));
+          $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array()))), t('Submission after file removal was successful.'));
 
           // Upload, then Remove, then Submit.
           $this->drupalGet($path);
-          $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
+          $edit = array('files[' . $input_base_name . '][]' => drupal_realpath($test_file->uri));
           if ($ajax) {
             $this->drupalPostAJAX(NULL, $edit, $input_base_name . '_upload_button');
             $this->drupalPostAJAX(NULL, array(), $input_base_name . '_remove_button');
@@ -94,7 +94,7 @@ function testManagedFile() {
             $this->drupalPost(NULL, array(), t('Remove'));
           }
           $this->drupalPost(NULL, array(), t('Save'));
-          $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submission after file upload and removal was successful.'));
+          $this->assertRaw(t('The file ids are %fids.', array('%fids' => implode(',', array()))), t('Submission after file upload and removal was successful.'));
         }
       }
     }
diff --git a/core/modules/file/lib/Drupal/file/Tests/SaveUploadTest.php b/core/modules/file/lib/Drupal/file/Tests/SaveUploadTest.php
index 90fec9d..ca3a613 100644
--- a/core/modules/file/lib/Drupal/file/Tests/SaveUploadTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/SaveUploadTest.php
@@ -53,7 +53,7 @@ function setUp() {
     // Upload with replace to guarantee there's something there.
     $edit = array(
       'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri),
+      'files[file_test_upload][]' => drupal_realpath($this->image->uri),
     );
     $this->drupalPost('file-test/upload', $edit, t('Submit'));
     $this->assertResponse(200, t('Received a 200 response for posted test file.'));
@@ -82,7 +82,7 @@ function testNormal() {
     // Upload a second file.
     $max_fid_before = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
     $image2 = current($this->drupalGetTestFiles('image'));
-    $edit = array('files[file_test_upload]' => drupal_realpath($image2->uri));
+    $edit = array('files[file_test_upload][]' => drupal_realpath($image2->uri));
     $this->drupalPost('file-test/upload', $edit, t('Submit'));
     $this->assertResponse(200, t('Received a 200 response for posted test file.'));
     $this->assertRaw(t('You WIN!'));
@@ -106,7 +106,7 @@ function testNormal() {
     $image3_realpath = drupal_realpath($image3->uri);
     $dir = $this->randomName();
     $edit = array(
-      'files[file_test_upload]' => $image3_realpath,
+      'files[file_test_upload][]' => $image3_realpath,
       'file_subdir' => $dir,
     );
     $this->drupalPost('file-test/upload', $edit, t('Submit'));
@@ -126,7 +126,7 @@ function testHandleExtension() {
     $extensions = 'foo';
     $edit = array(
       'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri),
+      'files[file_test_upload][]' => drupal_realpath($this->image->uri),
       'extensions' => $extensions,
     );
 
@@ -146,7 +146,7 @@ function testHandleExtension() {
     // Now tell file_save_upload() to allow the extension of our test image.
     $edit = array(
       'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri),
+      'files[file_test_upload][]' => drupal_realpath($this->image->uri),
       'extensions' => $extensions,
     );
 
@@ -164,7 +164,7 @@ function testHandleExtension() {
     // Now tell file_save_upload() to allow any extension.
     $edit = array(
       'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri),
+      'files[file_test_upload][]' => drupal_realpath($this->image->uri),
       'allow_all_extensions' => TRUE,
     );
     $this->drupalPost('file-test/upload', $edit, t('Submit'));
@@ -185,7 +185,7 @@ function testHandleDangerousFile() {
     // safety. Also check to make sure its MIME type was changed.
     $edit = array(
       'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => drupal_realpath($this->phpfile->uri),
+      'files[file_test_upload][]' => drupal_realpath($this->phpfile->uri),
       'is_image_file' => FALSE,
       'extensions' => 'php',
     );
@@ -232,7 +232,7 @@ function testHandleFileMunge() {
 
     $extensions = $this->image_extension;
     $edit = array(
-      'files[file_test_upload]' => drupal_realpath($this->image->uri),
+      'files[file_test_upload][]' => drupal_realpath($this->image->uri),
       'extensions' => $extensions,
     );
 
@@ -254,7 +254,7 @@ function testHandleFileMunge() {
     file_test_reset();
 
     $edit = array(
-      'files[file_test_upload]' => drupal_realpath($this->image->uri),
+      'files[file_test_upload][]' => drupal_realpath($this->image->uri),
       'allow_all_extensions' => TRUE,
     );
 
@@ -274,7 +274,7 @@ function testHandleFileMunge() {
   function testExistingRename() {
     $edit = array(
       'file_test_replace' => FILE_EXISTS_RENAME,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri)
+      'files[file_test_upload][]' => drupal_realpath($this->image->uri)
     );
     $this->drupalPost('file-test/upload', $edit, t('Submit'));
     $this->assertResponse(200, t('Received a 200 response for posted test file.'));
@@ -290,7 +290,7 @@ function testExistingRename() {
   function testExistingReplace() {
     $edit = array(
       'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri)
+      'files[file_test_upload][]' => drupal_realpath($this->image->uri)
     );
     $this->drupalPost('file-test/upload', $edit, t('Submit'));
     $this->assertResponse(200, t('Received a 200 response for posted test file.'));
@@ -306,7 +306,7 @@ function testExistingReplace() {
   function testExistingError() {
     $edit = array(
       'file_test_replace' => FILE_EXISTS_ERROR,
-      'files[file_test_upload]' => drupal_realpath($this->image->uri)
+      'files[file_test_upload][]' => drupal_realpath($this->image->uri)
     );
     $this->drupalPost('file-test/upload', $edit, t('Submit'));
     $this->assertResponse(200, t('Received a 200 response for posted test file.'));
diff --git a/core/modules/file/tests/file_module_test.module b/core/modules/file/tests/file_module_test.module
index b962e2a..bff3041 100644
--- a/core/modules/file/tests/file_module_test.module
+++ b/core/modules/file/tests/file_module_test.module
@@ -31,7 +31,7 @@ function file_module_test_menu() {
  * @see file_module_test_form_submit()
  * @ingroup forms
  */
-function file_module_test_form($form, &$form_state, $tree = TRUE, $extended = FALSE, $default_fid = NULL) {
+function file_module_test_form($form, &$form_state, $tree = TRUE, $extended = TRUE, $default_fids = NULL, $multiple = NULL) {
   $form['#tree'] = (bool) $tree;
 
   $form['nested']['file'] = array(
@@ -41,9 +41,11 @@ function file_module_test_form($form, &$form_state, $tree = TRUE, $extended = FA
     '#progress_message' => t('Please wait...'),
     '#extended' => (bool) $extended,
     '#size' => 13,
+    '#multiple' => (bool) $multiple,
   );
-  if ($default_fid) {
-    $form['nested']['file']['#default_value'] = $extended ? array('fid' => $default_fid) : $default_fid;
+  if ($default_fids) {
+    $default_fids = explode(',', $default_fids);
+    $form['nested']['file']['#default_value'] = $extended ? array('fids' => $default_fids) : $default_fids;
   }
 
   $form['textfield'] = array(
@@ -64,12 +66,22 @@ function file_module_test_form($form, &$form_state, $tree = TRUE, $extended = FA
  */
 function file_module_test_form_submit($form, &$form_state) {
   if ($form['#tree']) {
-    $fid = $form['nested']['file']['#extended'] ? $form_state['values']['nested']['file']['fid'] : $form_state['values']['nested']['file'];
+    $uploads = $form_state['values']['nested']['file'];
   }
   else {
-    $fid = $form['nested']['file']['#extended'] ? $form_state['values']['file']['fid'] : $form_state['values']['file'];
+    $uploads = $form_state['values']['file'];
   }
-  drupal_set_message(t('The file id is %fid.', array('%fid' => $fid)));
+
+  if ($form['nested']['file']['#extended']) {
+    $uploads = $uploads['fids'];
+  }
+
+  $fids = array();
+  foreach ($uploads as $fid) {
+    $fids[] = $fid;
+  }
+
+  drupal_set_message(t('The file ids are %fids.', array('%fids' => implode(',', $fids))));
 }
 
 /**
diff --git a/core/modules/file/tests/file_test/file_test.module b/core/modules/file/tests/file_test/file_test.module
index 1d924de..8cff297 100644
--- a/core/modules/file/tests/file_test/file_test.module
+++ b/core/modules/file/tests/file_test/file_test.module
@@ -126,7 +126,7 @@ function _file_test_form_submit(&$form, &$form_state) {
     $validators['file_validate_extensions'] = array($form_state['values']['extensions']);
   }
 
-  $file = file_save_upload('file_test_upload', $validators, $destination, $form_state['values']['file_test_replace']);
+  $file = file_save_upload('file_test_upload', $validators, $destination, 0, $form_state['values']['file_test_replace']);
   if ($file) {
     $form_state['values']['file_test_upload'] = $file;
     drupal_set_message(t('File @filepath was uploaded.', array('@filepath' => $file->uri)));
diff --git a/core/modules/image/image.field.inc b/core/modules/image/image.field.inc
index ceb2c8a..4605bde 100644
--- a/core/modules/image/image.field.inc
+++ b/core/modules/image/image.field.inc
@@ -18,7 +18,7 @@ function image_field_info() {
       'description' => t('This field stores the ID of an image file as an integer value.'),
       'settings' => array(
         'uri_scheme' => file_default_scheme(),
-        'default_image' => 0,
+        'default_image' => array(),
       ),
       'instance_settings' => array(
         'file_extensions' => 'png gif jpg jpeg',
@@ -30,7 +30,7 @@ function image_field_info() {
         'title_field_required' => 0,
         'max_resolution' => '',
         'min_resolution' => '',
-        'default_image' => 0,
+        'default_image' => array(),
       ),
       'default_widget' => 'image_image',
       'default_formatter' => 'image',
@@ -216,7 +216,7 @@ function image_field_prepare_view($entity_type, $entities, $field, $instances, $
   // If there are no files specified at all, use the default.
   foreach ($entities as $id => $entity) {
     if (empty($items[$id])) {
-      $fid = 0;
+      $fid = array();
       // Use the default for the instance if one is available.
       if (!empty($instances[$id]['settings']['default_image'])) {
         $fid = $instances[$id]['settings']['default_image'];
@@ -227,7 +227,7 @@ function image_field_prepare_view($entity_type, $entities, $field, $instances, $
       }
 
       // Add the default image if one is found.
-      if ($fid && ($file = file_load($fid))) {
+      if ($fid && ($file = file_load($fid[0]))) {
         $items[$id][0] = (array) $file + array(
           'is_default' => TRUE,
           'alt' => '',
@@ -298,16 +298,17 @@ function image_field_is_empty($item, $field) {
  */
 function image_field_widget_process($element, &$form_state, $form) {
   $item = $element['#value'];
-  $item['fid'] = $element['fid']['#value'];
+  $item['fids'] = $element['fids']['#value'];
 
   $element['#theme'] = 'image_widget';
   $element['#attached']['css'][] = drupal_get_path('module', 'image') . '/image.theme.css';
 
   // Add the image preview.
-  if ($element['#file'] && $element['#preview_image_style']) {
+  if (!empty($element['#files']) && $element['#preview_image_style']) {
+    $file = reset($element['#files']);
     $variables = array(
       'style_name' => $element['#preview_image_style'],
-      'uri' => $element['#file']->uri,
+      'uri' => $file->uri,
     );
 
     // Determine image dimensions.
@@ -316,7 +317,7 @@ function image_field_widget_process($element, &$form_state, $form) {
       $variables['height'] = $element['#value']['height'];
     }
     else {
-      $info = image_get_info($element['#file']->uri);
+      $info = image_get_info($file->uri);
 
       if (is_array($info)) {
         $variables['width'] = $info['width'];
@@ -357,7 +358,7 @@ function image_field_widget_process($element, &$form_state, $form) {
     // @see http://www.gawds.org/show.php?contentid=28
     '#maxlength' => 512,
     '#weight' => -2,
-    '#access' => (bool) $item['fid'] && $element['#alt_field'],
+    '#access' => (bool) $item['fids'] && $element['#alt_field'],
     '#element_validate' => $settings['alt_field_required'] == 1 ? array('_image_field_required_fields_validate') : array(),
   );
   $element['title'] = array(
@@ -367,7 +368,7 @@ function image_field_widget_process($element, &$form_state, $form) {
     '#description' => t('The title is used as a tool tip when the user hovers the mouse over the image.'),
     '#maxlength' => 1024,
     '#weight' => -1,
-    '#access' => (bool) $item['fid'] && $element['#title_field'],
+    '#access' => (bool) $item['fids'] && $element['#title_field'],
     '#element_validate' => $settings['alt_field_required'] == 1 ? array('_image_field_required_fields_validate') : array(),
   );
 
@@ -422,8 +423,9 @@ function theme_image_widget($variables) {
   }
 
   $output .= '<div class="image-widget-data">';
-  if ($element['fid']['#value'] != 0) {
-    $element['filename']['#markup'] .= ' <span class="file-size">(' . format_size($element['#file']->filesize) . ')</span> ';
+  if (!empty($element['fids']['#value'])) {
+    $file = reset($element['#files']);
+    $element['file_' . $file->fid]['filename']['#markup'] .= ' <span class="file-size">(' . format_size($file->filesize) . ')</span> ';
   }
   $output .= drupal_render_children($element);
   $output .= '</div>';
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 956eafd..cc392f6 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -402,8 +402,8 @@ function image_field_delete_field($field) {
   }
 
   // The value of a managed_file element can be an array if #extended == TRUE.
-  $fid = (is_array($field['settings']['default_image']) ? $field['settings']['default_image']['fid'] : $field['settings']['default_image']);
-  if ($fid && ($file = file_load($fid))) {
+  $fid = (isset($field['settings']['default_image']['fids']) ? $field['settings']['default_image']['fids'] : $field['settings']['default_image']);
+  if ($fid && ($file = file_load($fid[0]))) {
     file_usage()->delete($file, 'image', 'default_image', $field['id']);
   }
 }
@@ -417,10 +417,10 @@ function image_field_update_field($field, $prior_field, $has_data) {
   }
 
   // The value of a managed_file element can be an array if #extended == TRUE.
-  $fid_new = (is_array($field['settings']['default_image']) ? $field['settings']['default_image']['fid'] : $field['settings']['default_image']);
-  $fid_old = (is_array($prior_field['settings']['default_image']) ? $prior_field['settings']['default_image']['fid'] : $prior_field['settings']['default_image']);
+  $fid_new = (isset($field['settings']['default_image']['fids']) ? $field['settings']['default_image']['fids'] : $field['settings']['default_image']);
+  $fid_old = (isset($prior_field['settings']['default_image']['fids']) ? $prior_field['settings']['default_image']['fids'] : $prior_field['settings']['default_image']);
 
-  $file_new = $fid_new ? file_load($fid_new) : FALSE;
+  $file_new = $fid_new ? file_load($fid_new[0]) : FALSE;
 
   if ($fid_new != $fid_old) {
 
@@ -432,7 +432,7 @@ function image_field_update_field($field, $prior_field, $has_data) {
     }
 
     // Is there an old file?
-    if ($fid_old && ($file_old = file_load($fid_old))) {
+    if ($fid_old && ($file_old = file_load($fid_old[0]))) {
       file_usage()->delete($file_old, 'image', 'default_image', $field['id']);
     }
   }
@@ -481,16 +481,16 @@ function image_field_update_instance($instance, $prior_instance) {
   // The value of a managed_file element can be an array if the #extended
   // property is set to TRUE.
   $fid_new = $instance['settings']['default_image'];
-  if (is_array($fid_new)) {
-    $fid_new = $fid_new['fid'];
+  if (isset($fid_new['fids'])) {
+    $fid_new = $fid_new['fids'];
   }
   $fid_old = $prior_instance['settings']['default_image'];
-  if (is_array($fid_old)) {
-    $fid_old = $fid_old['fid'];
+  if (isset($fid_old['fids'])) {
+    $fid_old = $fid_old['fids'];
   }
 
   // If the old and new files do not match, update the default accordingly.
-  $file_new = $fid_new ? file_load($fid_new) : FALSE;
+  $file_new = $fid_new ? file_load($fid_new[0]) : FALSE;
   if ($fid_new != $fid_old) {
     // Save the new file, if present.
     if ($file_new) {
@@ -499,7 +499,7 @@ function image_field_update_instance($instance, $prior_instance) {
       file_usage()->add($file_new, 'image', 'default_image', $instance['id']);
     }
     // Delete the old file, if present.
-    if ($fid_old && ($file_old = file_load($fid_old))) {
+    if ($fid_old && ($file_old = file_load($fid_old[0]))) {
       file_usage()->delete($file_old, 'image', 'default_image', $instance['id']);
     }
   }
@@ -1063,4 +1063,3 @@ function image_filter_keyword($value, $current_pixels, $new_pixels) {
 function _image_effect_definitions_sort($a, $b) {
   return strcasecmp($a['name'], $b['name']);
 }
-
diff --git a/core/modules/image/lib/Drupal/image/Plugin/field/widget/ImageWidget.php b/core/modules/image/lib/Drupal/image/Plugin/field/widget/ImageWidget.php
index 35cb3fa..6ba4353 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/field/widget/ImageWidget.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/field/widget/ImageWidget.php
@@ -61,7 +61,7 @@ protected function formMultipleElements(EntityInterface $entity, array $items, $
 
     if ($this->field['cardinality'] == 1) {
       // If there's only one field, return it as delta 0.
-      if (empty($elements[0]['#default_value']['fid'])) {
+      if (empty($elements[0]['#default_value']['fids'])) {
         $elements[0]['#description'] = theme('file_upload_help', array('description' => $this->instance['description'], 'upload_validators' => $elements[0]['#upload_validators']));
       }
     }
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php
index 01fa5f4..a30b7ed 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php
@@ -43,10 +43,10 @@ function testDefaultImages() {
     // Create an image field and add an instance to the article content type.
     $field_name = strtolower($this->randomName());
     $field_settings = array(
-      'default_image' => $default_images['field']->fid,
+      'default_image' => array($default_images['field']->fid),
     );
     $instance_settings = array(
-      'default_image' => $default_images['instance']->fid,
+      'default_image' => array($default_images['instance']->fid),
     );
     $widget_settings = array(
       'preview_image_style' => 'medium',
@@ -63,7 +63,7 @@ function testDefaultImages() {
       'label' => $instance['label'],
       'required' => $instance['required'],
       'settings' => array(
-        'default_image' => $default_images['instance2']->fid,
+        'default_image' => array($default_images['instance2']->fid),
       ),
       'widget' => $instance['widget'],
     );
@@ -76,7 +76,7 @@ function testDefaultImages() {
     // Confirm the defaults are present on the article field settings form.
     $this->drupalGet("admin/structure/types/manage/article/fields/$field_name/field-settings");
     $this->assertFieldByXpath(
-      '//input[@name="field[settings][default_image][fid]"]',
+      '//input[@name="field[settings][default_image][fids]"]',
       $default_images['field']->fid,
       format_string(
         'Article image field default equals expected file ID of @fid.',
@@ -86,7 +86,7 @@ function testDefaultImages() {
     // Confirm the defaults are present on the article field edit form.
     $this->drupalGet("admin/structure/types/manage/article/fields/$field_name");
     $this->assertFieldByXpath(
-      '//input[@name="instance[settings][default_image][fid]"]',
+      '//input[@name="instance[settings][default_image][fids]"]',
       $default_images['instance']->fid,
       format_string(
         'Article image field instance default equals expected file ID of @fid.',
@@ -97,7 +97,7 @@ function testDefaultImages() {
     // Confirm the defaults are present on the page field settings form.
     $this->drupalGet("admin/structure/types/manage/page/fields/$field_name/field-settings");
     $this->assertFieldByXpath(
-      '//input[@name="field[settings][default_image][fid]"]',
+      '//input[@name="field[settings][default_image][fids]"]',
       $default_images['field']->fid,
       format_string(
         'Page image field default equals expected file ID of @fid.',
@@ -107,7 +107,7 @@ function testDefaultImages() {
     // Confirm the defaults are present on the page field edit form.
     $this->drupalGet("admin/structure/types/manage/page/fields/$field_name");
     $this->assertFieldByXpath(
-      '//input[@name="instance[settings][default_image][fid]"]',
+      '//input[@name="instance[settings][default_image][fids]"]',
       $default_images['instance2']->fid,
       format_string(
         'Page image field instance default equals expected file ID of @fid.',
@@ -140,13 +140,13 @@ function testDefaultImages() {
     );
 
     // Upload a new default for the field.
-    $field['settings']['default_image'] = $default_images['field_new']->fid;
+    $field['settings']['default_image'] = array($default_images['field_new']->fid);
     field_update_field($field);
 
     // Confirm that the new default is used on the article field settings form.
     $this->drupalGet("admin/structure/types/manage/article/fields/$field_name/field-settings");
     $this->assertFieldByXpath(
-      '//input[@name="field[settings][default_image][fid]"]',
+      '//input[@name="field[settings][default_image][fids]"]',
       $default_images['field_new']->fid,
       format_string(
         'Updated image field default equals expected file ID of @fid.',
@@ -175,14 +175,14 @@ function testDefaultImages() {
     );
 
     // Upload a new default for the article's field instance.
-    $instance['settings']['default_image'] = $default_images['instance_new']->fid;
+    $instance['settings']['default_image'] = array($default_images['instance_new']->fid);
     field_update_instance($instance);
 
     // Confirm the new field instance default is used on the article field
     // admin form.
     $this->drupalGet("admin/structure/types/manage/article/fields/$field_name");
     $this->assertFieldByXpath(
-      '//input[@name="instance[settings][default_image][fid]"]',
+      '//input[@name="instance[settings][default_image][fids]"]',
       $default_images['instance_new']->fid,
       format_string(
         'Updated article image field instance default equals expected file ID of @fid.',
@@ -220,7 +220,7 @@ function testDefaultImages() {
     // Confirm the article field instance default has been removed.
     $this->drupalGet("admin/structure/types/manage/article/fields/$field_name");
     $this->assertFieldByXpath(
-      '//input[@name="instance[settings][default_image][fid]"]',
+      '//input[@name="instance[settings][default_image][fids]"]',
       '',
       'Updated article image field instance default has been successfully removed.'
     );
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php
index 7c42a5b..743237a 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php
@@ -227,13 +227,13 @@ function testImageFieldDefaultImage() {
     // Add a default image to the public imagefield instance.
     $images = $this->drupalGetTestFiles('image');
     $edit = array(
-      'files[field_settings_default_image]' => drupal_realpath($images[0]->uri),
+      'files[field_settings_default_image][]' => drupal_realpath($images[0]->uri),
     );
     $this->drupalPost("admin/structure/types/manage/article/fields/$field_name/field-settings", $edit, t('Save field settings'));
     // Clear field info cache so the new default image is detected.
     field_info_cache_clear();
     $field = field_info_field($field_name);
-    $image = file_load($field['settings']['default_image']);
+    $image = file_load($field['settings']['default_image'][0]);
     $this->assertTrue($image->status == FILE_STATUS_PERMANENT, 'The default image status is permanent.');
     $default_output = theme('image', array('uri' => $image->uri));
     $this->drupalGet('node/' . $node->nid);
@@ -255,7 +255,7 @@ function testImageFieldDefaultImage() {
 
     // Remove default image from the field and make sure it is no longer used.
     $edit = array(
-      'field[settings][default_image][fid]' => 0,
+      'field[settings][default_image][fids]' => 0,
     );
     $this->drupalPost("admin/structure/types/manage/article/fields/$field_name/field-settings", $edit, t('Save field settings'));
     // Clear field info cache so the new default image is detected.
@@ -268,14 +268,14 @@ function testImageFieldDefaultImage() {
     $this->createImageField($private_field_name, 'article', array('uri_scheme' => 'private'));
     // Add a default image to the new field.
     $edit = array(
-      'files[field_settings_default_image]' => drupal_realpath($images[1]->uri),
+      'files[field_settings_default_image][]' => drupal_realpath($images[1]->uri),
     );
     $this->drupalPost('admin/structure/types/manage/article/fields/' . $private_field_name . '/field-settings', $edit, t('Save field settings'));
     // Clear field info cache so the new default image is detected.
     field_info_cache_clear();
 
     $private_field = field_info_field($private_field_name);
-    $image = file_load($private_field['settings']['default_image']);
+    $image = file_load($private_field['settings']['default_image'][0]);
     $this->assertEqual('private', file_uri_scheme($image->uri), 'Default image uses private:// scheme.');
     $this->assertTrue($image->status == FILE_STATUS_PERMANENT, 'The default image status is permanent.');
     // Create a new node with no image attached and ensure that default private
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php
index 5a4e20e..e8518ab 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php
@@ -117,7 +117,7 @@ function uploadNodeImage($image, $field_name, $type) {
     $edit = array(
       'title' => $this->randomName(),
     );
-    $edit['files[' . $field_name . '_' . LANGUAGE_NOT_SPECIFIED . '_0]'] = drupal_realpath($image->uri);
+    $edit['files[' . $field_name . '_' . LANGUAGE_NOT_SPECIFIED . '_0][]'] = drupal_realpath($image->uri);
     $this->drupalPost('node/add/' . $type, $edit, t('Save and publish'));
 
     // Retrieve ID of the newly created node from the current URL.
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleExportTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleExportTest.php
index 7ae1bf7..4f2748b 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleExportTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleExportTest.php
@@ -55,7 +55,7 @@ function testExportTranslation() {
     file_put_contents($name, $this->getPoFile());
     $this->drupalPost('admin/config/regional/translate/import', array(
       'langcode' => 'fr',
-      'files[file]' => $name,
+      'files[file][]' => $name,
     ), t('Import'));
     drupal_unlink($name);
 
@@ -74,7 +74,7 @@ function testExportTranslation() {
     file_put_contents($name, $this->getCustomPoFile());
     $this->drupalPost('admin/config/regional/translate/import', array(
       'langcode' => 'fr',
-      'files[file]' => $name,
+      'files[file][]' => $name,
       'customized' => 1,
     ), t('Import'));
     drupal_unlink($name);
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php
index fd3667c..dcd7bf2 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php
@@ -91,7 +91,7 @@ function testStandalonePoFile() {
     $name = $this->randomName(16);
     $this->drupalPost('admin/config/regional/translate/import', array(
       'langcode' => 'fr',
-      'files[file]' => $name,
+      'files[file][]' => $name,
     ), t('Import'));
     $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/import', array('absolute' => TRUE)), t('Correct page redirection.'));
     $this->assertText(t('File to import not found.'), t('File to import not found message.'));
@@ -248,7 +248,7 @@ function testEmptyMsgstr() {
   function importPoFile($contents, array $options = array()) {
     $name = tempnam('temporary://', "po_") . '.po';
     file_put_contents($name, $contents);
-    $options['files[file]'] = $name;
+    $options['files[file][]'] = $name;
     $this->drupalPost('admin/config/regional/translate/import', $options, t('Import'));
     drupal_unlink($name);
   }
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php
index 8119199..069dc4e 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php
@@ -268,7 +268,7 @@ function testPluralEditExport() {
   function importPoFile($contents, array $options = array()) {
     $name = tempnam('temporary://', "po_") . '.po';
     file_put_contents($name, $contents);
-    $options['files[file]'] = $name;
+    $options['files[file][]'] = $name;
     $this->drupalPost('admin/config/regional/translate/import', $options, t('Import'));
     drupal_unlink($name);
   }
diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc
index 6a452d8..49273d8 100644
--- a/core/modules/locale/locale.bulk.inc
+++ b/core/modules/locale/locale.bulk.inc
@@ -108,7 +108,7 @@ function locale_translate_import_form($form, &$form_state) {
  */
 function locale_translate_import_form_submit($form, &$form_state) {
   // Ensure we have the file uploaded.
-  if ($file = file_save_upload('file', $form['file']['#upload_validators'], 'translations://')) {
+  if ($file = file_save_upload('file', $form['file']['#upload_validators'], 'translations://', 0)) {
 
     // Add language, if not yet supported.
     $language = language_load($form_state['values']['langcode']);
diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php
index 1207238..e578310 100644
--- a/core/modules/node/node.api.php
+++ b/core/modules/node/node.api.php
@@ -1115,7 +1115,7 @@ function hook_delete(Drupal\node\Node $node) {
  */
 function hook_prepare(Drupal\node\Node $node) {
   if ($file = file_check_upload($field_name)) {
-    $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE));
+    $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE), FALSE, 0);
     if ($file) {
       if (!image_get_info($file->uri)) {
         form_set_error($field_name, t('Uploaded file is not a valid image'));
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php
index 6f99dae..589ad4b 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php
@@ -139,8 +139,8 @@ function testAttributesInMarkupFile() {
 
     // Create an array for drupalPost with the field names as the keys and
     // the URIs for the test files as the values.
-    $edit = array("files[" . $field_name . "_" . $langcode . "_0]" => drupal_realpath($file->uri),
-                  "files[" . $image_field . "_" . $langcode . "_0]" => drupal_realpath($image->uri));
+    $edit = array("files[" . $field_name . "_" . $langcode . "_0][]" => drupal_realpath($file->uri),
+                  "files[" . $image_field . "_" . $langcode . "_0][]" => drupal_realpath($image->uri));
 
     // Create node and save, then edit node to upload files.
     $node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1));
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/StateValuesCleanAdvancedTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/StateValuesCleanAdvancedTest.php
index ceae2af..b7c8e22 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/StateValuesCleanAdvancedTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/StateValuesCleanAdvancedTest.php
@@ -47,7 +47,7 @@ function testFormStateValuesCleanAdvanced() {
     $this->assertTrue(is_file($this->image->uri), "The image file we're going to upload exists.");
 
     // "Browse" for the desired file.
-    $edit = array('files[image]' => drupal_realpath($this->image->uri));
+    $edit = array('files[image][]' => drupal_realpath($this->image->uri));
 
     // Post the form.
     $this->drupalPost('form_test/form-state-values-clean-advanced', $edit, t('Submit'));
diff --git a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php
index 8cf14d5..6a6884f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php
@@ -155,7 +155,7 @@ function testThemeSettings() {
     $edit = array(
       'default_logo' => FALSE,
       'logo_path' => '',
-      'files[logo_upload]' => drupal_realpath($file->uri),
+      'files[logo_upload][]' => drupal_realpath($file->uri),
     );
     $this->drupalPost('admin/appearance/settings', $edit, t('Save configuration'));
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php
index 04070c9..c26dd86 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php
@@ -40,8 +40,8 @@ public function testUserPictureUpgrade() {
 
     // Retrieve the field instance and check for migrated settings.
     $instance = field_info_instance('user', 'user_picture', 'user');
-    $file = entity_load('file', $instance['settings']['default_image']);
-    $this->assertIdentical($instance['settings']['default_image'], $file->id(), 'Default user picture has been migrated.');
+    $file = entity_load('file', $instance['settings']['default_image'][0]);
+    $this->assertIdentical($instance['settings']['default_image'][0], $file->id(), 'Default user picture has been migrated.');
     $this->assertEqual($file->uri, 'public://user_pictures_dir/druplicon.png', 'File id matches the uri expected.');
     $this->assertEqual($file->filename, 'druplicon.png');
     $this->assertEqual($file->langcode, LANGUAGE_NOT_SPECIFIED);
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 6583d57..98f5702 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -615,7 +615,7 @@ function system_theme_settings_validate($form, &$form_state) {
     $validators = array('file_validate_is_image' => array());
 
     // Check for a new uploaded logo.
-    $file = file_save_upload('logo_upload', $validators);
+    $file = file_save_upload('logo_upload', $validators, FALSE, 0);
     if (isset($file)) {
       // File upload was attempted.
       if ($file) {
@@ -631,7 +631,7 @@ function system_theme_settings_validate($form, &$form_state) {
     $validators = array('file_validate_extensions' => array('ico png gif jpg jpeg apng svg'));
 
     // Check for a new uploaded favicon.
-    $file = file_save_upload('favicon_upload', $validators);
+    $file = file_save_upload('favicon_upload', $validators, FALSE, 0);
     if (isset($file)) {
       // File upload was attempted.
       if ($file) {
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 223af2e..9d12c43 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -496,6 +496,8 @@ function system_element_info() {
   );
   $types['file'] = array(
     '#input' => TRUE,
+    '#multiple' => FALSE,
+    '#process' => array('form_process_file'),
     '#size' => 60,
     '#pre_render' => array('form_pre_render_file'),
     '#theme' => 'input__file',
diff --git a/core/modules/update/lib/Drupal/update/Tests/UpdateUploadTest.php b/core/modules/update/lib/Drupal/update/Tests/UpdateUploadTest.php
index d3bcdb8..0a8db21 100644
--- a/core/modules/update/lib/Drupal/update/Tests/UpdateUploadTest.php
+++ b/core/modules/update/lib/Drupal/update/Tests/UpdateUploadTest.php
@@ -44,7 +44,7 @@ public function testUploadModule() {
     $imageTestFiles = $this->drupalGetTestFiles('image');
     $invalidArchiveFile = reset($imageTestFiles);
     $edit = array(
-      'files[project_upload]' => $invalidArchiveFile->uri,
+      'files[project_upload][]' => $invalidArchiveFile->uri,
     );
     // This also checks that the correct archive extensions are allowed.
     $this->drupalPost('admin/modules/install', $edit, t('Install'));
@@ -55,7 +55,7 @@ public function testUploadModule() {
     // installed until after extraction.
     $validArchiveFile = drupal_get_path('module', 'update') . '/tests/aaa_update_test.tar.gz';
     $edit = array(
-      'files[project_upload]' => $validArchiveFile,
+      'files[project_upload][]' => $validArchiveFile,
     );
     $this->drupalPost('admin/modules/install', $edit, t('Install'));
     $this->assertText(t('@module_name is already installed.', array('@module_name' => 'AAA Update test')), 'Existing module was extracted and not reinstalled.');
diff --git a/core/modules/update/update.manager.inc b/core/modules/update/update.manager.inc
index 0047a52..bc84058 100644
--- a/core/modules/update/update.manager.inc
+++ b/core/modules/update/update.manager.inc
@@ -645,7 +645,7 @@ function update_manager_install_form_submit($form, &$form_state) {
   elseif ($_FILES['files']['name']['project_upload']) {
     $validators = array('file_validate_extensions' => array(archiver_get_extensions()));
     $field = 'project_upload';
-    if (!($finfo = file_save_upload($field, $validators, NULL, FILE_EXISTS_REPLACE))) {
+    if (!($finfo = file_save_upload($field, $validators, NULL, 0, FILE_EXISTS_REPLACE))) {
       // Failed to upload the file. file_save_upload() calls form_set_error() on
       // failure.
       return;
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserPictureTest.php b/core/modules/user/lib/Drupal/user/Tests/UserPictureTest.php
index 8447031..fde2fbc 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserPictureTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserPictureTest.php
@@ -117,7 +117,7 @@ function testPictureOnNodeComment() {
    * Edits the user picture for the test user.
    */
   function saveUserPicture($image) {
-    $edit = array('files[user_picture_und_0]' => drupal_realpath($image->uri));
+    $edit = array('files[user_picture_und_0][]' => drupal_realpath($image->uri));
     $this->drupalPost('user/' . $this->web_user->uid . '/edit', $edit, t('Save'));
 
     // Load actual user data from database.
