daemon: give friendlier error messages to clients

When the git-daemon is asked about an inaccessible repository, it simply hangs up the connection without saying anything further. This makes it hard to distinguish between a repository we cannot access (e.g., due to typo), and a service or network outage. Instead, let's print an "ERR" line, which git clients understand since v1.6.1 (2008-12-24). Because there is a risk of leaking information about non-exported repositories, by default all errors simply say "access denied or repository not exported". Sites which don't have hidden repositories, or don't care, can pass a flag to turn on more specific messages. Signed-off-by: Jeff King <peff@peff.net> Helped-by: Sitaram Chamarty <sitaramc@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Oct 14, 2011 at 17:19 UTC d5570f4d2cb29f3db9f69ff5fcde0a7d54ad2696
2 files changed +31 -4
Documentation/git-daemon.txt
+10
@@ -161,6 +161,16 @@ the facility of inet daemon to achieve the same before spawning
161 repository configuration. By default, all the services
162 are overridable.
163
164 +--informative-errors::
165 +--no-informative-errors::
166 + When informative errors are turned on, git-daemon will report
167 + more verbose errors to the client, differentiating conditions
168 + like "no such repository" from "repository not exported". This
169 + is more convenient for clients, but may leak information about
170 + the existence of unexported repositories. When informative
171 + errors are not enabled, all errors report "access denied" to the
172 + client. The default is --no-informative-errors.
173 +
174 <directory>::
175 A directory to add to the whitelist of allowed directories. Unless
176 --strict-paths is specified this will also include subdirectories
daemon.c
+21 -4
@@ -20,6 +20,7 @@
20 static int log_syslog;
21 static int verbose;
22 static int reuseaddr;
23 +static int informative_errors;
24
25 static const char daemon_usage[] =
26 "git daemon [--verbose] [--syslog] [--export-all]\n"
@@ -247,6 +248,14 @@ static int git_daemon_config(const char *var, const char *value, void *cb)
248 return 0;
249 }
250
251 +static int daemon_error(const char *dir, const char *msg)
252 +{
253 + if (!informative_errors)
254 + msg = "access denied or repository not exported";
255 + packet_write(1, "ERR %s: %s", msg, dir);
256 + return -1;
257 +}
258 +
259 static int run_service(char *dir, struct daemon_service *service)
260 {
261 const char *path;
@@ -257,11 +266,11 @@ static int run_service(char *dir, struct daemon_service *service)
266 if (!enabled && !service->overridable) {
267 logerror("'%s': service not enabled.", service->name);
268 errno = EACCES;
260 - return -1;
269 + return daemon_error(dir, "service not enabled");
270 }
271
272 if (!(path = path_ok(dir)))
264 - return -1;
273 + return daemon_error(dir, "no such repository");
274
275 /*
276 * Security on the cheap.
@@ -277,7 +286,7 @@ static int run_service(char *dir, struct daemon_service *service)
286 if (!export_all_trees && access("git-daemon-export-ok", F_OK)) {
287 logerror("'%s': repository not exported.", path);
288 errno = EACCES;
280 - return -1;
289 + return daemon_error(dir, "repository not exported");
290 }
291
292 if (service->overridable) {
@@ -291,7 +300,7 @@ static int run_service(char *dir, struct daemon_service *service)
300 logerror("'%s': service not enabled for '%s'",
301 service->name, path);
302 errno = EACCES;
294 - return -1;
303 + return daemon_error(dir, "service not enabled");
304 }
305
306 /*
@@ -1167,6 +1176,14 @@ int main(int argc, char **argv)
1176 make_service_overridable(arg + 18, 0);
1177 continue;
1178 }
1179 + if (!prefixcmp(arg, "--informative-errors")) {
1180 + informative_errors = 1;
1181 + continue;
1182 + }
1183 + if (!prefixcmp(arg, "--no-informative-errors")) {
1184 + informative_errors = 0;
1185 + continue;
1186 + }
1187 if (!strcmp(arg, "--")) {
1188 ok_paths = &argv[i+1];
1189 break;