[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
-----------
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
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.
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;
/* 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;
static char *path_ok(char *dir)
{
+ static char rpath[PATH_MAX];
char *path;
if (avoid_alias(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);
}
}
+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)
}
#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 */
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;
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;
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);
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 &&
} 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);