2006-09-24 23:31:10 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2005, 2006 Rene Scharfe
|
|
|
|
*/
|
2023-02-24 08:09:24 +08:00
|
|
|
#include "git-compat-util.h"
|
|
|
|
#include "alloc.h"
|
2017-06-15 02:07:36 +08:00
|
|
|
#include "config.h"
|
2023-03-21 14:25:54 +08:00
|
|
|
#include "gettext.h"
|
2023-02-24 08:09:27 +08:00
|
|
|
#include "hex.h"
|
2006-09-24 23:31:10 +08:00
|
|
|
#include "tar.h"
|
|
|
|
#include "archive.h"
|
2018-05-16 07:42:15 +08:00
|
|
|
#include "object-store.h"
|
2012-05-03 09:51:04 +08:00
|
|
|
#include "streaming.h"
|
2011-06-22 09:26:31 +08:00
|
|
|
#include "run-command.h"
|
2006-09-24 23:31:10 +08:00
|
|
|
|
|
|
|
#define RECORDSIZE (512)
|
|
|
|
#define BLOCKSIZE (RECORDSIZE * 20)
|
|
|
|
|
|
|
|
static char block[BLOCKSIZE];
|
|
|
|
static unsigned long offset;
|
|
|
|
|
2007-01-06 06:30:22 +08:00
|
|
|
static int tar_umask = 002;
|
2006-09-24 23:31:10 +08:00
|
|
|
|
2011-06-22 09:26:31 +08:00
|
|
|
static int write_tar_filter_archive(const struct archiver *ar,
|
|
|
|
struct archiver_args *args);
|
|
|
|
|
archive-tar: write extended headers for file sizes >= 8GB
The ustar format has a fixed-length field for the size of
each file entry which is supposed to contain up to 11 bytes
of octal-formatted data plus a NUL or space terminator.
These means that the largest size we can represent is
077777777777, or 1 byte short of 8GB. The correct solution
for a larger file, according to POSIX.1-2001, is to add an
extended pax header, similar to how we handle long
filenames. This patch does that, and writes zero for the
size field in the ustar header (the last bit is not
mentioned by POSIX, but it matches how GNU tar behaves with
--format=pax).
This should be a strict improvement over the current
behavior, which is to die in xsnprintf with a "BUG".
However, there's some interesting history here.
Prior to f2f0267 (archive-tar: use xsnprintf for trivial
formatting, 2015-09-24), we silently overflowed the "size"
field. The extra bytes ended up in the "mtime" field of the
header, which was then immediately written itself,
overwriting our extra bytes. What that means depends on how
many bytes we wrote.
If the size was 64GB or greater, then we actually overflowed
digits into the mtime field, meaning our value was
effectively right-shifted by those lost octal digits. And
this patch is again a strict improvement over that.
But if the size was between 8GB and 64GB, then our 12-byte
field held all of the actual digits, and only our NUL
terminator overflowed. According to POSIX, there should be a
NUL or space at the end of the field. However, GNU tar seems
to be lenient here, and will correctly parse a size up 64GB
(minus one) from the field. So sizes in this range might
have just worked, depending on the implementation reading
the tarfile.
This patch is mostly still an improvement there, as the 8GB
limit is specifically mentioned in POSIX as the correct
limit. But it's possible that it could be a regression
(versus the pre-f2f0267 state) if all of the following are
true:
1. You have a file between 8GB and 64GB.
2. Your tar implementation _doesn't_ know about pax
extended headers.
3. Your tar implementation _does_ parse 12-byte sizes from
the ustar header without a delimiter.
It's probably not worth worrying about such an obscure set
of conditions, but I'm documenting it here just in case.
Helped-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-30 17:09:16 +08:00
|
|
|
/*
|
|
|
|
* This is the max value that a ustar size header can specify, as it is fixed
|
|
|
|
* at 11 octal digits. POSIX specifies that we switch to extended headers at
|
|
|
|
* this size.
|
archive-tar: write extended headers for far-future mtime
The ustar format represents timestamps as seconds since the
epoch, but only has room to store 11 octal digits. To
express anything larger, we need to use an extended header.
This is exactly the same case we fixed for the size field in
the previous commit, and the solution here follows the same
pattern.
This is even mentioned as an issue in f2f0267 (archive-tar:
use xsnprintf for trivial formatting, 2015-09-24), but since
it only affected things far in the future, it wasn't deemed
worth dealing with. But note that my calculations claiming
thousands of years were off there; because our xsnprintf
produces a NUL byte, we only have until the year 2242 to fix
this.
Given that this is just around the corner (geologically
speaking, anyway), and because it's easy to fix, let's just
make it work. Unlike the previous fix for "size", where we
had to write an individual extended header for each file, we
can write one global header (since we have only one mtime
for the whole archive).
There's a slight bit of trickiness there. We may already be
writing a global header with a "comment" field for the
commit sha1. So we need to write our new field into the same
header. To do this, we push the decision of whether to write
such a header down into write_global_extended_header(),
which will now assemble the header as it sees fit, and will
return early if we have nothing to write (in practice, we'll
only have a large mtime if it comes from a commit, but this
makes it also work if you set your system clock ahead such
that time() returns a huge value).
Note that we don't (and never did) handle negative
timestamps (i.e., before 1970). This would probably not be
too hard to support in the same way, but since git does not
support negative timestamps at all, I didn't bother here.
After writing the extended header, we munge the timestamp in
the ustar headers to the maximum-allowable size. This is
wrong, but it's the least-wrong thing we can provide to a
tar implementation that doesn't understand pax headers (it's
also what GNU tar does).
Helped-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-30 17:09:20 +08:00
|
|
|
*
|
|
|
|
* Likewise for the mtime (which happens to use a buffer of the same size).
|
archive-tar: write extended headers for file sizes >= 8GB
The ustar format has a fixed-length field for the size of
each file entry which is supposed to contain up to 11 bytes
of octal-formatted data plus a NUL or space terminator.
These means that the largest size we can represent is
077777777777, or 1 byte short of 8GB. The correct solution
for a larger file, according to POSIX.1-2001, is to add an
extended pax header, similar to how we handle long
filenames. This patch does that, and writes zero for the
size field in the ustar header (the last bit is not
mentioned by POSIX, but it matches how GNU tar behaves with
--format=pax).
This should be a strict improvement over the current
behavior, which is to die in xsnprintf with a "BUG".
However, there's some interesting history here.
Prior to f2f0267 (archive-tar: use xsnprintf for trivial
formatting, 2015-09-24), we silently overflowed the "size"
field. The extra bytes ended up in the "mtime" field of the
header, which was then immediately written itself,
overwriting our extra bytes. What that means depends on how
many bytes we wrote.
If the size was 64GB or greater, then we actually overflowed
digits into the mtime field, meaning our value was
effectively right-shifted by those lost octal digits. And
this patch is again a strict improvement over that.
But if the size was between 8GB and 64GB, then our 12-byte
field held all of the actual digits, and only our NUL
terminator overflowed. According to POSIX, there should be a
NUL or space at the end of the field. However, GNU tar seems
to be lenient here, and will correctly parse a size up 64GB
(minus one) from the field. So sizes in this range might
have just worked, depending on the implementation reading
the tarfile.
This patch is mostly still an improvement there, as the 8GB
limit is specifically mentioned in POSIX as the correct
limit. But it's possible that it could be a regression
(versus the pre-f2f0267 state) if all of the following are
true:
1. You have a file between 8GB and 64GB.
2. Your tar implementation _doesn't_ know about pax
extended headers.
3. Your tar implementation _does_ parse 12-byte sizes from
the ustar header without a delimiter.
It's probably not worth worrying about such an obscure set
of conditions, but I'm documenting it here just in case.
Helped-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-30 17:09:16 +08:00
|
|
|
*/
|
2016-07-15 04:04:43 +08:00
|
|
|
#if ULONG_MAX == 0xFFFFFFFF
|
|
|
|
#define USTAR_MAX_SIZE ULONG_MAX
|
|
|
|
#else
|
archive-tar: fix a sparse 'constant too large' warning
Commit dddbad728c ("timestamp_t: a new data type for timestamps",
26-04-2017) introduced a new typedef 'timestamp_t', as a synonym for an
unsigned long, which was used at the time to represent timestamps in
git. A later commit 28f4aee3fb ("use uintmax_t for timestamps",
26-04-2017) changed the typedef to use an 'uintmax_t' for the timestamp
representation type.
When building on a 32-bit Linux system, sparse complains that a constant
(USTAR_MAX_MTIME) used to detect a 'far-future mtime' timestamp, is too
large; 'warning: constant 077777777777UL is so big it is unsigned long
long' on lines 335 and 338 of archive-tar.c. Note that both gcc and
clang only issue a warning if this constant is used in a context that
requires an 'unsigned long' (rather than an uintmax_t). (Since TIME_MAX
is no longer equal to 0xFFFFFFFF, even on a 32-bit system, the macro
USTAR_MAX_MTIME is set to 077777777777UL, which cannot be represented as
an 'unsigned long' constant).
In order to suppress the warning, change the definition of the macro
constant USTAR_MAX_MTIME to use an 'ULL' type suffix.
In a similar vein, on systems which use a 64-bit representation of the
'unsigned long' type, the USTAR_MAX_SIZE constant macro is defined with
the value 077777777777ULL. Although this does not cause any warning
messages to be issued, it would be more appropriate for this constant
to use an 'UL' type suffix rather than 'ULL'.
Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-09 04:34:58 +08:00
|
|
|
#define USTAR_MAX_SIZE 077777777777UL
|
2017-04-27 03:29:31 +08:00
|
|
|
#endif
|
|
|
|
#if TIME_MAX == 0xFFFFFFFF
|
|
|
|
#define USTAR_MAX_MTIME TIME_MAX
|
|
|
|
#else
|
archive-tar: fix a sparse 'constant too large' warning
Commit dddbad728c ("timestamp_t: a new data type for timestamps",
26-04-2017) introduced a new typedef 'timestamp_t', as a synonym for an
unsigned long, which was used at the time to represent timestamps in
git. A later commit 28f4aee3fb ("use uintmax_t for timestamps",
26-04-2017) changed the typedef to use an 'uintmax_t' for the timestamp
representation type.
When building on a 32-bit Linux system, sparse complains that a constant
(USTAR_MAX_MTIME) used to detect a 'far-future mtime' timestamp, is too
large; 'warning: constant 077777777777UL is so big it is unsigned long
long' on lines 335 and 338 of archive-tar.c. Note that both gcc and
clang only issue a warning if this constant is used in a context that
requires an 'unsigned long' (rather than an uintmax_t). (Since TIME_MAX
is no longer equal to 0xFFFFFFFF, even on a 32-bit system, the macro
USTAR_MAX_MTIME is set to 077777777777UL, which cannot be represented as
an 'unsigned long' constant).
In order to suppress the warning, change the definition of the macro
constant USTAR_MAX_MTIME to use an 'ULL' type suffix.
In a similar vein, on systems which use a 64-bit representation of the
'unsigned long' type, the USTAR_MAX_SIZE constant macro is defined with
the value 077777777777ULL. Although this does not cause any warning
messages to be issued, it would be more appropriate for this constant
to use an 'UL' type suffix rather than 'ULL'.
Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-09 04:34:58 +08:00
|
|
|
#define USTAR_MAX_MTIME 077777777777ULL
|
2016-07-15 04:04:43 +08:00
|
|
|
#endif
|
archive-tar: write extended headers for file sizes >= 8GB
The ustar format has a fixed-length field for the size of
each file entry which is supposed to contain up to 11 bytes
of octal-formatted data plus a NUL or space terminator.
These means that the largest size we can represent is
077777777777, or 1 byte short of 8GB. The correct solution
for a larger file, according to POSIX.1-2001, is to add an
extended pax header, similar to how we handle long
filenames. This patch does that, and writes zero for the
size field in the ustar header (the last bit is not
mentioned by POSIX, but it matches how GNU tar behaves with
--format=pax).
This should be a strict improvement over the current
behavior, which is to die in xsnprintf with a "BUG".
However, there's some interesting history here.
Prior to f2f0267 (archive-tar: use xsnprintf for trivial
formatting, 2015-09-24), we silently overflowed the "size"
field. The extra bytes ended up in the "mtime" field of the
header, which was then immediately written itself,
overwriting our extra bytes. What that means depends on how
many bytes we wrote.
If the size was 64GB or greater, then we actually overflowed
digits into the mtime field, meaning our value was
effectively right-shifted by those lost octal digits. And
this patch is again a strict improvement over that.
But if the size was between 8GB and 64GB, then our 12-byte
field held all of the actual digits, and only our NUL
terminator overflowed. According to POSIX, there should be a
NUL or space at the end of the field. However, GNU tar seems
to be lenient here, and will correctly parse a size up 64GB
(minus one) from the field. So sizes in this range might
have just worked, depending on the implementation reading
the tarfile.
This patch is mostly still an improvement there, as the 8GB
limit is specifically mentioned in POSIX as the correct
limit. But it's possible that it could be a regression
(versus the pre-f2f0267 state) if all of the following are
true:
1. You have a file between 8GB and 64GB.
2. Your tar implementation _doesn't_ know about pax
extended headers.
3. Your tar implementation _does_ parse 12-byte sizes from
the ustar header without a delimiter.
It's probably not worth worrying about such an obscure set
of conditions, but I'm documenting it here just in case.
Helped-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-30 17:09:16 +08:00
|
|
|
|
archive-tar: add internal gzip implementation
Git uses zlib for its own object store, but calls gzip when creating tgz
archives. Add an option to perform the gzip compression for the latter
using zlib, without depending on the external gzip binary.
Plug it in by making write_block a function pointer and switching to a
compressing variant if the filter command has the magic value "git
archive gzip". Does that indirection slow down tar creation? Not
really, at least not in this test:
$ hyperfine -w3 -L rev HEAD,origin/main -p 'git checkout {rev} && make' \
'./git -C ../linux archive --format=tar HEAD # {rev}'
Benchmark #1: ./git -C ../linux archive --format=tar HEAD # HEAD
Time (mean ± σ): 4.044 s ± 0.007 s [User: 3.901 s, System: 0.137 s]
Range (min … max): 4.038 s … 4.059 s 10 runs
Benchmark #2: ./git -C ../linux archive --format=tar HEAD # origin/main
Time (mean ± σ): 4.047 s ± 0.009 s [User: 3.903 s, System: 0.138 s]
Range (min … max): 4.038 s … 4.066 s 10 runs
How does tgz creation perform?
$ hyperfine -w3 -L command 'gzip -cn','git archive gzip' \
'./git -c tar.tgz.command="{command}" -C ../linux archive --format=tgz HEAD'
Benchmark #1: ./git -c tar.tgz.command="gzip -cn" -C ../linux archive --format=tgz HEAD
Time (mean ± σ): 20.404 s ± 0.006 s [User: 23.943 s, System: 0.401 s]
Range (min … max): 20.395 s … 20.414 s 10 runs
Benchmark #2: ./git -c tar.tgz.command="git archive gzip" -C ../linux archive --format=tgz HEAD
Time (mean ± σ): 23.807 s ± 0.023 s [User: 23.655 s, System: 0.145 s]
Range (min … max): 23.782 s … 23.857 s 10 runs
Summary
'./git -c tar.tgz.command="gzip -cn" -C ../linux archive --format=tgz HEAD' ran
1.17 ± 0.00 times faster than './git -c tar.tgz.command="git archive gzip" -C ../linux archive --format=tgz HEAD'
So the internal implementation takes 17% longer on the Linux repo, but
uses 2% less CPU time. That's because the external gzip can run in
parallel on its own processor, while the internal one works sequentially
and avoids the inter-process communication overhead.
What are the benefits? Only an internal sequential implementation can
offer this eco mode, and it allows avoiding the gzip(1) requirement.
This implementation uses the helper functions from our zlib.c instead of
the convenient gz* functions from zlib, because the latter doesn't give
the control over the generated gzip header that the next patch requires.
Original-patch-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-06-16 01:02:33 +08:00
|
|
|
static void tar_write_block(const void *buf)
|
2022-06-16 01:01:14 +08:00
|
|
|
{
|
|
|
|
write_or_die(1, buf, BLOCKSIZE);
|
|
|
|
}
|
|
|
|
|
archive-tar: add internal gzip implementation
Git uses zlib for its own object store, but calls gzip when creating tgz
archives. Add an option to perform the gzip compression for the latter
using zlib, without depending on the external gzip binary.
Plug it in by making write_block a function pointer and switching to a
compressing variant if the filter command has the magic value "git
archive gzip". Does that indirection slow down tar creation? Not
really, at least not in this test:
$ hyperfine -w3 -L rev HEAD,origin/main -p 'git checkout {rev} && make' \
'./git -C ../linux archive --format=tar HEAD # {rev}'
Benchmark #1: ./git -C ../linux archive --format=tar HEAD # HEAD
Time (mean ± σ): 4.044 s ± 0.007 s [User: 3.901 s, System: 0.137 s]
Range (min … max): 4.038 s … 4.059 s 10 runs
Benchmark #2: ./git -C ../linux archive --format=tar HEAD # origin/main
Time (mean ± σ): 4.047 s ± 0.009 s [User: 3.903 s, System: 0.138 s]
Range (min … max): 4.038 s … 4.066 s 10 runs
How does tgz creation perform?
$ hyperfine -w3 -L command 'gzip -cn','git archive gzip' \
'./git -c tar.tgz.command="{command}" -C ../linux archive --format=tgz HEAD'
Benchmark #1: ./git -c tar.tgz.command="gzip -cn" -C ../linux archive --format=tgz HEAD
Time (mean ± σ): 20.404 s ± 0.006 s [User: 23.943 s, System: 0.401 s]
Range (min … max): 20.395 s … 20.414 s 10 runs
Benchmark #2: ./git -c tar.tgz.command="git archive gzip" -C ../linux archive --format=tgz HEAD
Time (mean ± σ): 23.807 s ± 0.023 s [User: 23.655 s, System: 0.145 s]
Range (min … max): 23.782 s … 23.857 s 10 runs
Summary
'./git -c tar.tgz.command="gzip -cn" -C ../linux archive --format=tgz HEAD' ran
1.17 ± 0.00 times faster than './git -c tar.tgz.command="git archive gzip" -C ../linux archive --format=tgz HEAD'
So the internal implementation takes 17% longer on the Linux repo, but
uses 2% less CPU time. That's because the external gzip can run in
parallel on its own processor, while the internal one works sequentially
and avoids the inter-process communication overhead.
What are the benefits? Only an internal sequential implementation can
offer this eco mode, and it allows avoiding the gzip(1) requirement.
This implementation uses the helper functions from our zlib.c instead of
the convenient gz* functions from zlib, because the latter doesn't give
the control over the generated gzip header that the next patch requires.
Original-patch-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-06-16 01:02:33 +08:00
|
|
|
static void (*write_block)(const void *) = tar_write_block;
|
|
|
|
|
2006-09-24 23:31:10 +08:00
|
|
|
/* writes out the whole block, but only if it is full */
|
|
|
|
static void write_if_needed(void)
|
|
|
|
{
|
|
|
|
if (offset == BLOCKSIZE) {
|
2022-06-16 01:01:14 +08:00
|
|
|
write_block(block);
|
2006-09-24 23:31:10 +08:00
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* queues up writes, so that all our write(2) calls write exactly one
|
|
|
|
* full block; pads writes to RECORDSIZE
|
|
|
|
*/
|
2012-05-03 09:51:04 +08:00
|
|
|
static void do_write_blocked(const void *data, unsigned long size)
|
2006-09-24 23:31:10 +08:00
|
|
|
{
|
|
|
|
const char *buf = data;
|
|
|
|
|
|
|
|
if (offset) {
|
|
|
|
unsigned long chunk = BLOCKSIZE - offset;
|
|
|
|
if (size < chunk)
|
|
|
|
chunk = size;
|
|
|
|
memcpy(block + offset, buf, chunk);
|
|
|
|
size -= chunk;
|
|
|
|
offset += chunk;
|
|
|
|
buf += chunk;
|
|
|
|
write_if_needed();
|
|
|
|
}
|
|
|
|
while (size >= BLOCKSIZE) {
|
2022-06-16 01:01:14 +08:00
|
|
|
write_block(buf);
|
2006-09-24 23:31:10 +08:00
|
|
|
size -= BLOCKSIZE;
|
|
|
|
buf += BLOCKSIZE;
|
|
|
|
}
|
|
|
|
if (size) {
|
|
|
|
memcpy(block + offset, buf, size);
|
|
|
|
offset += size;
|
|
|
|
}
|
2012-05-03 09:51:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void finish_record(void)
|
|
|
|
{
|
|
|
|
unsigned long tail;
|
2006-09-24 23:31:10 +08:00
|
|
|
tail = offset % RECORDSIZE;
|
|
|
|
if (tail) {
|
|
|
|
memset(block + offset, 0, RECORDSIZE - tail);
|
|
|
|
offset += RECORDSIZE - tail;
|
|
|
|
}
|
|
|
|
write_if_needed();
|
|
|
|
}
|
|
|
|
|
2012-05-03 09:51:04 +08:00
|
|
|
static void write_blocked(const void *data, unsigned long size)
|
|
|
|
{
|
|
|
|
do_write_blocked(data, size);
|
|
|
|
finish_record();
|
|
|
|
}
|
|
|
|
|
2006-09-24 23:31:10 +08:00
|
|
|
/*
|
|
|
|
* The end of tar archives is marked by 2*512 nul bytes and after that
|
|
|
|
* follows the rest of the block (if any).
|
|
|
|
*/
|
|
|
|
static void write_trailer(void)
|
|
|
|
{
|
|
|
|
int tail = BLOCKSIZE - offset;
|
|
|
|
memset(block + offset, 0, tail);
|
2022-06-16 01:01:14 +08:00
|
|
|
write_block(block);
|
2006-09-24 23:31:10 +08:00
|
|
|
if (tail < 2 * RECORDSIZE) {
|
|
|
|
memset(block, 0, offset);
|
2022-06-16 01:01:14 +08:00
|
|
|
write_block(block);
|
2006-09-24 23:31:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-03 09:51:04 +08:00
|
|
|
/*
|
|
|
|
* queues up writes, so that all our write(2) calls write exactly one
|
|
|
|
* full block; pads writes to RECORDSIZE
|
|
|
|
*/
|
2020-01-31 04:32:20 +08:00
|
|
|
static int stream_blocked(struct repository *r, const struct object_id *oid)
|
2012-05-03 09:51:04 +08:00
|
|
|
{
|
|
|
|
struct git_istream *st;
|
|
|
|
enum object_type type;
|
|
|
|
unsigned long sz;
|
|
|
|
char buf[BLOCKSIZE];
|
|
|
|
ssize_t readlen;
|
|
|
|
|
2020-01-31 04:32:20 +08:00
|
|
|
st = open_istream(r, oid, &type, &sz, NULL);
|
2012-05-03 09:51:04 +08:00
|
|
|
if (!st)
|
2018-07-21 15:49:20 +08:00
|
|
|
return error(_("cannot stream blob %s"), oid_to_hex(oid));
|
2012-05-03 09:51:04 +08:00
|
|
|
for (;;) {
|
|
|
|
readlen = read_istream(st, buf, sizeof(buf));
|
|
|
|
if (readlen <= 0)
|
|
|
|
break;
|
|
|
|
do_write_blocked(buf, readlen);
|
|
|
|
}
|
|
|
|
close_istream(st);
|
|
|
|
if (!readlen)
|
|
|
|
finish_record();
|
|
|
|
return readlen;
|
|
|
|
}
|
|
|
|
|
2006-09-24 23:31:10 +08:00
|
|
|
/*
|
|
|
|
* pax extended header records have the format "%u %s=%s\n". %u contains
|
|
|
|
* the size of the whole string (including the %u), the first %s is the
|
|
|
|
* keyword, the second one is the value. This function constructs such a
|
|
|
|
* string and appends it to a struct strbuf.
|
|
|
|
*/
|
|
|
|
static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
|
2019-08-18 00:24:13 +08:00
|
|
|
const char *value, size_t valuelen)
|
2006-09-24 23:31:10 +08:00
|
|
|
{
|
2019-08-18 00:23:52 +08:00
|
|
|
size_t orig_len = sb->len;
|
2019-08-18 00:24:13 +08:00
|
|
|
size_t len, tmp;
|
2006-09-24 23:31:10 +08:00
|
|
|
|
|
|
|
/* "%u %s=%s\n" */
|
|
|
|
len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;
|
2019-08-18 00:24:01 +08:00
|
|
|
for (tmp = 1; len / 10 >= tmp; tmp *= 10)
|
2006-09-24 23:31:10 +08:00
|
|
|
len++;
|
|
|
|
|
2007-09-06 19:20:06 +08:00
|
|
|
strbuf_grow(sb, len);
|
2019-08-18 00:24:13 +08:00
|
|
|
strbuf_addf(sb, "%"PRIuMAX" %s=", (uintmax_t)len, keyword);
|
2007-09-06 19:20:06 +08:00
|
|
|
strbuf_add(sb, value, valuelen);
|
|
|
|
strbuf_addch(sb, '\n');
|
2019-08-18 00:23:52 +08:00
|
|
|
|
|
|
|
if (len != sb->len - orig_len)
|
2019-08-18 00:24:23 +08:00
|
|
|
BUG("pax extended header length miscalculated as %"PRIuMAX
|
|
|
|
", should be %"PRIuMAX,
|
|
|
|
(uintmax_t)len, (uintmax_t)(sb->len - orig_len));
|
2006-09-24 23:31:10 +08:00
|
|
|
}
|
|
|
|
|
archive-tar: write extended headers for file sizes >= 8GB
The ustar format has a fixed-length field for the size of
each file entry which is supposed to contain up to 11 bytes
of octal-formatted data plus a NUL or space terminator.
These means that the largest size we can represent is
077777777777, or 1 byte short of 8GB. The correct solution
for a larger file, according to POSIX.1-2001, is to add an
extended pax header, similar to how we handle long
filenames. This patch does that, and writes zero for the
size field in the ustar header (the last bit is not
mentioned by POSIX, but it matches how GNU tar behaves with
--format=pax).
This should be a strict improvement over the current
behavior, which is to die in xsnprintf with a "BUG".
However, there's some interesting history here.
Prior to f2f0267 (archive-tar: use xsnprintf for trivial
formatting, 2015-09-24), we silently overflowed the "size"
field. The extra bytes ended up in the "mtime" field of the
header, which was then immediately written itself,
overwriting our extra bytes. What that means depends on how
many bytes we wrote.
If the size was 64GB or greater, then we actually overflowed
digits into the mtime field, meaning our value was
effectively right-shifted by those lost octal digits. And
this patch is again a strict improvement over that.
But if the size was between 8GB and 64GB, then our 12-byte
field held all of the actual digits, and only our NUL
terminator overflowed. According to POSIX, there should be a
NUL or space at the end of the field. However, GNU tar seems
to be lenient here, and will correctly parse a size up 64GB
(minus one) from the field. So sizes in this range might
have just worked, depending on the implementation reading
the tarfile.
This patch is mostly still an improvement there, as the 8GB
limit is specifically mentioned in POSIX as the correct
limit. But it's possible that it could be a regression
(versus the pre-f2f0267 state) if all of the following are
true:
1. You have a file between 8GB and 64GB.
2. Your tar implementation _doesn't_ know about pax
extended headers.
3. Your tar implementation _does_ parse 12-byte sizes from
the ustar header without a delimiter.
It's probably not worth worrying about such an obscure set
of conditions, but I'm documenting it here just in case.
Helped-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-30 17:09:16 +08:00
|
|
|
/*
|
|
|
|
* Like strbuf_append_ext_header, but for numeric values.
|
|
|
|
*/
|
|
|
|
static void strbuf_append_ext_header_uint(struct strbuf *sb,
|
|
|
|
const char *keyword,
|
|
|
|
uintmax_t value)
|
|
|
|
{
|
|
|
|
char buf[40]; /* big enough for 2^128 in decimal, plus NUL */
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = xsnprintf(buf, sizeof(buf), "%"PRIuMAX, value);
|
|
|
|
strbuf_append_ext_header(sb, keyword, buf, len);
|
|
|
|
}
|
|
|
|
|
2006-09-24 23:31:10 +08:00
|
|
|
static unsigned int ustar_header_chksum(const struct ustar_header *header)
|
|
|
|
{
|
2012-06-14 01:42:25 +08:00
|
|
|
const unsigned char *p = (const unsigned char *)header;
|
2006-09-24 23:31:10 +08:00
|
|
|
unsigned int chksum = 0;
|
2012-06-14 01:42:25 +08:00
|
|
|
while (p < (const unsigned char *)header->chksum)
|
2006-09-24 23:31:10 +08:00
|
|
|
chksum += *p++;
|
|
|
|
chksum += sizeof(header->chksum) * ' ';
|
|
|
|
p += sizeof(header->chksum);
|
2012-06-14 01:42:25 +08:00
|
|
|
while (p < (const unsigned char *)header + sizeof(struct ustar_header))
|
2006-09-24 23:31:10 +08:00
|
|
|
chksum += *p++;
|
|
|
|
return chksum;
|
|
|
|
}
|
|
|
|
|
2008-07-15 03:22:24 +08:00
|
|
|
static size_t get_path_prefix(const char *path, size_t pathlen, size_t maxlen)
|
2006-09-24 23:31:10 +08:00
|
|
|
{
|
2008-07-15 03:22:24 +08:00
|
|
|
size_t i = pathlen;
|
2013-01-06 06:49:54 +08:00
|
|
|
if (i > 1 && path[i - 1] == '/')
|
|
|
|
i--;
|
2006-09-24 23:31:10 +08:00
|
|
|
if (i > maxlen)
|
|
|
|
i = maxlen;
|
|
|
|
do {
|
|
|
|
i--;
|
2008-07-15 03:22:24 +08:00
|
|
|
} while (i > 0 && path[i] != '/');
|
2006-09-24 23:31:10 +08:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2012-05-03 09:51:01 +08:00
|
|
|
static void prepare_header(struct archiver_args *args,
|
|
|
|
struct ustar_header *header,
|
|
|
|
unsigned int mode, unsigned long size)
|
|
|
|
{
|
archive-tar: use xsnprintf for trivial formatting
When we generate tar headers, we sprintf() values directly
into a struct with the fixed-size header values. For the
most part this is fine, as we are formatting small values
(e.g., the octal format of "mode & 0x7777" is of fixed
length). But it's still a good idea to use xsnprintf here.
It communicates to readers what our expectation is, and it
provides a run-time check that we are not overflowing the
buffers.
The one exception here is the mtime, which comes from the
epoch time of the commit we are archiving. For sane values,
this fits into the 12-byte value allocated in the header.
But since git can handle 64-bit times, if I claim to be a
visitor from the year 10,000 AD, I can overflow the buffer.
This turns out to be harmless, as we simply overflow into
the chksum field, which is then overwritten.
This case is also best as an xsnprintf. It should never come
up, short of extremely malformed dates, and in that case we
are probably better off dying than silently truncating the
date value (and we cannot expand the size of the buffer,
since it is dictated by the ustar format). Our friends in
the year 5138 (when we legitimately flip to a 12-digit
epoch) can deal with that problem then.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-25 05:06:24 +08:00
|
|
|
xsnprintf(header->mode, sizeof(header->mode), "%07o", mode & 07777);
|
2018-11-11 15:05:04 +08:00
|
|
|
xsnprintf(header->size, sizeof(header->size), "%011"PRIoMAX , S_ISREG(mode) ? (uintmax_t)size : (uintmax_t)0);
|
archive-tar: use xsnprintf for trivial formatting
When we generate tar headers, we sprintf() values directly
into a struct with the fixed-size header values. For the
most part this is fine, as we are formatting small values
(e.g., the octal format of "mode & 0x7777" is of fixed
length). But it's still a good idea to use xsnprintf here.
It communicates to readers what our expectation is, and it
provides a run-time check that we are not overflowing the
buffers.
The one exception here is the mtime, which comes from the
epoch time of the commit we are archiving. For sane values,
this fits into the 12-byte value allocated in the header.
But since git can handle 64-bit times, if I claim to be a
visitor from the year 10,000 AD, I can overflow the buffer.
This turns out to be harmless, as we simply overflow into
the chksum field, which is then overwritten.
This case is also best as an xsnprintf. It should never come
up, short of extremely malformed dates, and in that case we
are probably better off dying than silently truncating the
date value (and we cannot expand the size of the buffer,
since it is dictated by the ustar format). Our friends in
the year 5138 (when we legitimately flip to a 12-digit
epoch) can deal with that problem then.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-25 05:06:24 +08:00
|
|
|
xsnprintf(header->mtime, sizeof(header->mtime), "%011lo", (unsigned long) args->time);
|
2012-05-03 09:51:01 +08:00
|
|
|
|
archive-tar: use xsnprintf for trivial formatting
When we generate tar headers, we sprintf() values directly
into a struct with the fixed-size header values. For the
most part this is fine, as we are formatting small values
(e.g., the octal format of "mode & 0x7777" is of fixed
length). But it's still a good idea to use xsnprintf here.
It communicates to readers what our expectation is, and it
provides a run-time check that we are not overflowing the
buffers.
The one exception here is the mtime, which comes from the
epoch time of the commit we are archiving. For sane values,
this fits into the 12-byte value allocated in the header.
But since git can handle 64-bit times, if I claim to be a
visitor from the year 10,000 AD, I can overflow the buffer.
This turns out to be harmless, as we simply overflow into
the chksum field, which is then overwritten.
This case is also best as an xsnprintf. It should never come
up, short of extremely malformed dates, and in that case we
are probably better off dying than silently truncating the
date value (and we cannot expand the size of the buffer,
since it is dictated by the ustar format). Our friends in
the year 5138 (when we legitimately flip to a 12-digit
epoch) can deal with that problem then.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-25 05:06:24 +08:00
|
|
|
xsnprintf(header->uid, sizeof(header->uid), "%07o", 0);
|
|
|
|
xsnprintf(header->gid, sizeof(header->gid), "%07o", 0);
|
2012-05-03 09:51:01 +08:00
|
|
|
strlcpy(header->uname, "root", sizeof(header->uname));
|
|
|
|
strlcpy(header->gname, "root", sizeof(header->gname));
|
archive-tar: use xsnprintf for trivial formatting
When we generate tar headers, we sprintf() values directly
into a struct with the fixed-size header values. For the
most part this is fine, as we are formatting small values
(e.g., the octal format of "mode & 0x7777" is of fixed
length). But it's still a good idea to use xsnprintf here.
It communicates to readers what our expectation is, and it
provides a run-time check that we are not overflowing the
buffers.
The one exception here is the mtime, which comes from the
epoch time of the commit we are archiving. For sane values,
this fits into the 12-byte value allocated in the header.
But since git can handle 64-bit times, if I claim to be a
visitor from the year 10,000 AD, I can overflow the buffer.
This turns out to be harmless, as we simply overflow into
the chksum field, which is then overwritten.
This case is also best as an xsnprintf. It should never come
up, short of extremely malformed dates, and in that case we
are probably better off dying than silently truncating the
date value (and we cannot expand the size of the buffer,
since it is dictated by the ustar format). Our friends in
the year 5138 (when we legitimately flip to a 12-digit
epoch) can deal with that problem then.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-25 05:06:24 +08:00
|
|
|
xsnprintf(header->devmajor, sizeof(header->devmajor), "%07o", 0);
|
|
|
|
xsnprintf(header->devminor, sizeof(header->devminor), "%07o", 0);
|
2012-05-03 09:51:01 +08:00
|
|
|
|
|
|
|
memcpy(header->magic, "ustar", 6);
|
|
|
|
memcpy(header->version, "00", 2);
|
|
|
|
|
2016-05-26 12:28:08 +08:00
|
|
|
xsnprintf(header->chksum, sizeof(header->chksum), "%07o", ustar_header_chksum(header));
|
2012-05-03 09:51:01 +08:00
|
|
|
}
|
|
|
|
|
2016-08-06 22:35:38 +08:00
|
|
|
static void write_extended_header(struct archiver_args *args,
|
2018-03-12 10:27:35 +08:00
|
|
|
const struct object_id *oid,
|
2016-08-06 22:35:38 +08:00
|
|
|
const void *buffer, unsigned long size)
|
2012-05-03 09:51:01 +08:00
|
|
|
{
|
|
|
|
struct ustar_header header;
|
|
|
|
unsigned int mode;
|
|
|
|
memset(&header, 0, sizeof(header));
|
|
|
|
*header.typeflag = TYPEFLAG_EXT_HEADER;
|
2014-10-21 03:04:46 +08:00
|
|
|
mode = 0100666;
|
2018-03-12 10:27:35 +08:00
|
|
|
xsnprintf(header.name, sizeof(header.name), "%s.paxheader", oid_to_hex(oid));
|
2012-05-03 09:51:01 +08:00
|
|
|
prepare_header(args, &header, mode, size);
|
|
|
|
write_blocked(&header, sizeof(header));
|
|
|
|
write_blocked(buffer, size);
|
|
|
|
}
|
|
|
|
|
2008-07-15 03:22:24 +08:00
|
|
|
static int write_tar_entry(struct archiver_args *args,
|
2018-03-12 10:27:35 +08:00
|
|
|
const struct object_id *oid,
|
2012-05-03 09:51:03 +08:00
|
|
|
const char *path, size_t pathlen,
|
2020-09-20 05:23:32 +08:00
|
|
|
unsigned int mode,
|
|
|
|
void *buffer, unsigned long size)
|
2006-09-24 23:31:10 +08:00
|
|
|
{
|
|
|
|
struct ustar_header header;
|
2008-10-10 03:12:12 +08:00
|
|
|
struct strbuf ext_header = STRBUF_INIT;
|
2020-09-20 05:23:32 +08:00
|
|
|
unsigned long size_in_header;
|
2008-07-15 03:22:24 +08:00
|
|
|
int err = 0;
|
2006-09-24 23:31:10 +08:00
|
|
|
|
|
|
|
memset(&header, 0, sizeof(header));
|
|
|
|
|
2012-05-03 09:51:02 +08:00
|
|
|
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
|
|
|
|
*header.typeflag = TYPEFLAG_DIR;
|
|
|
|
mode = (mode | 0777) & ~tar_umask;
|
|
|
|
} else if (S_ISLNK(mode)) {
|
|
|
|
*header.typeflag = TYPEFLAG_LNK;
|
|
|
|
mode |= 0777;
|
|
|
|
} else if (S_ISREG(mode)) {
|
|
|
|
*header.typeflag = TYPEFLAG_REG;
|
|
|
|
mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
|
2006-09-24 23:31:10 +08:00
|
|
|
} else {
|
2018-07-21 15:49:20 +08:00
|
|
|
return error(_("unsupported file mode: 0%o (SHA1: %s)"),
|
2018-03-12 10:27:35 +08:00
|
|
|
mode, oid_to_hex(oid));
|
2012-05-03 09:51:02 +08:00
|
|
|
}
|
|
|
|
if (pathlen > sizeof(header.name)) {
|
|
|
|
size_t plen = get_path_prefix(path, pathlen,
|
|
|
|
sizeof(header.prefix));
|
|
|
|
size_t rest = pathlen - plen - 1;
|
|
|
|
if (plen > 0 && rest <= sizeof(header.name)) {
|
|
|
|
memcpy(header.prefix, path, plen);
|
2015-09-25 05:03:49 +08:00
|
|
|
memcpy(header.name, path + plen + 1, rest);
|
2006-09-24 23:31:10 +08:00
|
|
|
} else {
|
archive-tar: use xsnprintf for trivial formatting
When we generate tar headers, we sprintf() values directly
into a struct with the fixed-size header values. For the
most part this is fine, as we are formatting small values
(e.g., the octal format of "mode & 0x7777" is of fixed
length). But it's still a good idea to use xsnprintf here.
It communicates to readers what our expectation is, and it
provides a run-time check that we are not overflowing the
buffers.
The one exception here is the mtime, which comes from the
epoch time of the commit we are archiving. For sane values,
this fits into the 12-byte value allocated in the header.
But since git can handle 64-bit times, if I claim to be a
visitor from the year 10,000 AD, I can overflow the buffer.
This turns out to be harmless, as we simply overflow into
the chksum field, which is then overwritten.
This case is also best as an xsnprintf. It should never come
up, short of extremely malformed dates, and in that case we
are probably better off dying than silently truncating the
date value (and we cannot expand the size of the buffer,
since it is dictated by the ustar format). Our friends in
the year 5138 (when we legitimately flip to a 12-digit
epoch) can deal with that problem then.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-25 05:06:24 +08:00
|
|
|
xsnprintf(header.name, sizeof(header.name), "%s.data",
|
2018-03-12 10:27:35 +08:00
|
|
|
oid_to_hex(oid));
|
2012-05-03 09:51:02 +08:00
|
|
|
strbuf_append_ext_header(&ext_header, "path",
|
|
|
|
path, pathlen);
|
2006-09-24 23:31:10 +08:00
|
|
|
}
|
2012-05-03 09:51:02 +08:00
|
|
|
} else
|
|
|
|
memcpy(header.name, path, pathlen);
|
2006-09-24 23:31:10 +08:00
|
|
|
|
2012-05-03 09:51:03 +08:00
|
|
|
if (S_ISLNK(mode)) {
|
2006-09-24 23:31:10 +08:00
|
|
|
if (size > sizeof(header.linkname)) {
|
archive-tar: use xsnprintf for trivial formatting
When we generate tar headers, we sprintf() values directly
into a struct with the fixed-size header values. For the
most part this is fine, as we are formatting small values
(e.g., the octal format of "mode & 0x7777" is of fixed
length). But it's still a good idea to use xsnprintf here.
It communicates to readers what our expectation is, and it
provides a run-time check that we are not overflowing the
buffers.
The one exception here is the mtime, which comes from the
epoch time of the commit we are archiving. For sane values,
this fits into the 12-byte value allocated in the header.
But since git can handle 64-bit times, if I claim to be a
visitor from the year 10,000 AD, I can overflow the buffer.
This turns out to be harmless, as we simply overflow into
the chksum field, which is then overwritten.
This case is also best as an xsnprintf. It should never come
up, short of extremely malformed dates, and in that case we
are probably better off dying than silently truncating the
date value (and we cannot expand the size of the buffer,
since it is dictated by the ustar format). Our friends in
the year 5138 (when we legitimately flip to a 12-digit
epoch) can deal with that problem then.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-25 05:06:24 +08:00
|
|
|
xsnprintf(header.linkname, sizeof(header.linkname),
|
2018-03-12 10:27:35 +08:00
|
|
|
"see %s.paxheader", oid_to_hex(oid));
|
2006-09-24 23:31:10 +08:00
|
|
|
strbuf_append_ext_header(&ext_header, "linkpath",
|
|
|
|
buffer, size);
|
|
|
|
} else
|
|
|
|
memcpy(header.linkname, buffer, size);
|
|
|
|
}
|
|
|
|
|
archive-tar: write extended headers for file sizes >= 8GB
The ustar format has a fixed-length field for the size of
each file entry which is supposed to contain up to 11 bytes
of octal-formatted data plus a NUL or space terminator.
These means that the largest size we can represent is
077777777777, or 1 byte short of 8GB. The correct solution
for a larger file, according to POSIX.1-2001, is to add an
extended pax header, similar to how we handle long
filenames. This patch does that, and writes zero for the
size field in the ustar header (the last bit is not
mentioned by POSIX, but it matches how GNU tar behaves with
--format=pax).
This should be a strict improvement over the current
behavior, which is to die in xsnprintf with a "BUG".
However, there's some interesting history here.
Prior to f2f0267 (archive-tar: use xsnprintf for trivial
formatting, 2015-09-24), we silently overflowed the "size"
field. The extra bytes ended up in the "mtime" field of the
header, which was then immediately written itself,
overwriting our extra bytes. What that means depends on how
many bytes we wrote.
If the size was 64GB or greater, then we actually overflowed
digits into the mtime field, meaning our value was
effectively right-shifted by those lost octal digits. And
this patch is again a strict improvement over that.
But if the size was between 8GB and 64GB, then our 12-byte
field held all of the actual digits, and only our NUL
terminator overflowed. According to POSIX, there should be a
NUL or space at the end of the field. However, GNU tar seems
to be lenient here, and will correctly parse a size up 64GB
(minus one) from the field. So sizes in this range might
have just worked, depending on the implementation reading
the tarfile.
This patch is mostly still an improvement there, as the 8GB
limit is specifically mentioned in POSIX as the correct
limit. But it's possible that it could be a regression
(versus the pre-f2f0267 state) if all of the following are
true:
1. You have a file between 8GB and 64GB.
2. Your tar implementation _doesn't_ know about pax
extended headers.
3. Your tar implementation _does_ parse 12-byte sizes from
the ustar header without a delimiter.
It's probably not worth worrying about such an obscure set
of conditions, but I'm documenting it here just in case.
Helped-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-30 17:09:16 +08:00
|
|
|
size_in_header = size;
|
|
|
|
if (S_ISREG(mode) && size > USTAR_MAX_SIZE) {
|
|
|
|
size_in_header = 0;
|
|
|
|
strbuf_append_ext_header_uint(&ext_header, "size", size);
|
|
|
|
}
|
|
|
|
|
|
|
|
prepare_header(args, &header, mode, size_in_header);
|
2006-09-24 23:31:10 +08:00
|
|
|
|
|
|
|
if (ext_header.len > 0) {
|
2018-03-12 10:27:35 +08:00
|
|
|
write_extended_header(args, oid, ext_header.buf,
|
2016-08-06 22:35:38 +08:00
|
|
|
ext_header.len);
|
2006-09-24 23:31:10 +08:00
|
|
|
}
|
2007-09-06 19:20:06 +08:00
|
|
|
strbuf_release(&ext_header);
|
2006-09-24 23:31:10 +08:00
|
|
|
write_blocked(&header, sizeof(header));
|
2012-05-03 09:51:04 +08:00
|
|
|
if (S_ISREG(mode) && size > 0) {
|
|
|
|
if (buffer)
|
|
|
|
write_blocked(buffer, size);
|
|
|
|
else
|
2020-01-31 04:32:20 +08:00
|
|
|
err = stream_blocked(args->repo, oid);
|
2012-05-03 09:51:04 +08:00
|
|
|
}
|
2008-07-15 03:22:24 +08:00
|
|
|
return err;
|
2006-09-24 23:31:10 +08:00
|
|
|
}
|
|
|
|
|
2016-06-30 17:09:26 +08:00
|
|
|
static void write_global_extended_header(struct archiver_args *args)
|
2006-09-24 23:31:10 +08:00
|
|
|
{
|
2019-02-19 08:05:20 +08:00
|
|
|
const struct object_id *oid = args->commit_oid;
|
2008-10-10 03:12:12 +08:00
|
|
|
struct strbuf ext_header = STRBUF_INIT;
|
2012-05-03 09:51:01 +08:00
|
|
|
struct ustar_header header;
|
|
|
|
unsigned int mode;
|
2007-09-06 19:20:06 +08:00
|
|
|
|
2019-02-19 08:05:20 +08:00
|
|
|
if (oid)
|
archive-tar: write extended headers for far-future mtime
The ustar format represents timestamps as seconds since the
epoch, but only has room to store 11 octal digits. To
express anything larger, we need to use an extended header.
This is exactly the same case we fixed for the size field in
the previous commit, and the solution here follows the same
pattern.
This is even mentioned as an issue in f2f0267 (archive-tar:
use xsnprintf for trivial formatting, 2015-09-24), but since
it only affected things far in the future, it wasn't deemed
worth dealing with. But note that my calculations claiming
thousands of years were off there; because our xsnprintf
produces a NUL byte, we only have until the year 2242 to fix
this.
Given that this is just around the corner (geologically
speaking, anyway), and because it's easy to fix, let's just
make it work. Unlike the previous fix for "size", where we
had to write an individual extended header for each file, we
can write one global header (since we have only one mtime
for the whole archive).
There's a slight bit of trickiness there. We may already be
writing a global header with a "comment" field for the
commit sha1. So we need to write our new field into the same
header. To do this, we push the decision of whether to write
such a header down into write_global_extended_header(),
which will now assemble the header as it sees fit, and will
return early if we have nothing to write (in practice, we'll
only have a large mtime if it comes from a commit, but this
makes it also work if you set your system clock ahead such
that time() returns a huge value).
Note that we don't (and never did) handle negative
timestamps (i.e., before 1970). This would probably not be
too hard to support in the same way, but since git does not
support negative timestamps at all, I didn't bother here.
After writing the extended header, we munge the timestamp in
the ustar headers to the maximum-allowable size. This is
wrong, but it's the least-wrong thing we can provide to a
tar implementation that doesn't understand pax headers (it's
also what GNU tar does).
Helped-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-30 17:09:20 +08:00
|
|
|
strbuf_append_ext_header(&ext_header, "comment",
|
2019-02-19 08:05:20 +08:00
|
|
|
oid_to_hex(oid),
|
|
|
|
the_hash_algo->hexsz);
|
archive-tar: write extended headers for far-future mtime
The ustar format represents timestamps as seconds since the
epoch, but only has room to store 11 octal digits. To
express anything larger, we need to use an extended header.
This is exactly the same case we fixed for the size field in
the previous commit, and the solution here follows the same
pattern.
This is even mentioned as an issue in f2f0267 (archive-tar:
use xsnprintf for trivial formatting, 2015-09-24), but since
it only affected things far in the future, it wasn't deemed
worth dealing with. But note that my calculations claiming
thousands of years were off there; because our xsnprintf
produces a NUL byte, we only have until the year 2242 to fix
this.
Given that this is just around the corner (geologically
speaking, anyway), and because it's easy to fix, let's just
make it work. Unlike the previous fix for "size", where we
had to write an individual extended header for each file, we
can write one global header (since we have only one mtime
for the whole archive).
There's a slight bit of trickiness there. We may already be
writing a global header with a "comment" field for the
commit sha1. So we need to write our new field into the same
header. To do this, we push the decision of whether to write
such a header down into write_global_extended_header(),
which will now assemble the header as it sees fit, and will
return early if we have nothing to write (in practice, we'll
only have a large mtime if it comes from a commit, but this
makes it also work if you set your system clock ahead such
that time() returns a huge value).
Note that we don't (and never did) handle negative
timestamps (i.e., before 1970). This would probably not be
too hard to support in the same way, but since git does not
support negative timestamps at all, I didn't bother here.
After writing the extended header, we munge the timestamp in
the ustar headers to the maximum-allowable size. This is
wrong, but it's the least-wrong thing we can provide to a
tar implementation that doesn't understand pax headers (it's
also what GNU tar does).
Helped-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-30 17:09:20 +08:00
|
|
|
if (args->time > USTAR_MAX_MTIME) {
|
|
|
|
strbuf_append_ext_header_uint(&ext_header, "mtime",
|
|
|
|
args->time);
|
|
|
|
args->time = USTAR_MAX_MTIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ext_header.len)
|
2016-06-30 17:09:26 +08:00
|
|
|
return;
|
archive-tar: write extended headers for far-future mtime
The ustar format represents timestamps as seconds since the
epoch, but only has room to store 11 octal digits. To
express anything larger, we need to use an extended header.
This is exactly the same case we fixed for the size field in
the previous commit, and the solution here follows the same
pattern.
This is even mentioned as an issue in f2f0267 (archive-tar:
use xsnprintf for trivial formatting, 2015-09-24), but since
it only affected things far in the future, it wasn't deemed
worth dealing with. But note that my calculations claiming
thousands of years were off there; because our xsnprintf
produces a NUL byte, we only have until the year 2242 to fix
this.
Given that this is just around the corner (geologically
speaking, anyway), and because it's easy to fix, let's just
make it work. Unlike the previous fix for "size", where we
had to write an individual extended header for each file, we
can write one global header (since we have only one mtime
for the whole archive).
There's a slight bit of trickiness there. We may already be
writing a global header with a "comment" field for the
commit sha1. So we need to write our new field into the same
header. To do this, we push the decision of whether to write
such a header down into write_global_extended_header(),
which will now assemble the header as it sees fit, and will
return early if we have nothing to write (in practice, we'll
only have a large mtime if it comes from a commit, but this
makes it also work if you set your system clock ahead such
that time() returns a huge value).
Note that we don't (and never did) handle negative
timestamps (i.e., before 1970). This would probably not be
too hard to support in the same way, but since git does not
support negative timestamps at all, I didn't bother here.
After writing the extended header, we munge the timestamp in
the ustar headers to the maximum-allowable size. This is
wrong, but it's the least-wrong thing we can provide to a
tar implementation that doesn't understand pax headers (it's
also what GNU tar does).
Helped-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-30 17:09:20 +08:00
|
|
|
|
2012-05-03 09:51:01 +08:00
|
|
|
memset(&header, 0, sizeof(header));
|
|
|
|
*header.typeflag = TYPEFLAG_GLOBAL_HEADER;
|
2014-10-21 03:04:46 +08:00
|
|
|
mode = 0100666;
|
2015-09-25 05:06:08 +08:00
|
|
|
xsnprintf(header.name, sizeof(header.name), "pax_global_header");
|
2012-05-03 09:51:01 +08:00
|
|
|
prepare_header(args, &header, mode, ext_header.len);
|
|
|
|
write_blocked(&header, sizeof(header));
|
|
|
|
write_blocked(ext_header.buf, ext_header.len);
|
2007-09-06 19:20:06 +08:00
|
|
|
strbuf_release(&ext_header);
|
2006-09-24 23:31:10 +08:00
|
|
|
}
|
|
|
|
|
2011-06-22 09:26:31 +08:00
|
|
|
static struct archiver **tar_filters;
|
|
|
|
static int nr_tar_filters;
|
|
|
|
static int alloc_tar_filters;
|
|
|
|
|
2020-04-11 03:44:28 +08:00
|
|
|
static struct archiver *find_tar_filter(const char *name, size_t len)
|
2011-06-22 09:26:31 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < nr_tar_filters; i++) {
|
|
|
|
struct archiver *ar = tar_filters[i];
|
|
|
|
if (!strncmp(ar->name, name, len) && !ar->name[len])
|
|
|
|
return ar;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-08-19 18:08:44 +08:00
|
|
|
static int tar_filter_config(const char *var, const char *value,
|
2022-08-26 01:09:48 +08:00
|
|
|
void *data UNUSED)
|
2011-06-22 09:26:31 +08:00
|
|
|
{
|
|
|
|
struct archiver *ar;
|
|
|
|
const char *name;
|
|
|
|
const char *type;
|
2020-04-11 03:44:28 +08:00
|
|
|
size_t namelen;
|
2011-06-22 09:26:31 +08:00
|
|
|
|
2013-01-23 14:23:27 +08:00
|
|
|
if (parse_config_key(var, "tar", &name, &namelen, &type) < 0 || !name)
|
2011-06-22 09:26:31 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
ar = find_tar_filter(name, namelen);
|
|
|
|
if (!ar) {
|
2021-03-14 00:17:22 +08:00
|
|
|
CALLOC_ARRAY(ar, 1);
|
2011-06-22 09:26:31 +08:00
|
|
|
ar->name = xmemdupz(name, namelen);
|
|
|
|
ar->write_archive = write_tar_filter_archive;
|
2020-11-10 00:05:31 +08:00
|
|
|
ar->flags = ARCHIVER_WANT_COMPRESSION_LEVELS |
|
|
|
|
ARCHIVER_HIGH_COMPRESSION_LEVELS;
|
2011-06-22 09:26:31 +08:00
|
|
|
ALLOC_GROW(tar_filters, nr_tar_filters + 1, alloc_tar_filters);
|
|
|
|
tar_filters[nr_tar_filters++] = ar;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(type, "command")) {
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
2022-06-16 00:59:57 +08:00
|
|
|
free(ar->filter_command);
|
|
|
|
ar->filter_command = xstrdup(value);
|
2011-06-22 09:26:31 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2011-06-22 11:17:35 +08:00
|
|
|
if (!strcmp(type, "remote")) {
|
|
|
|
if (git_config_bool(var, value))
|
|
|
|
ar->flags |= ARCHIVER_REMOTE;
|
|
|
|
else
|
|
|
|
ar->flags &= ~ARCHIVER_REMOTE;
|
|
|
|
return 0;
|
|
|
|
}
|
2011-06-22 09:26:31 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-05-15 01:46:53 +08:00
|
|
|
static int git_tar_config(const char *var, const char *value, void *cb)
|
2006-09-24 23:31:10 +08:00
|
|
|
{
|
|
|
|
if (!strcmp(var, "tar.umask")) {
|
2008-02-09 12:38:22 +08:00
|
|
|
if (value && !strcmp(value, "user")) {
|
2006-09-24 23:31:10 +08:00
|
|
|
tar_umask = umask(0);
|
|
|
|
umask(tar_umask);
|
|
|
|
} else {
|
|
|
|
tar_umask = git_config_int(var, value);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2011-06-22 09:26:31 +08:00
|
|
|
|
|
|
|
return tar_filter_config(var, value, cb);
|
2006-09-24 23:31:10 +08:00
|
|
|
}
|
|
|
|
|
2022-08-26 01:09:48 +08:00
|
|
|
static int write_tar_archive(const struct archiver *ar UNUSED,
|
2011-06-22 09:24:07 +08:00
|
|
|
struct archiver_args *args)
|
2006-09-24 23:31:10 +08:00
|
|
|
{
|
2008-07-15 03:22:24 +08:00
|
|
|
int err = 0;
|
2006-09-24 23:31:10 +08:00
|
|
|
|
2016-06-30 17:09:26 +08:00
|
|
|
write_global_extended_header(args);
|
|
|
|
err = write_archive_entries(args, write_tar_entry);
|
2008-07-15 03:22:24 +08:00
|
|
|
if (!err)
|
|
|
|
write_trailer();
|
|
|
|
return err;
|
2006-09-24 23:31:10 +08:00
|
|
|
}
|
2011-06-22 09:23:33 +08:00
|
|
|
|
archive-tar: add internal gzip implementation
Git uses zlib for its own object store, but calls gzip when creating tgz
archives. Add an option to perform the gzip compression for the latter
using zlib, without depending on the external gzip binary.
Plug it in by making write_block a function pointer and switching to a
compressing variant if the filter command has the magic value "git
archive gzip". Does that indirection slow down tar creation? Not
really, at least not in this test:
$ hyperfine -w3 -L rev HEAD,origin/main -p 'git checkout {rev} && make' \
'./git -C ../linux archive --format=tar HEAD # {rev}'
Benchmark #1: ./git -C ../linux archive --format=tar HEAD # HEAD
Time (mean ± σ): 4.044 s ± 0.007 s [User: 3.901 s, System: 0.137 s]
Range (min … max): 4.038 s … 4.059 s 10 runs
Benchmark #2: ./git -C ../linux archive --format=tar HEAD # origin/main
Time (mean ± σ): 4.047 s ± 0.009 s [User: 3.903 s, System: 0.138 s]
Range (min … max): 4.038 s … 4.066 s 10 runs
How does tgz creation perform?
$ hyperfine -w3 -L command 'gzip -cn','git archive gzip' \
'./git -c tar.tgz.command="{command}" -C ../linux archive --format=tgz HEAD'
Benchmark #1: ./git -c tar.tgz.command="gzip -cn" -C ../linux archive --format=tgz HEAD
Time (mean ± σ): 20.404 s ± 0.006 s [User: 23.943 s, System: 0.401 s]
Range (min … max): 20.395 s … 20.414 s 10 runs
Benchmark #2: ./git -c tar.tgz.command="git archive gzip" -C ../linux archive --format=tgz HEAD
Time (mean ± σ): 23.807 s ± 0.023 s [User: 23.655 s, System: 0.145 s]
Range (min … max): 23.782 s … 23.857 s 10 runs
Summary
'./git -c tar.tgz.command="gzip -cn" -C ../linux archive --format=tgz HEAD' ran
1.17 ± 0.00 times faster than './git -c tar.tgz.command="git archive gzip" -C ../linux archive --format=tgz HEAD'
So the internal implementation takes 17% longer on the Linux repo, but
uses 2% less CPU time. That's because the external gzip can run in
parallel on its own processor, while the internal one works sequentially
and avoids the inter-process communication overhead.
What are the benefits? Only an internal sequential implementation can
offer this eco mode, and it allows avoiding the gzip(1) requirement.
This implementation uses the helper functions from our zlib.c instead of
the convenient gz* functions from zlib, because the latter doesn't give
the control over the generated gzip header that the next patch requires.
Original-patch-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-06-16 01:02:33 +08:00
|
|
|
static git_zstream gzstream;
|
|
|
|
static unsigned char outbuf[16384];
|
|
|
|
|
|
|
|
static void tgz_deflate(int flush)
|
|
|
|
{
|
|
|
|
while (gzstream.avail_in || flush == Z_FINISH) {
|
|
|
|
int status = git_deflate(&gzstream, flush);
|
|
|
|
if (!gzstream.avail_out || status == Z_STREAM_END) {
|
|
|
|
write_or_die(1, outbuf, gzstream.next_out - outbuf);
|
|
|
|
gzstream.next_out = outbuf;
|
|
|
|
gzstream.avail_out = sizeof(outbuf);
|
|
|
|
if (status == Z_STREAM_END)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (status != Z_OK && status != Z_BUF_ERROR)
|
|
|
|
die(_("deflate error (%d)"), status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tgz_write_block(const void *data)
|
|
|
|
{
|
|
|
|
gzstream.next_in = (void *)data;
|
|
|
|
gzstream.avail_in = BLOCKSIZE;
|
|
|
|
tgz_deflate(Z_NO_FLUSH);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char internal_gzip_command[] = "git archive gzip";
|
|
|
|
|
2011-06-22 09:26:31 +08:00
|
|
|
static int write_tar_filter_archive(const struct archiver *ar,
|
|
|
|
struct archiver_args *args)
|
|
|
|
{
|
2022-06-16 01:04:09 +08:00
|
|
|
#if ZLIB_VERNUM >= 0x1221
|
|
|
|
struct gz_header_s gzhead = { .os = 3 }; /* Unix, for reproducibility */
|
|
|
|
#endif
|
2011-06-22 09:26:31 +08:00
|
|
|
struct strbuf cmd = STRBUF_INIT;
|
2014-08-20 03:09:35 +08:00
|
|
|
struct child_process filter = CHILD_PROCESS_INIT;
|
2011-06-22 09:26:31 +08:00
|
|
|
int r;
|
|
|
|
|
2022-06-16 00:59:57 +08:00
|
|
|
if (!ar->filter_command)
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("tar-filter archiver called with no filter defined");
|
2011-06-22 09:26:31 +08:00
|
|
|
|
archive-tar: add internal gzip implementation
Git uses zlib for its own object store, but calls gzip when creating tgz
archives. Add an option to perform the gzip compression for the latter
using zlib, without depending on the external gzip binary.
Plug it in by making write_block a function pointer and switching to a
compressing variant if the filter command has the magic value "git
archive gzip". Does that indirection slow down tar creation? Not
really, at least not in this test:
$ hyperfine -w3 -L rev HEAD,origin/main -p 'git checkout {rev} && make' \
'./git -C ../linux archive --format=tar HEAD # {rev}'
Benchmark #1: ./git -C ../linux archive --format=tar HEAD # HEAD
Time (mean ± σ): 4.044 s ± 0.007 s [User: 3.901 s, System: 0.137 s]
Range (min … max): 4.038 s … 4.059 s 10 runs
Benchmark #2: ./git -C ../linux archive --format=tar HEAD # origin/main
Time (mean ± σ): 4.047 s ± 0.009 s [User: 3.903 s, System: 0.138 s]
Range (min … max): 4.038 s … 4.066 s 10 runs
How does tgz creation perform?
$ hyperfine -w3 -L command 'gzip -cn','git archive gzip' \
'./git -c tar.tgz.command="{command}" -C ../linux archive --format=tgz HEAD'
Benchmark #1: ./git -c tar.tgz.command="gzip -cn" -C ../linux archive --format=tgz HEAD
Time (mean ± σ): 20.404 s ± 0.006 s [User: 23.943 s, System: 0.401 s]
Range (min … max): 20.395 s … 20.414 s 10 runs
Benchmark #2: ./git -c tar.tgz.command="git archive gzip" -C ../linux archive --format=tgz HEAD
Time (mean ± σ): 23.807 s ± 0.023 s [User: 23.655 s, System: 0.145 s]
Range (min … max): 23.782 s … 23.857 s 10 runs
Summary
'./git -c tar.tgz.command="gzip -cn" -C ../linux archive --format=tgz HEAD' ran
1.17 ± 0.00 times faster than './git -c tar.tgz.command="git archive gzip" -C ../linux archive --format=tgz HEAD'
So the internal implementation takes 17% longer on the Linux repo, but
uses 2% less CPU time. That's because the external gzip can run in
parallel on its own processor, while the internal one works sequentially
and avoids the inter-process communication overhead.
What are the benefits? Only an internal sequential implementation can
offer this eco mode, and it allows avoiding the gzip(1) requirement.
This implementation uses the helper functions from our zlib.c instead of
the convenient gz* functions from zlib, because the latter doesn't give
the control over the generated gzip header that the next patch requires.
Original-patch-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-06-16 01:02:33 +08:00
|
|
|
if (!strcmp(ar->filter_command, internal_gzip_command)) {
|
|
|
|
write_block = tgz_write_block;
|
|
|
|
git_deflate_init_gzip(&gzstream, args->compression_level);
|
2022-06-16 01:04:09 +08:00
|
|
|
#if ZLIB_VERNUM >= 0x1221
|
|
|
|
if (deflateSetHeader(&gzstream.z, &gzhead) != Z_OK)
|
|
|
|
BUG("deflateSetHeader() called too late");
|
|
|
|
#endif
|
archive-tar: add internal gzip implementation
Git uses zlib for its own object store, but calls gzip when creating tgz
archives. Add an option to perform the gzip compression for the latter
using zlib, without depending on the external gzip binary.
Plug it in by making write_block a function pointer and switching to a
compressing variant if the filter command has the magic value "git
archive gzip". Does that indirection slow down tar creation? Not
really, at least not in this test:
$ hyperfine -w3 -L rev HEAD,origin/main -p 'git checkout {rev} && make' \
'./git -C ../linux archive --format=tar HEAD # {rev}'
Benchmark #1: ./git -C ../linux archive --format=tar HEAD # HEAD
Time (mean ± σ): 4.044 s ± 0.007 s [User: 3.901 s, System: 0.137 s]
Range (min … max): 4.038 s … 4.059 s 10 runs
Benchmark #2: ./git -C ../linux archive --format=tar HEAD # origin/main
Time (mean ± σ): 4.047 s ± 0.009 s [User: 3.903 s, System: 0.138 s]
Range (min … max): 4.038 s … 4.066 s 10 runs
How does tgz creation perform?
$ hyperfine -w3 -L command 'gzip -cn','git archive gzip' \
'./git -c tar.tgz.command="{command}" -C ../linux archive --format=tgz HEAD'
Benchmark #1: ./git -c tar.tgz.command="gzip -cn" -C ../linux archive --format=tgz HEAD
Time (mean ± σ): 20.404 s ± 0.006 s [User: 23.943 s, System: 0.401 s]
Range (min … max): 20.395 s … 20.414 s 10 runs
Benchmark #2: ./git -c tar.tgz.command="git archive gzip" -C ../linux archive --format=tgz HEAD
Time (mean ± σ): 23.807 s ± 0.023 s [User: 23.655 s, System: 0.145 s]
Range (min … max): 23.782 s … 23.857 s 10 runs
Summary
'./git -c tar.tgz.command="gzip -cn" -C ../linux archive --format=tgz HEAD' ran
1.17 ± 0.00 times faster than './git -c tar.tgz.command="git archive gzip" -C ../linux archive --format=tgz HEAD'
So the internal implementation takes 17% longer on the Linux repo, but
uses 2% less CPU time. That's because the external gzip can run in
parallel on its own processor, while the internal one works sequentially
and avoids the inter-process communication overhead.
What are the benefits? Only an internal sequential implementation can
offer this eco mode, and it allows avoiding the gzip(1) requirement.
This implementation uses the helper functions from our zlib.c instead of
the convenient gz* functions from zlib, because the latter doesn't give
the control over the generated gzip header that the next patch requires.
Original-patch-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-06-16 01:02:33 +08:00
|
|
|
gzstream.next_out = outbuf;
|
|
|
|
gzstream.avail_out = sizeof(outbuf);
|
|
|
|
|
|
|
|
r = write_tar_archive(ar, args);
|
|
|
|
|
|
|
|
tgz_deflate(Z_FINISH);
|
|
|
|
git_deflate_end(&gzstream);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2022-06-16 00:59:57 +08:00
|
|
|
strbuf_addstr(&cmd, ar->filter_command);
|
2011-06-22 09:26:31 +08:00
|
|
|
if (args->compression_level >= 0)
|
|
|
|
strbuf_addf(&cmd, " -%d", args->compression_level);
|
|
|
|
|
2021-11-26 06:52:21 +08:00
|
|
|
strvec_push(&filter.args, cmd.buf);
|
2011-06-22 09:26:31 +08:00
|
|
|
filter.use_shell = 1;
|
|
|
|
filter.in = -1;
|
2022-10-29 18:06:06 +08:00
|
|
|
filter.silent_exec_failure = 1;
|
2011-06-22 09:26:31 +08:00
|
|
|
|
|
|
|
if (start_command(&filter) < 0)
|
2021-11-26 06:52:21 +08:00
|
|
|
die_errno(_("unable to start '%s' filter"), cmd.buf);
|
2011-06-22 09:26:31 +08:00
|
|
|
close(1);
|
|
|
|
if (dup2(filter.in, 1) < 0)
|
2018-07-21 15:49:20 +08:00
|
|
|
die_errno(_("unable to redirect descriptor"));
|
2011-06-22 09:26:31 +08:00
|
|
|
close(filter.in);
|
|
|
|
|
|
|
|
r = write_tar_archive(ar, args);
|
|
|
|
|
|
|
|
close(1);
|
|
|
|
if (finish_command(&filter) != 0)
|
2021-11-26 06:52:21 +08:00
|
|
|
die(_("'%s' filter reported error"), cmd.buf);
|
2011-06-22 09:26:31 +08:00
|
|
|
|
|
|
|
strbuf_release(&cmd);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-06-22 09:23:33 +08:00
|
|
|
static struct archiver tar_archiver = {
|
2022-02-24 17:33:02 +08:00
|
|
|
.name = "tar",
|
|
|
|
.write_archive = write_tar_archive,
|
|
|
|
.flags = ARCHIVER_REMOTE,
|
2011-06-22 09:23:33 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
void init_tar_archiver(void)
|
|
|
|
{
|
2011-06-22 09:26:31 +08:00
|
|
|
int i;
|
2011-06-22 09:23:33 +08:00
|
|
|
register_archiver(&tar_archiver);
|
2011-06-22 09:26:31 +08:00
|
|
|
|
2022-06-16 01:05:03 +08:00
|
|
|
tar_filter_config("tar.tgz.command", internal_gzip_command, NULL);
|
2011-06-22 11:17:35 +08:00
|
|
|
tar_filter_config("tar.tgz.remote", "true", NULL);
|
2022-06-16 01:05:03 +08:00
|
|
|
tar_filter_config("tar.tar.gz.command", internal_gzip_command, NULL);
|
2011-06-22 11:17:35 +08:00
|
|
|
tar_filter_config("tar.tar.gz.remote", "true", NULL);
|
2011-06-22 09:23:33 +08:00
|
|
|
git_config(git_tar_config, NULL);
|
2011-06-22 09:26:31 +08:00
|
|
|
for (i = 0; i < nr_tar_filters; i++) {
|
|
|
|
/* omit any filters that never had a command configured */
|
2022-06-16 00:59:57 +08:00
|
|
|
if (tar_filters[i]->filter_command)
|
2011-06-22 09:26:31 +08:00
|
|
|
register_archiver(tar_filters[i]);
|
|
|
|
}
|
2011-06-22 09:23:33 +08:00
|
|
|
}
|