git/builtin/upload-archive.c
Jeff King 7915691377 builtins: annotate always-empty prefix parameters
It's usually a bad idea for a builtin's cmd_foo() to ignore the "prefix"
argument it gets, as it needs to prepend that string when accessing any
paths given by the user.

But if a builtin does not ask for the git wrapper to run repository
setup (via the RUN_SETUP or RUN_SETUP_GENTLY flags), then we know the
prefix will always be NULL (it is adjusting for the chdir() done during
repo setup, but there cannot be one if we did not set up the repo). In
those cases it's OK to ignore "prefix", but it's worth annotating for a
few reasons:

  1. It serves as documentation to somebody reading the code about what
     we expect.

  2. If the flags in git.c ever change, the run-time assertion may help
     detect the problem (though only if the command is run from a
     subdirectory of the repository).

  3. It notes to the compiler that we are OK ignoring "prefix". In
     particular, this silences -Wunused-parameter. It _could_ also help
     the compiler generate better code (because it will know the prefix
     is NULL), but in practice this is quite unlikely to matter.

Note that I've only added this annotation to commands which triggered
-Wunused-parameter. It would be correct to add it to any builtin which
doesn't ask for RUN_SETUP, but most of the rest of them do the sensible
thing with "prefix" by passing it to parse_options(). So they're much
more likely to just work if they ever switched to RUN_SETUP, and aren't
worth annotating.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-03-28 14:11:24 -07:00

136 lines
3.2 KiB
C

/*
* Copyright (c) 2006 Franck Bui-Huu
*/
#include "cache.h"
#include "builtin.h"
#include "archive.h"
#include "pkt-line.h"
#include "sideband.h"
#include "run-command.h"
#include "strvec.h"
static const char upload_archive_usage[] =
"git upload-archive <repository>";
static const char deadchild[] =
"git upload-archive: archiver died with error";
#define MAX_ARGS (64)
int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
{
struct strvec sent_argv = STRVEC_INIT;
const char *arg_cmd = "argument ";
if (argc != 2 || !strcmp(argv[1], "-h"))
usage(upload_archive_usage);
if (!enter_repo(argv[1], 0))
die("'%s' does not appear to be a git repository", argv[1]);
init_archivers();
/* put received options in sent_argv[] */
strvec_push(&sent_argv, "git-upload-archive");
for (;;) {
char *buf = packet_read_line(0, NULL);
if (!buf)
break; /* got a flush */
if (sent_argv.nr > MAX_ARGS)
die("Too many options (>%d)", MAX_ARGS - 1);
if (!starts_with(buf, arg_cmd))
die("'argument' token or flush expected");
strvec_push(&sent_argv, buf + strlen(arg_cmd));
}
/* parse all options sent by the client */
return write_archive(sent_argv.nr, sent_argv.v, prefix,
the_repository, NULL, 1);
}
__attribute__((format (printf, 1, 2)))
static void error_clnt(const char *fmt, ...)
{
struct strbuf buf = STRBUF_INIT;
va_list params;
va_start(params, fmt);
strbuf_vaddf(&buf, fmt, params);
va_end(params);
send_sideband(1, 3, buf.buf, buf.len, LARGE_PACKET_MAX);
die("sent error to the client: %s", buf.buf);
}
static ssize_t process_input(int child_fd, int band)
{
char buf[16384];
ssize_t sz = read(child_fd, buf, sizeof(buf));
if (sz < 0) {
if (errno != EAGAIN && errno != EINTR)
error_clnt("read error: %s\n", strerror(errno));
return sz;
}
send_sideband(1, band, buf, sz, LARGE_PACKET_MAX);
return sz;
}
int cmd_upload_archive(int argc, const char **argv, const char *prefix)
{
struct child_process writer = CHILD_PROCESS_INIT;
BUG_ON_NON_EMPTY_PREFIX(prefix);
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(upload_archive_usage);
/*
* Set up sideband subprocess.
*
* We (parent) monitor and read from child, sending its fd#1 and fd#2
* multiplexed out to our fd#1. If the child dies, we tell the other
* end over channel #3.
*/
writer.out = writer.err = -1;
writer.git_cmd = 1;
strvec_push(&writer.args, "upload-archive--writer");
strvec_pushv(&writer.args, argv + 1);
if (start_command(&writer)) {
int err = errno;
packet_write_fmt(1, "NACK unable to spawn subprocess\n");
die("upload-archive: %s", strerror(err));
}
packet_write_fmt(1, "ACK\n");
packet_flush(1);
while (1) {
struct pollfd pfd[2];
pfd[0].fd = writer.out;
pfd[0].events = POLLIN;
pfd[1].fd = writer.err;
pfd[1].events = POLLIN;
if (poll(pfd, 2, -1) < 0) {
if (errno != EINTR) {
error_errno("poll failed resuming");
sleep(1);
}
continue;
}
if (pfd[1].revents & POLLIN)
/* Status stream ready */
if (process_input(pfd[1].fd, 2))
continue;
if (pfd[0].revents & POLLIN)
/* Data stream ready */
if (process_input(pfd[0].fd, 1))
continue;
if (finish_command(&writer))
error_clnt("%s", deadchild);
packet_flush(1);
break;
}
return 0;
}