[evolvis-commits] r18420: frs-download-as-zip pending merges:↵ Thorsten Glaser 2012-05-21 implement a 64 MiB limit on total ZIP archive member size↵ Thorsten Glaser 2012-05-21 explain why all PHP methods *and* stat(1) would fail to get the filesize↵ Thorsten Glaser 2012-05-21 refuse to archive individual files larger than 16 MiB↵ Thorsten Glaser 2012-05-21 no symlinks either, ma’am↵ Thorsten Glaser 2012-05-21 prevent trying to archive non-existing or non-files or non-readable f...↵ Thorsten Glaser 2012-05-21 because the ZipArchive::close method *insists* of creating a tempfile↵ to create the archive with and then rename it to the target name↵ atomically, which is a nice idea in a Unix world but the most idiotic↵ thing to do in a PHP world where scripts are forcibly terminated when↵ running out of ressource allowance (usually, execution time), preventing↵ the tempfiles from being cleaned up, add a daily cronjob doing just that.

mirabilos at evolvis.org mirabilos at evolvis.org
Mon May 21 11:53:37 CEST 2012


Author: mirabilos
Date: 2012-05-21 11:53:36 +0200 (Mon, 21 May 2012)
New Revision: 18420

Modified:
   trunk/gforge_base/evolvisforge-5.1/src/common/frs/FRSPackage.class.php
   trunk/gforge_base/evolvisforge-5.1/src/cronjobs/project_cleanup.php
Log:
frs-download-as-zip pending merges:
  Thorsten Glaser 2012-05-21 implement a 64 MiB limit on total ZIP archive member size
    Thorsten Glaser 2012-05-21 explain why all PHP methods *and* stat(1) would fail to get the filesize
    Thorsten Glaser 2012-05-21 refuse to archive individual files larger than 16 MiB
    Thorsten Glaser 2012-05-21 no symlinks either, ma’am
    Thorsten Glaser 2012-05-21 prevent trying to archive non-existing or non-files or non-readable f...
    Thorsten Glaser 2012-05-21 because the ZipArchive::close method *insists* of creating a tempfile
	to create the archive with and then rename it to the target name
	atomically, which is a nice idea in a Unix world but the most idiotic
	thing to do in a PHP world where scripts are forcibly terminated when
	running out of ressource allowance (usually, execution time), preventing
	the tempfiles from being cleaned up, add a daily cronjob doing just that.

	*spit*
    Thorsten Glaser 2012-05-21 attempt (ATTEMPT!) some (SOME!) error handling for ZIP creation

I hope that all these mechanisms will reduce the likelihood of tempfile
leaks due to ressource limit exceeding

Modified: trunk/gforge_base/evolvisforge-5.1/src/common/frs/FRSPackage.class.php
===================================================================
--- trunk/gforge_base/evolvisforge-5.1/src/common/frs/FRSPackage.class.php	2012-05-21 09:53:32 UTC (rev 18419)
+++ trunk/gforge_base/evolvisforge-5.1/src/common/frs/FRSPackage.class.php	2012-05-21 09:53:36 UTC (rev 18420)
@@ -506,22 +506,77 @@
 	public function createNewestReleaseFilesAsZip(){
 		$zip = new ZipArchive();
 		$release = $this->getNewestRelease();
+		$files = $release->getFiles();
 
 		$zipPath = $this->getNewestReleaseZipPath();
 		$filesPath = forge_get_config('upload_dir').'/'.$this->Group->getUnixName().'/'.$this->getFileName().'/'.$release->getFileName();
 
-		if ($zip->open($zipPath, ZIPARCHIVE::OVERWRITE)!==true) {
-			exit_error(_('Cannot open the file archive.').' '.$zipPath.'.');
+		$tsz = 0;
+		foreach ($files as $f) {
+			$filePath = $filesPath.'/'.$f->getName();
+			if (!file_exists($filePath) ||
+			    is_link($filePath) ||
+			    !is_file($filePath) ||
+			    !is_readable($filePath)) {
+				exit_error(sprintf(_('"%s" is not a readable regular file'),
+				    $filePath), 'frs');
+			}
+
+			/*
+			 * PHP freaks out at large files, stat isn’t
+			 * portable enough even across coreutils versions,
+			 * we could probably use Perl to get it – but…
+			 * let’s just hope we never encounter a file of,
+			 * say 4 GiB + 1 byte, size. (Especially as they
+			 * are uploaded into that directory via PHP.)
+			 */
+			$fsz = filesize($filePath);
+
+			if ($fsz > 16777216) {
+				exit_error(sprintf(_('"%s" is bigger than 16 MiB, will not archive such huge files'),
+				    $filePath), 'frs');
+			}
+
+			$tsz += $fsz;
+			if ($tsz > 67108864) {
+				exit_error(_('Will not archive files with a total of more than 64 MiB size'),
+				    'frs');
+			}
 		}
 
-		$files = $release->getFiles();
-	
+		@unlink($zipPath);
+		if (file_exists($zipPath)) {
+			exit_error(sprintf(_('Cannot delete old version of the file "%s"'),
+			    $zipPath), 'frs');
+		}
+
+		if ($zip->open($zipPath, ZIPARCHIVE::CREATE |
+		    ZIPARCHIVE::OVERWRITE) !== true) {
+			exit_error(sprintf(_('Cannot open the file archive "%s"'),
+			    $zipPath), 'frs');
+		}
+
+
+		$has_error = false;
 		foreach ($files as $f) {
 			$filePath = $filesPath.'/'.$f->getName();	
-			$zip->addFile($filePath,$f->getName());
+			if (!$zip->addFile($filePath, $f->getName())) {
+				/*
+				 * This is very ugly: we cannot abort here
+				 * without calling $zip->close() which will
+				 * write the already added files to disc
+				 * nevertheless. There’s no method to abort
+				 * archive creation, and we cannot simply
+				 * leave because it locks the files. Ugh.
+				 */
+				$has_error = true;
+				break;
+			}
 		} 
 
-		$zip->close();
+		if (!$zip->close() || $has_error) {
+			@unlink($zipPath);
+		}
 	}
 
 }

Modified: trunk/gforge_base/evolvisforge-5.1/src/cronjobs/project_cleanup.php
===================================================================
--- trunk/gforge_base/evolvisforge-5.1/src/cronjobs/project_cleanup.php	2012-05-21 09:53:32 UTC (rev 18419)
+++ trunk/gforge_base/evolvisforge-5.1/src/cronjobs/project_cleanup.php	2012-05-21 09:53:36 UTC (rev 18420)
@@ -110,6 +110,12 @@
 	$err .= "Error: ".db_error();
 }
 
+/* delete all remnants from ZipArchive ugliness */
+$uglies = glob(forge_get_config('upload_dir') . '/*/*/*.zip.??????');
+if ($uglies) foreach ($uglies as $ugly) {
+	@unlink($ugly);
+}
+
 cron_entry(7,$err);
 
 ?>



More information about the evolvis-commits mailing list