When updating plugins/themes verify that the files to be deleted can be modified before starting the deletion process.

This will avoid partially deleting an item during update which has inconsistent permissions.
This change only affects those using the direct & ssh transports as FTP's is_writable() currently always returns `true`.
Fixes #30921

Built from https://develop.svn.wordpress.org/trunk@32854


git-svn-id: http://core.svn.wordpress.org/trunk@32825 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Dion Hulse 2015-06-19 03:49:25 +00:00
parent a21f890abb
commit 8593dc3172
2 changed files with 65 additions and 10 deletions

View File

@ -132,6 +132,7 @@ class WP_Upgrader {
$this->strings['folder_exists'] = __('Destination folder already exists.');
$this->strings['mkdir_failed'] = __('Could not create directory.');
$this->strings['incompatible_archive'] = __('The package could not be installed.');
$this->strings['files_not_writable'] = __( 'The update cannot be installed because we will be unable to copy some files. This is usually due to inconsistent file permissions.' );
$this->strings['maintenance_start'] = __('Enabling Maintenance mode…');
$this->strings['maintenance_end'] = __('Disabling Maintenance mode…');
@ -292,6 +293,64 @@ class WP_Upgrader {
return $working_dir;
}
/**
* Clears the directory where this item is going to be installed into.
*
* @since 4.3.0
*
* @global WP_Filesystem_Base $wp_filesystem Subclass
*
* @param string $remote_destination The location on the remote filesystem to be cleared
*
* @return bool|WP_Error true upon success, {@see WP_Error} on failure.
*/
function clear_destination( $remote_destination ) {
global $wp_filesystem;
if ( ! $wp_filesystem->exists( $remote_destination ) ) {
return true;
}
// Check all files are writable before attempting to clear the destination
$unwritable_files = array();
$_files = $wp_filesystem->dirlist( $remote_destination, true, true );
// Flatten the resulting array, iterate using each as we append to the array during iteration
while ( $f = each( $_files ) ) {
$file = $f['value'];
$name = $f['key'];
if ( ! isset( $file['files'] ) ) {
continue;
}
foreach ( $file['files'] as $filename => $details ) {
$_files[ $name . '/' . $filename ] = $details;
}
}
// Check writability
foreach ( $_files as $filename => $file_details ) {
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 ) ) {
$unwritable_files[] = $filename;
}
}
}
if ( ! empty( $unwritable_files ) ) {
return new WP_Error( 'files_not_writable', $this->strings['files_not_writable'], implode( ', ', $unwritable_files ) );
}
if ( ! $wp_filesystem->delete( $remote_destination, true ) ) {
return new WP_Error( 'remove_old_failed', $this->strings['remove_old_failed'] );
}
return true;
}
/**
* Install a package.
*
@ -417,29 +476,25 @@ class WP_Upgrader {
}
if ( $clear_destination ) {
//We're going to clear the destination if there's something there
// We're going to clear the destination if there's something there
$this->skin->feedback('remove_old');
$removed = true;
if ( $wp_filesystem->exists( $remote_destination ) ) {
$removed = $wp_filesystem->delete( $remote_destination, true );
}
$removed = $this->clear_destination( $remote_destination );
/**
* Filter whether the upgrader cleared the destination.
*
* @since 2.8.0
*
* @param bool $removed Whether the destination was cleared.
* @param mixed $removed Whether the destination was cleared. true on success, WP_Error on failure
* @param string $local_destination The local package destination.
* @param string $remote_destination The remote package destination.
* @param array $hook_extra Extra arguments passed to hooked filters.
*/
$removed = apply_filters( 'upgrader_clear_destination', $removed, $local_destination, $remote_destination, $args['hook_extra'] );
if ( is_wp_error($removed) ) {
if ( is_wp_error( $removed ) ) {
return $removed;
} elseif ( ! $removed ) {
return new WP_Error('remove_old_failed', $this->strings['remove_old_failed']);
}
} elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists($remote_destination) ) {
//If we're not clearing the destination folder and something exists there already, Bail.

View File

@ -4,7 +4,7 @@
*
* @global string $wp_version
*/
$wp_version = '4.3-alpha-32852';
$wp_version = '4.3-alpha-32854';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.