I'm trying to integrate an on-demand backup system for a relatively small web-application which allows the user to download a zip file of all the files on the server and a database dump. The zip file is streamed to the user, instead of packaged as a whole in a temporary directory on the server.
I'm using a third-party PHP class, called ZipStream, to accomplish this. The class is hosted here and the specific class file can be viewed here. I've chosen this version (there is another, older one) as it has support for including directories. Also, the setup should work on servers without access to shell commands via shell_exec()
or passthru()
.
My simple test setup is working and I can extract the resulting streamed zip using the unzip
command from the console or using The Unarchiver.app
. OS X's Archive Utility (Mac OS X 10.6 and very likely older and later versions) is causing problems however as it fails to extract the archive with the following error:
Unable to unarchive "test.zip" into "test". (Error 1 - Operation not permitted.)
The actual error logged is:
ditto: Couldn't read pkzip signature.
I've found some possible explanations regarding this problem, here and here on StackOverflow and I would like to patch the ZipStream class to adhere to the Zip specification so that Archive Utility will expand the archive as expected.
The "Version needed to extract" (stored in the ATTR_VERSION_TO_EXTRACT
constant) in the ZipStream class is: x0Ax00
, but I'm not sure if that notation differs from the standard 0x000A
mentioned in the aforementioned question.
The problem seems related to the directories added to the zip file. A zip file only containing a text file doesn't cause issues using Archive Utility.
As this moves alongside the fringes of the realm of PHP I'm not sure how to edit the script with the correct hex strings or how to appropriate the Zend solution to this specific script.
I've uploaded a generated zip here: http://dl.dropbox.com/u/6887873/test.zip
The code to generate is a trimmed down version of the example code:
$extime = ini_get('max_execution_time');
ini_set('max_execution_time', 600);
include_once("ZipStream.php");
$fileTime = date("D, d M Y H:i:s T");
$chapter1 = "Chapter 1\n"
. "Lorem ipsum\n"
. "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec magna lorem, mattis sit amet porta vitae, consectetur ut eros. Nullam id mattis lacus. In eget neque magna, congue imperdiet nulla. Aenean erat lacus, imperdiet a adipiscing non, dignissim eget felis. Nulla facilisi. Vivamus sit amet lorem eget mauris dictum pharetra. In mauris nulla, placerat a accumsan ac, mollis sit amet ligula. Donec eget facilisis dui. Cras elit quam, imperdiet at malesuada vitae, luctus id orci. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Pellentesque eu libero in leo ultrices tristique. Etiam quis ornare massa. Donec in velit leo. Sed eu ante tortor.\n";
$zip = new ZipStream("test.zip");
$zip->addFile("Hello World!\r\n", "Hello.txt");
$zip->addDirectory("files/");
$zip->addDirectoryContent("../images", "files/testing/");
$zip->finalize();
EDIT:
I think I've narrowed the problem down a bit more. It doesn't seem the hex strings actually make a difference. An generated zip containing only a text file is expanded without errors using Archive Utility, so the corruption seems to occur when adding directories, specifically when calling addDirectoryContent()
, which in turn calls addLargeFile()
of the ZipStream class. Directories are added correctly and don't result in an invalid file, as soon as addDirectoryContent()
is added to the mix Archive Utility fails to extract the files, outputting the errors above.