Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit 2158f88

Browse files
René Scharfegitster
authored andcommitted
archive-zip: streaming for stored files
Write a data descriptor containing the CRC of the entry and its sizes after streaming it out. For simplicity, do that only if we're storing files (option -0) for now. t5000 verifies output. t1050 makes sure the command always respects core.bigfilethreshold Signed-off-by: Rene Scharfe <[email protected]> Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ebf5374 commit 2158f88

File tree

3 files changed

+88
-12
lines changed

3 files changed

+88
-12
lines changed

archive-zip.c

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44
#include "cache.h"
55
#include "archive.h"
6+
#include "streaming.h"
67

78
static int zip_date;
89
static int zip_time;
@@ -15,6 +16,7 @@ static unsigned int zip_dir_offset;
1516
static unsigned int zip_dir_entries;
1617

1718
#define ZIP_DIRECTORY_MIN_SIZE (1024 * 1024)
19+
#define ZIP_STREAM (8)
1820

1921
struct zip_local_header {
2022
unsigned char magic[4];
@@ -31,6 +33,14 @@ struct zip_local_header {
3133
unsigned char _end[1];
3234
};
3335

36+
struct zip_data_desc {
37+
unsigned char magic[4];
38+
unsigned char crc32[4];
39+
unsigned char compressed_size[4];
40+
unsigned char size[4];
41+
unsigned char _end[1];
42+
};
43+
3444
struct zip_dir_header {
3545
unsigned char magic[4];
3646
unsigned char creator_version[2];
@@ -70,6 +80,7 @@ struct zip_dir_trailer {
7080
* we're interested in.
7181
*/
7282
#define ZIP_LOCAL_HEADER_SIZE offsetof(struct zip_local_header, _end)
83+
#define ZIP_DATA_DESC_SIZE offsetof(struct zip_data_desc, _end)
7384
#define ZIP_DIR_HEADER_SIZE offsetof(struct zip_dir_header, _end)
7485
#define ZIP_DIR_TRAILER_SIZE offsetof(struct zip_dir_trailer, _end)
7586

@@ -120,6 +131,19 @@ static void *zlib_deflate(void *data, unsigned long size,
120131
return buffer;
121132
}
122133

134+
static void write_zip_data_desc(unsigned long size,
135+
unsigned long compressed_size,
136+
unsigned long crc)
137+
{
138+
struct zip_data_desc trailer;
139+
140+
copy_le32(trailer.magic, 0x08074b50);
141+
copy_le32(trailer.crc32, crc);
142+
copy_le32(trailer.compressed_size, compressed_size);
143+
copy_le32(trailer.size, size);
144+
write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE);
145+
}
146+
123147
static void set_zip_dir_data_desc(struct zip_dir_header *header,
124148
unsigned long size,
125149
unsigned long compressed_size,
@@ -140,6 +164,8 @@ static void set_zip_header_data_desc(struct zip_local_header *header,
140164
copy_le32(header->size, size);
141165
}
142166

167+
#define STREAM_BUFFER_SIZE (1024 * 16)
168+
143169
static int write_zip_entry(struct archiver_args *args,
144170
const unsigned char *sha1,
145171
const char *path, size_t pathlen,
@@ -155,6 +181,8 @@ static int write_zip_entry(struct archiver_args *args,
155181
unsigned char *out;
156182
void *deflated = NULL;
157183
void *buffer;
184+
struct git_istream *stream = NULL;
185+
unsigned long flags = 0;
158186
unsigned long size;
159187

160188
crc = crc32(0, NULL, 0);
@@ -173,25 +201,38 @@ static int write_zip_entry(struct archiver_args *args,
173201
buffer = NULL;
174202
size = 0;
175203
} else if (S_ISREG(mode) || S_ISLNK(mode)) {
176-
enum object_type type;
177-
buffer = sha1_file_to_archive(args, path, sha1, mode, &type, &size);
178-
if (!buffer)
179-
return error("cannot read %s", sha1_to_hex(sha1));
204+
enum object_type type = sha1_object_info(sha1, &size);
180205

181206
method = 0;
182207
attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) :
183208
(mode & 0111) ? ((mode) << 16) : 0;
184-
if (S_ISREG(mode) && args->compression_level != 0)
209+
if (S_ISREG(mode) && args->compression_level != 0 && size > 0)
185210
method = 8;
186-
crc = crc32(crc, buffer, size);
187-
out = buffer;
188211
compressed_size = size;
212+
213+
if (S_ISREG(mode) && type == OBJ_BLOB && !args->convert &&
214+
size > big_file_threshold && method == 0) {
215+
stream = open_istream(sha1, &type, &size, NULL);
216+
if (!stream)
217+
return error("cannot stream blob %s",
218+
sha1_to_hex(sha1));
219+
flags |= ZIP_STREAM;
220+
out = buffer = NULL;
221+
} else {
222+
buffer = sha1_file_to_archive(args, path, sha1, mode,
223+
&type, &size);
224+
if (!buffer)
225+
return error("cannot read %s",
226+
sha1_to_hex(sha1));
227+
crc = crc32(crc, buffer, size);
228+
out = buffer;
229+
}
189230
} else {
190231
return error("unsupported file mode: 0%o (SHA1: %s)", mode,
191232
sha1_to_hex(sha1));
192233
}
193234

194-
if (method == 8) {
235+
if (buffer && method == 8) {
195236
deflated = zlib_deflate(buffer, size, args->compression_level,
196237
&compressed_size);
197238
if (deflated && compressed_size - 6 < size) {
@@ -216,7 +257,7 @@ static int write_zip_entry(struct archiver_args *args,
216257
copy_le16(dirent.creator_version,
217258
S_ISLNK(mode) || (S_ISREG(mode) && (mode & 0111)) ? 0x0317 : 0);
218259
copy_le16(dirent.version, 10);
219-
copy_le16(dirent.flags, 0);
260+
copy_le16(dirent.flags, flags);
220261
copy_le16(dirent.compression_method, method);
221262
copy_le16(dirent.mtime, zip_time);
222263
copy_le16(dirent.mdate, zip_date);
@@ -231,18 +272,43 @@ static int write_zip_entry(struct archiver_args *args,
231272

232273
copy_le32(header.magic, 0x04034b50);
233274
copy_le16(header.version, 10);
234-
copy_le16(header.flags, 0);
275+
copy_le16(header.flags, flags);
235276
copy_le16(header.compression_method, method);
236277
copy_le16(header.mtime, zip_time);
237278
copy_le16(header.mdate, zip_date);
238-
set_zip_header_data_desc(&header, size, compressed_size, crc);
279+
if (flags & ZIP_STREAM)
280+
set_zip_header_data_desc(&header, 0, 0, 0);
281+
else
282+
set_zip_header_data_desc(&header, size, compressed_size, crc);
239283
copy_le16(header.filename_length, pathlen);
240284
copy_le16(header.extra_length, 0);
241285
write_or_die(1, &header, ZIP_LOCAL_HEADER_SIZE);
242286
zip_offset += ZIP_LOCAL_HEADER_SIZE;
243287
write_or_die(1, path, pathlen);
244288
zip_offset += pathlen;
245-
if (compressed_size > 0) {
289+
if (stream && method == 0) {
290+
unsigned char buf[STREAM_BUFFER_SIZE];
291+
ssize_t readlen;
292+
293+
for (;;) {
294+
readlen = read_istream(stream, buf, sizeof(buf));
295+
if (readlen <= 0)
296+
break;
297+
crc = crc32(crc, buf, readlen);
298+
write_or_die(1, buf, readlen);
299+
}
300+
close_istream(stream);
301+
if (readlen)
302+
return readlen;
303+
304+
compressed_size = size;
305+
zip_offset += compressed_size;
306+
307+
write_zip_data_desc(size, compressed_size, crc);
308+
zip_offset += ZIP_DATA_DESC_SIZE;
309+
310+
set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
311+
} else if (compressed_size > 0) {
246312
write_or_die(1, out, compressed_size);
247313
zip_offset += compressed_size;
248314
}

t/t1050-large.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,8 @@ test_expect_success 'tar achiving' '
138138
git archive --format=tar HEAD >/dev/null
139139
'
140140

141+
test_expect_success 'zip achiving, store only' '
142+
git archive --format=zip -0 HEAD >/dev/null
143+
'
144+
141145
test_done

t/t5000-tar-tree.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,12 @@ test_expect_success UNZIP \
244244
'validate file contents with prefix' \
245245
'diff -r a e/prefix/a'
246246

247+
test_expect_success UNZIP 'git archive -0 --format=zip on large files' '
248+
test_config core.bigfilethreshold 1 &&
249+
git archive -0 --format=zip HEAD >large.zip &&
250+
(mkdir large && cd large && $UNZIP ../large.zip)
251+
'
252+
247253
test_expect_success \
248254
'git archive --list outside of a git repo' \
249255
'GIT_DIR=some/non-existing/directory git archive --list'

0 commit comments

Comments
 (0)