aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorMorris Jobke <hey@morrisjobke.de>2018-04-09 23:11:30 +0200
committerGitHub <noreply@github.com>2018-04-09 23:11:30 +0200
commit0fa796fb8512490bf1bf126c9ebfa53300a945c7 (patch)
treedbefde2469dab184760f3a99526643a580e44c08 /lib
parent0327ef1044360361245c508587cba7505621360b (diff)
parent50ee978aa84197685107062f48074823ef328355 (diff)
downloadnextcloud-server-0fa796fb8512490bf1bf126c9ebfa53300a945c7.tar.gz
nextcloud-server-0fa796fb8512490bf1bf126c9ebfa53300a945c7.zip
Merge pull request #7972 from nextcloud/fix_7782
Use zip32 if possible
Diffstat (limited to 'lib')
-rw-r--r--lib/private/Streamer.php43
-rw-r--r--lib/private/legacy/files.php46
2 files changed, 80 insertions, 9 deletions
diff --git a/lib/private/Streamer.php b/lib/private/Streamer.php
index 7b178fda652..51c2c923c23 100644
--- a/lib/private/Streamer.php
+++ b/lib/private/Streamer.php
@@ -24,6 +24,7 @@
namespace OC;
+use OCP\IRequest;
use ownCloud\TarStreamer\TarStreamer;
use ZipStreamer\ZipStreamer;
@@ -33,12 +34,42 @@ class Streamer {
// streamer instance
private $streamerInstance;
-
- public function __construct(){
- /** @var \OCP\IRequest */
- $request = \OC::$server->getRequest();
-
- if ($request->isUserAgent($this->preferTarFor)) {
+
+ /**
+ * Streamer constructor.
+ *
+ * @param IRequest $request
+ * @param int $size The size of the files in bytes
+ * @param int $numberOfFiles The number of files (and directories) that will
+ * be included in the streamed file
+ */
+ public function __construct(IRequest $request, int $size, int $numberOfFiles){
+
+ /**
+ * zip32 constraints for a basic (without compression, volumes nor
+ * encryption) zip file according to the Zip specification:
+ * - No file size is larger than 4 bytes (file size < 4294967296); see
+ * 4.4.9 uncompressed size
+ * - The size of all files plus their local headers is not larger than
+ * 4 bytes; see 4.4.16 relative offset of local header and 4.4.24
+ * offset of start of central directory with respect to the starting
+ * disk number
+ * - The total number of entries (files and directories) in the zip file
+ * is not larger than 2 bytes (number of entries < 65536); see 4.4.22
+ * total number of entries in the central dir
+ * - The size of the central directory is not larger than 4 bytes; see
+ * 4.4.23 size of the central directory
+ *
+ * Due to all that, zip32 is used if the size is below 4GB and there are
+ * less than 65536 files; the margin between 4*1000^3 and 4*1024^3
+ * should give enough room for the extra zip metadata. Technically, it
+ * would still be possible to create an invalid zip32 file (for example,
+ * a zip file from files smaller than 4GB with a central directory
+ * larger than 4GiB), but it should not happen in the real world.
+ */
+ if ($size < 4 * 1000 * 1000 * 1000 && $numberOfFiles < 65536) {
+ $this->streamerInstance = new ZipStreamer(['zip64' => false]);
+ } else if ($request->isUserAgent($this->preferTarFor)) {
$this->streamerInstance = new TarStreamer();
} else {
$this->streamerInstance = new ZipStreamer(['zip64' => PHP_INT_SIZE !== 4]);
diff --git a/lib/private/legacy/files.php b/lib/private/legacy/files.php
index def9f82fab9..9281c1f7da4 100644
--- a/lib/private/legacy/files.php
+++ b/lib/private/legacy/files.php
@@ -144,17 +144,34 @@ class OC_Files {
}
}
- $streamer = new Streamer();
- OC_Util::obEnd();
-
self::lockFiles($view, $dir, $files);
+ /* Calculate filesize and number of files */
+ if ($getType === self::ZIP_FILES) {
+ $fileInfos = array();
+ $fileSize = 0;
+ foreach ($files as $file) {
+ $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $file);
+ $fileSize += $fileInfo->getSize();
+ $fileInfos[] = $fileInfo;
+ }
+ $numberOfFiles = self::getNumberOfFiles($fileInfos);
+ } elseif ($getType === self::ZIP_DIR) {
+ $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $files);
+ $fileSize = $fileInfo->getSize();
+ $numberOfFiles = self::getNumberOfFiles(array($fileInfo));
+ }
+
+ $streamer = new Streamer(\OC::$server->getRequest(), $fileSize, $numberOfFiles);
+ OC_Util::obEnd();
+
$streamer->sendHeaders($name);
$executionTime = (int)OC::$server->getIniWrapper()->getNumeric('max_execution_time');
if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
@set_time_limit(0);
}
ignore_user_abort(true);
+
if ($getType === self::ZIP_FILES) {
foreach ($files as $file) {
$file = $dir . '/' . $file;
@@ -314,6 +331,29 @@ class OC_Files {
}
/**
+ * Returns the total (recursive) number of files and folders in the given
+ * FileInfos.
+ *
+ * @param \OCP\Files\FileInfo[] $fileInfos the FileInfos to count
+ * @return int the total number of files and folders
+ */
+ private static function getNumberOfFiles($fileInfos) {
+ $numberOfFiles = 0;
+
+ $view = new View();
+
+ while ($fileInfo = array_pop($fileInfos)) {
+ $numberOfFiles++;
+
+ if ($fileInfo->getType() === \OCP\Files\FileInfo::TYPE_FOLDER) {
+ $fileInfos = array_merge($fileInfos, $view->getDirectoryContent($fileInfo->getPath()));
+ }
+ }
+
+ return $numberOfFiles;
+ }
+
+ /**
* @param View $view
* @param string $dir
* @param string[]|string $files