From: Pierre Habouzit <madcoder@debian.org>
To: git@vger.kernel.org
Cc: Pierre Habouzit <madcoder@debian.org>
Subject: [PATCH] full featured formating function of the --{base,user}_path arguments,
Date: Sun, 27 Aug 2006 13:39:18 +0200 [thread overview]
Message-ID: <11566787581030-git-send-email-madcoder@debian.org> (raw)
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
Documentation/git-daemon.txt | 54 +++++++++++
daemon.c | 207 ++++++++++++++++++++++++++++++++++++++----
2 files changed, 240 insertions(+), 21 deletions(-)
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index 0f7d274..796ae7e 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -11,6 +11,8 @@ 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]
+ [--default-hostname=hostname]
[--reuseaddr] [--detach] [--pid-file=file] [directory...]
DESCRIPTION
@@ -45,6 +47,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,9 +85,20 @@ 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.
+--default-hostname=hostname::
+ Backward compatibility switch: argument is used as a fallback
+ hostname to use with virtual hosting (to fill the `%h` format in
+ path formats) when the client is too old to provide the information
+ in the query (older than 1.4.0 series).
+
--reuseaddr::
Use SO_REUSEADDR when binding the listening socket.
This allows the server to restart without waiting for
@@ -98,10 +115,45 @@ OPTIONS
--strict-paths is specified this will also include subdirectories
of each named directory.
+Path Formats
+------------
+
+%h::
+ requested hostname. That won't work with clients older than 1.4.0
+ that do not pass the hostname in their query, and in that case, the
+ value of --default-hostname (if any) is used.
+
+%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
-<yoshfuji@linux-ipv6.org> and the git-list <git@vger.kernel.org>
+<yoshfuji@linux-ipv6.org>, Pierre Habouzit <madcoder@debian.org>,
+and the git-list <git@vger.kernel.org>
Documentation
--------------
diff --git a/daemon.c b/daemon.c
index 012936f..a2fb9b4 100644
--- a/daemon.c
+++ b/daemon.c
@@ -19,6 +19,8 @@ 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"
+" [--default-hostname=hostname]\n"
" [--reuseaddr] [--detach] [--pid-file=file] [directory...]";
/* List of acceptable pathname prefixes */
@@ -29,18 +31,27 @@ static int strict_paths;
static int export_all_trees;
/* Take all paths relative to this one if non-NULL */
-static char *base_path;
+static struct {
+ const char *path;
+ int use_as_fmt;
+} 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.
*/
-static const char *user_path;
+static struct {
+ const char *path;
+ int use_as_fmt;
+} user_path;
/* Timeout, and initial timeout */
static unsigned int timeout;
static unsigned int init_timeout;
+/* default host used when the client is old and does not provides host= */
+static char *default_host;
+
static void logreport(int priority, const char *err, va_list params)
{
/* We should do a single write so that it is atomic and output
@@ -148,7 +159,107 @@ 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 && pos < PATH_MAX) {
+ if (*p != '%') {
+ rpath[pos++] = *p++;
+ continue;
+ }
+
+ switch (*++p) {
+ case '%':
+ rpath[pos++] = *p;
+ break;
+
+ case 'h':
+ if (!vhost && !default_host) {
+ logerror("abort: missing host=, no --default-host given, client is too old");
+ return NULL;
+ }
+
+ if (!vhost) {
+ logerror("old client without host=, using <%s>", default_host);
+ pos += strlcpy(rpath + pos, default_host, PATH_MAX - pos);
+ } else {
+ 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;
@@ -159,11 +270,11 @@ static char *path_ok(char *dir)
}
if (*dir == '~') {
- if (!user_path) {
+ if (!user_path.path) {
logerror("'%s': User-path not allowed", dir);
return NULL;
}
- if (*user_path) {
+ if (*user_path.path) {
/* Got either "~alice" or "~alice/foo";
* rewrite them to "~alice/%s" or
* "~alice/%s/foo".
@@ -174,24 +285,45 @@ 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 (user_path.use_as_fmt) {
+ loginfo("host <%s>, "
+ "userpathfmt <%s>, request <%s>, "
+ "namlen %d, restlen %d, slash <%s>",
+ vhost,
+ user_path.path, dir,
+ namlen, restlen, slash);
+ dir = git_path_fmt(rpath, user_path.path, vhost,
+ slash, dir + 1, namlen - 1);
+ } else {
+ loginfo("userpath <%s>, request <%s>, "
+ "namlen %d, restlen %d, slash <%s>",
+ user_path.path, dir,
+ namlen, restlen, slash);
+ snprintf(rpath, PATH_MAX, "%.*s/%s%.*s",
+ namlen, dir, user_path.path, restlen, slash);
+ dir = rpath;
+ }
}
}
- else if (base_path) {
+ else if (base_path.path) {
if (*dir != '/') {
/* Allow only absolute */
logerror("'%s': Non-absolute path denied (base-path active)", dir);
return NULL;
}
- else {
- snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
+
+ if (base_path.use_as_fmt) {
+ dir = git_base_path_fmt(rpath, base_path.path, vhost, dir);
+ } else {
+ snprintf(rpath, PATH_MAX, "%s%s", base_path.path, dir);
dir = rpath;
}
}
+ if (!dir)
+ return NULL;
+
path = enter_repo(dir, strict_paths);
if (!path) {
@@ -229,7 +361,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 +369,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 +406,7 @@ static int execute(struct sockaddr *addr
{
static char line[1000];
int pktlen, len;
+ char *vhost = NULL;
if (addr) {
char addrbuf[256] = "";
@@ -303,15 +436,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;
@@ -767,19 +915,38 @@ int main(int argc, char **argv)
continue;
}
if (!strncmp(arg, "--base-path=", 12)) {
- base_path = arg+12;
+ base_path.path = arg+12;
+ base_path.use_as_fmt = 0;
+ continue;
+ }
+ if (!strncmp(arg, "--base-path-fmt=", 16)) {
+ base_path.path = arg+16;
+ base_path.use_as_fmt = 1;
+ check_path_fmt(base_path.path, 0);
continue;
}
if (!strcmp(arg, "--reuseaddr")) {
reuseaddr = 1;
continue;
}
+ if (!strncmp(arg, "--default-hostname=", 19)) {
+ default_host = arg + 19;
+ continue;
+ }
if (!strcmp(arg, "--user-path")) {
- user_path = "";
+ user_path.path = "";
+ user_path.use_as_fmt = 0;
continue;
}
if (!strncmp(arg, "--user-path=", 12)) {
- user_path = arg + 12;
+ user_path.path = arg + 12;
+ user_path.use_as_fmt = 0;
+ continue;
+ }
+ if (!strncmp(arg, "--user-path-fmt=", 16)) {
+ user_path.path = arg + 16;
+ user_path.use_as_fmt = 1;
+ check_path_fmt(user_path.path, 1);
continue;
}
if (!strncmp(arg, "--pid-file=", 11)) {
--
1.4.1.1
next reply other threads:[~2006-08-27 11:39 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-08-27 11:39 Pierre Habouzit [this message]
2006-08-28 5:35 ` [PATCH] full featured formating function of the --{base,user}_path arguments, Junio C Hamano
2006-08-28 6:37 ` Pierre Habouzit
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=11566787581030-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).