Created
June 16, 2021 09:45
-
-
Save AyrA/dd205487513823aa6e807b5f3b944c99 to your computer and use it in GitHub Desktop.
Copy of 4GB+ files failes with PHP on Windows even if FS is NTFS and PHP is 64 bit.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
//Both files must be on an NTFS volume. You may use the same volume. | |
$src='C:/temp/long.bin'; | |
$dst='D:/temp/long.bin'; | |
define('SIZE_4G',0x100000000); | |
//Create large file with mostly zeros but random content at the end | |
function create_file($src){ | |
@unlink($src); | |
echo 'Creating sparse via fsutil...'; | |
exec('fsutil file createnew ' . escapeshellarg($src) . ' ' . (SIZE_4G-0x100)); | |
echo ' OK' . PHP_EOL; | |
echo 'Appending some random bytes...'; | |
if($fp=fopen($src,'ab')){ | |
fwrite($fp,random_bytes(0x200)); | |
fclose($fp); | |
echo ' OK' . PHP_EOL; | |
return TRUE; | |
} | |
echo ' FAIL' . PHP_EOL; | |
return FALSE; | |
} | |
//Copy using copy() | |
function copy_1($src,$dst){ | |
!file_exists($dst) or unlink($dst) or die('dst in use?'); | |
copy($src,$dst); | |
} | |
//Copy using stream_copy_to_stream() | |
function copy_2($src,$dst){ | |
!file_exists($dst) or unlink($dst) or die('dst in use?'); | |
$f1=fopen($src,'rb') or die('src open failed'); | |
$f2=fopen($dst,'wb') or die('dst open failed'); | |
stream_copy_to_stream($f1,$f2); | |
fclose($f1); | |
fclose($f2); | |
} | |
//Copy manually | |
function copy_3($src,$dst){ | |
!file_exists($dst) or unlink($dst) or die('dst in use?'); | |
$f1=fopen($src,'rb') or die('src open failed'); | |
$f2=fopen($dst,'wb') or die('dst open failed'); | |
while(!feof($f1)){ | |
fwrite($f2,fread($f1,1024*1024*100)); | |
echo '.'; | |
} | |
fclose($f1); | |
fclose($f2); | |
} | |
//Verify contents | |
function verify($src,$dst){ | |
$buffer=1024*1024*100; | |
if(filesize($src)!==filesize($dst)){ | |
echo 'Files have different sizes!' . PHP_EOL; | |
return FALSE; | |
} | |
$f1=fopen($src,'rb') or die('src open failed'); | |
$f2=fopen($dst,'rb') or die('dst open failed'); | |
//Seek to 4 GB boundary, as this is the location where the problem occurs | |
fseek($f1,SIZE_4G-0x100,SEEK_SET); | |
fseek($f2,SIZE_4G-0x100,SEEK_SET); | |
$identical=fread($f1,0x200)===fread($f2,0x200); | |
if(!$identical){ | |
echo 'Files have different contents across 4GB boundary!' . PHP_EOL; | |
} | |
else{ | |
echo 'Files OK' . PHP_EOL; | |
} | |
fclose($f1); | |
fclose($f2); | |
return $identical; | |
} | |
!file_exists($dst) or unlink($dst) or die('dst in use?'); | |
echo 'Creating very large file' . PHP_EOL; | |
create_file($src) or die('create failed'); | |
echo 'File created. Size: ' . filesize($src) . ' bytes' . PHP_EOL; | |
echo 'Duplicate file using copy()...'; | |
copy_1($src,$dst); | |
echo ' Done' . PHP_EOL; | |
verify($src,$dst); | |
echo 'Duplicate file using stream_copy_to_stream()...'; | |
copy_2($src,$dst); | |
echo ' Done' . PHP_EOL; | |
verify($src,$dst); | |
echo 'Duplicate file using fread() and fwrite()...'; | |
copy_3($src,$dst); | |
echo ' Done' . PHP_EOL; | |
verify($src,$dst); | |
echo 'Done' . PHP_EOL; | |
echo 'PHP: ' . phpversion() . PHP_EOL; | |
echo 'Is x64: ' . (PHP_INT_MAX>0xFFFFFFFF?'Y':'N') . PHP_EOL; | |
echo 'OS: ' . php_uname(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment