core/includes/file.inc | 95 ++++++++------------ core/lib/Drupal/Core/StreamWrapper/LocalStream.php | 4 +- .../lib/Drupal/system/Tests/File/DirectoryTest.php | 11 +-- 3 files changed, 41 insertions(+), 69 deletions(-) diff --git a/core/includes/file.inc b/core/includes/file.inc index 131fe58..a56d3bd 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -1709,22 +1709,19 @@ function drupal_basename($uri, $suffix = NULL) { } /** - * Creates a directory using Drupal's default mode. + * Creates a directory. * - * PHP's mkdir() does not respect Drupal's default permissions mode. If a mode - * is not provided, this function will make sure that Drupal's is used. - * - * Compatibility: normal paths and stream wrappers. + * PHP's mkdir() does not default to the Drupal directory mode and only sets the + * mode on the top-level directory when creating recursively. * * @param $uri * A URI or pathname. * @param $mode - * 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(). + * Mode given to created directories. By default the Drupal mode is used. It + * must have a leading zero. * @param $recursive - * Create directories recursively, defaults to FALSE. Note: recursive mkdir - * does not work with a $mode which denies writing to the owner. + * Create directories recursively, defaults to FALSE. Cannot work with a $mode + * which denies writing or execution to the owner of the process. * @param $context * Refer to http://php.net/manual/ref.stream.php * @@ -1739,13 +1736,39 @@ 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 recursive and there is no URI scheme, create each missing component of + // the parent directory individually so they are created with the correct + // mode. Schemes can handle this issue in their own implementation. + if ($recursive && !file_uri_scheme($uri)) { + $components = explode(DIRECTORY_SEPARATOR, $uri); + array_pop($components); + $recursive_path = ''; + foreach ($components as $component) { + $recursive_path .= $component; + + if (!file_exists($recursive_path)) { + if (!_drupal_mkdir_call($recursive_path, $mode, FALSE, $context)) { + return FALSE; + } + } + + $recursive_path .= DIRECTORY_SEPARATOR; + } + + // The top-level directory must not exist yet as it needs to be created with + // the correct mode, so fall through to the final call. + $recursive = FALSE; + } + + return _drupal_mkdir_call($uri, $mode, $recursive, $context); } /** - * Ensures we don't pass a NULL as a context resource to mkdir(). + * Helper function. Ensures we don't pass a NULL as a context resource to + * mkdir(). * - * {@inheritdoc} + * @see drupal_mkdir() */ function _drupal_mkdir_call($uri, $mode, $recursive, $context) { if (isset($context)) { @@ -1757,52 +1780,6 @@ function _drupal_mkdir_call($uri, $mode, $recursive, $context) { } /** - * 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); - - 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, $mode, FALSE, $context)) { - return FALSE; - } - // 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)) { - return FALSE; - } - } - } - return TRUE; - } - else { - // If this is not recursive, PHP's mkdir() needs no adjustment. - return _drupal_mkdir_call($localpath, $mode, $recursive, $context); - } -} - -/** * Removes a directory. * * PHP's rmdir() is broken on Windows, as it can fail to remove a directory diff --git a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php index 570175e..f84ecb1 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 _drupal_mkdir_local($localpath, $mode, $recursive); + return drupal_mkdir($localpath, $mode, $recursive); } else { - return @_drupal_mkdir_local($localpath, $mode, $recursive); + return @drupal_mkdir($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 d25e823..e2b0f39 100644 --- a/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php @@ -32,24 +32,19 @@ class DirectoryTest extends FileTestBase { // 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'); + $this->assertTrue(drupal_mkdir($child_path, 0755, TRUE), 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); + $this->assertDirectoryPermissions($parent_path, 0755); + $this->assertDirectoryPermissions($child_path, 0755); } /**