#55307 closed defect (bug) (duplicate)
After upgrading Debian, WordPress sites no longer update through FTP
Reported by: | zippy1970 | Owned by: | |
---|---|---|---|
Milestone: | Priority: | normal | |
Severity: | major | Version: | 6.0 |
Component: | Upgrade/Install | Keywords: | |
Focuses: | Cc: |
Description
I upgraded my production server from Debian 10 (Buster) to Debian 11 (Bullseye) a few days ago. At first, none of the hosted WordPress sites worked. All gave me an error about a missing mysql module. As it turned out, I needed to install php7.4-mysqli.
But then when I wanted to update the WordPress sites from v5.9.0 to v5.9.1, all failed:
Downloading update from https://downloads.wordpress.org/release/wordpress-5.9.1-no-content.zip… The authenticity of wordpress-5.9.1-no-content.zip could not be verified as no signature was found. Unpacking the update… Verifying the unpacked files… The update could not be unpacked. Installation failed.
I took me quite a while to figure out what actually was going on. Now I know, I have no idea if this is a bug in WordPress, or caused by something else.
The error message WP is giving, is actually incorrect. As it turns out, it is able to unpack the update just fine in the wp-content/upgrade folder. But it’s when it checks if the files have actually unpacked, where it goes wrong. The problem lies in this piece of code in update-core.php:
<?php foreach ( $roots as $root ) { if ( $wp_filesystem->exists( $from . $root . 'readme.html' ) && $wp_filesystem->exists( $from . $root . 'wp-includes/version.php' ) ) { $distro = $root; break; } } if ( ! $distro ) { $wp_filesystem->delete( $from, true ); return new WP_Error( 'insane_distro', __( 'The update could not be unpacked.' ) ); }
What it does here, is simply check if two files exists in the folder it has just unpacked the zip file to. This fails. And the reason is as follows:
I use FTP method for installing updates. So when I tell it to update, it first figures out the folder it should download the zipfile to. This folder is stored in $working_dir in wp-admin/includes/class-wp-upgrader.php -> unpack_package()
. The full path on the server is /domains/domainname.com/htdocs/wp-content/upgrade/ but since FTP users are chrooted, WP finds and stores /htdocs/wp-content/upgrade/ instead. The update file is downloaded to this folder and then unpacked.
Next it does the above check in wp-admin/includes/update-core.php -> update_core()
. And that failes because it tries to find a file in /htdocs/wp-content/upgrade/ while the true location is /domains/domainname.com/htdocs/wp-content/upgrade/.
Updating through FTP used to work fine in Debian 10 but in Debian 11, something broke...
Change History (19)
#2
follow-up:
↓ 3
@
3 years ago
Is there some reason you must use the FTP option to upgrade? Does upgrading work if you set the following in your
wp-config.php
?
define('FS_METHOD','direct');
#3
in reply to:
↑ 2
@
3 years ago
Replying to afragen:
Is there some reason you must use the FTP option to upgrade?
If I use the direct method, I first get this:
Downloading update from https://downloads.wordpress.org/release/wordpress-5.9.1-no-content.zip… The authenticity of wordpress-5.9.1-no-content.zip could not be verified as no signature was found. Unpacking the update… Could not create directory. Installation failed.
So next I do chmod 0777 wp-content/upgrade
and then I get this:
Downloading update from https://downloads.wordpress.org/release/wordpress-5.9.1-no-content.zip… The authenticity of wordpress-5.9.1-no-content.zip could not be verified as no signature was found. Unpacking the update… Verifying the unpacked files… Preparing to install the latest version… The update cannot be installed because we will be unable to copy some files. This is usually due to inconsistent file permissions.: wp-includes/block-supports/elements.php, wp-includes/block-supports/duotone.php, wp-includes/block-supports/layout.php, wp-includes/default-filters.php, wp-includes/block-template.php, wp-includes/css/dist/edit-site/style-rtl.min.css, wp-includes/css/dist/edit-site/style.min.css, wp-includes/css/dist/edit-site/style-rtl.css, wp-includes/css/dist/edit-site/style.css, wp-includes/css/dist/block-library/editor.min.css, wp-includes/css/dist/block-library/style-rtl.min.css, wp-includes/css/dist/block-library/common-rtl.min.css, wp-includes/css/dist/block-library/editor.css, wp-includes/css/dist/block-library/common.css, wp-includes/css/dist/block-library/style.min.css, wp-includes/css/dist/block-library/editor-rtl.css, wp-includes/css/dist/block-library/editor-rtl.min.css, wp-includes/css/dist/block-library/style-rtl.css, wp-includes/css/dist/block-library/common.min.css, wp-includes/css/dist/block-library/common-rtl.css, wp-includes/css/dist/block-library/style.css, wp-includes/css/dist/components/style-rtl.min.css, wp-includes/css/dist/components/style.min.css, wp-includes/css/dist/components/style-rtl.css, wp-includes/css/dist/components/style.css, wp-includes/css/dist/block-editor/style-rtl.min.css, wp-includes/css/dist/block-editor/style.min.css, wp-includes/css/dist/block-editor/style-rtl.css, wp-includes/css/dist/block-editor/style.css, wp-includes/class-wp-theme-json.php, wp-includes/script-loader.php, wp-includes/assets/script-loader-packages.php, wp-includes/blocks/gallery/style-rtl.min.css, wp-includes/blocks/gallery/style.min.css, wp-includes/blocks/gallery/style-rtl.css, wp-includes/blocks/gallery/style.css, wp-includes/blocks/site-title.php, wp-includes/blocks/page-list/editor.min.css, wp-includes/blocks/page-list/editor.css, wp-includes/blocks/page-list/editor-rtl.css, wp-includes/blocks/page-list/editor-rtl.min.css, wp-includes/blocks/page-list.php, wp-includes/blocks/search.php, wp-includes/blocks/post-template/style-rtl.min.css, wp-includes/blocks/post-template/style.min.css, wp-includes/blocks/post-template/style-rtl.css, wp-includes/blocks/post-template/style.css, wp-includes/blocks/post-featured-image/editor.min.css, wp-includes/blocks/post-featured-image/editor.css, wp-includes/blocks/post-featured-image/editor-rtl.css, wp-includes/blocks/post-featured-image/editor-rtl.min.css, wp-includes/blocks/index.php, wp-includes/blocks/image/style-rtl.min.css, wp-includes/blocks/image/style.min.css, wp-includes/blocks/image/style-rtl.css, wp-includes/blocks/image/style.css, wp-includes/blocks/cover/style-rtl.min.css, wp-includes/blocks/cover/style.min.css, wp-includes/blocks/cover/style-rtl.css, wp-includes/blocks/cover/style.css, wp-includes/blocks/site-logo.php, wp-includes/blocks/spacer/editor.min.css, wp-includes/blocks/spacer/block.json, wp-includes/blocks/spacer/editor.css, wp-includes/blocks/spacer/editor-rtl.css, wp-includes/blocks/spacer/editor-rtl.min.css, wp-includes/deprecated.php, wp-includes/version.php, wp-includes/class-wp-theme-json-resolver.php, wp-includes/rest-api/endpoints/class-wp-rest-global-styles-controller.php, wp-includes/block-editor.php, wp-includes/post.php, wp-includes/global-styles-and-settings.php, wp-includes/js/dist/core-data.js, wp-includes/js/dist/components.js, wp-includes/js/dist/edit-site.min.js, wp-includes/js/dist/block-library.min.js, wp-includes/js/dist/edit-post.min.js, wp-includes/js/dist/block-editor.min.js, wp-includes/js/dist/core-data.min.js, wp-includes/js/dist/components.min.js, wp-includes/js/dist/editor.min.js, wp-includes/js/dist/edit-site.js, wp-includes/js/dist/block-editor.js, wp-includes/js/dist/block-library.js, wp-includes/js/dist/editor.js, wp-includes/js/dist/edit-post.js, wp-includes/js/wp-ajax-response.min.js, wp-includes/js/jquery/ui/effect-fade.js, wp-includes/js/jquery/ui/controlgroup.min.js, wp-includes/js/jquery/ui/effect-highlight.min.js, wp-includes/js/jquery/ui/effect-clip.js, wp-includes/js/jquery/ui/effect-highlight.js, wp-includes/js/jquery/ui/button.js, wp-includes/js/jquery/ui/tabs.min.js, wp-includes/js/jquery/ui/selectable.min.js, wp-includes/js/jquery/ui/draggable.js, wp-includes/js/jquery/ui/droppable.min.js, wp-includes/js/jquery/ui/effect.js, wp-includes/js/jquery/ui/spinner.js, wp-includes/js/jquery/ui/effect-explode.min.js, wp-includes/js/jquery/ui/accordion.js, wp-includes/js/jquery/ui/core.min.js, wp-includes/js/jquery/ui/effect-pulsate.min.js, wp-includes/js/jquery/ui/mouse.min.js, wp-includes/js/jquery/ui/effect-puff.min.js, wp-includes/js/jquery/ui/effect-shake.js, wp-includes/js/jquery/ui/menu.js, wp-includes/js/jquery/ui/draggable.min.js, wp-includes/js/jquery/ui/selectmenu.js, wp-includes/js/jquery/ui/sortable.js, wp-includes/js/jquery/ui/core.js, wp-includes/js/jquery/ui/effect-slide.min.js, wp-includes/js/jquery/ui/effect-drop.js, wp-includes/js/jquery/ui/effect-size.js, wp-includes/js/jquery/ui/autocomplete.js, wp-includes/js/jquery/ui/menu.min.js, wp-includes/js/jquery/ui/tabs.js, wp-includes/js/jquery/ui/effect-bounce.min.js, wp-includes/js/jquery/ui/effect-drop.min.js, wp-includes/js/jquery/ui/selectable.js, wp-includes/js/jquery/ui/dialog.js, wp-includes/js/jquery/ui/effect-fold.min.js, wp-includes/js/jquery/ui/checkboxradio.min.js, wp-includes/js/jquery/ui/effect-puff.js, wp-includes/js/jquery/ui/autocomplete.min.js, wp-includes/js/jquery/ui/slider.js, wp-includes/js/jquery/ui/tooltip.min.js, wp-includes/js/jquery/ui/sortable.min.js, wp-includes/js/jquery/ui/droppable.js, wp-includes/js/jquery/ui/effect-blind.min.js, wp-includes/js/jquery/ui/effect-pulsate.js, wp-includes/js/jquery/ui/resizable.min.js, wp-includes/js/jquery/ui/datepicker.js, wp-includes/js/jquery/ui/effect-scale.js, wp-includes/js/jquery/ui/checkboxradio.js, wp-includes/js/jquery/ui/spinner.min.js, wp-includes/js/jquery/ui/button.min.js, wp-includes/js/jquery/ui/progressbar.min.js, wp-includes/js/jquery/ui/tooltip.js, wp-includes/js/jquery/ui/effect.min.js, wp-includes/js/jquery/ui/effect-transfer.js, wp-includes/js/jquery/ui/accordion.min.js, wp-includes/js/jquery/ui/effect-fade.min.js, wp-includes/js/jquery/ui/effect-fold.js, wp-includes/js/jquery/ui/effect-transfer.min.js, wp-includes/js/jquery/ui/mouse.js, wp-includes/js/jquery/ui/controlgroup.js, wp-includes/js/jquery/ui/progressbar.js, wp-includes/js/jquery/ui/effect-clip.min.js, wp-includes/js/jquery/ui/effect-scale.min.js, wp-includes/js/jquery/ui/selectmenu.min.js, wp-includes/js/jquery/ui/effect-blind.js, wp-includes/js/jquery/ui/effect-explode.js, wp-includes/js/jquery/ui/resizable.js, wp-includes/js/jquery/ui/dialog.min.js, wp-includes/js/jquery/ui/effect-slide.js, wp-includes/js/jquery/ui/slider.min.js, wp-includes/js/jquery/ui/effect-bounce.js, wp-includes/js/jquery/ui/effect-size.min.js, wp-includes/js/jquery/ui/datepicker.min.js, wp-includes/js/jquery/ui/effect-shake.min.js, wp-includes/js/wp-ajax-response.js, wp-includes/option.php, wp-admin/css/dashboard-rtl.css, wp-admin/css/dashboard-rtl.min.css, wp-admin/css/dashboard.min.css, wp-admin/css/dashboard.css, wp-admin/about.php, wp-admin/update-core.php, wp-admin/includes/upgrade.php, wp-admin/includes/update-core.php, wp-admin/includes/class-core-upgrader.php, wp-admin/includes/file.php, wp-admin/edit-form-blocks.php Installation failed.
I'm sure I can get this to work by setting the proper file permissions, but I remember now why I chose FTP: security. Can't remember exactly what the security concern was, but this is a webserver with multiple virtual hosts all running WordPress.
#4
in reply to:
↑ 1
@
3 years ago
Replying to costdev:
In the meantime, would you be able to set up a website using WordPress 5.7.5 on Debian 11 and try to upgrade to 5.8? This would let us confirm that the issue only lies within Debian 11, and not a mixture of Debian 11 and WordPress 5.9, and allow us to assign the
Version
property of the ticket accurately.
I'll try setting up a website using 5.7.5 and try to upgrade it to 5.8. Will return with the results.
#5
@
3 years ago
Ok, I installed WordPress 5.7.5 and tried to update it to 5.9.1. Got the exact same error:
Downloading update from https://downloads.wordpress.org/release/wordpress-5.9.1-new-bundled.zip… The authenticity of wordpress-5.9.1-new-bundled.zip could not be verified as no signature was found. Unpacking the update… Verifying the unpacked files… The update could not be unpacked Installation failed.
#6
@
3 years ago
Oh wait, did you want me to try to update to 5.8 or 5.9?
EDIT:
I tried upgrading to 5.8 (using the WP Upgrade plugin) and got the exact same error.
#7
follow-up:
↓ 8
@
3 years ago
Did you need to setup the chroot after your Debian upgrade? It seems that might be where your issue is, especially as downloaded file are in the expected locations.
#9
@
3 years ago
- Version changed from 5.9 to trunk
Ok so we can be sure that it's not a 5.9 issue as the error occurs when upgrading from 5.7.5 to 5.8.
Changing version to trunk
for now while we determine if something needs to change in Core, your permissions, or if it's a Debian 11 issue.
From Changing File Permissions:
If WordPress is running as the FTP account, that account needs to have write access, i.e., be the owner of the files, or belong to a group that has write access. In the latter case, that would mean permissions are set more permissively than default (for example, 775 rather than 755 for folders, and 664 instead of 644).
Let's see if the issue still occurs if you use 775 for folders and 664 for files:
cd /path/to/wordpress find . -type d -exec chmod 775 {} \; find . -type f -exec chmod 664 {} \;
#10
@
3 years ago
Actually, I've done some testing and I now know exactly what is causing the problem.
ftp_nlist() always returns false when the second parameter is a file instead of a directory on my system.
<?php $ftp_server = "ftp.myserver.com"; $ftp_user_name = "myusername"; $ftp_user_pass = "mypassword"; $ftp = ftp_connect($ftp_server); $login_result = ftp_login($ftp, $ftp_user_name, $ftp_user_pass); // check connection if ((!$ftp) || (!$login_result)) { echo "FTP connection has failed!\n"; exit; } else { echo "Connected.\n"; } ftp_pasv( $ftp, true ); $list = ftp_nlist( $ftp, '/' ); if( empty( $list ) ) { echo "File list is empty.\n"; } else { foreach( $list as $file ) { echo "$file\n"; } $list = ftp_nlist( $ftp, '/ftptest.php' ); if( ! $list ) { echo "\nNot found.\n"; } else { echo "\nFound!\n"; } } // close the FTP connection ftp_close($ftp); >
On my server, the output is:
Connected. /. /.. /htdocs /cgi-bin /logs /ftptest.php Not found.
EDIT:
BTW, running pure-ftpd-mysql v1.0.49
#11
@
3 years ago
In WP_Filesystem_FTPext::exists()
Docs, it calls $list = ftp_nlist( $this->link, $file )
, which returns false
.
The result is checked against empty( $list ) && $this->is_dir( $file )
, which passes the first condition and fails the second.
Then the WP_Filesystem_FTPext::exists()
returns ! empty( $list );
.
! false
=== true
=== the file exists, so this shouldn't be causing the problem here.
#12
follow-up:
↓ 13
@
3 years ago
Does adding this before the call to ftp_pasv()
change the result for you?
ftp_set_option( $ftp, FTP_USEPASVADDRESS, false );
#13
in reply to:
↑ 12
@
3 years ago
Replying to costdev:
Does adding this before the call to
ftp_pasv()
change the result for you?
ftp_set_option( $ftp, FTP_USEPASVADDRESS, false );
No, ftp_nlist() (in class-wp-filesystem-ftpext.php) still returns false on a file.
EDIT:
Sorry, also tried it in my code above- same result.
#14
follow-up:
↓ 15
@
3 years ago
Ok, it turns out to be a "problem" with the new version of Pure-FTPd that was installed with Debian 11. It installs v1.0.49 (previously installed version was v1.0.47) and according to their changelog here, globbing was removed from the NLST command in v1.0.48 (which makes sense since it's actually not allowed according to the RFC).
So it's weird that this problem hasn't been reported numerous times before since everybody who uses the FTP method to update and also uses Pure-FTPd, will have this problem...
The only solution is a rewrite of the code in class-wp-filesystem-ftpext.php, or switching to the direct method of updating. Which sucks because that opens a whole can of worms of security issues.
#16
@
3 years ago
In class-wp-filesystem-ftpext.php I replaced the exists() function with my own version (below) and the update succeeded without a hitch.
<?php public function exists( $file ) { $retval = false; $list = ftp_nlist( $this->link, $file ); if( ! empty( $list ) ) { // if ftp_nlist returns *something*, the file or directory exists, on any FTP server $retval = true; } else { // if ftp_nlist returns nothing, either the file/dir doesn't exist or it's a file and // the FTP server's NLST command doesn't support globbing (i.e. Pure-FTPD > v1.0.47) // Check if it'a file if( ftp_size( $this->link, $file ) >= 0 ) { $retval = true; } } return $retval; }
Hi @zippy1970, welcome to Trac!
I'll add
needs-testing
so that we can try to get the issue reproduced and see where the problem should be resolved.In the meantime, would you be able to set up a website using WordPress 5.7.5 on Debian 11 and try to upgrade to 5.8? This would let us confirm that the issue only lies within Debian 11, and not a mixture of Debian 11 and WordPress 5.9, and allow us to assign the
Version
property of the ticket accurately.