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

Commit d41355f

Browse files
committed
Merge branch 'nd/stream-to-archive'
Stream large blobs directly out to archive files without slurping everything in memory first. By René Scharfe (6) and Nguyễn Thái Ngọc Duy (4) * nd/stream-to-archive: t5000: rationalize unzip tests archive-zip: streaming for deflated files archive-zip: streaming for stored files archive-zip: factor out helpers for writing sizes and CRC archive-zip: remove uncompressed_size archive-tar: stream large blobs to tar file archive: delegate blob reading to backend archive-tar: unindent write_tar_entry by one level archive-tar: turn write_tar_entry into blob-writing only streaming: void pointer instead of char pointer
2 parents aa6912b + 2dd4233 commit d41355f

File tree

8 files changed

+404
-135
lines changed

8 files changed

+404
-135
lines changed

archive-tar.c

Lines changed: 145 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "cache.h"
55
#include "tar.h"
66
#include "archive.h"
7+
#include "streaming.h"
78
#include "run-command.h"
89

910
#define RECORDSIZE (512)
@@ -30,10 +31,9 @@ static void write_if_needed(void)
3031
* queues up writes, so that all our write(2) calls write exactly one
3132
* full block; pads writes to RECORDSIZE
3233
*/
33-
static void write_blocked(const void *data, unsigned long size)
34+
static void do_write_blocked(const void *data, unsigned long size)
3435
{
3536
const char *buf = data;
36-
unsigned long tail;
3737

3838
if (offset) {
3939
unsigned long chunk = BLOCKSIZE - offset;
@@ -54,6 +54,11 @@ static void write_blocked(const void *data, unsigned long size)
5454
memcpy(block + offset, buf, size);
5555
offset += size;
5656
}
57+
}
58+
59+
static void finish_record(void)
60+
{
61+
unsigned long tail;
5762
tail = offset % RECORDSIZE;
5863
if (tail) {
5964
memset(block + offset, 0, RECORDSIZE - tail);
@@ -62,6 +67,12 @@ static void write_blocked(const void *data, unsigned long size)
6267
write_if_needed();
6368
}
6469

70+
static void write_blocked(const void *data, unsigned long size)
71+
{
72+
do_write_blocked(data, size);
73+
finish_record();
74+
}
75+
6576
/*
6677
* The end of tar archives is marked by 2*512 nul bytes and after that
6778
* follows the rest of the block (if any).
@@ -77,6 +88,33 @@ static void write_trailer(void)
7788
}
7889
}
7990

91+
/*
92+
* queues up writes, so that all our write(2) calls write exactly one
93+
* full block; pads writes to RECORDSIZE
94+
*/
95+
static int stream_blocked(const unsigned char *sha1)
96+
{
97+
struct git_istream *st;
98+
enum object_type type;
99+
unsigned long sz;
100+
char buf[BLOCKSIZE];
101+
ssize_t readlen;
102+
103+
st = open_istream(sha1, &type, &sz, NULL);
104+
if (!st)
105+
return error("cannot stream blob %s", sha1_to_hex(sha1));
106+
for (;;) {
107+
readlen = read_istream(st, buf, sizeof(buf));
108+
if (readlen <= 0)
109+
break;
110+
do_write_blocked(buf, readlen);
111+
}
112+
close_istream(st);
113+
if (!readlen)
114+
finish_record();
115+
return readlen;
116+
}
117+
80118
/*
81119
* pax extended header records have the format "%u %s=%s\n". %u contains
82120
* the size of the whole string (including the %u), the first %s is the
@@ -123,56 +161,101 @@ static size_t get_path_prefix(const char *path, size_t pathlen, size_t maxlen)
123161
return i;
124162
}
125163

164+
static void prepare_header(struct archiver_args *args,
165+
struct ustar_header *header,
166+
unsigned int mode, unsigned long size)
167+
{
168+
sprintf(header->mode, "%07o", mode & 07777);
169+
sprintf(header->size, "%011lo", S_ISREG(mode) ? size : 0);
170+
sprintf(header->mtime, "%011lo", (unsigned long) args->time);
171+
172+
sprintf(header->uid, "%07o", 0);
173+
sprintf(header->gid, "%07o", 0);
174+
strlcpy(header->uname, "root", sizeof(header->uname));
175+
strlcpy(header->gname, "root", sizeof(header->gname));
176+
sprintf(header->devmajor, "%07o", 0);
177+
sprintf(header->devminor, "%07o", 0);
178+
179+
memcpy(header->magic, "ustar", 6);
180+
memcpy(header->version, "00", 2);
181+
182+
sprintf(header->chksum, "%07o", ustar_header_chksum(header));
183+
}
184+
185+
static int write_extended_header(struct archiver_args *args,
186+
const unsigned char *sha1,
187+
const void *buffer, unsigned long size)
188+
{
189+
struct ustar_header header;
190+
unsigned int mode;
191+
memset(&header, 0, sizeof(header));
192+
*header.typeflag = TYPEFLAG_EXT_HEADER;
193+
mode = 0100666;
194+
sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
195+
prepare_header(args, &header, mode, size);
196+
write_blocked(&header, sizeof(header));
197+
write_blocked(buffer, size);
198+
return 0;
199+
}
200+
126201
static int write_tar_entry(struct archiver_args *args,
127-
const unsigned char *sha1, const char *path, size_t pathlen,
128-
unsigned int mode, void *buffer, unsigned long size)
202+
const unsigned char *sha1,
203+
const char *path, size_t pathlen,
204+
unsigned int mode)
129205
{
130206
struct ustar_header header;
131207
struct strbuf ext_header = STRBUF_INIT;
208+
unsigned int old_mode = mode;
209+
unsigned long size;
210+
void *buffer;
132211
int err = 0;
133212

134213
memset(&header, 0, sizeof(header));
135214

136-
if (!sha1) {
137-
*header.typeflag = TYPEFLAG_GLOBAL_HEADER;
138-
mode = 0100666;
139-
strcpy(header.name, "pax_global_header");
140-
} else if (!path) {
141-
*header.typeflag = TYPEFLAG_EXT_HEADER;
142-
mode = 0100666;
143-
sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
215+
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
216+
*header.typeflag = TYPEFLAG_DIR;
217+
mode = (mode | 0777) & ~tar_umask;
218+
} else if (S_ISLNK(mode)) {
219+
*header.typeflag = TYPEFLAG_LNK;
220+
mode |= 0777;
221+
} else if (S_ISREG(mode)) {
222+
*header.typeflag = TYPEFLAG_REG;
223+
mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
144224
} else {
145-
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
146-
*header.typeflag = TYPEFLAG_DIR;
147-
mode = (mode | 0777) & ~tar_umask;
148-
} else if (S_ISLNK(mode)) {
149-
*header.typeflag = TYPEFLAG_LNK;
150-
mode |= 0777;
151-
} else if (S_ISREG(mode)) {
152-
*header.typeflag = TYPEFLAG_REG;
153-
mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
225+
return error("unsupported file mode: 0%o (SHA1: %s)",
226+
mode, sha1_to_hex(sha1));
227+
}
228+
if (pathlen > sizeof(header.name)) {
229+
size_t plen = get_path_prefix(path, pathlen,
230+
sizeof(header.prefix));
231+
size_t rest = pathlen - plen - 1;
232+
if (plen > 0 && rest <= sizeof(header.name)) {
233+
memcpy(header.prefix, path, plen);
234+
memcpy(header.name, path + plen + 1, rest);
154235
} else {
155-
return error("unsupported file mode: 0%o (SHA1: %s)",
156-
mode, sha1_to_hex(sha1));
236+
sprintf(header.name, "%s.data",
237+
sha1_to_hex(sha1));
238+
strbuf_append_ext_header(&ext_header, "path",
239+
path, pathlen);
157240
}
158-
if (pathlen > sizeof(header.name)) {
159-
size_t plen = get_path_prefix(path, pathlen,
160-
sizeof(header.prefix));
161-
size_t rest = pathlen - plen - 1;
162-
if (plen > 0 && rest <= sizeof(header.name)) {
163-
memcpy(header.prefix, path, plen);
164-
memcpy(header.name, path + plen + 1, rest);
165-
} else {
166-
sprintf(header.name, "%s.data",
167-
sha1_to_hex(sha1));
168-
strbuf_append_ext_header(&ext_header, "path",
169-
path, pathlen);
170-
}
171-
} else
172-
memcpy(header.name, path, pathlen);
241+
} else
242+
memcpy(header.name, path, pathlen);
243+
244+
if (S_ISREG(mode) && !args->convert &&
245+
sha1_object_info(sha1, &size) == OBJ_BLOB &&
246+
size > big_file_threshold)
247+
buffer = NULL;
248+
else if (S_ISLNK(mode) || S_ISREG(mode)) {
249+
enum object_type type;
250+
buffer = sha1_file_to_archive(args, path, sha1, old_mode, &type, &size);
251+
if (!buffer)
252+
return error("cannot read %s", sha1_to_hex(sha1));
253+
} else {
254+
buffer = NULL;
255+
size = 0;
173256
}
174257

175-
if (S_ISLNK(mode) && buffer) {
258+
if (S_ISLNK(mode)) {
176259
if (size > sizeof(header.linkname)) {
177260
sprintf(header.linkname, "see %s.paxheader",
178261
sha1_to_hex(sha1));
@@ -182,44 +265,44 @@ static int write_tar_entry(struct archiver_args *args,
182265
memcpy(header.linkname, buffer, size);
183266
}
184267

185-
sprintf(header.mode, "%07o", mode & 07777);
186-
sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
187-
sprintf(header.mtime, "%011lo", (unsigned long) args->time);
188-
189-
sprintf(header.uid, "%07o", 0);
190-
sprintf(header.gid, "%07o", 0);
191-
strlcpy(header.uname, "root", sizeof(header.uname));
192-
strlcpy(header.gname, "root", sizeof(header.gname));
193-
sprintf(header.devmajor, "%07o", 0);
194-
sprintf(header.devminor, "%07o", 0);
195-
196-
memcpy(header.magic, "ustar", 6);
197-
memcpy(header.version, "00", 2);
198-
199-
sprintf(header.chksum, "%07o", ustar_header_chksum(&header));
268+
prepare_header(args, &header, mode, size);
200269

201270
if (ext_header.len > 0) {
202-
err = write_tar_entry(args, sha1, NULL, 0, 0, ext_header.buf,
203-
ext_header.len);
204-
if (err)
271+
err = write_extended_header(args, sha1, ext_header.buf,
272+
ext_header.len);
273+
if (err) {
274+
free(buffer);
205275
return err;
276+
}
206277
}
207278
strbuf_release(&ext_header);
208279
write_blocked(&header, sizeof(header));
209-
if (S_ISREG(mode) && buffer && size > 0)
210-
write_blocked(buffer, size);
280+
if (S_ISREG(mode) && size > 0) {
281+
if (buffer)
282+
write_blocked(buffer, size);
283+
else
284+
err = stream_blocked(sha1);
285+
}
286+
free(buffer);
211287
return err;
212288
}
213289

214290
static int write_global_extended_header(struct archiver_args *args)
215291
{
216292
const unsigned char *sha1 = args->commit_sha1;
217293
struct strbuf ext_header = STRBUF_INIT;
218-
int err;
294+
struct ustar_header header;
295+
unsigned int mode;
296+
int err = 0;
219297

220298
strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
221-
err = write_tar_entry(args, NULL, NULL, 0, 0, ext_header.buf,
222-
ext_header.len);
299+
memset(&header, 0, sizeof(header));
300+
*header.typeflag = TYPEFLAG_GLOBAL_HEADER;
301+
mode = 0100666;
302+
strcpy(header.name, "pax_global_header");
303+
prepare_header(args, &header, mode, ext_header.len);
304+
write_blocked(&header, sizeof(header));
305+
write_blocked(ext_header.buf, ext_header.len);
223306
strbuf_release(&ext_header);
224307
return err;
225308
}

0 commit comments

Comments
 (0)