git
/
archive-tar.c
550 строк · 14.0 Кб
1/*
2* Copyright (c) 2005, 2006 Rene Scharfe
3*/
4
5#define USE_THE_REPOSITORY_VARIABLE6
7#include "git-compat-util.h"8#include "config.h"9#include "gettext.h"10#include "git-zlib.h"11#include "hex.h"12#include "tar.h"13#include "archive.h"14#include "object-store-ll.h"15#include "strbuf.h"16#include "streaming.h"17#include "run-command.h"18#include "write-or-die.h"19
20#define RECORDSIZE (512)21#define BLOCKSIZE (RECORDSIZE * 20)22
23static char block[BLOCKSIZE];24static unsigned long offset;25
26static int tar_umask = 002;27
28static int write_tar_filter_archive(const struct archiver *ar,29struct archiver_args *args);30
31/*
32* This is the max value that a ustar size header can specify, as it is fixed
33* at 11 octal digits. POSIX specifies that we switch to extended headers at
34* this size.
35*
36* Likewise for the mtime (which happens to use a buffer of the same size).
37*/
38#if ULONG_MAX == 0xFFFFFFFF39#define USTAR_MAX_SIZE ULONG_MAX40#else41#define USTAR_MAX_SIZE 077777777777UL42#endif43#if TIME_MAX == 0xFFFFFFFF44#define USTAR_MAX_MTIME TIME_MAX45#else46#define USTAR_MAX_MTIME 077777777777ULL47#endif48
49static void tar_write_block(const void *buf)50{
51write_or_die(1, buf, BLOCKSIZE);52}
53
54static void (*write_block)(const void *) = tar_write_block;55
56/* writes out the whole block, but only if it is full */
57static void write_if_needed(void)58{
59if (offset == BLOCKSIZE) {60write_block(block);61offset = 0;62}63}
64
65/*
66* queues up writes, so that all our write(2) calls write exactly one
67* full block; pads writes to RECORDSIZE
68*/
69static void do_write_blocked(const void *data, unsigned long size)70{
71const char *buf = data;72
73if (offset) {74unsigned long chunk = BLOCKSIZE - offset;75if (size < chunk)76chunk = size;77memcpy(block + offset, buf, chunk);78size -= chunk;79offset += chunk;80buf += chunk;81write_if_needed();82}83while (size >= BLOCKSIZE) {84write_block(buf);85size -= BLOCKSIZE;86buf += BLOCKSIZE;87}88if (size) {89memcpy(block + offset, buf, size);90offset += size;91}92}
93
94static void finish_record(void)95{
96unsigned long tail;97tail = offset % RECORDSIZE;98if (tail) {99memset(block + offset, 0, RECORDSIZE - tail);100offset += RECORDSIZE - tail;101}102write_if_needed();103}
104
105static void write_blocked(const void *data, unsigned long size)106{
107do_write_blocked(data, size);108finish_record();109}
110
111/*
112* The end of tar archives is marked by 2*512 nul bytes and after that
113* follows the rest of the block (if any).
114*/
115static void write_trailer(void)116{
117int tail = BLOCKSIZE - offset;118memset(block + offset, 0, tail);119write_block(block);120if (tail < 2 * RECORDSIZE) {121memset(block, 0, offset);122write_block(block);123}124}
125
126/*
127* queues up writes, so that all our write(2) calls write exactly one
128* full block; pads writes to RECORDSIZE
129*/
130static int stream_blocked(struct repository *r, const struct object_id *oid)131{
132struct git_istream *st;133enum object_type type;134unsigned long sz;135char buf[BLOCKSIZE];136ssize_t readlen;137
138st = open_istream(r, oid, &type, &sz, NULL);139if (!st)140return error(_("cannot stream blob %s"), oid_to_hex(oid));141for (;;) {142readlen = read_istream(st, buf, sizeof(buf));143if (readlen <= 0)144break;145do_write_blocked(buf, readlen);146}147close_istream(st);148if (!readlen)149finish_record();150return readlen;151}
152
153/*
154* pax extended header records have the format "%u %s=%s\n". %u contains
155* the size of the whole string (including the %u), the first %s is the
156* keyword, the second one is the value. This function constructs such a
157* string and appends it to a struct strbuf.
158*/
159static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,160const char *value, size_t valuelen)161{
162size_t orig_len = sb->len;163size_t len, tmp;164
165/* "%u %s=%s\n" */166len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;167for (tmp = 1; len / 10 >= tmp; tmp *= 10)168len++;169
170strbuf_grow(sb, len);171strbuf_addf(sb, "%"PRIuMAX" %s=", (uintmax_t)len, keyword);172strbuf_add(sb, value, valuelen);173strbuf_addch(sb, '\n');174
175if (len != sb->len - orig_len)176BUG("pax extended header length miscalculated as %"PRIuMAX177", should be %"PRIuMAX,178(uintmax_t)len, (uintmax_t)(sb->len - orig_len));179}
180
181/*
182* Like strbuf_append_ext_header, but for numeric values.
183*/
184static void strbuf_append_ext_header_uint(struct strbuf *sb,185const char *keyword,186uintmax_t value)187{
188char buf[40]; /* big enough for 2^128 in decimal, plus NUL */189int len;190
191len = xsnprintf(buf, sizeof(buf), "%"PRIuMAX, value);192strbuf_append_ext_header(sb, keyword, buf, len);193}
194
195static unsigned int ustar_header_chksum(const struct ustar_header *header)196{
197const unsigned char *p = (const unsigned char *)header;198unsigned int chksum = 0;199while (p < (const unsigned char *)header->chksum)200chksum += *p++;201chksum += sizeof(header->chksum) * ' ';202p += sizeof(header->chksum);203while (p < (const unsigned char *)header + sizeof(struct ustar_header))204chksum += *p++;205return chksum;206}
207
208static size_t get_path_prefix(const char *path, size_t pathlen, size_t maxlen)209{
210size_t i = pathlen;211if (i > 1 && path[i - 1] == '/')212i--;213if (i > maxlen)214i = maxlen;215do {216i--;217} while (i > 0 && path[i] != '/');218return i;219}
220
221static void prepare_header(struct archiver_args *args,222struct ustar_header *header,223unsigned int mode, unsigned long size)224{
225xsnprintf(header->mode, sizeof(header->mode), "%07o", mode & 07777);226xsnprintf(header->size, sizeof(header->size), "%011"PRIoMAX , S_ISREG(mode) ? (uintmax_t)size : (uintmax_t)0);227xsnprintf(header->mtime, sizeof(header->mtime), "%011lo", (unsigned long) args->time);228
229xsnprintf(header->uid, sizeof(header->uid), "%07o", 0);230xsnprintf(header->gid, sizeof(header->gid), "%07o", 0);231strlcpy(header->uname, "root", sizeof(header->uname));232strlcpy(header->gname, "root", sizeof(header->gname));233xsnprintf(header->devmajor, sizeof(header->devmajor), "%07o", 0);234xsnprintf(header->devminor, sizeof(header->devminor), "%07o", 0);235
236memcpy(header->magic, "ustar", 6);237memcpy(header->version, "00", 2);238
239xsnprintf(header->chksum, sizeof(header->chksum), "%07o", ustar_header_chksum(header));240}
241
242static void write_extended_header(struct archiver_args *args,243const struct object_id *oid,244const void *buffer, unsigned long size)245{
246struct ustar_header header;247unsigned int mode;248memset(&header, 0, sizeof(header));249*header.typeflag = TYPEFLAG_EXT_HEADER;250mode = 0100666;251xsnprintf(header.name, sizeof(header.name), "%s.paxheader", oid_to_hex(oid));252prepare_header(args, &header, mode, size);253write_blocked(&header, sizeof(header));254write_blocked(buffer, size);255}
256
257static int write_tar_entry(struct archiver_args *args,258const struct object_id *oid,259const char *path, size_t pathlen,260unsigned int mode,261void *buffer, unsigned long size)262{
263struct ustar_header header;264struct strbuf ext_header = STRBUF_INIT;265unsigned long size_in_header;266int err = 0;267
268memset(&header, 0, sizeof(header));269
270if (S_ISDIR(mode) || S_ISGITLINK(mode)) {271*header.typeflag = TYPEFLAG_DIR;272mode = (mode | 0777) & ~tar_umask;273} else if (S_ISLNK(mode)) {274*header.typeflag = TYPEFLAG_LNK;275mode |= 0777;276} else if (S_ISREG(mode)) {277*header.typeflag = TYPEFLAG_REG;278mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;279} else {280return error(_("unsupported file mode: 0%o (SHA1: %s)"),281mode, oid_to_hex(oid));282}283if (pathlen > sizeof(header.name)) {284size_t plen = get_path_prefix(path, pathlen,285sizeof(header.prefix));286size_t rest = pathlen - plen - 1;287if (plen > 0 && rest <= sizeof(header.name)) {288memcpy(header.prefix, path, plen);289memcpy(header.name, path + plen + 1, rest);290} else {291xsnprintf(header.name, sizeof(header.name), "%s.data",292oid_to_hex(oid));293strbuf_append_ext_header(&ext_header, "path",294path, pathlen);295}296} else297memcpy(header.name, path, pathlen);298
299if (S_ISLNK(mode)) {300if (size > sizeof(header.linkname)) {301xsnprintf(header.linkname, sizeof(header.linkname),302"see %s.paxheader", oid_to_hex(oid));303strbuf_append_ext_header(&ext_header, "linkpath",304buffer, size);305} else306memcpy(header.linkname, buffer, size);307}308
309size_in_header = size;310if (S_ISREG(mode) && size > USTAR_MAX_SIZE) {311size_in_header = 0;312strbuf_append_ext_header_uint(&ext_header, "size", size);313}314
315prepare_header(args, &header, mode, size_in_header);316
317if (ext_header.len > 0) {318write_extended_header(args, oid, ext_header.buf,319ext_header.len);320}321strbuf_release(&ext_header);322write_blocked(&header, sizeof(header));323if (S_ISREG(mode) && size > 0) {324if (buffer)325write_blocked(buffer, size);326else327err = stream_blocked(args->repo, oid);328}329return err;330}
331
332static void write_global_extended_header(struct archiver_args *args)333{
334const struct object_id *oid = args->commit_oid;335struct strbuf ext_header = STRBUF_INIT;336struct ustar_header header;337unsigned int mode;338
339if (oid)340strbuf_append_ext_header(&ext_header, "comment",341oid_to_hex(oid),342the_hash_algo->hexsz);343if (args->time > USTAR_MAX_MTIME) {344strbuf_append_ext_header_uint(&ext_header, "mtime",345args->time);346args->time = USTAR_MAX_MTIME;347}348
349if (!ext_header.len)350return;351
352memset(&header, 0, sizeof(header));353*header.typeflag = TYPEFLAG_GLOBAL_HEADER;354mode = 0100666;355xsnprintf(header.name, sizeof(header.name), "pax_global_header");356prepare_header(args, &header, mode, ext_header.len);357write_blocked(&header, sizeof(header));358write_blocked(ext_header.buf, ext_header.len);359strbuf_release(&ext_header);360}
361
362static struct archiver **tar_filters;363static int nr_tar_filters;364static int alloc_tar_filters;365
366static struct archiver *find_tar_filter(const char *name, size_t len)367{
368int i;369for (i = 0; i < nr_tar_filters; i++) {370struct archiver *ar = tar_filters[i];371if (!xstrncmpz(ar->name, name, len))372return ar;373}374return NULL;375}
376
377static int tar_filter_config(const char *var, const char *value,378void *data UNUSED)379{
380struct archiver *ar;381const char *name;382const char *type;383size_t namelen;384
385if (parse_config_key(var, "tar", &name, &namelen, &type) < 0 || !name)386return 0;387
388ar = find_tar_filter(name, namelen);389if (!ar) {390CALLOC_ARRAY(ar, 1);391ar->name = xmemdupz(name, namelen);392ar->write_archive = write_tar_filter_archive;393ar->flags = ARCHIVER_WANT_COMPRESSION_LEVELS |394ARCHIVER_HIGH_COMPRESSION_LEVELS;395ALLOC_GROW(tar_filters, nr_tar_filters + 1, alloc_tar_filters);396tar_filters[nr_tar_filters++] = ar;397}398
399if (!strcmp(type, "command")) {400if (!value)401return config_error_nonbool(var);402free(ar->filter_command);403ar->filter_command = xstrdup(value);404return 0;405}406if (!strcmp(type, "remote")) {407if (git_config_bool(var, value))408ar->flags |= ARCHIVER_REMOTE;409else410ar->flags &= ~ARCHIVER_REMOTE;411return 0;412}413
414return 0;415}
416
417static int git_tar_config(const char *var, const char *value,418const struct config_context *ctx, void *cb)419{
420if (!strcmp(var, "tar.umask")) {421if (value && !strcmp(value, "user")) {422tar_umask = umask(0);423umask(tar_umask);424} else {425tar_umask = git_config_int(var, value, ctx->kvi);426}427return 0;428}429
430return tar_filter_config(var, value, cb);431}
432
433static int write_tar_archive(const struct archiver *ar UNUSED,434struct archiver_args *args)435{
436int err = 0;437
438write_global_extended_header(args);439err = write_archive_entries(args, write_tar_entry);440if (!err)441write_trailer();442return err;443}
444
445static git_zstream gzstream;446static unsigned char outbuf[16384];447
448static void tgz_deflate(int flush)449{
450while (gzstream.avail_in || flush == Z_FINISH) {451int status = git_deflate(&gzstream, flush);452if (!gzstream.avail_out || status == Z_STREAM_END) {453write_or_die(1, outbuf, gzstream.next_out - outbuf);454gzstream.next_out = outbuf;455gzstream.avail_out = sizeof(outbuf);456if (status == Z_STREAM_END)457break;458}459if (status != Z_OK && status != Z_BUF_ERROR)460die(_("deflate error (%d)"), status);461}462}
463
464static void tgz_write_block(const void *data)465{
466gzstream.next_in = (void *)data;467gzstream.avail_in = BLOCKSIZE;468tgz_deflate(Z_NO_FLUSH);469}
470
471static const char internal_gzip_command[] = "git archive gzip";472
473static int write_tar_filter_archive(const struct archiver *ar,474struct archiver_args *args)475{
476#if ZLIB_VERNUM >= 0x1221477struct gz_header_s gzhead = { .os = 3 }; /* Unix, for reproducibility */478#endif479struct strbuf cmd = STRBUF_INIT;480struct child_process filter = CHILD_PROCESS_INIT;481int r;482
483if (!ar->filter_command)484BUG("tar-filter archiver called with no filter defined");485
486if (!strcmp(ar->filter_command, internal_gzip_command)) {487write_block = tgz_write_block;488git_deflate_init_gzip(&gzstream, args->compression_level);489#if ZLIB_VERNUM >= 0x1221490if (deflateSetHeader(&gzstream.z, &gzhead) != Z_OK)491BUG("deflateSetHeader() called too late");492#endif493gzstream.next_out = outbuf;494gzstream.avail_out = sizeof(outbuf);495
496r = write_tar_archive(ar, args);497
498tgz_deflate(Z_FINISH);499git_deflate_end(&gzstream);500return r;501}502
503strbuf_addstr(&cmd, ar->filter_command);504if (args->compression_level >= 0)505strbuf_addf(&cmd, " -%d", args->compression_level);506
507strvec_push(&filter.args, cmd.buf);508filter.use_shell = 1;509filter.in = -1;510filter.silent_exec_failure = 1;511
512if (start_command(&filter) < 0)513die_errno(_("unable to start '%s' filter"), cmd.buf);514close(1);515if (dup2(filter.in, 1) < 0)516die_errno(_("unable to redirect descriptor"));517close(filter.in);518
519r = write_tar_archive(ar, args);520
521close(1);522if (finish_command(&filter) != 0)523die(_("'%s' filter reported error"), cmd.buf);524
525strbuf_release(&cmd);526return r;527}
528
529static struct archiver tar_archiver = {530.name = "tar",531.write_archive = write_tar_archive,532.flags = ARCHIVER_REMOTE,533};534
535void init_tar_archiver(void)536{
537int i;538register_archiver(&tar_archiver);539
540tar_filter_config("tar.tgz.command", internal_gzip_command, NULL);541tar_filter_config("tar.tgz.remote", "true", NULL);542tar_filter_config("tar.tar.gz.command", internal_gzip_command, NULL);543tar_filter_config("tar.tar.gz.remote", "true", NULL);544git_config(git_tar_config, NULL);545for (i = 0; i < nr_tar_filters; i++) {546/* omit any filters that never had a command configured */547if (tar_filters[i]->filter_command)548register_archiver(tar_filters[i]);549}550}
551