? ? Success

User tests: Successful: Unsuccessful:

avatar SharkyKZ
SharkyKZ
29 Sep 2020

Closes #23725 and #30736 .

Summary of Changes

This PR attempts to implement case sensitive file rename and rename some incorrectly cased files.

Testing Instructions

Coming soon.

Actual result BEFORE applying this Pull Request

Expected result AFTER applying this Pull Request

Documentation Changes Required

No.

avatar SharkyKZ SharkyKZ - open - 29 Sep 2020
avatar SharkyKZ SharkyKZ - change - 29 Sep 2020
Status New Pending
avatar joomla-cms-bot joomla-cms-bot - change - 29 Sep 2020
Category Administration com_admin
avatar SharkyKZ SharkyKZ - change - 29 Sep 2020
Labels Added: ?
avatar HLeithner
HLeithner - comment - 29 Sep 2020

I would prefer to have this in the framework at least the actual renaming including tests for all possible variants.

avatar joomla-cms-bot joomla-cms-bot - change - 4 Nov 2020
Category Administration com_admin Administration com_admin Libraries External Library Composer Change
avatar HLeithner HLeithner - change - 29 Dec 2020
Labels Added: ?
avatar HLeithner
HLeithner - comment - 30 Dec 2020

@SharkyKZ I merged current j3 into your pr and tested the upgrade and some basic functionality and it still works. Would you remove the draft status. @bembelimen cherry picked the relevant commit for j4 in #31804 to fix the release blocker.

So it would makes sense to merge this into j3 to complete the task. thanks

avatar richard67 richard67 - change - 5 Jan 2021
The description was changed
avatar richard67 richard67 - edited - 5 Jan 2021
avatar bembelimen
bembelimen - comment - 5 Jan 2021

Test cases:
Install Joomla! <= 3.9.23 => check the following files: https://github.com/joomla/joomla-cms/pull/30802/files#diff-db7eb77540ff419bd7e6557d2779f4cf1e31158c20cb4b63dbbe8d6c426385adR2670-R2674
Use https://ci.joomla.org/artifacts/joomla/joomla-cms/staging/30802/downloads/38795/ for Update

  • Update with the Joomla Updater
  • Copy the files via FTP and use "Fix Database"
  • Install the package as a fresh installation

Now check the file list again => files should in all three test cases have the correct name.

Especially on windows system tests are needed.

avatar bembelimen
bembelimen - comment - 6 Jan 2021

I have tested this item successfully on f349349


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/30802.

avatar bembelimen bembelimen - test_item - 6 Jan 2021 - Tested successfully
avatar wilsonge
wilsonge - comment - 6 Jan 2021

Following @bembelimen testing instructions above on OSX all the files mention are deleted after applying patch and not renamed. Admittedly OSX isn't exactly a realistic host. But yeah not working :(

avatar chmst
chmst - comment - 6 Jan 2021

Tested on win10 and it works as described, Files are renamed.

avatar softforge
softforge - comment - 6 Jan 2021

I have tested this item successfully on f349349

I have tested this on a centos server (linux) and it worked like a charm. All the test cases were correct after the change


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/30802.

avatar softforge softforge - test_item - 6 Jan 2021 - Tested successfully
avatar richard67
richard67 - comment - 6 Jan 2021

@wilsonge Do you think you can find the time to add some debug log (error_log or whatever) to see what goes wrong on OSX?

avatar HLeithner
HLeithner - comment - 6 Jan 2021

@wilsonge can you run only the relevant code?

avatar HLeithner
HLeithner - comment - 6 Jan 2021

I postponed it after 3.9.24 release so not urgent except that j4 waits for this pr #31804 and PRs pending on it like skipTo js update

avatar wilsonge
wilsonge - comment - 7 Jan 2021
<?php

function fixFilenameCasing()
{
    $files = array(
        'libraries/src/Filesystem/Support/Stringcontroller.php' => 'libraries/src/Filesystem/Support/StringController.php',
//        'libraries/vendor/paragonie/sodium_compat/src/Core/Xsalsa20.php' => 'libraries/vendor/paragonie/sodium_compat/src/Core/XSalsa20.php',
//        'media/mod_languages/images/si_LK.gif' => 'media/mod_languages/images/si_lk.gif',
    );

    foreach ($files as $old => $expected)
    {
        $oldRealpath = realpath(__DIR__ . '/' . $old);

        // On Unix without incorrectly cased file.
        if ($oldRealpath === false)
        {
            continue;
        }

        $oldBasename      = basename($oldRealpath);
        $newRealpath      = realpath(__DIR__ . '/' . $expected);
        $newBasename      = basename($newRealpath);
        $expectedBasename = basename($expected);

	    var_dump('Old Real Path: ' . $oldRealpath);
	    var_dump('New Real Path: ' . $newRealpath);
	    var_dump('New Base Name: ' . $newBasename);
	    var_dump('Expected Base Name: ' . $expectedBasename);

	    // On Windows with incorrectly cased file.
        if ($newBasename !== $expectedBasename)
        {
            // Rename the file.
            rename(__DIR__ . '/' . $old, __DIR__ . '/' . $old . '.tmp');
            rename(__DIR__ . '/' . $old . '.tmp', __DIR__ . '/' . $expected);

            continue;
        }

        // On Unix with correctly and incorrectly cased files.
        if ($oldBasename === basename($old) && $newBasename === $expectedBasename)
        {
            // Delete the file.
            unlink(__DIR__ . '/' . $old);
        }
    }
}

fixFilenameCasing();

So I've run this in the root dir of Joomla to decouple of it - same behaviour. var_dumps on OSX give me:

string(99) "Old Real Path: /Users/george/Sites/joomla-cms/libraries/src/Filesystem/Support/Stringcontroller.php"
string(99) "New Real Path: /Users/george/Sites/joomla-cms/libraries/src/Filesystem/Support/StringController.php"
string(35) "New Base Name: StringController.php"
string(40) "Expected Base Name: StringController.php"

Not 100% sure what I'm expecting to be seeing on other filesystems as a comparison though. If you and @bembelimen could give the results of those 4 vars on windows and linux would be helpful so I can compare

Screenshot 2021-01-07 at 01 54 05

And the path libraries/src/Filesystem/Support is empty of any files so it's not a git status bug ;)

avatar richard67
richard67 - comment - 7 Jan 2021

@wilsonge The problem might be that the stringcontroller.php file is still in the list of files to be removed:

'/libraries/joomla/filesystem/support/stringcontroller.php',
. I think the files to be renamed should not be in the list of files to be removed. For the other files to be renamed, the language flag and the xsalsa thing, this is given.

Update after 2 minutes: Sorry, I was wrong, the path to the file is different, so that should not be the problem.

avatar richard67
richard67 - comment - 7 Jan 2021

@wilsonge Could you also log the value of $newRealpath? Sorry, I need more coffee.

avatar richard67
richard67 - comment - 7 Jan 2021

@wilsonge I think the problem is what is mentioned in the comment on https://www.php.net/manual/en/function.realpath.php:

Note:
For case-insensitive filesystems realpath() may or may not normalize the character case.

That seems to work differently on Windows and OSX: On Windows the $newRealpath could be false because the file doesn't exist, while on OSX it is as shown in your test.

@bembelimen Could you check the value of $newRealpathon Windows?

Update: See also discussion in https://bugs.php.net/bug.php?id=67220.

avatar richard67
richard67 - comment - 7 Jan 2021

@wilsonge I think the solution could be to use pathinfo(..., PATHINFO_BASENAME); instead of basename(...) (except of the $expectedBasename). As https://www.php.net/manual/en/function.basename.php says, the basename function just uses the input string and not the real file info.

That would be for your testing script:

<?php

function fixFilenameCasing()
{
    $files = array(
        'libraries/src/Filesystem/Support/Stringcontroller.php' => 'libraries/src/Filesystem/Support/StringController.php',
//        'libraries/vendor/paragonie/sodium_compat/src/Core/Xsalsa20.php' => 'libraries/vendor/paragonie/sodium_compat/src/Core/XSalsa20.php',
//        'media/mod_languages/images/si_LK.gif' => 'media/mod_languages/images/si_lk.gif',
    );

    foreach ($files as $old => $expected)
    {
        $oldRealpath = realpath(__DIR__ . '/' . $old);

        // On Unix without incorrectly cased file.
        if ($oldRealpath === false)
        {
            continue;
        }

        $oldBasename      = pathinfo($oldRealpath, PATHINFO_BASENAME);
        $newRealpath      = realpath(__DIR__ . '/' . $expected);
        $newBasename      = pathinfo($newRealpath, PATHINFO_BASENAME);
        $expectedBasename = basename($expected);

        var_dump('Old Real Path: ' . $oldRealpath);
        var_dump('New Real Path: ' . $newRealpath);
        var_dump('New Base Name: ' . $newBasename);
        var_dump('Expected Base Name: ' . $expectedBasename);

        // On Windows with incorrectly cased file.
        if ($newBasename !== $expectedBasename)
        {
            // Rename the file.
            rename(__DIR__ . '/' . $old, __DIR__ . '/' . $old . '.tmp');
            rename(__DIR__ . '/' . $old . '.tmp', __DIR__ . '/' . $expected);

            continue;
        }

        // On Unix with correctly and incorrectly cased files.
        if ($oldBasename === basename($old) && $newBasename === $expectedBasename)
        {
            // Delete the file.
            unlink(__DIR__ . '/' . $old);
        }
    }
}

fixFilenameCasing();
avatar richard67
richard67 - comment - 8 Jan 2021

Meanwhile it turned out that my above suggestion won't help, thanks @wilsonge for testing on OSX. Am investigating possible solutions. Stay tuned.

avatar HLeithner
HLeithner - comment - 8 Jan 2021

it may or may not be osx it self, it could depend on the filesystem used in osx and if it's setting os x supports both case sensitive and insensitive files per setting per filesystem.

btw. I think it's possible we have the same problem on windows and linux.

avatar richard67
richard67 - comment - 8 Jan 2021

it may or may not be osx it self, it could depend on the filesystem used in osx and if it's setting os x supports both case sensitive and insensitive files per setting per filesystem.

I agree. But I think I have a solution almost ready.

avatar richard67
richard67 - comment - 8 Jan 2021

@wilsonge or whoever else has OSX for testing: Please test the following test script:

<?php

function fixFilenameCasing()
{
    $files = array(
        'libraries/src/Filesystem/Support/Stringcontroller.php' => 'libraries/src/Filesystem/Support/StringController.php',
//        'libraries/vendor/paragonie/sodium_compat/src/Core/Xsalsa20.php' => 'libraries/vendor/paragonie/sodium_compat/src/Core/XSalsa20.php',
//        'media/mod_languages/images/si_LK.gif' => 'media/mod_languages/images/si_lk.gif',
    );

    foreach ($files as $old => $expected)
    {
        $oldRealpath = realpath(__DIR__ . '/' . $old);

        // On case-sensitive file system without incorrectly cased file.
        if ($oldRealpath === false)
        {
            continue;
        }

        $oldBasename      = basename($oldRealpath);
        $oldInode         = fileinode($oldRealpath);
        $newRealpath      = realpath(__DIR__ . '/' . $expected);
        $newBasename      = basename($newRealpath);
        $newInode         = fileinode($newRealpath);
        $expectedBasename = basename($expected);

        var_dump('Old Real Path: ' . $oldRealpath);
        var_dump('New Real Path: ' . $newRealpath);
        var_dump('Old Base Name: ' . $oldBasename);
        var_dump('basename($old): ' . basename($old));
        var_dump('New Base Name: ' . $newBasename);
        var_dump('Expected Base Name: ' . $expectedBasename);
        var_dump('Old Inode: ' . $oldInode);
        var_dump('New Inode: ' . $newInode);

        // On case-insensitive file system with incorrectly cased file or where it's not clear.
        if ($newBasename !== $expectedBasename || $newInode === $oldInode)
        {
            // Rename the file.
            rename(__DIR__ . '/' . $old, __DIR__ . '/' . $old . '.tmp');
            rename(__DIR__ . '/' . $old . '.tmp', __DIR__ . '/' . $expected);

            continue;
        }

        // On case-sensitive file system with correctly and incorrectly cased files.
        if ($oldBasename === basename($old) && $newBasename === $expectedBasename)
        {
            // Delete the file.
            unlink(__DIR__ . '/' . $old);
        }
    }
}

fixFilenameCasing();
avatar wilsonge
wilsonge - comment - 8 Jan 2021

That works. But it does try and rename the file on every run.

string(99) "Old Real Path: /Users/george/Sites/joomla-cms/libraries/src/Filesystem/Support/Stringcontroller.php"
string(99) "New Real Path: /Users/george/Sites/joomla-cms/libraries/src/Filesystem/Support/StringController.php"
string(35) "Old Base Name: Stringcontroller.php"
string(36) "basename($old): Stringcontroller.php"
string(35) "New Base Name: StringController.php"
string(40) "Expected Base Name: StringController.php"
string(21) "Old Inode: 8689842387"
string(21) "New Inode: 8689842387"
avatar richard67
richard67 - comment - 8 Jan 2021

That works. But it does try and rename the file on every run.

I know ? . No idea how I can avoid that.

P.S.: That's only a problem on that kind of file system. On Linux it tries it only 1 time.

P.P.S.: ... and it is a problem on Windows now, too, I think.

avatar richard67
richard67 - comment - 8 Jan 2021

@wilsonge Do you also have Windows for testing?

avatar richard67
richard67 - comment - 8 Jan 2021

@wilsonge Could you add following to the test script just below the other var_dump and report back the result?

var_dump('glob($oldRealpath, GLOB_NOESCAPE)[0]: ' . glob($oldRealpath, GLOB_NOESCAPE)[0]);
var_dump('glob($newRealpath, GLOB_NOESCAPE)[0]: ' . glob($newRealpath, GLOB_NOESCAPE)[0]);

Update: Meanwhile I can test on Windows, too. Using e.g. glob($oldRealpath, GLOB_NOESCAPE)[0] would not change anything for Linux and Windows, i.e. things still would work. So the answer to this question here would tell us if it would help for OSX.

avatar richard67
richard67 - comment - 8 Jan 2021

@wilsonge New testing script:

<?php

function fixFilenameCasing()
{
    $files = array(
        'libraries/src/Filesystem/Support/Stringcontroller.php' => 'libraries/src/Filesystem/Support/StringController.php',
//        'libraries/vendor/paragonie/sodium_compat/src/Core/Xsalsa20.php' => 'libraries/vendor/paragonie/sodium_compat/src/Core/XSalsa20.php',
//        'media/mod_languages/images/si_LK.gif' => 'media/mod_languages/images/si_lk.gif',
    );

    foreach ($files as $old => $expected)
    {
        $oldRealpath = glob(realpath(__DIR__ . '/' . $old), GLOB_NOESCAPE)[0] ?? false;

        // On case-sensitive file system without incorrectly cased file.
        if ($oldRealpath === false)
        {
            continue;
        }

        $oldBasename      = basename($oldRealpath);
        $newRealpath      = glob(realpath(__DIR__ . '/' . $expected), GLOB_NOESCAPE)[0] ?? false;
        $newBasename      = basename($newRealpath);
        $expectedBasename = basename($expected);

        var_dump('Old Real Path: ' . $oldRealpath);
        var_dump('Old Base Name: ' . $oldBasename);
        var_dump('basename($old): ' . basename($old));
        var_dump('New Real Path: ' . $newRealpath);
        var_dump('New Base Name: ' . $newBasename);
        var_dump('Expected Base Name: ' . $expectedBasename);

        // On any file system with incorrectly cased file only.
        if ($newBasename !== $expectedBasename)
        {
            // Rename the file.
            rename(__DIR__ . '/' . $old, __DIR__ . '/' . $old . '.tmp');
            rename(__DIR__ . '/' . $old . '.tmp', __DIR__ . '/' . $expected);

            continue;
        }

        // On case-sensitive file system with correctly and incorrectly cased files.
        if ($oldBasename === basename($old))
        {
            // Delete the file.
            unlink(__DIR__ . '/' . $old);
        }
    }
}

fixFilenameCasing();
avatar wilsonge
wilsonge - comment - 9 Jan 2021
george@ ~/Sites/joomla-cms (fix/totp-feature) $ php test.php 
string(99) "Old Real Path: /Users/george/Sites/joomla-cms/libraries/src/Filesystem/Support/Stringcontroller.php"
string(35) "Old Base Name: Stringcontroller.php"
string(36) "basename($old): Stringcontroller.php"
string(99) "New Real Path: /Users/george/Sites/joomla-cms/libraries/src/Filesystem/Support/StringController.php"
string(35) "New Base Name: StringController.php"
string(40) "Expected Base Name: StringController.php"

it just deletes the file

avatar richard67
richard67 - comment - 9 Jan 2021

So no change. Then we either do my previous idea with the inode which renames the file always, or I have no idea what else we can do.

avatar wilsonge
wilsonge - comment - 9 Jan 2021

The extra var_dumps are more interesting though -

string(122) "glob($oldRealpath, GLOB_NOESCAPE)[0]: /Users/george/Sites/joomla-cms/libraries/src/Filesystem/Support/Stringcontroller.php"
string(122) "glob($newRealpath, GLOB_NOESCAPE)[0]: /Users/george/Sites/joomla-cms/libraries/src/Filesystem/Support/StringController.php"
avatar richard67
richard67 - comment - 9 Jan 2021

Interesting .. and exactly that's the problem. We don't really get the actual case.

avatar richard67
richard67 - comment - 10 Jan 2021

@wilsonge Next iteration: Could you test with the following test script?

<?php

function fixFilenameCasing()
{
    $files = array(
        'libraries/src/Filesystem/Support/Stringcontroller.php' => 'libraries/src/Filesystem/Support/StringController.php',
//        'libraries/vendor/paragonie/sodium_compat/src/Core/Xsalsa20.php' => 'libraries/vendor/paragonie/sodium_compat/src/Core/XSalsa20.php',
//        'media/mod_languages/images/si_LK.gif' => 'media/mod_languages/images/si_lk.gif',
    );

    foreach ($files as $old => $expected)
    {
        $oldRealpath = realpath(__DIR__ . '/' . $old);

        // On Unix without incorrectly cased file.
        if ($oldRealpath === false)
        {
            echo "Unix without incorrectly cased file.\n";

            continue;
        }

        $oldBasename      = basename($oldRealpath);
        $newRealpath      = realpath(__DIR__ . '/' . $expected);
        $newBasename      = basename($newRealpath);
        $expectedBasename = basename($expected);

        var_dump('Old Real Path: ' . $oldRealpath);
        var_dump('Old Base Name: ' . $oldBasename);
        var_dump('basename($old): ' . basename($old));
        var_dump('New Real Path: ' . $newRealpath);
        var_dump('New Base Name: ' . $newBasename);
        var_dump('Expected Base Name: ' . $expectedBasename);

        // On Windows or Unix with only the incorrectly cased file.
        if ($newBasename !== $expectedBasename)
        {
            echo "Renaming the file.\n";

            // Rename the file.
            rename(__DIR__ . '/' . $old, __DIR__ . '/' . $old . '.tmp');
            rename(__DIR__ . '/' . $old . '.tmp', __DIR__ . '/' . $expected);

            continue;
        }

        // There might still be an incorrectly cased file on other OS than Windows.
        if ($oldBasename === basename($old))
        {
            // Check if case-insensitive file system, eg on OSX.
            if (fileinode($oldRealpath) === fileinode($newRealpath))
            {
                // Check deeper because even realpath or glob might not return the actual case.
                if (!in_array($expectedBasename, scandir(dirname($newRealpath))))
                {
                    echo "Renaming the file on OSX.\n";

                    // Rename the file.
                    rename(__DIR__ . '/' . $old, __DIR__ . '/' . $old . '.tmp');
                    rename(__DIR__ . '/' . $old . '.tmp', __DIR__ . '/' . $expected);
                }
            }
            else
            {
                // On Unix with correctly and incorrectly cased files.
                echo "Deleting the incorrectly cased file on Unix.\n";

                unlink(__DIR__ . '/' . $old);
            }
        }
    }
}

fixFilenameCasing();

Thanks in advance.

avatar wilsonge
wilsonge - comment - 10 Jan 2021
george@ ~/Sites/joomla-cms (4.0-dev) $ php test.php 
string(99) "Old Real Path: /Users/george/Sites/joomla-cms/libraries/src/Filesystem/Support/Stringcontroller.php"
string(35) "Old Base Name: Stringcontroller.php"
string(36) "basename($old): Stringcontroller.php"
string(99) "New Real Path: /Users/george/Sites/joomla-cms/libraries/src/Filesystem/Support/StringController.php"
string(35) "New Base Name: StringController.php"
string(40) "Expected Base Name: StringController.php"
Renaming the file on OSX.
george@ ~/Sites/joomla-cms (4.0-dev) $ php test.php 
string(99) "Old Real Path: /Users/george/Sites/joomla-cms/libraries/src/Filesystem/Support/Stringcontroller.php"
string(35) "Old Base Name: Stringcontroller.php"
string(36) "basename($old): Stringcontroller.php"
string(99) "New Real Path: /Users/george/Sites/joomla-cms/libraries/src/Filesystem/Support/StringController.php"
string(35) "New Base Name: StringController.php"
string(40) "Expected Base Name: StringController.php"

I'd say that looks pretty good!

avatar richard67
richard67 - comment - 10 Jan 2021

Made PR SharkyKZ#8 with these changes.

avatar richard67
richard67 - comment - 11 Jan 2021

As @SharkyKZ doesn't want to merge my PR I have made a new PR which replaces this one here. Please test #32006 .

avatar richard67
richard67 - comment - 11 Jan 2021

Closing in favour of #32006 .

avatar richard67 richard67 - change - 11 Jan 2021
Status Pending Closed
Closed_Date 0000-00-00 00:00:00 2021-01-11 09:58:03
Closed_By richard67
avatar richard67 richard67 - close - 11 Jan 2021
avatar richard67
richard67 - comment - 17 Jan 2021

Add a Comment

Login with GitHub to post a comment