From 77d604c30974587a2e0d36d92c48cef87f6e8e4d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Oct 2005 14:46:10 -0700 Subject: Enhanced sq_quote() Create function to sq_quote into a buffer Handle !'s for csh-based shells Signed-off-by: H. Peter Anvin Signed-off-by: Junio C Hamano --- quote.c | 56 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 22 deletions(-) (limited to 'quote.c') diff --git a/quote.c b/quote.c index 5e6fda311c..9d5d0bcb0f 100644 --- a/quote.c +++ b/quote.c @@ -2,40 +2,52 @@ #include "quote.h" /* Help to copy the thing properly quoted for the shell safety. - * any single quote is replaced with '\'', and the caller is - * expected to enclose the result within a single quote pair. + * any single quote is replaced with '\'', any exclamation point + * is replaced with '\!', and the whole thing is enclosed in a * * E.g. * original sq_quote result * name ==> name ==> 'name' * a b ==> a b ==> 'a b' * a'b ==> a'\''b ==> 'a'\''b' + * a!b ==> a'\!'b ==> 'a'\!'b' */ -char *sq_quote(const char *src) -{ - static char *buf = NULL; - int cnt, c; - const char *cp; - char *bp; +#define EMIT(x) ( (++len < n) && (*bp++ = (x)) ) - /* count bytes needed to store the quoted string. */ - for (cnt = 3, cp = src; *cp; cnt++, cp++) - if (*cp == '\'') - cnt += 3; +size_t sq_quote_buf(char *dst, size_t n, const char *src) +{ + char c; + char *bp = dst; + size_t len = 0; - buf = xmalloc(cnt); - bp = buf; - *bp++ = '\''; + EMIT('\''); while ((c = *src++)) { - if (c != '\'') - *bp++ = c; - else { - bp = strcpy(bp, "'\\''"); - bp += 4; + if (c == '\'' || c == '!') { + EMIT('\''); + EMIT('\\'); + EMIT(c); + EMIT('\''); + } else { + EMIT(c); } } - *bp++ = '\''; - *bp = 0; + EMIT('\''); + + if ( n ) + *bp = 0; + + return len; +} + +char *sq_quote(const char *src) +{ + char *buf; + size_t cnt; + + cnt = sq_quote_buf(NULL, 0, src) + 1; + buf = xmalloc(cnt); + sq_quote_buf(buf, cnt, src); + return buf; } -- cgit v1.3-5-g45d5 From 4f6fbcdcf960b5afaeb2f5010be61b919c1805fd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 14 Oct 2005 21:54:47 -0700 Subject: Functions to quote and unquote pathnames in C-style. Following the list discussion, define two functions, quote_c_style and unquote_c_style, to help adopting the proposed way for quoting funny pathname letters for GNU patch. The rule is described in: http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2 Currently we do not support the leading '!', but we probably should barf upon seeing it. Rule B4. is interpreted to require always 3 octal digits in \XYZ notation. Signed-off-by: Junio C Hamano --- quote.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ quote.h | 12 ++++- 2 files changed, 175 insertions(+), 2 deletions(-) (limited to 'quote.c') diff --git a/quote.c b/quote.c index 9d5d0bcb0f..7df05a9c7c 100644 --- a/quote.c +++ b/quote.c @@ -12,6 +12,7 @@ * a'b ==> a'\''b ==> 'a'\''b' * a!b ==> a'\!'b ==> 'a'\!'b' */ +#undef EMIT #define EMIT(x) ( (++len < n) && (*bp++ = (x)) ) size_t sq_quote_buf(char *dst, size_t n, const char *src) @@ -51,3 +52,167 @@ char *sq_quote(const char *src) return buf; } +/* + * C-style name quoting. + * + * Does one of three things: + * + * (1) if outbuf and outfp are both NULL, inspect the input name and + * counts the number of bytes that are needed to hold c_style + * quoted version of name, counting the double quotes around + * it but not terminating NUL, and returns it. However, if name + * does not need c_style quoting, it returns 0. + * + * (2) if outbuf is not NULL, it must point at a buffer large enough + * to hold the c_style quoted version of name, enclosing double + * quotes, and terminating NUL. Fills outbuf with c_style quoted + * version of name enclosed in double-quote pair. Return value + * is undefined. + * + * (3) if outfp is not NULL, outputs c_style quoted version of name, + * but not enclosed in double-quote pair. Return value is undefined. + */ + +int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq) +{ +#undef EMIT +#define EMIT(c) \ + (outbuf ? (*outbuf++ = (c)) : outfp ? fputc(c, outfp) : (count++)) + +#define EMITQ() EMIT('\\') + + const char *sp; + int ch, count = 0, needquote = 0; + + if (!no_dq) + EMIT('"'); + for (sp = name; (ch = *sp++); ) { + + if ((ch <= ' ') || (ch == '"') || + (ch == '\\') || (ch == 0177)) { + needquote = 1; + switch (ch) { + case '\a': EMITQ(); ch = 'a'; break; + case '\b': EMITQ(); ch = 'b'; break; + case '\f': EMITQ(); ch = 'f'; break; + case '\n': EMITQ(); ch = 'n'; break; + case '\r': EMITQ(); ch = 'r'; break; + case '\t': EMITQ(); ch = 't'; break; + case '\v': EMITQ(); ch = 'v'; break; + + case '\\': /* fallthru */ + case '"': EMITQ(); break; + case ' ': + break; + default: + /* octal */ + EMITQ(); + EMIT(((ch >> 6) & 03) + '0'); + EMIT(((ch >> 3) & 07) + '0'); + ch = (ch & 07) + '0'; + break; + } + } + EMIT(ch); + } + if (!no_dq) + EMIT('"'); + if (outbuf) + *outbuf = 0; + + return needquote ? count : 0; +} + +/* + * C-style name unquoting. + * + * Quoted should point at the opening double quote. Returns + * an allocated memory that holds unquoted name, which the caller + * should free when done. Updates endp pointer to point at + * one past the ending double quote if given. + */ + +char *unquote_c_style(const char *quoted, const char **endp) +{ + const char *sp; + char *name = NULL, *outp = NULL; + int count = 0, ch, ac; + +#undef EMIT +#define EMIT(c) (outp ? (*outp++ = (c)) : (count++)) + + if (*quoted++ != '"') + return NULL; + + while (1) { + /* first pass counts and allocates, second pass fills */ + for (sp = quoted; (ch = *sp++) != '"'; ) { + if (ch == '\\') { + switch (ch = *sp++) { + case 'a': ch = '\a'; break; + case 'b': ch = '\b'; break; + case 'f': ch = '\f'; break; + case 'n': ch = '\n'; break; + case 'r': ch = '\r'; break; + case 't': ch = '\t'; break; + case 'v': ch = '\v'; break; + + case '\\': case '"': + break; /* verbatim */ + + case '0'...'7': + /* octal */ + ac = ((ch - '0') << 6); + if ((ch = *sp++) < '0' || '7' < ch) + return NULL; + ac |= ((ch - '0') << 3); + if ((ch = *sp++) < '0' || '7' < ch) + return NULL; + ac |= (ch - '0'); + ch = ac; + break; + default: + return NULL; /* malformed */ + } + } + EMIT(ch); + } + + if (name) { + *outp = 0; + if (endp) + *endp = sp; + return name; + } + outp = name = xmalloc(count + 1); + } +} + +void write_name_quoted(const char *prefix, const char *name, + int quote, FILE *out) +{ + int needquote; + + if (!quote) { + no_quote: + if (prefix && prefix[0]) + fputs(prefix, out); + fputs(name, out); + return; + } + + needquote = 0; + if (prefix && prefix[0]) + needquote = quote_c_style(prefix, NULL, NULL, 0); + if (!needquote) + needquote = quote_c_style(name, NULL, NULL, 0); + if (needquote) { + fputc('"', out); + if (prefix && prefix[0]) + quote_c_style(prefix, NULL, out, 1); + quote_c_style(name, NULL, out, 1); + fputc('"', out); + } + else + goto no_quote; +} diff --git a/quote.h b/quote.h index 50ce1df976..2fdde3b66b 100644 --- a/quote.h +++ b/quote.h @@ -2,6 +2,7 @@ #define QUOTE_H #include +#include /* Help to copy the thing properly quoted for the shell safety. * any single quote is replaced with '\'', any exclamation point @@ -27,7 +28,14 @@ * excluding the final null regardless of the buffer size. */ -char *sq_quote(const char *src); -size_t sq_quote_buf(char *dst, size_t n, const char *src); +extern char *sq_quote(const char *src); +extern size_t sq_quote_buf(char *dst, size_t n, const char *src); + +extern int quote_c_style(const char *name, char *outbuf, FILE *outfp, + int nodq); +extern char *unquote_c_style(const char *quoted, const char **endp); + +extern void write_name_quoted(const char *prefix, const char *name, + int quote, FILE *out); #endif -- cgit v1.3-5-g45d5 From 28fba290e3868a41e36379906407cf318cedfe57 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 17 Oct 2005 13:32:03 -0700 Subject: Do not quote SP. Follow the "encode minimally" principle -- our tools, including git-apply and git-status, can handle pathnames with embedded SP just fine. The only problematic ones are TAB and LF, and we need to quote the metacharacters introduced for quoting. Signed-off-by: Junio C Hamano --- quote.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'quote.c') diff --git a/quote.c b/quote.c index 7df05a9c7c..009e69494b 100644 --- a/quote.c +++ b/quote.c @@ -88,8 +88,8 @@ int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq) EMIT('"'); for (sp = name; (ch = *sp++); ) { - if ((ch <= ' ') || (ch == '"') || - (ch == '\\') || (ch == 0177)) { + if ((ch < ' ') || (ch == '"') || (ch == '\\') || + (ch == 0177)) { needquote = 1; switch (ch) { case '\a': EMITQ(); ch = 'a'; break; -- cgit v1.3-5-g45d5 From 35eb2d36418a25f9e956b116a622d8d99f4abad9 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 23 Oct 2005 14:30:45 -0700 Subject: Add git-shell. This adds a very git specific restricted shell, that can be added to /etc/shells and set to the pw_shell in the /etc/passwd file, to give users ability to push into repositories over ssh without giving them full interactive shell acount. [jc: I updated Linus' patch to match what the current sq_quote() does.] Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 2 +- quote.c | 41 ++++++++++++++++++++++++++++++++++++++++- quote.h | 6 ++++++ shell.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 shell.c (limited to 'quote.c') diff --git a/Makefile b/Makefile index 5bdf3cc8dd..5b0306d391 100644 --- a/Makefile +++ b/Makefile @@ -116,7 +116,7 @@ PROGRAMS = \ git-merge-index$X git-mktag$X git-pack-objects$X git-patch-id$X \ git-peek-remote$X git-prune-packed$X git-read-tree$X \ git-receive-pack$X git-rev-list$X git-rev-parse$X \ - git-send-pack$X git-show-branch$X \ + git-send-pack$X git-show-branch$X git-shell$X \ git-show-index$X git-ssh-fetch$X \ git-ssh-upload$X git-tar-tree$X git-unpack-file$X \ git-unpack-objects$X git-update-index$X git-update-server-info$X \ diff --git a/quote.c b/quote.c index 009e69494b..e662a7da71 100644 --- a/quote.c +++ b/quote.c @@ -15,6 +15,11 @@ #undef EMIT #define EMIT(x) ( (++len < n) && (*bp++ = (x)) ) +static inline int need_bs_quote(char c) +{ + return (c == '\'' || c == '!'); +} + size_t sq_quote_buf(char *dst, size_t n, const char *src) { char c; @@ -23,7 +28,7 @@ size_t sq_quote_buf(char *dst, size_t n, const char *src) EMIT('\''); while ((c = *src++)) { - if (c == '\'' || c == '!') { + if (need_bs_quote(c)) { EMIT('\''); EMIT('\\'); EMIT(c); @@ -52,6 +57,40 @@ char *sq_quote(const char *src) return buf; } +char *sq_dequote(char *arg) +{ + char *dst = arg; + char *src = arg; + char c; + + if (*src != '\'') + return NULL; + for (;;) { + c = *++src; + if (!c) + return NULL; + if (c != '\'') { + *dst++ = c; + continue; + } + /* We stepped out of sq */ + switch (*++src) { + case '\0': + *dst = 0; + return arg; + case '\\': + c = *++src; + if (need_bs_quote(c) && *++src == '\'') { + *dst++ = c; + continue; + } + /* Fallthrough */ + default: + return NULL; + } + } +} + /* * C-style name quoting. * diff --git a/quote.h b/quote.h index 2fdde3b66b..2486e6e68c 100644 --- a/quote.h +++ b/quote.h @@ -31,6 +31,12 @@ extern char *sq_quote(const char *src); extern size_t sq_quote_buf(char *dst, size_t n, const char *src); +/* This unwraps what sq_quote() produces in place, but returns + * NULL if the input does not look like what sq_quote would have + * produced. + */ +extern char *sq_dequote(char *); + extern int quote_c_style(const char *name, char *outbuf, FILE *outfp, int nodq); extern char *unquote_c_style(const char *quoted, const char **endp); diff --git a/shell.c b/shell.c new file mode 100644 index 0000000000..2c4789e944 --- /dev/null +++ b/shell.c @@ -0,0 +1,59 @@ +#include "cache.h" +#include "quote.h" + +static int do_generic_cmd(const char *me, char *arg) +{ + const char *my_argv[4]; + + arg = sq_dequote(arg); + if (!arg) + die("bad argument"); + + my_argv[0] = me; + my_argv[1] = arg; + my_argv[2] = NULL; + + return execvp(me, (char**) my_argv); +} + +static struct commands { + const char *name; + int (*exec)(const char *me, char *arg); +} cmd_list[] = { + { "git-receive-pack", do_generic_cmd }, + { "git-upload-pack", do_generic_cmd }, + { NULL }, +}; + +int main(int argc, char **argv) +{ + char *prog; + struct commands *cmd; + + /* We want to see "-c cmd args", and nothing else */ + if (argc != 3 || strcmp(argv[1], "-c")) + die("What do you think I am? A shell?"); + + prog = argv[2]; + argv += 2; + argc -= 2; + for (cmd = cmd_list ; cmd->name ; cmd++) { + int len = strlen(cmd->name); + char *arg; + if (strncmp(cmd->name, prog, len)) + continue; + arg = NULL; + switch (prog[len]) { + case '\0': + arg = NULL; + break; + case ' ': + arg = prog + len + 1; + break; + default: + continue; + } + exit(cmd->exec(cmd->name, arg)); + } + die("unrecognized command '%s'", prog); +} -- cgit v1.3-5-g45d5