git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Pierre Habouzit <madcoder@debian.org>
To: git@vger.kernel.org
Subject: [PATCH] git-daemon: more powerful base-path/user-path settings, using formats.
Date: Thu, 24 Aug 2006 01:32:54 +0200	[thread overview]
Message-ID: <115637597423-git-send-email-madcoder@debian.org> (raw)
In-Reply-To: <7vmz9vgqlm.fsf@assigned-by-dhcp.cox.net>

Allow a form of virtualhosting, when %h format is used.

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---

    This is intended to be a more flexible solution, that also gives virtual
    hosting as a bonus. I still see no way to deal with older clients when
    virtual hosting is used by the admin though, having a "default" hostname
    won't solve anything at all anyway.

 Documentation/git-daemon.txt |   42 ++++++++++
 daemon.c                     |  170 +++++++++++++++++++++++++++++++++++++++---
 2 files changed, 201 insertions(+), 11 deletions(-)

diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index 0f7d274..0aa34d4 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -11,6 +11,7 @@ SYNOPSIS
 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]
              [--timeout=n] [--init-timeout=n] [--strict-paths]
              [--base-path=path] [--user-path | --user-path=path]
+             [--base-path-fmt=pathfmt] [--user-path-fmt=pathfmt]
 	     [--reuseaddr] [--detach] [--pid-file=file] [directory...]
 
 DESCRIPTION
@@ -45,6 +46,10 @@ OPTIONS
 	'git://example.com/hello.git', `git-daemon` will interpret the path
 	as '/srv/git/hello.git'.
 
+--base-path-fmt=pathfmt::
+	Works like --base-path, but uses a format instead. See Path
+	Formats section below.
+
 --export-all::
 	Allow pulling from all directories that look like GIT repositories
 	(have the 'objects' and 'refs' subdirectories), even if they
@@ -79,6 +84,11 @@ OPTIONS
 	taken as a request to access `path/foo` repository in
 	the home directory of user `alice`.
 
+--user-path-fmt=pathfmt::
+	Allow ~user notation to be used in requests. The path used to look
+	for the git repository is a format string, see Path Formats
+	section below.
+
 --verbose::
 	Log details about the incoming connections and requested files.
 
@@ -98,6 +108,38 @@ OPTIONS
 	--strict-paths is specified this will also include subdirectories
 	of each named directory.
 
+Path Formats
+------------
+
+%h::
+	requested hostname (won't work with clients older than 1.4.0).
+
+%p::
+	requested path.
+
+%P::
+	requested path without the first slash when exists (`/`).
+
+%u::
+	requested username (only allowed for --user-path-fmt).
+
+%%::
+	plain '%'
+
+Examples
+~~~~~~~~
+
+* `--base-path-fmt=/srv/git/%P` emulates `--base-path=/srv/git`
+
+* `--base-path-fmt=/srv/git/%h%p` will look into
+  `/srv/git/git.example.com/foo` if the request goes to
+  git://git.example.com/foo
+
+* `--user-path-fmt=/home/%u/public_git/%P` works mostly like
+  `--user-path=public_git` (the difference is that it assumes that home
+  directories live in `/home/username` instead of using the real `username`
+  home).
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki
diff --git a/daemon.c b/daemon.c
index 012936f..1ffebd6 100644
--- a/daemon.c
+++ b/daemon.c
@@ -19,6 +19,7 @@ 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] [--user-path | --user-path=path]\n"
+"           [--base-path-fmt=pathfmt] [--user-path-fmt=pathfmt]\n"
 "           [--reuseaddr] [--detach] [--pid-file=file] [directory...]";
 
 /* List of acceptable pathname prefixes */
@@ -29,12 +30,14 @@ static int strict_paths;
 static int export_all_trees;
 
 /* Take all paths relative to this one if non-NULL */
+int is_base_path_fmt;
 static char *base_path;
 
 /* 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.
  */
+int is_user_path_fmt;
 static const char *user_path;
 
 /* Timeout, and initial timeout */
@@ -148,7 +151,100 @@ static int avoid_alias(char *p)
 	}
 }
 
-static char *path_ok(char *dir)
+static void check_path_fmt(const char *check, int allow_user)
+{
+	const char *p = check;
+
+	while (*p) {
+		if (*p++ != '%')
+			continue;
+
+		switch (*p) {
+		  case '\0':
+		    die("invalid format: <%s> ends with an unescaped %%", check);
+
+		  case '%':
+		  case 'p': case 'P':
+		  case 'h':
+		    break;
+
+		  case 'u':
+		    if (allow_user)
+			    break;
+		    /* fallthrough */
+
+		  default:
+		    die("invalid format: <%s> uses unknown specifier %%%c",
+			check, *p);
+		}
+
+		p++;
+	}
+}
+
+static char *
+git_path_fmt(char rpath[PATH_MAX], const char *fmt,
+             const char *vhost, const char *path,
+             const char *username, int namlen)
+{
+	const char *p = fmt;
+	int pos = 0;
+
+	while (*p) {
+		if (*p != '%') {
+			rpath[pos++] = *p++;
+			continue;
+		}
+
+		switch (*++p) {
+		  case '%':
+		    rpath[pos++] = *p;
+		    break;
+
+		  case 'h':
+                    if (!vhost) {
+                        logerror("missing host=, client is too old");
+                        return NULL;
+                    }
+		    pos += strlcpy(rpath + pos, vhost, PATH_MAX - pos);
+		    break;
+
+		  case 'P':
+		    pos += strlcpy(rpath + pos, path + (*path == '/'),
+				   PATH_MAX - pos);
+		    break;
+
+		  case 'p':
+		    pos += strlcpy(rpath + pos, path, PATH_MAX - pos);
+		    break;
+
+		  case 'u':
+		    pos += snprintf(rpath + pos, PATH_MAX - pos, "%.*s",
+				    namlen, username);
+		    break;
+		}
+
+		p++;
+
+		if (pos >= PATH_MAX) {
+                        logerror("generated path is too long");
+			return NULL;
+		}
+	}
+
+	rpath[pos] = '\0';
+
+        return rpath;
+}
+
+static inline char *
+git_base_path_fmt(char rpath[PATH_MAX], const char *fmt,
+                  const char *vhost, const char *path)
+{
+    return git_path_fmt(rpath, fmt, vhost, path, NULL, 0);
+}
+
+static char *path_ok(char *dir, char *vhost)
 {
 	static char rpath[PATH_MAX];
 	char *path;
@@ -174,10 +270,25 @@ static char *path_ok(char *dir)
 				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;
+
+			if (is_user_path_fmt) {
+				loginfo("host <%s>, "
+					"userpathfmt <%s>, request <%s>, "
+					"namlen %d, restlen %d, slash <%s>",
+					vhost,
+					user_path, dir,
+					namlen, restlen, slash);
+                                dir = git_path_fmt(rpath, user_path, vhost,
+                                                   slash, dir + 1, namlen - 1);
+			} else {
+				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) {
@@ -186,12 +297,18 @@ static char *path_ok(char *dir)
 			logerror("'%s': Non-absolute path denied (base-path active)", dir);
 			return NULL;
 		}
-		else {
+
+		if (is_base_path_fmt) {
+                        dir = git_base_path_fmt(rpath, base_path, vhost, dir);
+		} else {
 			snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
 			dir = rpath;
 		}
 	}
 
+	if (!dir)
+		return NULL;
+
 	path = enter_repo(dir, strict_paths);
 
 	if (!path) {
@@ -229,7 +346,7 @@ static char *path_ok(char *dir)
 	return NULL;		/* Fallthrough. Deny by default */
 }
 
-static int upload(char *dir)
+static int upload(char *dir, char *vhost)
 {
 	/* Timeout as string */
 	char timeout_buf[64];
@@ -237,7 +354,7 @@ static int upload(char *dir)
 
 	loginfo("Request for '%s'", dir);
 
-	if (!(path = path_ok(dir)))
+	if (!(path = path_ok(dir, vhost)))
 		return -1;
 
 	/*
@@ -274,6 +391,7 @@ static int execute(struct sockaddr *addr
 {
 	static char line[1000];
 	int pktlen, len;
+	char *vhost = NULL;
 
 	if (addr) {
 		char addrbuf[256] = "";
@@ -303,15 +421,30 @@ #endif
 	alarm(0);
 
 	len = strlen(line);
-	if (pktlen != len)
+
+	if (pktlen != len) {
+		int arg_pos = len + 1;
+
 		loginfo("Extended attributes (%d bytes) exist <%.*s>",
 			(int) pktlen - len,
-			(int) pktlen - len, line + len + 1);
+			(int) pktlen - len, line + arg_pos);
+
+		while (arg_pos < pktlen) {
+			int arg_len = strlen(line + arg_pos);
+
+			if (!strncmp("host=", line + arg_pos, 5)) {
+				vhost = line + arg_pos + 5;
+			}
+
+			arg_pos += arg_len + 1;
+		}
+	}
+
 	if (len && line[len-1] == '\n')
 		line[--len] = 0;
 
 	if (!strncmp("git-upload-pack ", line, 16))
-		return upload(line+16);
+		return upload(line+16, vhost);
 
 	logerror("Protocol error: '%s'", line);
 	return -1;
@@ -768,6 +901,13 @@ int main(int argc, char **argv)
 		}
 		if (!strncmp(arg, "--base-path=", 12)) {
 			base_path = arg+12;
+			is_base_path_fmt = 0;
+			continue;
+		}
+		if (!strncmp(arg, "--base-path-fmt=", 16)) {
+			base_path = arg+16;
+			is_base_path_fmt = 1;
+			check_path_fmt(base_path, 0);
 			continue;
 		}
 		if (!strcmp(arg, "--reuseaddr")) {
@@ -776,10 +916,18 @@ int main(int argc, char **argv)
 		}
 		if (!strcmp(arg, "--user-path")) {
 			user_path = "";
+			is_user_path_fmt = 0;
 			continue;
 		}
 		if (!strncmp(arg, "--user-path=", 12)) {
 			user_path = arg + 12;
+			is_user_path_fmt = 0;
+			continue;
+		}
+		if (!strncmp(arg, "--user-path-fmt=", 16)) {
+			user_path = arg + 16;
+			is_user_path_fmt = 1;
+			check_path_fmt(user_path, 1);
 			continue;
 		}
 		if (!strncmp(arg, "--pid-file=", 11)) {
-- 
1.4.2.g44c10-dirty

  parent reply	other threads:[~2006-08-23 23:33 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-08-23 18:52 [PATCH] git-daemon virtual hosting implementation Pierre Habouzit
2006-08-23 20:11 ` Junio C Hamano
2006-08-23 20:56   ` Pierre Habouzit
2006-08-24 20:15     ` Jon Loeliger
2006-08-24 20:35       ` Junio C Hamano
2006-08-24 20:34         ` Jon Loeliger
2006-08-23 23:32   ` Pierre Habouzit [this message]
2006-08-24  0:17     ` [PATCH] git-daemon: more powerful base-path/user-path settings, using formats Junio C Hamano
2006-08-24  7:50       ` Pierre Habouzit
2006-08-27  6:12     ` Junio C Hamano
2006-08-27 10:28       ` Pierre Habouzit
2006-08-27 10:52         ` Junio C Hamano
2006-08-27 11:40           ` Pierre Habouzit
2006-08-27 15:30             ` Jakub Narebski
2006-08-27 16:26               ` Pierre Habouzit
2006-08-27 16:06           ` Randal L. Schwartz
  -- strict thread matches above, loose matches on Subject: below --
2006-08-27 21:49 Jon Loeliger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=115637597423-git-send-email-madcoder@debian.org \
    --to=madcoder@debian.org \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).