/* * User defined low-level merge driver support. */ static int ll_ext_merge(const struct ll_merge_driver *fn, mmbuffer_t *result, const char *path, mmfile_t *orig, const char *orig_name, mmfile_t *src1, const char *name1, mmfile_t *src2, const char *name2, const struct ll_merge_options *opts, int marker_size) { char temp[4][50]; struct strbuf cmd = STRBUF_INIT; struct strbuf_expand_dict_entry dict[6]; struct strbuf path_sq = STRBUF_INIT; const char *args[] = { NULL, NULL }; int status, fd, i; struct stat st; assert(opts); sq_quote_buf(&path_sq, path); dict[0].placeholder = "O"; dict[0].value = temp[0]; dict[1].placeholder = "A"; dict[1].value = temp[1]; dict[2].placeholder = "B"; dict[2].value = temp[2]; dict[3].placeholder = "L"; dict[3].value = temp[3]; dict[4].placeholder = "P"; dict[4].value = path_sq.buf; dict[5].placeholder = NULL; dict[5].value = NULL; if (fn->cmdline == NULL) die("custom merge driver %s lacks command line.", fn->name); result->ptr = NULL; result->size = 0; create_temp(orig, temp[0], sizeof(temp[0])); create_temp(src1, temp[1], sizeof(temp[1])); create_temp(src2, temp[2], sizeof(temp[2])); xsnprintf(temp[3], sizeof(temp[3]), "%d", marker_size); strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict); args[0] = cmd.buf; status = run_command_v_opt(args, RUN_USING_SHELL); fd = open(temp[1], O_RDONLY); if (fd < 0) goto bad; if (fstat(fd, &st)) goto close_bad; result->size = st.st_size; result->ptr = xmallocz(result->size); if (read_in_full(fd, result->ptr, result->size) != result->size) { FREE_AND_NULL(result->ptr); result->size = 0; } close_bad: close(fd); bad: for (i = 0; i < 3; i++) unlink_or_warn(temp[i]); strbuf_release(&cmd); strbuf_release(&path_sq); return status; }
void sq_quote_argv(struct strbuf *dst, const char **argv) { int i; /* Copy into destination buffer. */ strbuf_grow(dst, 255); for (i = 0; argv[i]; ++i) { strbuf_addch(dst, ' '); sq_quote_buf(dst, argv[i]); } }
void sq_quotef(struct strbuf *dst, const char *fmt, ...) { struct strbuf src = STRBUF_INIT; va_list ap; va_start(ap, fmt); strbuf_vaddf(&src, fmt, ap); va_end(ap); sq_quote_buf(dst, src.buf); strbuf_release(&src); }
void git_config_push_parameter(const char *text) { struct strbuf env = STRBUF_INIT; const char *old = getenv(CONFIG_DATA_ENVIRONMENT); if (old) { strbuf_addstr(&env, old); strbuf_addch(&env, ' '); } sq_quote_buf(&env, text); setenv(CONFIG_DATA_ENVIRONMENT, env.buf, 1); strbuf_release(&env); }
void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen) { int i; /* Copy into destination buffer. */ strbuf_grow(dst, 255); for (i = 0; argv[i]; ++i) { strbuf_addch(dst, ' '); sq_quote_buf(dst, argv[i]); if (maxlen && dst->len > maxlen) die("Too many or long arguments"); } }
static int parseopt_dump(const struct option *o, const char *arg, int unset) { struct strbuf *parsed = o->value; if (unset) strbuf_addf(parsed, " --no-%s", o->long_name); else if (o->short_name) strbuf_addf(parsed, " -%c", o->short_name); else strbuf_addf(parsed, " --%s", o->long_name); if (arg) { strbuf_addch(parsed, ' '); sq_quote_buf(parsed, arg); } return 0; }
void sq_quote_buf_pretty(struct strbuf *dst, const char *src) { static const char ok_punct[] = "+,-./:=@_^"; const char *p; for (p = src; *p; p++) { if (!isalpha(*p) && !isdigit(*p) && !strchr(ok_punct, *p)) { sq_quote_buf(dst, src); return; } } /* if we get here, we did not need quoting */ strbuf_addstr(dst, src); }
int sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen) { int i, ret; /* Copy into destination buffer. */ ret = strbuf_grow(dst, 255); for (i = 0; !ret && argv[i]; ++i) { ret = strbuf_addch(dst, ' '); if (ret) break; ret = sq_quote_buf(dst, argv[i]); if (maxlen && dst->len > maxlen) return -ENOSPC; } return ret; }
static int sanitize_submodule_config(const char *var, const char *value, void *data) { struct strbuf *out = data; if (submodule_config_ok(var)) { if (out->len) strbuf_addch(out, ' '); if (value) sq_quotef(out, "%s=%s", var, value); else sq_quote_buf(out, var); } return 0; }
/* * Append a string to a string buffer, with or without shell quoting. * Return true if the buffer overflowed. */ static int add_to_string(char **ptrp, int *sizep, const char *str, int quote) { char *p = *ptrp; int size = *sizep; int oc; int err = 0; if ( quote ) { oc = sq_quote_buf(p, size, str); } else { oc = strlen(str); memcpy(p, str, (oc >= size) ? size-1 : oc); } if ( oc >= size ) { err = 1; oc = size-1; } *ptrp += oc; **ptrp = '\0'; *sizep -= oc; return err; }
/* * This returns a dummy child_process if the transport protocol does not * need fork(2), or a struct child_process object if it does. Once done, * finish the connection with finish_connect() with the value returned from * this function (it is safe to call finish_connect() with NULL to support * the former case). * * If it returns, the connect is successful; it just dies on errors (this * will hopefully be changed in a libification effort, to return NULL when * the connection failed). */ struct child_process *git_connect(int fd[2], const char *url_orig, const char *prog, int flags) { char *url = xstrdup(url_orig); char *host, *path; char *end; int c; struct child_process *conn; enum protocol protocol = PROTO_LOCAL; int free_path = 0; char *port = NULL; const char **arg; struct strbuf cmd; /* Without this we cannot rely on waitpid() to tell * what happened to our children. */ signal(SIGCHLD, SIG_DFL); host = strstr(url, "://"); if (host) { *host = '\0'; protocol = get_protocol(url); host += 3; c = '/'; } else { host = url; c = ':'; } /* * Don't do destructive transforms with git:// as that * protocol code does '[]' unwrapping of its own. */ if (host[0] == '[') { end = strchr(host + 1, ']'); if (end) { if (protocol != PROTO_GIT) { *end = 0; host++; } end++; } else end = host; } else end = host; path = strchr(end, c); if (path && !has_dos_drive_prefix(end)) { if (c == ':') { protocol = PROTO_SSH; *path++ = '\0'; } } else path = end; if (!path || !*path) die("No path specified. See 'man git-pull' for valid url syntax"); /* * null-terminate hostname and point path to ~ for URL's like this: * ssh://host.xz/~user/repo */ if (protocol != PROTO_LOCAL && host != url) { char *ptr = path; if (path[1] == '~') path++; else { path = xstrdup(ptr); free_path = 1; } *ptr = '\0'; } /* * Add support for ssh port: ssh://host.xy:<port>/... */ if (protocol == PROTO_SSH && host != url) port = get_port(host); if (protocol == PROTO_GIT) { /* These underlying connection commands die() if they * cannot connect. */ char *target_host = xstrdup(host); if (git_use_proxy(host)) git_proxy_connect(fd, host); else git_tcp_connect(fd, host, flags); /* * Separate original protocol components prog and path * from extended host header with a NUL byte. * * Note: Do not add any other headers here! Doing so * will cause older git-daemon servers to crash. */ packet_write(fd[1], "%s %s%chost=%s%c", prog, path, 0, target_host, 0); free(target_host); free(url); if (free_path) free(path); return &no_fork; } conn = xcalloc(1, sizeof(*conn)); strbuf_init(&cmd, MAX_CMD_LEN); strbuf_addstr(&cmd, prog); strbuf_addch(&cmd, ' '); sq_quote_buf(&cmd, path); if (cmd.len >= MAX_CMD_LEN) die("command line too long"); conn->in = conn->out = -1; conn->argv = arg = xcalloc(7, sizeof(*arg)); if (protocol == PROTO_SSH) { const char *ssh = getenv("GIT_SSH"); int putty = ssh && strcasestr(ssh, "plink"); if (!ssh) ssh = "ssh"; *arg++ = ssh; if (putty && !strcasestr(ssh, "tortoiseplink")) *arg++ = "-batch"; if (port) { /* P is for PuTTY, p is for OpenSSH */ *arg++ = putty ? "-P" : "-p"; *arg++ = port; } *arg++ = host; } else { /* remove these from the environment */ const char *env[] = { ALTERNATE_DB_ENVIRONMENT, DB_ENVIRONMENT, GIT_DIR_ENVIRONMENT, GIT_WORK_TREE_ENVIRONMENT, GRAFT_ENVIRONMENT, INDEX_ENVIRONMENT, NO_REPLACE_OBJECTS_ENVIRONMENT, NULL }; conn->env = env; conn->use_shell = 1; } *arg++ = cmd.buf; *arg = NULL; if (start_command(conn)) die("unable to fork"); fd[0] = conn->out; /* read from child's stdout */ fd[1] = conn->in; /* write to child's stdin */ strbuf_release(&cmd); free(url); if (free_path) free(path); return conn; }
/* * This returns a dummy child_process if the transport protocol does not * need fork(2), or a struct child_process object if it does. Once done, * finish the connection with finish_connect() with the value returned from * this function (it is safe to call finish_connect() with NULL to support * the former case). * * If it returns, the connect is successful; it just dies on errors (this * will hopefully be changed in a libification effort, to return NULL when * the connection failed). */ struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags) { char *hostandport, *path; struct child_process *conn = &no_fork; enum protocol protocol; struct strbuf cmd = STRBUF_INIT; /* Without this we cannot rely on waitpid() to tell * what happened to our children. */ signal(SIGCHLD, SIG_DFL); protocol = parse_connect_url(url, &hostandport, &path); if ((flags & CONNECT_DIAG_URL) && (protocol != PROTO_SSH)) { printf("Diag: url=%s\n", url ? url : "NULL"); printf("Diag: protocol=%s\n", prot_name(protocol)); printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL"); printf("Diag: path=%s\n", path ? path : "NULL"); conn = NULL; } else if (protocol == PROTO_GIT) { /* * Set up virtual host information based on where we will * connect, unless the user has overridden us in * the environment. */ char *target_host = getenv("GIT_OVERRIDE_VIRTUAL_HOST"); if (target_host) target_host = xstrdup(target_host); else target_host = xstrdup(hostandport); transport_check_allowed("git"); /* These underlying connection commands die() if they * cannot connect. */ if (git_use_proxy(hostandport)) conn = git_proxy_connect(fd, hostandport); else git_tcp_connect(fd, hostandport, flags); /* * Separate original protocol components prog and path * from extended host header with a NUL byte. * * Note: Do not add any other headers here! Doing so * will cause older git-daemon servers to crash. */ packet_write(fd[1], "%s %s%chost=%s%c", prog, path, 0, target_host, 0); free(target_host); } else { conn = xmalloc(sizeof(*conn)); child_process_init(conn); strbuf_addstr(&cmd, prog); strbuf_addch(&cmd, ' '); sq_quote_buf(&cmd, path); conn->in = conn->out = -1; if (protocol == PROTO_SSH) { const char *ssh; int putty, tortoiseplink = 0; char *ssh_host = hostandport; const char *port = NULL; transport_check_allowed("ssh"); get_host_and_port(&ssh_host, &port); if (!port) port = get_port(ssh_host); if (flags & CONNECT_DIAG_URL) { printf("Diag: url=%s\n", url ? url : "NULL"); printf("Diag: protocol=%s\n", prot_name(protocol)); printf("Diag: userandhost=%s\n", ssh_host ? ssh_host : "NULL"); printf("Diag: port=%s\n", port ? port : "NONE"); printf("Diag: path=%s\n", path ? path : "NULL"); free(hostandport); free(path); free(conn); return NULL; } ssh = getenv("GIT_SSH_COMMAND"); if (ssh) { conn->use_shell = 1; putty = 0; } else { const char *base; char *ssh_dup; ssh = getenv("GIT_SSH"); if (!ssh) ssh = "ssh"; ssh_dup = xstrdup(ssh); base = basename(ssh_dup); tortoiseplink = !strcasecmp(base, "tortoiseplink") || !strcasecmp(base, "tortoiseplink.exe"); putty = !strcasecmp(base, "plink") || !strcasecmp(base, "plink.exe") || tortoiseplink; free(ssh_dup); } argv_array_push(&conn->args, ssh); if (tortoiseplink) argv_array_push(&conn->args, "-batch"); if (port) { /* P is for PuTTY, p is for OpenSSH */ argv_array_push(&conn->args, putty ? "-P" : "-p"); argv_array_push(&conn->args, port); } argv_array_push(&conn->args, ssh_host); } else { /* remove repo-local variables from the environment */ conn->env = local_repo_env; conn->use_shell = 1; transport_check_allowed("file"); } argv_array_push(&conn->args, cmd.buf); if (start_command(conn)) die("unable to fork"); fd[0] = conn->out; /* read from child's stdout */ fd[1] = conn->in; /* write to child's stdin */ strbuf_release(&cmd); } free(hostandport); free(path); return conn; }
/* * This returns the dummy child_process `no_fork` if the transport protocol * does not need fork(2), or a struct child_process object if it does. Once * done, finish the connection with finish_connect() with the value returned * from this function (it is safe to call finish_connect() with NULL to * support the former case). * * If it returns, the connect is successful; it just dies on errors (this * will hopefully be changed in a libification effort, to return NULL when * the connection failed). */ struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags) { char *hostandport, *path; struct child_process *conn; enum protocol protocol; /* Without this we cannot rely on waitpid() to tell * what happened to our children. */ signal(SIGCHLD, SIG_DFL); protocol = parse_connect_url(url, &hostandport, &path); if ((flags & CONNECT_DIAG_URL) && (protocol != PROTO_SSH)) { printf("Diag: url=%s\n", url ? url : "NULL"); printf("Diag: protocol=%s\n", prot_name(protocol)); printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL"); printf("Diag: path=%s\n", path ? path : "NULL"); conn = NULL; } else if (protocol == PROTO_GIT) { conn = git_connect_git(fd, hostandport, path, prog, flags); } else { struct strbuf cmd = STRBUF_INIT; const char *const *var; conn = xmalloc(sizeof(*conn)); child_process_init(conn); if (looks_like_command_line_option(path)) die("strange pathname '%s' blocked", path); strbuf_addstr(&cmd, prog); strbuf_addch(&cmd, ' '); sq_quote_buf(&cmd, path); /* remove repo-local variables from the environment */ for (var = local_repo_env; *var; var++) argv_array_push(&conn->env_array, *var); conn->use_shell = 1; conn->in = conn->out = -1; if (protocol == PROTO_SSH) { char *ssh_host = hostandport; const char *port = NULL; transport_check_allowed("ssh"); get_host_and_port(&ssh_host, &port); if (!port) port = get_port(ssh_host); if (flags & CONNECT_DIAG_URL) { printf("Diag: url=%s\n", url ? url : "NULL"); printf("Diag: protocol=%s\n", prot_name(protocol)); printf("Diag: userandhost=%s\n", ssh_host ? ssh_host : "NULL"); printf("Diag: port=%s\n", port ? port : "NONE"); printf("Diag: path=%s\n", path ? path : "NULL"); free(hostandport); free(path); free(conn); strbuf_release(&cmd); return NULL; } fill_ssh_args(conn, ssh_host, port, flags); } else { transport_check_allowed("file"); if (get_protocol_version_config() > 0) { argv_array_pushf(&conn->env_array, GIT_PROTOCOL_ENVIRONMENT "=version=%d", get_protocol_version_config()); } } argv_array_push(&conn->args, cmd.buf); if (start_command(conn)) die("unable to fork"); fd[0] = conn->out; /* read from child's stdout */ fd[1] = conn->in; /* write to child's stdin */ strbuf_release(&cmd); } free(hostandport); free(path); return conn; }
static void create_pack_file(void) { struct child_process pack_objects = CHILD_PROCESS_INIT; char data[8193], progress[128]; char abort_msg[] = "aborting due to possible repository " "corruption on the remote side."; int buffered = -1; ssize_t sz; int i; FILE *pipe_fd; if (!pack_objects_hook) pack_objects.git_cmd = 1; else { argv_array_push(&pack_objects.args, pack_objects_hook); argv_array_push(&pack_objects.args, "git"); pack_objects.use_shell = 1; } if (shallow_nr) { argv_array_push(&pack_objects.args, "--shallow-file"); argv_array_push(&pack_objects.args, ""); } argv_array_push(&pack_objects.args, "pack-objects"); argv_array_push(&pack_objects.args, "--revs"); if (use_thin_pack) argv_array_push(&pack_objects.args, "--thin"); argv_array_push(&pack_objects.args, "--stdout"); if (shallow_nr) argv_array_push(&pack_objects.args, "--shallow"); if (!no_progress) argv_array_push(&pack_objects.args, "--progress"); if (use_ofs_delta) argv_array_push(&pack_objects.args, "--delta-base-offset"); if (use_include_tag) argv_array_push(&pack_objects.args, "--include-tag"); if (filter_options.filter_spec) { if (pack_objects.use_shell) { struct strbuf buf = STRBUF_INIT; sq_quote_buf(&buf, filter_options.filter_spec); argv_array_pushf(&pack_objects.args, "--filter=%s", buf.buf); strbuf_release(&buf); } else { argv_array_pushf(&pack_objects.args, "--filter=%s", filter_options.filter_spec); } } pack_objects.in = -1; pack_objects.out = -1; pack_objects.err = -1; if (start_command(&pack_objects)) die("git upload-pack: unable to fork git-pack-objects"); pipe_fd = xfdopen(pack_objects.in, "w"); if (shallow_nr) for_each_commit_graft(write_one_shallow, pipe_fd); for (i = 0; i < want_obj.nr; i++) fprintf(pipe_fd, "%s\n", oid_to_hex(&want_obj.objects[i].item->oid)); fprintf(pipe_fd, "--not\n"); for (i = 0; i < have_obj.nr; i++) fprintf(pipe_fd, "%s\n", oid_to_hex(&have_obj.objects[i].item->oid)); for (i = 0; i < extra_edge_obj.nr; i++) fprintf(pipe_fd, "%s\n", oid_to_hex(&extra_edge_obj.objects[i].item->oid)); fprintf(pipe_fd, "\n"); fflush(pipe_fd); fclose(pipe_fd); /* We read from pack_objects.err to capture stderr output for * progress bar, and pack_objects.out to capture the pack data. */ while (1) { struct pollfd pfd[2]; int pe, pu, pollsize; int ret; reset_timeout(); pollsize = 0; pe = pu = -1; if (0 <= pack_objects.out) { pfd[pollsize].fd = pack_objects.out; pfd[pollsize].events = POLLIN; pu = pollsize; pollsize++; } if (0 <= pack_objects.err) { pfd[pollsize].fd = pack_objects.err; pfd[pollsize].events = POLLIN; pe = pollsize; pollsize++; } if (!pollsize) break; ret = poll(pfd, pollsize, keepalive < 0 ? -1 : 1000 * keepalive); if (ret < 0) { if (errno != EINTR) { error_errno("poll failed, resuming"); sleep(1); } continue; } if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) { /* Status ready; we ship that in the side-band * or dump to the standard error. */ sz = xread(pack_objects.err, progress, sizeof(progress)); if (0 < sz) send_client_data(2, progress, sz); else if (sz == 0) { close(pack_objects.err); pack_objects.err = -1; } else goto fail; /* give priority to status messages */ continue; } if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) { /* Data ready; we keep the last byte to ourselves * in case we detect broken rev-list, so that we * can leave the stream corrupted. This is * unfortunate -- unpack-objects would happily * accept a valid packdata with trailing garbage, * so appending garbage after we pass all the * pack data is not good enough to signal * breakage to downstream. */ char *cp = data; ssize_t outsz = 0; if (0 <= buffered) { *cp++ = buffered; outsz++; } sz = xread(pack_objects.out, cp, sizeof(data) - outsz); if (0 < sz) ; else if (sz == 0) { close(pack_objects.out); pack_objects.out = -1; } else goto fail; sz += outsz; if (1 < sz) { buffered = data[sz-1] & 0xFF; sz--; } else buffered = -1; send_client_data(1, data, sz); } /* * We hit the keepalive timeout without saying anything; send * an empty message on the data sideband just to let the other * side know we're still working on it, but don't have any data * yet. * * If we don't have a sideband channel, there's no room in the * protocol to say anything, so those clients are just out of * luck. */ if (!ret && use_sideband) { static const char buf[] = "0005\1"; write_or_die(1, buf, 5); } } if (finish_command(&pack_objects)) { error("git upload-pack: git-pack-objects died with error."); goto fail; } /* flush the data */ if (0 <= buffered) { data[0] = buffered; send_client_data(1, data, 1); fprintf(stderr, "flushed.\n"); } if (use_sideband) packet_flush(1); return; fail: send_client_data(3, abort_msg, sizeof(abort_msg)); die("git upload-pack: %s", abort_msg); }