Skip to content

Instantly share code, notes, and snippets.

@AyrA
Created June 16, 2021 09:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AyrA/dd205487513823aa6e807b5f3b944c99 to your computer and use it in GitHub Desktop.
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.
<?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