diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt index a20e0533fc..2cc6075fb0 100644 --- a/Documentation/git-daemon.txt +++ b/Documentation/git-daemon.txt @@ -10,7 +10,8 @@ SYNOPSIS [verse] 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all] [--timeout=n] [--init-timeout=n] [--strict-paths] - [--base-path=path] [directory...] + [--base-path=path] [--user-path | --user-path=path] + [directory...] DESCRIPTION ----------- @@ -42,8 +43,7 @@ OPTIONS This is sort of "GIT root" - if you run git-daemon with '--base-path=/srv/git' on example.com, then if you later try to pull 'git://example.com/hello.git', `git-daemon` will interpret the path - as '/srv/git/hello.git'. Home directories (the '~login' notation) - access is disabled. + as '/srv/git/hello.git'. --export-all:: Allow pulling from all directories that look like GIT repositories @@ -70,6 +70,15 @@ OPTIONS Log to syslog instead of stderr. Note that this option does not imply --verbose, thus by default only error conditions will be logged. +--user-path, --user-path=path:: + Allow ~user notation to be used in requests. When + specified with no parameter, requests to + git://host/~alice/foo is taken as a request to access + 'foo' repository in the home directory of user `alice`. + If `--user-path=path` is specified, the same request is + taken as a request to access `path/foo` repository in + the home directory of user `alice`. + --verbose:: Log details about the incoming connections and requested files. diff --git a/daemon.c b/daemon.c index 532bb0c325..a1ccda30e2 100644 --- a/daemon.c +++ b/daemon.c @@ -13,11 +13,13 @@ static int log_syslog; static int verbose; +static int reuseaddr; static const char daemon_usage[] = "git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n" " [--timeout=n] [--init-timeout=n] [--strict-paths]\n" -" [--base-path=path] [directory...]"; +" [--base-path=path] [--user-path | --user-path=path]\n" +" [--reuseaddr] [directory...]"; /* List of acceptable pathname prefixes */ static char **ok_paths = NULL; @@ -29,6 +31,12 @@ static int export_all_trees = 0; /* Take all paths relative to this one if non-NULL */ static char *base_path = NULL; +/* If defined, ~user notation is allowed and the string is inserted + * after ~user/. E.g. a request to git://host/~alice/frotz would + * go to /home/alice/pub_git/frotz with --user-path=pub_git. + */ +static char *user_path = NULL; + /* Timeout, and initial timeout */ static unsigned int timeout = 0; static unsigned int init_timeout = 0; @@ -136,6 +144,7 @@ static int avoid_alias(char *p) static char *path_ok(char *dir) { + static char rpath[PATH_MAX]; char *path; if (avoid_alias(dir)) { @@ -143,15 +152,38 @@ static char *path_ok(char *dir) return NULL; } - if (base_path) { - static char rpath[PATH_MAX]; + 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 (base_path) { if (*dir != '/') { - /* Forbid possible base-path evasion using ~paths. */ + /* 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; + else { + snprintf(rpath, PATH_MAX, "%s%s", base_path, dir); + dir = rpath; + } } path = enter_repo(dir, strict_paths); @@ -447,6 +479,16 @@ static void child_handler(int signo) } } +static int set_reuse_addr(int sockfd) +{ + int on = 1; + + if (!reuseaddr) + return 0; + return setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, + &on, sizeof(on)); +} + #ifndef NO_IPV6 static int socksetup(int port, int **socklist_p) @@ -491,6 +533,11 @@ static int socksetup(int port, int **socklist_p) } #endif + if (set_reuse_addr(sockfd)) { + close(sockfd); + return 0; /* not fatal */ + } + if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { close(sockfd); continue; /* not fatal */ @@ -533,6 +580,11 @@ static int socksetup(int port, int **socklist_p) sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_port = htons(port); + if (set_reuse_addr(sockfd)) { + close(sockfd); + return 0; + } + if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) { close(sockfd); return 0; @@ -659,6 +711,18 @@ int main(int argc, char **argv) base_path = arg+12; continue; } + if (!strcmp(arg, "--reuseaddr")) { + reuseaddr = 1; + continue; + } + if (!strcmp(arg, "--user-path")) { + user_path = ""; + continue; + } + if (!strncmp(arg, "--user-path=", 12)) { + user_path = arg + 12; + continue; + } if (!strcmp(arg, "--")) { ok_paths = &argv[i+1]; break; diff --git a/http-fetch.c b/http-fetch.c index 72edf28b00..bddbd6b100 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -311,7 +311,7 @@ void fill_active_slots(void) while (active_requests < max_requests && obj_req != NULL) { if (obj_req->state == WAITING) { if (has_sha1_file(obj_req->sha1)) - release_object_request(obj_req); + obj_req->state = COMPLETE; else start_object_request(obj_req); curl_multi_perform(curlm, &num_transfers); @@ -468,13 +468,11 @@ static void process_alternates_response(void *callback_data) alt_req->url); active_requests++; slot->in_use = 1; - if (start_active_slot(slot)) { - return; - } else { + if (!start_active_slot(slot)) { got_alternates = -1; slot->in_use = 0; - return; } + return; } } else if (slot->curl_result != CURLE_OK) { if (slot->http_code != 404 && @@ -822,9 +820,8 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1) } else if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) { ret = error("File %s has bad hash\n", hex); } else if (obj_req->rename < 0) { - ret = error("unable to write sha1 filename %s: %s", - obj_req->filename, - strerror(obj_req->rename)); + ret = error("unable to write sha1 filename %s", + obj_req->filename); } release_object_request(obj_req); diff --git a/http.c b/http.c index eefb0f03d2..632c2c5c2f 100644 --- a/http.c +++ b/http.c @@ -192,6 +192,9 @@ static CURL* get_curl_handle(void) curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1); + if (getenv("GIT_CURL_VERBOSE")) + curl_easy_setopt(result, CURLOPT_VERBOSE, 1); + return result; }