MDL-77149 core_files: Network filesystem (Amazon EFS) can warn

If you delete a file with a hash and then create another file with
the same hash, sometimes on EFS filesystems while trying to create
the new file, it returns true to the file_exists check even though
the file doesn't exist, but then fails other calls.

This change makes Moodle tolerate that behaviour.
This commit is contained in:
sam marshall 2023-02-06 15:21:46 +00:00
parent 3312a6814a
commit dadceea8f5

View File

@ -352,8 +352,9 @@ class file_system_filedir extends file_system {
$newfile = true;
if (file_exists($hashfile)) {
if (filesize($hashfile) === $filesize) {
$hashsize = self::check_file_exists_and_get_size($hashfile);
if ($hashsize !== null) {
if ($hashsize === $filesize) {
return array($contenthash, $filesize, false);
}
if (file_storage::hash_from_path($hashfile) === $contenthash) {
@ -411,6 +412,42 @@ class file_system_filedir extends file_system {
return array($contenthash, $filesize, $newfile);
}
/**
* Checks if the file exists and gets its size. This function avoids a specific issue with
* networked file systems if they incorrectly report the file exists, but then decide it doesn't
* as soon as you try to get the file size.
*
* @param string $hashfile File to check
* @return int|null Null if the file does not exist, or the result of filesize(), or -1 if error
*/
protected static function check_file_exists_and_get_size(string $hashfile): ?int {
if (!file_exists($hashfile)) {
// The file does not exist, return null.
return null;
}
// In some networked file systems, it's possible that file_exists will return true when
// the file doesn't exist (due to caching), but filesize will then return false because
// it doesn't exist.
$hashsize = @filesize($hashfile);
if ($hashsize !== false) {
// We successfully got a file size. Return it.
return $hashsize;
}
// If we can't get the filesize, let's check existence again to see if we really
// for sure think it exists.
clearstatcache();
if (!file_exists($hashfile)) {
// The file doesn't exist any more, so return null.
return null;
}
// It still thinks the file exists, but filesize failed, so we had better return an invalid
// value for filesize.
return -1;
}
/**
* Add a file with the supplied content to the file system.
*
@ -433,8 +470,9 @@ class file_system_filedir extends file_system {
$newfile = true;
if (file_exists($hashfile)) {
if (filesize($hashfile) === $filesize) {
$hashsize = self::check_file_exists_and_get_size($hashfile);
if ($hashsize !== null) {
if ($hashsize === $filesize) {
return array($contenthash, $filesize, false);
}
if (file_storage::hash_from_path($hashfile) === $contenthash) {