int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix) { struct argv_array sent_argv = ARGV_ARRAY_INIT; const char *arg_cmd = "argument "; if (argc != 2) usage(upload_archive_usage); if (!enter_repo(argv[1], 0)) die("'%s' does not appear to be a git repository", argv[1]); /* put received options in sent_argv[] */ argv_array_push(&sent_argv, "git-upload-archive"); for (;;) { char *buf = packet_read_line(0, NULL); if (!buf) break; /* got a flush */ if (sent_argv.argc > MAX_ARGS) die("Too many options (>%d)", MAX_ARGS - 1); if (prefixcmp(buf, arg_cmd)) die("'argument' token or flush expected"); argv_array_push(&sent_argv, buf + strlen(arg_cmd)); } /* parse all options sent by the client */ return write_archive(sent_argv.argc, sent_argv.argv, prefix, 0, NULL, 1); }
int cmd_receive_pack(int argc, const char **argv, const char *prefix) { int i; char *dir = NULL; argv++; for (i = 1; i < argc; i++) { const char *arg = *argv++; if (*arg == '-') { /* Do flag handling here */ usage(receive_pack_usage); } if (dir) usage(receive_pack_usage); dir = xstrdup(arg); } if (!dir) usage(receive_pack_usage); setup_path(); if (!enter_repo(dir, 0)) die("'%s': unable to chdir or not a git archive", dir); if (is_repository_shallow()) die("attempt to push into a shallow repository"); git_config(receive_pack_config, NULL); if (0 <= transfer_unpack_limit) unpack_limit = transfer_unpack_limit; else if (0 <= receive_unpack_limit) unpack_limit = receive_unpack_limit; add_alternate_refs(); write_head_info(); clear_extra_refs(); /* EOF */ packet_flush(1); read_head_info(); if (commands) { const char *unpack_status = NULL; if (!delete_only(commands)) unpack_status = unpack(); execute_commands(unpack_status); if (pack_lockfile) unlink(pack_lockfile); if (report_status) report(unpack_status); run_receive_hook(post_receive_hook); run_update_post_hook(commands); } return 0; }
int cmd_main(int argc, const char **argv) { char *method = getenv("REQUEST_METHOD"); char *dir; struct service_cmd *cmd = NULL; char *cmd_arg = NULL; int i; struct strbuf hdr = STRBUF_INIT; set_die_routine(die_webcgi); set_die_is_recursing_routine(die_webcgi_recursing); if (!method) die("No REQUEST_METHOD from server"); if (!strcmp(method, "HEAD")) method = "GET"; dir = getdir(); for (i = 0; i < ARRAY_SIZE(services); i++) { struct service_cmd *c = &services[i]; regex_t re; regmatch_t out[1]; if (regcomp(&re, c->pattern, REG_EXTENDED)) die("Bogus regex in service table: %s", c->pattern); if (!regexec(&re, dir, 1, out, 0)) { size_t n; if (strcmp(method, c->method)) return bad_request(&hdr, c); cmd = c; n = out[0].rm_eo - out[0].rm_so; cmd_arg = xmemdupz(dir + out[0].rm_so + 1, n - 1); dir[out[0].rm_so] = 0; break; } regfree(&re); } if (!cmd) not_found(&hdr, "Request not supported: '%s'", dir); setup_path(); if (!enter_repo(dir, 0)) not_found(&hdr, "Not a git repository: '%s'", dir); git_config(git_default_config, NULL); if (!getenv("GIT_HTTP_EXPORT_ALL") && access("git-daemon-export-ok", F_OK) ) not_found(&hdr, "Repository not exported: '%s'", dir); http_config(); max_request_buffer = git_env_ulong("GIT_HTTP_MAX_REQUEST_BUFFER", max_request_buffer); cmd->imp(&hdr, cmd_arg); return 0; }
int cmd_upload_archive(int argc, const char **argv, const char *prefix) { const char *sent_argv[MAX_ARGS]; struct child_process cld = { sent_argv }; cld.out = cld.err = -1; cld.git_cmd = 1; if (argc != 2) usage(upload_archive_usage); if (!enter_repo(argv[1], 0)) die("'%s' does not appear to be a git repository", argv[1]); prepare_argv(sent_argv, argv); if (start_command(&cld)) { int err = errno; packet_write(1, "NACK fork failed on the remote side\n"); die("upload-archive: %s", strerror(err)); } /* parent - read from child, multiplex and send out to fd#1 */ packet_write(1, "ACK\n"); packet_flush(1); while (1) { struct pollfd pfd[2]; int status; pfd[0].fd = cld.out; pfd[0].events = POLLIN; pfd[1].fd = cld.err; pfd[1].events = POLLIN; if (poll(pfd, 2, -1) < 0) { if (errno != EINTR) { error("poll failed resuming: %s", strerror(errno)); 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 (waitpid(cld.pid, &status, 0) < 0) error_clnt("%s", lostchild); else if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) error_clnt("%s", deadchild); packet_flush(1); break; } return 0; }
int main(int argc, char **argv) { char *dir; int i; int strict = 0; git_setup_gettext(); packet_trace_identity("upload-pack"); git_extract_argv0_path(argv[0]); read_replace_refs = 0; for (i = 1; i < argc; i++) { char *arg = argv[i]; if (arg[0] != '-') break; if (!strcmp(arg, "--advertise-refs")) { advertise_refs = 1; continue; } if (!strcmp(arg, "--stateless-rpc")) { stateless_rpc = 1; continue; } if (!strcmp(arg, "--strict")) { strict = 1; continue; } if (!prefixcmp(arg, "--timeout=")) { timeout = atoi(arg+10); daemon_mode = 1; continue; } if (!strcmp(arg, "--")) { i++; break; } } if (i != argc-1) usage(upload_pack_usage); setup_path(); dir = argv[i]; if (!enter_repo(dir, strict)) die("'%s' does not appear to be a git repository", dir); if (is_repository_shallow()) die("attempt to fetch/clone from a shallow repository"); git_config(upload_pack_config, NULL); upload_pack(); return 0; }
int cmd_main(int argc, const char **argv) { const char *dir; int strict = 0; struct option options[] = { OPT_BOOL(0, "stateless-rpc", &stateless_rpc, N_("quit after a single request/response exchange")), OPT_BOOL(0, "advertise-refs", &advertise_refs, N_("exit immediately after initial ref advertisement")), OPT_BOOL(0, "strict", &strict, N_("do not try <directory>/.git/ if <directory> is no Git directory")), OPT_INTEGER(0, "timeout", &timeout, N_("interrupt transfer after <n> seconds of inactivity")), OPT_END() }; packet_trace_identity("upload-pack"); check_replace_refs = 0; argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0); if (argc != 1) usage_with_options(upload_pack_usage, options); if (timeout) daemon_mode = 1; setup_path(); dir = argv[0]; if (!enter_repo(dir, strict)) die("'%s' does not appear to be a git repository", dir); git_config(upload_pack_config, NULL); switch (determine_protocol_version_server()) { case protocol_v1: /* * v1 is just the original protocol with a version string, * so just fall through after writing the version string. */ if (advertise_refs || !stateless_rpc) packet_write_fmt(1, "version 1\n"); /* fallthrough */ case protocol_v0: upload_pack(); break; case protocol_unknown_version: BUG("unknown protocol version"); } return 0; }
static int run_upload_archive(int argc, const char **argv, const char *prefix) { const char *sent_argv[MAX_ARGS]; const char *arg_cmd = "argument "; char *p, buf[4096]; int sent_argc; int len; if (argc != 2) usage(upload_archive_usage); if (strlen(argv[1]) + 1 > sizeof(buf)) die("insanely long repository name"); strcpy(buf, argv[1]); /* enter-repo smudges its argument */ if (!enter_repo(buf, 0)) die("'%s' does not appear to be a git repository", buf); /* put received options in sent_argv[] */ sent_argc = 1; sent_argv[0] = "git-upload-archive"; for (p = buf;;) { /* This will die if not enough free space in buf */ len = packet_read_line(0, p, (buf + sizeof buf) - p); if (len == 0) break; /* got a flush */ if (sent_argc > MAX_ARGS - 2) die("Too many options (>%d)", MAX_ARGS - 2); if (p[len-1] == '\n') { p[--len] = 0; } if (len < strlen(arg_cmd) || strncmp(arg_cmd, p, strlen(arg_cmd))) die("'argument' token or flush expected"); len -= strlen(arg_cmd); memmove(p, p + strlen(arg_cmd), len); sent_argv[sent_argc++] = p; p += len; *p++ = 0; } sent_argv[sent_argc] = NULL; /* parse all options sent by the client */ return write_archive(sent_argc, sent_argv, prefix, 0); }
int main(int argc, char **argv) { char *dir; int i; int strict = 0; git_extract_argv0_path(argv[0]); read_replace_refs = 0; for (i = 1; i < argc; i++) { char *arg = argv[i]; if (arg[0] != '-') break; if (!strcmp(arg, "--strict")) { strict = 1; continue; } if (!prefixcmp(arg, "--timeout=")) { timeout = atoi(arg+10); daemon_mode = 1; continue; } if (!strcmp(arg, "--")) { i++; break; } } if (i != argc-1) usage(upload_pack_usage); setup_path(); dir = argv[i]; if (!enter_repo(dir, strict)) die("'%s' does not appear to be a git repository", dir); if (is_repository_shallow()) die("attempt to fetch/clone from a shallow repository"); if (getenv("GIT_DEBUG_SEND_PACK")) debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK")); upload_pack(); return 0; }
int cmd_main(int argc, const char **argv) { const char *dir; int strict = 0; struct option options[] = { OPT_BOOL(0, "stateless-rpc", &stateless_rpc, N_("quit after a single request/response exchange")), OPT_BOOL(0, "advertise-refs", &advertise_refs, N_("exit immediately after intial ref advertisement")), OPT_BOOL(0, "strict", &strict, N_("do not try <directory>/.git/ if <directory> is no Git directory")), OPT_INTEGER(0, "timeout", &timeout, N_("interrupt transfer after <n> seconds of inactivity")), OPT_END() }; packet_trace_identity("upload-pack"); check_replace_refs = 0; argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0); if (argc != 1) usage_with_options(upload_pack_usage, options); if (timeout) daemon_mode = 1; setup_path(); dir = argv[0]; if (!enter_repo(dir, strict)) die("'%s' does not appear to be a git repository", dir); git_config(upload_pack_config, NULL); upload_pack(); return 0; }
int main(int argc, char **argv) { char *method = getenv("REQUEST_METHOD"); char *dir; struct service_cmd *cmd = NULL; char *cmd_arg = NULL; int i; git_setup_gettext(); git_extract_argv0_path(argv[0]); set_die_routine(die_webcgi); if (!method) die("No REQUEST_METHOD from server"); if (!strcmp(method, "HEAD")) method = "GET"; dir = getdir(); for (i = 0; i < ARRAY_SIZE(services); i++) { struct service_cmd *c = &services[i]; regex_t re; regmatch_t out[1]; if (regcomp(&re, c->pattern, REG_EXTENDED)) die("Bogus regex in service table: %s", c->pattern); if (!regexec(&re, dir, 1, out, 0)) { size_t n; if (strcmp(method, c->method)) { const char *proto = getenv("SERVER_PROTOCOL"); if (proto && !strcmp(proto, "HTTP/1.1")) { http_status(405, "Method Not Allowed"); hdr_str("Allow", !strcmp(c->method, "GET") ? "GET, HEAD" : c->method); } else http_status(400, "Bad Request"); hdr_nocache(); end_headers(); return 0; } cmd = c; n = out[0].rm_eo - out[0].rm_so; cmd_arg = xmemdupz(dir + out[0].rm_so + 1, n - 1); dir[out[0].rm_so] = 0; break; } regfree(&re); } if (!cmd) not_found("Request not supported: '%s'", dir); setup_path(); if (!enter_repo(dir, 0)) not_found("Not a git repository: '%s'", dir); if (!getenv("GIT_HTTP_EXPORT_ALL") && access("git-daemon-export-ok", F_OK) ) not_found("Repository not exported: '%s'", dir); git_config(http_config, NULL); cmd->imp(cmd_arg); return 0; }
static const char *path_ok(char *directory) { static char rpath[PATH_MAX]; static char interp_path[PATH_MAX]; const char *path; char *dir; dir = directory; if (daemon_avoid_alias(dir)) { logerror("'%s': aliased", dir); return NULL; } if (*dir == '~') { if (!user_path) { logerror("'%s': User-path not allowed", dir); return NULL; } if (*user_path) { /* Got either "~alice" or "~alice/foo"; * rewrite them to "~alice/%s" or * "~alice/%s/foo". */ int namlen, restlen = strlen(dir); char *slash = strchr(dir, '/'); if (!slash) slash = dir + restlen; namlen = slash - dir; restlen -= namlen; loginfo("userpath <%s>, request <%s>, namlen %d, restlen %d, slash <%s>", user_path, dir, namlen, restlen, slash); snprintf(rpath, PATH_MAX, "%.*s/%s%.*s", namlen, dir, user_path, restlen, slash); dir = rpath; } } else if (interpolated_path && saw_extended_args) { struct strbuf expanded_path = STRBUF_INIT; struct strbuf_expand_dict_entry dict[6]; dict[0].placeholder = "H"; dict[0].value = hostname; dict[1].placeholder = "CH"; dict[1].value = canon_hostname; dict[2].placeholder = "IP"; dict[2].value = ip_address; dict[3].placeholder = "P"; dict[3].value = tcp_port; dict[4].placeholder = "D"; dict[4].value = directory; dict[5].placeholder = NULL; dict[5].value = NULL; if (*dir != '/') { /* Allow only absolute */ logerror("'%s': Non-absolute path denied (interpolated-path active)", dir); return NULL; } strbuf_expand(&expanded_path, interpolated_path, strbuf_expand_dict_cb, &dict); strlcpy(interp_path, expanded_path.buf, PATH_MAX); strbuf_release(&expanded_path); loginfo("Interpolated dir '%s'", interp_path); dir = interp_path; } else if (base_path) { if (*dir != '/') { /* Allow only absolute */ logerror("'%s': Non-absolute path denied (base-path active)", dir); return NULL; } snprintf(rpath, PATH_MAX, "%s%s", base_path, dir); dir = rpath; } path = enter_repo(dir, strict_paths); if (!path && base_path && base_path_relaxed) { /* * if we fail and base_path_relaxed is enabled, try without * prefixing the base path */ dir = directory; path = enter_repo(dir, strict_paths); } if (!path) { logerror("'%s' does not appear to be a git repository", dir); return NULL; } if ( ok_paths && *ok_paths ) { char **pp; int pathlen = strlen(path); /* The validation is done on the paths after enter_repo * appends optional {.git,.git/.git} and friends, but * it does not use getcwd(). So if your /pub is * a symlink to /mnt/pub, you can whitelist /pub and * do not have to say /mnt/pub. * Do not say /pub/. */ for ( pp = ok_paths ; *pp ; pp++ ) { int len = strlen(*pp); if (len <= pathlen && !memcmp(*pp, path, len) && (path[len] == '\0' || (!strict_paths && path[len] == '/'))) return path; } } else { /* be backwards compatible */ if (!strict_paths) return path; } logerror("'%s': not in whitelist", path); return NULL; /* Fallthrough. Deny by default */ }
int cmd_receive_pack(int argc, const char **argv, const char *prefix) { int advertise_refs = 0; int stateless_rpc = 0; int i; char *dir = NULL; struct command *commands; packet_trace_identity("receive-pack"); argv++; for (i = 1; i < argc; i++) { const char *arg = *argv++; if (*arg == '-') { if (!strcmp(arg, "--advertise-refs")) { advertise_refs = 1; continue; } if (!strcmp(arg, "--stateless-rpc")) { stateless_rpc = 1; continue; } usage(receive_pack_usage); } if (dir) usage(receive_pack_usage); dir = xstrdup(arg); } if (!dir) usage(receive_pack_usage); setup_path(); if (!enter_repo(dir, 0)) die("'%s' does not appear to be a git repository", dir); if (is_repository_shallow()) die("attempt to push into a shallow repository"); git_config(receive_pack_config, NULL); if (0 <= transfer_unpack_limit) unpack_limit = transfer_unpack_limit; else if (0 <= receive_unpack_limit) unpack_limit = receive_unpack_limit; if (advertise_refs || !stateless_rpc) { write_head_info(); } if (advertise_refs) return 0; if ((commands = read_head_info()) != NULL) { const char *unpack_status = NULL; if (!delete_only(commands)) unpack_status = unpack(); execute_commands(commands, unpack_status); if (pack_lockfile) unlink_or_warn(pack_lockfile); if (report_status) report(commands, unpack_status); run_receive_hook(commands, post_receive_hook, 1); run_update_post_hook(commands); if (auto_gc) { const char *argv_gc_auto[] = { "gc", "--auto", "--quiet", NULL, }; run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); } if (auto_update_server_info) update_server_info(0); } if (use_sideband) packet_flush(1); return 0; }
int cmd_receive_pack(int argc, const char **argv, const char *prefix) { int advertise_refs = 0; struct command *commands; struct sha1_array shallow = SHA1_ARRAY_INIT; struct sha1_array ref = SHA1_ARRAY_INIT; struct shallow_info si; struct option options[] = { OPT__QUIET(&quiet, N_("quiet")), OPT_HIDDEN_BOOL(0, "stateless-rpc", &stateless_rpc, NULL), OPT_HIDDEN_BOOL(0, "advertise-refs", &advertise_refs, NULL), OPT_HIDDEN_BOOL(0, "reject-thin-pack-for-testing", &reject_thin, NULL), OPT_END() }; packet_trace_identity("receive-pack"); argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0); if (argc > 1) usage_msg_opt(_("Too many arguments."), receive_pack_usage, options); if (argc == 0) usage_msg_opt(_("You must specify a directory."), receive_pack_usage, options); service_dir = argv[0]; setup_path(); if (!enter_repo(service_dir, 0)) die("'%s' does not appear to be a git repository", service_dir); git_config(receive_pack_config, NULL); if (cert_nonce_seed) push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL)); if (0 <= transfer_unpack_limit) unpack_limit = transfer_unpack_limit; else if (0 <= receive_unpack_limit) unpack_limit = receive_unpack_limit; if (advertise_refs || !stateless_rpc) { write_head_info(); } if (advertise_refs) return 0; if ((commands = read_head_info(&shallow)) != NULL) { const char *unpack_status = NULL; struct string_list push_options = STRING_LIST_INIT_DUP; if (use_push_options) read_push_options(&push_options); prepare_shallow_info(&si, &shallow); if (!si.nr_ours && !si.nr_theirs) shallow_update = 0; if (!delete_only(commands)) { unpack_status = unpack_with_sideband(&si); update_shallow_info(commands, &si, &ref); } use_keepalive = KEEPALIVE_ALWAYS; execute_commands(commands, unpack_status, &si, &push_options); if (pack_lockfile) unlink_or_warn(pack_lockfile); if (report_status) report(commands, unpack_status); run_receive_hook(commands, "post-receive", 1, &push_options); run_update_post_hook(commands); if (push_options.nr) string_list_clear(&push_options, 0); if (auto_gc) { const char *argv_gc_auto[] = { "gc", "--auto", "--quiet", NULL, }; struct child_process proc = CHILD_PROCESS_INIT; proc.no_stdin = 1; proc.stdout_to_stderr = 1; proc.err = use_sideband ? -1 : 0; proc.git_cmd = 1; proc.argv = argv_gc_auto; close_all_packs(); if (!start_command(&proc)) { if (use_sideband) copy_to_sideband(proc.err, -1, NULL); finish_command(&proc); } } if (auto_update_server_info) update_server_info(0); clear_shallow_info(&si); } if (use_sideband) packet_flush(1); sha1_array_clear(&shallow); sha1_array_clear(&ref); free((void *)push_cert_nonce); return 0; }
int cmd_receive_pack(int argc, const char **argv, const char *prefix) { int advertise_refs = 0; int i; struct command *commands; struct sha1_array shallow = SHA1_ARRAY_INIT; struct sha1_array ref = SHA1_ARRAY_INIT; struct shallow_info si; packet_trace_identity("receive-pack"); argv++; for (i = 1; i < argc; i++) { const char *arg = *argv++; if (*arg == '-') { if (!strcmp(arg, "--quiet")) { quiet = 1; continue; } if (!strcmp(arg, "--advertise-refs")) { advertise_refs = 1; continue; } if (!strcmp(arg, "--stateless-rpc")) { stateless_rpc = 1; continue; } if (!strcmp(arg, "--reject-thin-pack-for-testing")) { fix_thin = 0; continue; } usage(receive_pack_usage); } if (service_dir) usage(receive_pack_usage); service_dir = arg; } if (!service_dir) usage(receive_pack_usage); setup_path(); if (!enter_repo(service_dir, 0)) die("'%s' does not appear to be a git repository", service_dir); git_config(receive_pack_config, NULL); if (cert_nonce_seed) push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL)); if (0 <= transfer_unpack_limit) unpack_limit = transfer_unpack_limit; else if (0 <= receive_unpack_limit) unpack_limit = receive_unpack_limit; if (advertise_refs || !stateless_rpc) { write_head_info(); } if (advertise_refs) return 0; if ((commands = read_head_info(&shallow)) != NULL) { const char *unpack_status = NULL; prepare_shallow_info(&si, &shallow); if (!si.nr_ours && !si.nr_theirs) shallow_update = 0; if (!delete_only(commands)) { unpack_status = unpack_with_sideband(&si); update_shallow_info(commands, &si, &ref); } execute_commands(commands, unpack_status, &si); if (pack_lockfile) unlink_or_warn(pack_lockfile); if (report_status) report(commands, unpack_status); run_receive_hook(commands, "post-receive", 1); run_update_post_hook(commands); if (auto_gc) { const char *argv_gc_auto[] = { "gc", "--auto", "--quiet", NULL, }; int opt = RUN_GIT_CMD | RUN_COMMAND_STDOUT_TO_STDERR; run_command_v_opt(argv_gc_auto, opt); } if (auto_update_server_info) update_server_info(0); clear_shallow_info(&si); } if (use_sideband) packet_flush(1); sha1_array_clear(&shallow); sha1_array_clear(&ref); free((void *)push_cert_nonce); return 0; }
static char *path_ok(struct interp *itable) { static char rpath[PATH_MAX]; static char interp_path[PATH_MAX]; int retried_path = 0; char *path; char *dir; dir = itable[INTERP_SLOT_DIR].value; if (avoid_alias(dir)) { logerror("'%s': aliased", dir); return NULL; } if (*dir == '~') { if (!user_path) { logerror("'%s': User-path not allowed", dir); return NULL; } if (*user_path) { /* Got either "~alice" or "~alice/foo"; * rewrite them to "~alice/%s" or * "~alice/%s/foo". */ int namlen, restlen = strlen(dir); char *slash = strchr(dir, '/'); if (!slash) slash = dir + restlen; namlen = slash - dir; restlen -= namlen; loginfo("userpath <%s>, request <%s>, namlen %d, restlen %d, slash <%s>", user_path, dir, namlen, restlen, slash); snprintf(rpath, PATH_MAX, "%.*s/%s%.*s", namlen, dir, user_path, restlen, slash); dir = rpath; } } else if (interpolated_path && saw_extended_args) { if (*dir != '/') { /* Allow only absolute */ logerror("'%s': Non-absolute path denied (interpolated-path active)", dir); return NULL; } interpolate(interp_path, PATH_MAX, interpolated_path, interp_table, ARRAY_SIZE(interp_table)); loginfo("Interpolated dir '%s'", interp_path); dir = interp_path; } else if (base_path) { if (*dir != '/') { /* Allow only absolute */ logerror("'%s': Non-absolute path denied (base-path active)", dir); return NULL; } snprintf(rpath, PATH_MAX, "%s%s", base_path, dir); dir = rpath; } do { path = enter_repo(dir, strict_paths); if (path) break; /* * if we fail and base_path_relaxed is enabled, try without * prefixing the base path */ if (base_path && base_path_relaxed && !retried_path) { dir = itable[INTERP_SLOT_DIR].value; retried_path = 1; continue; } break; } while (1); if (!path) { logerror("'%s': unable to chdir or not a git archive", dir); return NULL; } if ( ok_paths && *ok_paths ) { char **pp; int pathlen = strlen(path); /* The validation is done on the paths after enter_repo * appends optional {.git,.git/.git} and friends, but * it does not use getcwd(). So if your /pub is * a symlink to /mnt/pub, you can whitelist /pub and * do not have to say /mnt/pub. * Do not say /pub/. */ for ( pp = ok_paths ; *pp ; pp++ ) { int len = strlen(*pp); if (len <= pathlen && !memcmp(*pp, path, len) && (path[len] == '\0' || (!strict_paths && path[len] == '/'))) return path; } } else { /* be backwards compatible */ if (!strict_paths) return path; } logerror("'%s': not in whitelist", path); return NULL; /* Fallthrough. Deny by default */ }