Make WordPress Core

Changeset 55720


Ignore:
Timestamp:
05/04/2023 02:34:58 AM (3 years ago)
Author:
SergeyBiryukov
Message:

Upgrade/Install: Create a temporary backup of plugins and themes before updating.

This aims to make the update process more reliable and ensures that if a plugin or theme update fails, the previous version can be safely restored.

  • When updating a plugin or theme, the old version is moved to a temporary backup directory:
    • wp-content/upgrade-temp-backup/plugins/[plugin-slug] for plugins
    • wp-content/upgrade-temp-backup/themes/[theme-slug] for themes.
  • If the update fails, then the backup kept in the temporary backup directory is restored to its original location.
  • If the update succeeds, the temporary backup is deleted.

To further help troubleshoot plugin and theme updates, two new checks were added to the Site Health screen:

  • A check to make sure that the upgrade-temp-backup directory is writable.
  • A check that there is enough disk space available to safely perform updates.

To avoid confusion: The temporary backup directory will NOT be used to “roll back” a plugin to a previous version after a completed update. This directory will simply contain a transient backup of the previous version of a plugin or theme being updated, and as soon as the update process finishes, the directory will be empty.

Follow-up to [55204], [55220].

Props afragen, costdev, pbiron, azaozz, hellofromTonya, aristath, peterwilsoncc, TJNowell, bronsonquick, Clorith, dd32, poena, TimothyBlynJacobs, audrasjb, mikeschroder, a2hosting, KZeni, galbaras, richards1052, Boniu91, mai21, francina, TobiasBg, desrosj, noisysocks, johnbillion, dlh, chaion07, davidbaumwald, jrf, thisisyeasin, ignatggeorgiev, SergeyBiryukov.
Fixes #51857.

Location:
trunk/src
Files:
5 edited

Legend:

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

    r53426 r55720  
    227227                                'clear_working'     => true,
    228228                                'hook_extra'        => array(
    229                                         'plugin' => $plugin,
    230                                         'type'   => 'plugin',
    231                                         'action' => 'update',
     229                                        'plugin'      => $plugin,
     230                                        'type'        => 'plugin',
     231                                        'action'      => 'update',
     232                                        'temp_backup' => array(
     233                                                'slug' => dirname( $plugin ),
     234                                                'src'  => WP_PLUGIN_DIR,
     235                                                'dir'  => 'plugins',
     236                                        ),
    232237                                ),
    233238                        )
     
    343348                                        'is_multi'          => true,
    344349                                        'hook_extra'        => array(
    345                                                 'plugin' => $plugin,
     350                                                'plugin'      => $plugin,
     351                                                'temp_backup' => array(
     352                                                        'slug' => dirname( $plugin ),
     353                                                        'src'  => WP_PLUGIN_DIR,
     354                                                        'dir'  => 'plugins',
     355                                                ),
    346356                                        ),
    347357                                )
  • trunk/src/wp-admin/includes/class-theme-upgrader.php

    r53426 r55720  
    329329                                'clear_working'     => true,
    330330                                'hook_extra'        => array(
    331                                         'theme'  => $theme,
    332                                         'type'   => 'theme',
    333                                         'action' => 'update',
     331                                        'theme'       => $theme,
     332                                        'type'        => 'theme',
     333                                        'action'      => 'update',
     334                                        'temp_backup' => array(
     335                                                'slug' => $theme,
     336                                                'src'  => get_theme_root( $theme ),
     337                                                'dir'  => 'themes',
     338                                        ),
    334339                                ),
    335340                        )
     
    444449                                        'is_multi'          => true,
    445450                                        'hook_extra'        => array(
    446                                                 'theme' => $theme,
     451                                                'theme'       => $theme,
     452                                                'temp_backup' => array(
     453                                                        'slug' => $theme,
     454                                                        'src'  => get_theme_root( $theme ),
     455                                                        'dir'  => 'themes',
     456                                                ),
    447457                                        ),
    448458                                )
  • trunk/src/wp-admin/includes/class-wp-site-health.php

    r55703 r55720  
    19271927
    19281928        /**
     1929         * Tests available disk space for updates.
     1930         *
     1931         * @since 6.3.0
     1932         *
     1933         * @return array The test results.
     1934         */
     1935        public function get_test_available_updates_disk_space() {
     1936                $available_space = function_exists( 'disk_free_space' ) ? @disk_free_space( WP_CONTENT_DIR . '/upgrade/' ) : false;
     1937
     1938                $available_space = false !== $available_space
     1939                        ? (int) $available_space
     1940                        : 0;
     1941
     1942                $result = array(
     1943                        'label'       => __( 'Disk space available to safely perform updates' ),
     1944                        'status'      => 'good',
     1945                        'badge'       => array(
     1946                                'label' => __( 'Security' ),
     1947                                'color' => 'blue',
     1948                        ),
     1949                        'description' => sprintf(
     1950                                /* translators: %s: Available disk space in MB or GB. */
     1951                                '<p>' . __( '%s available disk space was detected, update routines can be performed safely.' ) . '</p>',
     1952                                size_format( $available_space )
     1953                        ),
     1954                        'actions'     => '',
     1955                        'test'        => 'available_updates_disk_space',
     1956                );
     1957
     1958                if ( $available_space < 100 * MB_IN_BYTES ) {
     1959                        $result['description'] = __( 'Available disk space is low, less than 100 MB available.' );
     1960                        $result['status']      = 'recommended';
     1961                }
     1962
     1963                if ( $available_space < 20 * MB_IN_BYTES ) {
     1964                        $result['description'] = __( 'Available disk space is critically low, less than 20 MB available. Proceed with caution, updates may fail.' );
     1965                        $result['status']      = 'critical';
     1966                }
     1967
     1968                if ( ! $available_space ) {
     1969                        $result['description'] = __( 'Could not determine available disk space for updates.' );
     1970                        $result['status']      = 'recommended';
     1971                }
     1972
     1973                return $result;
     1974        }
     1975
     1976        /**
     1977         * Tests if plugin and theme temporary backup directories are writable or can be created.
     1978         *
     1979         * @since 6.3.0
     1980         *
     1981         * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
     1982         *
     1983         * @return array The test results.
     1984         */
     1985        public function get_test_update_temp_backup_writable() {
     1986                global $wp_filesystem;
     1987
     1988                $result = array(
     1989                        'label'       => __( 'Plugin and theme temporary backup directory is writable' ),
     1990                        'status'      => 'good',
     1991                        'badge'       => array(
     1992                                'label' => __( 'Security' ),
     1993                                'color' => 'blue',
     1994                        ),
     1995                        'description' => sprintf(
     1996                                /* translators: %s: wp-content/upgrade-temp-backup */
     1997                                '<p>' . __( 'The %s directory used to improve the stability of plugin and theme updates is writable.' ) . '</p>',
     1998                                '<code>wp-content/upgrade-temp-backup</code>'
     1999                        ),
     2000                        'actions'     => '',
     2001                        'test'        => 'update_temp_backup_writable',
     2002                );
     2003
     2004                if ( ! $wp_filesystem ) {
     2005                        require_once ABSPATH . '/wp-admin/includes/file.php';
     2006                        WP_Filesystem();
     2007                }
     2008
     2009                $wp_content = $wp_filesystem->wp_content_dir();
     2010
     2011                if ( ! $wp_content ) {
     2012                        $result['status'] = 'critical';
     2013                        $result['label']  = sprintf(
     2014                                /* translators: %s: wp-content */
     2015                                __( 'Unable to locate WordPress content directory (%s)' ),
     2016                                '<code>wp-content</code>'
     2017                        );
     2018                        $result['description'] = sprintf(
     2019                                /* translators: %s: wp-content */
     2020                                '<p>' . __( 'The %s directory cannot be located.' ) . '</p>',
     2021                                '<code>wp-content</code>'
     2022                        );
     2023                        return $result;
     2024                }
     2025
     2026                $upgrade_dir_exists      = $wp_filesystem->is_dir( "$wp_content/upgrade" );
     2027                $upgrade_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade" );
     2028                $backup_dir_exists       = $wp_filesystem->is_dir( "$wp_content/upgrade-temp-backup" );
     2029                $backup_dir_is_writable  = $wp_filesystem->is_writable( "$wp_content/upgrade-temp-backup" );
     2030
     2031                $plugins_dir_exists      = $wp_filesystem->is_dir( "$wp_content/upgrade-temp-backup/plugins" );
     2032                $plugins_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade-temp-backup/plugins" );
     2033                $themes_dir_exists       = $wp_filesystem->is_dir( "$wp_content/upgrade-temp-backup/themes" );
     2034                $themes_dir_is_writable  = $wp_filesystem->is_writable( "$wp_content/upgrade-temp-backup/themes" );
     2035
     2036                if ( $plugins_dir_exists && ! $plugins_dir_is_writable && $themes_dir_exists && ! $themes_dir_is_writable ) {
     2037                        $result['status']      = 'critical';
     2038                        $result['label']       = __( 'Plugin and theme temporary backup directories exist but are not writable' );
     2039                        $result['description'] = sprintf(
     2040                                /* translators: 1: wp-content/upgrade-temp-backup/plugins, 2: wp-content/upgrade-temp-backup/themes. */
     2041                                '<p>' . __( 'The %1$s and %2$s directories exist but are not writable. These directories are used to improve the stability of plugin updates. Please make sure the server has write permissions to these directories.' ) . '</p>',
     2042                                '<code>wp-content/upgrade-temp-backup/plugins</code>',
     2043                                '<code>wp-content/upgrade-temp-backup/themes</code>'
     2044                        );
     2045                        return $result;
     2046                }
     2047
     2048                if ( $plugins_dir_exists && ! $plugins_dir_is_writable ) {
     2049                        $result['status']      = 'critical';
     2050                        $result['label']       = __( 'Plugin temporary backup directory exists but is not writable' );
     2051                        $result['description'] = sprintf(
     2052                                /* translators: %s: wp-content/upgrade-temp-backup/plugins */
     2053                                '<p>' . __( 'The %s directory exists but is not writable. This directory is used to improve the stability of plugin updates. Please make sure the server has write permissions to this directory.' ) . '</p>',
     2054                                '<code>wp-content/upgrade-temp-backup/plugins</code>'
     2055                        );
     2056                        return $result;
     2057                }
     2058
     2059                if ( $themes_dir_exists && ! $themes_dir_is_writable ) {
     2060                        $result['status']      = 'critical';
     2061                        $result['label']       = __( 'Theme temporary backup directory exists but is not writable' );
     2062                        $result['description'] = sprintf(
     2063                                /* translators: %s: wp-content/upgrade-temp-backup/themes */
     2064                                '<p>' . __( 'The %s directory exists but is not writable. This directory is used to improve the stability of theme updates. Please make sure the server has write permissions to this directory.' ) . '</p>',
     2065                                '<code>wp-content/upgrade-temp-backup/themes</code>'
     2066                        );
     2067                        return $result;
     2068                }
     2069
     2070                if ( ( ! $plugins_dir_exists || ! $themes_dir_exists ) && $backup_dir_exists && ! $backup_dir_is_writable ) {
     2071                        $result['status']      = 'critical';
     2072                        $result['label']       = __( 'The temporary backup directory exists but is not writable' );
     2073                        $result['description'] = sprintf(
     2074                                /* translators: %s: wp-content/upgrade-temp-backup */
     2075                                '<p>' . __( 'The %s directory exists but is not writable. This directory is used to improve the stability of plugin and theme updates. Please make sure the server has write permissions to this directory.' ) . '</p>',
     2076                                '<code>wp-content/upgrade-temp-backup</code>'
     2077                        );
     2078                        return $result;
     2079                }
     2080
     2081                if ( ! $backup_dir_exists && $upgrade_dir_exists && ! $upgrade_dir_is_writable ) {
     2082                        $result['status'] = 'critical';
     2083                        $result['label']  = sprintf(
     2084                                /* translators: %s: upgrade */
     2085                                __( 'The %s directory exists but is not writable' ),
     2086                                'upgrade'
     2087                        );
     2088                        $result['description'] = sprintf(
     2089                                /* translators: %s: wp-content/upgrade */
     2090                                '<p>' . __( 'The %s directory exists but is not writable. This directory is used for plugin and theme updates. Please make sure the server has write permissions to this directory.' ) . '</p>',
     2091                                '<code>wp-content/upgrade</code>'
     2092                        );
     2093                        return $result;
     2094                }
     2095
     2096                if ( ! $upgrade_dir_exists && ! $wp_filesystem->is_writable( $wp_content ) ) {
     2097                        $result['status'] = 'critical';
     2098                        $result['label']  = sprintf(
     2099                                /* translators: %s: upgrade */
     2100                                __( 'The %s directory cannot be created' ),
     2101                                'upgrade'
     2102                        );
     2103                        $result['description'] = sprintf(
     2104                                /* translators: 1: wp-content/upgrade, 2: wp-content. */
     2105                                '<p>' . __( 'The %1$s directory does not exist, and the server does not have write permissions in %2$s to create it. This directory is used for plugin and theme updates. Please make sure the server has write permissions in %2$s.' ) . '</p>',
     2106                                '<code>wp-content/upgrade</code>',
     2107                                '<code>wp-content</code>'
     2108                        );
     2109                        return $result;
     2110                }
     2111
     2112                return $result;
     2113        }
     2114
     2115        /**
    19292116         * Tests if loopbacks work as expected.
    19302117         *
     
    25332720                $tests = array(
    25342721                        'direct' => array(
    2535                                 'wordpress_version'         => array(
     2722                                'wordpress_version'            => array(
    25362723                                        'label' => __( 'WordPress Version' ),
    25372724                                        'test'  => 'wordpress_version',
    25382725                                ),
    2539                                 'plugin_version'            => array(
     2726                                'plugin_version'               => array(
    25402727                                        'label' => __( 'Plugin Versions' ),
    25412728                                        'test'  => 'plugin_version',
    25422729                                ),
    2543                                 'theme_version'             => array(
     2730                                'theme_version'                => array(
    25442731                                        'label' => __( 'Theme Versions' ),
    25452732                                        'test'  => 'theme_version',
    25462733                                ),
    2547                                 'php_version'               => array(
     2734                                'php_version'                  => array(
    25482735                                        'label' => __( 'PHP Version' ),
    25492736                                        'test'  => 'php_version',
    25502737                                ),
    2551                                 'php_extensions'            => array(
     2738                                'php_extensions'               => array(
    25522739                                        'label' => __( 'PHP Extensions' ),
    25532740                                        'test'  => 'php_extensions',
    25542741                                ),
    2555                                 'php_default_timezone'      => array(
     2742                                'php_default_timezone'         => array(
    25562743                                        'label' => __( 'PHP Default Timezone' ),
    25572744                                        'test'  => 'php_default_timezone',
    25582745                                ),
    2559                                 'php_sessions'              => array(
     2746                                'php_sessions'                 => array(
    25602747                                        'label' => __( 'PHP Sessions' ),
    25612748                                        'test'  => 'php_sessions',
    25622749                                ),
    2563                                 'sql_server'                => array(
     2750                                'sql_server'                   => array(
    25642751                                        'label' => __( 'Database Server version' ),
    25652752                                        'test'  => 'sql_server',
    25662753                                ),
    2567                                 'utf8mb4_support'           => array(
     2754                                'utf8mb4_support'              => array(
    25682755                                        'label' => __( 'MySQL utf8mb4 support' ),
    25692756                                        'test'  => 'utf8mb4_support',
    25702757                                ),
    2571                                 'ssl_support'               => array(
     2758                                'ssl_support'                  => array(
    25722759                                        'label' => __( 'Secure communication' ),
    25732760                                        'test'  => 'ssl_support',
    25742761                                ),
    2575                                 'scheduled_events'          => array(
     2762                                'scheduled_events'             => array(
    25762763                                        'label' => __( 'Scheduled events' ),
    25772764                                        'test'  => 'scheduled_events',
    25782765                                ),
    2579                                 'http_requests'             => array(
     2766                                'http_requests'                => array(
    25802767                                        'label' => __( 'HTTP Requests' ),
    25812768                                        'test'  => 'http_requests',
    25822769                                ),
    2583                                 'rest_availability'         => array(
     2770                                'rest_availability'            => array(
    25842771                                        'label'     => __( 'REST API availability' ),
    25852772                                        'test'      => 'rest_availability',
    25862773                                        'skip_cron' => true,
    25872774                                ),
    2588                                 'debug_enabled'             => array(
     2775                                'debug_enabled'                => array(
    25892776                                        'label' => __( 'Debugging enabled' ),
    25902777                                        'test'  => 'is_in_debug_mode',
    25912778                                ),
    2592                                 'file_uploads'              => array(
     2779                                'file_uploads'                 => array(
    25932780                                        'label' => __( 'File uploads' ),
    25942781                                        'test'  => 'file_uploads',
    25952782                                ),
    2596                                 'plugin_theme_auto_updates' => array(
     2783                                'plugin_theme_auto_updates'    => array(
    25972784                                        'label' => __( 'Plugin and theme auto-updates' ),
    25982785                                        'test'  => 'plugin_theme_auto_updates',
     2786                                ),
     2787                                'update_temp_backup_writable'  => array(
     2788                                        'label' => __( 'Plugin and theme temporary backup directory access' ),
     2789                                        'test'  => 'update_temp_backup_writable',
     2790                                ),
     2791                                'available_updates_disk_space' => array(
     2792                                        'label' => __( 'Available disk space' ),
     2793                                        'test'  => 'available_updates_disk_space',
    25992794                                ),
    26002795                        ),
  • trunk/src/wp-admin/includes/class-wp-upgrader.php

    r55258 r55720  
    114114
    115115        /**
     116         * Stores the list of plugins or themes added to temporary backup directory.
     117         *
     118         * Used by the rollback functions.
     119         *
     120         * @since 6.3.0
     121         * @var array
     122         */
     123        private $temp_backups = array();
     124
     125        /**
     126         * Stores the list of plugins or themes to be restored from temporary backup directory.
     127         *
     128         * Used by the rollback functions.
     129         *
     130         * @since 6.3.0
     131         * @var array
     132         */
     133        private $temp_restores = array();
     134
     135        /**
    116136         * Construct the upgrader with a skin.
    117137         *
     
    135155         * and also add the generic strings to `WP_Upgrader::$strings`.
    136156         *
     157         * Additionally, it will schedule a weekly task to clean up the temporary backup directory.
     158         *
    137159         * @since 2.8.0
     160         * @since 6.3.0 Added the `schedule_temp_backup_cleanup()` task.
    138161         */
    139162        public function init() {
    140163                $this->skin->set_upgrader( $this );
    141164                $this->generic_strings();
     165
     166                if ( ! wp_installing() ) {
     167                        $this->schedule_temp_backup_cleanup();
     168                }
     169        }
     170
     171        /**
     172         * Schedules the cleanup of the temporary backup directory.
     173         *
     174         * @since 6.3.0
     175         */
     176        protected function schedule_temp_backup_cleanup() {
     177                if ( false === wp_next_scheduled( 'wp_delete_temp_updater_backups' ) ) {
     178                        wp_schedule_event( time(), 'weekly', 'wp_delete_temp_updater_backups' );
     179                }
    142180        }
    143181
     
    168206                $this->strings['maintenance_start'] = __( 'Enabling Maintenance mode&#8230;' );
    169207                $this->strings['maintenance_end']   = __( 'Disabling Maintenance mode&#8230;' );
     208
     209                /* translators: %s: upgrade-temp-backup */
     210                $this->strings['temp_backup_mkdir_failed'] = sprintf( __( 'Could not create the %s directory.' ), 'upgrade-temp-backup' );
     211                /* translators: %s: upgrade-temp-backup */
     212                $this->strings['temp_backup_move_failed'] = sprintf( __( 'Could not move the old version to the %s directory.' ), 'upgrade-temp-backup' );
     213                /* translators: %s: The plugin or theme slug. */
     214                $this->strings['temp_backup_restore_failed'] = __( 'Could not restore the original version of %s.' );
     215                /* translators: %s: The plugin or theme slug. */
     216                $this->strings['temp_backup_delete_failed'] = __( 'Could not delete the temporary backup directory for %s.' );
    170217        }
    171218
     
    309356                $this->skin->feedback( 'unpack_package' );
    310357
     358                if ( ! $wp_filesystem->wp_content_dir() ) {
     359                        return new WP_Error( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] );
     360                }
     361
    311362                $upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/';
    312363
     
    533584                if ( is_wp_error( $source ) ) {
    534585                        return $source;
     586                }
     587
     588                if ( ! empty( $args['hook_extra']['temp_backup'] ) ) {
     589                        $temp_backup = $this->move_to_temp_backup_dir( $args['hook_extra']['temp_backup'] );
     590
     591                        if ( is_wp_error( $temp_backup ) ) {
     592                                return $temp_backup;
     593                        }
     594
     595                        $this->temp_backups[] = $args['hook_extra']['temp_backup'];
    535596                }
    536597
     
    615676                }
    616677
    617                 // Clear the working folder?
     678                // Clear the working directory?
    618679                if ( $args['clear_working'] ) {
    619680                        $wp_filesystem->delete( $remote_source, true );
     
    828889
    829890                $this->skin->set_result( $result );
     891
    830892                if ( is_wp_error( $result ) ) {
     893                        if ( ! empty( $options['hook_extra']['temp_backup'] ) ) {
     894                                $this->temp_restores[] = $options['hook_extra']['temp_backup'];
     895
     896                                /*
     897                                 * Restore the backup on shutdown.
     898                                 * Actions running on `shutdown` are immune to PHP timeouts,
     899                                 * so in case the failure was due to a PHP timeout,
     900                                 * it will still be able to properly restore the previous version.
     901                                 */
     902                                add_action( 'shutdown', array( $this, 'restore_temp_backup' ) );
     903                        }
    831904                        $this->skin->error( $result );
    832905
     
    840913
    841914                $this->skin->after();
     915
     916                // Clean up the backup kept in the temporary backup directory.
     917                if ( ! empty( $options['hook_extra']['temp_backup'] ) ) {
     918                        // Delete the backup on `shutdown` to avoid a PHP timeout.
     919                        add_action( 'shutdown', array( $this, 'delete_temp_backup' ), 100, 0 );
     920                }
    842921
    843922                if ( ! $options['is_multi'] ) {
     
    9671046                return delete_option( $lock_name . '.lock' );
    9681047        }
     1048
     1049        /**
     1050         * Moves the plugin or theme being updated into a temporary backup directory.
     1051         *
     1052         * @since 6.3.0
     1053         *
     1054         * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
     1055         *
     1056         * @param string[] $args {
     1057         *     Array of data for the temporary backup.
     1058         *
     1059         *     @type string $slug Plugin or theme slug.
     1060         *     @type string $src  Path to the root directory for plugins or themes.
     1061         *     @type string $dir  Destination subdirectory name. Accepts 'plugins' or 'themes'.
     1062         * }
     1063         *
     1064         * @return bool|WP_Error True on success, false on early exit, otherwise WP_Error.
     1065         */
     1066        public function move_to_temp_backup_dir( $args ) {
     1067                global $wp_filesystem;
     1068
     1069                if ( empty( $args['slug'] ) || empty( $args['src'] ) || empty( $args['dir'] ) ) {
     1070                        return false;
     1071                }
     1072
     1073                /*
     1074                 * Skip any plugin that has "." as its slug.
     1075                 * A slug of "." will result in a `$src` value ending in a period.
     1076                 *
     1077                 * On Windows, this will cause the 'plugins' folder to be moved,
     1078                 * and will cause a failure when attempting to call `mkdir()`.
     1079                 */
     1080                if ( '.' === $args['slug'] ) {
     1081                        return false;
     1082                }
     1083
     1084                if ( ! $wp_filesystem->wp_content_dir() ) {
     1085                        return new WP_Error( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] );
     1086                }
     1087
     1088                $dest_dir = $wp_filesystem->wp_content_dir() . 'upgrade-temp-backup/';
     1089                $sub_dir  = $dest_dir . $args['dir'] . '/';
     1090
     1091                // Create the temporary backup directory if it does not exist.
     1092                if ( ! $wp_filesystem->is_dir( $sub_dir ) ) {
     1093                        if ( ! $wp_filesystem->is_dir( $dest_dir ) ) {
     1094                                $wp_filesystem->mkdir( $dest_dir, FS_CHMOD_DIR );
     1095                        }
     1096
     1097                        if ( ! $wp_filesystem->mkdir( $sub_dir, FS_CHMOD_DIR ) ) {
     1098                                // Could not create the backup directory.
     1099                                return new WP_Error( 'fs_temp_backup_mkdir', $this->strings['temp_backup_mkdir_failed'] );
     1100                        }
     1101                }
     1102
     1103                $src_dir = $wp_filesystem->find_folder( $args['src'] );
     1104                $src     = trailingslashit( $src_dir ) . $args['slug'];
     1105                $dest    = $dest_dir . trailingslashit( $args['dir'] ) . $args['slug'];
     1106
     1107                // Delete the temporary backup directory if it already exists.
     1108                if ( $wp_filesystem->is_dir( $dest ) ) {
     1109                        $wp_filesystem->delete( $dest, true );
     1110                }
     1111
     1112                // Move to the temporary backup directory.
     1113                $result = move_dir( $src, $dest, true );
     1114                if ( is_wp_error( $result ) ) {
     1115                        return new WP_Error( 'fs_temp_backup_move', $this->strings['temp_backup_move_failed'] );
     1116                }
     1117
     1118                return true;
     1119        }
     1120
     1121        /**
     1122         * Restores the plugin or theme from temporary backup.
     1123         *
     1124         * @since 6.3.0
     1125         *
     1126         * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
     1127         *
     1128         * @return bool|WP_Error True on success, false on early exit, otherwise WP_Error.
     1129         */
     1130        public function restore_temp_backup() {
     1131                global $wp_filesystem;
     1132
     1133                $errors = new WP_Error();
     1134
     1135                foreach ( $this->temp_restores as $args ) {
     1136                        if ( empty( $args['slug'] ) || empty( $args['src'] ) || empty( $args['dir'] ) ) {
     1137                                return false;
     1138                        }
     1139
     1140                        if ( ! $wp_filesystem->wp_content_dir() ) {
     1141                                $errors->add( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] );
     1142                                return $errors;
     1143                        }
     1144
     1145                        $src      = $wp_filesystem->wp_content_dir() . 'upgrade-temp-backup/' . $args['dir'] . '/' . $args['slug'];
     1146                        $dest_dir = $wp_filesystem->find_folder( $args['src'] );
     1147                        $dest     = trailingslashit( $dest_dir ) . $args['slug'];
     1148
     1149                        if ( $wp_filesystem->is_dir( $src ) ) {
     1150                                // Cleanup.
     1151                                if ( $wp_filesystem->is_dir( $dest ) && ! $wp_filesystem->delete( $dest, true ) ) {
     1152                                        $errors->add(
     1153                                                'fs_temp_backup_delete',
     1154                                                sprintf( $this->strings['temp_backup_restore_failed'], $args['slug'] )
     1155                                        );
     1156                                        continue;
     1157                                }
     1158
     1159                                // Move it.
     1160                                $result = move_dir( $src, $dest, true );
     1161                                if ( is_wp_error( $result ) ) {
     1162                                        $errors->add(
     1163                                                'fs_temp_backup_delete',
     1164                                                sprintf( $this->strings['temp_backup_restore_failed'], $args['slug'] )
     1165                                        );
     1166                                        continue;
     1167                                }
     1168                        }
     1169                }
     1170
     1171                return $errors->has_errors() ? $errors : true;
     1172        }
     1173
     1174        /**
     1175         * Deletes a temporary backup.
     1176         *
     1177         * @since 6.3.0
     1178         *
     1179         * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
     1180         *
     1181         * @return bool|WP_Error True on success, false on early exit, otherwise WP_Error.
     1182         */
     1183        public function delete_temp_backup() {
     1184                global $wp_filesystem;
     1185
     1186                $errors = new WP_Error();
     1187
     1188                foreach ( $this->temp_backups as $args ) {
     1189                        if ( empty( $args['slug'] ) || empty( $args['dir'] ) ) {
     1190                                return false;
     1191                        }
     1192
     1193                        if ( ! $wp_filesystem->wp_content_dir() ) {
     1194                                $errors->add( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] );
     1195                                return $errors;
     1196                        }
     1197
     1198                        $temp_backup_dir = $wp_filesystem->wp_content_dir() . "upgrade-temp-backup/{$args['dir']}/{$args['slug']}";
     1199
     1200                        if ( ! $wp_filesystem->delete( $temp_backup_dir, true ) ) {
     1201                                $errors->add(
     1202                                        'temp_backup_delete_failed',
     1203                                        sprintf( $this->strings['temp_backup_delete_failed'] ),
     1204                                        $args['slug']
     1205                                );
     1206                                continue;
     1207                        }
     1208                }
     1209
     1210                return $errors->has_errors() ? $errors : true;
     1211        }
    9691212}
    9701213
  • trunk/src/wp-includes/update.php

    r55641 r55720  
    10781078}
    10791079
     1080/**
     1081 * Schedules the removal of all contents in the temporary backup directory.
     1082 *
     1083 * @since 6.3.0
     1084 */
     1085function wp_delete_all_temp_backups() {
     1086        /*
     1087         * Check if there is a lock, or if currently performing an Ajax request,
     1088         * in which case there is a chance an update is running.
     1089         * Reschedule for an hour from now and exit early.
     1090         */
     1091        if ( get_option( 'core_updater.lock' ) || get_option( 'auto_updater.lock' ) || wp_doing_ajax() ) {
     1092                wp_schedule_single_event( time() + HOUR_IN_SECONDS, 'wp_delete_temp_updater_backups' );
     1093                return;
     1094        }
     1095
     1096        // This action runs on shutdown to make sure there are no plugin updates currently running.
     1097        add_action( 'shutdown', '_wp_delete_all_temp_backups' );
     1098}
     1099
     1100/**
     1101 * Deletes all contents in the temporary backup directory.
     1102 *
     1103 * @since 6.3.0
     1104 *
     1105 * @access private
     1106 *
     1107 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
     1108 *
     1109 * @return void|WP_Error Void on success, or a WP_Error object on failure.
     1110 */
     1111function _wp_delete_all_temp_backups() {
     1112        global $wp_filesystem;
     1113
     1114        if ( ! $wp_filesystem ) {
     1115                require_once ABSPATH . '/wp-admin/includes/file.php';
     1116                WP_Filesystem();
     1117        }
     1118
     1119        if ( ! $wp_filesystem->wp_content_dir() ) {
     1120                return new WP_Error( 'fs_no_content_dir', __( 'Unable to locate WordPress content directory (wp-content).' ) );
     1121        }
     1122
     1123        $temp_backup_dir = $wp_filesystem->wp_content_dir() . 'upgrade-temp-backup/';
     1124        $dirlist         = $wp_filesystem->dirlist( $temp_backup_dir );
     1125        $dirlist         = $dirlist ? $dirlist : array();
     1126
     1127        foreach ( array_keys( $dirlist ) as $dir ) {
     1128                if ( '.' === $dir || '..' === $dir ) {
     1129                        continue;
     1130                }
     1131
     1132                $wp_filesystem->delete( $temp_backup_dir . $dir, true );
     1133        }
     1134}
     1135
    10801136if ( ( ! is_main_site() && ! is_network_admin() ) || wp_doing_ajax() ) {
    10811137        return;
     
    11021158
    11031159add_action( 'init', 'wp_schedule_update_checks' );
     1160
     1161add_action( 'wp_delete_temp_updater_backups', 'wp_delete_all_temp_backups' );
Note: See TracChangeset for help on using the changeset viewer.