Skip to content

Commit

Permalink
Fix big file download for 32-bit servers
Browse files Browse the repository at this point in the history
When a folder larger than ~2GB is downloaded, zip32 doesn't cut it.
Since zip64 doesn't work on 32-bit clients, fall back to generating a
tarball.

Fixes nextcloud#12422 and nextcloud#15117
  • Loading branch information
fwsmit committed Jul 11, 2022
1 parent 16b5e6b commit 219b6d1
Showing 1 changed file with 19 additions and 8 deletions.
27 changes: 19 additions & 8 deletions lib/private/Streamer.php
Expand Up @@ -49,11 +49,14 @@ class Streamer {
* Streamer constructor.
*
* @param IRequest $request
* @param int $size The size of the files in bytes
* @param $size The size of the files in bytes. The type hint is left
* out. Size may be a float if the size is more than PHP_INT_MAX
* (notably on 32-bit systems, see #12422). Note that when size
* is converted to a float, it might not be accurate to the byte.
* @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) {
public function __construct(IRequest $request, $size, int $numberOfFiles) {

/**
* zip32 constraints for a basic (without compression, volumes nor
Expand All @@ -72,21 +75,29 @@ public function __construct(IRequest $request, int $size, int $numberOfFiles) {
*
* 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.
* should give enough room for the extra zip metadata and
* tolerance for rounding when size is a float. 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.
*
* We also have to check for a size above 0. As negative sizes could be
* from not fully scanned external storage. And then things fall apart
* if somebody tries to package to much.
*/
if ($size > 0 && $size < 4 * 1000 * 1000 * 1000 && $numberOfFiles < 65536) {
$this->streamerInstance = new ZipStreamer(['zip64' => false]);
} elseif ($request->isUserAgent($this->preferTarFor)) {
} elseif ($request->isUserAgent($this->preferTarFor) || PHP_INT_SIZE < 8) {
/**
* On 32-bit servers generating a 64-bit zip will
* result in broken zip files. Some clients might also
* have bugs in their zip64 decompression. For these
* cases, generate a tar file.
*/
$this->streamerInstance = new TarStreamer();
} else {
$this->streamerInstance = new ZipStreamer(['zip64' => PHP_INT_SIZE !== 4]);
$this->streamerInstance = new ZipStreamer(['zip64' => true]);
}
}

Expand Down

0 comments on commit 219b6d1

Please sign in to comment.