Merge branch 'jc/ignore-epipe-in-filter'

Filter scripts were run with SIGPIPE disabled on the Git side,
expecting that they may not read what Git feeds them to filter.
We however treated a filter that does not read its input fully
before exiting as an error.

This changes semantics, but arguably in a good way.  If a filter
can produce its output without consuming its input using whatever
magic, we now let it do so, instead of diagnosing it as a
programming error.

* jc/ignore-epipe-in-filter:
  filter_buffer_or_fd(): ignore EPIPE
  copy.c: make copy_fd() report its status silently
This commit is contained in:
Junio C Hamano 2015-05-22 12:41:57 -07:00
Родитель 5bf66689d5 0c4dd67a04
Коммит ddaf4e2e9a
5 изменённых файлов: 32 добавлений и 8 удалений

Просмотреть файл

@ -1541,9 +1541,13 @@ extern const char *git_mailmap_blob;
extern void maybe_flush_or_die(FILE *, const char *); extern void maybe_flush_or_die(FILE *, const char *);
__attribute__((format (printf, 2, 3))) __attribute__((format (printf, 2, 3)))
extern void fprintf_or_die(FILE *, const char *fmt, ...); extern void fprintf_or_die(FILE *, const char *fmt, ...);
#define COPY_READ_ERROR (-2)
#define COPY_WRITE_ERROR (-3)
extern int copy_fd(int ifd, int ofd); extern int copy_fd(int ifd, int ofd);
extern int copy_file(const char *dst, const char *src, int mode); extern int copy_file(const char *dst, const char *src, int mode);
extern int copy_file_with_time(const char *dst, const char *src, int mode); extern int copy_file_with_time(const char *dst, const char *src, int mode);
extern void write_or_die(int fd, const void *buf, size_t count); extern void write_or_die(int fd, const void *buf, size_t count);
extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg); extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg); extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);

Просмотреть файл

@ -356,9 +356,14 @@ static int filter_buffer_or_fd(int in, int out, void *data)
sigchain_push(SIGPIPE, SIG_IGN); sigchain_push(SIGPIPE, SIG_IGN);
if (params->src) { if (params->src) {
write_err = (write_in_full(child_process.in, params->src, params->size) < 0); write_err = (write_in_full(child_process.in,
params->src, params->size) < 0);
if (errno == EPIPE)
write_err = 0;
} else { } else {
write_err = copy_fd(params->fd, child_process.in); write_err = copy_fd(params->fd, child_process.in);
if (write_err == COPY_WRITE_ERROR && errno == EPIPE)
write_err = 0;
} }
if (close(child_process.in)) if (close(child_process.in))

17
copy.c
Просмотреть файл

@ -7,13 +7,10 @@ int copy_fd(int ifd, int ofd)
ssize_t len = xread(ifd, buffer, sizeof(buffer)); ssize_t len = xread(ifd, buffer, sizeof(buffer));
if (!len) if (!len)
break; break;
if (len < 0) { if (len < 0)
return error("copy-fd: read returned %s", return COPY_READ_ERROR;
strerror(errno));
}
if (write_in_full(ofd, buffer, len) < 0) if (write_in_full(ofd, buffer, len) < 0)
return error("copy-fd: write returned %s", return COPY_WRITE_ERROR;
strerror(errno));
} }
return 0; return 0;
} }
@ -43,6 +40,14 @@ int copy_file(const char *dst, const char *src, int mode)
return fdo; return fdo;
} }
status = copy_fd(fdi, fdo); status = copy_fd(fdi, fdo);
switch (status) {
case COPY_READ_ERROR:
error("copy-fd: read returned %s", strerror(errno));
break;
case COPY_WRITE_ERROR:
error("copy-fd: write returned %s", strerror(errno));
break;
}
close(fdi); close(fdi);
if (close(fdo) != 0) if (close(fdo) != 0)
return error("%s: close error: %s", dst, strerror(errno)); return error("%s: close error: %s", dst, strerror(errno));

Просмотреть файл

@ -289,7 +289,7 @@ int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
int save_errno = errno; int save_errno = errno;
if (flags & LOCK_DIE_ON_ERROR) if (flags & LOCK_DIE_ON_ERROR)
exit(128); die("failed to prepare '%s' for appending", path);
close(orig_fd); close(orig_fd);
rollback_lock_file(lk); rollback_lock_file(lk);
errno = save_errno; errno = save_errno;

Просмотреть файл

@ -204,6 +204,16 @@ test_expect_success 'filtering large input to small output should use little mem
GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB
' '
test_expect_success 'filter that does not read is fine' '
test-genrandom foo $((128 * 1024 + 1)) >big &&
echo "big filter=epipe" >.gitattributes &&
git config filter.epipe.clean "echo xyzzy" &&
git add big &&
git cat-file blob :big >actual &&
echo xyzzy >expect &&
test_cmp expect actual
'
test_expect_success EXPENSIVE 'filter large file' ' test_expect_success EXPENSIVE 'filter large file' '
git config filter.largefile.smudge cat && git config filter.largefile.smudge cat &&
git config filter.largefile.clean cat && git config filter.largefile.clean cat &&