Opened 7 weeks ago
Last modified 4 days ago
#63173 new defect (bug)
FS_Method with ftpext and Core Updates
Reported by: |
|
Owned by: | |
---|---|---|---|
Milestone: | Awaiting Review | Priority: | normal |
Severity: | major | Version: | trunk |
Component: | Filesystem API | Keywords: | has-test-info dev-feedback has-patch |
Focuses: | Cc: |
Description
This is one of the strangest bugs I've found so far.
There is a weird behaving section in the code:
https://github.com/WordPress/wordpress-develop/blob/b943118a5b1967591fa1d15ee1ee87f93b7c0147/src/wp-admin/includes/update-core.php#L1057-L1073
When using FS_METHOD
ftpext
, it behaves in the following way:
- During a core update, it checks for files in the update package:
- It checks for readme.html and it finds it
- It checks for version.php and it doesn't find it.
But both do exist.
So it ends in this error: The update could not be unpacked
Given how generic everything is, this doesn't make sense. FTP Methods for FS_METHOD
have been my nemesis for a decade.
I've pushed my current FTP setup in case anyone wants to test this issue on top of this:
https://github.com/WordPress/wordpress-develop/pull/8589
Once set-up, for wp-config.php
we need:
define( 'FS_METHOD', 'ftpext' ); define( 'FTP_USER', 'admin' ); define( 'FTP_PASS', 'password' ); define( 'FTP_HOST', 'ftp' ); define( 'FTP_SSL', false );
I'm going to add some screenshots to show the behaviour with more detail
Change History (7)
This ticket was mentioned in PR #8599 on WordPress/wordpress-develop by @SirLouen.
7 weeks ago
#2
- Keywords has-patch added
This patch is very tricky to test, here I'm going to provide some info and instructions.
The error comes when the FTP path is extremely long. This happens for example on core updates (with nightly versions), it creates a folder with a very long name, and then a full wordpress structure inside wp-content/upgrades
The simplest way to test this is the following:
- Set an FTP in the server
- Set up the FS_METHOD to ftpext (
wp-config.php
) with the FTP credentials - Install a WordPress nightly version but not the latest (go with the previous one so it will trigger an update)
You can also do these steps: https://core.trac.wordpress.org/ticket/62718#comment:20 to setup your own docker environment using the wordpress-develop
env
Once you have everything set-up:
- Go to the Admin area, then Dashboard > Updates
- Update to latest nightly button
- Without the patch it should throw an error:
The update could not be unpacked
- With the patch it might throw other errors unrelated to this particular report, but it will end updating.
Trac ticket: https://core.trac.wordpress.org/ticket/63173
#3
@
7 weeks ago
- Keywords needs-testing dev-feedback added
I've been researching, and it seems that ftp_nlist can be faulty when working with numerous files or longer paths
https://stackoverflow.com/questions/66416395/php-ftp-nlist-returning-false-when-retrieving-large-directory, specially if not using directory navigation to short distances with ftp_chdir
The better alternative is to switch to ftp_rawlist
which can handle paths more conveniently.
Luckily, it seems that ftp_nlist
is only used there.
https://github.com/WordPress/wordpress-develop/blob/baab2b8f2273319817edede1b3360f212cf42241/src/wp-admin/includes/class-wp-filesystem-ftpext.php#L427-L445
I've provided a patch for this. For performance purposes, we could still use ftp_nlist
under 2 directories, but thereafter, we should be using ftp_rawlist
for accuracy. Maybe even 3 directories.
I'm not sure how we could introduce some unit-tests apart from hard-coding some mocks for the PHP ftp functions.
#4
@
7 weeks ago
- Keywords needs-copy-review added; needs-testing removed
Just for more context, I want to provide just one example of what's going on (and this problem is scaling big): https://github.com/WordPress/wordpress-develop/blob/baab2b8f2273319817edede1b3360f212cf42241/src/wp-admin/includes/update-core.php#L1296-L1297
In a ftpext
, this is the delete operation: https://github.com/WordPress/wordpress-develop/blob/baab2b8f2273319817edede1b3360f212cf42241/src/wp-admin/includes/class-wp-filesystem-ftpext.php#L399-L400
.maintenance
is a file. But the deletion of .maintenance
is being handled as a directory (in fact, since it's not a directory and the function is not called as recursive, it will always be jumping to a recursive delete mode because this line is not specifying that it's a file with the 'f' type)
Yes, everything is working because ultimately this recursive function will end deleting most files. But as soon as we play with a more sensible filesystem (like in the sample I'm analyzing, a very specific FTP with ftpext
), everything falls apart. And this is affecting a nice amount of deletes, file creations as mkdirs, etc…
And this is why this report became a blocker to me (truly irrelevant blocker by the way because the warning was still there and the reason of why I started debugging all this)
So probably for the average user, this is completely irrelevant. But, as I say, this must be fixed taking all this context in mind. An innocuous patch that brings more good than evil.
#5
@
7 weeks ago
- Keywords needs-copy-review removed
I'm having serious issues with ftpext
and I'm trying to debug everything from scratch
For now, I'm 100% confident that ftp_nlist
is problematic, and I've been able to reproduce this consistently with this test file:
https://gist.github.com/SirLouen/ac3de9972a6b4c6d904f014921ab1a44
The algorithm I've proposed can be improved, I've going to submit a new commit with a better version. Basically, Ì've found that nlist
should be completely wiped, and not even be used for shorter paths.
#6
@
7 weeks ago
Finally, I've been able to do a full core update with the environment proposed.
I've located further issues in this function, I've repaired:
- It was not accounting for
.invisible
files, so I needed to add the-a
to the command - There is an ultra-tricky scenario, that might only happen in special file systems like FTP: when you point straight to root
/
, this is impossible that happens in other scenarios likedirect
because no one is going to mount their WP in their root directory for massive security concerns.
With this, I think that the function is completely ready for production, pinging Filesystem maintainer @costdev
I'm still wondering how people have been able to update core with ftpext
, maybe some ftp servers are very permissive with functions or something?
Time for Screenshots and debugging:
1st checkpoint: https://i.imgur.com/wVuHBvg.png
$from
=/wp-content/upgrade/wordpress-6.8-latest-sjCZm4
$root
=/wordpress/
So the full URL will be
/wp-content/upgrade/wordpress-6.8-latest-sjCZm4/wordpress/readme.html
2nd checkpoint: https://i.imgur.com/cE7zaYy.png
As expected,
$path = /wp-content/upgrade/wordpress-6.8-latest-sjCZm4/wordpress/readme.html
So far, so good.
3rd checkpoint: https://i.imgur.com/XLVnrdi.png
We can see that the $list has one item, hence
return true
So we can confirm that the first part is TRUE
Now lets go with the next file:
4th checkpoint: https://i.imgur.com/6UX1v0N.png
The
$path
looks decent:/wp-content/upgrade/wordpress-6.8-latest-sjCZm4/wordpress/wp-includes/version.php
And finally, the last part:
5th checkpoint: https://i.imgur.com/IXZ1eWg.png
$list
= array(0), hence the result is FALSEOk, wait, probably is a problem with permissions. Lets check the FTP:
readme.html
has 644, pretty commonThe parent directory of
readme.html
, has 755, everything is fine so far644 for
version.php
And finally... the parent directory of
version.php
:755! What kind of unexplained wizardry is this?