From 80ecab6fab670ff85339bb204b4c951971e5fd0d Mon Sep 17 00:00:00 2001
From: Paul Biron <paul@sparrowhawkcomputing.com>
Date: Thu, 6 May 2021 19:02:02 -0600
Subject: [PATCH] Refresh patch, and incorporate the Windows-specific changes
 to WP_Filesystem_Direct::delete() from
 https://core.trac.wordpress.org/attachment/ticket/36710/36710.6.diff.

---
 .../includes/class-wp-filesystem-direct.php   | 32 ++++++++++++++++---
 src/wp-admin/includes/class-wp-upgrader.php   |  4 ++-
 2 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/src/wp-admin/includes/class-wp-filesystem-direct.php b/src/wp-admin/includes/class-wp-filesystem-direct.php
index 099d9814af..661e52fd1d 100644
--- a/src/wp-admin/includes/class-wp-filesystem-direct.php
+++ b/src/wp-admin/includes/class-wp-filesystem-direct.php
@@ -365,8 +365,16 @@ class WP_Filesystem_Direct extends WP_Filesystem_Base {
 
 		$file = str_replace( '\\', '/', $file ); // For Win32, occasional problems deleting files otherwise.
 
-		if ( 'f' === $type || $this->is_file( $file ) ) {
-			return @unlink( $file );
+		if ( 'f' === $type || $this->is_file( $file ) || $this->is_link( $file ) ) {
+			if ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) &&
+					$this->is_link( $file ) && $this->is_dir( $file ) ) {
+				// on windows unlink()'ing a symlink that points to a directory fails.
+				// @link https://bugs.php.net/bug.php?id=52176
+				return @rmdir( $file );
+			} else {
+				// untrailingslashit() needed to remove symlinks successfully.
+				return @unlink( untrailingslashit( $file ) );
+			}
 		}
 
 		if ( ! $recursive && $this->is_dir( $file ) ) {
@@ -454,6 +462,19 @@ class WP_Filesystem_Direct extends WP_Filesystem_Base {
 		return @is_writable( $file );
 	}
 
+	/**
+	 * Checks if a file or directory is a symbolic link.
+	 *
+	 * @since 5.8.0
+	 *
+	 * @param string $file Path to file or directory.
+	 * @return bool Whether $file is a symbolic link.
+	 */
+	public function is_link( $file ) {
+		// Strip trailing slashes, to avoid directory symlinks resolving.
+		return @is_link( untrailingslashit( $file ) );
+	}
+
 	/**
 	 * Gets the file's last access time.
 	 *
@@ -599,7 +620,10 @@ class WP_Filesystem_Direct extends WP_Filesystem_Base {
 	 * }
 	 */
 	public function dirlist( $path, $include_hidden = true, $recursive = false ) {
-		if ( $this->is_file( $path ) ) {
+		if ( $this->is_dir( $path ) && $this->is_link( $path ) ) {
+			// Directory is symlink, therefore return no listing.
+			return array();
+		} elseif ( $this->is_file( $path ) || $this->is_link( $path ) ) {
 			$limit_file = basename( $path );
 			$path       = dirname( $path );
 		} else {
@@ -645,7 +669,7 @@ class WP_Filesystem_Direct extends WP_Filesystem_Base {
 			$struc['time']        = gmdate( 'h:i:s', $struc['lastmodunix'] );
 			$struc['type']        = $this->is_dir( $path . '/' . $entry ) ? 'd' : 'f';
 
-			if ( 'd' === $struc['type'] ) {
+			if ( 'd' === $struc['type'] && !$this->is_link( $path . '/' . $struc['name'] )) {
 				if ( $recursive ) {
 					$struc['files'] = $this->dirlist( $path . '/' . $struc['name'], $include_hidden, $recursive );
 				} else {
diff --git a/src/wp-admin/includes/class-wp-upgrader.php b/src/wp-admin/includes/class-wp-upgrader.php
index e456191dae..3f870d3925 100644
--- a/src/wp-admin/includes/class-wp-upgrader.php
+++ b/src/wp-admin/includes/class-wp-upgrader.php
@@ -402,7 +402,9 @@ class WP_Upgrader {
 			if ( ! $wp_filesystem->is_writable( $remote_destination . $filename ) ) {
 				// Attempt to alter permissions to allow writes and try again.
 				$wp_filesystem->chmod( $remote_destination . $filename, ( 'd' === $file_details['type'] ? FS_CHMOD_DIR : FS_CHMOD_FILE ) );
-				if ( ! $wp_filesystem->is_writable( $remote_destination . $filename ) ) {
+				// is_writable() returns false on symlinks for some reason.
+				if ( ! $wp_filesystem->is_writable( $remote_destination . $filename )
+						&& ! $wp_filesystem->is_link( $remote_destination . $filename ) ) {
 					$unwritable_files[] = $filename;
 				}
 			}
-- 
2.30.2.windows.1

