diff --git a/core/includes/file.inc b/core/includes/file.inc
index 133d64f..cc72644 100644
--- a/core/includes/file.inc
+++ b/core/includes/file.inc
@@ -2304,9 +2304,12 @@ function drupal_basename($uri, $suffix = NULL) {
  * @param $uri
  *   A URI or pathname.
  * @param $mode
- *   By default the Drupal mode is used.
+ *   By default the Drupal mode is used. Note that you probably want to
+ *   specify the mode as an octal number, which means it should have a
+ *   leading zero. Also note that Drupal does NOT obey umask().
  * @param $recursive
- *   Default to FALSE.
+ *   Create directories recursively, defaults to FALSE. Note: recursive mkdir
+ *   does not work with a $mode which denies writing to the owner.
  * @param $context
  *   Refer to http://php.net/manual/ref.stream.php
  *
@@ -2321,13 +2324,71 @@ function drupal_mkdir($uri, $mode = NULL, $recursive = FALSE, $context = NULL) {
   if (!isset($mode)) {
     $mode = variable_get('file_chmod_directory', 0775);
   }
+  return file_uri_scheme($uri) ? _drupal_mkdir_call($uri, $mode, $recursive, $context) : _drupal_mkdir_local($uri, $mode, $recursive, $context);
+}
 
-  if (!isset($context)) {
+/**
+ * Ensures we don't pass a NULL as a context resource to mkdir().
+ *
+ * {@inheritdoc}
+ */
+function _drupal_mkdir_call($uri, $mode, $recursive, $context) {
+  if (isset($context)) {
+    return mkdir($uri, $mode, $recursive, $context);
+  }
+  else {
     return mkdir($uri, $mode, $recursive);
   }
+}
+
+/**
+ * Creates one or more directories from a local path.
+ *
+ * @param string $localpath
+ *   A local pathname.
+ * @param integer $mode
+ *   The mode will be passed only to new directories in the path. Defaults to
+ *   Drupal mode.
+ * @param boolean $recursive
+ *   Whether or not directories should be created recursively.
+ * @param resource $context
+ *   Refer to http://php.net/manual/ref.stream.php
+ *
+ * @return boolean
+ *   TRUE on success, or FALSE on failure.
+ */
+function _drupal_mkdir_local($localpath, $mode = NULL, $recursive = FALSE, $context = NULL) {
+  $mode = isset($mode) ? $mode : variable_get('file_chmod_directory', 0775);
+
+  // Note this does not literally recurse because we must create directories
+  // first, then change mode in reverse, to fix a PHP mkdir() bug.
+  // See http://drupal.org/node/1068266 for reasoning.
+  if ($recursive) {
+    $directories = explode(DIRECTORY_SEPARATOR, $localpath);
+    $directory_path = '';
+    foreach ($directories as $directory) {
+      $directory_path = $directory_path ? $directory_path . DIRECTORY_SEPARATOR . $directory : $directory;
+      if (!file_exists($directory_path)) {
+        if (!_drupal_mkdir_call($directory_path, 0777, FALSE, $context)) {
+          break;
+        }
+        // We can't rely on mkdir() setting the mode recursively because mkdir
+        // (correctly) obeys umask() but we can't run umask() because it is set
+        // on a process level:
+        // @see http://php.net/manual/en/function.umask.php#105150
+        // Therefore, sadly, we need to chmod after the dirs are created.
+        if (!chmod($directory_path, $mode)) {
+          break;
+        }
+      }
+    }
+  }
   else {
-    return mkdir($uri, $mode, $recursive, $context);
+    // If this is not recursive, PHP's mkdir() needs no adjustment.
+    return _drupal_mkdir_call($localpath, $mode, $recursive, $context);
   }
+
+  return $success;
 }
 
 /**
diff --git a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php
index b6e1dd1..e66d0f2 100644
--- a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php
+++ b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php
@@ -425,10 +425,10 @@ abstract class LocalStream implements StreamWrapperInterface {
       $localpath = $this->getLocalPath($uri);
     }
     if ($options & STREAM_REPORT_ERRORS) {
-      return mkdir($localpath, $mode, $recursive);
+      return _drupal_mkdir_local($localpath, $mode, $recursive);
     }
     else {
-      return @mkdir($localpath, $mode, $recursive);
+      return @_drupal_mkdir_local($localpath, $mode, $recursive);
     }
   }
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php b/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php
index 5d57bdf..d25e823 100644
--- a/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php
@@ -20,6 +20,39 @@ class DirectoryTest extends FileTestBase {
   }
 
   /**
+   * Test local directory handling functions.
+   */
+  function testFileCheckLocalDirectoryHandling() {
+    $directory = conf_path() . '/files';
+
+    // Check a new recursively created local directory for correct file system
+    // permissions.
+    $parent = $this->randomName();
+    $child = $this->randomName();
+
+    // Files directory already exists.
+    $this->assertTrue(is_dir($directory), t('Files directory already exists.'), 'File');
+    // Make files directory writable only.
+    $old_mode = fileperms($directory);
+
+    // Create the directories.
+    $parent_path = $directory . DIRECTORY_SEPARATOR . $parent;
+    $child_path = $parent_path . DIRECTORY_SEPARATOR . $child;
+    $this->assertTrue(drupal_mkdir($child_path, 0444), t('No error reported when creating new local directories.'), 'File');
+
+    // Ensure new directories also exist.
+    $this->assertTrue(is_dir($parent_path), t('New parent directory actually exists.'), 'File');
+    $this->assertTrue(is_dir($child_path), t('New child directory actually exists.'), 'File');
+
+    // Check that new directory permissions were set properly.
+    $this->assertDirectoryPermissions($parent_path, 0444);
+    $this->assertDirectoryPermissions($child_path, 0444);
+
+    // Check that existing directory permissions were not modified.
+    $this->assertDirectoryPermissions($directory, $old_mode);
+  }
+
+  /**
    * Test directory handling functions.
    */
   function testFileCheckDirectoryHandling() {
