From 38b6b39cb34882e7905dc23073a96418c05e6ce4 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 29 Dec 2021 15:26:34 +0000 Subject: [PATCH] Filesystem: workaround a stupid Windows issue in safeFilePutContents() occasionally Windows will randomly decide to deny us access to rename the file for no reason whatsoever. If this happens, we attempt an old-style copy and delete. If the rename failed for a legit reason, the copy and delete should also fail and generate an error message. If it was Windows being a spaz, it should work normally without errors. --- src/utils/Filesystem.php | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/utils/Filesystem.php b/src/utils/Filesystem.php index fd3eca26b..4b7420ead 100644 --- a/src/utils/Filesystem.php +++ b/src/utils/Filesystem.php @@ -265,12 +265,33 @@ final class Filesystem{ throw new \RuntimeException("Failed to write to temporary file $temporaryFileName (possibly out of free disk space)"); } + //TODO: the @ prevents us receiving the actual error message, but right now it's necessary since we can't assume + //that the error handler has been set :( $renameTemporaryFileResult = $context !== null ? - rename($temporaryFileName, $fileName, $context) : - rename($temporaryFileName, $fileName); - + @rename($temporaryFileName, $fileName, $context) : + @rename($temporaryFileName, $fileName); if(!$renameTemporaryFileResult){ - throw new \RuntimeException("Failed to move temporary file contents into target file"); + /* + * The following code works around a bug in Windows where rename() will periodically decide to give us a + * spurious "Access is denied (code: 5)" error. As far as I could determine, the fault comes from Windows + * itself, but since I couldn't reliably reproduce the issue it's very hard to debug. + * + * The following code can be used to test. Usually it will fail anywhere before 100,000 iterations. + * + * for($i = 0; $i < 10_000_000; ++$i){ + * file_put_contents('ops.txt.0.tmp', 'some data ' . $i, 0); + * if(!rename('ops.txt.0.tmp', 'ops.txt')){ + * throw new \Error("something weird happened"); + * } + * } + */ + $copyTemporaryFileResult = $context !== null ? + copy($temporaryFileName, $fileName, $context) : + copy($temporaryFileName, $fileName); + if(!$copyTemporaryFileResult){ + throw new \RuntimeException("Failed to move temporary file contents into target file"); + } + @unlink($temporaryFileName); } } }