From f09ba6bc280678582461608a6c09e40322d14ed2 Mon Sep 17 00:00:00 2001
From: Michael Orlitzky <michael@orlitzky.com>
Date: Fri, 9 Nov 2018 09:10:04 -0500
Subject: [PATCH 1/1] Fix Drupal (v7.65) issue #1899126, "Add wrappers to fix
 permission checks."

---
 includes/file.inc               | 45 +++++++++++++++++++++++++++++++--
 includes/updater.inc            |  2 +-
 modules/system/system.admin.inc |  2 +-
 modules/system/system.install   |  2 +-
 modules/system/system.module    |  2 +-
 modules/user/user.test          |  2 +-
 6 files changed, 48 insertions(+), 7 deletions(-)

diff --git a/includes/file.inc b/includes/file.inc
index 95bb658..639c3c8 100644
--- a/includes/file.inc
+++ b/includes/file.inc
@@ -445,7 +445,7 @@ function file_prepare_directory(&$directory, $options = FILE_MODIFY_PERMISSIONS)
     return FALSE;
   }
   // The directory exists, so check to see if it is writable.
-  $writable = is_writable($directory);
+  $writable = file_directory_is_writable($directory);
   if (!$writable && ($options & FILE_MODIFY_PERMISSIONS)) {
     return drupal_chmod($directory);
   }
@@ -2270,6 +2270,26 @@ function file_get_mimetype($uri, $mapping = NULL) {
   }
 }
 
+
+/**
+ * Determines if a directory is writable by the web server.
+ *
+ * In order to be able to write files within the directory, the directory
+ * itself must be writable, and it must also have the executable bit set. This
+ * helper function checks both at the same time.
+ *
+ * @param $uri
+ *   A URI or pathname pointing to the directory that will be checked.
+ *
+ * @return
+ *   TRUE if the directory is writable and executable; FALSE otherwise.
+ */
+function file_directory_is_writable($uri) {
+  // By converting the URI to a normal path using drupal_realpath(), we can
+  // correctly handle both stream wrappers and normal paths.
+  return is_writable(drupal_realpath($uri)) && drupal_is_executable($uri);
+}
+
 /**
  * Sets the permissions on a file or directory.
  *
@@ -2451,6 +2471,27 @@ function drupal_basename($uri, $suffix = NULL) {
   return $filename;
 }
 
+/**
+ * Determines if a file or directory is executable.
+ *
+ * PHP's is_executable() does not fully support stream wrappers, so this
+ * function fills that gap.
+ *
+ * @param $uri
+ *   A URI or pathname pointing to the file or directory that will be checked.
+ *
+ * @return
+ *   TRUE if the file or directory is executable; FALSE otherwise.
+ *
+ * @see is_executable()
+ * @ingroup php_wrappers
+ */
+function drupal_is_executable($uri) {
+  // By converting the URI to a normal path using drupal_realpath(), we can
+  // correctly handle both stream wrappers and normal paths.
+  return is_executable(drupal_realpath($uri));
+}
+
 /**
  * Creates a directory using Drupal's default mode.
  *
@@ -2589,7 +2630,7 @@ function file_directory_temp() {
     }
 
     foreach ($directories as $directory) {
-      if (is_dir($directory) && is_writable($directory)) {
+      if (is_dir($directory) && file_directory_is_writable($directory)) {
         $temporary_directory = $directory;
         break;
       }
diff --git a/includes/updater.inc b/includes/updater.inc
index d4f99e9..4b03a26 100644
--- a/includes/updater.inc
+++ b/includes/updater.inc
@@ -351,7 +351,7 @@ class Updater {
    *   If the chmod should be applied recursively.
    */
   public function makeWorldReadable(&$filetransfer, $path, $recursive = TRUE) {
-    if (!is_executable($path)) {
+    if (!drupal_is_executable($path)) {
       // Set it to read + execute.
       $new_perms = substr(sprintf('%o', fileperms($path)), -4, -1) . "5";
       $filetransfer->chmod($path, intval($new_perms, 8), $recursive);
diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc
index b7e6fc9..7277ddc 100644
--- a/modules/system/system.admin.inc
+++ b/modules/system/system.admin.inc
@@ -1730,7 +1730,7 @@ function system_performance_settings() {
   );
 
   $directory = 'public://';
-  $is_writable = is_dir($directory) && is_writable($directory);
+  $is_writable = is_dir($directory) && file_directory_is_writable($directory);
   $disabled = !$is_writable;
   $disabled_message = '';
   if (!$is_writable) {
diff --git a/modules/system/system.install b/modules/system/system.install
index 9ad153c..0ad57aa 100644
--- a/modules/system/system.install
+++ b/modules/system/system.install
@@ -365,7 +365,7 @@ function system_requirements($phase) {
     if ($phase == 'install') {
       file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
     }
-    $is_writable = is_writable($directory);
+    $is_writable = file_directory_is_writable($directory);
     $is_directory = is_dir($directory);
     if (!$is_writable || !$is_directory) {
       $description = '';
diff --git a/modules/system/system.module b/modules/system/system.module
index 53844d8..bea5493 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -2190,7 +2190,7 @@ function system_check_directory($form_element) {
     watchdog('file system', 'The directory %directory does not exist and could not be created.', array('%directory' => $directory), WATCHDOG_ERROR);
   }
 
-  if (is_dir($directory) && !is_writable($directory) && !drupal_chmod($directory)) {
+  if (is_dir($directory) && !file_directory_is_writable($directory) && !drupal_chmod($directory)) {
     // If the directory is not writable and cannot be made so.
     form_set_error($form_element['#parents'][0], t('The directory %directory exists but is not writable and could not be made writable.', array('%directory' => $directory)));
     watchdog('file system', 'The directory %directory exists but is not writable and could not be made writable.', array('%directory' => $directory), WATCHDOG_ERROR);
diff --git a/modules/user/user.test b/modules/user/user.test
index fb82c93..37da496 100644
--- a/modules/user/user.test
+++ b/modules/user/user.test
@@ -1100,7 +1100,7 @@ class UserPictureTestCase extends DrupalWebTestCase {
     $picture_path = $file_dir . $picture_dir;
 
     $pic_check = file_prepare_directory($picture_path, FILE_CREATE_DIRECTORY);
-    $this->_directory_test = is_writable($picture_path);
+    $this->_directory_test = file_directory_is_writable($picture_path);
     $this->assertTrue($this->_directory_test, "The directory $picture_path doesn't exist or is not writable. Further tests won't be made.");
   }
 
-- 
2.19.2

