diff options
author | Daniel Calviño Sánchez <danxuliu@gmail.com> | 2018-02-06 14:03:50 +0100 |
---|---|---|
committer | Roeland Jago Douma <roeland@famdouma.nl> | 2018-04-06 15:59:30 +0200 |
commit | 90fdf83ca7648418f899e28490f65de53fcd31d1 (patch) | |
tree | 50d98da38954e800576d75e34da47301df891cec /lib | |
parent | 4a73f645e5a60d8adece72e6c231c2a1be13473d (diff) | |
download | nextcloud-server-90fdf83ca7648418f899e28490f65de53fcd31d1.tar.gz nextcloud-server-90fdf83ca7648418f899e28490f65de53fcd31d1.zip |
Use zip32 only if there are less than 65536 files
A zip32 file can contain, at most, 65535 files (and folders), so take
that constraint into account.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/private/Streamer.php | 28 | ||||
-rw-r--r-- | lib/private/legacy/files.php | 37 |
2 files changed, 57 insertions, 8 deletions
diff --git a/lib/private/Streamer.php b/lib/private/Streamer.php index 3b033e265e7..51c2c923c23 100644 --- a/lib/private/Streamer.php +++ b/lib/private/Streamer.php @@ -40,14 +40,34 @@ class Streamer { * * @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){ + public function __construct(IRequest $request, int $size, int $numberOfFiles){ /** - * If the size if below 4GB always use zip32 - * Use 4*1000*1000*1000 so we have a buffer for all the extra zip data + * 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) { + if ($size < 4 * 1000 * 1000 * 1000 && $numberOfFiles < 65536) { $this->streamerInstance = new ZipStreamer(['zip64' => false]); } else if ($request->isUserAgent($this->preferTarFor)) { $this->streamerInstance = new TarStreamer(); diff --git a/lib/private/legacy/files.php b/lib/private/legacy/files.php index b7c99b7fef8..9281c1f7da4 100644 --- a/lib/private/legacy/files.php +++ b/lib/private/legacy/files.php @@ -146,17 +146,23 @@ class OC_Files { self::lockFiles($view, $dir, $files); - /* Calculate filesize */ + /* Calculate filesize and number of files */ if ($getType === self::ZIP_FILES) { + $fileInfos = array(); $fileSize = 0; foreach ($files as $file) { - $fileSize += \OC\Files\Filesystem::getFileInfo($dir . '/' . $file)->getSize(); + $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $file); + $fileSize += $fileInfo->getSize(); + $fileInfos[] = $fileInfo; } + $numberOfFiles = self::getNumberOfFiles($fileInfos); } elseif ($getType === self::ZIP_DIR) { - $fileSize = \OC\Files\Filesystem::getFileInfo($dir . '/' . $files)->getSize(); + $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $files); + $fileSize = $fileInfo->getSize(); + $numberOfFiles = self::getNumberOfFiles(array($fileInfo)); } - $streamer = new Streamer(\OC::$server->getRequest(), $fileSize); + $streamer = new Streamer(\OC::$server->getRequest(), $fileSize, $numberOfFiles); OC_Util::obEnd(); $streamer->sendHeaders($name); @@ -325,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 |