WordPress.org

Make WordPress Core

Changeset 32854


Ignore:
Timestamp:
06/19/2015 03:48:55 AM (6 years ago)
Author:
dd32
Message:

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/class-wp-upgrader.php

    r32783 r32854  
    133133        $this->strings['mkdir_failed'] = __('Could not create directory.');
    134134        $this->strings['incompatible_archive'] = __('The package could not be installed.');
     135        $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.' );
    135136
    136137        $this->strings['maintenance_start'] = __('Enabling Maintenance mode…');
     
    294295
    295296    /**
     297     * Clears the directory where this item is going to be installed into.
     298     *
     299     * @since 4.3.0
     300     *
     301     * @global WP_Filesystem_Base $wp_filesystem Subclass
     302     *
     303     * @param string $remote_destination The location on the remote filesystem to be cleared
     304     *
     305     * @return bool|WP_Error true upon success, {@see WP_Error} on failure.
     306     */
     307    function clear_destination( $remote_destination ) {
     308        global $wp_filesystem;
     309
     310        if ( ! $wp_filesystem->exists( $remote_destination ) ) {
     311            return true;
     312        }
     313
     314        // Check all files are writable before attempting to clear the destination
     315        $unwritable_files = array();
     316
     317        $_files = $wp_filesystem->dirlist( $remote_destination, true, true );
     318        // Flatten the resulting array, iterate using each as we append to the array during iteration
     319        while ( $f = each( $_files ) ) {
     320            $file = $f['value'];
     321            $name = $f['key'];
     322
     323            if ( ! isset( $file['files'] ) ) {
     324                continue;
     325            }
     326
     327            foreach ( $file['files'] as $filename => $details ) {
     328                $_files[ $name . '/' . $filename ] = $details;
     329            }
     330        }
     331
     332        // Check writability
     333        foreach ( $_files as $filename => $file_details ) {
     334            if ( ! $wp_filesystem->is_writable( $remote_destination . $filename ) ) {
     335                // Attempt to alter permissions to allow writes and try again
     336                $wp_filesystem->chmod( $remote_destination . $filename, ( 'd' == $file_details['type'] ? FS_CHMOD_DIR : FS_CHMOD_FILE ) );
     337                if ( ! $wp_filesystem->is_writable( $remote_destination . $filename ) ) {
     338                    $unwritable_files[] = $filename;
     339                }
     340            }
     341        }
     342
     343        if ( ! empty( $unwritable_files ) ) {
     344            return new WP_Error( 'files_not_writable', $this->strings['files_not_writable'], implode( ', ', $unwritable_files ) );
     345        }
     346
     347        if ( ! $wp_filesystem->delete( $remote_destination, true ) ) {
     348            return new WP_Error( 'remove_old_failed', $this->strings['remove_old_failed'] );
     349        }
     350
     351        return true;
     352    }
     353
     354    /**
    296355     * Install a package.
    297356     *
     
    418477
    419478        if ( $clear_destination ) {
    420             //We're going to clear the destination if there's something there
     479            // We're going to clear the destination if there's something there
    421480            $this->skin->feedback('remove_old');
    422             $removed = true;
    423             if ( $wp_filesystem->exists( $remote_destination ) ) {
    424                 $removed = $wp_filesystem->delete( $remote_destination, true );
    425             }
     481
     482            $removed = $this->clear_destination( $remote_destination );
    426483
    427484            /**
     
    430487             * @since 2.8.0
    431488             *
    432              * @param bool   $removed            Whether the destination was cleared.
     489             * @param mixed  $removed            Whether the destination was cleared. true on success, WP_Error on failure
    433490             * @param string $local_destination  The local package destination.
    434491             * @param string $remote_destination The remote package destination.
     
    437494            $removed = apply_filters( 'upgrader_clear_destination', $removed, $local_destination, $remote_destination, $args['hook_extra'] );
    438495
    439             if ( is_wp_error($removed) ) {
     496            if ( is_wp_error( $removed ) ) {
    440497                return $removed;
    441             } elseif ( ! $removed ) {
    442                 return new WP_Error('remove_old_failed', $this->strings['remove_old_failed']);
    443498            }
    444499        } elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists($remote_destination) ) {
Note: See TracChangeset for help on using the changeset viewer.