Make WordPress Core

Changeset 25540


Ignore:
Timestamp:
09/21/2013 06:48:20 AM (13 years ago)
Author:
dd32
Message:

Upgrader: Perform a MD5 file verification check on the files during upgrade. This ensures that both a Partial upgrade build can be used, and that all the files were copied into place correctly.
Props pento for initial patch. Fixes #18201

Location:
trunk/src/wp-admin/includes
Files:
4 edited

Legend:

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

    r25496 r25540  
    11121112                $wp_dir = trailingslashit($wp_filesystem->abspath());
    11131113
     1114                // Pre-cache the checksums for the versions we care about
     1115                get_core_checksums( array( $wp_version, $current->version ) );
     1116
     1117                $no_partial = false;
     1118                if ( ! $this->check_files() )
     1119                        $no_partial = true;
     1120
    11141121                // If partial update is returned from the API, use that, unless we're doing a reinstall.
    11151122                // If we cross the new_bundled version number, then use the new_bundled zip.
    11161123                // Don't though if the constant is set to skip bundled items.
    11171124                // If the API returns a no_content zip, go with it. Finally, default to the full zip.
    1118                 if ( $current->packages->partial && 'reinstall' != $current->response && $wp_version == $current->partial_version )
     1125                if ( $current->packages->partial && 'reinstall' != $current->response && $wp_version == $current->partial_version && ! $no_partial )
    11191126                        $to_download = 'partial';
    11201127                elseif ( $current->packages->new_bundled && version_compare( $wp_version, $current->new_bundled, '<' )
     
    12061213        }
    12071214
     1215        function check_files() {
     1216                global $wp_version;
     1217
     1218                $checksums = get_core_checksums( $wp_version );
     1219
     1220                if ( empty( $checksums[ $wp_version ] ) || ! is_array( $checksums[ $wp_version ] ) )
     1221                        return false;
     1222
     1223                foreach ( $checksums[ $wp_version ] as $file => $checksum ) {
     1224                        if ( md5_file( ABSPATH . $file ) !== $checksum )
     1225                                return false;
     1226                }
     1227
     1228                return true;
     1229        }
    12081230}
    12091231
  • trunk/src/wp-admin/includes/file.php

    r25469 r25540  
    724724        $to = trailingslashit($to);
    725725
    726         $skip_regex = '';
    727         foreach ( (array)$skip_list as $key => $skip_file )
    728                 $skip_regex .= preg_quote($skip_file, '!') . '|';
    729 
    730         if ( !empty($skip_regex) )
    731                 $skip_regex = '!(' . rtrim($skip_regex, '|') . ')$!i';
    732 
    733726        foreach ( (array) $dirlist as $filename => $fileinfo ) {
    734                 if ( !empty($skip_regex) )
    735                         if ( preg_match($skip_regex, $from . $filename) )
    736                                 continue;
     727                if ( in_array( $filename, $skip_list ) )
     728                        continue;
    737729
    738730                if ( 'f' == $fileinfo['type'] ) {
     
    748740                                        return new WP_Error('mkdir_failed', __('Could not create directory.'), $to . $filename);
    749741                        }
    750                         $result = copy_dir($from . $filename, $to . $filename, $skip_list);
     742
     743                        // generate the $sub_skip_list for the subdirectory as a sub-set of the existing $skip_list
     744                        $sub_skip_list = array();
     745                        foreach ( $skip_list as $skip_item ) {
     746                                if ( 0 === strpos( $skip_item, $filename . '/' ) )
     747                                        $sub_skip_list[] = preg_replace( '!^' . preg_quote( $filename, '!' ) . '/!i', '', $skip_item );
     748                        }
     749
     750                        $result = copy_dir($from . $filename, $to . $filename, $sub_skip_list);
    751751                        if ( is_wp_error($result) )
    752752                                return $result;
  • trunk/src/wp-admin/includes/update-core.php

    r25307 r25540  
    689689                return new WP_Error( 'mysql_not_compatible', sprintf( __('The update cannot be installed because WordPress %1$s requires MySQL version %2$s or higher. You are running version %3$s.'), $wp_version, $required_mysql_version, $mysql_version ) );
    690690
    691         apply_filters('update_feedback', __('Installing the latest version&#8230;'));
    692 
     691        apply_filters( 'update_feedback', __( 'Preparing to install the latest version&#8230;' ) );
     692
     693        // Don't copy wp-content, we'll deal with that below
     694        $skip = array( 'wp-content' );
     695
     696        // Check to see which files don't really need updating - only available for 3.7 and higher
     697        if ( function_exists( 'get_core_checksums' ) ) {
     698                $checksums = get_core_checksums( $wp_version );
     699                if ( ! empty( $checksums[ $wp_version ] ) && is_array( $checksums[ $wp_version ] ) ) {
     700                        foreach( $checksums[ $wp_version ] as $file => $checksum ) {
     701                                if ( md5_file( ABSPATH . $file ) === $checksum )
     702                                        $skip[] = $file;
     703                        }
     704                }
     705        }
     706
     707        apply_filters( 'update_feedback', __( 'Enabling Maintenance mode&#8230;' ) );
    693708        // Create maintenance file to signal that we are upgrading
    694709        $maintenance_string = '<?php $upgrading = ' . time() . '; ?>';
     
    697712        $wp_filesystem->put_contents($maintenance_file, $maintenance_string, FS_CHMOD_FILE);
    698713
     714        apply_filters( 'update_feedback', __( 'Copying the required files&#8230;' ) );
    699715        // Copy new versions of WP files into place.
    700         $result = _copy_dir($from . $distro, $to, array('wp-content') );
     716        $result = _copy_dir( $from . $distro, $to, $skip );
     717
     718        // Check to make sure everything copied correctly, ignoring the contents of wp-content
     719        $skip = array( 'wp-content' );
     720        $failed = array();
     721        if ( ! empty( $checksums[ $wp_version ] ) && is_array( $checksums[ $wp_version ] ) ) {
     722                foreach ( $checksums[ $wp_version ] as $file => $checksum ) {
     723                        if ( 0 === strpos( $file, 'wp-content' ) )
     724                                continue;
     725
     726                        if ( md5_file( ABSPATH . $file ) == $checksum )
     727                                $skip[] = $file;
     728                        else
     729                                $failed[] = $file;
     730                }
     731        }
     732
     733        // Some files didn't copy properly
     734        if ( ! empty( $failed ) ) {
     735                $total_size = 0;
     736                // Find the local version of the working directory
     737                $working_dir_local = str_replace( trailingslashit( $wp_filesystem->wp_content_dir() ), trailingslashit( WP_CONTENT_DIR ), $from . $distro );
     738                foreach ( $failed as $file )
     739                        $total_size += filesize( $working_dir_local . '/' . $file );
     740
     741                // If we don't have enough free space, it isn't worth trying again
     742                if ( $total_size >= disk_free_space( ABSPATH ) )
     743                        $result = new WP_Error( 'disk_full', __( "There isn't enough free disk space to complete the upgrade." ), $to );
     744                else
     745                        $result = _copy_dir( $from . $distro, $to, $skip );
     746        }
    701747
    702748        // Custom Content Directory needs updating now.
     
    796842                delete_option('update_core');
    797843
     844        apply_filters( 'update_feedback', __( 'Disabling Maintenance mode&#8230;' ) );
    798845        // Remove maintenance file, we're done.
    799846        $wp_filesystem->delete($maintenance_file);
     
    809856 * Assumes that WP_Filesystem() has already been called and setup.
    810857 *
    811  * This is a temporary function for the 3.1 -> 3.2 upgrade only and will be removed in 3.3
     858 * This is a temporary function for the 3.1 -> 3.2 upgrade, as well as for those upgrading to
     859 * 3.7+
    812860 *
    813861 * @ignore
    814862 * @since 3.2.0
     863 * @since 3.7.0 Updated not to use a regular expression for the skip list
    815864 * @see copy_dir()
    816865 *
     
    828877        $to = trailingslashit($to);
    829878
    830         $skip_regex = '';
    831         foreach ( (array)$skip_list as $key => $skip_file )
    832                 $skip_regex .= preg_quote($skip_file, '!') . '|';
    833 
    834         if ( !empty($skip_regex) )
    835                 $skip_regex = '!(' . rtrim($skip_regex, '|') . ')$!i';
    836 
    837879        foreach ( (array) $dirlist as $filename => $fileinfo ) {
    838                 if ( !empty($skip_regex) )
    839                         if ( preg_match($skip_regex, $from . $filename) )
    840                                 continue;
     880                if ( in_array( $filename, $skip_list ) )
     881                        continue;
    841882
    842883                if ( 'f' == $fileinfo['type'] ) {
     
    852893                                        return new WP_Error('mkdir_failed', __('Could not create directory.'), $to . $filename);
    853894                        }
    854                         $result = _copy_dir($from . $filename, $to . $filename, $skip_list);
     895
     896                        // generate the $sub_skip_list for the subdirectory as a sub-set of the existing $skip_list
     897                        $sub_skip_list = array();
     898                        foreach ( $skip_list as $skip_item ) {
     899                                if ( 0 === strpos( $skip_item, $filename . '/' ) )
     900                                        $sub_skip_list[] = preg_replace( '!^' . preg_quote( $filename, '!' ) . '/!i', '', $skip_item );
     901                        }
     902
     903                        $result = _copy_dir($from . $filename, $to . $filename, $sub_skip_list);
    855904                        if ( is_wp_error($result) )
    856905                                return $result;
  • trunk/src/wp-admin/includes/update.php

    r25421 r25540  
    8888}
    8989
     90/**
     91 * Gets and caches the checksums for the given versions of WordPress
     92 *
     93 * @since 3.7.0
     94 *
     95 * @param $version string|array A single version, or an array of versions to fetch
     96 *
     97 * @return bool|array False on failure, otherwise the array of checksums, keyed by version
     98 */
     99function get_core_checksums( $version ) {
     100        if ( ! is_array( $version ) )
     101                $version = array( $version );
     102
     103        $return = array();
     104
     105        // Check to see if we have cached copies available, if we do, no need to request them
     106        foreach ( $version as $i => $v ) {
     107                if ( $checksums = get_site_transient( "core_checksums_$v" ) ) {
     108                        unset( $version[ $i ] );
     109                        $return[ $v ] = $checksums;
     110                }
     111        }
     112
     113        // We had cached copies for all of the versions!
     114        if ( empty( $version ) )
     115                return $return;
     116
     117        $url = 'http://api.wordpress.org/core/checksums/1.0/?' . http_build_query( array( 'version' => $version ), null, '&' );
     118
     119        if ( wp_http_supports( array( 'ssl' ) ) )
     120                $url = set_url_scheme( $url, 'https' );
     121
     122        $options = array(
     123                'timeout' => ( ( defined('DOING_CRON') && DOING_CRON ) ? 30 : 3 ),
     124        );
     125
     126        $response = wp_remote_get( $url, $options );
     127
     128        if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) )
     129                return false;
     130
     131        $body = trim( wp_remote_retrieve_body( $response ) );
     132        $body = json_decode( $body, true );
     133
     134        if ( ! is_array( $body ) || ! isset( $body['checksums'] ) || ! is_array( $body['checksums'] ) )
     135                return false;
     136
     137        // Cache the checksums for later
     138        foreach ( $version as $v ) {
     139                set_site_transient( "core_checksums_$v", $body['checksums'][ $v ], HOUR_IN_SECONDS );
     140                $return[ $v ] = $body['checksums'][ $v ];
     141        }
     142
     143        // If the API didn't return anything for a version, explicitly set it's return value to false
     144        foreach ( $return as $v => $r ) {
     145                if ( empty( $r ) )
     146                        $return[ $v ] = false;
     147        }
     148
     149        return $return;
     150}
     151
    90152function dismiss_core_update( $update ) {
    91153        $dismissed = get_site_option( 'dismissed_update_core' );
Note: See TracChangeset for help on using the changeset viewer.