{"thread":{"id":"40424","subject":"[PATCH v2 0/68] war on sprintf","startedAt":"2015-09-24T21:02:25Z","lastAt":"2015-10-06T03:24:33Z","messageCount":93,"participants":["Jeff King","Eric Sunshine","René Scharfe","Torsten Bögershausen","Rasmus Villemoes","Michael Blume","Junio C Hamano"],"isPatch":true,"patchVersion":2,"patchTotal":68},"messages":[{"id":"270649","messageId":"20150924210225.GA23624@sigill.intra.peff.net","threadId":"40424","inReplyTo":null,"subject":"[PATCH v2 0/68] war on sprintf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:02:25Z","receivedAt":"2015-09-24T21:02:25Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This is a revised version of the series I sent earlier[1].\n\nFor those just joining us, the goal is to remove sprintf, strcpy, etc,\nto make it easier to audit the code base for buffer overflows. I've been\naddressing review comments for individual patches as the discussion\nprogressed, so there's nothing here that hasn't been on the list (and I\nthink I've addressed all of the comments from the first round of\nreview).\n\nHere's a list of the commits, with an overview[2] of the changes from v1\n(patches without comment are the same as in v1):\n\n  [01/68]: show-branch: avoid segfault with --reflog of unborn branch\n  [02/68]: mailsplit: fix FILE* leak in split_maildir\n  [03/68]: archive-tar: fix minor indentation violation\n  [04/68]: fsck: don't fsck alternates for connectivity-only check\n  [05/68]: add xsnprintf helper function\n  [06/68]: add git_path_buf helper function\n  [07/68]: strbuf: make strbuf_complete_line more generic\n\n    Docstring now makes clear the behavior when the strbuf is empty.\n\n  [08/68]: add reentrant variants of sha1_to_hex and find_unique_abbrev\n\n    These are now called sha1_to_hex_r, etc. The re-entrancy conditions\n    are documented.\n\n  [09/68]: fsck: use strbuf to generate alternate directories\n  [10/68]: mailsplit: make PATH_MAX buffers dynamic\n\n    Use xstrfmt consistently instead of sometimes using a strbuf.\n\n  [11/68]: trace: use strbuf for quote_crnl output\n\n    Further cleanup of \"p2\" variable in quote_crnl.\n\n  [12/68]: progress: store throughput display in a strbuf\n  [13/68]: test-dump-cache-tree: avoid overflow of cache-tree name\n  [14/68]: compat/inet_ntop: fix off-by-one in inet_ntop4\n  [15/68]: convert trivial sprintf / strcpy calls to xsnprintf\n\n    Prefer \"fixed-size\" to \"static\" for clarity in commit message.\n\n  [16/68]: archive-tar: use xsnprintf for trivial formatting\n  [17/68]: use xsnprintf for generating git object headers\n  [18/68]: find_short_object_filename: convert sprintf to xsnprintf\n  [19/68]: stop_progress_msg: convert sprintf to xsnprintf\n\n    Ditto on \"fixed-size\" versus \"static\".\n\n  [20/68]: compat/hstrerror: convert sprintf to snprintf\n  [21/68]: grep: use xsnprintf to format failure message\n  [22/68]: entry.c: convert strcpy to xsnprintf\n  [23/68]: add_packed_git: convert strcpy into xsnprintf\n\n    Drop useless comment. Add comment on magic strlen().\n\n  [24/68]: http-push: replace strcat with xsnprintf\n  [25/68]: receive-pack: convert strncpy to xsnprintf\n  [26/68]: replace trivial malloc + sprintf / strcpy calls with xstrfmt\n\n    Include NUL in base64 of imap-send cram response. My guess is this\n    is a bug in what we send that is overlooked by most servers, but I'd\n    prefer to be conservative here and keep the behavior the same.\n\n  [27/68]: config: use xstrfmt in normalize_value\n  [28/68]: fetch: replace static buffer with xstrfmt\n  [29/68]: use strip_suffix and xstrfmt to replace suffix\n  [30/68]: ref-filter: drop sprintf and strcpy calls\n  [31/68]: help: drop prepend function in favor of xstrfmt\n  [32/68]: mailmap: replace strcpy with xstrdup\n  [33/68]: read_branches_file: simplify string handling\n\n    This goes much further than the original in cleaning up the use of a\n    static buffer. From what I sent earlier during review, I dropped\n    strbuf_read_file in favor of strbuf_getline, to keep the behavior\n    identical to the original.\n\n  [34/68]: read_remotes_file: simplify string handling\n\n    New in this iteration; cleanups to match those in 33/68.\n\n  [35/68]: resolve_ref: use strbufs for internal buffers\n  [36/68]: upload-archive: convert sprintf to strbuf\n  [37/68]: remote-ext: simplify git pkt-line generation\n\n    The v1 of this patch was totally buggy. This is a rewrite to use\n    packet_write(), which has several advantages, and includes tests.\n\n  [38/68]: http-push: use strbuf instead of fwrite_buffer\n  [39/68]: http-walker: store url in a strbuf\n  [40/68]: sha1_get_pack_name: use a strbuf\n  [41/68]: init: use strbufs to store paths\n  [42/68]: apply: convert root string to strbuf\n  [43/68]: transport: use strbufs for status table \"quickref\" strings\n  [44/68]: merge-recursive: convert malloc / strcpy to strbuf\n  [45/68]: enter_repo: convert fixed-size buffers to strbufs\n  [46/68]: remove_leading_path: use a strbuf for internal storage\n  [47/68]: write_loose_object: convert to strbuf\n\n    Clarify comment on subtle mkstemp behavior.\n\n  [48/68]: diagnose_invalid_index_path: use strbuf to avoid strcpy/strcat\n  [49/68]: fetch-pack: use argv_array for index-pack / unpack-objects\n  [50/68]: http-push: use an argv_array for setup_revisions\n  [51/68]: stat_tracking_info: convert to argv_array\n  [52/68]: daemon: use cld->env_array when re-spawning\n  [53/68]: use sha1_to_hex_r() instead of strcpy\n\n    Use \"_r\" versions to match change in patch 8.\n\n  [54/68]: drop strcpy in favor of raw sha1_to_hex\n\n    More history in the commit message, courtesy of Eric.\n\n  [55/68]: color: add overflow checks for parsing colors\n\n    Be more careful with OUT() macro.\n\n  [56/68]: use alloc_ref rather than hand-allocating \"struct ref\"\n  [57/68]: avoid sprintf and strcpy with flex arrays\n\n    Trivial typo-fix in comment.\n\n  [58/68]: receive-pack: simplify keep_arg computation\n\n    Another s/static/fixed-size/.\n\n  [59/68]: help: clean up kfmclient munging\n  [60/68]: prefer memcpy to strcpy\n  [61/68]: color: add color_set helper for copying raw colors\n  [62/68]: notes: document length of fanout path with a constant\n  [63/68]: convert strncpy to memcpy\n  [64/68]: fsck: drop inode-sorting code\n  [65/68]: Makefile: drop D_INO_IN_DIRENT build knob\n  [66/68]: fsck: use for_each_loose_file_in_objdir\n  [67/68]: use strbuf_complete to conditionally append slash\n\n    Drop bogus conversion from imap-send.\n\n  [68/68]: name-rev: use strip_suffix to avoid magic numbers\n\nHopefully that makes reviewing v2 a little less painful for those who\nalready slogged through v1. Thanks for your patience. :)\n\n-Peff\n\n[1] Gmane seems down, but once it's back up you should be able to get to\n    v1 at: http://mid.gmane.org/20150915152125.GA27504@sigill.intra.peff.net\n\n[2] Keeping track of the tweaks to this many patches would have been\n    near impossible without Thomas Rast's git-tbdiff tool:\n\n      https://github.com/trast/tbdiff\n\n    I highly recommend it for people handling multiple versions of a\n    long series.\n"},{"id":"270650","messageId":"20150924210253.GA30744@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 01/68] show-branch: avoid segfault with --reflog of unborn branch","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:02:54Z","receivedAt":"2015-09-24T21:02:54Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When no branch is given to the \"--reflog\" option, we resolve\nHEAD to get the default branch. However, if HEAD points to\nan unborn branch, resolve_ref returns NULL, and we later\nsegfault trying to access it.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/show-branch.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/builtin/show-branch.c b/builtin/show-branch.c\nindex 408ce70..092b59b 100644\n--- a/builtin/show-branch.c\n+++ b/builtin/show-branch.c\n@@ -743,6 +743,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)\n \t\t\tfake_av[1] = NULL;\n \t\t\tav = fake_av;\n \t\t\tac = 1;\n+\t\t\tif (!*av)\n+\t\t\t\tdie(\"no branches given, and HEAD is not valid\");\n \t\t}\n \t\tif (ac != 1)\n \t\t\tdie(\"--reflog option needs one branch name\");\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270651","messageId":"20150924210305.GB30744@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 02/68] mailsplit: fix FILE* leak in split_maildir","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:03:05Z","receivedAt":"2015-09-24T21:03:05Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"If we encounter an error while splitting a maildir, we exit\nthe function early, leaking the open filehandle. This isn't\na big deal, since we exit the program soon after, but it's\neasy enough to be careful.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/mailsplit.c | 5 ++++-\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/mailsplit.c b/builtin/mailsplit.c\nindex 8e02ea1..9de06e3 100644\n--- a/builtin/mailsplit.c\n+++ b/builtin/mailsplit.c\n@@ -150,6 +150,7 @@ static int split_maildir(const char *maildir, const char *dir,\n {\n \tchar file[PATH_MAX];\n \tchar name[PATH_MAX];\n+\tFILE *f = NULL;\n \tint ret = -1;\n \tint i;\n \tstruct string_list list = STRING_LIST_INIT_DUP;\n@@ -160,7 +161,6 @@ static int split_maildir(const char *maildir, const char *dir,\n \t\tgoto out;\n \n \tfor (i = 0; i < list.nr; i++) {\n-\t\tFILE *f;\n \t\tsnprintf(file, sizeof(file), \"%s/%s\", maildir, list.items[i].string);\n \t\tf = fopen(file, \"r\");\n \t\tif (!f) {\n@@ -177,10 +177,13 @@ static int split_maildir(const char *maildir, const char *dir,\n \t\tsplit_one(f, name, 1);\n \n \t\tfclose(f);\n+\t\tf = NULL;\n \t}\n \n \tret = skip;\n out:\n+\tif (f)\n+\t\tfclose(f);\n \tstring_list_clear(&list, 1);\n \treturn ret;\n }\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270652","messageId":"20150924210349.GC30744@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 03/68] archive-tar: fix minor indentation violation","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:03:49Z","receivedAt":"2015-09-24T21:03:49Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This looks like a simple omission from 8539070 (archive-tar:\nunindent write_tar_entry by one level, 2012-05-03).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n archive-tar.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/archive-tar.c b/archive-tar.c\nindex 0d1e6bd..b6b30bb 100644\n--- a/archive-tar.c\n+++ b/archive-tar.c\n@@ -233,7 +233,7 @@ static int write_tar_entry(struct archiver_args *args,\n \t\tsize_t rest = pathlen - plen - 1;\n \t\tif (plen > 0 && rest <= sizeof(header.name)) {\n \t\t\tmemcpy(header.prefix, path, plen);\n-\t\t\t\tmemcpy(header.name, path + plen + 1, rest);\n+\t\t\tmemcpy(header.name, path + plen + 1, rest);\n \t\t} else {\n \t\t\tsprintf(header.name, \"%s.data\",\n \t\t\t\tsha1_to_hex(sha1));\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270653","messageId":"20150924210530.GA30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 04/68] fsck: don't fsck alternates for connectivity-only check","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:05:30Z","receivedAt":"2015-09-24T21:05:30Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Commit 02976bf (fsck: introduce `git fsck --connectivity-only`,\n2015-06-22) recently gave fsck an option to perform only a\nsubset of the checks, by skipping the fsck_object_dir()\ncall. However, it does so only for the local object\ndirectory, and we still do expensive checks on any alternate\nrepos. We should skip them in this case, too.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/fsck.c | 17 +++++++++--------\n 1 file changed, 9 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 0794703..46c7235 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -678,16 +678,17 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \tgit_config(fsck_config, NULL);\n \n \tfsck_head_link();\n-\tif (!connectivity_only)\n+\tif (!connectivity_only) {\n \t\tfsck_object_dir(get_object_directory());\n \n-\tprepare_alt_odb();\n-\tfor (alt = alt_odb_list; alt; alt = alt->next) {\n-\t\tchar namebuf[PATH_MAX];\n-\t\tint namelen = alt->name - alt->base;\n-\t\tmemcpy(namebuf, alt->base, namelen);\n-\t\tnamebuf[namelen - 1] = 0;\n-\t\tfsck_object_dir(namebuf);\n+\t\tprepare_alt_odb();\n+\t\tfor (alt = alt_odb_list; alt; alt = alt->next) {\n+\t\t\tchar namebuf[PATH_MAX];\n+\t\t\tint namelen = alt->name - alt->base;\n+\t\t\tmemcpy(namebuf, alt->base, namelen);\n+\t\t\tnamebuf[namelen - 1] = 0;\n+\t\t\tfsck_object_dir(namebuf);\n+\t\t}\n \t}\n \n \tif (check_full) {\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270654","messageId":"20150924210537.GB30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 05/68] add xsnprintf helper function","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:05:37Z","receivedAt":"2015-09-24T21:05:37Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"There are a number of places in the code where we call\nsprintf(), with the assumption that the output will fit into\nthe buffer. In many cases this is true (e.g., formatting a\nnumber into a large buffer), but it is hard to tell\nimmediately from looking at the code. It would be nice if we\nhad some run-time check to make sure that our assumption is\ncorrect (and to communicate to readers of the code that we\nare not blindly calling sprintf, but have actually thought\nabout this case).\n\nThis patch introduces xsnprintf, which behaves just like\nsnprintf, except that it dies whenever the output is\ntruncated. This acts as a sort of assert() for these cases,\nwhich can help find places where the assumption is violated\n(as opposed to truncating and proceeding, which may just\nsilently give a wrong answer).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n git-compat-util.h |  3 +++\n wrapper.c         | 16 ++++++++++++++++\n 2 files changed, 19 insertions(+)\n\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex f649e81..348b9dc 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -744,6 +744,9 @@ static inline size_t xsize_t(off_t len)\n \treturn (size_t)len;\n }\n \n+__attribute__((format (printf, 3, 4)))\n+extern int xsnprintf(char *dst, size_t max, const char *fmt, ...);\n+\n /* in ctype.c, for kwset users */\n extern const unsigned char tolower_trans_tbl[256];\n \ndiff --git a/wrapper.c b/wrapper.c\nindex 0e22d43..6fcaa4d 100644\n--- a/wrapper.c\n+++ b/wrapper.c\n@@ -621,6 +621,22 @@ char *xgetcwd(void)\n \treturn strbuf_detach(&sb, NULL);\n }\n \n+int xsnprintf(char *dst, size_t max, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tint len;\n+\n+\tva_start(ap, fmt);\n+\tlen = vsnprintf(dst, max, fmt, ap);\n+\tva_end(ap);\n+\n+\tif (len < 0)\n+\t\tdie(\"BUG: your snprintf is broken\");\n+\tif (len >= max)\n+\t\tdie(\"BUG: attempt to snprintf into too-small buffer\");\n+\treturn len;\n+}\n+\n static int write_file_v(const char *path, int fatal,\n \t\t\tconst char *fmt, va_list params)\n {\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270655","messageId":"20150924210540.GC30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 06/68] add git_path_buf helper function","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:05:40Z","receivedAt":"2015-09-24T21:05:40Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"If you have a function that uses git_path a lot, but would\nprefer to avoid the static buffers, it's useful to keep a\nsingle scratch buffer locally and reuse it for each call.\nYou used to be able to do this with git_snpath:\n\n  char buf[PATH_MAX];\n\n  foo(git_snpath(buf, sizeof(buf), \"foo\"));\n  bar(git_snpath(buf, sizeof(buf), \"bar\"));\n\nbut since 1a83c24, git_snpath has been replaced with\nstrbuf_git_path. This is good, because it removes the\narbitrary PATH_MAX limit. But using strbuf_git_path is more\nawkward for two reasons:\n\n  1. It adds to the buffer, rather than replacing it. This\n     is consistent with other strbuf functions, but makes\n     reuse of a single buffer more tedious.\n\n  2. It doesn't return the buffer, so you can't format\n     as part of a function's arguments.\n\nThe new git_path_buf solves both of these, so you can use it\nlike:\n\n  struct strbuf buf = STRBUF_INIT;\n\n  foo(git_path_buf(&buf, \"foo\"));\n  bar(git_path_buf(&buf, \"bar\"));\n\n  strbuf_release(&buf);\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n cache.h |  2 ++\n path.c  | 10 ++++++++++\n 2 files changed, 12 insertions(+)\n\ndiff --git a/cache.h b/cache.h\nindex 79066e5..e231e47 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -723,6 +723,8 @@ extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)\n \t__attribute__((format (printf, 3, 4)));\n extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)\n \t__attribute__((format (printf, 2, 3)));\n+extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)\n+\t__attribute__((format (printf, 2, 3)));\n extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,\n \t\t\t\t      const char *fmt, ...)\n \t__attribute__((format (printf, 3, 4)));\ndiff --git a/path.c b/path.c\nindex 95acbaf..46a4d27 100644\n--- a/path.c\n+++ b/path.c\n@@ -175,6 +175,16 @@ static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)\n \tstrbuf_cleanup_path(buf);\n }\n \n+char *git_path_buf(struct strbuf *buf, const char *fmt, ...)\n+{\n+\tva_list args;\n+\tstrbuf_reset(buf);\n+\tva_start(args, fmt);\n+\tdo_git_path(buf, fmt, args);\n+\tva_end(args);\n+\treturn buf->buf;\n+}\n+\n void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)\n {\n \tva_list args;\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270657","messageId":"20150924210542.GD30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 07/68] strbuf: make strbuf_complete_line more generic","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:05:43Z","receivedAt":"2015-09-24T21:05:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The strbuf_complete_line function makes sure that a buffer\nends in a newline. But we may want to do this for any\ncharacter (e.g., \"/\" on the end of a path). Let's factor out\na generic version, and keep strbuf_complete_line as a thin\nwrapper.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n strbuf.h | 15 +++++++++++++--\n 1 file changed, 13 insertions(+), 2 deletions(-)\n\ndiff --git a/strbuf.h b/strbuf.h\nindex aef2794..43f27c3 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -491,10 +491,21 @@ extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *\n  */\n extern void strbuf_addstr_xml_quoted(struct strbuf *sb, const char *s);\n \n+/**\n+ * \"Complete\" the contents of `sb` by ensuring that either it ends with the\n+ * character `term`, or it is empty.  This can be used, for example,\n+ * to ensure that text ends with a newline, but without creating an empty\n+ * blank line if there is no content in the first place.\n+ */\n+static inline void strbuf_complete(struct strbuf *sb, char term)\n+{\n+\tif (sb->len && sb->buf[sb->len - 1] != term)\n+\t\tstrbuf_addch(sb, term);\n+}\n+\n static inline void strbuf_complete_line(struct strbuf *sb)\n {\n-\tif (sb->len && sb->buf[sb->len - 1] != '\\n')\n-\t\tstrbuf_addch(sb, '\\n');\n+\tstrbuf_complete(sb, '\\n');\n }\n \n extern int strbuf_branchname(struct strbuf *sb, const char *name);\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270656","messageId":"20150924210545.GE30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 08/68] add reentrant variants of sha1_to_hex and find_unique_abbrev","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:05:45Z","receivedAt":"2015-09-24T21:05:45Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The sha1_to_hex and find_unique_abbrev functions always\nwrite into reusable static buffers. There are a few problems\nwith this:\n\n  - future calls overwrite our result. This is especially\n    annoying with find_unique_abbrev, which does not have a\n    ring of buffers, so you cannot even printf() a result\n    that has two abbreviated sha1s.\n\n  - if you want to put the result into another buffer, we\n    often strcpy, which looks suspicious when auditing for\n    overflows.\n\nThis patch introduces sha1_to_hex_r and find_unique_abbrev_r,\nwhich write into a user-provided buffer. Of course this is\njust punting on the overflow-auditing, as the buffer\nobviously needs to be GIT_SHA1_HEXSZ + 1 bytes. But it is\nmuch easier to audit, since that is a well-known size.\n\nWe retain the non-reentrant forms, which just become thin\nwrappers around the reentrant ones. This patch also adds a\nstrbuf variant of find_unique_abbrev, which will be handy in\nlater patches.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n cache.h     | 31 ++++++++++++++++++++++++++++++-\n hex.c       | 13 +++++++++----\n sha1_name.c | 16 +++++++++++-----\n strbuf.c    |  9 +++++++++\n strbuf.h    |  8 ++++++++\n 5 files changed, 67 insertions(+), 10 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex e231e47..030b880 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -785,7 +785,24 @@ extern char *sha1_pack_name(const unsigned char *sha1);\n  */\n extern char *sha1_pack_index_name(const unsigned char *sha1);\n \n-extern const char *find_unique_abbrev(const unsigned char *sha1, int);\n+/*\n+ * Return an abbreviated sha1 unique within this repository's object database.\n+ * The result will be at least `len` characters long, and will be NUL\n+ * terminated.\n+ *\n+ * The non-`_r` version returns a static buffer which will be overwritten by\n+ * subsequent calls.\n+ *\n+ * The `_r` variant writes to a buffer supplied by the caller, which must be at\n+ * least `GIT_SHA1_HEXSZ + 1` bytes. The return value is the number of bytes\n+ * written (excluding the NUL terminator).\n+ *\n+ * Note that while this version avoids the static buffer, it is not fully\n+ * reentrant, as it calls into other non-reentrant git code.\n+ */\n+extern const char *find_unique_abbrev(const unsigned char *sha1, int len);\n+extern int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len);\n+\n extern const unsigned char null_sha1[GIT_SHA1_RAWSZ];\n \n static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)\n@@ -1067,6 +1084,18 @@ extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);\n extern int get_sha1_hex(const char *hex, unsigned char *sha1);\n extern int get_oid_hex(const char *hex, struct object_id *sha1);\n \n+/*\n+ * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,\n+ * and writes the NUL-terminated output to the buffer `out`, which must be at\n+ * least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for\n+ * convenience.\n+ *\n+ * The non-`_r` variant returns a static buffer, but uses a ring of 4\n+ * buffers, making it safe to make multiple calls for a single statement, like:\n+ *\n+ *   printf(\"%s -> %s\", sha1_to_hex(one), sha1_to_hex(two));\n+ */\n+extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);\n extern char *sha1_to_hex(const unsigned char *sha1);\t/* static buffer result! */\n extern char *oid_to_hex(const struct object_id *oid);\t/* same static buffer as sha1_to_hex */\n \ndiff --git a/hex.c b/hex.c\nindex 899b74a..0519f85 100644\n--- a/hex.c\n+++ b/hex.c\n@@ -61,12 +61,10 @@ int get_oid_hex(const char *hex, struct object_id *oid)\n \treturn get_sha1_hex(hex, oid->hash);\n }\n \n-char *sha1_to_hex(const unsigned char *sha1)\n+char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)\n {\n-\tstatic int bufno;\n-\tstatic char hexbuffer[4][GIT_SHA1_HEXSZ + 1];\n \tstatic const char hex[] = \"0123456789abcdef\";\n-\tchar *buffer = hexbuffer[3 & ++bufno], *buf = buffer;\n+\tchar *buf = buffer;\n \tint i;\n \n \tfor (i = 0; i < GIT_SHA1_RAWSZ; i++) {\n@@ -79,6 +77,13 @@ char *sha1_to_hex(const unsigned char *sha1)\n \treturn buffer;\n }\n \n+char *sha1_to_hex(const unsigned char *sha1)\n+{\n+\tstatic int bufno;\n+\tstatic char hexbuffer[4][GIT_SHA1_HEXSZ + 1];\n+\treturn sha1_to_hex_r(hexbuffer[3 & ++bufno], sha1);\n+}\n+\n char *oid_to_hex(const struct object_id *oid)\n {\n \treturn sha1_to_hex(oid->hash);\ndiff --git a/sha1_name.c b/sha1_name.c\nindex da6874c..c58b477 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -368,14 +368,13 @@ int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)\n \treturn ds.ambiguous;\n }\n \n-const char *find_unique_abbrev(const unsigned char *sha1, int len)\n+int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)\n {\n \tint status, exists;\n-\tstatic char hex[41];\n \n-\tmemcpy(hex, sha1_to_hex(sha1), 40);\n+\tsha1_to_hex_r(hex, sha1);\n \tif (len == 40 || !len)\n-\t\treturn hex;\n+\t\treturn 40;\n \texists = has_sha1_file(sha1);\n \twhile (len < 40) {\n \t\tunsigned char sha1_ret[20];\n@@ -384,10 +383,17 @@ const char *find_unique_abbrev(const unsigned char *sha1, int len)\n \t\t    ? !status\n \t\t    : status == SHORT_NAME_NOT_FOUND) {\n \t\t\thex[len] = 0;\n-\t\t\treturn hex;\n+\t\t\treturn len;\n \t\t}\n \t\tlen++;\n \t}\n+\treturn len;\n+}\n+\n+const char *find_unique_abbrev(const unsigned char *sha1, int len)\n+{\n+\tstatic char hex[GIT_SHA1_HEXSZ + 1];\n+\tfind_unique_abbrev_r(hex, sha1, len);\n \treturn hex;\n }\n \ndiff --git a/strbuf.c b/strbuf.c\nindex 29df55b..f3c44fb 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -743,3 +743,12 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm)\n \t}\n \tstrbuf_setlen(sb, sb->len + len);\n }\n+\n+void strbuf_add_unique_abbrev(struct strbuf *sb, const unsigned char *sha1,\n+\t\t\t      int abbrev_len)\n+{\n+\tint r;\n+\tstrbuf_grow(sb, GIT_SHA1_HEXSZ + 1);\n+\tr = find_unique_abbrev_r(sb->buf + sb->len, sha1, abbrev_len);\n+\tstrbuf_setlen(sb, sb->len + r);\n+}\ndiff --git a/strbuf.h b/strbuf.h\nindex 43f27c3..0f9c8a7 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -475,6 +475,14 @@ static inline struct strbuf **strbuf_split(const struct strbuf *sb,\n extern void strbuf_list_free(struct strbuf **);\n \n /**\n+ * Add the abbreviation, as generated by find_unique_abbrev, of `sha1` to\n+ * the strbuf `sb`.\n+ */\n+extern void strbuf_add_unique_abbrev(struct strbuf *sb,\n+\t\t\t\t     const unsigned char *sha1,\n+\t\t\t\t     int abbrev_len);\n+\n+/**\n  * Launch the user preferred editor to edit a file and fill the buffer\n  * with the file's contents upon the user completing their editing. The\n  * third argument can be used to set the environment which the editor is\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270658","messageId":"20150924210548.GF30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 09/68] fsck: use strbuf to generate alternate directories","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:05:48Z","receivedAt":"2015-09-24T21:05:48Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When fsck-ing alternates, we make a copy of the alternate\ndirectory in a fixed PATH_MAX buffer. We memcpy directly,\nwithout any check whether we are overflowing the buffer.\nThis is OK if PATH_MAX is a true representation of the\nmaximum path on the system, because any path here will have\nalready been vetted by the alternates subsystem. But that is\nnot true on every system, so we should be more careful.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/fsck.c | 11 ++++++-----\n 1 file changed, 6 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 46c7235..a019f4a 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -683,11 +683,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \n \t\tprepare_alt_odb();\n \t\tfor (alt = alt_odb_list; alt; alt = alt->next) {\n-\t\t\tchar namebuf[PATH_MAX];\n-\t\t\tint namelen = alt->name - alt->base;\n-\t\t\tmemcpy(namebuf, alt->base, namelen);\n-\t\t\tnamebuf[namelen - 1] = 0;\n-\t\t\tfsck_object_dir(namebuf);\n+\t\t\t/* directory name, minus trailing slash */\n+\t\t\tsize_t namelen = alt->name - alt->base - 1;\n+\t\t\tstruct strbuf name = STRBUF_INIT;\n+\t\t\tstrbuf_add(&name, alt->base, namelen);\n+\t\t\tfsck_object_dir(name.buf);\n+\t\t\tstrbuf_release(&name);\n \t\t}\n \t}\n \n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270661","messageId":"20150924210551.GG30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 10/68] mailsplit: make PATH_MAX buffers dynamic","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:05:51Z","receivedAt":"2015-09-24T21:05:51Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"There are several PATH_MAX-sized buffers in mailsplit, along\nwith some questionable uses of sprintf.  These are not\nreally of security interest, as local mailsplit pathnames\nare not typically under control of an attacker, and you\ncould generally only overflow a few numbers at the end of a\npath that approaches PATH_MAX (a longer path would choke\nmailsplit long before). But it does not hurt to be careful,\nand as a bonus we lift some limits for systems with\ntoo-small PATH_MAX varibles.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/mailsplit.c | 34 +++++++++++++++++++++++-----------\n 1 file changed, 23 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/mailsplit.c b/builtin/mailsplit.c\nindex 9de06e3..104277a 100644\n--- a/builtin/mailsplit.c\n+++ b/builtin/mailsplit.c\n@@ -98,30 +98,37 @@ static int populate_maildir_list(struct string_list *list, const char *path)\n {\n \tDIR *dir;\n \tstruct dirent *dent;\n-\tchar name[PATH_MAX];\n+\tchar *name = NULL;\n \tchar *subs[] = { \"cur\", \"new\", NULL };\n \tchar **sub;\n+\tint ret = -1;\n \n \tfor (sub = subs; *sub; ++sub) {\n-\t\tsnprintf(name, sizeof(name), \"%s/%s\", path, *sub);\n+\t\tfree(name);\n+\t\tname = xstrfmt(\"%s/%s\", path, *sub);\n \t\tif ((dir = opendir(name)) == NULL) {\n \t\t\tif (errno == ENOENT)\n \t\t\t\tcontinue;\n \t\t\terror(\"cannot opendir %s (%s)\", name, strerror(errno));\n-\t\t\treturn -1;\n+\t\t\tgoto out;\n \t\t}\n \n \t\twhile ((dent = readdir(dir)) != NULL) {\n \t\t\tif (dent->d_name[0] == '.')\n \t\t\t\tcontinue;\n-\t\t\tsnprintf(name, sizeof(name), \"%s/%s\", *sub, dent->d_name);\n+\t\t\tfree(name);\n+\t\t\tname = xstrfmt(\"%s/%s\", *sub, dent->d_name);\n \t\t\tstring_list_insert(list, name);\n \t\t}\n \n \t\tclosedir(dir);\n \t}\n \n-\treturn 0;\n+\tret = 0;\n+\n+out:\n+\tfree(name);\n+\treturn ret;\n }\n \n static int maildir_filename_cmp(const char *a, const char *b)\n@@ -148,8 +155,7 @@ static int maildir_filename_cmp(const char *a, const char *b)\n static int split_maildir(const char *maildir, const char *dir,\n \tint nr_prec, int skip)\n {\n-\tchar file[PATH_MAX];\n-\tchar name[PATH_MAX];\n+\tchar *file = NULL;\n \tFILE *f = NULL;\n \tint ret = -1;\n \tint i;\n@@ -161,7 +167,11 @@ static int split_maildir(const char *maildir, const char *dir,\n \t\tgoto out;\n \n \tfor (i = 0; i < list.nr; i++) {\n-\t\tsnprintf(file, sizeof(file), \"%s/%s\", maildir, list.items[i].string);\n+\t\tchar *name;\n+\n+\t\tfree(file);\n+\t\tfile = xstrfmt(\"%s/%s\", maildir, list.items[i].string);\n+\n \t\tf = fopen(file, \"r\");\n \t\tif (!f) {\n \t\t\terror(\"cannot open mail %s (%s)\", file, strerror(errno));\n@@ -173,8 +183,9 @@ static int split_maildir(const char *maildir, const char *dir,\n \t\t\tgoto out;\n \t\t}\n \n-\t\tsprintf(name, \"%s/%0*d\", dir, nr_prec, ++skip);\n+\t\tname = xstrfmt(\"%s/%0*d\", dir, nr_prec, ++skip);\n \t\tsplit_one(f, name, 1);\n+\t\tfree(name);\n \n \t\tfclose(f);\n \t\tf = NULL;\n@@ -184,6 +195,7 @@ static int split_maildir(const char *maildir, const char *dir,\n out:\n \tif (f)\n \t\tfclose(f);\n+\tfree(file);\n \tstring_list_clear(&list, 1);\n \treturn ret;\n }\n@@ -191,7 +203,6 @@ out:\n static int split_mbox(const char *file, const char *dir, int allow_bare,\n \t\t      int nr_prec, int skip)\n {\n-\tchar name[PATH_MAX];\n \tint ret = -1;\n \tint peek;\n \n@@ -218,8 +229,9 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,\n \t}\n \n \twhile (!file_done) {\n-\t\tsprintf(name, \"%s/%0*d\", dir, nr_prec, ++skip);\n+\t\tchar *name = xstrfmt(\"%s/%0*d\", dir, nr_prec, ++skip);\n \t\tfile_done = split_one(f, name, allow_bare);\n+\t\tfree(name);\n \t}\n \n \tif (f != stdin)\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270660","messageId":"20150924210553.GH30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 11/68] trace: use strbuf for quote_crnl output","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:05:54Z","receivedAt":"2015-09-24T21:05:54Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When we output GIT_TRACE_SETUP paths, we quote any\nmeta-characters. But our buffer to hold the result is only\nPATH_MAX bytes, and we could double the size of the input\npath (if every character needs quoting). We could use a\n2*PATH_MAX buffer, if we assume the input will never be more\nthan PATH_MAX. But it's easier still to just switch to a\nstrbuf and not worry about whether the input can exceed\nPATH_MAX or not.\n\nThe original copied the \"p2\" pointer to \"p1\", advancing\nboth. Since this gets rid of \"p1\", let's also drop \"p2\",\nwhose name is now confusing. We can just advance the\noriginal \"path\" pointer.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n trace.c | 23 +++++++++++------------\n 1 file changed, 11 insertions(+), 12 deletions(-)\n\ndiff --git a/trace.c b/trace.c\nindex 7393926..4aeea60 100644\n--- a/trace.c\n+++ b/trace.c\n@@ -277,25 +277,24 @@ void trace_performance_fl(const char *file, int line, uint64_t nanos,\n \n static const char *quote_crnl(const char *path)\n {\n-\tstatic char new_path[PATH_MAX];\n-\tconst char *p2 = path;\n-\tchar *p1 = new_path;\n+\tstatic struct strbuf new_path = STRBUF_INIT;\n \n \tif (!path)\n \t\treturn NULL;\n \n-\twhile (*p2) {\n-\t\tswitch (*p2) {\n-\t\tcase '\\\\': *p1++ = '\\\\'; *p1++ = '\\\\'; break;\n-\t\tcase '\\n': *p1++ = '\\\\'; *p1++ = 'n'; break;\n-\t\tcase '\\r': *p1++ = '\\\\'; *p1++ = 'r'; break;\n+\tstrbuf_reset(&new_path);\n+\n+\twhile (*path) {\n+\t\tswitch (*path) {\n+\t\tcase '\\\\': strbuf_addstr(&new_path, \"\\\\\\\\\"); break;\n+\t\tcase '\\n': strbuf_addstr(&new_path, \"\\\\n\"); break;\n+\t\tcase '\\r': strbuf_addstr(&new_path, \"\\\\r\"); break;\n \t\tdefault:\n-\t\t\t*p1++ = *p2;\n+\t\t\tstrbuf_addch(&new_path, *path);\n \t\t}\n-\t\tp2++;\n+\t\tpath++;\n \t}\n-\t*p1 = '\\0';\n-\treturn new_path;\n+\treturn new_path.buf;\n }\n \n /* FIXME: move prefix to startup_info struct and get rid of this arg */\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270669","messageId":"20150924210556.GI30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 12/68] progress: store throughput display in a strbuf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:05:57Z","receivedAt":"2015-09-24T21:05:57Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Coverity noticed that we strncpy() into a fixed-size buffer\nwithout making sure that it actually ended up\nNUL-terminated. This is unlikely to be a bug in practice,\nsince throughput strings rarely hit 32 characters, but it\nwould be nice to clean it up.\n\nThe most obvious way to do so is to add a NUL-terminator.\nBut instead, this patch switches the fixed-size buffer out\nfor a strbuf. At first glance this seems much less\nefficient, until we realize that filling in the fixed-size\nbuffer is done by writing into a strbuf and copying the\nresult!\n\nBy writing straight to the buffer, we actually end up more\nefficient:\n\n  1. We avoid an extra copy of the bytes.\n\n  2. Rather than malloc/free each time progress is shown, we\n     can strbuf_reset and use the same buffer each time.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n progress.c | 18 ++++++++----------\n 1 file changed, 8 insertions(+), 10 deletions(-)\n\ndiff --git a/progress.c b/progress.c\nindex 2e31bec..a3efcfd 100644\n--- a/progress.c\n+++ b/progress.c\n@@ -25,7 +25,7 @@ struct throughput {\n \tunsigned int last_bytes[TP_IDX_MAX];\n \tunsigned int last_misecs[TP_IDX_MAX];\n \tunsigned int idx;\n-\tchar display[32];\n+\tstruct strbuf display;\n };\n \n struct progress {\n@@ -98,7 +98,7 @@ static int display(struct progress *progress, unsigned n, const char *done)\n \t}\n \n \tprogress->last_value = n;\n-\ttp = (progress->throughput) ? progress->throughput->display : \"\";\n+\ttp = (progress->throughput) ? progress->throughput->display.buf : \"\";\n \teol = done ? done : \"   \\r\";\n \tif (progress->total) {\n \t\tunsigned percent = n * 100 / progress->total;\n@@ -129,6 +129,7 @@ static int display(struct progress *progress, unsigned n, const char *done)\n static void throughput_string(struct strbuf *buf, off_t total,\n \t\t\t      unsigned int rate)\n {\n+\tstrbuf_reset(buf);\n \tstrbuf_addstr(buf, \", \");\n \tstrbuf_humanise_bytes(buf, total);\n \tstrbuf_addstr(buf, \" | \");\n@@ -141,7 +142,6 @@ void display_throughput(struct progress *progress, off_t total)\n \tstruct throughput *tp;\n \tuint64_t now_ns;\n \tunsigned int misecs, count, rate;\n-\tstruct strbuf buf = STRBUF_INIT;\n \n \tif (!progress)\n \t\treturn;\n@@ -154,6 +154,7 @@ void display_throughput(struct progress *progress, off_t total)\n \t\tif (tp) {\n \t\t\ttp->prev_total = tp->curr_total = total;\n \t\t\ttp->prev_ns = now_ns;\n+\t\t\tstrbuf_init(&tp->display, 0);\n \t\t}\n \t\treturn;\n \t}\n@@ -193,9 +194,7 @@ void display_throughput(struct progress *progress, off_t total)\n \ttp->last_misecs[tp->idx] = misecs;\n \ttp->idx = (tp->idx + 1) % TP_IDX_MAX;\n \n-\tthroughput_string(&buf, total, rate);\n-\tstrncpy(tp->display, buf.buf, sizeof(tp->display));\n-\tstrbuf_release(&buf);\n+\tthroughput_string(&tp->display, total, rate);\n \tif (progress->last_value != -1 && progress_update)\n \t\tdisplay(progress, progress->last_value, NULL);\n }\n@@ -250,12 +249,9 @@ void stop_progress_msg(struct progress **p_progress, const char *msg)\n \n \t\tbufp = (len < sizeof(buf)) ? buf : xmalloc(len + 1);\n \t\tif (tp) {\n-\t\t\tstruct strbuf strbuf = STRBUF_INIT;\n \t\t\tunsigned int rate = !tp->avg_misecs ? 0 :\n \t\t\t\t\ttp->avg_bytes / tp->avg_misecs;\n-\t\t\tthroughput_string(&strbuf, tp->curr_total, rate);\n-\t\t\tstrncpy(tp->display, strbuf.buf, sizeof(tp->display));\n-\t\t\tstrbuf_release(&strbuf);\n+\t\t\tthroughput_string(&tp->display, tp->curr_total, rate);\n \t\t}\n \t\tprogress_update = 1;\n \t\tsprintf(bufp, \", %s.\\n\", msg);\n@@ -264,6 +260,8 @@ void stop_progress_msg(struct progress **p_progress, const char *msg)\n \t\t\tfree(bufp);\n \t}\n \tclear_progress_signal();\n+\tif (progress->throughput)\n+\t\tstrbuf_release(&progress->throughput->display);\n \tfree(progress->throughput);\n \tfree(progress);\n }\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270659","messageId":"20150924210603.GJ30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 13/68] test-dump-cache-tree: avoid overflow of cache-tree name","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:06:03Z","receivedAt":"2015-09-24T21:06:03Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When dumping a cache-tree, we sprintf sub-tree names directly\ninto a fixed-size buffer, which can overflow. We can\ntrivially fix this by converting to xsnprintf to at least\nnotice and die.\n\nThis probably should handle arbitrary-sized names, but\nthere's not much point. It's used only by the test scripts,\nso the trivial fix is enough.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n test-dump-cache-tree.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/test-dump-cache-tree.c b/test-dump-cache-tree.c\nindex 54c0872..bb53c0a 100644\n--- a/test-dump-cache-tree.c\n+++ b/test-dump-cache-tree.c\n@@ -47,7 +47,7 @@ static int dump_cache_tree(struct cache_tree *it,\n \t\tstruct cache_tree_sub *rdwn;\n \n \t\trdwn = cache_tree_sub(ref, down->name);\n-\t\tsprintf(path, \"%s%.*s/\", pfx, down->namelen, down->name);\n+\t\txsnprintf(path, sizeof(path), \"%s%.*s/\", pfx, down->namelen, down->name);\n \t\tif (dump_cache_tree(down->cache_tree, rdwn->cache_tree, path))\n \t\t\terrs = 1;\n \t}\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270662","messageId":"20150924210605.GK30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 14/68] compat/inet_ntop: fix off-by-one in inet_ntop4","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:06:06Z","receivedAt":"2015-09-24T21:06:06Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Our compat inet_ntop4 function writes to a temporary buffer\nwith snprintf, and then uses strcpy to put the result into\nthe final \"dst\" buffer. We check the return value of\nsnprintf against the size of \"dst\", but fail to account for\nthe NUL terminator. As a result, we may overflow \"dst\" with\na single NUL. In practice, this doesn't happen because the\noutput of inet_ntop is limited, and we provide buffers that\nare way oversized.\n\nWe can fix the off-by-one check easily, but while we are\nhere let's also use strlcpy for increased safety, just in\ncase there are other bugs lurking.\n\nAs a side note, this compat code seems to be BSD-derived.\nSearching for \"vixie inet_ntop\" turns up NetBSD's latest\nversion of the same code, which has an identical fix (and\nswitches to strlcpy, too!).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n compat/inet_ntop.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/compat/inet_ntop.c b/compat/inet_ntop.c\nindex 90b7cc4..6830726 100644\n--- a/compat/inet_ntop.c\n+++ b/compat/inet_ntop.c\n@@ -53,11 +53,11 @@ inet_ntop4(const u_char *src, char *dst, size_t size)\n \tnprinted = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);\n \tif (nprinted < 0)\n \t\treturn (NULL);\t/* we assume \"errno\" was set by \"snprintf()\" */\n-\tif ((size_t)nprinted > size) {\n+\tif ((size_t)nprinted >= size) {\n \t\terrno = ENOSPC;\n \t\treturn (NULL);\n \t}\n-\tstrcpy(dst, tmp);\n+\tstrlcpy(dst, tmp, size);\n \treturn (dst);\n }\n \n@@ -154,7 +154,7 @@ inet_ntop6(const u_char *src, char *dst, size_t size)\n \t\terrno = ENOSPC;\n \t\treturn (NULL);\n \t}\n-\tstrcpy(dst, tmp);\n+\tstrlcpy(dst, tmp, size);\n \treturn (dst);\n }\n #endif\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270664","messageId":"20150924210608.GL30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 15/68] convert trivial sprintf / strcpy calls to xsnprintf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:06:08Z","receivedAt":"2015-09-24T21:06:08Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We sometimes sprintf into fixed-size buffers when we know\nthat the buffer is large enough to fit the input (either\nbecause it's a constant, or because it's numeric input that\nis bounded in size). Likewise with strcpy of constant\nstrings.\n\nHowever, these sites make it hard to audit sprintf and\nstrcpy calls for buffer overflows, as a reader has to\ncross-reference the size of the array with the input. Let's\nuse xsnprintf instead, which communicates to a reader that\nwe don't expect this to overflow (and catches the mistake in\ncase we do).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n archive-tar.c             |  2 +-\n builtin/gc.c              |  2 +-\n builtin/init-db.c         | 11 ++++++-----\n builtin/ls-tree.c         |  9 +++++----\n builtin/merge-index.c     |  2 +-\n builtin/merge-recursive.c |  2 +-\n builtin/read-tree.c       |  2 +-\n builtin/unpack-file.c     |  2 +-\n compat/mingw.c            |  8 +++++---\n compat/winansi.c          |  2 +-\n connect.c                 |  2 +-\n convert.c                 |  3 ++-\n daemon.c                  |  4 ++--\n diff.c                    | 12 ++++++------\n http-push.c               |  2 +-\n http.c                    |  6 +++---\n ll-merge.c                | 12 ++++++------\n refs.c                    |  8 ++++----\n sideband.c                |  4 ++--\n strbuf.c                  |  4 ++--\n 20 files changed, 52 insertions(+), 47 deletions(-)\n\ndiff --git a/archive-tar.c b/archive-tar.c\nindex b6b30bb..d543f93 100644\n--- a/archive-tar.c\n+++ b/archive-tar.c\n@@ -301,7 +301,7 @@ static int write_global_extended_header(struct archiver_args *args)\n \tmemset(&header, 0, sizeof(header));\n \t*header.typeflag = TYPEFLAG_GLOBAL_HEADER;\n \tmode = 0100666;\n-\tstrcpy(header.name, \"pax_global_header\");\n+\txsnprintf(header.name, sizeof(header.name), \"pax_global_header\");\n \tprepare_header(args, &header, mode, ext_header.len);\n \twrite_blocked(&header, sizeof(header));\n \twrite_blocked(ext_header.buf, ext_header.len);\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 0ad8d30..57584bc 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -194,7 +194,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)\n \t\treturn NULL;\n \n \tif (gethostname(my_host, sizeof(my_host)))\n-\t\tstrcpy(my_host, \"unknown\");\n+\t\txsnprintf(my_host, sizeof(my_host), \"unknown\");\n \n \tpidfile_path = git_pathdup(\"gc.pid\");\n \tfd = hold_lock_file_for_update(&lock, pidfile_path,\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 69323e1..e7d0e31 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -262,7 +262,8 @@ static int create_default_files(const char *template_path)\n \t}\n \n \t/* This forces creation of new config file */\n-\tsprintf(repo_version_string, \"%d\", GIT_REPO_VERSION);\n+\txsnprintf(repo_version_string, sizeof(repo_version_string),\n+\t\t  \"%d\", GIT_REPO_VERSION);\n \tgit_config_set(\"core.repositoryformatversion\", repo_version_string);\n \n \tpath[len] = 0;\n@@ -414,13 +415,13 @@ int init_db(const char *template_dir, unsigned int flags)\n \t\t */\n \t\tif (shared_repository < 0)\n \t\t\t/* force to the mode value */\n-\t\t\tsprintf(buf, \"0%o\", -shared_repository);\n+\t\t\txsnprintf(buf, sizeof(buf), \"0%o\", -shared_repository);\n \t\telse if (shared_repository == PERM_GROUP)\n-\t\t\tsprintf(buf, \"%d\", OLD_PERM_GROUP);\n+\t\t\txsnprintf(buf, sizeof(buf), \"%d\", OLD_PERM_GROUP);\n \t\telse if (shared_repository == PERM_EVERYBODY)\n-\t\t\tsprintf(buf, \"%d\", OLD_PERM_EVERYBODY);\n+\t\t\txsnprintf(buf, sizeof(buf), \"%d\", OLD_PERM_EVERYBODY);\n \t\telse\n-\t\t\tdie(\"oops\");\n+\t\t\tdie(\"BUG: invalid value for shared_repository\");\n \t\tgit_config_set(\"core.sharedrepository\", buf);\n \t\tgit_config_set(\"receive.denyNonFastforwards\", \"true\");\n \t}\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3b04a0f..0e30d86 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -96,12 +96,13 @@ static int show_tree(const unsigned char *sha1, struct strbuf *base,\n \t\t\tif (!strcmp(type, blob_type)) {\n \t\t\t\tunsigned long size;\n \t\t\t\tif (sha1_object_info(sha1, &size) == OBJ_BAD)\n-\t\t\t\t\tstrcpy(size_text, \"BAD\");\n+\t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n+\t\t\t\t\t\t  \"BAD\");\n \t\t\t\telse\n-\t\t\t\t\tsnprintf(size_text, sizeof(size_text),\n-\t\t\t\t\t\t \"%lu\", size);\n+\t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n+\t\t\t\t\t\t  \"%lu\", size);\n \t\t\t} else\n-\t\t\t\tstrcpy(size_text, \"-\");\n+\t\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n \t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type,\n \t\t\t       find_unique_abbrev(sha1, abbrev),\n \t\t\t       size_text);\ndiff --git a/builtin/merge-index.c b/builtin/merge-index.c\nindex 1a1eafa..1d66111 100644\n--- a/builtin/merge-index.c\n+++ b/builtin/merge-index.c\n@@ -23,7 +23,7 @@ static int merge_entry(int pos, const char *path)\n \t\t\tbreak;\n \t\tfound++;\n \t\tstrcpy(hexbuf[stage], sha1_to_hex(ce->sha1));\n-\t\tsprintf(ownbuf[stage], \"%o\", ce->ce_mode);\n+\t\txsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), \"%o\", ce->ce_mode);\n \t\targuments[stage] = hexbuf[stage];\n \t\targuments[stage + 4] = ownbuf[stage];\n \t} while (++pos < active_nr);\ndiff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c\nindex a90f28f..491efd5 100644\n--- a/builtin/merge-recursive.c\n+++ b/builtin/merge-recursive.c\n@@ -14,7 +14,7 @@ static const char *better_branch_name(const char *branch)\n \n \tif (strlen(branch) != 40)\n \t\treturn branch;\n-\tsprintf(githead_env, \"GITHEAD_%s\", branch);\n+\txsnprintf(githead_env, sizeof(githead_env), \"GITHEAD_%s\", branch);\n \tname = getenv(githead_env);\n \treturn name ? name : branch;\n }\ndiff --git a/builtin/read-tree.c b/builtin/read-tree.c\nindex 2379e11..8c693e7 100644\n--- a/builtin/read-tree.c\n+++ b/builtin/read-tree.c\n@@ -90,7 +90,7 @@ static int debug_merge(const struct cache_entry * const *stages,\n \tdebug_stage(\"index\", stages[0], o);\n \tfor (i = 1; i <= o->merge_size; i++) {\n \t\tchar buf[24];\n-\t\tsprintf(buf, \"ent#%d\", i);\n+\t\txsnprintf(buf, sizeof(buf), \"ent#%d\", i);\n \t\tdebug_stage(buf, stages[i], o);\n \t}\n \treturn 0;\ndiff --git a/builtin/unpack-file.c b/builtin/unpack-file.c\nindex 1920029..6fc6bcd 100644\n--- a/builtin/unpack-file.c\n+++ b/builtin/unpack-file.c\n@@ -12,7 +12,7 @@ static char *create_temp_file(unsigned char *sha1)\n \tif (!buf || type != OBJ_BLOB)\n \t\tdie(\"unable to read blob object %s\", sha1_to_hex(sha1));\n \n-\tstrcpy(path, \".merge_file_XXXXXX\");\n+\txsnprintf(path, sizeof(path), \".merge_file_XXXXXX\");\n \tfd = xmkstemp(path);\n \tif (write_in_full(fd, buf, size) != size)\n \t\tdie_errno(\"unable to write temp-file\");\ndiff --git a/compat/mingw.c b/compat/mingw.c\nindex f74da23..a168800 100644\n--- a/compat/mingw.c\n+++ b/compat/mingw.c\n@@ -2133,9 +2133,11 @@ int uname(struct utsname *buf)\n {\n \tDWORD v = GetVersion();\n \tmemset(buf, 0, sizeof(*buf));\n-\tstrcpy(buf->sysname, \"Windows\");\n-\tsprintf(buf->release, \"%u.%u\", v & 0xff, (v >> 8) & 0xff);\n+\txsnprintf(buf->sysname, sizeof(buf->sysname), \"Windows\");\n+\txsnprintf(buf->release, sizeof(buf->release),\n+\t\t \"%u.%u\", v & 0xff, (v >> 8) & 0xff);\n \t/* assuming NT variants only.. */\n-\tsprintf(buf->version, \"%u\", (v >> 16) & 0x7fff);\n+\txsnprintf(buf->version, sizeof(buf->version),\n+\t\t  \"%u\", (v >> 16) & 0x7fff);\n \treturn 0;\n }\ndiff --git a/compat/winansi.c b/compat/winansi.c\nindex efc5bb3..ceff55b 100644\n--- a/compat/winansi.c\n+++ b/compat/winansi.c\n@@ -539,7 +539,7 @@ void winansi_init(void)\n \t\treturn;\n \n \t/* create a named pipe to communicate with the console thread */\n-\tsprintf(name, \"\\\\\\\\.\\\\pipe\\\\winansi%lu\", GetCurrentProcessId());\n+\txsnprintf(name, sizeof(name), \"\\\\\\\\.\\\\pipe\\\\winansi%lu\", GetCurrentProcessId());\n \thwrite = CreateNamedPipe(name, PIPE_ACCESS_OUTBOUND,\n \t\tPIPE_TYPE_BYTE | PIPE_WAIT, 1, BUFFER_SIZE, 0, 0, NULL);\n \tif (hwrite == INVALID_HANDLE_VALUE)\ndiff --git a/connect.c b/connect.c\nindex c0144d8..1d5c5e0 100644\n--- a/connect.c\n+++ b/connect.c\n@@ -332,7 +332,7 @@ static const char *ai_name(const struct addrinfo *ai)\n \tstatic char addr[NI_MAXHOST];\n \tif (getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), NULL, 0,\n \t\t\tNI_NUMERICHOST) != 0)\n-\t\tstrcpy(addr, \"(unknown)\");\n+\t\txsnprintf(addr, sizeof(addr), \"(unknown)\");\n \n \treturn addr;\n }\ndiff --git a/convert.c b/convert.c\nindex f3bd3e9..814e814 100644\n--- a/convert.c\n+++ b/convert.c\n@@ -1289,7 +1289,8 @@ static struct stream_filter *ident_filter(const unsigned char *sha1)\n {\n \tstruct ident_filter *ident = xmalloc(sizeof(*ident));\n \n-\tsprintf(ident->ident, \": %s $\", sha1_to_hex(sha1));\n+\txsnprintf(ident->ident, sizeof(ident->ident),\n+\t\t  \": %s $\", sha1_to_hex(sha1));\n \tstrbuf_init(&ident->left, 0);\n \tident->filter.vtbl = &ident_vtbl;\n \tident->state = 0;\ndiff --git a/daemon.c b/daemon.c\nindex f9eb296..5218a3f 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -901,7 +901,7 @@ static const char *ip2str(int family, struct sockaddr *sin, socklen_t len)\n \t\tinet_ntop(family, &((struct sockaddr_in*)sin)->sin_addr, ip, len);\n \t\tbreak;\n \tdefault:\n-\t\tstrcpy(ip, \"<unknown>\");\n+\t\txsnprintf(ip, sizeof(ip), \"<unknown>\");\n \t}\n \treturn ip;\n }\n@@ -916,7 +916,7 @@ static int setup_named_sock(char *listen_addr, int listen_port, struct socketlis\n \tint gai;\n \tlong flags;\n \n-\tsprintf(pbuf, \"%d\", listen_port);\n+\txsnprintf(pbuf, sizeof(pbuf), \"%d\", listen_port);\n \tmemset(&hints, 0, sizeof(hints));\n \thints.ai_family = AF_UNSPEC;\n \thints.ai_socktype = SOCK_STREAM;\ndiff --git a/diff.c b/diff.c\nindex 08508f6..788e371 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -2880,7 +2880,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,\n \ttemp->name = get_tempfile_path(&temp->tempfile);\n \tstrcpy(temp->hex, sha1_to_hex(sha1));\n \ttemp->hex[40] = 0;\n-\tsprintf(temp->mode, \"%06o\", mode);\n+\txsnprintf(temp->mode, sizeof(temp->mode), \"%06o\", mode);\n \tstrbuf_release(&buf);\n \tstrbuf_release(&template);\n \tfree(path_dup);\n@@ -2897,8 +2897,8 @@ static struct diff_tempfile *prepare_temp_file(const char *name,\n \t\t * a '+' entry produces this for file-1.\n \t\t */\n \t\ttemp->name = \"/dev/null\";\n-\t\tstrcpy(temp->hex, \".\");\n-\t\tstrcpy(temp->mode, \".\");\n+\t\txsnprintf(temp->hex, sizeof(temp->hex), \".\");\n+\t\txsnprintf(temp->mode, sizeof(temp->mode), \".\");\n \t\treturn temp;\n \t}\n \n@@ -2935,7 +2935,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name,\n \t\t\t * !(one->sha1_valid), as long as\n \t\t\t * DIFF_FILE_VALID(one).\n \t\t\t */\n-\t\t\tsprintf(temp->mode, \"%06o\", one->mode);\n+\t\t\txsnprintf(temp->mode, sizeof(temp->mode), \"%06o\", one->mode);\n \t\t}\n \t\treturn temp;\n \t}\n@@ -4081,9 +4081,9 @@ const char *diff_unique_abbrev(const unsigned char *sha1, int len)\n \tif (abblen < 37) {\n \t\tstatic char hex[41];\n \t\tif (len < abblen && abblen <= len + 2)\n-\t\t\tsprintf(hex, \"%s%.*s\", abbrev, len+3-abblen, \"..\");\n+\t\t\txsnprintf(hex, sizeof(hex), \"%s%.*s\", abbrev, len+3-abblen, \"..\");\n \t\telse\n-\t\t\tsprintf(hex, \"%s...\", abbrev);\n+\t\t\txsnprintf(hex, sizeof(hex), \"%s...\", abbrev);\n \t\treturn hex;\n \t}\n \treturn sha1_to_hex(sha1);\ndiff --git a/http-push.c b/http-push.c\nindex c98dad2..154e67b 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -881,7 +881,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)\n \tstrbuf_addf(&out_buffer.buf, LOCK_REQUEST, escaped);\n \tfree(escaped);\n \n-\tsprintf(timeout_header, \"Timeout: Second-%ld\", timeout);\n+\txsnprintf(timeout_header, sizeof(timeout_header), \"Timeout: Second-%ld\", timeout);\n \tdav_headers = curl_slist_append(dav_headers, timeout_header);\n \tdav_headers = curl_slist_append(dav_headers, \"Content-Type: text/xml\");\n \ndiff --git a/http.c b/http.c\nindex 9dce380..7b02259 100644\n--- a/http.c\n+++ b/http.c\n@@ -1104,7 +1104,7 @@ static void write_accept_language(struct strbuf *buf)\n \t\t     decimal_places++, max_q *= 10)\n \t\t\t;\n \n-\t\tsprintf(q_format, \";q=0.%%0%dd\", decimal_places);\n+\t\txsnprintf(q_format, sizeof(q_format), \";q=0.%%0%dd\", decimal_places);\n \n \t\tstrbuf_addstr(buf, \"Accept-Language: \");\n \n@@ -1601,7 +1601,7 @@ struct http_pack_request *new_http_pack_request(\n \t\t\tfprintf(stderr,\n \t\t\t\t\"Resuming fetch of pack %s at byte %ld\\n\",\n \t\t\t\tsha1_to_hex(target->sha1), prev_posn);\n-\t\tsprintf(range, \"Range: bytes=%ld-\", prev_posn);\n+\t\txsnprintf(range, sizeof(range), \"Range: bytes=%ld-\", prev_posn);\n \t\tpreq->range_header = curl_slist_append(NULL, range);\n \t\tcurl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,\n \t\t\tpreq->range_header);\n@@ -1761,7 +1761,7 @@ struct http_object_request *new_http_object_request(const char *base_url,\n \t\t\tfprintf(stderr,\n \t\t\t\t\"Resuming fetch of object %s at byte %ld\\n\",\n \t\t\t\thex, prev_posn);\n-\t\tsprintf(range, \"Range: bytes=%ld-\", prev_posn);\n+\t\txsnprintf(range, sizeof(range), \"Range: bytes=%ld-\", prev_posn);\n \t\trange_header = curl_slist_append(range_header, range);\n \t\tcurl_easy_setopt(freq->slot->curl,\n \t\t\t\t CURLOPT_HTTPHEADER, range_header);\ndiff --git a/ll-merge.c b/ll-merge.c\nindex fc3c049..56f73b3 100644\n--- a/ll-merge.c\n+++ b/ll-merge.c\n@@ -142,11 +142,11 @@ static struct ll_merge_driver ll_merge_drv[] = {\n \t{ \"union\", \"built-in union merge\", ll_union_merge },\n };\n \n-static void create_temp(mmfile_t *src, char *path)\n+static void create_temp(mmfile_t *src, char *path, size_t len)\n {\n \tint fd;\n \n-\tstrcpy(path, \".merge_file_XXXXXX\");\n+\txsnprintf(path, len, \".merge_file_XXXXXX\");\n \tfd = xmkstemp(path);\n \tif (write_in_full(fd, src->ptr, src->size) != src->size)\n \t\tdie_errno(\"unable to write temp-file\");\n@@ -187,10 +187,10 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,\n \n \tresult->ptr = NULL;\n \tresult->size = 0;\n-\tcreate_temp(orig, temp[0]);\n-\tcreate_temp(src1, temp[1]);\n-\tcreate_temp(src2, temp[2]);\n-\tsprintf(temp[3], \"%d\", marker_size);\n+\tcreate_temp(orig, temp[0], sizeof(temp[0]));\n+\tcreate_temp(src1, temp[1], sizeof(temp[1]));\n+\tcreate_temp(src2, temp[2], sizeof(temp[2]));\n+\txsnprintf(temp[3], sizeof(temp[3]), \"%d\", marker_size);\n \n \tstrbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);\n \ndiff --git a/refs.c b/refs.c\nindex 4e15f60..d5c8b2f 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -3326,10 +3326,10 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,\n \tmsglen = msg ? strlen(msg) : 0;\n \tmaxlen = strlen(committer) + msglen + 100;\n \tlogrec = xmalloc(maxlen);\n-\tlen = sprintf(logrec, \"%s %s %s\\n\",\n-\t\t      sha1_to_hex(old_sha1),\n-\t\t      sha1_to_hex(new_sha1),\n-\t\t      committer);\n+\tlen = xsnprintf(logrec, maxlen, \"%s %s %s\\n\",\n+\t\t\tsha1_to_hex(old_sha1),\n+\t\t\tsha1_to_hex(new_sha1),\n+\t\t\tcommitter);\n \tif (msglen)\n \t\tlen += copy_msg(logrec + len - 1, msg) - 1;\n \ndiff --git a/sideband.c b/sideband.c\nindex 7f9dc22..fde8adc 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -137,11 +137,11 @@ ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet\n \t\tif (packet_max - 5 < n)\n \t\t\tn = packet_max - 5;\n \t\tif (0 <= band) {\n-\t\t\tsprintf(hdr, \"%04x\", n + 5);\n+\t\t\txsnprintf(hdr, sizeof(hdr), \"%04x\", n + 5);\n \t\t\thdr[4] = band;\n \t\t\twrite_or_die(fd, hdr, 5);\n \t\t} else {\n-\t\t\tsprintf(hdr, \"%04x\", n + 4);\n+\t\t\txsnprintf(hdr, sizeof(hdr), \"%04x\", n + 4);\n \t\t\twrite_or_die(fd, hdr, 4);\n \t\t}\n \t\twrite_or_die(fd, p, n);\ndiff --git a/strbuf.c b/strbuf.c\nindex f3c44fb..107c45d 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -245,8 +245,8 @@ void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size\n \tstatic char prefix2[2];\n \n \tif (prefix1[0] != comment_line_char) {\n-\t\tsprintf(prefix1, \"%c \", comment_line_char);\n-\t\tsprintf(prefix2, \"%c\", comment_line_char);\n+\t\txsnprintf(prefix1, sizeof(prefix1), \"%c \", comment_line_char);\n+\t\txsnprintf(prefix2, sizeof(prefix2), \"%c\", comment_line_char);\n \t}\n \tadd_lines(out, prefix1, prefix2, buf, size);\n }\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270667","messageId":"20150924210624.GM30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 16/68] archive-tar: use xsnprintf for trivial formatting","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:06:24Z","receivedAt":"2015-09-24T21:06:24Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When we generate tar headers, we sprintf() values directly\ninto a struct with the fixed-size header values. For the\nmost part this is fine, as we are formatting small values\n(e.g., the octal format of \"mode & 0x7777\" is of fixed\nlength). But it's still a good idea to use xsnprintf here.\nIt communicates to readers what our expectation is, and it\nprovides a run-time check that we are not overflowing the\nbuffers.\n\nThe one exception here is the mtime, which comes from the\nepoch time of the commit we are archiving. For sane values,\nthis fits into the 12-byte value allocated in the header.\nBut since git can handle 64-bit times, if I claim to be a\nvisitor from the year 10,000 AD, I can overflow the buffer.\nThis turns out to be harmless, as we simply overflow into\nthe chksum field, which is then overwritten.\n\nThis case is also best as an xsnprintf. It should never come\nup, short of extremely malformed dates, and in that case we\nare probably better off dying than silently truncating the\ndate value (and we cannot expand the size of the buffer,\nsince it is dictated by the ustar format). Our friends in\nthe year 5138 (when we legitimately flip to a 12-digit\nepoch) can deal with that problem then.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n archive-tar.c | 26 +++++++++++++-------------\n 1 file changed, 13 insertions(+), 13 deletions(-)\n\ndiff --git a/archive-tar.c b/archive-tar.c\nindex d543f93..501ca97 100644\n--- a/archive-tar.c\n+++ b/archive-tar.c\n@@ -167,21 +167,21 @@ static void prepare_header(struct archiver_args *args,\n \t\t\t   struct ustar_header *header,\n \t\t\t   unsigned int mode, unsigned long size)\n {\n-\tsprintf(header->mode, \"%07o\", mode & 07777);\n-\tsprintf(header->size, \"%011lo\", S_ISREG(mode) ? size : 0);\n-\tsprintf(header->mtime, \"%011lo\", (unsigned long) args->time);\n+\txsnprintf(header->mode, sizeof(header->mode), \"%07o\", mode & 07777);\n+\txsnprintf(header->size, sizeof(header->size), \"%011lo\", S_ISREG(mode) ? size : 0);\n+\txsnprintf(header->mtime, sizeof(header->mtime), \"%011lo\", (unsigned long) args->time);\n \n-\tsprintf(header->uid, \"%07o\", 0);\n-\tsprintf(header->gid, \"%07o\", 0);\n+\txsnprintf(header->uid, sizeof(header->uid), \"%07o\", 0);\n+\txsnprintf(header->gid, sizeof(header->gid), \"%07o\", 0);\n \tstrlcpy(header->uname, \"root\", sizeof(header->uname));\n \tstrlcpy(header->gname, \"root\", sizeof(header->gname));\n-\tsprintf(header->devmajor, \"%07o\", 0);\n-\tsprintf(header->devminor, \"%07o\", 0);\n+\txsnprintf(header->devmajor, sizeof(header->devmajor), \"%07o\", 0);\n+\txsnprintf(header->devminor, sizeof(header->devminor), \"%07o\", 0);\n \n \tmemcpy(header->magic, \"ustar\", 6);\n \tmemcpy(header->version, \"00\", 2);\n \n-\tsprintf(header->chksum, \"%07o\", ustar_header_chksum(header));\n+\tsnprintf(header->chksum, sizeof(header->chksum), \"%07o\", ustar_header_chksum(header));\n }\n \n static int write_extended_header(struct archiver_args *args,\n@@ -193,7 +193,7 @@ static int write_extended_header(struct archiver_args *args,\n \tmemset(&header, 0, sizeof(header));\n \t*header.typeflag = TYPEFLAG_EXT_HEADER;\n \tmode = 0100666;\n-\tsprintf(header.name, \"%s.paxheader\", sha1_to_hex(sha1));\n+\txsnprintf(header.name, sizeof(header.name), \"%s.paxheader\", sha1_to_hex(sha1));\n \tprepare_header(args, &header, mode, size);\n \twrite_blocked(&header, sizeof(header));\n \twrite_blocked(buffer, size);\n@@ -235,8 +235,8 @@ static int write_tar_entry(struct archiver_args *args,\n \t\t\tmemcpy(header.prefix, path, plen);\n \t\t\tmemcpy(header.name, path + plen + 1, rest);\n \t\t} else {\n-\t\t\tsprintf(header.name, \"%s.data\",\n-\t\t\t\tsha1_to_hex(sha1));\n+\t\t\txsnprintf(header.name, sizeof(header.name), \"%s.data\",\n+\t\t\t\t  sha1_to_hex(sha1));\n \t\t\tstrbuf_append_ext_header(&ext_header, \"path\",\n \t\t\t\t\t\t path, pathlen);\n \t\t}\n@@ -259,8 +259,8 @@ static int write_tar_entry(struct archiver_args *args,\n \n \tif (S_ISLNK(mode)) {\n \t\tif (size > sizeof(header.linkname)) {\n-\t\t\tsprintf(header.linkname, \"see %s.paxheader\",\n-\t\t\t        sha1_to_hex(sha1));\n+\t\t\txsnprintf(header.linkname, sizeof(header.linkname),\n+\t\t\t\t  \"see %s.paxheader\", sha1_to_hex(sha1));\n \t\t\tstrbuf_append_ext_header(&ext_header, \"linkpath\",\n \t\t\t                         buffer, size);\n \t\t} else\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270666","messageId":"20150924210641.GN30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 17/68] use xsnprintf for generating git object headers","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:06:42Z","receivedAt":"2015-09-24T21:06:42Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We generally use 32-byte buffers to format git's \"type size\"\nheader fields. These should not generally overflow unless\nyou can produce some truly gigantic objects (and our types\ncome from our internal array of constant strings). But it is\na good idea to use xsnprintf to make sure this is the case.\n\nNote that we slightly modify the interface to\nwrite_sha1_file_prepare, which nows uses \"hdrlen\" as an \"in\"\nparameter as well as an \"out\" (on the way in it stores the\nallocated size of the header, and on the way out it returns\nthe ultimate size of the header).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/index-pack.c |  2 +-\n bulk-checkin.c       |  4 ++--\n fast-import.c        |  4 ++--\n http-push.c          |  2 +-\n sha1_file.c          | 13 +++++++------\n 5 files changed, 13 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 3431de2..1ad1bde 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -441,7 +441,7 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size,\n \tint hdrlen;\n \n \tif (!is_delta_type(type)) {\n-\t\thdrlen = sprintf(hdr, \"%s %lu\", typename(type), size) + 1;\n+\t\thdrlen = xsnprintf(hdr, sizeof(hdr), \"%s %lu\", typename(type), size) + 1;\n \t\tgit_SHA1_Init(&c);\n \t\tgit_SHA1_Update(&c, hdr, hdrlen);\n \t} else\ndiff --git a/bulk-checkin.c b/bulk-checkin.c\nindex 7cffc3a..4347f5c 100644\n--- a/bulk-checkin.c\n+++ b/bulk-checkin.c\n@@ -200,8 +200,8 @@ static int deflate_to_pack(struct bulk_checkin_state *state,\n \tif (seekback == (off_t) -1)\n \t\treturn error(\"cannot find the current offset\");\n \n-\theader_len = sprintf((char *)obuf, \"%s %\" PRIuMAX,\n-\t\t\t     typename(type), (uintmax_t)size) + 1;\n+\theader_len = xsnprintf((char *)obuf, sizeof(obuf), \"%s %\" PRIuMAX,\n+\t\t\t       typename(type), (uintmax_t)size) + 1;\n \tgit_SHA1_Init(&ctx);\n \tgit_SHA1_Update(&ctx, obuf, header_len);\n \ndiff --git a/fast-import.c b/fast-import.c\nindex 6c7c3c9..d0c2502 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -1035,8 +1035,8 @@ static int store_object(\n \tgit_SHA_CTX c;\n \tgit_zstream s;\n \n-\thdrlen = sprintf((char *)hdr,\"%s %lu\", typename(type),\n-\t\t(unsigned long)dat->len) + 1;\n+\thdrlen = xsnprintf((char *)hdr, sizeof(hdr), \"%s %lu\",\n+\t\t\t   typename(type), (unsigned long)dat->len) + 1;\n \tgit_SHA1_Init(&c);\n \tgit_SHA1_Update(&c, hdr, hdrlen);\n \tgit_SHA1_Update(&c, dat->buf, dat->len);\ndiff --git a/http-push.c b/http-push.c\nindex 154e67b..1f3788f 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -361,7 +361,7 @@ static void start_put(struct transfer_request *request)\n \tgit_zstream stream;\n \n \tunpacked = read_sha1_file(request->obj->sha1, &type, &len);\n-\thdrlen = sprintf(hdr, \"%s %lu\", typename(type), len) + 1;\n+\thdrlen = xsnprintf(hdr, sizeof(hdr), \"%s %lu\", typename(type), len) + 1;\n \n \t/* Set it up */\n \tgit_deflate_init(&stream, zlib_compression_level);\ndiff --git a/sha1_file.c b/sha1_file.c\nindex d295a32..f106091 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -1464,7 +1464,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map,\n \t\treturn -1;\n \n \t/* Generate the header */\n-\thdrlen = sprintf(hdr, \"%s %lu\", typename(obj_type), size) + 1;\n+\thdrlen = xsnprintf(hdr, sizeof(hdr), \"%s %lu\", typename(obj_type), size) + 1;\n \n \t/* Sha1.. */\n \tgit_SHA1_Init(&c);\n@@ -2930,7 +2930,7 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len,\n \tgit_SHA_CTX c;\n \n \t/* Generate the header */\n-\t*hdrlen = sprintf(hdr, \"%s %lu\", type, len)+1;\n+\t*hdrlen = xsnprintf(hdr, *hdrlen, \"%s %lu\", type, len)+1;\n \n \t/* Sha1.. */\n \tgit_SHA1_Init(&c);\n@@ -2993,7 +2993,7 @@ int hash_sha1_file(const void *buf, unsigned long len, const char *type,\n                    unsigned char *sha1)\n {\n \tchar hdr[32];\n-\tint hdrlen;\n+\tint hdrlen = sizeof(hdr);\n \twrite_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);\n \treturn 0;\n }\n@@ -3139,7 +3139,7 @@ static int freshen_packed_object(const unsigned char *sha1)\n int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1)\n {\n \tchar hdr[32];\n-\tint hdrlen;\n+\tint hdrlen = sizeof(hdr);\n \n \t/* Normally if we have it in the pack then we do not bother writing\n \t * it out into .git/objects/??/?{38} file.\n@@ -3157,7 +3157,8 @@ int hash_sha1_file_literally(const void *buf, unsigned long len, const char *typ\n \tint hdrlen, status = 0;\n \n \t/* type string, SP, %lu of the length plus NUL must fit this */\n-\theader = xmalloc(strlen(type) + 32);\n+\thdrlen = strlen(type) + 32;\n+\theader = xmalloc(hdrlen);\n \twrite_sha1_file_prepare(buf, len, type, sha1, header, &hdrlen);\n \n \tif (!(flags & HASH_WRITE_OBJECT))\n@@ -3185,7 +3186,7 @@ int force_object_loose(const unsigned char *sha1, time_t mtime)\n \tbuf = read_packed_sha1(sha1, &type, &len);\n \tif (!buf)\n \t\treturn error(\"cannot read sha1_file for %s\", sha1_to_hex(sha1));\n-\thdrlen = sprintf(hdr, \"%s %lu\", typename(type), len) + 1;\n+\thdrlen = xsnprintf(hdr, sizeof(hdr), \"%s %lu\", typename(type), len) + 1;\n \tret = write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);\n \tfree(buf);\n \n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270663","messageId":"20150924210644.GO30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 18/68] find_short_object_filename: convert sprintf to xsnprintf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:06:44Z","receivedAt":"2015-09-24T21:06:44Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We use sprintf() to format some hex data into a buffer. The\nbuffer is clearly long enough, and using snprintf here is\nnot necessary. And in fact, it does not really make anything\neasier to audit, as the size we feed to snprintf accounts\nfor the magic extra 42 bytes found in each alt->name field\nof struct alternate_object_database (which is there exactly\nto do this formatting).\n\nStill, it is nice to remove an sprintf call and replace it\nwith an xsnprintf and explanatory comment, which makes it\neasier to audit the code base for overflows.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n sha1_name.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/sha1_name.c b/sha1_name.c\nindex c58b477..80753b6 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -96,11 +96,15 @@ static void find_short_object_filename(int len, const char *hex_pfx, struct disa\n \t}\n \tfakeent->next = alt_odb_list;\n \n-\tsprintf(hex, \"%.2s\", hex_pfx);\n+\txsnprintf(hex, sizeof(hex), \"%.2s\", hex_pfx);\n \tfor (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {\n \t\tstruct dirent *de;\n \t\tDIR *dir;\n-\t\tsprintf(alt->name, \"%.2s/\", hex_pfx);\n+\t\t/*\n+\t\t * every alt_odb struct has 42 extra bytes after the base\n+\t\t * for exactly this purpose\n+\t\t */\n+\t\txsnprintf(alt->name, 42, \"%.2s/\", hex_pfx);\n \t\tdir = opendir(alt->base);\n \t\tif (!dir)\n \t\t\tcontinue;\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270670","messageId":"20150924210646.GP30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 19/68] stop_progress_msg: convert sprintf to xsnprintf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:06:46Z","receivedAt":"2015-09-24T21:06:46Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The usual arguments for using xsnprintf over sprintf apply,\nbut this case is a little tricky. We print to a fixed-size\nbuffer if we have room, and otherwise to an allocated\nbuffer. So there should be no overflow here, but it is still\ngood to communicate our intention, as well as to check our\nearlier math for how much space the string will need.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n progress.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/progress.c b/progress.c\nindex a3efcfd..353bd37 100644\n--- a/progress.c\n+++ b/progress.c\n@@ -254,7 +254,7 @@ void stop_progress_msg(struct progress **p_progress, const char *msg)\n \t\t\tthroughput_string(&tp->display, tp->curr_total, rate);\n \t\t}\n \t\tprogress_update = 1;\n-\t\tsprintf(bufp, \", %s.\\n\", msg);\n+\t\txsnprintf(bufp, len + 1, \", %s.\\n\", msg);\n \t\tdisplay(progress, progress->last_value, bufp);\n \t\tif (buf != bufp)\n \t\t\tfree(bufp);\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270672","messageId":"20150924210648.GQ30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 20/68] compat/hstrerror: convert sprintf to snprintf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:06:48Z","receivedAt":"2015-09-24T21:06:48Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This is a trivially correct use of sprintf, as our error\nnumber should not be excessively long. But it's still nice\nto drop an sprintf call.\n\nNote that we cannot use xsnprintf here, because this is\ncompat code which does not load git-compat-util.h.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n compat/hstrerror.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/compat/hstrerror.c b/compat/hstrerror.c\nindex 069c555..b85a2fa 100644\n--- a/compat/hstrerror.c\n+++ b/compat/hstrerror.c\n@@ -16,6 +16,6 @@ const char *githstrerror(int err)\n \tcase TRY_AGAIN:\n \t\treturn \"Non-authoritative \\\"host not found\\\", or SERVERFAIL\";\n \t}\n-\tsprintf(buffer, \"Name resolution error %d\", err);\n+\tsnprintf(buffer, sizeof(buffer), \"Name resolution error %d\", err);\n \treturn buffer;\n }\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270665","messageId":"20150924210650.GR30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 21/68] grep: use xsnprintf to format failure message","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:06:51Z","receivedAt":"2015-09-24T21:06:51Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This looks at first glance like the sprintf can overflow our\nbuffer, but it's actually fine; the p->origin string is\nsomething constant and small, like \"command line\" or \"-e\noption\".\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n grep.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/grep.c b/grep.c\nindex b58c7c6..6c68d5b 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -306,9 +306,9 @@ static NORETURN void compile_regexp_failed(const struct grep_pat *p,\n \tchar where[1024];\n \n \tif (p->no)\n-\t\tsprintf(where, \"In '%s' at %d, \", p->origin, p->no);\n+\t\txsnprintf(where, sizeof(where), \"In '%s' at %d, \", p->origin, p->no);\n \telse if (p->origin)\n-\t\tsprintf(where, \"%s, \", p->origin);\n+\t\txsnprintf(where, sizeof(where), \"%s, \", p->origin);\n \telse\n \t\twhere[0] = 0;\n \n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270671","messageId":"20150924210653.GS30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 22/68] entry.c: convert strcpy to xsnprintf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:06:53Z","receivedAt":"2015-09-24T21:06:53Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This particular conversion is non-obvious, because nobody\nhas passed our function the length of the destination\nbuffer. However, the interface to checkout_entry specifies\nthat the buffer must be at least TEMPORARY_FILENAME_LENGTH\nbytes long, so we can check that (meaning the existing code\nwas not buggy, but merely worrisome to somebody reading it).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n entry.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/entry.c b/entry.c\nindex 1eda8e9..582c400 100644\n--- a/entry.c\n+++ b/entry.c\n@@ -96,8 +96,8 @@ static int open_output_fd(char *path, const struct cache_entry *ce, int to_tempf\n {\n \tint symlink = (ce->ce_mode & S_IFMT) != S_IFREG;\n \tif (to_tempfile) {\n-\t\tstrcpy(path, symlink\n-\t\t       ? \".merge_link_XXXXXX\" : \".merge_file_XXXXXX\");\n+\t\txsnprintf(path, TEMPORARY_FILENAME_LENGTH, \"%s\",\n+\t\t\t  symlink ? \".merge_link_XXXXXX\" : \".merge_file_XXXXXX\");\n \t\treturn mkstemp(path);\n \t} else {\n \t\treturn create_file(path, !symlink ? ce->ce_mode : 0666);\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270668","messageId":"20150924210655.GT30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 23/68] add_packed_git: convert strcpy into xsnprintf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:06:55Z","receivedAt":"2015-09-24T21:06:55Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We have the path \"foo.idx\", and we create a buffer big\nenough to hold \"foo.pack\" and \"foo.keep\", and then strcpy\nstraight into it. This isn't a bug (we have enough space),\nbut it's very hard to tell from the strcpy that this is so.\n\nLet's instead use strip_suffix to take off the \".idx\",\nrecord the size of our allocation, and use xsnprintf to make\nsure we don't violate our assumptions.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n cache.h     |  2 +-\n sha1_file.c | 21 +++++++++++++--------\n 2 files changed, 14 insertions(+), 9 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 030b880..d206d64 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1309,7 +1309,7 @@ extern void close_pack_windows(struct packed_git *);\n extern void unuse_pack(struct pack_window **);\n extern void free_pack_by_name(const char *);\n extern void clear_delta_base_cache(void);\n-extern struct packed_git *add_packed_git(const char *, int, int);\n+extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);\n \n /*\n  * Return the SHA-1 of the nth object within the specified packfile.\ndiff --git a/sha1_file.c b/sha1_file.c\nindex f106091..592226e 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -1146,11 +1146,12 @@ static void try_to_free_pack_memory(size_t size)\n \trelease_pack_memory(size);\n }\n \n-struct packed_git *add_packed_git(const char *path, int path_len, int local)\n+struct packed_git *add_packed_git(const char *path, size_t path_len, int local)\n {\n \tstatic int have_set_try_to_free_routine;\n \tstruct stat st;\n-\tstruct packed_git *p = alloc_packed_git(path_len + 2);\n+\tsize_t alloc;\n+\tstruct packed_git *p;\n \n \tif (!have_set_try_to_free_routine) {\n \t\thave_set_try_to_free_routine = 1;\n@@ -1161,18 +1162,22 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)\n \t * Make sure a corresponding .pack file exists and that\n \t * the index looks sane.\n \t */\n-\tpath_len -= strlen(\".idx\");\n-\tif (path_len < 1) {\n-\t\tfree(p);\n+\tif (!strip_suffix_mem(path, &path_len, \".idx\"))\n \t\treturn NULL;\n-\t}\n+\n+\t/*\n+\t * \".pack\" is long enough to hold any suffix we're adding (and\n+\t * the use xsnprintf double-checks that)\n+\t */\n+\talloc = path_len + strlen(\".pack\") + 1;\n+\tp = alloc_packed_git(alloc);\n \tmemcpy(p->pack_name, path, path_len);\n \n-\tstrcpy(p->pack_name + path_len, \".keep\");\n+\txsnprintf(p->pack_name + path_len, alloc - path_len, \".keep\");\n \tif (!access(p->pack_name, F_OK))\n \t\tp->pack_keep = 1;\n \n-\tstrcpy(p->pack_name + path_len, \".pack\");\n+\txsnprintf(p->pack_name + path_len, alloc - path_len, \".pack\");\n \tif (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {\n \t\tfree(p);\n \t\treturn NULL;\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270717","messageId":"20150924210657.GU30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 24/68] http-push: replace strcat with xsnprintf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:06:58Z","receivedAt":"2015-09-24T21:06:58Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We account for these strcats in our initial allocation, but\nthe code is confusing to follow and verify. Let's remember\nour original allocation length, and then xsnprintf can\nverify that we don't exceed it.\n\nNote that we can't just use xstrfmt here (which would be\neven cleaner) because the code tries to grow the buffer only\nwhen necessary.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n http-push.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/http-push.c b/http-push.c\nindex 1f3788f..37baff8 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -786,21 +786,21 @@ xml_start_tag(void *userData, const char *name, const char **atts)\n {\n \tstruct xml_ctx *ctx = (struct xml_ctx *)userData;\n \tconst char *c = strchr(name, ':');\n-\tint new_len;\n+\tint old_namelen, new_len;\n \n \tif (c == NULL)\n \t\tc = name;\n \telse\n \t\tc++;\n \n-\tnew_len = strlen(ctx->name) + strlen(c) + 2;\n+\told_namelen = strlen(ctx->name);\n+\tnew_len = old_namelen + strlen(c) + 2;\n \n \tif (new_len > ctx->len) {\n \t\tctx->name = xrealloc(ctx->name, new_len);\n \t\tctx->len = new_len;\n \t}\n-\tstrcat(ctx->name, \".\");\n-\tstrcat(ctx->name, c);\n+\txsnprintf(ctx->name + old_namelen, ctx->len - old_namelen, \".%s\", c);\n \n \tfree(ctx->cdata);\n \tctx->cdata = NULL;\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270673","messageId":"20150924210700.GV30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 25/68] receive-pack: convert strncpy to xsnprintf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:00Z","receivedAt":"2015-09-24T21:07:00Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This strncpy is pointless; we pass the strlen() of the src\nstring, meaning that it works just like a memcpy. Worse,\nthough, is that the size has no relation to the destination\nbuffer, meaning it is a potential overflow.  In practice,\nit's not. We pass only short constant strings like\n\"warning: \" and \"error: \", which are much smaller than the\ndestination buffer.\n\nWe can make this much simpler by just using xsnprintf, which\nwill check for overflow and return the size for our next\nvsnprintf, without us having to run a separate strlen().\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/receive-pack.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex e6b93d0..04d2bdf 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -280,10 +280,10 @@ static void rp_warning(const char *err, ...) __attribute__((format (printf, 1, 2\n \n static void report_message(const char *prefix, const char *err, va_list params)\n {\n-\tint sz = strlen(prefix);\n+\tint sz;\n \tchar msg[4096];\n \n-\tstrncpy(msg, prefix, sz);\n+\tsz = xsnprintf(msg, sizeof(msg), \"%s\", prefix);\n \tsz += vsnprintf(msg + sz, sizeof(msg) - sz, err, params);\n \tif (sz > (sizeof(msg) - 1))\n \t\tsz = sizeof(msg) - 1;\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270674","messageId":"20150924210702.GW30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 26/68] replace trivial malloc + sprintf / strcpy calls with xstrfmt","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:03Z","receivedAt":"2015-09-24T21:07:03Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"It's a common pattern to do:\n\n  foo = xmalloc(strlen(one) + strlen(two) + 1 + 1);\n  sprintf(foo, \"%s %s\", one, two);\n\n(or possibly some variant with strcpy()s or a more\ncomplicated length computation).  We can switch these to use\nxstrfmt, which is shorter, involves less error-prone manual\ncomputation, and removes many sprintf and strcpy calls which\nmake it harder to audit the code for real buffer overflows.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/apply.c     |  5 +----\n builtin/ls-remote.c |  8 ++------\n builtin/name-rev.c  | 13 +++++--------\n environment.c       |  7 ++-----\n imap-send.c         |  5 ++---\n reflog-walk.c       |  7 +++----\n remote.c            |  7 +------\n setup.c             | 12 +++---------\n unpack-trees.c      |  4 +---\n 9 files changed, 20 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 4aa53f7..094a20f 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -698,10 +698,7 @@ static char *find_name_common(const char *line, const char *def,\n \t}\n \n \tif (root) {\n-\t\tchar *ret = xmalloc(root_len + len + 1);\n-\t\tstrcpy(ret, root);\n-\t\tmemcpy(ret + root_len, start, len);\n-\t\tret[root_len + len] = '\\0';\n+\t\tchar *ret = xstrfmt(\"%s%.*s\", root, len, start);\n \t\treturn squash_slash(ret);\n \t}\n \ndiff --git a/builtin/ls-remote.c b/builtin/ls-remote.c\nindex 4554dbc..5b6d679 100644\n--- a/builtin/ls-remote.c\n+++ b/builtin/ls-remote.c\n@@ -93,12 +93,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)\n \tif (argv[i]) {\n \t\tint j;\n \t\tpattern = xcalloc(argc - i + 1, sizeof(const char *));\n-\t\tfor (j = i; j < argc; j++) {\n-\t\t\tint len = strlen(argv[j]);\n-\t\t\tchar *p = xmalloc(len + 3);\n-\t\t\tsprintf(p, \"*/%s\", argv[j]);\n-\t\t\tpattern[j - i] = p;\n-\t\t}\n+\t\tfor (j = i; j < argc; j++)\n+\t\t\tpattern[j - i] = xstrfmt(\"*/%s\", argv[j]);\n \t}\n \tremote = remote_get(dest);\n \tif (!remote) {\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 248a3eb..8a3a0cd 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -56,19 +56,16 @@ copy_data:\n \t\t\tparents = parents->next, parent_number++) {\n \t\tif (parent_number > 1) {\n \t\t\tint len = strlen(tip_name);\n-\t\t\tchar *new_name = xmalloc(len +\n-\t\t\t\t1 + decimal_length(generation) +  /* ~<n> */\n-\t\t\t\t1 + 2 +\t\t\t\t  /* ^NN */\n-\t\t\t\t1);\n+\t\t\tchar *new_name;\n \n \t\t\tif (len > 2 && !strcmp(tip_name + len - 2, \"^0\"))\n \t\t\t\tlen -= 2;\n \t\t\tif (generation > 0)\n-\t\t\t\tsprintf(new_name, \"%.*s~%d^%d\", len, tip_name,\n-\t\t\t\t\t\tgeneration, parent_number);\n+\t\t\t\tnew_name = xstrfmt(\"%.*s~%d^%d\", len, tip_name,\n+\t\t\t\t\t\t   generation, parent_number);\n \t\t\telse\n-\t\t\t\tsprintf(new_name, \"%.*s^%d\", len, tip_name,\n-\t\t\t\t\t\tparent_number);\n+\t\t\t\tnew_name = xstrfmt(\"%.*s^%d\", len, tip_name,\n+\t\t\t\t\t\t   parent_number);\n \n \t\t\tname_rev(parents->item, new_name, 0,\n \t\t\t\tdistance + MERGE_TRAVERSAL_WEIGHT, 0);\ndiff --git a/environment.c b/environment.c\nindex a533aed..c5b65f5 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -143,11 +143,8 @@ static char *git_path_from_env(const char *envvar, const char *git_dir,\n \t\t\t       const char *path, int *fromenv)\n {\n \tconst char *value = getenv(envvar);\n-\tif (!value) {\n-\t\tchar *buf = xmalloc(strlen(git_dir) + strlen(path) + 2);\n-\t\tsprintf(buf, \"%s/%s\", git_dir, path);\n-\t\treturn buf;\n-\t}\n+\tif (!value)\n+\t\treturn xstrfmt(\"%s/%s\", git_dir, path);\n \tif (fromenv)\n \t\t*fromenv = 1;\n \treturn xstrdup(value);\ndiff --git a/imap-send.c b/imap-send.c\nindex 37ac4aa..e9faaea 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -889,9 +889,8 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)\n \t}\n \n \t/* response: \"<user> <digest in hex>\" */\n-\tresp_len = strlen(user) + 1 + strlen(hex) + 1;\n-\tresponse = xmalloc(resp_len);\n-\tsprintf(response, \"%s %s\", user, hex);\n+\tresponse = xstrfmt(\"%s %s\", user, hex);\n+\tresp_len = strlen(response) + 1;\n \n \tresponse_64 = xmalloc(ENCODED_SIZE(resp_len) + 1);\n \tencoded_len = EVP_EncodeBlock((unsigned char *)response_64,\ndiff --git a/reflog-walk.c b/reflog-walk.c\nindex f8e743a..85b8a54 100644\n--- a/reflog-walk.c\n+++ b/reflog-walk.c\n@@ -56,12 +56,11 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)\n \t\t}\n \t}\n \tif (reflogs->nr == 0) {\n-\t\tint len = strlen(ref);\n-\t\tchar *refname = xmalloc(len + 12);\n-\t\tsprintf(refname, \"refs/%s\", ref);\n+\t\tchar *refname = xstrfmt(\"refs/%s\", ref);\n \t\tfor_each_reflog_ent(refname, read_one_reflog, reflogs);\n \t\tif (reflogs->nr == 0) {\n-\t\t\tsprintf(refname, \"refs/heads/%s\", ref);\n+\t\t\tfree(refname);\n+\t\t\trefname = xstrfmt(\"refs/heads/%s\", ref);\n \t\t\tfor_each_reflog_ent(refname, read_one_reflog, reflogs);\n \t\t}\n \t\tfree(refname);\ndiff --git a/remote.c b/remote.c\nindex 26504b7..5ab0f7f 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -65,7 +65,6 @@ static int valid_remote(const struct remote *remote)\n static const char *alias_url(const char *url, struct rewrites *r)\n {\n \tint i, j;\n-\tchar *ret;\n \tstruct counted_string *longest;\n \tint longest_i;\n \n@@ -86,11 +85,7 @@ static const char *alias_url(const char *url, struct rewrites *r)\n \tif (!longest)\n \t\treturn url;\n \n-\tret = xmalloc(r->rewrite[longest_i]->baselen +\n-\t\t     (strlen(url) - longest->len) + 1);\n-\tstrcpy(ret, r->rewrite[longest_i]->base);\n-\tstrcpy(ret + r->rewrite[longest_i]->baselen, url + longest->len);\n-\treturn ret;\n+\treturn xstrfmt(\"%s%s\", r->rewrite[longest_i]->base, url + longest->len);\n }\n \n static void add_push_refspec(struct remote *remote, const char *ref)\ndiff --git a/setup.c b/setup.c\nindex a17c51e..2b64cbb 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -99,10 +99,7 @@ char *prefix_path_gently(const char *prefix, int len,\n \t\t\treturn NULL;\n \t\t}\n \t} else {\n-\t\tsanitized = xmalloc(len + strlen(path) + 1);\n-\t\tif (len)\n-\t\t\tmemcpy(sanitized, prefix, len);\n-\t\tstrcpy(sanitized + len, path);\n+\t\tsanitized = xstrfmt(\"%.*s%s\", len, prefix, path);\n \t\tif (remaining_prefix)\n \t\t\t*remaining_prefix = len;\n \t\tif (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) {\n@@ -468,11 +465,8 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)\n \n \tif (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) {\n \t\tsize_t pathlen = slash+1 - path;\n-\t\tsize_t dirlen = pathlen + len - 8;\n-\t\tdir = xmalloc(dirlen + 1);\n-\t\tstrncpy(dir, path, pathlen);\n-\t\tstrncpy(dir + pathlen, buf + 8, len - 8);\n-\t\tdir[dirlen] = '\\0';\n+\t\tdir = xstrfmt(\"%.*s%.*s\", (int)pathlen, path,\n+\t\t\t      (int)(len - 8), buf + 8);\n \t\tfree(buf);\n \t\tbuf = dir;\n \t}\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex f932e80..8e2032f 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1350,9 +1350,7 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,\n \t * Then we need to make sure that we do not lose a locally\n \t * present file that is not ignored.\n \t */\n-\tpathbuf = xmalloc(namelen + 2);\n-\tmemcpy(pathbuf, ce->name, namelen);\n-\tstrcpy(pathbuf+namelen, \"/\");\n+\tpathbuf = xstrfmt(\"%.*s/\", namelen, ce->name);\n \n \tmemset(&d, 0, sizeof(d));\n \tif (o->dir)\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270675","messageId":"20150924210705.GX30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 27/68] config: use xstrfmt in normalize_value","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:05Z","receivedAt":"2015-09-24T21:07:05Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We xmalloc a fixed-size buffer and sprintf into it; this is\nOK because the size of our formatting types is finite, but\nthat's not immediately clear to a reader auditing sprintf\ncalls. Let's switch to xstrfmt, which is shorter and\nobviously correct.\n\nNote that just dropping the common xmalloc here causes gcc\nto complain with -Wmaybe-uninitialized. That's because if\n\"types\" does not match any of our known types, we never\nwrite anything into the \"normalized\" pointer. With the\ncurrent code, gcc doesn't notice because we always return a\nvalid pointer (just one which might point to uninitialized\ndata, but the compiler doesn't know that). In other words,\nthe current code is potentially buggy if new types are added\nwithout updating this spot.\n\nSo let's take this opportunity to clean up the function a\nbit more. We can drop the \"normalized\" pointer entirely, and\njust return directly from each code path. And then add an\nassertion at the end in case we haven't covered any cases.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/config.c | 34 +++++++++++++---------------------\n 1 file changed, 13 insertions(+), 21 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 71acc44..adc7727 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -246,8 +246,6 @@ free_strings:\n \n static char *normalize_value(const char *key, const char *value)\n {\n-\tchar *normalized;\n-\n \tif (!value)\n \t\treturn NULL;\n \n@@ -258,27 +256,21 @@ static char *normalize_value(const char *key, const char *value)\n \t\t * \"~/foobar/\" in the config file, and to expand the ~\n \t\t * when retrieving the value.\n \t\t */\n-\t\tnormalized = xstrdup(value);\n-\telse {\n-\t\tnormalized = xmalloc(64);\n-\t\tif (types == TYPE_INT) {\n-\t\t\tint64_t v = git_config_int64(key, value);\n-\t\t\tsprintf(normalized, \"%\"PRId64, v);\n-\t\t}\n-\t\telse if (types == TYPE_BOOL)\n-\t\t\tsprintf(normalized, \"%s\",\n-\t\t\t\tgit_config_bool(key, value) ? \"true\" : \"false\");\n-\t\telse if (types == TYPE_BOOL_OR_INT) {\n-\t\t\tint is_bool, v;\n-\t\t\tv = git_config_bool_or_int(key, value, &is_bool);\n-\t\t\tif (!is_bool)\n-\t\t\t\tsprintf(normalized, \"%d\", v);\n-\t\t\telse\n-\t\t\t\tsprintf(normalized, \"%s\", v ? \"true\" : \"false\");\n-\t\t}\n+\t\treturn xstrdup(value);\n+\tif (types == TYPE_INT)\n+\t\treturn xstrfmt(\"%\"PRId64, git_config_int64(key, value));\n+\tif (types == TYPE_BOOL)\n+\t\treturn xstrdup(git_config_bool(key, value) ?  \"true\" : \"false\");\n+\tif (types == TYPE_BOOL_OR_INT) {\n+\t\tint is_bool, v;\n+\t\tv = git_config_bool_or_int(key, value, &is_bool);\n+\t\tif (!is_bool)\n+\t\t\treturn xstrfmt(\"%d\", v);\n+\t\telse\n+\t\t\treturn xstrdup(v ? \"true\" : \"false\");\n \t}\n \n-\treturn normalized;\n+\tdie(\"BUG: cannot normalize type %d\", types);\n }\n \n static int get_color_found;\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270714","messageId":"20150924210707.GY30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 28/68] fetch: replace static buffer with xstrfmt","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:07Z","receivedAt":"2015-09-24T21:07:07Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We parse the INFINITE_DEPTH constant into a static,\nfixed-size buffer using sprintf. This buffer is sufficiently\nlarge for the current constant, but it's a suspicious\npattern, as the constant is defined far away, and it's not\nimmediately obvious that 12 bytes are large enough to hold\nit.\n\nWe can just use xstrfmt here, which gets rid of any question\nof the buffer size. It also removes any concerns with object\nlifetime, which means we do not have to wonder why this\nbuffer deep within a conditional is marked \"static\" (we\nnever free our newly allocated result, of course, but that's\nOK; it's global that lasts the lifetime of the whole program\nanyway).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/fetch.c | 7 ++-----\n 1 file changed, 2 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 9a3869f..4703725 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1156,11 +1156,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \t\t\tdie(_(\"--depth and --unshallow cannot be used together\"));\n \t\telse if (!is_repository_shallow())\n \t\t\tdie(_(\"--unshallow on a complete repository does not make sense\"));\n-\t\telse {\n-\t\t\tstatic char inf_depth[12];\n-\t\t\tsprintf(inf_depth, \"%d\", INFINITE_DEPTH);\n-\t\t\tdepth = inf_depth;\n-\t\t}\n+\t\telse\n+\t\t\tdepth = xstrfmt(\"%d\", INFINITE_DEPTH);\n \t}\n \n \t/* no need to be strict, transport_set_option() will validate it again */\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270716","messageId":"20150924210709.GZ30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 29/68] use strip_suffix and xstrfmt to replace suffix","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:09Z","receivedAt":"2015-09-24T21:07:09Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When we want to convert \"foo.pack\" to \"foo.idx\", we do it by\nduplicating the original string and then munging the bytes\nin place. Let's use strip_suffix and xstrfmt instead, which\nhas several advantages:\n\n  1. It's more clear what the intent is.\n\n  2. It does not implicitly rely on the fact that\n     strlen(\".idx\") <= strlen(\".pack\") to avoid an overflow.\n\n  3. We communicate the assumption that the input file ends\n     with \".pack\" (and get a run-time check that this is so).\n\n  4. We drop calls to strcpy, which makes auditing the code\n     base easier.\n\nLikewise, we can do this to convert \".pack\" to \".bitmap\",\navoiding some manual memory computation.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n http.c        |  7 ++++---\n pack-bitmap.c | 13 ++++---------\n sha1_file.c   |  6 ++++--\n 3 files changed, 12 insertions(+), 14 deletions(-)\n\ndiff --git a/http.c b/http.c\nindex 7b02259..e0ff876 100644\n--- a/http.c\n+++ b/http.c\n@@ -1511,6 +1511,7 @@ int finish_http_pack_request(struct http_pack_request *preq)\n \tstruct packed_git **lst;\n \tstruct packed_git *p = preq->target;\n \tchar *tmp_idx;\n+\tsize_t len;\n \tstruct child_process ip = CHILD_PROCESS_INIT;\n \tconst char *ip_argv[8];\n \n@@ -1524,9 +1525,9 @@ int finish_http_pack_request(struct http_pack_request *preq)\n \t\tlst = &((*lst)->next);\n \t*lst = (*lst)->next;\n \n-\ttmp_idx = xstrdup(preq->tmpfile);\n-\tstrcpy(tmp_idx + strlen(tmp_idx) - strlen(\".pack.temp\"),\n-\t       \".idx.temp\");\n+\tif (!strip_suffix(preq->tmpfile, \".pack.temp\", &len))\n+\t\tdie(\"BUG: pack tmpfile does not end in .pack.temp?\");\n+\ttmp_idx = xstrfmt(\"%.*s.idx.temp\", (int)len, preq->tmpfile);\n \n \tip_argv[0] = \"index-pack\";\n \tip_argv[1] = \"-o\";\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex 637770a..7dfcb34 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -252,16 +252,11 @@ static int load_bitmap_entries_v1(struct bitmap_index *index)\n \n static char *pack_bitmap_filename(struct packed_git *p)\n {\n-\tchar *idx_name;\n-\tint len;\n-\n-\tlen = strlen(p->pack_name) - strlen(\".pack\");\n-\tidx_name = xmalloc(len + strlen(\".bitmap\") + 1);\n-\n-\tmemcpy(idx_name, p->pack_name, len);\n-\tmemcpy(idx_name + len, \".bitmap\", strlen(\".bitmap\") + 1);\n+\tsize_t len;\n \n-\treturn idx_name;\n+\tif (!strip_suffix(p->pack_name, \".pack\", &len))\n+\t\tdie(\"BUG: pack_name does not end in .pack\");\n+\treturn xstrfmt(\"%.*s.bitmap\", (int)len, p->pack_name);\n }\n \n static int open_pack_bitmap_1(struct packed_git *packfile)\ndiff --git a/sha1_file.c b/sha1_file.c\nindex 592226e..2be1afd 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -671,13 +671,15 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)\n int open_pack_index(struct packed_git *p)\n {\n \tchar *idx_name;\n+\tsize_t len;\n \tint ret;\n \n \tif (p->index_data)\n \t\treturn 0;\n \n-\tidx_name = xstrdup(p->pack_name);\n-\tstrcpy(idx_name + strlen(idx_name) - strlen(\".pack\"), \".idx\");\n+\tif (!strip_suffix(p->pack_name, \".pack\", &len))\n+\t\tdie(\"BUG: pack_name does not end in .pack\");\n+\tidx_name = xstrfmt(\"%.*s.idx\", (int)len, p->pack_name);\n \tret = check_packed_git_idx(idx_name, p);\n \tfree(idx_name);\n \treturn ret;\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270718","messageId":"20150924210711.GA30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 30/68] ref-filter: drop sprintf and strcpy calls","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:12Z","receivedAt":"2015-09-24T21:07:12Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The ref-filter code comes from for-each-ref, and inherited a\nnumber of raw sprintf and strcpy calls. These are generally\nall safe, as we custom-size the buffers, or are formatting\nnumbers into sufficiently large buffers. But we can make the\nresulting code even simpler and more obviously correct by\nusing some of our helper functions.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n ref-filter.c | 70 +++++++++++++++++++-----------------------------------------\n 1 file changed, 22 insertions(+), 48 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex f38dee4..1f71870 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -192,9 +192,7 @@ static int grab_objectname(const char *name, const unsigned char *sha1,\n \t\t\t    struct atom_value *v)\n {\n \tif (!strcmp(name, \"objectname\")) {\n-\t\tchar *s = xmalloc(41);\n-\t\tstrcpy(s, sha1_to_hex(sha1));\n-\t\tv->s = s;\n+\t\tv->s = xstrdup(sha1_to_hex(sha1));\n \t\treturn 1;\n \t}\n \tif (!strcmp(name, \"objectname:short\")) {\n@@ -219,10 +217,8 @@ static void grab_common_values(struct atom_value *val, int deref, struct object\n \t\tif (!strcmp(name, \"objecttype\"))\n \t\t\tv->s = typename(obj->type);\n \t\telse if (!strcmp(name, \"objectsize\")) {\n-\t\t\tchar *s = xmalloc(40);\n-\t\t\tsprintf(s, \"%lu\", sz);\n \t\t\tv->ul = sz;\n-\t\t\tv->s = s;\n+\t\t\tv->s = xstrfmt(\"%lu\", sz);\n \t\t}\n \t\telse if (deref)\n \t\t\tgrab_objectname(name, obj->sha1, v);\n@@ -246,11 +242,8 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob\n \t\t\tv->s = tag->tag;\n \t\telse if (!strcmp(name, \"type\") && tag->tagged)\n \t\t\tv->s = typename(tag->tagged->type);\n-\t\telse if (!strcmp(name, \"object\") && tag->tagged) {\n-\t\t\tchar *s = xmalloc(41);\n-\t\t\tstrcpy(s, sha1_to_hex(tag->tagged->sha1));\n-\t\t\tv->s = s;\n-\t\t}\n+\t\telse if (!strcmp(name, \"object\") && tag->tagged)\n+\t\t\tv->s = xstrdup(sha1_to_hex(tag->tagged->sha1));\n \t}\n }\n \n@@ -268,32 +261,22 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object\n \t\tif (deref)\n \t\t\tname++;\n \t\tif (!strcmp(name, \"tree\")) {\n-\t\t\tchar *s = xmalloc(41);\n-\t\t\tstrcpy(s, sha1_to_hex(commit->tree->object.sha1));\n-\t\t\tv->s = s;\n+\t\t\tv->s = xstrdup(sha1_to_hex(commit->tree->object.sha1));\n \t\t}\n-\t\tif (!strcmp(name, \"numparent\")) {\n-\t\t\tchar *s = xmalloc(40);\n+\t\telse if (!strcmp(name, \"numparent\")) {\n \t\t\tv->ul = commit_list_count(commit->parents);\n-\t\t\tsprintf(s, \"%lu\", v->ul);\n-\t\t\tv->s = s;\n+\t\t\tv->s = xstrfmt(\"%lu\", v->ul);\n \t\t}\n \t\telse if (!strcmp(name, \"parent\")) {\n-\t\t\tint num = commit_list_count(commit->parents);\n-\t\t\tint i;\n \t\t\tstruct commit_list *parents;\n-\t\t\tchar *s = xmalloc(41 * num + 1);\n-\t\t\tv->s = s;\n-\t\t\tfor (i = 0, parents = commit->parents;\n-\t\t\t     parents;\n-\t\t\t     parents = parents->next, i = i + 41) {\n+\t\t\tstruct strbuf s = STRBUF_INIT;\n+\t\t\tfor (parents = commit->parents; parents; parents = parents->next) {\n \t\t\t\tstruct commit *parent = parents->item;\n-\t\t\t\tstrcpy(s+i, sha1_to_hex(parent->object.sha1));\n-\t\t\t\tif (parents->next)\n-\t\t\t\t\ts[i+40] = ' ';\n+\t\t\t\tif (parents != commit->parents)\n+\t\t\t\t\tstrbuf_addch(&s, ' ');\n+\t\t\t\tstrbuf_addstr(&s, sha1_to_hex(parent->object.sha1));\n \t\t\t}\n-\t\t\tif (!i)\n-\t\t\t\t*s = '\\0';\n+\t\t\tv->s = strbuf_detach(&s, NULL);\n \t\t}\n \t}\n }\n@@ -700,7 +683,6 @@ static void populate_value(struct ref_array_item *ref)\n \t\t\telse if (!strcmp(formatp, \"track\") &&\n \t\t\t\t (starts_with(name, \"upstream\") ||\n \t\t\t\t  starts_with(name, \"push\"))) {\n-\t\t\t\tchar buf[40];\n \n \t\t\t\tif (stat_tracking_info(branch, &num_ours,\n \t\t\t\t\t\t       &num_theirs, NULL))\n@@ -708,17 +690,13 @@ static void populate_value(struct ref_array_item *ref)\n \n \t\t\t\tif (!num_ours && !num_theirs)\n \t\t\t\t\tv->s = \"\";\n-\t\t\t\telse if (!num_ours) {\n-\t\t\t\t\tsprintf(buf, \"[behind %d]\", num_theirs);\n-\t\t\t\t\tv->s = xstrdup(buf);\n-\t\t\t\t} else if (!num_theirs) {\n-\t\t\t\t\tsprintf(buf, \"[ahead %d]\", num_ours);\n-\t\t\t\t\tv->s = xstrdup(buf);\n-\t\t\t\t} else {\n-\t\t\t\t\tsprintf(buf, \"[ahead %d, behind %d]\",\n-\t\t\t\t\t\tnum_ours, num_theirs);\n-\t\t\t\t\tv->s = xstrdup(buf);\n-\t\t\t\t}\n+\t\t\t\telse if (!num_ours)\n+\t\t\t\t\tv->s = xstrfmt(\"[behind %d]\", num_theirs);\n+\t\t\t\telse if (!num_theirs)\n+\t\t\t\t\tv->s = xstrfmt(\"[ahead %d]\", num_ours);\n+\t\t\t\telse\n+\t\t\t\t\tv->s = xstrfmt(\"[ahead %d, behind %d]\",\n+\t\t\t\t\t\t       num_ours, num_theirs);\n \t\t\t\tcontinue;\n \t\t\t} else if (!strcmp(formatp, \"trackshort\") &&\n \t\t\t\t   (starts_with(name, \"upstream\") ||\n@@ -745,12 +723,8 @@ static void populate_value(struct ref_array_item *ref)\n \n \t\tif (!deref)\n \t\t\tv->s = refname;\n-\t\telse {\n-\t\t\tint len = strlen(refname);\n-\t\t\tchar *s = xmalloc(len + 4);\n-\t\t\tsprintf(s, \"%s^{}\", refname);\n-\t\t\tv->s = s;\n-\t\t}\n+\t\telse\n+\t\t\tv->s = xstrfmt(\"%s^{}\", refname);\n \t}\n \n \tfor (i = 0; i < used_atom_cnt; i++) {\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270676","messageId":"20150924210713.GB30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 31/68] help: drop prepend function in favor of xstrfmt","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:14Z","receivedAt":"2015-09-24T21:07:14Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This function predates xstrfmt, and its functionality is a\nsubset. Let's just use xstrfmt.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/help.c | 14 ++------------\n 1 file changed, 2 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/help.c b/builtin/help.c\nindex 3422e73..fba8c01 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -295,16 +295,6 @@ static int is_git_command(const char *s)\n \t\tis_in_cmdlist(&other_cmds, s);\n }\n \n-static const char *prepend(const char *prefix, const char *cmd)\n-{\n-\tsize_t pre_len = strlen(prefix);\n-\tsize_t cmd_len = strlen(cmd);\n-\tchar *p = xmalloc(pre_len + cmd_len + 1);\n-\tmemcpy(p, prefix, pre_len);\n-\tstrcpy(p + pre_len, cmd);\n-\treturn p;\n-}\n-\n static const char *cmd_to_page(const char *git_cmd)\n {\n \tif (!git_cmd)\n@@ -312,9 +302,9 @@ static const char *cmd_to_page(const char *git_cmd)\n \telse if (starts_with(git_cmd, \"git\"))\n \t\treturn git_cmd;\n \telse if (is_git_command(git_cmd))\n-\t\treturn prepend(\"git-\", git_cmd);\n+\t\treturn xstrfmt(\"git-%s\", git_cmd);\n \telse\n-\t\treturn prepend(\"git\", git_cmd);\n+\t\treturn xstrfmt(\"git%s\", git_cmd);\n }\n \n static void setup_man_path(void)\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270678","messageId":"20150924210716.GC30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 32/68] mailmap: replace strcpy with xstrdup","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:16Z","receivedAt":"2015-09-24T21:07:16Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We want to make a copy of a string without any leading\nwhitespace. To do so, we allocate a buffer large enough to\nhold the original, skip past the whitespace, then copy that.\nIt's much simpler to just allocate after we've skipped, in\nwhich case we can just copy the remainder of the string,\nleaving no question of whether \"len\" is large enough.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n mailmap.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/mailmap.c b/mailmap.c\nindex 9e95897..f4a0f1c 100644\n--- a/mailmap.c\n+++ b/mailmap.c\n@@ -162,11 +162,10 @@ static void read_mailmap_line(struct string_list *map, char *buffer,\n \t\t\tchar *cp;\n \n \t\t\tfree(*repo_abbrev);\n-\t\t\t*repo_abbrev = xmalloc(len);\n \n \t\t\tfor (cp = buffer + abblen; isspace(*cp); cp++)\n \t\t\t\t; /* nothing */\n-\t\t\tstrcpy(*repo_abbrev, cp);\n+\t\t\t*repo_abbrev = xstrdup(cp);\n \t\t}\n \t\treturn;\n \t}\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270677","messageId":"20150924210718.GD30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 33/68] read_branches_file: simplify string handling","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:18Z","receivedAt":"2015-09-24T21:07:18Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This function does a lot of manual string handling, and has\nsome unnecessary limits. This patch cleans up a number of\nthings:\n\n  1. Drop the arbitrary 1000-byte limit on the size of the\n     remote name (we do not have such a limit in any of the\n     other remote-reading mechanisms).\n\n  2. Replace fgets into a fixed-size buffer with a strbuf,\n     eliminating any limits on the length of the URL.\n\n  3. Replace manual whitespace handling with strbuf_trim\n     (since we now have a strbuf). This also gets rid\n     of a call to strcpy, and the confusing reuse of the \"p\"\n     pointer for multiple purposes.\n\n  4. We currently build up the refspecs over multiple strbuf\n     calls. We do this to handle the fact that the URL \"frag\"\n     may not be present. But rather than have multiple\n     conditionals, let's just default \"frag\" to \"master\".\n     This lets us format the refspecs with a single xstrfmt.\n     It's shorter, and easier to see what the final string\n     looks like.\n\n     We also update the misleading comment in this area (the\n     local branch is named after the remote name, not after\n     the branch name on the remote side).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n remote.c | 54 ++++++++++++++++++++----------------------------------\n 1 file changed, 20 insertions(+), 34 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 5ab0f7f..22a60fc 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -293,56 +293,42 @@ static void read_remotes_file(struct remote *remote)\n static void read_branches_file(struct remote *remote)\n {\n \tchar *frag;\n-\tstruct strbuf branch = STRBUF_INIT;\n-\tint n = 1000;\n-\tFILE *f = fopen(git_path(\"branches/%.*s\", n, remote->name), \"r\");\n-\tchar *s, *p;\n-\tint len;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tFILE *f = fopen(git_path(\"branches/%s\", remote->name), \"r\");\n \n \tif (!f)\n \t\treturn;\n-\ts = fgets(buffer, BUF_SIZE, f);\n-\tfclose(f);\n-\tif (!s)\n-\t\treturn;\n-\twhile (isspace(*s))\n-\t\ts++;\n-\tif (!*s)\n+\n+\tstrbuf_getline(&buf, f, '\\n');\n+\tstrbuf_trim(&buf);\n+\tif (!buf.len) {\n+\t\tstrbuf_release(&buf);\n \t\treturn;\n+\t}\n+\n \tremote->origin = REMOTE_BRANCHES;\n-\tp = s + strlen(s);\n-\twhile (isspace(p[-1]))\n-\t\t*--p = 0;\n-\tlen = p - s;\n-\tp = xmalloc(len + 1);\n-\tstrcpy(p, s);\n \n \t/*\n \t * The branches file would have URL and optionally\n \t * #branch specified.  The \"master\" (or specified) branch is\n-\t * fetched and stored in the local branch of the same name.\n+\t * fetched and stored in the local branch matching the\n+\t * remote name.\n \t */\n-\tfrag = strchr(p, '#');\n-\tif (frag) {\n+\tfrag = strchr(buf.buf, '#');\n+\tif (frag)\n \t\t*(frag++) = '\\0';\n-\t\tstrbuf_addf(&branch, \"refs/heads/%s\", frag);\n-\t} else\n-\t\tstrbuf_addstr(&branch, \"refs/heads/master\");\n+\telse\n+\t\tfrag = \"master\";\n+\n+\tadd_url_alias(remote, strbuf_detach(&buf, NULL));\n+\tadd_fetch_refspec(remote, xstrfmt(\"refs/heads/%s:refs/heads/%s\",\n+\t\t\t\t\t  frag, remote->name));\n \n-\tstrbuf_addf(&branch, \":refs/heads/%s\", remote->name);\n-\tadd_url_alias(remote, p);\n-\tadd_fetch_refspec(remote, strbuf_detach(&branch, NULL));\n \t/*\n \t * Cogito compatible push: push current HEAD to remote #branch\n \t * (master if missing)\n \t */\n-\tstrbuf_init(&branch, 0);\n-\tstrbuf_addstr(&branch, \"HEAD\");\n-\tif (frag)\n-\t\tstrbuf_addf(&branch, \":refs/heads/%s\", frag);\n-\telse\n-\t\tstrbuf_addstr(&branch, \":refs/heads/master\");\n-\tadd_push_refspec(remote, strbuf_detach(&branch, NULL));\n+\tadd_push_refspec(remote, xstrfmt(\"HEAD:refs/heads/%s\", frag));\n \tremote->fetch_tags = 1; /* always auto-follow */\n }\n \n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270713","messageId":"20150924210720.GE30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 34/68] read_remotes_file: simplify string handling","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:20Z","receivedAt":"2015-09-24T21:07:20Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The main motivation for this cleanup is to switch our\nline-reading to a strbuf, which removes the use of a\nfixed-size buffer (which limited the size of remote URLs).\nSince we have the strbuf, we can make use of strbuf_rtrim().\n\nWhile we're here, we can also simplify the parsing of each\nline.  First, we can use skip_prefix() to avoid some magic\nnumbers.\n\nBut second, we can avoid splitting the parsing and actions\nfor each line into two stages. Right now we figure out which\ntype of line we have, set an int to a magic number,\nskip any intermediate whitespace, and then act on\nthe resulting value based on the magic number.\n\nInstead, let's factor the whitespace skipping into a\nfunction. That lets us avoid the magic numbers and keep the\nactions close to the parsing.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n remote.c | 55 ++++++++++++++++++-------------------------------------\n 1 file changed, 18 insertions(+), 37 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 22a60fc..a01f13a 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -54,9 +54,6 @@ static const char *pushremote_name;\n static struct rewrites rewrites;\n static struct rewrites rewrites_push;\n \n-#define BUF_SIZE (2048)\n-static char buffer[BUF_SIZE];\n-\n static int valid_remote(const struct remote *remote)\n {\n \treturn (!!remote->url) || (!!remote->foreign_vcs);\n@@ -243,50 +240,34 @@ static void add_instead_of(struct rewrite *rewrite, const char *instead_of)\n \trewrite->instead_of_nr++;\n }\n \n+static const char *skip_spaces(const char *s)\n+{\n+\twhile (isspace(*s))\n+\t\ts++;\n+\treturn s;\n+}\n+\n static void read_remotes_file(struct remote *remote)\n {\n+\tstruct strbuf buf = STRBUF_INIT;\n \tFILE *f = fopen(git_path(\"remotes/%s\", remote->name), \"r\");\n \n \tif (!f)\n \t\treturn;\n \tremote->origin = REMOTE_REMOTES;\n-\twhile (fgets(buffer, BUF_SIZE, f)) {\n-\t\tint value_list;\n-\t\tchar *s, *p;\n-\n-\t\tif (starts_with(buffer, \"URL:\")) {\n-\t\t\tvalue_list = 0;\n-\t\t\ts = buffer + 4;\n-\t\t} else if (starts_with(buffer, \"Push:\")) {\n-\t\t\tvalue_list = 1;\n-\t\t\ts = buffer + 5;\n-\t\t} else if (starts_with(buffer, \"Pull:\")) {\n-\t\t\tvalue_list = 2;\n-\t\t\ts = buffer + 5;\n-\t\t} else\n-\t\t\tcontinue;\n-\n-\t\twhile (isspace(*s))\n-\t\t\ts++;\n-\t\tif (!*s)\n-\t\t\tcontinue;\n+\twhile (strbuf_getline(&buf, f, '\\n') != EOF) {\n+\t\tconst char *v;\n \n-\t\tp = s + strlen(s);\n-\t\twhile (isspace(p[-1]))\n-\t\t\t*--p = 0;\n+\t\tstrbuf_rtrim(&buf);\n \n-\t\tswitch (value_list) {\n-\t\tcase 0:\n-\t\t\tadd_url_alias(remote, xstrdup(s));\n-\t\t\tbreak;\n-\t\tcase 1:\n-\t\t\tadd_push_refspec(remote, xstrdup(s));\n-\t\t\tbreak;\n-\t\tcase 2:\n-\t\t\tadd_fetch_refspec(remote, xstrdup(s));\n-\t\t\tbreak;\n-\t\t}\n+\t\tif (skip_prefix(buf.buf, \"URL:\", &v))\n+\t\t\tadd_url_alias(remote, xstrdup(skip_spaces(v)));\n+\t\telse if (skip_prefix(buf.buf, \"Push:\", &v))\n+\t\t\tadd_push_refspec(remote, xstrdup(skip_spaces(v)));\n+\t\telse if (skip_prefix(buf.buf, \"Pull:\", &v))\n+\t\t\tadd_fetch_refspec(remote, xstrdup(skip_spaces(v)));\n \t}\n+\tstrbuf_release(&buf);\n \tfclose(f);\n }\n \n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270679","messageId":"20150924210722.GF30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 35/68] resolve_ref: use strbufs for internal buffers","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:22Z","receivedAt":"2015-09-24T21:07:22Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"resolve_ref already uses a strbuf internally when generating\npathnames, but it uses fixed-size buffers for storing the\nrefname and symbolic refs. This means that you cannot\nactually point HEAD to a ref that is larger than 256 bytes.\n\nWe can lift this limit by using strbufs here, too. Like\nsb_path, we pass the the buffers into our helper function,\nso that we can easily clean up all output paths. We can also\ndrop the \"unsafe\" name from our helper function, as it no\nlonger uses a single static buffer (but of course\nresolve_ref_unsafe is still unsafe, because the static\nbuffers moved there).\n\nAs a bonus, we also get to drop some strcpy calls between\nthe two fixed buffers (that cannot currently overflow\nbecause the two buffers are sized identically).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n refs.c                  | 57 ++++++++++++++++++++++++++-----------------------\n t/t1401-symbolic-ref.sh | 29 +++++++++++++++++++++++++\n 2 files changed, 59 insertions(+), 27 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex d5c8b2f..c2709de 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1579,16 +1579,15 @@ static int resolve_missing_loose_ref(const char *refname,\n }\n \n /* This function needs to return a meaningful errno on failure */\n-static const char *resolve_ref_unsafe_1(const char *refname,\n-\t\t\t\t\tint resolve_flags,\n-\t\t\t\t\tunsigned char *sha1,\n-\t\t\t\t\tint *flags,\n-\t\t\t\t\tstruct strbuf *sb_path)\n+static const char *resolve_ref_1(const char *refname,\n+\t\t\t\t int resolve_flags,\n+\t\t\t\t unsigned char *sha1,\n+\t\t\t\t int *flags,\n+\t\t\t\t struct strbuf *sb_refname,\n+\t\t\t\t struct strbuf *sb_path,\n+\t\t\t\t struct strbuf *sb_contents)\n {\n \tint depth = MAXDEPTH;\n-\tssize_t len;\n-\tchar buffer[256];\n-\tstatic char refname_buffer[256];\n \tint bad_name = 0;\n \n \tif (flags)\n@@ -1654,19 +1653,18 @@ static const char *resolve_ref_unsafe_1(const char *refname,\n \n \t\t/* Follow \"normalized\" - ie \"refs/..\" symlinks by hand */\n \t\tif (S_ISLNK(st.st_mode)) {\n-\t\t\tlen = readlink(path, buffer, sizeof(buffer)-1);\n-\t\t\tif (len < 0) {\n+\t\t\tstrbuf_reset(sb_contents);\n+\t\t\tif (strbuf_readlink(sb_contents, path, 0) < 0) {\n \t\t\t\tif (errno == ENOENT || errno == EINVAL)\n \t\t\t\t\t/* inconsistent with lstat; retry */\n \t\t\t\t\tgoto stat_ref;\n \t\t\t\telse\n \t\t\t\t\treturn NULL;\n \t\t\t}\n-\t\t\tbuffer[len] = 0;\n-\t\t\tif (starts_with(buffer, \"refs/\") &&\n-\t\t\t\t\t!check_refname_format(buffer, 0)) {\n-\t\t\t\tstrcpy(refname_buffer, buffer);\n-\t\t\t\trefname = refname_buffer;\n+\t\t\tif (starts_with(sb_contents->buf, \"refs/\") &&\n+\t\t\t    !check_refname_format(sb_contents->buf, 0)) {\n+\t\t\t\tstrbuf_swap(sb_refname, sb_contents);\n+\t\t\t\trefname = sb_refname->buf;\n \t\t\t\tif (flags)\n \t\t\t\t\t*flags |= REF_ISSYMREF;\n \t\t\t\tif (resolve_flags & RESOLVE_REF_NO_RECURSE) {\n@@ -1695,28 +1693,26 @@ static const char *resolve_ref_unsafe_1(const char *refname,\n \t\t\telse\n \t\t\t\treturn NULL;\n \t\t}\n-\t\tlen = read_in_full(fd, buffer, sizeof(buffer)-1);\n-\t\tif (len < 0) {\n+\t\tstrbuf_reset(sb_contents);\n+\t\tif (strbuf_read(sb_contents, fd, 256) < 0) {\n \t\t\tint save_errno = errno;\n \t\t\tclose(fd);\n \t\t\terrno = save_errno;\n \t\t\treturn NULL;\n \t\t}\n \t\tclose(fd);\n-\t\twhile (len && isspace(buffer[len-1]))\n-\t\t\tlen--;\n-\t\tbuffer[len] = '\\0';\n+\t\tstrbuf_rtrim(sb_contents);\n \n \t\t/*\n \t\t * Is it a symbolic ref?\n \t\t */\n-\t\tif (!starts_with(buffer, \"ref:\")) {\n+\t\tif (!starts_with(sb_contents->buf, \"ref:\")) {\n \t\t\t/*\n \t\t\t * Please note that FETCH_HEAD has a second\n \t\t\t * line containing other data.\n \t\t\t */\n-\t\t\tif (get_sha1_hex(buffer, sha1) ||\n-\t\t\t    (buffer[40] != '\\0' && !isspace(buffer[40]))) {\n+\t\t\tif (get_sha1_hex(sb_contents->buf, sha1) ||\n+\t\t\t    (sb_contents->buf[40] != '\\0' && !isspace(sb_contents->buf[40]))) {\n \t\t\t\tif (flags)\n \t\t\t\t\t*flags |= REF_ISBROKEN;\n \t\t\t\terrno = EINVAL;\n@@ -1731,10 +1727,12 @@ static const char *resolve_ref_unsafe_1(const char *refname,\n \t\t}\n \t\tif (flags)\n \t\t\t*flags |= REF_ISSYMREF;\n-\t\tbuf = buffer + 4;\n+\t\tbuf = sb_contents->buf + 4;\n \t\twhile (isspace(*buf))\n \t\t\tbuf++;\n-\t\trefname = strcpy(refname_buffer, buf);\n+\t\tstrbuf_reset(sb_refname);\n+\t\tstrbuf_addstr(sb_refname, buf);\n+\t\trefname = sb_refname->buf;\n \t\tif (resolve_flags & RESOLVE_REF_NO_RECURSE) {\n \t\t\thashclr(sha1);\n \t\t\treturn refname;\n@@ -1756,10 +1754,15 @@ static const char *resolve_ref_unsafe_1(const char *refname,\n const char *resolve_ref_unsafe(const char *refname, int resolve_flags,\n \t\t\t       unsigned char *sha1, int *flags)\n {\n+\tstatic struct strbuf sb_refname = STRBUF_INIT;\n+\tstruct strbuf sb_contents = STRBUF_INIT;\n \tstruct strbuf sb_path = STRBUF_INIT;\n-\tconst char *ret = resolve_ref_unsafe_1(refname, resolve_flags,\n-\t\t\t\t\t       sha1, flags, &sb_path);\n+\tconst char *ret;\n+\n+\tret = resolve_ref_1(refname, resolve_flags, sha1, flags,\n+\t\t\t    &sb_refname, &sb_path, &sb_contents);\n \tstrbuf_release(&sb_path);\n+\tstrbuf_release(&sb_contents);\n \treturn ret;\n }\n \ndiff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh\nindex 36378b0..20b022a 100755\n--- a/t/t1401-symbolic-ref.sh\n+++ b/t/t1401-symbolic-ref.sh\n@@ -63,4 +63,33 @@ test_expect_success 'symbolic-ref fails to delete real ref' '\n '\n reset_to_sane\n \n+test_expect_success 'create large ref name' '\n+\t# make 256+ character ref; some systems may not handle that,\n+\t# so be gentle\n+\tlong=0123456789abcdef &&\n+\tlong=$long/$long/$long/$long &&\n+\tlong=$long/$long/$long/$long &&\n+\tlong_ref=refs/heads/$long &&\n+\ttree=$(git write-tree) &&\n+\tcommit=$(echo foo | git commit-tree $tree) &&\n+\tif git update-ref $long_ref $commit; then\n+\t\ttest_set_prereq LONG_REF\n+\telse\n+\t\techo >&2 \"long refs not supported\"\n+\tfi\n+'\n+\n+test_expect_success LONG_REF 'symbolic-ref can point to large ref name' '\n+\tgit symbolic-ref HEAD $long_ref &&\n+\techo $long_ref >expect &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success LONG_REF 'we can parse long symbolic ref' '\n+\techo $commit >expect &&\n+\tgit rev-parse --verify HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270681","messageId":"20150924210724.GG30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 36/68] upload-archive: convert sprintf to strbuf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:25Z","receivedAt":"2015-09-24T21:07:25Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When we report an error to the client, we format it into a\nfixed-size buffer using vsprintf(). This can't actually\noverflow in practice, since we only format a very tame\nsubset of strings (mostly strerror() output). However, it's\nhard to tell immediately, so let's just use a strbuf so\nreaders do not have to wonder.\n\nWe do add an allocation here, but the performance is not\nimportant; the next step is to call die() anyway.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/upload-archive.c | 9 ++++-----\n 1 file changed, 4 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/upload-archive.c b/builtin/upload-archive.c\nindex 32ab94c..dbfe14f 100644\n--- a/builtin/upload-archive.c\n+++ b/builtin/upload-archive.c\n@@ -49,15 +49,14 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)\n __attribute__((format (printf, 1, 2)))\n static void error_clnt(const char *fmt, ...)\n {\n-\tchar buf[1024];\n+\tstruct strbuf buf = STRBUF_INIT;\n \tva_list params;\n-\tint len;\n \n \tva_start(params, fmt);\n-\tlen = vsprintf(buf, fmt, params);\n+\tstrbuf_vaddf(&buf, fmt, params);\n \tva_end(params);\n-\tsend_sideband(1, 3, buf, len, LARGE_PACKET_MAX);\n-\tdie(\"sent error to the client: %s\", buf);\n+\tsend_sideband(1, 3, buf.buf, buf.len, LARGE_PACKET_MAX);\n+\tdie(\"sent error to the client: %s\", buf.buf);\n }\n \n static ssize_t process_input(int child_fd, int band)\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270715","messageId":"20150924210727.GH30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 37/68] remote-ext: simplify git pkt-line generation","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:27Z","receivedAt":"2015-09-24T21:07:27Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We format a pkt-line into a heap buffer, which requires\nmanual computation of the required size, and uses some bare\nsprintf calls. We could use a strbuf instead, which would\ntake care of the computation for us. But it's even easier\nstill to use packet_write(). Besides handling the formatting\nand writing for us, it fixes two things:\n\n  1. Our manual max-size check used 0xFFFF, while technically\n     LARGE_PACKET_MAX is slightly smaller than this.\n\n  2. Our packet will now be output as part of\n     GIT_TRACE_PACKET debugging.\n\nUnfortunately packet_write() does not let us build up the\nbuffer progressively, so we do have to repeat ourselves a\nlittle depending on the \"vhost\" setting, but the end result\nis still far more readable than the original.\n\nSince there were no tests covering this feature at all,\nwe'll add a few into t5802.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/remote-ext.c      | 34 +++++-----------------------------\n t/t5802-connect-helper.sh | 28 ++++++++++++++++++++++++++++\n 2 files changed, 33 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/remote-ext.c b/builtin/remote-ext.c\nindex 3b8c22c..e3cd25d 100644\n--- a/builtin/remote-ext.c\n+++ b/builtin/remote-ext.c\n@@ -1,6 +1,7 @@\n #include \"builtin.h\"\n #include \"transport.h\"\n #include \"run-command.h\"\n+#include \"pkt-line.h\"\n \n /*\n  * URL syntax:\n@@ -142,36 +143,11 @@ static const char **parse_argv(const char *arg, const char *service)\n static void send_git_request(int stdin_fd, const char *serv, const char *repo,\n \tconst char *vhost)\n {\n-\tsize_t bufferspace;\n-\tsize_t wpos = 0;\n-\tchar *buffer;\n-\n-\t/*\n-\t * Request needs 12 bytes extra if there is vhost (xxxx \\0host=\\0) and\n-\t * 6 bytes extra (xxxx \\0) if there is no vhost.\n-\t */\n-\tif (vhost)\n-\t\tbufferspace = strlen(serv) + strlen(repo) + strlen(vhost) + 12;\n+\tif (!vhost)\n+\t\tpacket_write(stdin_fd, \"%s %s%c\", serv, repo, 0);\n \telse\n-\t\tbufferspace = strlen(serv) + strlen(repo) + 6;\n-\n-\tif (bufferspace > 0xFFFF)\n-\t\tdie(\"Request too large to send\");\n-\tbuffer = xmalloc(bufferspace);\n-\n-\t/* Make the packet. */\n-\twpos = sprintf(buffer, \"%04x%s %s%c\", (unsigned)bufferspace,\n-\t\tserv, repo, 0);\n-\n-\t/* Add vhost if any. */\n-\tif (vhost)\n-\t\tsprintf(buffer + wpos, \"host=%s%c\", vhost, 0);\n-\n-\t/* Send the request */\n-\tif (write_in_full(stdin_fd, buffer, bufferspace) < 0)\n-\t\tdie_errno(\"Failed to send request\");\n-\n-\tfree(buffer);\n+\t\tpacket_write(stdin_fd, \"%s %s%chost=%s%c\", serv, repo, 0,\n+\t\t\t     vhost, 0);\n }\n \n static int run_child(const char *arg, const char *service)\ndiff --git a/t/t5802-connect-helper.sh b/t/t5802-connect-helper.sh\nindex 878faf2..b7a7f9d 100755\n--- a/t/t5802-connect-helper.sh\n+++ b/t/t5802-connect-helper.sh\n@@ -69,4 +69,32 @@ test_expect_success 'update backfilled tag without primary transfer' '\n \ttest_cmp expect actual\n '\n \n+\n+test_expect_success 'set up fake git-daemon' '\n+\tmkdir remote &&\n+\tgit init --bare remote/one.git &&\n+\tmkdir remote/host &&\n+\tgit init --bare remote/host/two.git &&\n+\twrite_script fake-daemon <<-\\EOF &&\n+\tgit daemon --inetd \\\n+\t\t--informative-errors \\\n+\t\t--export-all \\\n+\t\t--base-path=\"$TRASH_DIRECTORY/remote\" \\\n+\t\t--interpolated-path=\"$TRASH_DIRECTORY/remote/%H%D\" \\\n+\t\t\"$TRASH_DIRECTORY/remote\"\n+\tEOF\n+\texport TRASH_DIRECTORY &&\n+\tPATH=$TRASH_DIRECTORY:$PATH\n+'\n+\n+test_expect_success 'ext command can connect to git daemon (no vhost)' '\n+\trm -rf dst &&\n+\tgit clone \"ext::fake-daemon %G/one.git\" dst\n+'\n+\n+test_expect_success 'ext command can connect to git daemon (vhost)' '\n+\trm -rf dst &&\n+\tgit clone \"ext::fake-daemon %G/two.git %Vhost\" dst\n+'\n+\n test_done\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270712","messageId":"20150924210729.GI30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 38/68] http-push: use strbuf instead of fwrite_buffer","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:29Z","receivedAt":"2015-09-24T21:07:29Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The http-push code defines an fwrite_buffer function for use\nas a curl callback; it just writes to a strbuf. There's no\nreason we need to use it ourselves, as we know we have a\nstrbuf. This lets us format directly into it, rather than\ndealing with an extra temporary buffer (which required\nmanual length computation).\n\nWhile we're here, let's also remove the literal tabs from\nthe source in favor of \"\\t\", which is more visually obvious.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n http-push.c | 21 +++++----------------\n 1 file changed, 5 insertions(+), 16 deletions(-)\n\ndiff --git a/http-push.c b/http-push.c\nindex 37baff8..e501c28 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1459,8 +1459,6 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls)\n {\n \tstruct strbuf *buf = (struct strbuf *)ls->userData;\n \tstruct object *o;\n-\tint len;\n-\tchar *ref_info;\n \tstruct ref *ref;\n \n \tref = alloc_ref(ls->dentry_name);\n@@ -1484,23 +1482,14 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls)\n \t\treturn;\n \t}\n \n-\tlen = strlen(ls->dentry_name) + 42;\n-\tref_info = xcalloc(len + 1, 1);\n-\tsprintf(ref_info, \"%s\t%s\\n\",\n-\t\tsha1_to_hex(ref->old_sha1), ls->dentry_name);\n-\tfwrite_buffer(ref_info, 1, len, buf);\n-\tfree(ref_info);\n+\tstrbuf_addf(buf, \"%s\\t%s\\n\",\n+\t\t    sha1_to_hex(ref->old_sha1), ls->dentry_name);\n \n \tif (o->type == OBJ_TAG) {\n \t\to = deref_tag(o, ls->dentry_name, 0);\n-\t\tif (o) {\n-\t\t\tlen = strlen(ls->dentry_name) + 45;\n-\t\t\tref_info = xcalloc(len + 1, 1);\n-\t\t\tsprintf(ref_info, \"%s\t%s^{}\\n\",\n-\t\t\t\tsha1_to_hex(o->sha1), ls->dentry_name);\n-\t\t\tfwrite_buffer(ref_info, 1, len, buf);\n-\t\t\tfree(ref_info);\n-\t\t}\n+\t\tif (o)\n+\t\t\tstrbuf_addf(buf, \"%s\\t%s^{}\\n\",\n+\t\t\t\t    sha1_to_hex(o->sha1), ls->dentry_name);\n \t}\n \tfree(ref);\n }\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270680","messageId":"20150924210731.GJ30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 39/68] http-walker: store url in a strbuf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:31Z","receivedAt":"2015-09-24T21:07:31Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We do an unchecked sprintf directly into our url buffer.\nThis doesn't overflow because we know that it was sized for\n\"$base/objects/info/http-alternates\", and we are writing\n\"$base/objects/info/alternates\", which must be smaller. But\nthat is not immediately obvious to a reader who is looking\nfor buffer overflows. Let's switch to a strbuf, so that we\ndo not have to think about this issue at all.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n http-walker.c | 19 ++++++++++---------\n 1 file changed, 10 insertions(+), 9 deletions(-)\n\ndiff --git a/http-walker.c b/http-walker.c\nindex 88da546..2c721f0 100644\n--- a/http-walker.c\n+++ b/http-walker.c\n@@ -29,7 +29,7 @@ struct object_request {\n struct alternates_request {\n \tstruct walker *walker;\n \tconst char *base;\n-\tchar *url;\n+\tstruct strbuf *url;\n \tstruct strbuf *buffer;\n \tstruct active_request_slot *slot;\n \tint http_specific;\n@@ -195,10 +195,11 @@ static void process_alternates_response(void *callback_data)\n \n \t\t\t/* Try reusing the slot to get non-http alternates */\n \t\t\talt_req->http_specific = 0;\n-\t\t\tsprintf(alt_req->url, \"%s/objects/info/alternates\",\n-\t\t\t\tbase);\n+\t\t\tstrbuf_reset(alt_req->url);\n+\t\t\tstrbuf_addf(alt_req->url, \"%s/objects/info/alternates\",\n+\t\t\t\t    base);\n \t\t\tcurl_easy_setopt(slot->curl, CURLOPT_URL,\n-\t\t\t\t\t alt_req->url);\n+\t\t\t\t\t alt_req->url->buf);\n \t\t\tactive_requests++;\n \t\t\tslot->in_use = 1;\n \t\t\tif (slot->finished != NULL)\n@@ -312,7 +313,7 @@ static void process_alternates_response(void *callback_data)\n static void fetch_alternates(struct walker *walker, const char *base)\n {\n \tstruct strbuf buffer = STRBUF_INIT;\n-\tchar *url;\n+\tstruct strbuf url = STRBUF_INIT;\n \tstruct active_request_slot *slot;\n \tstruct alternates_request alt_req;\n \tstruct walker_data *cdata = walker->data;\n@@ -338,7 +339,7 @@ static void fetch_alternates(struct walker *walker, const char *base)\n \tif (walker->get_verbosely)\n \t\tfprintf(stderr, \"Getting alternates list for %s\\n\", base);\n \n-\turl = xstrfmt(\"%s/objects/info/http-alternates\", base);\n+\tstrbuf_addf(&url, \"%s/objects/info/http-alternates\", base);\n \n \t/*\n \t * Use a callback to process the result, since another request\n@@ -351,10 +352,10 @@ static void fetch_alternates(struct walker *walker, const char *base)\n \n \tcurl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);\n \tcurl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);\n-\tcurl_easy_setopt(slot->curl, CURLOPT_URL, url);\n+\tcurl_easy_setopt(slot->curl, CURLOPT_URL, url.buf);\n \n \talt_req.base = base;\n-\talt_req.url = url;\n+\talt_req.url = &url;\n \talt_req.buffer = &buffer;\n \talt_req.http_specific = 1;\n \talt_req.slot = slot;\n@@ -365,7 +366,7 @@ static void fetch_alternates(struct walker *walker, const char *base)\n \t\tcdata->got_alternates = -1;\n \n \tstrbuf_release(&buffer);\n-\tfree(url);\n+\tstrbuf_release(&url);\n }\n \n static int fetch_indices(struct walker *walker, struct alt_base *repo)\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270710","messageId":"20150924210733.GK30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 40/68] sha1_get_pack_name: use a strbuf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:34Z","receivedAt":"2015-09-24T21:07:34Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We do some manual memory computation here, and there's no\ncheck that our 60 is not overflowed by the raw sprintf (it\nisn't, because the \"which\" parameter is never longer than\n\"pack\"). We can simplify this greatly with a strbuf.\n\nTechnically the end result is not identical, as the original\ntook care not to rewrite the object directory on each call\nfor performance reasons.  We could do that here, too (by\nsaving the baselen and resetting to it), but it's not worth\nthe complexity; this function is not called a lot (generally\nonce per packfile that we open).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n sha1_file.c | 39 ++++++++++-----------------------------\n 1 file changed, 10 insertions(+), 29 deletions(-)\n\ndiff --git a/sha1_file.c b/sha1_file.c\nindex 2be1afd..c26fdcb 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -208,44 +208,25 @@ const char *sha1_file_name(const unsigned char *sha1)\n  * provided by the caller.  which should be \"pack\" or \"idx\".\n  */\n static char *sha1_get_pack_name(const unsigned char *sha1,\n-\t\t\t\tchar **name, char **base, const char *which)\n+\t\t\t\tstruct strbuf *buf,\n+\t\t\t\tconst char *which)\n {\n-\tstatic const char hex[] = \"0123456789abcdef\";\n-\tchar *buf;\n-\tint i;\n-\n-\tif (!*base) {\n-\t\tconst char *sha1_file_directory = get_object_directory();\n-\t\tint len = strlen(sha1_file_directory);\n-\t\t*base = xmalloc(len + 60);\n-\t\tsprintf(*base, \"%s/pack/pack-1234567890123456789012345678901234567890.%s\",\n-\t\t\tsha1_file_directory, which);\n-\t\t*name = *base + len + 11;\n-\t}\n-\n-\tbuf = *name;\n-\n-\tfor (i = 0; i < 20; i++) {\n-\t\tunsigned int val = *sha1++;\n-\t\t*buf++ = hex[val >> 4];\n-\t\t*buf++ = hex[val & 0xf];\n-\t}\n-\n-\treturn *base;\n+\tstrbuf_reset(buf);\n+\tstrbuf_addf(buf, \"%s/pack/pack-%s.%s\", get_object_directory(),\n+\t\t    sha1_to_hex(sha1), which);\n+\treturn buf->buf;\n }\n \n char *sha1_pack_name(const unsigned char *sha1)\n {\n-\tstatic char *name, *base;\n-\n-\treturn sha1_get_pack_name(sha1, &name, &base, \"pack\");\n+\tstatic struct strbuf buf = STRBUF_INIT;\n+\treturn sha1_get_pack_name(sha1, &buf, \"pack\");\n }\n \n char *sha1_pack_index_name(const unsigned char *sha1)\n {\n-\tstatic char *name, *base;\n-\n-\treturn sha1_get_pack_name(sha1, &name, &base, \"idx\");\n+\tstatic struct strbuf buf = STRBUF_INIT;\n+\treturn sha1_get_pack_name(sha1, &buf, \"idx\");\n }\n \n struct alternate_object_database *alt_odb_list;\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270711","messageId":"20150924210736.GL30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 41/68] init: use strbufs to store paths","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:36Z","receivedAt":"2015-09-24T21:07:36Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The init code predates strbufs, and uses PATH_MAX-sized\nbuffers along with many manual checks on intermediate sizes\n(some of which make magic assumptions, such as that init\nwill not create a path inside .git longer than 50\ncharacters).\n\nWe can simplify this greatly by using strbufs, which drops\nsome hard-to-verify strcpy calls.  Note that we need to\nupdate probe_utf8_pathname_composition, too, as it assumes\nwe are passing a buffer large enough to append its probe\nfilenames (it now just takes a strbuf, which also gets rid\nof the confusing \"len\" parameter, which was not the length of\n\"path\" but rather the offset to start writing).\n\nSome of the conversion makes new calls to git_path_buf.\nWhile we're in the area, let's also convert existing calls\nto git_path to the safer git_path_buf (our existing calls\nwere passed to pretty tame functions, and so were not a\nproblem, but it's easy to be consistent and safe here).\n\nNote that we had an explicit test that \"git init\" rejects\nlong template directories. This comes from 32d1776 (init: Do\nnot segfault on big GIT_TEMPLATE_DIR environment variable,\n2009-04-18). We can drop the test_must_fail here, as we now\naccept this and need only confirm that we don't segfault,\nwhich was the original point of the test.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/init-db.c        | 174 ++++++++++++++++++++---------------------------\n compat/precompose_utf8.c |  12 ++--\n compat/precompose_utf8.h |   2 +-\n git-compat-util.h        |   2 +-\n t/t0001-init.sh          |   4 +-\n 5 files changed, 87 insertions(+), 107 deletions(-)\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex e7d0e31..cf6a3c8 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -36,10 +36,11 @@ static void safe_create_dir(const char *dir, int share)\n \t\tdie(_(\"Could not make %s writable by group\"), dir);\n }\n \n-static void copy_templates_1(char *path, int baselen,\n-\t\t\t     char *template, int template_baselen,\n+static void copy_templates_1(struct strbuf *path, struct strbuf *template,\n \t\t\t     DIR *dir)\n {\n+\tsize_t path_baselen = path->len;\n+\tsize_t template_baselen = template->len;\n \tstruct dirent *de;\n \n \t/* Note: if \".git/hooks\" file exists in the repository being\n@@ -49,77 +50,64 @@ static void copy_templates_1(char *path, int baselen,\n \t * with the way the namespace under .git/ is organized, should\n \t * be really carefully chosen.\n \t */\n-\tsafe_create_dir(path, 1);\n+\tsafe_create_dir(path->buf, 1);\n \twhile ((de = readdir(dir)) != NULL) {\n \t\tstruct stat st_git, st_template;\n-\t\tint namelen;\n \t\tint exists = 0;\n \n+\t\tstrbuf_setlen(path, path_baselen);\n+\t\tstrbuf_setlen(template, template_baselen);\n+\n \t\tif (de->d_name[0] == '.')\n \t\t\tcontinue;\n-\t\tnamelen = strlen(de->d_name);\n-\t\tif ((PATH_MAX <= baselen + namelen) ||\n-\t\t    (PATH_MAX <= template_baselen + namelen))\n-\t\t\tdie(_(\"insanely long template name %s\"), de->d_name);\n-\t\tmemcpy(path + baselen, de->d_name, namelen+1);\n-\t\tmemcpy(template + template_baselen, de->d_name, namelen+1);\n-\t\tif (lstat(path, &st_git)) {\n+\t\tstrbuf_addstr(path, de->d_name);\n+\t\tstrbuf_addstr(template, de->d_name);\n+\t\tif (lstat(path->buf, &st_git)) {\n \t\t\tif (errno != ENOENT)\n-\t\t\t\tdie_errno(_(\"cannot stat '%s'\"), path);\n+\t\t\t\tdie_errno(_(\"cannot stat '%s'\"), path->buf);\n \t\t}\n \t\telse\n \t\t\texists = 1;\n \n-\t\tif (lstat(template, &st_template))\n-\t\t\tdie_errno(_(\"cannot stat template '%s'\"), template);\n+\t\tif (lstat(template->buf, &st_template))\n+\t\t\tdie_errno(_(\"cannot stat template '%s'\"), template->buf);\n \n \t\tif (S_ISDIR(st_template.st_mode)) {\n-\t\t\tDIR *subdir = opendir(template);\n-\t\t\tint baselen_sub = baselen + namelen;\n-\t\t\tint template_baselen_sub = template_baselen + namelen;\n+\t\t\tDIR *subdir = opendir(template->buf);\n \t\t\tif (!subdir)\n-\t\t\t\tdie_errno(_(\"cannot opendir '%s'\"), template);\n-\t\t\tpath[baselen_sub++] =\n-\t\t\t\ttemplate[template_baselen_sub++] = '/';\n-\t\t\tpath[baselen_sub] =\n-\t\t\t\ttemplate[template_baselen_sub] = 0;\n-\t\t\tcopy_templates_1(path, baselen_sub,\n-\t\t\t\t\t template, template_baselen_sub,\n-\t\t\t\t\t subdir);\n+\t\t\t\tdie_errno(_(\"cannot opendir '%s'\"), template->buf);\n+\t\t\tstrbuf_addch(path, '/');\n+\t\t\tstrbuf_addch(template, '/');\n+\t\t\tcopy_templates_1(path, template, subdir);\n \t\t\tclosedir(subdir);\n \t\t}\n \t\telse if (exists)\n \t\t\tcontinue;\n \t\telse if (S_ISLNK(st_template.st_mode)) {\n-\t\t\tchar lnk[256];\n-\t\t\tint len;\n-\t\t\tlen = readlink(template, lnk, sizeof(lnk));\n-\t\t\tif (len < 0)\n-\t\t\t\tdie_errno(_(\"cannot readlink '%s'\"), template);\n-\t\t\tif (sizeof(lnk) <= len)\n-\t\t\t\tdie(_(\"insanely long symlink %s\"), template);\n-\t\t\tlnk[len] = 0;\n-\t\t\tif (symlink(lnk, path))\n-\t\t\t\tdie_errno(_(\"cannot symlink '%s' '%s'\"), lnk, path);\n+\t\t\tstruct strbuf lnk = STRBUF_INIT;\n+\t\t\tif (strbuf_readlink(&lnk, template->buf, 0) < 0)\n+\t\t\t\tdie_errno(_(\"cannot readlink '%s'\"), template->buf);\n+\t\t\tif (symlink(lnk.buf, path->buf))\n+\t\t\t\tdie_errno(_(\"cannot symlink '%s' '%s'\"),\n+\t\t\t\t\t  lnk.buf, path->buf);\n+\t\t\tstrbuf_release(&lnk);\n \t\t}\n \t\telse if (S_ISREG(st_template.st_mode)) {\n-\t\t\tif (copy_file(path, template, st_template.st_mode))\n-\t\t\t\tdie_errno(_(\"cannot copy '%s' to '%s'\"), template,\n-\t\t\t\t\t  path);\n+\t\t\tif (copy_file(path->buf, template->buf, st_template.st_mode))\n+\t\t\t\tdie_errno(_(\"cannot copy '%s' to '%s'\"),\n+\t\t\t\t\t  template->buf, path->buf);\n \t\t}\n \t\telse\n-\t\t\terror(_(\"ignoring template %s\"), template);\n+\t\t\terror(_(\"ignoring template %s\"), template->buf);\n \t}\n }\n \n static void copy_templates(const char *template_dir)\n {\n-\tchar path[PATH_MAX];\n-\tchar template_path[PATH_MAX];\n-\tint template_len;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf template_path = STRBUF_INIT;\n+\tsize_t template_len;\n \tDIR *dir;\n-\tconst char *git_dir = get_git_dir();\n-\tint len = strlen(git_dir);\n \tchar *to_free = NULL;\n \n \tif (!template_dir)\n@@ -132,26 +120,23 @@ static void copy_templates(const char *template_dir)\n \t\tfree(to_free);\n \t\treturn;\n \t}\n-\ttemplate_len = strlen(template_dir);\n-\tif (PATH_MAX <= (template_len+strlen(\"/config\")))\n-\t\tdie(_(\"insanely long template path %s\"), template_dir);\n-\tstrcpy(template_path, template_dir);\n-\tif (template_path[template_len-1] != '/') {\n-\t\ttemplate_path[template_len++] = '/';\n-\t\ttemplate_path[template_len] = 0;\n-\t}\n-\tdir = opendir(template_path);\n+\n+\tstrbuf_addstr(&template_path, template_dir);\n+\tstrbuf_complete(&template_path, '/');\n+\ttemplate_len = template_path.len;\n+\n+\tdir = opendir(template_path.buf);\n \tif (!dir) {\n \t\twarning(_(\"templates not found %s\"), template_dir);\n \t\tgoto free_return;\n \t}\n \n \t/* Make sure that template is from the correct vintage */\n-\tstrcpy(template_path + template_len, \"config\");\n+\tstrbuf_addstr(&template_path, \"config\");\n \trepository_format_version = 0;\n \tgit_config_from_file(check_repository_format_version,\n-\t\t\t     template_path, NULL);\n-\ttemplate_path[template_len] = 0;\n+\t\t\t     template_path.buf, NULL);\n+\tstrbuf_setlen(&template_path, template_len);\n \n \tif (repository_format_version &&\n \t    repository_format_version != GIT_REPO_VERSION) {\n@@ -162,17 +147,15 @@ static void copy_templates(const char *template_dir)\n \t\tgoto close_free_return;\n \t}\n \n-\tmemcpy(path, git_dir, len);\n-\tif (len && path[len - 1] != '/')\n-\t\tpath[len++] = '/';\n-\tpath[len] = 0;\n-\tcopy_templates_1(path, len,\n-\t\t\t template_path, template_len,\n-\t\t\t dir);\n+\tstrbuf_addstr(&path, get_git_dir());\n+\tstrbuf_complete(&path, '/');\n+\tcopy_templates_1(&path, &template_path, dir);\n close_free_return:\n \tclosedir(dir);\n free_return:\n \tfree(to_free);\n+\tstrbuf_release(&path);\n+\tstrbuf_release(&template_path);\n }\n \n static int git_init_db_config(const char *k, const char *v, void *cb)\n@@ -199,28 +182,20 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)\n \n static int create_default_files(const char *template_path)\n {\n-\tconst char *git_dir = get_git_dir();\n-\tunsigned len = strlen(git_dir);\n-\tstatic char path[PATH_MAX];\n \tstruct stat st1;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tchar *path;\n \tchar repo_version_string[10];\n \tchar junk[2];\n \tint reinit;\n \tint filemode;\n \n-\tif (len > sizeof(path)-50)\n-\t\tdie(_(\"insane git directory %s\"), git_dir);\n-\tmemcpy(path, git_dir, len);\n-\n-\tif (len && path[len-1] != '/')\n-\t\tpath[len++] = '/';\n-\n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tsafe_create_dir(git_path(\"refs\"), 1);\n-\tsafe_create_dir(git_path(\"refs/heads\"), 1);\n-\tsafe_create_dir(git_path(\"refs/tags\"), 1);\n+\tsafe_create_dir(git_path_buf(&buf, \"refs\"), 1);\n+\tsafe_create_dir(git_path_buf(&buf, \"refs/heads\"), 1);\n+\tsafe_create_dir(git_path_buf(&buf, \"refs/tags\"), 1);\n \n \t/* Just look for `init.templatedir` */\n \tgit_config(git_init_db_config, NULL);\n@@ -244,16 +219,16 @@ static int create_default_files(const char *template_path)\n \t */\n \tif (shared_repository) {\n \t\tadjust_shared_perm(get_git_dir());\n-\t\tadjust_shared_perm(git_path(\"refs\"));\n-\t\tadjust_shared_perm(git_path(\"refs/heads\"));\n-\t\tadjust_shared_perm(git_path(\"refs/tags\"));\n+\t\tadjust_shared_perm(git_path_buf(&buf, \"refs\"));\n+\t\tadjust_shared_perm(git_path_buf(&buf, \"refs/heads\"));\n+\t\tadjust_shared_perm(git_path_buf(&buf, \"refs/tags\"));\n \t}\n \n \t/*\n \t * Create the default symlink from \".git/HEAD\" to the \"master\"\n \t * branch, if it does not exist yet.\n \t */\n-\tstrcpy(path + len, \"HEAD\");\n+\tpath = git_path_buf(&buf, \"HEAD\");\n \treinit = (!access(path, R_OK)\n \t\t  || readlink(path, junk, sizeof(junk)-1) != -1);\n \tif (!reinit) {\n@@ -266,10 +241,8 @@ static int create_default_files(const char *template_path)\n \t\t  \"%d\", GIT_REPO_VERSION);\n \tgit_config_set(\"core.repositoryformatversion\", repo_version_string);\n \n-\tpath[len] = 0;\n-\tstrcpy(path + len, \"config\");\n-\n \t/* Check filemode trustability */\n+\tpath = git_path_buf(&buf, \"config\");\n \tfilemode = TEST_FILEMODE;\n \tif (TEST_FILEMODE && !lstat(path, &st1)) {\n \t\tstruct stat st2;\n@@ -290,14 +263,13 @@ static int create_default_files(const char *template_path)\n \t\t/* allow template config file to override the default */\n \t\tif (log_all_ref_updates == -1)\n \t\t    git_config_set(\"core.logallrefupdates\", \"true\");\n-\t\tif (needs_work_tree_config(git_dir, work_tree))\n+\t\tif (needs_work_tree_config(get_git_dir(), work_tree))\n \t\t\tgit_config_set(\"core.worktree\", work_tree);\n \t}\n \n \tif (!reinit) {\n \t\t/* Check if symlink is supported in the work tree */\n-\t\tpath[len] = 0;\n-\t\tstrcpy(path + len, \"tXXXXXX\");\n+\t\tpath = git_path_buf(&buf, \"tXXXXXX\");\n \t\tif (!close(xmkstemp(path)) &&\n \t\t    !unlink(path) &&\n \t\t    !symlink(\"testing\", path) &&\n@@ -308,31 +280,35 @@ static int create_default_files(const char *template_path)\n \t\t\tgit_config_set(\"core.symlinks\", \"false\");\n \n \t\t/* Check if the filesystem is case-insensitive */\n-\t\tpath[len] = 0;\n-\t\tstrcpy(path + len, \"CoNfIg\");\n+\t\tpath = git_path_buf(&buf, \"CoNfIg\");\n \t\tif (!access(path, F_OK))\n \t\t\tgit_config_set(\"core.ignorecase\", \"true\");\n-\t\tprobe_utf8_pathname_composition(path, len);\n+\t\tprobe_utf8_pathname_composition(path);\n \t}\n \n+\tstrbuf_release(&buf);\n \treturn reinit;\n }\n \n static void create_object_directory(void)\n {\n-\tconst char *object_directory = get_object_directory();\n-\tint len = strlen(object_directory);\n-\tchar *path = xmalloc(len + 40);\n+\tstruct strbuf path = STRBUF_INIT;\n+\tsize_t baselen;\n+\n+\tstrbuf_addstr(&path, get_object_directory());\n+\tbaselen = path.len;\n+\n+\tsafe_create_dir(path.buf, 1);\n \n-\tmemcpy(path, object_directory, len);\n+\tstrbuf_setlen(&path, baselen);\n+\tstrbuf_addstr(&path, \"/pack\");\n+\tsafe_create_dir(path.buf, 1);\n \n-\tsafe_create_dir(object_directory, 1);\n-\tstrcpy(path+len, \"/pack\");\n-\tsafe_create_dir(path, 1);\n-\tstrcpy(path+len, \"/info\");\n-\tsafe_create_dir(path, 1);\n+\tstrbuf_setlen(&path, baselen);\n+\tstrbuf_addstr(&path, \"/info\");\n+\tsafe_create_dir(path.buf, 1);\n \n-\tfree(path);\n+\tstrbuf_release(&path);\n }\n \n int set_git_dir_init(const char *git_dir, const char *real_git_dir,\ndiff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\nindex 95fe849..b4dd3c7 100644\n--- a/compat/precompose_utf8.c\n+++ b/compat/precompose_utf8.c\n@@ -36,24 +36,28 @@ static size_t has_non_ascii(const char *s, size_t maxlen, size_t *strlen_c)\n }\n \n \n-void probe_utf8_pathname_composition(char *path, int len)\n+void probe_utf8_pathname_composition(struct strbuf *path)\n {\n \tstatic const char *auml_nfc = \"\\xc3\\xa4\";\n \tstatic const char *auml_nfd = \"\\x61\\xcc\\x88\";\n+\tsize_t baselen = path->len;\n \tint output_fd;\n \tif (precomposed_unicode != -1)\n \t\treturn; /* We found it defined in the global config, respect it */\n-\tstrcpy(path + len, auml_nfc);\n+\tstrbuf_addstr(path, auml_nfc);\n \toutput_fd = open(path, O_CREAT|O_EXCL|O_RDWR, 0600);\n \tif (output_fd >= 0) {\n \t\tclose(output_fd);\n-\t\tstrcpy(path + len, auml_nfd);\n+\t\tstrbuf_setlen(path, baselen);\n+\t\tstrbuf_addstr(path, auml_nfd);\n \t\tprecomposed_unicode = access(path, R_OK) ? 0 : 1;\n \t\tgit_config_set(\"core.precomposeunicode\", precomposed_unicode ? \"true\" : \"false\");\n-\t\tstrcpy(path + len, auml_nfc);\n+\t\tstrbuf_setlen(path, baselen);\n+\t\tstrbuf_addstr(path, auml_nfc);\n \t\tif (unlink(path))\n \t\t\tdie_errno(_(\"failed to unlink '%s'\"), path);\n \t}\n+\tstrbuf_setlen(path, baselen);\n }\n \n \ndiff --git a/compat/precompose_utf8.h b/compat/precompose_utf8.h\nindex 3b73585..7fc7be5 100644\n--- a/compat/precompose_utf8.h\n+++ b/compat/precompose_utf8.h\n@@ -27,7 +27,7 @@ typedef struct {\n } PREC_DIR;\n \n void precompose_argv(int argc, const char **argv);\n-void probe_utf8_pathname_composition(char *, int);\n+void probe_utf8_pathname_composition(struct strbuf *path);\n \n PREC_DIR *precompose_utf8_opendir(const char *dirname);\n struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *dirp);\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 348b9dc..712de7f 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -229,7 +229,7 @@ typedef unsigned long uintptr_t;\n #else\n #define precompose_str(in,i_nfd2nfc)\n #define precompose_argv(c,v)\n-#define probe_utf8_pathname_composition(a,b)\n+#define probe_utf8_pathname_composition(p)\n #endif\n \n #ifdef MKDIR_WO_TRAILING_SLASH\ndiff --git a/t/t0001-init.sh b/t/t0001-init.sh\nindex 7de8d85..f91bbcf 100755\n--- a/t/t0001-init.sh\n+++ b/t/t0001-init.sh\n@@ -202,8 +202,8 @@ test_expect_success 'init honors global core.sharedRepository' '\n \tx$(git config -f shared-honor-global/.git/config core.sharedRepository)\n '\n \n-test_expect_success 'init rejects insanely long --template' '\n-\ttest_must_fail git init --template=$(printf \"x%09999dx\" 1) test\n+test_expect_success 'init allows insanely long --template' '\n+\tgit init --template=$(printf \"x%09999dx\" 1) test\n '\n \n test_expect_success 'init creates a new directory' '\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270683","messageId":"20150924210738.GM30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 42/68] apply: convert root string to strbuf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:38Z","receivedAt":"2015-09-24T21:07:38Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We use manual computation and strcpy to allocate the \"root\"\nvariable. This would be much simpler using xstrfmt.  But\nsince we store the length, too, we can just use a strbuf,\nwhich handles that for us.\n\nNote that we stop distinguishing between \"no root\" and\n\"empty root\" in some cases, but that's OK; the results are\nthe same (e.g., inserting an empty string is a noop).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/apply.c | 26 ++++++++++----------------\n 1 file changed, 10 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex 094a20f..1d4d439 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -77,8 +77,7 @@ static enum ws_ignore {\n \n \n static const char *patch_input_file;\n-static const char *root;\n-static int root_len;\n+struct strbuf root = STRBUF_INIT;\n static int read_stdin = 1;\n static int options;\n \n@@ -494,8 +493,8 @@ static char *find_name_gnu(const char *line, const char *def, int p_value)\n \t}\n \n \tstrbuf_remove(&name, 0, cp - name.buf);\n-\tif (root)\n-\t\tstrbuf_insert(&name, 0, root, root_len);\n+\tif (root.len)\n+\t\tstrbuf_insert(&name, 0, root.buf, root.len);\n \treturn squash_slash(strbuf_detach(&name, NULL));\n }\n \n@@ -697,8 +696,8 @@ static char *find_name_common(const char *line, const char *def,\n \t\t\treturn squash_slash(xstrdup(def));\n \t}\n \n-\tif (root) {\n-\t\tchar *ret = xstrfmt(\"%s%.*s\", root, len, start);\n+\tif (root.len) {\n+\t\tchar *ret = xstrfmt(\"%s%.*s\", root.buf, len, start);\n \t\treturn squash_slash(ret);\n \t}\n \n@@ -1274,8 +1273,8 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct\n \t * the default name from the header.\n \t */\n \tpatch->def_name = git_header_name(line, len);\n-\tif (patch->def_name && root) {\n-\t\tchar *s = xstrfmt(\"%s%s\", root, patch->def_name);\n+\tif (patch->def_name && root.len) {\n+\t\tchar *s = xstrfmt(\"%s%s\", root.buf, patch->def_name);\n \t\tfree(patch->def_name);\n \t\tpatch->def_name = s;\n \t}\n@@ -4498,14 +4497,9 @@ static int option_parse_whitespace(const struct option *opt,\n static int option_parse_directory(const struct option *opt,\n \t\t\t\t  const char *arg, int unset)\n {\n-\troot_len = strlen(arg);\n-\tif (root_len && arg[root_len - 1] != '/') {\n-\t\tchar *new_root;\n-\t\troot = new_root = xmalloc(root_len + 2);\n-\t\tstrcpy(new_root, arg);\n-\t\tstrcpy(new_root + root_len++, \"/\");\n-\t} else\n-\t\troot = arg;\n+\tstrbuf_reset(&root);\n+\tstrbuf_addstr(&root, arg);\n+\tstrbuf_complete(&root, '/');\n \treturn 0;\n }\n \n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270682","messageId":"20150924210740.GN30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 43/68] transport: use strbufs for status table \"quickref\" strings","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:40Z","receivedAt":"2015-09-24T21:07:40Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We generate range strings like \"1234abcd...5678efab\" for use\nin the the fetch and push status tables. We use fixed-size\nbuffers along with strcat to do so. These aren't buggy, as\nour manual size computation is correct, but there's nothing\nchecking that this is so.  Let's switch them to strbufs\ninstead, which are obviously correct, and make it easier to\naudit the code base for problematic calls to strcat().\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/fetch.c | 22 ++++++++++++----------\n transport.c     | 13 +++++++------\n 2 files changed, 19 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 4703725..841880e 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -528,36 +528,38 @@ static int update_local_ref(struct ref *ref,\n \t}\n \n \tif (in_merge_bases(current, updated)) {\n-\t\tchar quickref[83];\n+\t\tstruct strbuf quickref = STRBUF_INIT;\n \t\tint r;\n-\t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n-\t\tstrcat(quickref, \"..\");\n-\t\tstrcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));\n+\t\tstrbuf_add_unique_abbrev(&quickref, current->object.sha1, DEFAULT_ABBREV);\n+\t\tstrbuf_addstr(&quickref, \"..\");\n+\t\tstrbuf_add_unique_abbrev(&quickref, ref->new_sha1, DEFAULT_ABBREV);\n \t\tif ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_sha1);\n \t\tr = s_update_ref(\"fast-forward\", ref, 1);\n \t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s%s\",\n \t\t\t    r ? '!' : ' ',\n-\t\t\t    TRANSPORT_SUMMARY_WIDTH, quickref,\n+\t\t\t    TRANSPORT_SUMMARY_WIDTH, quickref.buf,\n \t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n \t\t\t    r ? _(\"  (unable to update local ref)\") : \"\");\n+\t\tstrbuf_release(&quickref);\n \t\treturn r;\n \t} else if (force || ref->force) {\n-\t\tchar quickref[84];\n+\t\tstruct strbuf quickref = STRBUF_INIT;\n \t\tint r;\n-\t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n-\t\tstrcat(quickref, \"...\");\n-\t\tstrcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));\n+\t\tstrbuf_add_unique_abbrev(&quickref, current->object.sha1, DEFAULT_ABBREV);\n+\t\tstrbuf_addstr(&quickref, \"...\");\n+\t\tstrbuf_add_unique_abbrev(&quickref, ref->new_sha1, DEFAULT_ABBREV);\n \t\tif ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_sha1);\n \t\tr = s_update_ref(\"forced-update\", ref, 1);\n \t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s  (%s)\",\n \t\t\t    r ? '!' : '+',\n-\t\t\t    TRANSPORT_SUMMARY_WIDTH, quickref,\n+\t\t\t    TRANSPORT_SUMMARY_WIDTH, quickref.buf,\n \t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n \t\t\t    r ? _(\"unable to update local ref\") : _(\"forced update\"));\n+\t\tstrbuf_release(&quickref);\n \t\treturn r;\n \t} else {\n \t\tstrbuf_addf(display, \"! %-*s %-*s -> %s  %s\",\ndiff --git a/transport.c b/transport.c\nindex 2d51348..3b47d49 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -654,23 +654,24 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \t\t\t\"[new branch]\"),\n \t\t\tref, ref->peer_ref, NULL, porcelain);\n \telse {\n-\t\tchar quickref[84];\n+\t\tstruct strbuf quickref = STRBUF_INIT;\n \t\tchar type;\n \t\tconst char *msg;\n \n-\t\tstrcpy(quickref, status_abbrev(ref->old_sha1));\n+\t\tstrbuf_addstr(&quickref, status_abbrev(ref->old_sha1));\n \t\tif (ref->forced_update) {\n-\t\t\tstrcat(quickref, \"...\");\n+\t\t\tstrbuf_addstr(&quickref, \"...\");\n \t\t\ttype = '+';\n \t\t\tmsg = \"forced update\";\n \t\t} else {\n-\t\t\tstrcat(quickref, \"..\");\n+\t\t\tstrbuf_addstr(&quickref, \"..\");\n \t\t\ttype = ' ';\n \t\t\tmsg = NULL;\n \t\t}\n-\t\tstrcat(quickref, status_abbrev(ref->new_sha1));\n+\t\tstrbuf_addstr(&quickref, status_abbrev(ref->new_sha1));\n \n-\t\tprint_ref_status(type, quickref, ref, ref->peer_ref, msg, porcelain);\n+\t\tprint_ref_status(type, quickref.buf, ref, ref->peer_ref, msg, porcelain);\n+\t\tstrbuf_release(&quickref);\n \t}\n }\n \n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270709","messageId":"20150924210742.GO30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 44/68] merge-recursive: convert malloc / strcpy to strbuf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:43Z","receivedAt":"2015-09-24T21:07:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This would be a fairly routine use of xstrfmt, except that\nwe need to remember the length of the result to pass to\ncache_name_pos. So just use a strbuf, which makes this\nsimple.\n\nAs a bonus, this gets rid of confusing references to\n\"pathlen+1\". The \"1\" is for the trailing slash we added, but\nthat is automatically accounted for in the strbuf's len\nparameter.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n merge-recursive.c | 17 ++++++++---------\n 1 file changed, 8 insertions(+), 9 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 44d85be..a5e74d8 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -630,25 +630,24 @@ static char *unique_path(struct merge_options *o, const char *path, const char *\n \n static int dir_in_way(const char *path, int check_working_copy)\n {\n-\tint pos, pathlen = strlen(path);\n-\tchar *dirpath = xmalloc(pathlen + 2);\n+\tint pos;\n+\tstruct strbuf dirpath = STRBUF_INIT;\n \tstruct stat st;\n \n-\tstrcpy(dirpath, path);\n-\tdirpath[pathlen] = '/';\n-\tdirpath[pathlen+1] = '\\0';\n+\tstrbuf_addstr(&dirpath, path);\n+\tstrbuf_addch(&dirpath, '/');\n \n-\tpos = cache_name_pos(dirpath, pathlen+1);\n+\tpos = cache_name_pos(dirpath.buf, dirpath.len);\n \n \tif (pos < 0)\n \t\tpos = -1 - pos;\n \tif (pos < active_nr &&\n-\t    !strncmp(dirpath, active_cache[pos]->name, pathlen+1)) {\n-\t\tfree(dirpath);\n+\t    !strncmp(dirpath.buf, active_cache[pos]->name, dirpath.len)) {\n+\t\tstrbuf_release(&dirpath);\n \t\treturn 1;\n \t}\n \n-\tfree(dirpath);\n+\tstrbuf_release(&dirpath);\n \treturn check_working_copy && !lstat(path, &st) && S_ISDIR(st.st_mode);\n }\n \n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270685","messageId":"20150924210745.GP30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 45/68] enter_repo: convert fixed-size buffers to strbufs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:45Z","receivedAt":"2015-09-24T21:07:45Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We use two PATH_MAX-sized buffers to represent the repo\npath, and must make sure not to overflow them. We do take\ncare to check the lengths, but the logic is rather hard to\nfollow, as we use several magic numbers (e.g., \"PATH_MAX -\n10\"). And in fact you _can_ overflow the buffer if you have\na \".git\" file with an extremely long path in it.\n\nBy switching to strbufs, these problems all go away. We do,\nhowever, retain the check that the initial input we get is\nno larger than PATH_MAX. This function is an entry point for\nuntrusted repo names from the network, and it's a good idea\nto keep a sanity check (both to avoid allocating arbitrary\namounts of memory, and also as a layer of defense against\nany downstream users of the names).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n path.c | 57 +++++++++++++++++++++++++++++----------------------------\n 1 file changed, 29 insertions(+), 28 deletions(-)\n\ndiff --git a/path.c b/path.c\nindex 46a4d27..60e0390 100644\n--- a/path.c\n+++ b/path.c\n@@ -391,8 +391,8 @@ return_null:\n  */\n const char *enter_repo(const char *path, int strict)\n {\n-\tstatic char used_path[PATH_MAX];\n-\tstatic char validated_path[PATH_MAX];\n+\tstatic struct strbuf validated_path = STRBUF_INIT;\n+\tstatic struct strbuf used_path = STRBUF_INIT;\n \n \tif (!path)\n \t\treturn NULL;\n@@ -407,46 +407,47 @@ const char *enter_repo(const char *path, int strict)\n \t\twhile ((1 < len) && (path[len-1] == '/'))\n \t\t\tlen--;\n \n+\t\t/*\n+\t\t * We can handle arbitrary-sized buffers, but this remains as a\n+\t\t * sanity check on untrusted input.\n+\t\t */\n \t\tif (PATH_MAX <= len)\n \t\t\treturn NULL;\n-\t\tstrncpy(used_path, path, len); used_path[len] = 0 ;\n-\t\tstrcpy(validated_path, used_path);\n \n-\t\tif (used_path[0] == '~') {\n-\t\t\tchar *newpath = expand_user_path(used_path);\n-\t\t\tif (!newpath || (PATH_MAX - 10 < strlen(newpath))) {\n-\t\t\t\tfree(newpath);\n+\t\tstrbuf_reset(&used_path);\n+\t\tstrbuf_reset(&validated_path);\n+\t\tstrbuf_add(&used_path, path, len);\n+\t\tstrbuf_add(&validated_path, path, len);\n+\n+\t\tif (used_path.buf[0] == '~') {\n+\t\t\tchar *newpath = expand_user_path(used_path.buf);\n+\t\t\tif (!newpath)\n \t\t\t\treturn NULL;\n-\t\t\t}\n-\t\t\t/*\n-\t\t\t * Copy back into the static buffer. A pity\n-\t\t\t * since newpath was not bounded, but other\n-\t\t\t * branches of the if are limited by PATH_MAX\n-\t\t\t * anyway.\n-\t\t\t */\n-\t\t\tstrcpy(used_path, newpath); free(newpath);\n+\t\t\tstrbuf_attach(&used_path, newpath, strlen(newpath),\n+\t\t\t\t      strlen(newpath));\n \t\t}\n-\t\telse if (PATH_MAX - 10 < len)\n-\t\t\treturn NULL;\n-\t\tlen = strlen(used_path);\n \t\tfor (i = 0; suffix[i]; i++) {\n \t\t\tstruct stat st;\n-\t\t\tstrcpy(used_path + len, suffix[i]);\n-\t\t\tif (!stat(used_path, &st) &&\n+\t\t\tsize_t baselen = used_path.len;\n+\t\t\tstrbuf_addstr(&used_path, suffix[i]);\n+\t\t\tif (!stat(used_path.buf, &st) &&\n \t\t\t    (S_ISREG(st.st_mode) ||\n-\t\t\t    (S_ISDIR(st.st_mode) && is_git_directory(used_path)))) {\n-\t\t\t\tstrcat(validated_path, suffix[i]);\n+\t\t\t    (S_ISDIR(st.st_mode) && is_git_directory(used_path.buf)))) {\n+\t\t\t\tstrbuf_addstr(&validated_path, suffix[i]);\n \t\t\t\tbreak;\n \t\t\t}\n+\t\t\tstrbuf_setlen(&used_path, baselen);\n \t\t}\n \t\tif (!suffix[i])\n \t\t\treturn NULL;\n-\t\tgitfile = read_gitfile(used_path) ;\n-\t\tif (gitfile)\n-\t\t\tstrcpy(used_path, gitfile);\n-\t\tif (chdir(used_path))\n+\t\tgitfile = read_gitfile(used_path.buf) ;\n+\t\tif (gitfile) {\n+\t\t\tstrbuf_reset(&used_path);\n+\t\t\tstrbuf_addstr(&used_path, gitfile);\n+\t\t}\n+\t\tif (chdir(used_path.buf))\n \t\t\treturn NULL;\n-\t\tpath = validated_path;\n+\t\tpath = validated_path.buf;\n \t}\n \telse if (chdir(path))\n \t\treturn NULL;\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270686","messageId":"20150924210747.GQ30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 46/68] remove_leading_path: use a strbuf for internal storage","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:47Z","receivedAt":"2015-09-24T21:07:47Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This function strcpy's directly into a PATH_MAX-sized\nbuffer. There's only one caller, which feeds the git_dir into\nit, so it's not easy to trigger in practice (even if you fed\na large $GIT_DIR through the environment or .git file, it\nwould have to actually exist and be accessible on the\nfilesystem to get to this point). We can fix it by moving to\na strbuf.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n path.c | 10 ++++++----\n 1 file changed, 6 insertions(+), 4 deletions(-)\n\ndiff --git a/path.c b/path.c\nindex 60e0390..c597473 100644\n--- a/path.c\n+++ b/path.c\n@@ -632,7 +632,7 @@ const char *relative_path(const char *in, const char *prefix,\n  */\n const char *remove_leading_path(const char *in, const char *prefix)\n {\n-\tstatic char buf[PATH_MAX + 1];\n+\tstatic struct strbuf buf = STRBUF_INIT;\n \tint i = 0, j = 0;\n \n \tif (!prefix || !prefix[0])\n@@ -661,11 +661,13 @@ const char *remove_leading_path(const char *in, const char *prefix)\n \t\treturn in;\n \twhile (is_dir_sep(in[j]))\n \t\tj++;\n+\n+\tstrbuf_reset(&buf);\n \tif (!in[j])\n-\t\tstrcpy(buf, \".\");\n+\t\tstrbuf_addstr(&buf, \".\");\n \telse\n-\t\tstrcpy(buf, in + j);\n-\treturn buf;\n+\t\tstrbuf_addstr(&buf, in + j);\n+\treturn buf.buf;\n }\n \n /*\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270688","messageId":"20150924210749.GR30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 47/68] write_loose_object: convert to strbuf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:49Z","receivedAt":"2015-09-24T21:07:49Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When creating a loose object tempfile, we use a fixed\nPATH_MAX-sized buffer, and strcpy directly into it. This\nisn't buggy, because we do a rough check of the size, but\nthere's no verification that our guesstimate of the required\nspace is enough (in fact, it's several bytes too big for the\ncurrent naming scheme).\n\nLet's switch to a strbuf, which makes this much easier to\nverify. The allocation overhead should be negligible, since\nwe are replacing a static buffer with a static strbuf, and\nwe'll only need to allocate on the first call.\n\nWhile we're here, we can also document a subtle interaction\nwith mkstemp that would be easy to overlook.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n sha1_file.c | 42 ++++++++++++++++++++++--------------------\n 1 file changed, 22 insertions(+), 20 deletions(-)\n\ndiff --git a/sha1_file.c b/sha1_file.c\nindex c26fdcb..4211af1 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -3011,29 +3011,31 @@ static inline int directory_size(const char *filename)\n  * We want to avoid cross-directory filename renames, because those\n  * can have problems on various filesystems (FAT, NFS, Coda).\n  */\n-static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)\n+static int create_tmpfile(struct strbuf *tmp, const char *filename)\n {\n \tint fd, dirlen = directory_size(filename);\n \n-\tif (dirlen + 20 > bufsiz) {\n-\t\terrno = ENAMETOOLONG;\n-\t\treturn -1;\n-\t}\n-\tmemcpy(buffer, filename, dirlen);\n-\tstrcpy(buffer + dirlen, \"tmp_obj_XXXXXX\");\n-\tfd = git_mkstemp_mode(buffer, 0444);\n+\tstrbuf_reset(tmp);\n+\tstrbuf_add(tmp, filename, dirlen);\n+\tstrbuf_addstr(tmp, \"tmp_obj_XXXXXX\");\n+\tfd = git_mkstemp_mode(tmp->buf, 0444);\n \tif (fd < 0 && dirlen && errno == ENOENT) {\n-\t\t/* Make sure the directory exists */\n-\t\tmemcpy(buffer, filename, dirlen);\n-\t\tbuffer[dirlen-1] = 0;\n-\t\tif (mkdir(buffer, 0777) && errno != EEXIST)\n+\t\t/*\n+\t\t * Make sure the directory exists; note that the contents\n+\t\t * of the buffer are undefined after mkstemp returns an\n+\t\t * error, so we have to rewrite the whole buffer from\n+\t\t * scratch.\n+\t\t */\n+\t\tstrbuf_reset(tmp);\n+\t\tstrbuf_add(tmp, filename, dirlen - 1);\n+\t\tif (mkdir(tmp->buf, 0777) && errno != EEXIST)\n \t\t\treturn -1;\n-\t\tif (adjust_shared_perm(buffer))\n+\t\tif (adjust_shared_perm(tmp->buf))\n \t\t\treturn -1;\n \n \t\t/* Try again */\n-\t\tstrcpy(buffer + dirlen - 1, \"/tmp_obj_XXXXXX\");\n-\t\tfd = git_mkstemp_mode(buffer, 0444);\n+\t\tstrbuf_addstr(tmp, \"/tmp_obj_XXXXXX\");\n+\t\tfd = git_mkstemp_mode(tmp->buf, 0444);\n \t}\n \treturn fd;\n }\n@@ -3046,10 +3048,10 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,\n \tgit_zstream stream;\n \tgit_SHA_CTX c;\n \tunsigned char parano_sha1[20];\n-\tstatic char tmp_file[PATH_MAX];\n+\tstatic struct strbuf tmp_file = STRBUF_INIT;\n \tconst char *filename = sha1_file_name(sha1);\n \n-\tfd = create_tmpfile(tmp_file, sizeof(tmp_file), filename);\n+\tfd = create_tmpfile(&tmp_file, filename);\n \tif (fd < 0) {\n \t\tif (errno == EACCES)\n \t\t\treturn error(\"insufficient permission for adding an object to repository database %s\", get_object_directory());\n@@ -3098,12 +3100,12 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,\n \t\tstruct utimbuf utb;\n \t\tutb.actime = mtime;\n \t\tutb.modtime = mtime;\n-\t\tif (utime(tmp_file, &utb) < 0)\n+\t\tif (utime(tmp_file.buf, &utb) < 0)\n \t\t\twarning(\"failed utime() on %s: %s\",\n-\t\t\t\ttmp_file, strerror(errno));\n+\t\t\t\ttmp_file.buf, strerror(errno));\n \t}\n \n-\treturn finalize_object_file(tmp_file, filename);\n+\treturn finalize_object_file(tmp_file.buf, filename);\n }\n \n static int freshen_loose_object(const unsigned char *sha1)\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270684","messageId":"20150924210751.GS30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 48/68] diagnose_invalid_index_path: use strbuf to avoid strcpy/strcat","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:52Z","receivedAt":"2015-09-24T21:07:52Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We dynamically allocate a buffer and then strcpy and strcat\ninto it. This isn't buggy, but we'd prefer to avoid these\nsuspicious functions.\n\nThis would be a good candidate for converstion to xstrfmt,\nbut we need to record the length for dealing with index\nentries. A strbuf handles that for us.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n sha1_name.c | 21 +++++++++------------\n 1 file changed, 9 insertions(+), 12 deletions(-)\n\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 80753b6..3242c5e 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -1293,8 +1293,7 @@ static void diagnose_invalid_index_path(int stage,\n \tconst struct cache_entry *ce;\n \tint pos;\n \tunsigned namelen = strlen(filename);\n-\tunsigned fullnamelen;\n-\tchar *fullname;\n+\tstruct strbuf fullname = STRBUF_INIT;\n \n \tif (!prefix)\n \t\tprefix = \"\";\n@@ -1314,21 +1313,19 @@ static void diagnose_invalid_index_path(int stage,\n \t}\n \n \t/* Confusion between relative and absolute filenames? */\n-\tfullnamelen = namelen + strlen(prefix);\n-\tfullname = xmalloc(fullnamelen + 1);\n-\tstrcpy(fullname, prefix);\n-\tstrcat(fullname, filename);\n-\tpos = cache_name_pos(fullname, fullnamelen);\n+\tstrbuf_addstr(&fullname, prefix);\n+\tstrbuf_addstr(&fullname, filename);\n+\tpos = cache_name_pos(fullname.buf, fullname.len);\n \tif (pos < 0)\n \t\tpos = -pos - 1;\n \tif (pos < active_nr) {\n \t\tce = active_cache[pos];\n-\t\tif (ce_namelen(ce) == fullnamelen &&\n-\t\t    !memcmp(ce->name, fullname, fullnamelen))\n+\t\tif (ce_namelen(ce) == fullname.len &&\n+\t\t    !memcmp(ce->name, fullname.buf, fullname.len))\n \t\t\tdie(\"Path '%s' is in the index, but not '%s'.\\n\"\n \t\t\t    \"Did you mean ':%d:%s' aka ':%d:./%s'?\",\n-\t\t\t    fullname, filename,\n-\t\t\t    ce_stage(ce), fullname,\n+\t\t\t    fullname.buf, filename,\n+\t\t\t    ce_stage(ce), fullname.buf,\n \t\t\t    ce_stage(ce), filename);\n \t}\n \n@@ -1338,7 +1335,7 @@ static void diagnose_invalid_index_path(int stage,\n \t\tdie(\"Path '%s' does not exist (neither on disk nor in the index).\",\n \t\t    filename);\n \n-\tfree(fullname);\n+\tstrbuf_release(&fullname);\n }\n \n \n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270687","messageId":"20150924210753.GT30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 49/68] fetch-pack: use argv_array for index-pack / unpack-objects","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:54Z","receivedAt":"2015-09-24T21:07:54Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This cleans up a magic number that must be kept in sync with\nthe rest of the code (the number of argv slots). It also\nlets us drop some fixed buffers and an sprintf (since we\ncan now use argv_array_pushf).\n\nWe do still have to keep one fixed buffer for calling\ngethostname, but at least now the size computations for it\nare much simpler.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n fetch-pack.c | 56 +++++++++++++++++++++++++++-----------------------------\n 1 file changed, 27 insertions(+), 29 deletions(-)\n\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 820251a..2dabee9 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -681,11 +681,10 @@ static int get_pack(struct fetch_pack_args *args,\n \t\t    int xd[2], char **pack_lockfile)\n {\n \tstruct async demux;\n-\tconst char *argv[22];\n-\tchar keep_arg[256];\n-\tchar hdr_arg[256];\n-\tconst char **av, *cmd_name;\n \tint do_keep = args->keep_pack;\n+\tconst char *cmd_name;\n+\tstruct pack_header header;\n+\tint pass_header = 0;\n \tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tint ret;\n \n@@ -705,17 +704,11 @@ static int get_pack(struct fetch_pack_args *args,\n \telse\n \t\tdemux.out = xd[0];\n \n-\tcmd.argv = argv;\n-\tav = argv;\n-\t*hdr_arg = 0;\n \tif (!args->keep_pack && unpack_limit) {\n-\t\tstruct pack_header header;\n \n \t\tif (read_pack_header(demux.out, &header))\n \t\t\tdie(\"protocol error: bad pack header\");\n-\t\tsnprintf(hdr_arg, sizeof(hdr_arg),\n-\t\t\t \"--pack_header=%\"PRIu32\",%\"PRIu32,\n-\t\t\t ntohl(header.hdr_version), ntohl(header.hdr_entries));\n+\t\tpass_header = 1;\n \t\tif (ntohl(header.hdr_entries) < unpack_limit)\n \t\t\tdo_keep = 0;\n \t\telse\n@@ -723,44 +716,49 @@ static int get_pack(struct fetch_pack_args *args,\n \t}\n \n \tif (alternate_shallow_file) {\n-\t\t*av++ = \"--shallow-file\";\n-\t\t*av++ = alternate_shallow_file;\n+\t\targv_array_push(&cmd.args, \"--shallow-file\");\n+\t\targv_array_push(&cmd.args, alternate_shallow_file);\n \t}\n \n \tif (do_keep) {\n \t\tif (pack_lockfile)\n \t\t\tcmd.out = -1;\n-\t\t*av++ = cmd_name = \"index-pack\";\n-\t\t*av++ = \"--stdin\";\n+\t\tcmd_name = \"index-pack\";\n+\t\targv_array_push(&cmd.args, cmd_name);\n+\t\targv_array_push(&cmd.args, \"--stdin\");\n \t\tif (!args->quiet && !args->no_progress)\n-\t\t\t*av++ = \"-v\";\n+\t\t\targv_array_push(&cmd.args, \"-v\");\n \t\tif (args->use_thin_pack)\n-\t\t\t*av++ = \"--fix-thin\";\n+\t\t\targv_array_push(&cmd.args, \"--fix-thin\");\n \t\tif (args->lock_pack || unpack_limit) {\n-\t\t\tint s = sprintf(keep_arg,\n-\t\t\t\t\t\"--keep=fetch-pack %\"PRIuMAX \" on \", (uintmax_t) getpid());\n-\t\t\tif (gethostname(keep_arg + s, sizeof(keep_arg) - s))\n-\t\t\t\tstrcpy(keep_arg + s, \"localhost\");\n-\t\t\t*av++ = keep_arg;\n+\t\t\tchar hostname[256];\n+\t\t\tif (gethostname(hostname, sizeof(hostname)))\n+\t\t\t\txsnprintf(hostname, sizeof(hostname), \"localhost\");\n+\t\t\targv_array_pushf(&cmd.args,\n+\t\t\t\t\t\"--keep=fetch-pack %\"PRIuMAX \" on %s\",\n+\t\t\t\t\t(uintmax_t)getpid(), hostname);\n \t\t}\n \t\tif (args->check_self_contained_and_connected)\n-\t\t\t*av++ = \"--check-self-contained-and-connected\";\n+\t\t\targv_array_push(&cmd.args, \"--check-self-contained-and-connected\");\n \t}\n \telse {\n-\t\t*av++ = cmd_name = \"unpack-objects\";\n+\t\tcmd_name = \"unpack-objects\";\n+\t\targv_array_push(&cmd.args, cmd_name);\n \t\tif (args->quiet || args->no_progress)\n-\t\t\t*av++ = \"-q\";\n+\t\t\targv_array_push(&cmd.args, \"-q\");\n \t\targs->check_self_contained_and_connected = 0;\n \t}\n-\tif (*hdr_arg)\n-\t\t*av++ = hdr_arg;\n+\n+\tif (pass_header)\n+\t\targv_array_pushf(&cmd.args, \"--pack_header=%\"PRIu32\",%\"PRIu32,\n+\t\t\t\t ntohl(header.hdr_version),\n+\t\t\t\t ntohl(header.hdr_entries));\n \tif (fetch_fsck_objects >= 0\n \t    ? fetch_fsck_objects\n \t    : transfer_fsck_objects >= 0\n \t    ? transfer_fsck_objects\n \t    : 0)\n-\t\t*av++ = \"--strict\";\n-\t*av++ = NULL;\n+\t\targv_array_push(&cmd.args, \"--strict\");\n \n \tcmd.in = demux.out;\n \tcmd.git_cmd = 1;\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270701","messageId":"20150924210756.GU30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 50/68] http-push: use an argv_array for setup_revisions","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:56Z","receivedAt":"2015-09-24T21:07:56Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This drops the magic number for the fixed-size argv arrays,\nso we do not have to wonder if we are overflowing it. We can\nalso drop some confusing sha1_to_hex memory allocation\n(which seems to predate the ring of buffers allowing\nmultiple calls), and get rid of an unchecked sprintf call.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n http-push.c | 32 ++++++++++----------------------\n 1 file changed, 10 insertions(+), 22 deletions(-)\n\ndiff --git a/http-push.c b/http-push.c\nindex e501c28..43a9036 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -10,6 +10,7 @@\n #include \"remote.h\"\n #include \"list-objects.h\"\n #include \"sigchain.h\"\n+#include \"argv-array.h\"\n \n #ifdef EXPAT_NEEDS_XMLPARSE_H\n #include <xmlparse.h>\n@@ -1856,9 +1857,7 @@ int main(int argc, char **argv)\n \tnew_refs = 0;\n \tfor (ref = remote_refs; ref; ref = ref->next) {\n \t\tchar old_hex[60], *new_hex;\n-\t\tconst char *commit_argv[5];\n-\t\tint commit_argc;\n-\t\tchar *new_sha1_hex, *old_sha1_hex;\n+\t\tstruct argv_array commit_argv = ARGV_ARRAY_INIT;\n \n \t\tif (!ref->peer_ref)\n \t\t\tcontinue;\n@@ -1937,27 +1936,15 @@ int main(int argc, char **argv)\n \t\t}\n \n \t\t/* Set up revision info for this refspec */\n-\t\tcommit_argc = 3;\n-\t\tnew_sha1_hex = xstrdup(sha1_to_hex(ref->new_sha1));\n-\t\told_sha1_hex = NULL;\n-\t\tcommit_argv[1] = \"--objects\";\n-\t\tcommit_argv[2] = new_sha1_hex;\n-\t\tif (!push_all && !is_null_sha1(ref->old_sha1)) {\n-\t\t\told_sha1_hex = xmalloc(42);\n-\t\t\tsprintf(old_sha1_hex, \"^%s\",\n-\t\t\t\tsha1_to_hex(ref->old_sha1));\n-\t\t\tcommit_argv[3] = old_sha1_hex;\n-\t\t\tcommit_argc++;\n-\t\t}\n-\t\tcommit_argv[commit_argc] = NULL;\n+\t\targv_array_push(&commit_argv, \"\"); /* ignored */\n+\t\targv_array_push(&commit_argv, \"--objects\");\n+\t\targv_array_push(&commit_argv, sha1_to_hex(ref->new_sha1));\n+\t\tif (!push_all && !is_null_sha1(ref->old_sha1))\n+\t\t\targv_array_pushf(&commit_argv, \"^%s\",\n+\t\t\t\t\t sha1_to_hex(ref->old_sha1));\n \t\tinit_revisions(&revs, setup_git_directory());\n-\t\tsetup_revisions(commit_argc, commit_argv, &revs, NULL);\n+\t\tsetup_revisions(commit_argv.argc, commit_argv.argv, &revs, NULL);\n \t\trevs.edge_hint = 0; /* just in case */\n-\t\tfree(new_sha1_hex);\n-\t\tif (old_sha1_hex) {\n-\t\t\tfree(old_sha1_hex);\n-\t\t\tcommit_argv[1] = NULL;\n-\t\t}\n \n \t\t/* Generate a list of objects that need to be pushed */\n \t\tpushing = 0;\n@@ -1986,6 +1973,7 @@ int main(int argc, char **argv)\n \t\t\tprintf(\"%s %s\\n\", !rc ? \"ok\" : \"error\", ref->name);\n \t\tunlock_remote(ref_lock);\n \t\tcheck_locks();\n+\t\targv_array_clear(&commit_argv);\n \t}\n \n \t/* Update remote server info if appropriate */\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270705","messageId":"20150924210758.GV30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 51/68] stat_tracking_info: convert to argv_array","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:07:58Z","receivedAt":"2015-09-24T21:07:58Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"In addition to dropping the magic number for the fixed-size\nargv, we can also drop a fixed-length buffer and some\nstrcpy's into it.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n remote.c | 26 ++++++++++++--------------\n 1 file changed, 12 insertions(+), 14 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex a01f13a..1101f82 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -8,6 +8,7 @@\n #include \"tag.h\"\n #include \"string-list.h\"\n #include \"mergesort.h\"\n+#include \"argv-array.h\"\n \n enum map_direction { FROM_SRC, FROM_DST };\n \n@@ -1997,10 +1998,9 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n {\n \tunsigned char sha1[20];\n \tstruct commit *ours, *theirs;\n-\tchar symmetric[84];\n \tstruct rev_info revs;\n-\tconst char *rev_argv[10], *base;\n-\tint rev_argc;\n+\tconst char *base;\n+\tstruct argv_array argv = ARGV_ARRAY_INIT;\n \n \t/* Cannot stat unless we are marked to build on top of somebody else. */\n \tbase = branch_get_upstream(branch, NULL);\n@@ -2029,19 +2029,15 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n \t}\n \n \t/* Run \"rev-list --left-right ours...theirs\" internally... */\n-\trev_argc = 0;\n-\trev_argv[rev_argc++] = NULL;\n-\trev_argv[rev_argc++] = \"--left-right\";\n-\trev_argv[rev_argc++] = symmetric;\n-\trev_argv[rev_argc++] = \"--\";\n-\trev_argv[rev_argc] = NULL;\n-\n-\tstrcpy(symmetric, sha1_to_hex(ours->object.sha1));\n-\tstrcpy(symmetric + 40, \"...\");\n-\tstrcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));\n+\targv_array_push(&argv, \"\"); /* ignored */\n+\targv_array_push(&argv, \"--left-right\");\n+\targv_array_pushf(&argv, \"%s...%s\",\n+\t\t\t sha1_to_hex(ours->object.sha1),\n+\t\t\t sha1_to_hex(theirs->object.sha1));\n+\targv_array_push(&argv, \"--\");\n \n \tinit_revisions(&revs, NULL);\n-\tsetup_revisions(rev_argc, rev_argv, &revs, NULL);\n+\tsetup_revisions(argv.argc, argv.argv, &revs, NULL);\n \tif (prepare_revision_walk(&revs))\n \t\tdie(\"revision walk setup failed\");\n \n@@ -2061,6 +2057,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n \t/* clear object flags smudged by the above traversal */\n \tclear_commit_marks(ours, ALL_REV_FLAGS);\n \tclear_commit_marks(theirs, ALL_REV_FLAGS);\n+\n+\targv_array_clear(&argv);\n \treturn 0;\n }\n \n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270704","messageId":"20150924210800.GW30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 52/68] daemon: use cld->env_array when re-spawning","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:00Z","receivedAt":"2015-09-24T21:08:00Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This avoids an ugly strcat into a fixed-size buffer. It's\nnot wrong (the buffer is plenty large enough for an IPv6\naddress plus some minor formatting), but it takes some\neffort to verify that.\n\nUnfortunately we are still stuck with some fixed-size\nbuffers to hold the output of inet_ntop. But at least we now\npass very easy-to-verify parameters, rather than doing a\nmanual computation to account for other data in the buffer.\n\nAs a side effect, this also fixes the case where we might\npass an uninitialized portbuf buffer through the\nenvironment. This probably couldn't happen in practice, as\nit would mean that addr->sa_family was neither AF_INET nor\nAF_INET6 (and that is all we are listening on).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n daemon.c | 26 ++++++++++----------------\n 1 file changed, 10 insertions(+), 16 deletions(-)\n\ndiff --git a/daemon.c b/daemon.c\nindex 5218a3f..56679a1 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -811,8 +811,6 @@ static char **cld_argv;\n static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)\n {\n \tstruct child_process cld = CHILD_PROCESS_INIT;\n-\tchar addrbuf[300] = \"REMOTE_ADDR=\", portbuf[300];\n-\tchar *env[] = { addrbuf, portbuf, NULL };\n \n \tif (max_connections && live_children >= max_connections) {\n \t\tkill_some_child();\n@@ -826,27 +824,23 @@ static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)\n \t}\n \n \tif (addr->sa_family == AF_INET) {\n+\t\tchar buf[128] = \"\";\n \t\tstruct sockaddr_in *sin_addr = (void *) addr;\n-\t\tinet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf + 12,\n-\t\t    sizeof(addrbuf) - 12);\n-\t\tsnprintf(portbuf, sizeof(portbuf), \"REMOTE_PORT=%d\",\n-\t\t    ntohs(sin_addr->sin_port));\n+\t\tinet_ntop(addr->sa_family, &sin_addr->sin_addr, buf, sizeof(buf));\n+\t\targv_array_pushf(&cld.env_array, \"REMOTE_ADDR=%s\", buf);\n+\t\targv_array_pushf(&cld.env_array, \"REMOTE_PORT=%d\",\n+\t\t\t\t ntohs(sin_addr->sin_port));\n #ifndef NO_IPV6\n \t} else if (addr->sa_family == AF_INET6) {\n+\t\tchar buf[128] = \"\";\n \t\tstruct sockaddr_in6 *sin6_addr = (void *) addr;\n-\n-\t\tchar *buf = addrbuf + 12;\n-\t\t*buf++ = '['; *buf = '\\0'; /* stpcpy() is cool */\n-\t\tinet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf,\n-\t\t    sizeof(addrbuf) - 13);\n-\t\tstrcat(buf, \"]\");\n-\n-\t\tsnprintf(portbuf, sizeof(portbuf), \"REMOTE_PORT=%d\",\n-\t\t    ntohs(sin6_addr->sin6_port));\n+\t\tinet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(buf));\n+\t\targv_array_pushf(&cld.env_array, \"REMOTE_ADDR=[%s]\", buf);\n+\t\targv_array_pushf(&cld.env_array, \"REMOTE_PORT=%d\",\n+\t\t\t\t ntohs(sin6_addr->sin6_port));\n #endif\n \t}\n \n-\tcld.env = (const char **)env;\n \tcld.argv = (const char **)cld_argv;\n \tcld.in = incoming;\n \tcld.out = dup(incoming);\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270689","messageId":"20150924210802.GX30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 53/68] use sha1_to_hex_r() instead of strcpy","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:03Z","receivedAt":"2015-09-24T21:08:03Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Before sha1_to_hex_r() existed, a simple way to get hex\nsha1 into a buffer was with:\n\n  strcpy(buf, sha1_to_hex(sha1));\n\nThis isn't wrong (assuming the buf is 41 characters), but it\nmakes auditing the code base for bad strcpy() calls harder,\nas these become false positives.\n\nLet's convert them to sha1_to_hex_r(), and likewise for\nsome calls to find_unique_abbrev(). While we're here, we'll\ndouble-check that all of the buffers are correctly sized,\nand use the more obvious GIT_SHA1_HEXSZ constant.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/blame.c        |  8 ++++----\n builtin/merge-index.c  |  4 ++--\n builtin/merge.c        | 20 ++++++++++----------\n builtin/receive-pack.c | 15 +++++++++------\n builtin/rev-list.c     |  4 ++--\n diff.c                 |  9 ++++-----\n 6 files changed, 31 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 4db01c1..e253ac0 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1867,9 +1867,9 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,\n \tint cnt;\n \tconst char *cp;\n \tstruct origin *suspect = ent->suspect;\n-\tchar hex[41];\n+\tchar hex[GIT_SHA1_HEXSZ + 1];\n \n-\tstrcpy(hex, sha1_to_hex(suspect->commit->object.sha1));\n+\tsha1_to_hex_r(hex, suspect->commit->object.sha1);\n \tprintf(\"%s %d %d %d\\n\",\n \t       hex,\n \t       ent->s_lno + 1,\n@@ -1905,11 +1905,11 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \tconst char *cp;\n \tstruct origin *suspect = ent->suspect;\n \tstruct commit_info ci;\n-\tchar hex[41];\n+\tchar hex[GIT_SHA1_HEXSZ + 1];\n \tint show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);\n \n \tget_commit_info(suspect->commit, &ci, 1);\n-\tstrcpy(hex, sha1_to_hex(suspect->commit->object.sha1));\n+\tsha1_to_hex_r(hex, suspect->commit->object.sha1);\n \n \tcp = nth_line(sb, ent->lno);\n \tfor (cnt = 0; cnt < ent->num_lines; cnt++) {\ndiff --git a/builtin/merge-index.c b/builtin/merge-index.c\nindex 1d66111..1c3427c 100644\n--- a/builtin/merge-index.c\n+++ b/builtin/merge-index.c\n@@ -9,7 +9,7 @@ static int merge_entry(int pos, const char *path)\n {\n \tint found;\n \tconst char *arguments[] = { pgm, \"\", \"\", \"\", path, \"\", \"\", \"\", NULL };\n-\tchar hexbuf[4][60];\n+\tchar hexbuf[4][GIT_SHA1_HEXSZ + 1];\n \tchar ownbuf[4][60];\n \n \tif (pos >= active_nr)\n@@ -22,7 +22,7 @@ static int merge_entry(int pos, const char *path)\n \t\tif (strcmp(ce->name, path))\n \t\t\tbreak;\n \t\tfound++;\n-\t\tstrcpy(hexbuf[stage], sha1_to_hex(ce->sha1));\n+\t\tsha1_to_hex_r(hexbuf[stage], ce->sha1);\n \t\txsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), \"%o\", ce->ce_mode);\n \t\targuments[stage] = hexbuf[stage];\n \t\targuments[stage + 4] = ownbuf[stage];\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex a0edaca..a0a9328 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -1319,13 +1319,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (verify_signatures) {\n \t\tfor (p = remoteheads; p; p = p->next) {\n \t\t\tstruct commit *commit = p->item;\n-\t\t\tchar hex[41];\n+\t\t\tchar hex[GIT_SHA1_HEXSZ + 1];\n \t\t\tstruct signature_check signature_check;\n \t\t\tmemset(&signature_check, 0, sizeof(signature_check));\n \n \t\t\tcheck_commit_signature(commit, &signature_check);\n \n-\t\t\tstrcpy(hex, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));\n+\t\t\tfind_unique_abbrev_r(hex, commit->object.sha1, DEFAULT_ABBREV);\n \t\t\tswitch (signature_check.result) {\n \t\t\tcase 'G':\n \t\t\t\tbreak;\n@@ -1415,15 +1415,15 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t/* Again the most common case of merging one remote. */\n \t\tstruct strbuf msg = STRBUF_INIT;\n \t\tstruct commit *commit;\n-\t\tchar hex[41];\n \n-\t\tstrcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV));\n-\n-\t\tif (verbosity >= 0)\n-\t\t\tprintf(_(\"Updating %s..%s\\n\"),\n-\t\t\t\thex,\n-\t\t\t\tfind_unique_abbrev(remoteheads->item->object.sha1,\n-\t\t\t\tDEFAULT_ABBREV));\n+\t\tif (verbosity >= 0) {\n+\t\t\tchar from[GIT_SHA1_HEXSZ + 1], to[GIT_SHA1_HEXSZ + 1];\n+\t\t\tfind_unique_abbrev_r(from, head_commit->object.sha1,\n+\t\t\t\t\t      DEFAULT_ABBREV);\n+\t\t\tfind_unique_abbrev_r(to, remoteheads->item->object.sha1,\n+\t\t\t\t\t      DEFAULT_ABBREV);\n+\t\t\tprintf(_(\"Updating %s..%s\\n\"), from, to);\n+\t\t}\n \t\tstrbuf_addstr(&msg, \"Fast-forward\");\n \t\tif (have_message)\n \t\t\tstrbuf_addstr(&msg,\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 04d2bdf..39eb064 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -1071,8 +1071,11 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)\n \tconst char *dst_name;\n \tstruct string_list_item *item;\n \tstruct command *dst_cmd;\n-\tunsigned char sha1[20];\n-\tchar cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];\n+\tunsigned char sha1[GIT_SHA1_RAWSZ];\n+\tchar cmd_oldh[GIT_SHA1_HEXSZ + 1],\n+\t     cmd_newh[GIT_SHA1_HEXSZ + 1],\n+\t     dst_oldh[GIT_SHA1_HEXSZ + 1],\n+\t     dst_newh[GIT_SHA1_HEXSZ + 1];\n \tint flag;\n \n \tstrbuf_addf(&buf, \"%s%s\", get_git_namespace(), cmd->ref_name);\n@@ -1103,10 +1106,10 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)\n \n \tdst_cmd->skip_update = 1;\n \n-\tstrcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));\n-\tstrcpy(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));\n-\tstrcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));\n-\tstrcpy(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));\n+\tfind_unique_abbrev_r(cmd_oldh, cmd->old_sha1, DEFAULT_ABBREV);\n+\tfind_unique_abbrev_r(cmd_newh, cmd->new_sha1, DEFAULT_ABBREV);\n+\tfind_unique_abbrev_r(dst_oldh, dst_cmd->old_sha1, DEFAULT_ABBREV);\n+\tfind_unique_abbrev_r(dst_newh, dst_cmd->new_sha1, DEFAULT_ABBREV);\n \trp_error(\"refusing inconsistent update between symref '%s' (%s..%s) and\"\n \t\t \" its target '%s' (%s..%s)\",\n \t\t cmd->ref_name, cmd_oldh, cmd_newh,\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex d80d1ed..491d298 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -217,7 +217,7 @@ static void print_var_int(const char *var, int val)\n static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)\n {\n \tint cnt, flags = info->flags;\n-\tchar hex[41] = \"\";\n+\tchar hex[GIT_SHA1_HEXSZ + 1] = \"\";\n \tstruct commit_list *tried;\n \tstruct rev_info *revs = info->revs;\n \n@@ -242,7 +242,7 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)\n \t\tcnt = reaches;\n \n \tif (revs->commits)\n-\t\tstrcpy(hex, sha1_to_hex(revs->commits->item->object.sha1));\n+\t\tsha1_to_hex_r(hex, revs->commits->item->object.sha1);\n \n \tif (flags & BISECT_SHOW_ALL) {\n \t\ttraverse_commit_list(revs, show_commit, show_object, info);\ndiff --git a/diff.c b/diff.c\nindex 788e371..2a37378 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -322,7 +322,7 @@ static struct diff_tempfile {\n \t */\n \tconst char *name;\n \n-\tchar hex[41];\n+\tchar hex[GIT_SHA1_HEXSZ + 1];\n \tchar mode[10];\n \n \t/*\n@@ -2878,8 +2878,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,\n \t\tdie_errno(\"unable to write temp-file\");\n \tclose_tempfile(&temp->tempfile);\n \ttemp->name = get_tempfile_path(&temp->tempfile);\n-\tstrcpy(temp->hex, sha1_to_hex(sha1));\n-\ttemp->hex[40] = 0;\n+\tsha1_to_hex_r(temp->hex, sha1);\n \txsnprintf(temp->mode, sizeof(temp->mode), \"%06o\", mode);\n \tstrbuf_release(&buf);\n \tstrbuf_release(&template);\n@@ -2926,9 +2925,9 @@ static struct diff_tempfile *prepare_temp_file(const char *name,\n \t\t\t/* we can borrow from the file in the work tree */\n \t\t\ttemp->name = name;\n \t\t\tif (!one->sha1_valid)\n-\t\t\t\tstrcpy(temp->hex, sha1_to_hex(null_sha1));\n+\t\t\t\tsha1_to_hex_r(temp->hex, null_sha1);\n \t\t\telse\n-\t\t\t\tstrcpy(temp->hex, sha1_to_hex(one->sha1));\n+\t\t\t\tsha1_to_hex_r(temp->hex, one->sha1);\n \t\t\t/* Even though we may sometimes borrow the\n \t\t\t * contents from the work tree, we always want\n \t\t\t * one->mode.  mode is trustworthy even when\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270706","messageId":"20150924210805.GY30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 54/68] drop strcpy in favor of raw sha1_to_hex","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:05Z","receivedAt":"2015-09-24T21:08:05Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"In some cases where we strcpy() the result of sha1_to_hex(),\nthere's no need; the result goes directly into a printf\nstatement, and we can simply pass the return value from\nsha1_to_hex() directly.\n\nWhen this code was originally written, sha1_to_hex used a\nsingle buffer, and it was not safe to use it twice within a\nsingle expression. That changed as of dcb3450 (sha1_to_hex()\nusage cleanup, 2006-05-03), but this code ewas never\nupdated.\n\nHistory-dug-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n http-push.c | 6 ++----\n walker.c    | 5 ++---\n 2 files changed, 4 insertions(+), 7 deletions(-)\n\ndiff --git a/http-push.c b/http-push.c\nindex 43a9036..48f39b7 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1856,7 +1856,6 @@ int main(int argc, char **argv)\n \n \tnew_refs = 0;\n \tfor (ref = remote_refs; ref; ref = ref->next) {\n-\t\tchar old_hex[60], *new_hex;\n \t\tstruct argv_array commit_argv = ARGV_ARRAY_INIT;\n \n \t\tif (!ref->peer_ref)\n@@ -1911,13 +1910,12 @@ int main(int argc, char **argv)\n \t\t}\n \t\thashcpy(ref->new_sha1, ref->peer_ref->new_sha1);\n \t\tnew_refs++;\n-\t\tstrcpy(old_hex, sha1_to_hex(ref->old_sha1));\n-\t\tnew_hex = sha1_to_hex(ref->new_sha1);\n \n \t\tfprintf(stderr, \"updating '%s'\", ref->name);\n \t\tif (strcmp(ref->name, ref->peer_ref->name))\n \t\t\tfprintf(stderr, \" using '%s'\", ref->peer_ref->name);\n-\t\tfprintf(stderr, \"\\n  from %s\\n  to   %s\\n\", old_hex, new_hex);\n+\t\tfprintf(stderr, \"\\n  from %s\\n  to   %s\\n\",\n+\t\t\tsha1_to_hex(ref->old_sha1), sha1_to_hex(ref->new_sha1));\n \t\tif (dry_run) {\n \t\t\tif (helper_status)\n \t\t\t\tprintf(\"ok %s\\n\", ref->name);\ndiff --git a/walker.c b/walker.c\nindex 44a936c..cdeb63f 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -17,10 +17,9 @@ void walker_say(struct walker *walker, const char *fmt, const char *hex)\n \n static void report_missing(const struct object *obj)\n {\n-\tchar missing_hex[41];\n-\tstrcpy(missing_hex, sha1_to_hex(obj->sha1));\n \tfprintf(stderr, \"Cannot obtain needed %s %s\\n\",\n-\t\tobj->type ? typename(obj->type): \"object\", missing_hex);\n+\t\tobj->type ? typename(obj->type): \"object\",\n+\t\tsha1_to_hex(obj->sha1));\n \tif (!is_null_sha1(current_commit_sha1))\n \t\tfprintf(stderr, \"while processing commit %s.\\n\",\n \t\t\tsha1_to_hex(current_commit_sha1));\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270691","messageId":"20150924210807.GZ30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 55/68] color: add overflow checks for parsing colors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:07Z","receivedAt":"2015-09-24T21:08:07Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Our color parsing is designed to never exceed COLOR_MAXLEN\nbytes. But the relationship between that hand-computed\nnumber and the parsing code is not at all obvious, and we\nmerely hope that it has been computed correctly for all\ncases.\n\nLet's mark the expected \"end\" pointer for the destination\nbuffer and make sure that we do not exceed it.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n color.c | 41 ++++++++++++++++++++++++++---------------\n 1 file changed, 26 insertions(+), 15 deletions(-)\n\ndiff --git a/color.c b/color.c\nindex 9027352..22782f8 100644\n--- a/color.c\n+++ b/color.c\n@@ -150,22 +150,24 @@ int color_parse(const char *value, char *dst)\n  * already have the ANSI escape code in it. \"out\" should have enough\n  * space in it to fit any color.\n  */\n-static char *color_output(char *out, const struct color *c, char type)\n+static char *color_output(char *out, int len, const struct color *c, char type)\n {\n \tswitch (c->type) {\n \tcase COLOR_UNSPECIFIED:\n \tcase COLOR_NORMAL:\n \t\tbreak;\n \tcase COLOR_ANSI:\n+\t\tif (len < 2)\n+\t\t\tdie(\"BUG: color parsing ran out of space\");\n \t\t*out++ = type;\n \t\t*out++ = '0' + c->value;\n \t\tbreak;\n \tcase COLOR_256:\n-\t\tout += sprintf(out, \"%c8;5;%d\", type, c->value);\n+\t\tout += xsnprintf(out, len, \"%c8;5;%d\", type, c->value);\n \t\tbreak;\n \tcase COLOR_RGB:\n-\t\tout += sprintf(out, \"%c8;2;%d;%d;%d\", type,\n-\t\t\t       c->red, c->green, c->blue);\n+\t\tout += xsnprintf(out, len, \"%c8;2;%d;%d;%d\", type,\n+\t\t\t\t c->red, c->green, c->blue);\n \t\tbreak;\n \t}\n \treturn out;\n@@ -180,12 +182,13 @@ int color_parse_mem(const char *value, int value_len, char *dst)\n {\n \tconst char *ptr = value;\n \tint len = value_len;\n+\tchar *end = dst + COLOR_MAXLEN;\n \tunsigned int attr = 0;\n \tstruct color fg = { COLOR_UNSPECIFIED };\n \tstruct color bg = { COLOR_UNSPECIFIED };\n \n \tif (!strncasecmp(value, \"reset\", len)) {\n-\t\tstrcpy(dst, GIT_COLOR_RESET);\n+\t\txsnprintf(dst, end - dst, GIT_COLOR_RESET);\n \t\treturn 0;\n \t}\n \n@@ -224,12 +227,19 @@ int color_parse_mem(const char *value, int value_len, char *dst)\n \t\t\tgoto bad;\n \t}\n \n+#undef OUT\n+#define OUT(x) do { \\\n+\tif (dst == end) \\\n+\t\tdie(\"BUG: color parsing ran out of space\"); \\\n+\t*dst++ = (x); \\\n+} while(0)\n+\n \tif (attr || !color_empty(&fg) || !color_empty(&bg)) {\n \t\tint sep = 0;\n \t\tint i;\n \n-\t\t*dst++ = '\\033';\n-\t\t*dst++ = '[';\n+\t\tOUT('\\033');\n+\t\tOUT('[');\n \n \t\tfor (i = 0; attr; i++) {\n \t\t\tunsigned bit = (1 << i);\n@@ -237,27 +247,28 @@ int color_parse_mem(const char *value, int value_len, char *dst)\n \t\t\t\tcontinue;\n \t\t\tattr &= ~bit;\n \t\t\tif (sep++)\n-\t\t\t\t*dst++ = ';';\n-\t\t\tdst += sprintf(dst, \"%d\", i);\n+\t\t\t\tOUT(';');\n+\t\t\tdst += xsnprintf(dst, end - dst, \"%d\", i);\n \t\t}\n \t\tif (!color_empty(&fg)) {\n \t\t\tif (sep++)\n-\t\t\t\t*dst++ = ';';\n+\t\t\t\tOUT(';');\n \t\t\t/* foreground colors are all in the 3x range */\n-\t\t\tdst = color_output(dst, &fg, '3');\n+\t\t\tdst = color_output(dst, end - dst, &fg, '3');\n \t\t}\n \t\tif (!color_empty(&bg)) {\n \t\t\tif (sep++)\n-\t\t\t\t*dst++ = ';';\n+\t\t\t\tOUT(';');\n \t\t\t/* background colors are all in the 4x range */\n-\t\t\tdst = color_output(dst, &bg, '4');\n+\t\t\tdst = color_output(dst, end - dst, &bg, '4');\n \t\t}\n-\t\t*dst++ = 'm';\n+\t\tOUT('m');\n \t}\n-\t*dst = 0;\n+\tOUT(0);\n \treturn 0;\n bad:\n \treturn error(_(\"invalid color value: %.*s\"), value_len, value);\n+#undef OUT\n }\n \n int git_config_colorbool(const char *var, const char *value)\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270690","messageId":"20150924210809.GA30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 56/68] use alloc_ref rather than hand-allocating \"struct ref\"","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:09Z","receivedAt":"2015-09-24T21:08:09Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This saves us some manual computation, and eliminates a call\nto strcpy.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/fetch.c | 3 +--\n remote-curl.c   | 5 +----\n 2 files changed, 2 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 841880e..ed84963 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -639,8 +639,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\t\tcontinue;\n \n \t\t\tif (rm->peer_ref) {\n-\t\t\t\tref = xcalloc(1, sizeof(*ref) + strlen(rm->peer_ref->name) + 1);\n-\t\t\t\tstrcpy(ref->name, rm->peer_ref->name);\n+\t\t\t\tref = alloc_ref(rm->peer_ref->name);\n \t\t\t\thashcpy(ref->old_sha1, rm->peer_ref->old_sha1);\n \t\t\t\thashcpy(ref->new_sha1, rm->old_sha1);\n \t\t\t\tref->force = rm->peer_ref->force;\ndiff --git a/remote-curl.c b/remote-curl.c\nindex 71fbbb6..cc7a8a6 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -168,10 +168,7 @@ static struct ref *parse_info_refs(struct discovery *heads)\n \t\t\t\t    url.buf);\n \t\t\tdata[i] = 0;\n \t\t\tref_name = mid + 1;\n-\t\t\tref = xmalloc(sizeof(struct ref) +\n-\t\t\t\t      strlen(ref_name) + 1);\n-\t\t\tmemset(ref, 0, sizeof(struct ref));\n-\t\t\tstrcpy(ref->name, ref_name);\n+\t\t\tref = alloc_ref(ref_name);\n \t\t\tget_sha1_hex(start, ref->old_sha1);\n \t\t\tif (!refs)\n \t\t\t\trefs = ref;\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270692","messageId":"20150924210811.GB30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 57/68] avoid sprintf and strcpy with flex arrays","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:12Z","receivedAt":"2015-09-24T21:08:12Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When we are allocating a struct with a FLEX_ARRAY member, we\ngenerally compute the size of the array and then sprintf or\nstrcpy into it. Normally we could improve a dynamic allocation\nlike this by using xstrfmt, but it doesn't work here; we\nhave to account for the size of the rest of the struct.\n\nBut we can improve things a bit by storing the length that\nwe use for the allocation, and then feeding it to xsnprintf\nor memcpy, which makes it more obvious that we are not\nwriting more than the allocated number of bytes.\n\nIt would be nice if we had some kind of helper for\nallocating generic flex arrays, but it doesn't work that\nwell:\n\n - the call signature is a little bit unwieldy:\n\n      d = flex_struct(sizeof(*d), offsetof(d, path), fmt, ...);\n\n   You need offsetof here instead of just writing to the\n   end of the base size, because we don't know how the\n   struct is packed (partially this is because FLEX_ARRAY\n   might not be zero, though we can account for that; but\n   the size of the struct may actually be rounded up for\n   alignment, and we can't know that).\n\n - some sites do clever things, like over-allocating because\n   they know they will write larger things into the buffer\n   later (e.g., struct packed_git here).\n\nSo we're better off to just write out each allocation (or\nadd type-specific helpers, though many of these are one-off\nallocations anyway).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n archive.c       | 5 +++--\n builtin/blame.c | 5 +++--\n fast-import.c   | 6 ++++--\n refs.c          | 8 ++++----\n sha1_file.c     | 5 +++--\n submodule.c     | 6 ++++--\n 6 files changed, 21 insertions(+), 14 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 01b0899..4ac86c8 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -171,13 +171,14 @@ static void queue_directory(const unsigned char *sha1,\n \t\tunsigned mode, int stage, struct archiver_context *c)\n {\n \tstruct directory *d;\n-\td = xmallocz(sizeof(*d) + base->len + 1 + strlen(filename));\n+\tsize_t len = base->len + 1 + strlen(filename) + 1;\n+\td = xmalloc(sizeof(*d) + len);\n \td->up\t   = c->bottom;\n \td->baselen = base->len;\n \td->mode\t   = mode;\n \td->stage   = stage;\n \tc->bottom  = d;\n-\td->len = sprintf(d->path, \"%.*s%s/\", (int)base->len, base->buf, filename);\n+\td->len = xsnprintf(d->path, len, \"%.*s%s/\", (int)base->len, base->buf, filename);\n \thashcpy(d->oid.hash, sha1);\n }\n \ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex e253ac0..e70fb6d 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -459,12 +459,13 @@ static void queue_blames(struct scoreboard *sb, struct origin *porigin,\n static struct origin *make_origin(struct commit *commit, const char *path)\n {\n \tstruct origin *o;\n-\to = xcalloc(1, sizeof(*o) + strlen(path) + 1);\n+\tsize_t pathlen = strlen(path) + 1;\n+\to = xcalloc(1, sizeof(*o) + pathlen);\n \to->commit = commit;\n \to->refcnt = 1;\n \to->next = commit->util;\n \tcommit->util = o;\n-\tstrcpy(o->path, path);\n+\tmemcpy(o->path, path, pathlen); /* includes NUL */\n \treturn o;\n }\n \ndiff --git a/fast-import.c b/fast-import.c\nindex d0c2502..895c6b4 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -863,13 +863,15 @@ static void start_packfile(void)\n {\n \tstatic char tmp_file[PATH_MAX];\n \tstruct packed_git *p;\n+\tint namelen;\n \tstruct pack_header hdr;\n \tint pack_fd;\n \n \tpack_fd = odb_mkstemp(tmp_file, sizeof(tmp_file),\n \t\t\t      \"pack/tmp_pack_XXXXXX\");\n-\tp = xcalloc(1, sizeof(*p) + strlen(tmp_file) + 2);\n-\tstrcpy(p->pack_name, tmp_file);\n+\tnamelen = strlen(tmp_file) + 2;\n+\tp = xcalloc(1, sizeof(*p) + namelen);\n+\txsnprintf(p->pack_name, namelen, \"%s\", tmp_file);\n \tp->pack_fd = pack_fd;\n \tp->do_not_close = 1;\n \tpack_file = sha1fd(pack_fd, p->pack_name);\ndiff --git a/refs.c b/refs.c\nindex c2709de..9937a40 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2695,7 +2695,7 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)\n \t\tint namelen = strlen(entry->name) + 1;\n \t\tstruct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);\n \t\thashcpy(n->sha1, entry->u.value.oid.hash);\n-\t\tstrcpy(n->name, entry->name);\n+\t\tmemcpy(n->name, entry->name, namelen); /* includes NUL */\n \t\tn->next = cb->ref_to_prune;\n \t\tcb->ref_to_prune = n;\n \t}\n@@ -3984,10 +3984,10 @@ void ref_transaction_free(struct ref_transaction *transaction)\n static struct ref_update *add_update(struct ref_transaction *transaction,\n \t\t\t\t     const char *refname)\n {\n-\tsize_t len = strlen(refname);\n-\tstruct ref_update *update = xcalloc(1, sizeof(*update) + len + 1);\n+\tsize_t len = strlen(refname) + 1;\n+\tstruct ref_update *update = xcalloc(1, sizeof(*update) + len);\n \n-\tstrcpy((char *)update->refname, refname);\n+\tmemcpy((char *)update->refname, refname, len); /* includes NUL */\n \tALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);\n \ttransaction->updates[transaction->nr++] = update;\n \treturn update;\ndiff --git a/sha1_file.c b/sha1_file.c\nindex 4211af1..cc3de24 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -1180,9 +1180,10 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local)\n struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)\n {\n \tconst char *path = sha1_pack_name(sha1);\n-\tstruct packed_git *p = alloc_packed_git(strlen(path) + 1);\n+\tint alloc = strlen(path) + 1;\n+\tstruct packed_git *p = alloc_packed_git(alloc);\n \n-\tstrcpy(p->pack_name, path);\n+\tmemcpy(p->pack_name, path, alloc); /* includes NUL */\n \thashcpy(p->sha1, sha1);\n \tif (check_packed_git_idx(idx_path, p)) {\n \t\tfree(p);\ndiff --git a/submodule.c b/submodule.c\nindex 245ed4d..c480ed5 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -122,6 +122,7 @@ static int add_submodule_odb(const char *path)\n \tstruct strbuf objects_directory = STRBUF_INIT;\n \tstruct alternate_object_database *alt_odb;\n \tint ret = 0;\n+\tint alloc;\n \tconst char *git_dir;\n \n \tstrbuf_addf(&objects_directory, \"%s/.git\", path);\n@@ -142,9 +143,10 @@ static int add_submodule_odb(const char *path)\n \t\t\t\t\tobjects_directory.len))\n \t\t\tgoto done;\n \n-\talt_odb = xmalloc(objects_directory.len + 42 + sizeof(*alt_odb));\n+\talloc = objects_directory.len + 42; /* for \"12/345...\" sha1 */\n+\talt_odb = xmalloc(sizeof(*alt_odb) + alloc);\n \talt_odb->next = alt_odb_list;\n-\tstrcpy(alt_odb->base, objects_directory.buf);\n+\txsnprintf(alt_odb->base, alloc, \"%s\", objects_directory.buf);\n \talt_odb->name = alt_odb->base + objects_directory.len;\n \talt_odb->name[2] = '/';\n \talt_odb->name[40] = '\\0';\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270695","messageId":"20150924210814.GC30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 58/68] receive-pack: simplify keep_arg computation","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:14Z","receivedAt":"2015-09-24T21:08:14Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"To generate \"--keep=receive-pack $pid on $host\", we write\nprogressively into a single buffer, which requires keeping\ntrack of how much we've written so far. But since the result\nis destined to go into our argv array, we can simply use\nargv_array_pushf.\n\nUnfortunately we still have to have a fixed-size buffer for\nthe gethostname() call, but at least it now doesn't involve\nany extra size computation. And as a bonus, we drop an\nsprintf and a strcpy call.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/receive-pack.c | 17 ++++++++++-------\n 1 file changed, 10 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 39eb064..bcb624b 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -1524,15 +1524,18 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\tif (status)\n \t\t\treturn \"unpack-objects abnormal exit\";\n \t} else {\n-\t\tint s;\n-\t\tchar keep_arg[256];\n-\n-\t\ts = sprintf(keep_arg, \"--keep=receive-pack %\"PRIuMAX\" on \", (uintmax_t) getpid());\n-\t\tif (gethostname(keep_arg + s, sizeof(keep_arg) - s))\n-\t\t\tstrcpy(keep_arg + s, \"localhost\");\n+\t\tchar hostname[256];\n \n \t\targv_array_pushl(&child.args, \"index-pack\",\n-\t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n+\t\t\t\t \"--stdin\", hdr_arg, NULL);\n+\n+\t\tif (gethostname(hostname, sizeof(hostname)))\n+\t\t\txsnprintf(hostname, sizeof(hostname), \"localhost\");\n+\t\targv_array_pushf(&child.args,\n+\t\t\t\t \"--keep=receive-pack %\"PRIuMAX\" on %s\",\n+\t\t\t\t (uintmax_t)getpid(),\n+\t\t\t\t hostname);\n+\n \t\tif (fsck_objects)\n \t\t\targv_array_pushf(&child.args, \"--strict%s\",\n \t\t\t\tfsck_msg_types.buf);\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270693","messageId":"20150924210816.GD30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 59/68] help: clean up kfmclient munging","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:16Z","receivedAt":"2015-09-24T21:08:16Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When we are going to launch \"/path/to/konqueror\", we instead\nrewrite this into \"/path/to/kfmclient\" by duplicating the\noriginal string and writing over the ending bits. This can\nbe done more obviously with strip_suffix and xstrfmt.\n\nNote that we also fix a subtle bug with the \"filename\"\nparameter, which is passed as argv[0] to the child. If the\nuser has configured a program name with no directory\ncomponent, we always pass the string \"kfmclient\", even if\nyour program is called something else. But if you give a\nfull path, we give the basename of that path. But more\nbizarrely, if we rewrite \"konqueror\" to \"kfmclient\", we\nstill pass \"konqueror\".\n\nThe history of this function doesn't reveal anything\ninteresting, so it looks like just an oversight from\ncombining the suffix-munging with the basename-finding.\nLet's just call basename on the munged path, which produces\nconsistent results (if you gave a program, whether a full\npath or not, we pass its basename).\n\nProbably this doesn't matter at all in practice, but it\nmakes the code slightly less confusing to read.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/help.c | 15 ++++-----------\n 1 file changed, 4 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/help.c b/builtin/help.c\nindex fba8c01..e1650ab 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -140,17 +140,10 @@ static void exec_man_konqueror(const char *path, const char *page)\n \n \t\t/* It's simpler to launch konqueror using kfmclient. */\n \t\tif (path) {\n-\t\t\tconst char *file = strrchr(path, '/');\n-\t\t\tif (file && !strcmp(file + 1, \"konqueror\")) {\n-\t\t\t\tchar *new = xstrdup(path);\n-\t\t\t\tchar *dest = strrchr(new, '/');\n-\n-\t\t\t\t/* strlen(\"konqueror\") == strlen(\"kfmclient\") */\n-\t\t\t\tstrcpy(dest + 1, \"kfmclient\");\n-\t\t\t\tpath = new;\n-\t\t\t}\n-\t\t\tif (file)\n-\t\t\t\tfilename = file;\n+\t\t\tsize_t len;\n+\t\t\tif (strip_suffix(path, \"/konqueror\", &len))\n+\t\t\t\tpath = xstrfmt(\"%.*s/kfmclient\", (int)len, path);\n+\t\t\tfilename = basename((char *)path);\n \t\t} else\n \t\t\tpath = \"kfmclient\";\n \t\tstrbuf_addf(&man_page, \"man:%s(1)\", page);\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270707","messageId":"20150924210818.GE30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 60/68] prefer memcpy to strcpy","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:19Z","receivedAt":"2015-09-24T21:08:19Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When we already know the length of a string (e.g., because\nwe just malloc'd to fit it), it's nicer to use memcpy than\nstrcpy, as it makes it more obvious that we are not going to\noverflow the buffer (because the size we pass matches the\nsize in the allocation).\n\nThis also eliminates calls to strcpy, which make auditing\nthe code base harder.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n compat/nedmalloc/nedmalloc.c | 5 +++--\n fast-import.c                | 5 +++--\n revision.c                   | 2 +-\n 3 files changed, 7 insertions(+), 5 deletions(-)\n\ndiff --git a/compat/nedmalloc/nedmalloc.c b/compat/nedmalloc/nedmalloc.c\nindex 609ebba..a0a16eb 100644\n--- a/compat/nedmalloc/nedmalloc.c\n+++ b/compat/nedmalloc/nedmalloc.c\n@@ -957,8 +957,9 @@ char *strdup(const char *s1)\n {\n \tchar *s2 = 0;\n \tif (s1) {\n-\t\ts2 = malloc(strlen(s1) + 1);\n-\t\tstrcpy(s2, s1);\n+\t\tsize_t len = strlen(s1) + 1;\n+\t\ts2 = malloc(len);\n+\t\tmemcpy(s2, s1, len);\n \t}\n \treturn s2;\n }\ndiff --git a/fast-import.c b/fast-import.c\nindex 895c6b4..cf6d8bc 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -644,8 +644,9 @@ static void *pool_calloc(size_t count, size_t size)\n \n static char *pool_strdup(const char *s)\n {\n-\tchar *r = pool_alloc(strlen(s) + 1);\n-\tstrcpy(r, s);\n+\tsize_t len = strlen(s) + 1;\n+\tchar *r = pool_alloc(len);\n+\tmemcpy(r, s, len);\n \treturn r;\n }\n \ndiff --git a/revision.c b/revision.c\nindex af2a18e..2236463 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -38,7 +38,7 @@ char *path_name(const struct name_path *path, const char *name)\n \t}\n \tn = xmalloc(len);\n \tm = n + len - (nlen + 1);\n-\tstrcpy(m, name);\n+\tmemcpy(m, name, nlen + 1);\n \tfor (p = path; p; p = p->up) {\n \t\tif (p->elem_len) {\n \t\t\tm -= p->elem_len + 1;\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270694","messageId":"20150924210821.GF30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 61/68] color: add color_set helper for copying raw colors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:21Z","receivedAt":"2015-09-24T21:08:21Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"To set up default colors, we sometimes strcpy() from the\ndefault string literals into our color buffers. This isn't a\nbug (assuming the destination is COLOR_MAXLEN bytes), but\nmakes it harder to audit the code for problematic strcpy\ncalls.\n\nLet's introduce a color_set which copies under the\nassumption that there are COLOR_MAXLEN bytes in the\ndestination (of course you can call it on a smaller buffer,\nso this isn't providing a huge amount of safety, but it's\nmore convenient than calling xsnprintf yourself).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n color.c |  5 +++++\n color.h |  7 +++++++\n grep.c  | 32 ++++++++++++++++----------------\n 3 files changed, 28 insertions(+), 16 deletions(-)\n\ndiff --git a/color.c b/color.c\nindex 22782f8..8f85153 100644\n--- a/color.c\n+++ b/color.c\n@@ -145,6 +145,11 @@ int color_parse(const char *value, char *dst)\n \treturn color_parse_mem(value, strlen(value), dst);\n }\n \n+void color_set(char *dst, const char *color_bytes)\n+{\n+\txsnprintf(dst, COLOR_MAXLEN, \"%s\", color_bytes);\n+}\n+\n /*\n  * Write the ANSI color codes for \"c\" to \"out\"; the string should\n  * already have the ANSI escape code in it. \"out\" should have enough\ndiff --git a/color.h b/color.h\nindex 7fe77fb..e155d13 100644\n--- a/color.h\n+++ b/color.h\n@@ -75,6 +75,13 @@ extern int color_stdout_is_tty;\n int git_color_config(const char *var, const char *value, void *cb);\n int git_color_default_config(const char *var, const char *value, void *cb);\n \n+/*\n+ * Set the color buffer (which must be COLOR_MAXLEN bytes)\n+ * to the raw color bytes; this is useful for initializing\n+ * default color variables.\n+ */\n+void color_set(char *dst, const char *color_bytes);\n+\n int git_config_colorbool(const char *var, const char *value);\n int want_color(int var);\n int color_parse(const char *value, char *dst);\ndiff --git a/grep.c b/grep.c\nindex 6c68d5b..7b2b96a 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -31,14 +31,14 @@ void init_grep_defaults(void)\n \topt->max_depth = -1;\n \topt->pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED;\n \topt->extended_regexp_option = 0;\n-\tstrcpy(opt->color_context, \"\");\n-\tstrcpy(opt->color_filename, \"\");\n-\tstrcpy(opt->color_function, \"\");\n-\tstrcpy(opt->color_lineno, \"\");\n-\tstrcpy(opt->color_match_context, GIT_COLOR_BOLD_RED);\n-\tstrcpy(opt->color_match_selected, GIT_COLOR_BOLD_RED);\n-\tstrcpy(opt->color_selected, \"\");\n-\tstrcpy(opt->color_sep, GIT_COLOR_CYAN);\n+\tcolor_set(opt->color_context, \"\");\n+\tcolor_set(opt->color_filename, \"\");\n+\tcolor_set(opt->color_function, \"\");\n+\tcolor_set(opt->color_lineno, \"\");\n+\tcolor_set(opt->color_match_context, GIT_COLOR_BOLD_RED);\n+\tcolor_set(opt->color_match_selected, GIT_COLOR_BOLD_RED);\n+\tcolor_set(opt->color_selected, \"\");\n+\tcolor_set(opt->color_sep, GIT_COLOR_CYAN);\n \topt->color = -1;\n }\n \n@@ -151,14 +151,14 @@ void grep_init(struct grep_opt *opt, const char *prefix)\n \topt->regflags = def->regflags;\n \topt->relative = def->relative;\n \n-\tstrcpy(opt->color_context, def->color_context);\n-\tstrcpy(opt->color_filename, def->color_filename);\n-\tstrcpy(opt->color_function, def->color_function);\n-\tstrcpy(opt->color_lineno, def->color_lineno);\n-\tstrcpy(opt->color_match_context, def->color_match_context);\n-\tstrcpy(opt->color_match_selected, def->color_match_selected);\n-\tstrcpy(opt->color_selected, def->color_selected);\n-\tstrcpy(opt->color_sep, def->color_sep);\n+\tcolor_set(opt->color_context, def->color_context);\n+\tcolor_set(opt->color_filename, def->color_filename);\n+\tcolor_set(opt->color_function, def->color_function);\n+\tcolor_set(opt->color_lineno, def->color_lineno);\n+\tcolor_set(opt->color_match_context, def->color_match_context);\n+\tcolor_set(opt->color_match_selected, def->color_match_selected);\n+\tcolor_set(opt->color_selected, def->color_selected);\n+\tcolor_set(opt->color_sep, def->color_sep);\n }\n \n void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt)\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270696","messageId":"20150924210823.GG30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 62/68] notes: document length of fanout path with a constant","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:24Z","receivedAt":"2015-09-24T21:08:24Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We know that a fanned-out sha1 in a notes tree cannot be\nmore than \"aa/bb/cc/...\", and we have an assert() to confirm\nthat. But let's factor out that length into a constant so we\ncan be sure it is used consistently. And even though we\nassert() earlier, let's replace a strcpy with xsnprintf, so\nit is clear to a reader that all cases are covered.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n notes.c | 9 ++++++---\n 1 file changed, 6 insertions(+), 3 deletions(-)\n\ndiff --git a/notes.c b/notes.c\nindex eacd2a6..db77922 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -539,6 +539,9 @@ static unsigned char determine_fanout(struct int_node *tree, unsigned char n,\n \treturn fanout + 1;\n }\n \n+/* hex SHA1 + 19 * '/' + NUL */\n+#define FANOUT_PATH_MAX 40 + 19 + 1\n+\n static void construct_path_with_fanout(const unsigned char *sha1,\n \t\tunsigned char fanout, char *path)\n {\n@@ -551,7 +554,7 @@ static void construct_path_with_fanout(const unsigned char *sha1,\n \t\tpath[i++] = '/';\n \t\tfanout--;\n \t}\n-\tstrcpy(path + i, hex_sha1 + j);\n+\txsnprintf(path + i, FANOUT_PATH_MAX - i, \"%s\", hex_sha1 + j);\n }\n \n static int for_each_note_helper(struct notes_tree *t, struct int_node *tree,\n@@ -562,7 +565,7 @@ static int for_each_note_helper(struct notes_tree *t, struct int_node *tree,\n \tvoid *p;\n \tint ret = 0;\n \tstruct leaf_node *l;\n-\tstatic char path[40 + 19 + 1];  /* hex SHA1 + 19 * '/' + NUL */\n+\tstatic char path[FANOUT_PATH_MAX];\n \n \tfanout = determine_fanout(tree, n, fanout);\n \tfor (i = 0; i < 16; i++) {\n@@ -595,7 +598,7 @@ redo:\n \t\t\t\t/* invoke callback with subtree */\n \t\t\t\tunsigned int path_len =\n \t\t\t\t\tl->key_sha1[19] * 2 + fanout;\n-\t\t\t\tassert(path_len < 40 + 19);\n+\t\t\t\tassert(path_len < FANOUT_PATH_MAX - 1);\n \t\t\t\tconstruct_path_with_fanout(l->key_sha1, fanout,\n \t\t\t\t\t\t\t   path);\n \t\t\t\t/* Create trailing slash, if needed */\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270697","messageId":"20150924210826.GH30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 63/68] convert strncpy to memcpy","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:26Z","receivedAt":"2015-09-24T21:08:26Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"strncpy is known to be a confusing function because of its\ntermination semantics.  These calls are all correct, but it\ntakes some examination to see why. In particular, every one\nof them expects to copy up to the length limit, and then\nmakes some arrangement for terminating the result.\n\nWe can just use memcpy, along with noting explicitly how the\nresult is terminated (if it is not already obvious). That\nshould make it more clear to a reader that we are doing the\nright thing.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/help.c | 4 ++--\n fast-import.c  | 2 +-\n tag.c          | 2 +-\n 3 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/help.c b/builtin/help.c\nindex e1650ab..1cd0c1e 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -176,7 +176,7 @@ static void add_man_viewer(const char *name)\n \twhile (*p)\n \t\tp = &((*p)->next);\n \t*p = xcalloc(1, (sizeof(**p) + len + 1));\n-\tstrncpy((*p)->name, name, len);\n+\tmemcpy((*p)->name, name, len); /* NUL-terminated by xcalloc */\n }\n \n static int supported_man_viewer(const char *name, size_t len)\n@@ -192,7 +192,7 @@ static void do_add_man_viewer_info(const char *name,\n {\n \tstruct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1);\n \n-\tstrncpy(new->name, name, len);\n+\tmemcpy(new->name, name, len); /* NUL-terminated by xcalloc */\n \tnew->info = xstrdup(value);\n \tnew->next = man_viewer_info_list;\n \tman_viewer_info_list = new;\ndiff --git a/fast-import.c b/fast-import.c\nindex cf6d8bc..4d01efc 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -703,7 +703,7 @@ static struct atom_str *to_atom(const char *s, unsigned short len)\n \n \tc = pool_alloc(sizeof(struct atom_str) + len + 1);\n \tc->str_len = len;\n-\tstrncpy(c->str_dat, s, len);\n+\tmemcpy(c->str_dat, s, len);\n \tc->str_dat[len] = 0;\n \tc->next_atom = atom_table[hc];\n \tatom_table[hc] = c;\ndiff --git a/tag.c b/tag.c\nindex 5b0ac62..5b2a06d 100644\n--- a/tag.c\n+++ b/tag.c\n@@ -82,7 +82,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)\n \tnl = memchr(bufptr, '\\n', tail - bufptr);\n \tif (!nl || sizeof(type) <= (nl - bufptr))\n \t\treturn -1;\n-\tstrncpy(type, bufptr, nl - bufptr);\n+\tmemcpy(type, bufptr, nl - bufptr);\n \ttype[nl - bufptr] = '\\0';\n \tbufptr = nl + 1;\n \n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270700","messageId":"20150924210828.GI30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 64/68] fsck: drop inode-sorting code","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:28Z","receivedAt":"2015-09-24T21:08:28Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Fsck tries to access loose objects in order of inode number,\nwith the hope that this would make cold cache access faster\non a spinning disk. This dates back to 7e8c174 (fsck-cache:\nsort entries by inode number, 2005-05-02), which predates\nthe invention of packfiles.\n\nThese days, there's not much point in trying to optimize\ncold cache for a large number of loose objects. You are much\nbetter off to simply pack the objects, which will reduce the\ndisk footprint _and_ provide better locality of data access.\n\nSo while you can certainly construct pathological cases\nwhere this code might help, it is not worth the trouble\nanymore.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/fsck.c | 70 ++--------------------------------------------------------\n 1 file changed, 2 insertions(+), 68 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex a019f4a..73c3596 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -39,14 +39,6 @@ static int show_dangling = 1;\n #define ERROR_REACHABLE 02\n #define ERROR_PACK 04\n \n-#ifdef NO_D_INO_IN_DIRENT\n-#define SORT_DIRENT 0\n-#define DIRENT_SORT_HINT(de) 0\n-#else\n-#define SORT_DIRENT 1\n-#define DIRENT_SORT_HINT(de) ((de)->d_ino)\n-#endif\n-\n static int fsck_config(const char *var, const char *value, void *cb)\n {\n \tif (strcmp(var, \"fsck.skiplist\") == 0) {\n@@ -373,64 +365,6 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,\n \treturn fsck_obj(obj);\n }\n \n-/*\n- * This is the sorting chunk size: make it reasonably\n- * big so that we can sort well..\n- */\n-#define MAX_SHA1_ENTRIES (1024)\n-\n-struct sha1_entry {\n-\tunsigned long ino;\n-\tunsigned char sha1[20];\n-};\n-\n-static struct {\n-\tunsigned long nr;\n-\tstruct sha1_entry *entry[MAX_SHA1_ENTRIES];\n-} sha1_list;\n-\n-static int ino_compare(const void *_a, const void *_b)\n-{\n-\tconst struct sha1_entry *a = _a, *b = _b;\n-\tunsigned long ino1 = a->ino, ino2 = b->ino;\n-\treturn ino1 < ino2 ? -1 : ino1 > ino2 ? 1 : 0;\n-}\n-\n-static void fsck_sha1_list(void)\n-{\n-\tint i, nr = sha1_list.nr;\n-\n-\tif (SORT_DIRENT)\n-\t\tqsort(sha1_list.entry, nr,\n-\t\t      sizeof(struct sha1_entry *), ino_compare);\n-\tfor (i = 0; i < nr; i++) {\n-\t\tstruct sha1_entry *entry = sha1_list.entry[i];\n-\t\tunsigned char *sha1 = entry->sha1;\n-\n-\t\tsha1_list.entry[i] = NULL;\n-\t\tif (fsck_sha1(sha1))\n-\t\t\terrors_found |= ERROR_OBJECT;\n-\t\tfree(entry);\n-\t}\n-\tsha1_list.nr = 0;\n-}\n-\n-static void add_sha1_list(unsigned char *sha1, unsigned long ino)\n-{\n-\tstruct sha1_entry *entry = xmalloc(sizeof(*entry));\n-\tint nr;\n-\n-\tentry->ino = ino;\n-\thashcpy(entry->sha1, sha1);\n-\tnr = sha1_list.nr;\n-\tif (nr == MAX_SHA1_ENTRIES) {\n-\t\tfsck_sha1_list();\n-\t\tnr = 0;\n-\t}\n-\tsha1_list.entry[nr] = entry;\n-\tsha1_list.nr = ++nr;\n-}\n-\n static inline int is_loose_object_file(struct dirent *de,\n \t\t\t\t       char *name, unsigned char *sha1)\n {\n@@ -459,7 +393,8 @@ static void fsck_dir(int i, char *path)\n \t\tif (is_dot_or_dotdot(de->d_name))\n \t\t\tcontinue;\n \t\tif (is_loose_object_file(de, name, sha1)) {\n-\t\t\tadd_sha1_list(sha1, DIRENT_SORT_HINT(de));\n+\t\t\tif (fsck_sha1(sha1))\n+\t\t\t\terrors_found |= ERROR_OBJECT;\n \t\t\tcontinue;\n \t\t}\n \t\tif (starts_with(de->d_name, \"tmp_obj_\"))\n@@ -573,7 +508,6 @@ static void fsck_object_dir(const char *path)\n \t\tdisplay_progress(progress, i+1);\n \t}\n \tstop_progress(&progress);\n-\tfsck_sha1_list();\n }\n \n static int fsck_head_link(void)\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270703","messageId":"20150924210830.GJ30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 65/68] Makefile: drop D_INO_IN_DIRENT build knob","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:30Z","receivedAt":"2015-09-24T21:08:30Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Now that fsck has dropped its inode-sorting, there are no\nlonger any users of this knob, and it can go away.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Makefile         | 5 -----\n config.mak.uname | 3 ---\n configure.ac     | 7 -------\n 3 files changed, 15 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 8d5df7e..2f350ca 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -74,8 +74,6 @@ all::\n # Define HAVE_PATHS_H if you have paths.h and want to use the default PATH\n # it specifies.\n #\n-# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.\n-#\n # Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks\n # d_type in struct dirent (Cygwin 1.5, fixed in Cygwin 1.7).\n #\n@@ -1160,9 +1158,6 @@ endif\n ifdef NO_D_TYPE_IN_DIRENT\n \tBASIC_CFLAGS += -DNO_D_TYPE_IN_DIRENT\n endif\n-ifdef NO_D_INO_IN_DIRENT\n-\tBASIC_CFLAGS += -DNO_D_INO_IN_DIRENT\n-endif\n ifdef NO_GECOS_IN_PWENT\n \tBASIC_CFLAGS += -DNO_GECOS_IN_PWENT\n endif\ndiff --git a/config.mak.uname b/config.mak.uname\nindex 943c439..f34dcaa 100644\n--- a/config.mak.uname\n+++ b/config.mak.uname\n@@ -166,7 +166,6 @@ endif\n ifeq ($(uname_O),Cygwin)\n \tifeq ($(shell expr \"$(uname_R)\" : '1\\.[1-6]\\.'),4)\n \t\tNO_D_TYPE_IN_DIRENT = YesPlease\n-\t\tNO_D_INO_IN_DIRENT = YesPlease\n \t\tNO_STRCASESTR = YesPlease\n \t\tNO_MEMMEM = YesPlease\n \t\tNO_MKSTEMPS = YesPlease\n@@ -370,7 +369,6 @@ ifeq ($(uname_S),Windows)\n \tNO_POSIX_GOODIES = UnfortunatelyYes\n \tNATIVE_CRLF = YesPlease\n \tDEFAULT_HELP_FORMAT = html\n-\tNO_D_INO_IN_DIRENT = YesPlease\n \n \tCC = compat/vcbuild/scripts/clink.pl\n \tAR = compat/vcbuild/scripts/lib.pl\n@@ -520,7 +518,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))\n \tNO_INET_NTOP = YesPlease\n \tNO_POSIX_GOODIES = UnfortunatelyYes\n \tDEFAULT_HELP_FORMAT = html\n-\tNO_D_INO_IN_DIRENT = YesPlease\n \tCOMPAT_CFLAGS += -D__USE_MINGW_ACCESS -D_USE_32BIT_TIME_T -DNOGDI -Icompat -Icompat/win32\n \tCOMPAT_CFLAGS += -DSTRIP_EXTENSION=\\\".exe\\\"\n \tCOMPAT_OBJS += compat/mingw.o compat/winansi.o \\\ndiff --git a/configure.ac b/configure.ac\nindex 14012fa..3fcca61 100644\n--- a/configure.ac\n+++ b/configure.ac\n@@ -767,13 +767,6 @@ elif test x$ac_cv_member_struct_stat_st_mtim_tv_nsec != xyes; then\n \tGIT_CONF_SUBST([NO_NSEC])\n fi\n #\n-# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.\n-AC_CHECK_MEMBER(struct dirent.d_ino,\n-[NO_D_INO_IN_DIRENT=],\n-[NO_D_INO_IN_DIRENT=YesPlease],\n-[#include <dirent.h>])\n-GIT_CONF_SUBST([NO_D_INO_IN_DIRENT])\n-#\n # Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks\n # d_type in struct dirent (latest Cygwin -- will be fixed soonish).\n AC_CHECK_MEMBER(struct dirent.d_type,\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270702","messageId":"20150924210832.GK30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 66/68] fsck: use for_each_loose_file_in_objdir","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:33Z","receivedAt":"2015-09-24T21:08:33Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Since 27e1e22 (prune: factor out loose-object directory\ntraversal, 2014-10-15), we now have a generic callback\nsystem for iterating over the loose object directories. This\nis used by prune, count-objects, etc.\n\nWe did not convert git-fsck at the time because it\nimplemented an inode-sorting scheme that was not part of the\ngeneric code. Now that the inode-sorting code is gone, we\ncan reuse the generic code.  The result is shorter,\nhopefully more readable, and drops some unchecked sprintf\ncalls.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/fsck.c | 69 ++++++++++++++++++++--------------------------------------\n 1 file changed, 23 insertions(+), 46 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 73c3596..2fe6a31 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -365,45 +365,6 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,\n \treturn fsck_obj(obj);\n }\n \n-static inline int is_loose_object_file(struct dirent *de,\n-\t\t\t\t       char *name, unsigned char *sha1)\n-{\n-\tif (strlen(de->d_name) != 38)\n-\t\treturn 0;\n-\tmemcpy(name + 2, de->d_name, 39);\n-\treturn !get_sha1_hex(name, sha1);\n-}\n-\n-static void fsck_dir(int i, char *path)\n-{\n-\tDIR *dir = opendir(path);\n-\tstruct dirent *de;\n-\tchar name[100];\n-\n-\tif (!dir)\n-\t\treturn;\n-\n-\tif (verbose)\n-\t\tfprintf(stderr, \"Checking directory %s\\n\", path);\n-\n-\tsprintf(name, \"%02x\", i);\n-\twhile ((de = readdir(dir)) != NULL) {\n-\t\tunsigned char sha1[20];\n-\n-\t\tif (is_dot_or_dotdot(de->d_name))\n-\t\t\tcontinue;\n-\t\tif (is_loose_object_file(de, name, sha1)) {\n-\t\t\tif (fsck_sha1(sha1))\n-\t\t\t\terrors_found |= ERROR_OBJECT;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (starts_with(de->d_name, \"tmp_obj_\"))\n-\t\t\tcontinue;\n-\t\tfprintf(stderr, \"bad sha1 file: %s/%s\\n\", path, de->d_name);\n-\t}\n-\tclosedir(dir);\n-}\n-\n static int default_refs;\n \n static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1)\n@@ -491,9 +452,28 @@ static void get_default_heads(void)\n \t}\n }\n \n+static int fsck_loose(const unsigned char *sha1, const char *path, void *data)\n+{\n+\tif (fsck_sha1(sha1))\n+\t\terrors_found |= ERROR_OBJECT;\n+\treturn 0;\n+}\n+\n+static int fsck_cruft(const char *basename, const char *path, void *data)\n+{\n+\tif (!starts_with(basename, \"tmp_obj_\"))\n+\t\tfprintf(stderr, \"bad sha1 file: %s\\n\", path);\n+\treturn 0;\n+}\n+\n+static int fsck_subdir(int nr, const char *path, void *progress)\n+{\n+\tdisplay_progress(progress, nr + 1);\n+\treturn 0;\n+}\n+\n static void fsck_object_dir(const char *path)\n {\n-\tint i;\n \tstruct progress *progress = NULL;\n \n \tif (verbose)\n@@ -501,12 +481,9 @@ static void fsck_object_dir(const char *path)\n \n \tif (show_progress)\n \t\tprogress = start_progress(_(\"Checking object directories\"), 256);\n-\tfor (i = 0; i < 256; i++) {\n-\t\tstatic char dir[4096];\n-\t\tsprintf(dir, \"%s/%02x\", path, i);\n-\t\tfsck_dir(i, dir);\n-\t\tdisplay_progress(progress, i+1);\n-\t}\n+\n+\tfor_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir,\n+\t\t\t\t      progress);\n \tstop_progress(&progress);\n }\n \n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270699","messageId":"20150924210835.GL30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 67/68] use strbuf_complete to conditionally append slash","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:35Z","receivedAt":"2015-09-24T21:08:35Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When working with paths in strbufs, we frequently want to\nensure that a directory contains a trailing slash before\nappending to it. We can shorten this code (and make the\nintent more obvious) by calling strbuf_complete.\n\nMost of these cases are trivially identical conversions, but\nthere are two things to note:\n\n  - in a few cases we did not check that the strbuf is\n    non-empty (which would lead to an out-of-bounds memory\n    access). These were generally not triggerable in\n    practice, either from earlier assertions, or typically\n    because we would have just fed the strbuf to opendir(),\n    which would choke on an empty path.\n\n  - in a few cases we indexed the buffer with \"original_len\"\n    or similar, rather than the current sb->len, and it is\n    not immediately obvious from the diff that they are the\n    same. In all of these cases, I manually verified that\n    the strbuf does not change between the assignment and\n    the strbuf_complete call.\n\nThis does not convert cases which look like:\n\n  if (sb->len && !is_dir_sep(sb->buf[sb->len - 1]))\n\t  strbuf_addch(sb, '/');\n\nas those are obviously semantically different. Some of these\ncases arguably should be doing that, but that is out of\nscope for this change, which aims purely for cleanup with no\nbehavior change (and at least it will make such sites easier\nto find and examine in the future, as we can grep for\nstrbuf_complete).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/clean.c | 6 ++----\n builtin/log.c   | 3 +--\n diff-no-index.c | 6 ++----\n dir.c           | 6 ++----\n path.c          | 3 +--\n refs.c          | 3 +--\n url.c           | 3 +--\n 7 files changed, 10 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/clean.c b/builtin/clean.c\nindex df53def..d7acb94 100644\n--- a/builtin/clean.c\n+++ b/builtin/clean.c\n@@ -159,8 +159,7 @@ static int is_git_repository(struct strbuf *path)\n \tint gitfile_error;\n \tsize_t orig_path_len = path->len;\n \tassert(orig_path_len != 0);\n-\tif (path->buf[orig_path_len - 1] != '/')\n-\t\tstrbuf_addch(path, '/');\n+\tstrbuf_complete(path, '/');\n \tstrbuf_addstr(path, \".git\");\n \tif (read_gitfile_gently(path->buf, &gitfile_error) || is_git_directory(path->buf))\n \t\tret = 1;\n@@ -206,8 +205,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,\n \t\treturn res;\n \t}\n \n-\tif (path->buf[original_len - 1] != '/')\n-\t\tstrbuf_addch(path, '/');\n+\tstrbuf_complete(path, '/');\n \n \tlen = path->len;\n \twhile ((e = readdir(dir)) != NULL) {\ndiff --git a/builtin/log.c b/builtin/log.c\nindex a491d3d..dda671d 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -796,8 +796,7 @@ static int reopen_stdout(struct commit *commit, const char *subject,\n \t\tif (filename.len >=\n \t\t    PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)\n \t\t\treturn error(_(\"name of output directory is too long\"));\n-\t\tif (filename.buf[filename.len - 1] != '/')\n-\t\t\tstrbuf_addch(&filename, '/');\n+\t\tstrbuf_complete(&filename, '/');\n \t}\n \n \tif (rev->numbered_files)\ndiff --git a/diff-no-index.c b/diff-no-index.c\nindex 0320605..8e0fd27 100644\n--- a/diff-no-index.c\n+++ b/diff-no-index.c\n@@ -136,15 +136,13 @@ static int queue_diff(struct diff_options *o,\n \n \t\tif (name1) {\n \t\t\tstrbuf_addstr(&buffer1, name1);\n-\t\t\tif (buffer1.len && buffer1.buf[buffer1.len - 1] != '/')\n-\t\t\t\tstrbuf_addch(&buffer1, '/');\n+\t\t\tstrbuf_complete(&buffer1, '/');\n \t\t\tlen1 = buffer1.len;\n \t\t}\n \n \t\tif (name2) {\n \t\t\tstrbuf_addstr(&buffer2, name2);\n-\t\t\tif (buffer2.len && buffer2.buf[buffer2.len - 1] != '/')\n-\t\t\t\tstrbuf_addch(&buffer2, '/');\n+\t\t\tstrbuf_complete(&buffer2, '/');\n \t\t\tlen2 = buffer2.len;\n \t\t}\n \ndiff --git a/dir.c b/dir.c\nindex 7b25634..79fdad8 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1519,8 +1519,7 @@ static enum path_treatment treat_path_fast(struct dir_struct *dir,\n \t}\n \tstrbuf_addstr(path, cdir->ucd->name);\n \t/* treat_one_path() does this before it calls treat_directory() */\n-\tif (path->buf[path->len - 1] != '/')\n-\t\tstrbuf_addch(path, '/');\n+\tstrbuf_complete(path, '/');\n \tif (cdir->ucd->check_only)\n \t\t/*\n \t\t * check_only is set as a result of treat_directory() getting\n@@ -2126,8 +2125,7 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)\n \t\telse\n \t\t\treturn -1;\n \t}\n-\tif (path->buf[original_len - 1] != '/')\n-\t\tstrbuf_addch(path, '/');\n+\tstrbuf_complete(path, '/');\n \n \tlen = path->len;\n \twhile ((e = readdir(dir)) != NULL) {\ndiff --git a/path.c b/path.c\nindex c597473..c105a9e 100644\n--- a/path.c\n+++ b/path.c\n@@ -240,8 +240,7 @@ static void do_submodule_path(struct strbuf *buf, const char *path,\n \tconst char *git_dir;\n \n \tstrbuf_addstr(buf, path);\n-\tif (buf->len && buf->buf[buf->len - 1] != '/')\n-\t\tstrbuf_addch(buf, '/');\n+\tstrbuf_complete(buf, '/');\n \tstrbuf_addstr(buf, \".git\");\n \n \tgit_dir = read_gitfile(buf->buf);\ndiff --git a/refs.c b/refs.c\nindex 9937a40..b2a9229 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2193,8 +2193,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,\n \n \tif (!has_glob_specials(pattern)) {\n \t\t/* Append implied '/' '*' if not present. */\n-\t\tif (real_pattern.buf[real_pattern.len - 1] != '/')\n-\t\t\tstrbuf_addch(&real_pattern, '/');\n+\t\tstrbuf_complete(&real_pattern, '/');\n \t\t/* No need to check for '*', there is none. */\n \t\tstrbuf_addch(&real_pattern, '*');\n \t}\ndiff --git a/url.c b/url.c\nindex 7ca2a69..2d89ad1 100644\n--- a/url.c\n+++ b/url.c\n@@ -120,8 +120,7 @@ char *url_decode_parameter_value(const char **query)\n void end_url_with_slash(struct strbuf *buf, const char *url)\n {\n \tstrbuf_addstr(buf, url);\n-\tif (buf->len && buf->buf[buf->len - 1] != '/')\n-\t\tstrbuf_addch(buf, '/');\n+\tstrbuf_complete(buf, '/');\n }\n \n void str_end_url_with_slash(const char *url, char **dest) {\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270698","messageId":"20150924210837.GM30946@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210225.GA23624@sigill.intra.peff.net","subject":"[PATCH 68/68] name-rev: use strip_suffix to avoid magic numbers","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-24T21:08:37Z","receivedAt":"2015-09-24T21:08:37Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The manual size computations here are correct, but using\nstrip_suffix makes that obvious, and hopefully communicates\nthe intent of the code more clearly.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/name-rev.c | 9 ++++-----\n 1 file changed, 4 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 8a3a0cd..0377fc1 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -55,16 +55,15 @@ copy_data:\n \t\t\tparents;\n \t\t\tparents = parents->next, parent_number++) {\n \t\tif (parent_number > 1) {\n-\t\t\tint len = strlen(tip_name);\n+\t\t\tsize_t len;\n \t\t\tchar *new_name;\n \n-\t\t\tif (len > 2 && !strcmp(tip_name + len - 2, \"^0\"))\n-\t\t\t\tlen -= 2;\n+\t\t\tstrip_suffix(tip_name, \"^0\", &len);\n \t\t\tif (generation > 0)\n-\t\t\t\tnew_name = xstrfmt(\"%.*s~%d^%d\", len, tip_name,\n+\t\t\t\tnew_name = xstrfmt(\"%.*s~%d^%d\", (int)len, tip_name,\n \t\t\t\t\t\t   generation, parent_number);\n \t\t\telse\n-\t\t\t\tnew_name = xstrfmt(\"%.*s^%d\", len, tip_name,\n+\t\t\t\tnew_name = xstrfmt(\"%.*s^%d\", (int)len, tip_name,\n \t\t\t\t\t\t   parent_number);\n \n \t\t\tname_rev(parents->item, new_name, 0,\n-- \n2.6.0.rc3.454.g204ad51\n"},{"id":"270726","messageId":"CAPig+cQSOoYwG7EKDWPWoB+vHvYWv6ZHg9Zhd8bvH6XKfmG_1w@mail.gmail.com","threadId":"40424","inReplyTo":"20150924210805.GY30946@sigill.intra.peff.net","subject":"Re: [PATCH 54/68] drop strcpy in favor of raw sha1_to_hex","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-09-24T23:42:05Z","receivedAt":"2015-09-24T23:42:05Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Sep 24, 2015 at 5:08 PM, Jeff King <peff@peff.net> wrote:\n> In some cases where we strcpy() the result of sha1_to_hex(),\n> there's no need; the result goes directly into a printf\n> statement, and we can simply pass the return value from\n> sha1_to_hex() directly.\n>\n> When this code was originally written, sha1_to_hex used a\n> single buffer, and it was not safe to use it twice within a\n> single expression. That changed as of dcb3450 (sha1_to_hex()\n> usage cleanup, 2006-05-03), but this code ewas never\n\ns/ewas/was/\n\n> updated.\n>\n> History-dug-by: Eric Sunshine <sunshine@sunshineco.com>\n> Signed-off-by: Jeff King <peff@peff.net>\n"},{"id":"270736","messageId":"20150925013635.GB7522@sigill.intra.peff.net","threadId":"40424","inReplyTo":"CAPig+cQSOoYwG7EKDWPWoB+vHvYWv6ZHg9Zhd8bvH6XKfmG_1w@mail.gmail.com","subject":"Re: [PATCH 54/68] drop strcpy in favor of raw sha1_to_hex","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-25T01:36:35Z","receivedAt":"2015-09-25T01:36:35Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Sep 24, 2015 at 07:42:05PM -0400, Eric Sunshine wrote:\n\n> On Thu, Sep 24, 2015 at 5:08 PM, Jeff King <peff@peff.net> wrote:\n> > In some cases where we strcpy() the result of sha1_to_hex(),\n> > there's no need; the result goes directly into a printf\n> > statement, and we can simply pass the return value from\n> > sha1_to_hex() directly.\n> >\n> > When this code was originally written, sha1_to_hex used a\n> > single buffer, and it was not safe to use it twice within a\n> > single expression. That changed as of dcb3450 (sha1_to_hex()\n> > usage cleanup, 2006-05-03), but this code ewas never\n> \n> s/ewas/was/\n\nWhoops. New content, new errors. :)\n\n-Peff\n"},{"id":"270774","messageId":"20150926033636.GA18753@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20150924210832.GK30946@sigill.intra.peff.net","subject":"Re: [PATCH 66/68] fsck: use for_each_loose_file_in_objdir","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-26T03:36:36Z","receivedAt":"2015-09-26T03:36:36Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Sep 24, 2015 at 05:08:32PM -0400, Jeff King wrote:\n\n> +static int fsck_subdir(int nr, const char *path, void *progress)\n> +{\n> +\tdisplay_progress(progress, nr + 1);\n> +\treturn 0;\n> +}\n> +\n>  static void fsck_object_dir(const char *path)\n>  {\n> -\tint i;\n>  \tstruct progress *progress = NULL;\n>  \n>  \tif (verbose)\n> @@ -501,12 +481,9 @@ static void fsck_object_dir(const char *path)\n>  \n>  \tif (show_progress)\n>  \t\tprogress = start_progress(_(\"Checking object directories\"), 256);\n> -\tfor (i = 0; i < 256; i++) {\n> -\t\tstatic char dir[4096];\n> -\t\tsprintf(dir, \"%s/%02x\", path, i);\n> -\t\tfsck_dir(i, dir);\n> -\t\tdisplay_progress(progress, i+1);\n> -\t}\n> +\n> +\tfor_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir,\n> +\t\t\t\t      progress);\n>  \tstop_progress(&progress);\n\nI happened to be running git-fsck today and noticed that it finished\nwith the progress bar still reading 94%. The problem is that we update\nthe progress when we finish a subdir, but of course we do not\nnecessarily have all 256 subdirs, and the for_each_loose code only\ntriggers our callback for ones that exist.\n\nSo we need this on top:\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 2fe6a31..d50efd5 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -484,6 +484,7 @@ static void fsck_object_dir(const char *path)\n \n \tfor_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir,\n \t\t\t\t      progress);\n+\tdisplay_progress(progress, 256);\n \tstop_progress(&progress);\n }\n \n\nto make things pretty.\n\n-Peff\n"},{"id":"270789","messageId":"5607D0A9.4000506@web.de","threadId":"40424","inReplyTo":"20150924210818.GE30946@sigill.intra.peff.net","subject":"Re: [PATCH 60/68] prefer memcpy to strcpy","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2015-09-27T11:19:05Z","receivedAt":"2015-09-27T11:19:05Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 24.09.2015 um 23:08 schrieb Jeff King:\n> When we already know the length of a string (e.g., because\n> we just malloc'd to fit it), it's nicer to use memcpy than\n> strcpy, as it makes it more obvious that we are not going to\n> overflow the buffer (because the size we pass matches the\n> size in the allocation).\n>\n> This also eliminates calls to strcpy, which make auditing\n> the code base harder.\n>\n> Signed-off-by: Jeff King <peff@peff.net>\n> ---\n>   compat/nedmalloc/nedmalloc.c | 5 +++--\n>   fast-import.c                | 5 +++--\n>   revision.c                   | 2 +-\n>   3 files changed, 7 insertions(+), 5 deletions(-)\n>\n> diff --git a/compat/nedmalloc/nedmalloc.c b/compat/nedmalloc/nedmalloc.c\n> index 609ebba..a0a16eb 100644\n> --- a/compat/nedmalloc/nedmalloc.c\n> +++ b/compat/nedmalloc/nedmalloc.c\n> @@ -957,8 +957,9 @@ char *strdup(const char *s1)\n>   {\n>   \tchar *s2 = 0;\n>   \tif (s1) {\n> -\t\ts2 = malloc(strlen(s1) + 1);\n> -\t\tstrcpy(s2, s1);\n> +\t\tsize_t len = strlen(s1) + 1;\n> +\t\ts2 = malloc(len);\n> +\t\tmemcpy(s2, s1, len);\n\nThis leaves the last byte uninitialized; it was set to NUL by strcpy() \nbefore.\n\n>   \t}\n>   \treturn s2;\n>   }\n> diff --git a/fast-import.c b/fast-import.c\n> index 895c6b4..cf6d8bc 100644\n> --- a/fast-import.c\n> +++ b/fast-import.c\n> @@ -644,8 +644,9 @@ static void *pool_calloc(size_t count, size_t size)\n>\n>   static char *pool_strdup(const char *s)\n>   {\n> -\tchar *r = pool_alloc(strlen(s) + 1);\n> -\tstrcpy(r, s);\n> +\tsize_t len = strlen(s) + 1;\n> +\tchar *r = pool_alloc(len);\n> +\tmemcpy(r, s, len);\n\nSame here.\n\n>   \treturn r;\n>   }\n>\n> diff --git a/revision.c b/revision.c\n> index af2a18e..2236463 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -38,7 +38,7 @@ char *path_name(const struct name_path *path, const char *name)\n>   \t}\n>   \tn = xmalloc(len);\n>   \tm = n + len - (nlen + 1);\n> -\tstrcpy(m, name);\n> +\tmemcpy(m, name, nlen + 1);\n\nThis copies the NUL byte terminating the string, so it's OK.  However, I \nwonder if using a strbuf for building the path in one go instead would \nsimplify this function without too much of a performance impact.\n\n>   \tfor (p = path; p; p = p->up) {\n>   \t\tif (p->elem_len) {\n>   \t\t\tm -= p->elem_len + 1;\n>\n"},{"id":"270791","messageId":"5607E9E4.4010209@web.de","threadId":"40424","inReplyTo":"5607D0A9.4000506@web.de","subject":"Re: [PATCH 60/68] prefer memcpy to strcpy","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2015-09-27T13:06:44Z","receivedAt":"2015-09-27T13:06:44Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On 2015-09-27 13.19, René Scharfe wrote:\n> Am 24.09.2015 um 23:08 schrieb Jeff King:\n>> When we already know the length of a string (e.g., because\n>> we just malloc'd to fit it), it's nicer to use memcpy than\n>> strcpy, as it makes it more obvious that we are not going to\n>> overflow the buffer (because the size we pass matches the\n>> size in the allocation).\n>>\n>> This also eliminates calls to strcpy, which make auditing\n>> the code base harder.\n>>\n>> Signed-off-by: Jeff King <peff@peff.net>\n>> ---\n>>   compat/nedmalloc/nedmalloc.c | 5 +++--\n>>   fast-import.c                | 5 +++--\n>>   revision.c                   | 2 +-\n>>   3 files changed, 7 insertions(+), 5 deletions(-)\n>>\n>> diff --git a/compat/nedmalloc/nedmalloc.c b/compat/nedmalloc/nedmalloc.c\n>> index 609ebba..a0a16eb 100644\n>> --- a/compat/nedmalloc/nedmalloc.c\n>> +++ b/compat/nedmalloc/nedmalloc.c\n>> @@ -957,8 +957,9 @@ char *strdup(const char *s1)\n>>   {\n>>       char *s2 = 0;\n>>       if (s1) {\n>> -        s2 = malloc(strlen(s1) + 1);\n>> -        strcpy(s2, s1);\n>> +        size_t len = strlen(s1) + 1;\n>> +        s2 = malloc(len);\n>> +        memcpy(s2, s1, len);\n> \n> This leaves the last byte uninitialized; it was set to NUL by strcpy() before.\n\nlen is == strlen() +1, which should cover the NUL:\n\n1 byte extra for NUL is allocated,\nand memcpy will copy NUL from source.\n(Or do I miss somethong ?)\n"},{"id":"270792","messageId":"5607EB5D.2050706@web.de","threadId":"40424","inReplyTo":"5607E9E4.4010209@web.de","subject":"Re: [PATCH 60/68] prefer memcpy to strcpy","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2015-09-27T13:13:01Z","receivedAt":"2015-09-27T13:13:01Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 27.09.2015 um 15:06 schrieb Torsten Bögershausen:\n> On 2015-09-27 13.19, René Scharfe wrote:\n>> Am 24.09.2015 um 23:08 schrieb Jeff King:\n>>> When we already know the length of a string (e.g., because\n>>> we just malloc'd to fit it), it's nicer to use memcpy than\n>>> strcpy, as it makes it more obvious that we are not going to\n>>> overflow the buffer (because the size we pass matches the\n>>> size in the allocation).\n>>>\n>>> This also eliminates calls to strcpy, which make auditing\n>>> the code base harder.\n>>>\n>>> Signed-off-by: Jeff King <peff@peff.net>\n>>> ---\n>>>    compat/nedmalloc/nedmalloc.c | 5 +++--\n>>>    fast-import.c                | 5 +++--\n>>>    revision.c                   | 2 +-\n>>>    3 files changed, 7 insertions(+), 5 deletions(-)\n>>>\n>>> diff --git a/compat/nedmalloc/nedmalloc.c b/compat/nedmalloc/nedmalloc.c\n>>> index 609ebba..a0a16eb 100644\n>>> --- a/compat/nedmalloc/nedmalloc.c\n>>> +++ b/compat/nedmalloc/nedmalloc.c\n>>> @@ -957,8 +957,9 @@ char *strdup(const char *s1)\n>>>    {\n>>>        char *s2 = 0;\n>>>        if (s1) {\n>>> -        s2 = malloc(strlen(s1) + 1);\n>>> -        strcpy(s2, s1);\n>>> +        size_t len = strlen(s1) + 1;\n>>> +        s2 = malloc(len);\n>>> +        memcpy(s2, s1, len);\n>>\n>> This leaves the last byte uninitialized; it was set to NUL by strcpy() before.\n>\n> len is == strlen() +1, which should cover the NUL:\n>\n> 1 byte extra for NUL is allocated,\n> and memcpy will copy NUL from source.\n> (Or do I miss somethong ?)\n\nNo, you're right.  Sorry for the noise.\n\nI fully blame this on lack of coffeine because my electric kettle just \nbroke. O_o\n\nRené\n"},{"id":"270793","messageId":"5607EE1E.4050304@web.de","threadId":"40424","inReplyTo":"5607EB5D.2050706@web.de","subject":"Re: [PATCH 60/68] prefer memcpy to strcpy","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2015-09-27T13:24:46Z","receivedAt":"2015-09-27T13:24:46Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 27.09.2015 um 15:13 schrieb René Scharfe:\n> Am 27.09.2015 um 15:06 schrieb Torsten Bögershausen:\n>> On 2015-09-27 13.19, René Scharfe wrote:\n>>> Am 24.09.2015 um 23:08 schrieb Jeff King:\n>>>> When we already know the length of a string (e.g., because\n>>>> we just malloc'd to fit it), it's nicer to use memcpy than\n>>>> strcpy, as it makes it more obvious that we are not going to\n>>>> overflow the buffer (because the size we pass matches the\n>>>> size in the allocation).\n>>>>\n>>>> This also eliminates calls to strcpy, which make auditing\n>>>> the code base harder.\n>>>>\n>>>> Signed-off-by: Jeff King <peff@peff.net>\n>>>> ---\n>>>>    compat/nedmalloc/nedmalloc.c | 5 +++--\n>>>>    fast-import.c                | 5 +++--\n>>>>    revision.c                   | 2 +-\n>>>>    3 files changed, 7 insertions(+), 5 deletions(-)\n>>>>\n>>>> diff --git a/compat/nedmalloc/nedmalloc.c\n>>>> b/compat/nedmalloc/nedmalloc.c\n>>>> index 609ebba..a0a16eb 100644\n>>>> --- a/compat/nedmalloc/nedmalloc.c\n>>>> +++ b/compat/nedmalloc/nedmalloc.c\n>>>> @@ -957,8 +957,9 @@ char *strdup(const char *s1)\n>>>>    {\n>>>>        char *s2 = 0;\n>>>>        if (s1) {\n>>>> -        s2 = malloc(strlen(s1) + 1);\n>>>> -        strcpy(s2, s1);\n>>>> +        size_t len = strlen(s1) + 1;\n>>>> +        s2 = malloc(len);\n>>>> +        memcpy(s2, s1, len);\n>>>\n>>> This leaves the last byte uninitialized; it was set to NUL by\n>>> strcpy() before.\n>>\n>> len is == strlen() +1, which should cover the NUL:\n>>\n>> 1 byte extra for NUL is allocated,\n>> and memcpy will copy NUL from source.\n>> (Or do I miss somethong ?)\n>\n> No, you're right.  Sorry for the noise.\n>\n> I fully blame this on lack of coffeine because my electric kettle just\n> broke. O_o\n\nThinking a bit more about it (slowly): The choice of the variable name \nmight have been a factor as well.  When I see \"len\" for a string then I \ndon't expect it to include the trailing NUL.  \"size\" would be better \nbecause I expect it to contain the number of bytes needed to store an \nobject.\n\nRené\n"},{"id":"270800","messageId":"87fv1zf29x.fsf@rasmusvillemoes.dk","threadId":"40424","inReplyTo":"20150924210818.GE30946@sigill.intra.peff.net","subject":"Re: [PATCH 60/68] prefer memcpy to strcpy","fromName":"Rasmus Villemoes","fromEmail":"rv@rasmusvillemoes.dk","sentAt":"2015-09-28T07:09:46Z","receivedAt":"2015-09-28T07:09:46Z","isPatch":true,"sender":{"key":"rv@rasmusvillemoes.dk","avatar":"https://avatars.githubusercontent.com/u/4375908?v=4"},"body":"On Thu, Sep 24 2015, Jeff King <peff@peff.net> wrote:\n\n> This also eliminates calls to strcpy, which make auditing\n> the code base harder.\n\nMaybe may English parser is broken, but this doesn't immediately sound\nlike what you meant to say. Also, in 29/68 you say \"We drop calls to\nstrcpy, which makes auditing the code base easier.\" Maybe it's all ok,\nsince on second reading the first \"make\" probably refers to the plural\n\"calls for strcpy\", while in the second case \"makes\" refers to \"[the\ndropping of] calls to strcpy\".\n\nRasmus\n"},{"id":"270923","messageId":"CAO2U3QjunOPoAbGSRjAmCwfk-TnoMveXOJhpb351eh1a_3Xp3A@mail.gmail.com","threadId":"40424","inReplyTo":"20150924210736.GL30946@sigill.intra.peff.net","subject":"Re: [PATCH 41/68] init: use strbufs to store paths","fromName":"Michael Blume","fromEmail":"blume.mike@gmail.com","sentAt":"2015-09-29T23:50:39Z","receivedAt":"2015-09-29T23:50:39Z","isPatch":true,"sender":{"key":"blume.mike@gmail.com","avatar":"https://gravatar.com/avatar/1a7b440e1d942425ff4098ac7fc15b86b30cecaa56e1692a7ef8b5939ba25ea7?d=mp&s=160"},"body":"On Thu, Sep 24, 2015 at 2:07 PM, Jeff King <peff@peff.net> wrote:\n> The init code predates strbufs, and uses PATH_MAX-sized\n> buffers along with many manual checks on intermediate sizes\n> (some of which make magic assumptions, such as that init\n> will not create a path inside .git longer than 50\n> characters).\n>\n> We can simplify this greatly by using strbufs, which drops\n> some hard-to-verify strcpy calls.  Note that we need to\n> update probe_utf8_pathname_composition, too, as it assumes\n> we are passing a buffer large enough to append its probe\n> filenames (it now just takes a strbuf, which also gets rid\n> of the confusing \"len\" parameter, which was not the length of\n> \"path\" but rather the offset to start writing).\n>\n> Some of the conversion makes new calls to git_path_buf.\n> While we're in the area, let's also convert existing calls\n> to git_path to the safer git_path_buf (our existing calls\n> were passed to pretty tame functions, and so were not a\n> problem, but it's easy to be consistent and safe here).\n>\n> Note that we had an explicit test that \"git init\" rejects\n> long template directories. This comes from 32d1776 (init: Do\n> not segfault on big GIT_TEMPLATE_DIR environment variable,\n> 2009-04-18). We can drop the test_must_fail here, as we now\n> accept this and need only confirm that we don't segfault,\n> which was the original point of the test.\n>\n> Signed-off-by: Jeff King <peff@peff.net>\n> ---\n>  builtin/init-db.c        | 174 ++++++++++++++++++++---------------------------\n>  compat/precompose_utf8.c |  12 ++--\n>  compat/precompose_utf8.h |   2 +-\n>  git-compat-util.h        |   2 +-\n>  t/t0001-init.sh          |   4 +-\n>  5 files changed, 87 insertions(+), 107 deletions(-)\n>\n> diff --git a/builtin/init-db.c b/builtin/init-db.c\n> index e7d0e31..cf6a3c8 100644\n> --- a/builtin/init-db.c\n> +++ b/builtin/init-db.c\n> @@ -36,10 +36,11 @@ static void safe_create_dir(const char *dir, int share)\n>                 die(_(\"Could not make %s writable by group\"), dir);\n>  }\n>\n> -static void copy_templates_1(char *path, int baselen,\n> -                            char *template, int template_baselen,\n> +static void copy_templates_1(struct strbuf *path, struct strbuf *template,\n>                              DIR *dir)\n>  {\n> +       size_t path_baselen = path->len;\n> +       size_t template_baselen = template->len;\n>         struct dirent *de;\n>\n>         /* Note: if \".git/hooks\" file exists in the repository being\n> @@ -49,77 +50,64 @@ static void copy_templates_1(char *path, int baselen,\n>          * with the way the namespace under .git/ is organized, should\n>          * be really carefully chosen.\n>          */\n> -       safe_create_dir(path, 1);\n> +       safe_create_dir(path->buf, 1);\n>         while ((de = readdir(dir)) != NULL) {\n>                 struct stat st_git, st_template;\n> -               int namelen;\n>                 int exists = 0;\n>\n> +               strbuf_setlen(path, path_baselen);\n> +               strbuf_setlen(template, template_baselen);\n> +\n>                 if (de->d_name[0] == '.')\n>                         continue;\n> -               namelen = strlen(de->d_name);\n> -               if ((PATH_MAX <= baselen + namelen) ||\n> -                   (PATH_MAX <= template_baselen + namelen))\n> -                       die(_(\"insanely long template name %s\"), de->d_name);\n> -               memcpy(path + baselen, de->d_name, namelen+1);\n> -               memcpy(template + template_baselen, de->d_name, namelen+1);\n> -               if (lstat(path, &st_git)) {\n> +               strbuf_addstr(path, de->d_name);\n> +               strbuf_addstr(template, de->d_name);\n> +               if (lstat(path->buf, &st_git)) {\n>                         if (errno != ENOENT)\n> -                               die_errno(_(\"cannot stat '%s'\"), path);\n> +                               die_errno(_(\"cannot stat '%s'\"), path->buf);\n>                 }\n>                 else\n>                         exists = 1;\n>\n> -               if (lstat(template, &st_template))\n> -                       die_errno(_(\"cannot stat template '%s'\"), template);\n> +               if (lstat(template->buf, &st_template))\n> +                       die_errno(_(\"cannot stat template '%s'\"), template->buf);\n>\n>                 if (S_ISDIR(st_template.st_mode)) {\n> -                       DIR *subdir = opendir(template);\n> -                       int baselen_sub = baselen + namelen;\n> -                       int template_baselen_sub = template_baselen + namelen;\n> +                       DIR *subdir = opendir(template->buf);\n>                         if (!subdir)\n> -                               die_errno(_(\"cannot opendir '%s'\"), template);\n> -                       path[baselen_sub++] =\n> -                               template[template_baselen_sub++] = '/';\n> -                       path[baselen_sub] =\n> -                               template[template_baselen_sub] = 0;\n> -                       copy_templates_1(path, baselen_sub,\n> -                                        template, template_baselen_sub,\n> -                                        subdir);\n> +                               die_errno(_(\"cannot opendir '%s'\"), template->buf);\n> +                       strbuf_addch(path, '/');\n> +                       strbuf_addch(template, '/');\n> +                       copy_templates_1(path, template, subdir);\n>                         closedir(subdir);\n>                 }\n>                 else if (exists)\n>                         continue;\n>                 else if (S_ISLNK(st_template.st_mode)) {\n> -                       char lnk[256];\n> -                       int len;\n> -                       len = readlink(template, lnk, sizeof(lnk));\n> -                       if (len < 0)\n> -                               die_errno(_(\"cannot readlink '%s'\"), template);\n> -                       if (sizeof(lnk) <= len)\n> -                               die(_(\"insanely long symlink %s\"), template);\n> -                       lnk[len] = 0;\n> -                       if (symlink(lnk, path))\n> -                               die_errno(_(\"cannot symlink '%s' '%s'\"), lnk, path);\n> +                       struct strbuf lnk = STRBUF_INIT;\n> +                       if (strbuf_readlink(&lnk, template->buf, 0) < 0)\n> +                               die_errno(_(\"cannot readlink '%s'\"), template->buf);\n> +                       if (symlink(lnk.buf, path->buf))\n> +                               die_errno(_(\"cannot symlink '%s' '%s'\"),\n> +                                         lnk.buf, path->buf);\n> +                       strbuf_release(&lnk);\n>                 }\n>                 else if (S_ISREG(st_template.st_mode)) {\n> -                       if (copy_file(path, template, st_template.st_mode))\n> -                               die_errno(_(\"cannot copy '%s' to '%s'\"), template,\n> -                                         path);\n> +                       if (copy_file(path->buf, template->buf, st_template.st_mode))\n> +                               die_errno(_(\"cannot copy '%s' to '%s'\"),\n> +                                         template->buf, path->buf);\n>                 }\n>                 else\n> -                       error(_(\"ignoring template %s\"), template);\n> +                       error(_(\"ignoring template %s\"), template->buf);\n>         }\n>  }\n>\n>  static void copy_templates(const char *template_dir)\n>  {\n> -       char path[PATH_MAX];\n> -       char template_path[PATH_MAX];\n> -       int template_len;\n> +       struct strbuf path = STRBUF_INIT;\n> +       struct strbuf template_path = STRBUF_INIT;\n> +       size_t template_len;\n>         DIR *dir;\n> -       const char *git_dir = get_git_dir();\n> -       int len = strlen(git_dir);\n>         char *to_free = NULL;\n>\n>         if (!template_dir)\n> @@ -132,26 +120,23 @@ static void copy_templates(const char *template_dir)\n>                 free(to_free);\n>                 return;\n>         }\n> -       template_len = strlen(template_dir);\n> -       if (PATH_MAX <= (template_len+strlen(\"/config\")))\n> -               die(_(\"insanely long template path %s\"), template_dir);\n> -       strcpy(template_path, template_dir);\n> -       if (template_path[template_len-1] != '/') {\n> -               template_path[template_len++] = '/';\n> -               template_path[template_len] = 0;\n> -       }\n> -       dir = opendir(template_path);\n> +\n> +       strbuf_addstr(&template_path, template_dir);\n> +       strbuf_complete(&template_path, '/');\n> +       template_len = template_path.len;\n> +\n> +       dir = opendir(template_path.buf);\n>         if (!dir) {\n>                 warning(_(\"templates not found %s\"), template_dir);\n>                 goto free_return;\n>         }\n>\n>         /* Make sure that template is from the correct vintage */\n> -       strcpy(template_path + template_len, \"config\");\n> +       strbuf_addstr(&template_path, \"config\");\n>         repository_format_version = 0;\n>         git_config_from_file(check_repository_format_version,\n> -                            template_path, NULL);\n> -       template_path[template_len] = 0;\n> +                            template_path.buf, NULL);\n> +       strbuf_setlen(&template_path, template_len);\n>\n>         if (repository_format_version &&\n>             repository_format_version != GIT_REPO_VERSION) {\n> @@ -162,17 +147,15 @@ static void copy_templates(const char *template_dir)\n>                 goto close_free_return;\n>         }\n>\n> -       memcpy(path, git_dir, len);\n> -       if (len && path[len - 1] != '/')\n> -               path[len++] = '/';\n> -       path[len] = 0;\n> -       copy_templates_1(path, len,\n> -                        template_path, template_len,\n> -                        dir);\n> +       strbuf_addstr(&path, get_git_dir());\n> +       strbuf_complete(&path, '/');\n> +       copy_templates_1(&path, &template_path, dir);\n>  close_free_return:\n>         closedir(dir);\n>  free_return:\n>         free(to_free);\n> +       strbuf_release(&path);\n> +       strbuf_release(&template_path);\n>  }\n>\n>  static int git_init_db_config(const char *k, const char *v, void *cb)\n> @@ -199,28 +182,20 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)\n>\n>  static int create_default_files(const char *template_path)\n>  {\n> -       const char *git_dir = get_git_dir();\n> -       unsigned len = strlen(git_dir);\n> -       static char path[PATH_MAX];\n>         struct stat st1;\n> +       struct strbuf buf = STRBUF_INIT;\n> +       char *path;\n>         char repo_version_string[10];\n>         char junk[2];\n>         int reinit;\n>         int filemode;\n>\n> -       if (len > sizeof(path)-50)\n> -               die(_(\"insane git directory %s\"), git_dir);\n> -       memcpy(path, git_dir, len);\n> -\n> -       if (len && path[len-1] != '/')\n> -               path[len++] = '/';\n> -\n>         /*\n>          * Create .git/refs/{heads,tags}\n>          */\n> -       safe_create_dir(git_path(\"refs\"), 1);\n> -       safe_create_dir(git_path(\"refs/heads\"), 1);\n> -       safe_create_dir(git_path(\"refs/tags\"), 1);\n> +       safe_create_dir(git_path_buf(&buf, \"refs\"), 1);\n> +       safe_create_dir(git_path_buf(&buf, \"refs/heads\"), 1);\n> +       safe_create_dir(git_path_buf(&buf, \"refs/tags\"), 1);\n>\n>         /* Just look for `init.templatedir` */\n>         git_config(git_init_db_config, NULL);\n> @@ -244,16 +219,16 @@ static int create_default_files(const char *template_path)\n>          */\n>         if (shared_repository) {\n>                 adjust_shared_perm(get_git_dir());\n> -               adjust_shared_perm(git_path(\"refs\"));\n> -               adjust_shared_perm(git_path(\"refs/heads\"));\n> -               adjust_shared_perm(git_path(\"refs/tags\"));\n> +               adjust_shared_perm(git_path_buf(&buf, \"refs\"));\n> +               adjust_shared_perm(git_path_buf(&buf, \"refs/heads\"));\n> +               adjust_shared_perm(git_path_buf(&buf, \"refs/tags\"));\n>         }\n>\n>         /*\n>          * Create the default symlink from \".git/HEAD\" to the \"master\"\n>          * branch, if it does not exist yet.\n>          */\n> -       strcpy(path + len, \"HEAD\");\n> +       path = git_path_buf(&buf, \"HEAD\");\n>         reinit = (!access(path, R_OK)\n>                   || readlink(path, junk, sizeof(junk)-1) != -1);\n>         if (!reinit) {\n> @@ -266,10 +241,8 @@ static int create_default_files(const char *template_path)\n>                   \"%d\", GIT_REPO_VERSION);\n>         git_config_set(\"core.repositoryformatversion\", repo_version_string);\n>\n> -       path[len] = 0;\n> -       strcpy(path + len, \"config\");\n> -\n>         /* Check filemode trustability */\n> +       path = git_path_buf(&buf, \"config\");\n>         filemode = TEST_FILEMODE;\n>         if (TEST_FILEMODE && !lstat(path, &st1)) {\n>                 struct stat st2;\n> @@ -290,14 +263,13 @@ static int create_default_files(const char *template_path)\n>                 /* allow template config file to override the default */\n>                 if (log_all_ref_updates == -1)\n>                     git_config_set(\"core.logallrefupdates\", \"true\");\n> -               if (needs_work_tree_config(git_dir, work_tree))\n> +               if (needs_work_tree_config(get_git_dir(), work_tree))\n>                         git_config_set(\"core.worktree\", work_tree);\n>         }\n>\n>         if (!reinit) {\n>                 /* Check if symlink is supported in the work tree */\n> -               path[len] = 0;\n> -               strcpy(path + len, \"tXXXXXX\");\n> +               path = git_path_buf(&buf, \"tXXXXXX\");\n>                 if (!close(xmkstemp(path)) &&\n>                     !unlink(path) &&\n>                     !symlink(\"testing\", path) &&\n> @@ -308,31 +280,35 @@ static int create_default_files(const char *template_path)\n>                         git_config_set(\"core.symlinks\", \"false\");\n>\n>                 /* Check if the filesystem is case-insensitive */\n> -               path[len] = 0;\n> -               strcpy(path + len, \"CoNfIg\");\n> +               path = git_path_buf(&buf, \"CoNfIg\");\n>                 if (!access(path, F_OK))\n>                         git_config_set(\"core.ignorecase\", \"true\");\n> -               probe_utf8_pathname_composition(path, len);\n> +               probe_utf8_pathname_composition(path);\n>         }\n>\n> +       strbuf_release(&buf);\n>         return reinit;\n>  }\n>\n>  static void create_object_directory(void)\n>  {\n> -       const char *object_directory = get_object_directory();\n> -       int len = strlen(object_directory);\n> -       char *path = xmalloc(len + 40);\n> +       struct strbuf path = STRBUF_INIT;\n> +       size_t baselen;\n> +\n> +       strbuf_addstr(&path, get_object_directory());\n> +       baselen = path.len;\n> +\n> +       safe_create_dir(path.buf, 1);\n>\n> -       memcpy(path, object_directory, len);\n> +       strbuf_setlen(&path, baselen);\n> +       strbuf_addstr(&path, \"/pack\");\n> +       safe_create_dir(path.buf, 1);\n>\n> -       safe_create_dir(object_directory, 1);\n> -       strcpy(path+len, \"/pack\");\n> -       safe_create_dir(path, 1);\n> -       strcpy(path+len, \"/info\");\n> -       safe_create_dir(path, 1);\n> +       strbuf_setlen(&path, baselen);\n> +       strbuf_addstr(&path, \"/info\");\n> +       safe_create_dir(path.buf, 1);\n>\n> -       free(path);\n> +       strbuf_release(&path);\n>  }\n>\n>  int set_git_dir_init(const char *git_dir, const char *real_git_dir,\n> diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\n> index 95fe849..b4dd3c7 100644\n> --- a/compat/precompose_utf8.c\n> +++ b/compat/precompose_utf8.c\n> @@ -36,24 +36,28 @@ static size_t has_non_ascii(const char *s, size_t maxlen, size_t *strlen_c)\n>  }\n>\n>\n> -void probe_utf8_pathname_composition(char *path, int len)\n> +void probe_utf8_pathname_composition(struct strbuf *path)\n>  {\n>         static const char *auml_nfc = \"\\xc3\\xa4\";\n>         static const char *auml_nfd = \"\\x61\\xcc\\x88\";\n> +       size_t baselen = path->len;\n>         int output_fd;\n>         if (precomposed_unicode != -1)\n>                 return; /* We found it defined in the global config, respect it */\n> -       strcpy(path + len, auml_nfc);\n> +       strbuf_addstr(path, auml_nfc);\n>         output_fd = open(path, O_CREAT|O_EXCL|O_RDWR, 0600);\n>         if (output_fd >= 0) {\n>                 close(output_fd);\n> -               strcpy(path + len, auml_nfd);\n> +               strbuf_setlen(path, baselen);\n> +               strbuf_addstr(path, auml_nfd);\n>                 precomposed_unicode = access(path, R_OK) ? 0 : 1;\n>                 git_config_set(\"core.precomposeunicode\", precomposed_unicode ? \"true\" : \"false\");\n> -               strcpy(path + len, auml_nfc);\n> +               strbuf_setlen(path, baselen);\n> +               strbuf_addstr(path, auml_nfc);\n>                 if (unlink(path))\n>                         die_errno(_(\"failed to unlink '%s'\"), path);\n>         }\n> +       strbuf_setlen(path, baselen);\n>  }\n>\n>\n> diff --git a/compat/precompose_utf8.h b/compat/precompose_utf8.h\n> index 3b73585..7fc7be5 100644\n> --- a/compat/precompose_utf8.h\n> +++ b/compat/precompose_utf8.h\n> @@ -27,7 +27,7 @@ typedef struct {\n>  } PREC_DIR;\n>\n>  void precompose_argv(int argc, const char **argv);\n> -void probe_utf8_pathname_composition(char *, int);\n> +void probe_utf8_pathname_composition(struct strbuf *path);\n>\n>  PREC_DIR *precompose_utf8_opendir(const char *dirname);\n>  struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *dirp);\n> diff --git a/git-compat-util.h b/git-compat-util.h\n> index 348b9dc..712de7f 100644\n> --- a/git-compat-util.h\n> +++ b/git-compat-util.h\n> @@ -229,7 +229,7 @@ typedef unsigned long uintptr_t;\n>  #else\n>  #define precompose_str(in,i_nfd2nfc)\n>  #define precompose_argv(c,v)\n> -#define probe_utf8_pathname_composition(a,b)\n> +#define probe_utf8_pathname_composition(p)\n>  #endif\n>\n>  #ifdef MKDIR_WO_TRAILING_SLASH\n> diff --git a/t/t0001-init.sh b/t/t0001-init.sh\n> index 7de8d85..f91bbcf 100755\n> --- a/t/t0001-init.sh\n> +++ b/t/t0001-init.sh\n> @@ -202,8 +202,8 @@ test_expect_success 'init honors global core.sharedRepository' '\n>         x$(git config -f shared-honor-global/.git/config core.sharedRepository)\n>  '\n>\n> -test_expect_success 'init rejects insanely long --template' '\n> -       test_must_fail git init --template=$(printf \"x%09999dx\" 1) test\n> +test_expect_success 'init allows insanely long --template' '\n> +       git init --template=$(printf \"x%09999dx\" 1) test\n>  '\n>\n>  test_expect_success 'init creates a new directory' '\n> --\n> 2.6.0.rc3.454.g204ad51\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n\nI see compile errors on my mac:\n\nFirst a whole bunch of\n\n./compat/precompose_utf8.h:30:45: warning: declaration of 'struct\nstrbuf' will not be visible outside of this function [-Wvisibility]\nvoid probe_utf8_pathname_composition(struct strbuf *path);\n                                            ^\n1 warning generated.\n    CC trace.o\nIn file included from trace.c:25:\nIn file included from ./cache.h:4:\nIn file included from ./git-compat-util.h:228:\n\nand then\n\ncompat/precompose_utf8.c:39:6: error: conflicting types for\n'probe_utf8_pathname_composition'\nvoid probe_utf8_pathname_composition(struct strbuf *path)\n     ^\ncompat/precompose_utf8.h:30:6: note: previous declaration is here\nvoid probe_utf8_pathname_composition(struct strbuf *path);\n     ^\ncompat/precompose_utf8.c:48:19: warning: incompatible pointer types\npassing 'struct strbuf *' to parameter of type 'const char *'\n[-Wincompatible-pointer-types]\n        output_fd = open(path, O_CREAT|O_EXCL|O_RDWR, 0600);\n                         ^~~~\n/usr/include/sys/fcntl.h:470:22: note: passing argument to parameter here\nint     open(const char *, int, ...) __DARWIN_ALIAS_C(open);\n                         ^\ncompat/precompose_utf8.c:53:32: warning: incompatible pointer types\npassing 'struct strbuf *' to parameter of type 'const char *'\n[-Wincompatible-pointer-types]\n                precomposed_unicode = access(path, R_OK) ? 0 : 1;\n                                             ^~~~\n/usr/include/unistd.h:425:25: note: passing argument to parameter here\nint      access(const char *, int);\n                            ^\ncompat/precompose_utf8.c:57:14: warning: incompatible pointer types\npassing 'struct strbuf *' to parameter of type 'const char *'\n[-Wincompatible-pointer-types]\n                if (unlink(path))\n                           ^~~~\n/usr/include/unistd.h:488:25: note: passing argument to parameter here\nint      unlink(const char *);\n                            ^\ncompat/precompose_utf8.c:58:42: warning: format specifies type 'char\n*' but the argument has type 'struct strbuf *' [-Wformat]\n                        die_errno(_(\"failed to unlink '%s'\"), path);\n                                                       ~~     ^~~~\n5 warnings and 1 error generated.\nmake: *** [compat/precompose_utf8.o] Error 1\n"},{"id":"270924","messageId":"20150930002347.GA23406@sigill.intra.peff.net","threadId":"40424","inReplyTo":"CAO2U3QjunOPoAbGSRjAmCwfk-TnoMveXOJhpb351eh1a_3Xp3A@mail.gmail.com","subject":"Re: [PATCH 41/68] init: use strbufs to store paths","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-09-30T00:23:47Z","receivedAt":"2015-09-30T00:23:47Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Sep 29, 2015 at 04:50:39PM -0700, Michael Blume wrote:\n\n> I see compile errors on my mac:\n> \n> First a whole bunch of\n> \n> ./compat/precompose_utf8.h:30:45: warning: declaration of 'struct\n> strbuf' will not be visible outside of this function [-Wvisibility]\n> void probe_utf8_pathname_composition(struct strbuf *path);\n\nWow, my patch isn't even close to reasonable. I didn't realize because\nwe do not compile this code at all for non-Mac platforms. Sorry.\n\nIt probably needs something like this squashed in (completely untested):\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex cf6a3c8..c643054 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -283,7 +283,7 @@ static int create_default_files(const char *template_path)\n \t\tpath = git_path_buf(&buf, \"CoNfIg\");\n \t\tif (!access(path, F_OK))\n \t\t\tgit_config_set(\"core.ignorecase\", \"true\");\n-\t\tprobe_utf8_pathname_composition(path);\n+\t\tprobe_utf8_pathname_composition(&buf);\n \t}\n \n \tstrbuf_release(&buf);\ndiff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\nindex b4dd3c7..d2d2405 100644\n--- a/compat/precompose_utf8.c\n+++ b/compat/precompose_utf8.c\n@@ -8,6 +8,7 @@\n #include \"cache.h\"\n #include \"utf8.h\"\n #include \"precompose_utf8.h\"\n+#include \"strbuf.h\"\n \n typedef char *iconv_ibp;\n static const char *repo_encoding = \"UTF-8\";\n@@ -45,17 +46,17 @@ void probe_utf8_pathname_composition(struct strbuf *path)\n \tif (precomposed_unicode != -1)\n \t\treturn; /* We found it defined in the global config, respect it */\n \tstrbuf_addstr(path, auml_nfc);\n-\toutput_fd = open(path, O_CREAT|O_EXCL|O_RDWR, 0600);\n+\toutput_fd = open(path->buf, O_CREAT|O_EXCL|O_RDWR, 0600);\n \tif (output_fd >= 0) {\n \t\tclose(output_fd);\n \t\tstrbuf_setlen(path, baselen);\n \t\tstrbuf_addstr(path, auml_nfd);\n-\t\tprecomposed_unicode = access(path, R_OK) ? 0 : 1;\n+\t\tprecomposed_unicode = access(path->buf, R_OK) ? 0 : 1;\n \t\tgit_config_set(\"core.precomposeunicode\", precomposed_unicode ? \"true\" : \"false\");\n \t\tstrbuf_setlen(path, baselen);\n \t\tstrbuf_addstr(path, auml_nfc);\n-\t\tif (unlink(path))\n-\t\t\tdie_errno(_(\"failed to unlink '%s'\"), path);\n+\t\tif (unlink(path->buf))\n+\t\t\tdie_errno(_(\"failed to unlink '%s'\"), path->buf);\n \t}\n \tstrbuf_setlen(path, baselen);\n }\ndiff --git a/compat/precompose_utf8.h b/compat/precompose_utf8.h\nindex 7fc7be5..229e772 100644\n--- a/compat/precompose_utf8.h\n+++ b/compat/precompose_utf8.h\n@@ -4,6 +4,7 @@\n #include <dirent.h>\n #include <iconv.h>\n \n+struct strbuf;\n \n typedef struct dirent_prec_psx {\n \tino_t d_ino;            /* Posix */\n"},{"id":"270952","messageId":"xmqqh9mb7k3r.fsf@gitster.mtv.corp.google.com","threadId":"40424","inReplyTo":"20150930002347.GA23406@sigill.intra.peff.net","subject":"Re: [PATCH 41/68] init: use strbufs to store paths","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-09-30T20:00:56Z","receivedAt":"2015-09-30T20:00:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Tue, Sep 29, 2015 at 04:50:39PM -0700, Michael Blume wrote:\n>\n>> I see compile errors on my mac:\n>> \n>> First a whole bunch of\n>> \n>> ./compat/precompose_utf8.h:30:45: warning: declaration of 'struct\n>> strbuf' will not be visible outside of this function [-Wvisibility]\n>> void probe_utf8_pathname_composition(struct strbuf *path);\n>\n> Wow, my patch isn't even close to reasonable. I didn't realize because\n> we do not compile this code at all for non-Mac platforms. Sorry.\n\nPerhaps the way we completely stub out the platform specific helpers\ncontributes to this kind of gotchas?  I am wondering how much additional\nsafety we would gain if we start doing something like this.\n\nTwo things to note:\n\n * \"struct strbuf\" needs to be visible when the compiler sees this\n   part, which is an indication of the same issue shown in the above\n   error message, is not addressed.\n\n * precompose_str() does not seem to be defined or used, hence\n   removed.\n\n git-compat-util.h | 8 +++++---\n 1 file changed, 5 insertions(+), 3 deletions(-)\n\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 712de7f..6710ff7 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -227,9 +227,11 @@ typedef unsigned long uintptr_t;\n #ifdef PRECOMPOSE_UNICODE\n #include \"compat/precompose_utf8.h\"\n #else\n-#define precompose_str(in,i_nfd2nfc)\n-#define precompose_argv(c,v)\n-#define probe_utf8_pathname_composition(p)\n+static inline void precompose_argv(int, const char **);\n+static inline void probe_utf8_pathname_composition(struct strbuf *buf)\n+{\n+\t; /* no-op */\n+}\n #endif\n \n #ifdef MKDIR_WO_TRAILING_SLASH\n"},{"id":"270967","messageId":"20151001025119.GA31565@sigill.intra.peff.net","threadId":"40424","inReplyTo":"xmqqh9mb7k3r.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 41/68] init: use strbufs to store paths","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-10-01T02:51:19Z","receivedAt":"2015-10-01T02:51:19Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Sep 30, 2015 at 01:00:56PM -0700, Junio C Hamano wrote:\n\n> > Wow, my patch isn't even close to reasonable. I didn't realize because\n> > we do not compile this code at all for non-Mac platforms. Sorry.\n> \n> Perhaps the way we completely stub out the platform specific helpers\n> contributes to this kind of gotchas?  I am wondering how much additional\n> safety we would gain if we start doing something like this.\n\nI think it is an improvement, but it does not solve all of the problems.\nI also botched the implementation of probe_utf8_pathname_composition,\nand that does not get compiled on most platforms (though we _could_\ncompile it and just never call it).\n\n-Peff\n"},{"id":"270995","messageId":"560E1D78.1040805@web.de","threadId":"40424","inReplyTo":"20151001025119.GA31565@sigill.intra.peff.net","subject":"Re: [PATCH 41/68] init: use strbufs to store paths","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2015-10-02T06:00:24Z","receivedAt":"2015-10-02T06:00:24Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On 10/01/2015 04:51 AM, Jeff King wrote:\n> On Wed, Sep 30, 2015 at 01:00:56PM -0700, Junio C Hamano wrote:\n>\n>>> Wow, my patch isn't even close to reasonable. I didn't realize because\n>>> we do not compile this code at all for non-Mac platforms. Sorry.\n>> Perhaps the way we completely stub out the platform specific helpers\n>> contributes to this kind of gotchas?  I am wondering how much additional\n>> safety we would gain if we start doing something like this.\n> I think it is an improvement, but it does not solve all of the problems.\n> I also botched the implementation of probe_utf8_pathname_composition,\n> and that does not get compiled on most platforms (though we _could_\n> compile it and just never call it).\n>\n> -Peff\n>\nPeff, are you planing a re-roll ?\nOr. Junio, do you plan to fix it ?\nOr should I send a patch on top of pu ?\n\nThe compilation can be tested under Linux like this:\n\ndiff --git a/config.mak.uname b/config.mak.uname\nindex 7486a7e..6d09bd0 100644\n--- a/config.mak.uname\n+++ b/config.mak.uname\n@@ -13,6 +13,9 @@ ifdef MSVC\n         uname_O := Windows\n  endif\n\n+COMPAT_OBJS += compat/precompose_utf8.o\n+BASIC_CFLAGS += -DPRECOMPOSE_UNICODE\n+\n"},{"id":"271010","messageId":"20151002153346.GA31378@sigill.intra.peff.net","threadId":"40424","inReplyTo":"560E1D78.1040805@web.de","subject":"Re: [PATCH 41/68] init: use strbufs to store paths","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-10-02T15:33:47Z","receivedAt":"2015-10-02T15:33:47Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 02, 2015 at 08:00:24AM +0200, Torsten Bögershausen wrote:\n\n> Peff, are you planing a re-roll ?\n> Or. Junio, do you plan to fix it ?\n> Or should I send a patch on top of pu ?\n\nI am on vacation, so I am hoping that somebody on OS X can confirm that\nthe patch that I sent earlier does indeed fix it, and that Junio\ncan squash it in.\n\nMakefile hack similar to what you posted, but I cannot actually on Linux\nthat the code does what it should).\n\n> The compilation can be tested under Linux like this:\n\nYeah, I did something similar to see that it at least compiled, but I\ndon't have an easy way to actually run t3910.\n\n-Peff\n"},{"id":"271042","messageId":"560F6E98.8030305@web.de","threadId":"40424","inReplyTo":"20150930002347.GA23406@sigill.intra.peff.net","subject":"Re: [PATCH 41/68] init: use strbufs to store paths","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2015-10-03T05:58:48Z","receivedAt":"2015-10-03T05:58:48Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On 30.09.15 02:23, Jeff King wrote:\n> On Tue, Sep 29, 2015 at 04:50:39PM -0700, Michael Blume wrote:\n> \n>> I see compile errors on my mac:\n>>\n\nThis is my attempt, passing the test, but not fully polished.\n\n\n\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 89f2c05..60b559c 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -276,7 +276,9 @@ static int create_default_files(const char *template_path)\n \t\tpath = git_path_buf(&buf, \"CoNfIg\");\n \t\tif (!access(path, F_OK))\n \t\t\tgit_config_set(\"core.ignorecase\", \"true\");\n-\t\tprobe_utf8_pathname_composition(path);\n+\t\t/* Probe utf-8 normalization withou mangling CoNfIG */\n+\t\tpath = git_path_buf(&buf, \"config\");\n+\t\tprobe_utf8_pathname_composition(path, strlen(path));\n \t}\n \n \tstrbuf_release(&buf);\ndiff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\nindex b4dd3c7..37172a4 100644\n--- a/compat/precompose_utf8.c\n+++ b/compat/precompose_utf8.c\n@@ -8,6 +8,7 @@\n #include \"cache.h\"\n #include \"utf8.h\"\n #include \"precompose_utf8.h\"\n+#include \"strbuf.h\"\n \n typedef char *iconv_ibp;\n static const char *repo_encoding = \"UTF-8\";\n@@ -36,28 +37,33 @@ static size_t has_non_ascii(const char *s, size_t maxlen, size_t *strlen_c)\n }\n \n \n-void probe_utf8_pathname_composition(struct strbuf *path)\n+void probe_utf8_pathname_composition(char *path, int len)\n {\n \tstatic const char *auml_nfc = \"\\xc3\\xa4\";\n \tstatic const char *auml_nfd = \"\\x61\\xcc\\x88\";\n-\tsize_t baselen = path->len;\n+\tstruct strbuf sbuf;\n \tint output_fd;\n \tif (precomposed_unicode != -1)\n \t\treturn; /* We found it defined in the global config, respect it */\n-\tstrbuf_addstr(path, auml_nfc);\n-\toutput_fd = open(path, O_CREAT|O_EXCL|O_RDWR, 0600);\n+\tstrbuf_init(&sbuf, len+3);\n+\tstrbuf_add(&sbuf, path, len);\n+\tstrbuf_addstr(&sbuf, auml_nfc);\n+\toutput_fd = open(sbuf.buf, O_CREAT|O_EXCL|O_RDWR, 0600);\n+\tfprintf(stderr, \"%s/%s:%d sbuf.buf=%s\\n\",\n+\t\t\t\t\t__FILE__, __FUNCTION__, __LINE__, sbuf.buf);\n \tif (output_fd >= 0) {\n \t\tclose(output_fd);\n-\t\tstrbuf_setlen(path, baselen);\n-\t\tstrbuf_addstr(path, auml_nfd);\n+\t\tstrbuf_setlen(&sbuf, len);\n+\t\tstrbuf_addstr(&sbuf, auml_nfd);\n+\tfprintf(stderr, \"%s/%s:%d sbuf.buf=%s\\n\",\n+\t\t\t\t\t__FILE__, __FUNCTION__, __LINE__, sbuf.buf);\n \t\tprecomposed_unicode = access(path, R_OK) ? 0 : 1;\n \t\tgit_config_set(\"core.precomposeunicode\", precomposed_unicode ? \"true\" : \"false\");\n-\t\tstrbuf_setlen(path, baselen);\n-\t\tstrbuf_addstr(path, auml_nfc);\n+\t\tstrcpy(path + len, auml_nfc);\n \t\tif (unlink(path))\n \t\t\tdie_errno(_(\"failed to unlink '%s'\"), path);\n \t}\n-\tstrbuf_setlen(path, baselen);\n+\tstrbuf_release(&sbuf);\n }\n \n \ndiff --git a/compat/precompose_utf8.h b/compat/precompose_utf8.h\nindex 7fc7be5..3b73585 100644\n--- a/compat/precompose_utf8.h\n+++ b/compat/precompose_utf8.h\n@@ -27,7 +27,7 @@ typedef struct {\n } PREC_DIR;\n \n void precompose_argv(int argc, const char **argv);\n-void probe_utf8_pathname_composition(struct strbuf *path);\n+void probe_utf8_pathname_composition(char *, int);\n \n PREC_DIR *precompose_utf8_opendir(const char *dirname);\n struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *dirp);\n"},{"id":"271061","messageId":"xmqqtwq73nbj.fsf@gitster.mtv.corp.google.com","threadId":"40424","inReplyTo":"560F6E98.8030305@web.de","subject":"Re: [PATCH 41/68] init: use strbufs to store paths","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-10-03T16:54:08Z","receivedAt":"2015-10-03T16:54:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Torsten Bögershausen <tboegi@web.de> writes:\n\n> On 30.09.15 02:23, Jeff King wrote:\n>> On Tue, Sep 29, 2015 at 04:50:39PM -0700, Michael Blume wrote:\n>> \n>>> I see compile errors on my mac:\n>>>\n>\n> This is my attempt, passing the test, but not fully polished.\n\nThanks.\n\n> diff --git a/builtin/init-db.c b/builtin/init-db.c\n> index 89f2c05..60b559c 100644\n> --- a/builtin/init-db.c\n> +++ b/builtin/init-db.c\n> @@ -276,7 +276,9 @@ static int create_default_files(const char *template_path)\n>  \t\tpath = git_path_buf(&buf, \"CoNfIg\");\n>  \t\tif (!access(path, F_OK))\n>  \t\t\tgit_config_set(\"core.ignorecase\", \"true\");\n> -\t\tprobe_utf8_pathname_composition(path);\n> +\t\t/* Probe utf-8 normalization withou mangling CoNfIG */\n> +\t\tpath = git_path_buf(&buf, \"config\");\n> +\t\tprobe_utf8_pathname_composition(path, strlen(path));\n\nHmph, Peff's quick-fix passed the original \"CoNfIg\" in &buf directly\nto probe_utf8_pathname_composition() without changing its signature.\n\nWhat is the reason behind these two changes?  i.e. why is it\ninappropriate to use \"CoNfIg\" (and append the auml to it to use for\nthe checking) and why does the function need to take pointer + len,\nonly to store it in another strbuf itself?\n\n> diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\n> index b4dd3c7..37172a4 100644\n> --- a/compat/precompose_utf8.c\n> +++ b/compat/precompose_utf8.c\n> @@ -8,6 +8,7 @@\n>  #include \"cache.h\"\n>  #include \"utf8.h\"\n>  #include \"precompose_utf8.h\"\n> +#include \"strbuf.h\"\n>  \n>  typedef char *iconv_ibp;\n>  static const char *repo_encoding = \"UTF-8\";\n> @@ -36,28 +37,33 @@ static size_t has_non_ascii(const char *s, size_t maxlen, size_t *strlen_c)\n>  }\n>  \n>  \n> -void probe_utf8_pathname_composition(struct strbuf *path)\n> +void probe_utf8_pathname_composition(char *path, int len)\n>  {\n>  \tstatic const char *auml_nfc = \"\\xc3\\xa4\";\n>  \tstatic const char *auml_nfd = \"\\x61\\xcc\\x88\";\n> -\tsize_t baselen = path->len;\n> +\tstruct strbuf sbuf;\n>  \tint output_fd;\n>  \tif (precomposed_unicode != -1)\n>  \t\treturn; /* We found it defined in the global config, respect it */\n> -\tstrbuf_addstr(path, auml_nfc);\n> -\toutput_fd = open(path, O_CREAT|O_EXCL|O_RDWR, 0600);\n> +\tstrbuf_init(&sbuf, len+3);\n> +\tstrbuf_add(&sbuf, path, len);\n> +\tstrbuf_addstr(&sbuf, auml_nfc);\n> +\toutput_fd = open(sbuf.buf, O_CREAT|O_EXCL|O_RDWR, 0600);\n> +\tfprintf(stderr, \"%s/%s:%d sbuf.buf=%s\\n\",\n> +\t\t\t\t\t__FILE__, __FUNCTION__, __LINE__, sbuf.buf);\n>  \tif (output_fd >= 0) {\n>  \t\tclose(output_fd);\n> -\t\tstrbuf_setlen(path, baselen);\n> -\t\tstrbuf_addstr(path, auml_nfd);\n> +\t\tstrbuf_setlen(&sbuf, len);\n> +\t\tstrbuf_addstr(&sbuf, auml_nfd);\n> +\tfprintf(stderr, \"%s/%s:%d sbuf.buf=%s\\n\",\n> +\t\t\t\t\t__FILE__, __FUNCTION__, __LINE__, sbuf.buf);\n>  \t\tprecomposed_unicode = access(path, R_OK) ? 0 : 1;\n>  \t\tgit_config_set(\"core.precomposeunicode\", precomposed_unicode ? \"true\" : \"false\");\n> -\t\tstrbuf_setlen(path, baselen);\n> -\t\tstrbuf_addstr(path, auml_nfc);\n> +\t\tstrcpy(path + len, auml_nfc);\n>  \t\tif (unlink(path))\n>  \t\t\tdie_errno(_(\"failed to unlink '%s'\"), path);\n>  \t}\n> -\tstrbuf_setlen(path, baselen);\n> +\tstrbuf_release(&sbuf);\n>  }\n>  \n>  \n> diff --git a/compat/precompose_utf8.h b/compat/precompose_utf8.h\n> index 7fc7be5..3b73585 100644\n> --- a/compat/precompose_utf8.h\n> +++ b/compat/precompose_utf8.h\n> @@ -27,7 +27,7 @@ typedef struct {\n>  } PREC_DIR;\n>  \n>  void precompose_argv(int argc, const char **argv);\n> -void probe_utf8_pathname_composition(struct strbuf *path);\n> +void probe_utf8_pathname_composition(char *, int);\n>  \n>  PREC_DIR *precompose_utf8_opendir(const char *dirname);\n>  struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *dirp);\n"},{"id":"271072","messageId":"561044AD.8010803@web.de","threadId":"40424","inReplyTo":"xmqqtwq73nbj.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 41/68] init: use strbufs to store paths","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2015-10-03T21:12:13Z","receivedAt":"2015-10-03T21:12:13Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On 03.10.15 18:54, Junio C Hamano wrote:\n> Torsten Bögershausen <tboegi@web.de> writes:\n> \n>> On 30.09.15 02:23, Jeff King wrote:\n>>> On Tue, Sep 29, 2015 at 04:50:39PM -0700, Michael Blume wrote:\n>>>\n>>>> I see compile errors on my mac:\n>>>>\n>>\n>> This is my attempt, passing the test, but not fully polished.\n> \n> Thanks.\n> \n>> diff --git a/builtin/init-db.c b/builtin/init-db.c\n>> index 89f2c05..60b559c 100644\n>> --- a/builtin/init-db.c\n>> +++ b/builtin/init-db.c\n>> @@ -276,7 +276,9 @@ static int create_default_files(const char *template_path)\n>>  \t\tpath = git_path_buf(&buf, \"CoNfIg\");\n>>  \t\tif (!access(path, F_OK))\n>>  \t\t\tgit_config_set(\"core.ignorecase\", \"true\");\n>> -\t\tprobe_utf8_pathname_composition(path);\n>> +\t\t/* Probe utf-8 normalization withou mangling CoNfIG */\n>> +\t\tpath = git_path_buf(&buf, \"config\");\n>> +\t\tprobe_utf8_pathname_composition(path, strlen(path));\n> \n> Hmph, Peff's quick-fix passed the original \"CoNfIg\" in &buf directly\n> to probe_utf8_pathname_composition() without changing its signature.\nTrue, ( I was thinking that the test did only work on case insensitive FS).\nWe can skip that change.\n\nBeside that, I later realized, that a better signature could be:\n+void probe_utf8_pathname_composition(const char *path, size_t len)\n\nI can send a proper patch the next days.\n"},{"id":"271077","messageId":"20151004033713.GB20876@sigill.intra.peff.net","threadId":"40424","inReplyTo":"561044AD.8010803@web.de","subject":"Re: [PATCH 41/68] init: use strbufs to store paths","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-10-04T03:37:13Z","receivedAt":"2015-10-04T03:37:13Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Oct 03, 2015 at 11:12:13PM +0200, Torsten Bögershausen wrote:\n\n> > Hmph, Peff's quick-fix passed the original \"CoNfIg\" in &buf directly\n> > to probe_utf8_pathname_composition() without changing its signature.\n> True, ( I was thinking that the test did only work on case insensitive FS).\n> We can skip that change.\n> \n> Beside that, I later realized, that a better signature could be:\n> +void probe_utf8_pathname_composition(const char *path, size_t len)\n> \n> I can send a proper patch the next days.\n\nThat is the original signature, before my sprintf series. I do not mind\nleaving that as-is, and simply cleaning up probe_utf8_pathname_composition\nby using a strbuf internally there. Though I have to wonder if it even\nneeds us to pass _anything_ at that point. It could just call\ngit_path_buf(\"config%s\", auml_nfd) itself. The whole reason to pass\nanything was to let it reuse the buffer the caller had.\n\n-Peff\n"},{"id":"271080","messageId":"5610C7C3.807@web.de","threadId":"40424","inReplyTo":"20151004033713.GB20876@sigill.intra.peff.net","subject":"Re: [PATCH 41/68] init: use strbufs to store paths","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2015-10-04T06:31:31Z","receivedAt":"2015-10-04T06:31:31Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"\n\nOn 2015-10-04 05.37, Jeff King wrote:\n> On Sat, Oct 03, 2015 at 11:12:13PM +0200, Torsten Bögershausen wrote:\n>\n>>> Hmph, Peff's quick-fix passed the original \"CoNfIg\" in &buf directly\n>>> to probe_utf8_pathname_composition() without changing its signature.\n>> True, ( I was thinking that the test did only work on case insensitive FS).\n>> We can skip that change.\n>>\n>> Beside that, I later realized, that a better signature could be:\n>> +void probe_utf8_pathname_composition(const char *path, size_t len)\n>>\n>> I can send a proper patch the next days.\n> That is the original signature, before my sprintf series. I do not mind\n> leaving that as-is, and simply cleaning up probe_utf8_pathname_composition\n> by using a strbuf internally there. Though I have to wonder if it even\n> needs us to pass _anything_ at that point. It could just call\n> git_path_buf(\"config%s\", auml_nfd) itself. The whole reason to pass\n> anything was to let it reuse the buffer the caller had.\n>\n> -Peff\nMakes sense, here is V2:\n git diff  07690109b6a252ac7cbede\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 89f2c05..4892579 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -276,7 +276,7 @@ static int create_default_files(const char *template_path)\n         path = git_path_buf(&buf, \"CoNfIg\");\n         if (!access(path, F_OK))\n             git_config_set(\"core.ignorecase\", \"true\");\n-        probe_utf8_pathname_composition(path);\n+        probe_utf8_pathname_composition();\n     }\n \n     strbuf_release(&buf);\ndiff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\nindex b4dd3c7..64b85f2 100644\n--- a/compat/precompose_utf8.c\n+++ b/compat/precompose_utf8.c\n@@ -8,6 +8,7 @@\n #include \"cache.h\"\n #include \"utf8.h\"\n #include \"precompose_utf8.h\"\n+#include \"strbuf.h\"\n \n typedef char *iconv_ibp;\n static const char *repo_encoding = \"UTF-8\";\n@@ -36,28 +37,27 @@ static size_t has_non_ascii(const char *s, size_t maxlen,\nsize_t *strlen_c)\n }\n \n \n-void probe_utf8_pathname_composition(struct strbuf *path)\n+void probe_utf8_pathname_composition(void)\n {\n+    struct strbuf sbuf = STRBUF_INIT;\n     static const char *auml_nfc = \"\\xc3\\xa4\";\n     static const char *auml_nfd = \"\\x61\\xcc\\x88\";\n-    size_t baselen = path->len;\n+    const char *path;\n     int output_fd;\n     if (precomposed_unicode != -1)\n         return; /* We found it defined in the global config, respect it */\n-    strbuf_addstr(path, auml_nfc);\n+    path = git_path_buf(&sbuf, \"%s\", auml_nfc);\n     output_fd = open(path, O_CREAT|O_EXCL|O_RDWR, 0600);\n     if (output_fd >= 0) {\n         close(output_fd);\n-        strbuf_setlen(path, baselen);\n-        strbuf_addstr(path, auml_nfd);\n+        path = git_path_buf(&sbuf, \"%s\", auml_nfd);\n         precomposed_unicode = access(path, R_OK) ? 0 : 1;\n         git_config_set(\"core.precomposeunicode\", precomposed_unicode ? \"true\" :\n\"false\");\n-        strbuf_setlen(path, baselen);\n-        strbuf_addstr(path, auml_nfc);\n+        path = git_path_buf(&sbuf, \"%s\", auml_nfc);\n         if (unlink(path))\n             die_errno(_(\"failed to unlink '%s'\"), path);\n     }\n-    strbuf_setlen(path, baselen);\n+    strbuf_release(&sbuf);\n }\n \n \ndiff --git a/compat/precompose_utf8.h b/compat/precompose_utf8.h\nindex 7fc7be5..a94e7c4 100644\n--- a/compat/precompose_utf8.h\n+++ b/compat/precompose_utf8.h\n@@ -27,7 +27,7 @@ typedef struct {\n } PREC_DIR;\n \n void precompose_argv(int argc, const char **argv);\n-void probe_utf8_pathname_composition(struct strbuf *path);\n+void probe_utf8_pathname_composition(void);\n \n PREC_DIR *precompose_utf8_opendir(const char *dirname);\n struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *dirp);\n\n\n============================\n\nAnd this is fix for David:\n\n\ndiff --git a/refs.h b/refs.h\nindex f499093..7dee497 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -670,7 +670,6 @@ typedef int (*ref_transaction_verify_fn)(struct\nref_transaction *transaction,\n         unsigned int flags, struct strbuf *err);\n typedef int (*ref_transaction_commit_fn)(struct ref_transaction *transaction,\n                      struct strbuf *err);\n-typedef void (*ref_transaction_free_fn)(struct ref_transaction *transaction);\n \n /* reflog functions */\n typedef int (*for_each_reflog_ent_fn)(const char *refname,\n"},{"id":"271114","messageId":"20151005034126.GA5639@sigill.intra.peff.net","threadId":"40424","inReplyTo":"5610C7C3.807@web.de","subject":"Re: [PATCH 41/68] init: use strbufs to store paths","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-10-05T03:41:26Z","receivedAt":"2015-10-05T03:41:26Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Oct 04, 2015 at 08:31:31AM +0200, Torsten Bögershausen wrote:\n\n> > That is the original signature, before my sprintf series. I do not mind\n> > leaving that as-is, and simply cleaning up probe_utf8_pathname_composition\n> > by using a strbuf internally there. Though I have to wonder if it even\n> > needs us to pass _anything_ at that point. It could just call\n> > git_path_buf(\"config%s\", auml_nfd) itself. The whole reason to pass\n> > anything was to let it reuse the buffer the caller had.\n> >\n> > -Peff\n> Makes sense, here is V2:\n\nYeah, I think this is much nicer.\n\nAnd because it decouples the interface between init-db.c and the\nprecompose code, it is easy to do it as a separate patch before the\ninit-db one.\n\n> diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\n> index b4dd3c7..64b85f2 100644\n> --- a/compat/precompose_utf8.c\n> +++ b/compat/precompose_utf8.c\n> @@ -8,6 +8,7 @@\n>  #include \"cache.h\"\n>  #include \"utf8.h\"\n>  #include \"precompose_utf8.h\"\n> +#include \"strbuf.h\"\n\nI think this is actually redundant; it is part of cache.h included above\n(and the precompose_utf8.h header file does not need to care anymore,\nsince the strbuf is not part of the interface).\n\n> -void probe_utf8_pathname_composition(struct strbuf *path)\n> +void probe_utf8_pathname_composition(void)\n>  {\n> +    struct strbuf sbuf = STRBUF_INIT;\n>      static const char *auml_nfc = \"\\xc3\\xa4\";\n>      static const char *auml_nfd = \"\\x61\\xcc\\x88\";\n> -    size_t baselen = path->len;\n> +    const char *path;\n\nI don't think we need this separate \"path\"; we can just access the\nstrbuf directly (that makes the diff a little noisier, but I think the\nend result is simpler).\n\n> diff --git a/compat/precompose_utf8.h b/compat/precompose_utf8.h\n> index 7fc7be5..a94e7c4 100644\n> --- a/compat/precompose_utf8.h\n> +++ b/compat/precompose_utf8.h\n> @@ -27,7 +27,7 @@ typedef struct {\n>  } PREC_DIR;\n>  \n>  void precompose_argv(int argc, const char **argv);\n> -void probe_utf8_pathname_composition(struct strbuf *path);\n> +void probe_utf8_pathname_composition(void);\n\nI think we need a similar fix for the compat macro to build on non-Mac\nplatforms.\n\nHere's a mini-series I came up with, which I hope is polished enough for\nJunio to apply as a drop-in replacement for the \"init: use strbufs\"\npatch from my original series. I compiled-tested it on Linux, with and\nwithout precompose_utf8.o support hacked in. I don't have access to an\nOS X machine to test on, so I'd appreciate confirmation that t3910 still\npasses there.\n\n  [1/3]: precompose_utf8: drop unused variable\n  [2/3]: probe_utf8_pathname_composition: use internal strbuf\n  [3/3]: init: use strbufs to store paths\n\n-Peff\n"},{"id":"271115","messageId":"20151005034313.GA25502@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20151005034126.GA5639@sigill.intra.peff.net","subject":"[PATCH 1/3] precompose_utf8: drop unused variable","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-10-05T03:43:14Z","receivedAt":"2015-10-05T03:43:14Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The result of iconv is assigned to a variable, but we never\nuse it (instead, we check errno and whether the function\nconsumed all bytes). Let's drop the assignment, as it\ntriggers gcc's -Wunused-but-set-variable.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nThis is obviously completely optional; I just needed it to get the\n\"precompose on Linux\" hack to build at all with my usual \"-Wall -Werror\"\nsettings. I guess clang doesn't have a similar warning, or maybe people\non Macs are not as pedantic as I am.\n\n compat/precompose_utf8.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\nindex 95fe849..044c686 100644\n--- a/compat/precompose_utf8.c\n+++ b/compat/precompose_utf8.c\n@@ -139,9 +139,8 @@ struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *prec_dir)\n \t\t\t\tsize_t inleft = namelenz;\n \t\t\t\tchar *outpos = &prec_dir->dirent_nfc->d_name[0];\n \t\t\t\tsize_t outsz = prec_dir->dirent_nfc->max_name_len;\n-\t\t\t\tsize_t cnt;\n \t\t\t\terrno = 0;\n-\t\t\t\tcnt = iconv(prec_dir->ic_precompose, &cp, &inleft, &outpos, &outsz);\n+\t\t\t\ticonv(prec_dir->ic_precompose, &cp, &inleft, &outpos, &outsz);\n \t\t\t\tif (errno || inleft) {\n \t\t\t\t\t/*\n \t\t\t\t\t * iconv() failed and errno could be E2BIG, EILSEQ, EINVAL, EBADF\n-- \n2.6.0.455.ga3f9923\n"},{"id":"271116","messageId":"20151005034526.GB25502@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20151005034126.GA5639@sigill.intra.peff.net","subject":"[PATCH 2/3] probe_utf8_pathname_composition: use internal strbuf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-10-05T03:45:26Z","receivedAt":"2015-10-05T03:45:26Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When we are initializing a .git directory, we may call\nprobe_utf8_pathname_composition to detect utf8 mangling. We\npass in a path buffer for it to use, and it blindly\nstrcpy()s into it, not knowing whether the buffer is large\nenough to hold the result or not.\n\nIn practice this isn't a big deal, because the buffer we\npass in already contains \"$GIT_DIR/config\", and we append\nonly a few extra bytes to it. But we can easily do the right\nthing just by calling git_path_buf ourselves. Technically\nthis results in a different pathname (before we appended our\nutf8 characters to the \"config\" path, and now they get their\nown files in $GIT_DIR), but that should not matter for our\npurposes.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nI assume that \"$GIT_DIR/$auml_nfc\" is fine to perform this test based on\nTorsten's patch showing the same simplification. If it matters, or if we\nsimply want to be ultra-conservative, changing the \"%s\" to \"CoNfIg%s\"\nwould yield identical behavior (but if it doesn't matter, I think I\nprefer this as a simplification).\n\n builtin/init-db.c        |  2 +-\n compat/precompose_utf8.c | 18 ++++++++++--------\n compat/precompose_utf8.h |  2 +-\n git-compat-util.h        |  2 +-\n 4 files changed, 13 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex e7d0e31..89addda 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -312,7 +312,7 @@ static int create_default_files(const char *template_path)\n \t\tstrcpy(path + len, \"CoNfIg\");\n \t\tif (!access(path, F_OK))\n \t\t\tgit_config_set(\"core.ignorecase\", \"true\");\n-\t\tprobe_utf8_pathname_composition(path, len);\n+\t\tprobe_utf8_pathname_composition();\n \t}\n \n \treturn reinit;\ndiff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\nindex 044c686..079070f 100644\n--- a/compat/precompose_utf8.c\n+++ b/compat/precompose_utf8.c\n@@ -36,24 +36,26 @@ static size_t has_non_ascii(const char *s, size_t maxlen, size_t *strlen_c)\n }\n \n \n-void probe_utf8_pathname_composition(char *path, int len)\n+void probe_utf8_pathname_composition(void)\n {\n+\tstruct strbuf path = STRBUF_INIT;\n \tstatic const char *auml_nfc = \"\\xc3\\xa4\";\n \tstatic const char *auml_nfd = \"\\x61\\xcc\\x88\";\n \tint output_fd;\n \tif (precomposed_unicode != -1)\n \t\treturn; /* We found it defined in the global config, respect it */\n-\tstrcpy(path + len, auml_nfc);\n-\toutput_fd = open(path, O_CREAT|O_EXCL|O_RDWR, 0600);\n+\tgit_path_buf(&path, \"%s\", auml_nfc);\n+\toutput_fd = open(path.buf, O_CREAT|O_EXCL|O_RDWR, 0600);\n \tif (output_fd >= 0) {\n \t\tclose(output_fd);\n-\t\tstrcpy(path + len, auml_nfd);\n-\t\tprecomposed_unicode = access(path, R_OK) ? 0 : 1;\n+\t\tgit_path_buf(&path, \"%s\", auml_nfd);\n+\t\tprecomposed_unicode = access(path.buf, R_OK) ? 0 : 1;\n \t\tgit_config_set(\"core.precomposeunicode\", precomposed_unicode ? \"true\" : \"false\");\n-\t\tstrcpy(path + len, auml_nfc);\n-\t\tif (unlink(path))\n-\t\t\tdie_errno(_(\"failed to unlink '%s'\"), path);\n+\t\tgit_path_buf(&path, \"%s\", auml_nfc);\n+\t\tif (unlink(path.buf))\n+\t\t\tdie_errno(_(\"failed to unlink '%s'\"), path.buf);\n \t}\n+\tstrbuf_release(&path);\n }\n \n \ndiff --git a/compat/precompose_utf8.h b/compat/precompose_utf8.h\nindex 3b73585..a94e7c4 100644\n--- a/compat/precompose_utf8.h\n+++ b/compat/precompose_utf8.h\n@@ -27,7 +27,7 @@ typedef struct {\n } PREC_DIR;\n \n void precompose_argv(int argc, const char **argv);\n-void probe_utf8_pathname_composition(char *, int);\n+void probe_utf8_pathname_composition(void);\n \n PREC_DIR *precompose_utf8_opendir(const char *dirname);\n struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *dirp);\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 348b9dc..9a3e559 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -229,7 +229,7 @@ typedef unsigned long uintptr_t;\n #else\n #define precompose_str(in,i_nfd2nfc)\n #define precompose_argv(c,v)\n-#define probe_utf8_pathname_composition(a,b)\n+#define probe_utf8_pathname_composition()\n #endif\n \n #ifdef MKDIR_WO_TRAILING_SLASH\n-- \n2.6.0.455.ga3f9923\n"},{"id":"271117","messageId":"20151005034604.GC25502@sigill.intra.peff.net","threadId":"40424","inReplyTo":"20151005034126.GA5639@sigill.intra.peff.net","subject":"[PATCH 3/3] init: use strbufs to store paths","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-10-05T03:46:04Z","receivedAt":"2015-10-05T03:46:04Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The init code predates strbufs, and uses PATH_MAX-sized\nbuffers along with many manual checks on intermediate sizes\n(some of which make magic assumptions, such as that init\nwill not create a path inside .git longer than 50\ncharacters).\n\nWe can simplify this greatly by using strbufs, which drops\nsome hard-to-verify strcpy calls in favor of git_path_buf.\nWhile we're in the area, let's also convert existing calls\nto git_path to the safer git_path_buf (our existing calls\nwere passed to pretty tame functions, and so were not a\nproblem, but it's easy to be consistent and safe here).\n\nNote that we had an explicit test that \"git init\" rejects\nlong template directories. This comes from 32d1776 (init: Do\nnot segfault on big GIT_TEMPLATE_DIR environment variable,\n2009-04-18). We can drop the test_must_fail here, as we now\naccept this and need only confirm that we don't segfault,\nwhich was the original point of the test.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nSame as the original, but minus the bits that touch the precompose\nprobing.\n\n builtin/init-db.c | 172 +++++++++++++++++++++++-------------------------------\n t/t0001-init.sh   |   4 +-\n 2 files changed, 76 insertions(+), 100 deletions(-)\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 89addda..f59f407 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -36,10 +36,11 @@ static void safe_create_dir(const char *dir, int share)\n \t\tdie(_(\"Could not make %s writable by group\"), dir);\n }\n \n-static void copy_templates_1(char *path, int baselen,\n-\t\t\t     char *template, int template_baselen,\n+static void copy_templates_1(struct strbuf *path, struct strbuf *template,\n \t\t\t     DIR *dir)\n {\n+\tsize_t path_baselen = path->len;\n+\tsize_t template_baselen = template->len;\n \tstruct dirent *de;\n \n \t/* Note: if \".git/hooks\" file exists in the repository being\n@@ -49,77 +50,64 @@ static void copy_templates_1(char *path, int baselen,\n \t * with the way the namespace under .git/ is organized, should\n \t * be really carefully chosen.\n \t */\n-\tsafe_create_dir(path, 1);\n+\tsafe_create_dir(path->buf, 1);\n \twhile ((de = readdir(dir)) != NULL) {\n \t\tstruct stat st_git, st_template;\n-\t\tint namelen;\n \t\tint exists = 0;\n \n+\t\tstrbuf_setlen(path, path_baselen);\n+\t\tstrbuf_setlen(template, template_baselen);\n+\n \t\tif (de->d_name[0] == '.')\n \t\t\tcontinue;\n-\t\tnamelen = strlen(de->d_name);\n-\t\tif ((PATH_MAX <= baselen + namelen) ||\n-\t\t    (PATH_MAX <= template_baselen + namelen))\n-\t\t\tdie(_(\"insanely long template name %s\"), de->d_name);\n-\t\tmemcpy(path + baselen, de->d_name, namelen+1);\n-\t\tmemcpy(template + template_baselen, de->d_name, namelen+1);\n-\t\tif (lstat(path, &st_git)) {\n+\t\tstrbuf_addstr(path, de->d_name);\n+\t\tstrbuf_addstr(template, de->d_name);\n+\t\tif (lstat(path->buf, &st_git)) {\n \t\t\tif (errno != ENOENT)\n-\t\t\t\tdie_errno(_(\"cannot stat '%s'\"), path);\n+\t\t\t\tdie_errno(_(\"cannot stat '%s'\"), path->buf);\n \t\t}\n \t\telse\n \t\t\texists = 1;\n \n-\t\tif (lstat(template, &st_template))\n-\t\t\tdie_errno(_(\"cannot stat template '%s'\"), template);\n+\t\tif (lstat(template->buf, &st_template))\n+\t\t\tdie_errno(_(\"cannot stat template '%s'\"), template->buf);\n \n \t\tif (S_ISDIR(st_template.st_mode)) {\n-\t\t\tDIR *subdir = opendir(template);\n-\t\t\tint baselen_sub = baselen + namelen;\n-\t\t\tint template_baselen_sub = template_baselen + namelen;\n+\t\t\tDIR *subdir = opendir(template->buf);\n \t\t\tif (!subdir)\n-\t\t\t\tdie_errno(_(\"cannot opendir '%s'\"), template);\n-\t\t\tpath[baselen_sub++] =\n-\t\t\t\ttemplate[template_baselen_sub++] = '/';\n-\t\t\tpath[baselen_sub] =\n-\t\t\t\ttemplate[template_baselen_sub] = 0;\n-\t\t\tcopy_templates_1(path, baselen_sub,\n-\t\t\t\t\t template, template_baselen_sub,\n-\t\t\t\t\t subdir);\n+\t\t\t\tdie_errno(_(\"cannot opendir '%s'\"), template->buf);\n+\t\t\tstrbuf_addch(path, '/');\n+\t\t\tstrbuf_addch(template, '/');\n+\t\t\tcopy_templates_1(path, template, subdir);\n \t\t\tclosedir(subdir);\n \t\t}\n \t\telse if (exists)\n \t\t\tcontinue;\n \t\telse if (S_ISLNK(st_template.st_mode)) {\n-\t\t\tchar lnk[256];\n-\t\t\tint len;\n-\t\t\tlen = readlink(template, lnk, sizeof(lnk));\n-\t\t\tif (len < 0)\n-\t\t\t\tdie_errno(_(\"cannot readlink '%s'\"), template);\n-\t\t\tif (sizeof(lnk) <= len)\n-\t\t\t\tdie(_(\"insanely long symlink %s\"), template);\n-\t\t\tlnk[len] = 0;\n-\t\t\tif (symlink(lnk, path))\n-\t\t\t\tdie_errno(_(\"cannot symlink '%s' '%s'\"), lnk, path);\n+\t\t\tstruct strbuf lnk = STRBUF_INIT;\n+\t\t\tif (strbuf_readlink(&lnk, template->buf, 0) < 0)\n+\t\t\t\tdie_errno(_(\"cannot readlink '%s'\"), template->buf);\n+\t\t\tif (symlink(lnk.buf, path->buf))\n+\t\t\t\tdie_errno(_(\"cannot symlink '%s' '%s'\"),\n+\t\t\t\t\t  lnk.buf, path->buf);\n+\t\t\tstrbuf_release(&lnk);\n \t\t}\n \t\telse if (S_ISREG(st_template.st_mode)) {\n-\t\t\tif (copy_file(path, template, st_template.st_mode))\n-\t\t\t\tdie_errno(_(\"cannot copy '%s' to '%s'\"), template,\n-\t\t\t\t\t  path);\n+\t\t\tif (copy_file(path->buf, template->buf, st_template.st_mode))\n+\t\t\t\tdie_errno(_(\"cannot copy '%s' to '%s'\"),\n+\t\t\t\t\t  template->buf, path->buf);\n \t\t}\n \t\telse\n-\t\t\terror(_(\"ignoring template %s\"), template);\n+\t\t\terror(_(\"ignoring template %s\"), template->buf);\n \t}\n }\n \n static void copy_templates(const char *template_dir)\n {\n-\tchar path[PATH_MAX];\n-\tchar template_path[PATH_MAX];\n-\tint template_len;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstruct strbuf template_path = STRBUF_INIT;\n+\tsize_t template_len;\n \tDIR *dir;\n-\tconst char *git_dir = get_git_dir();\n-\tint len = strlen(git_dir);\n \tchar *to_free = NULL;\n \n \tif (!template_dir)\n@@ -132,26 +120,23 @@ static void copy_templates(const char *template_dir)\n \t\tfree(to_free);\n \t\treturn;\n \t}\n-\ttemplate_len = strlen(template_dir);\n-\tif (PATH_MAX <= (template_len+strlen(\"/config\")))\n-\t\tdie(_(\"insanely long template path %s\"), template_dir);\n-\tstrcpy(template_path, template_dir);\n-\tif (template_path[template_len-1] != '/') {\n-\t\ttemplate_path[template_len++] = '/';\n-\t\ttemplate_path[template_len] = 0;\n-\t}\n-\tdir = opendir(template_path);\n+\n+\tstrbuf_addstr(&template_path, template_dir);\n+\tstrbuf_complete(&template_path, '/');\n+\ttemplate_len = template_path.len;\n+\n+\tdir = opendir(template_path.buf);\n \tif (!dir) {\n \t\twarning(_(\"templates not found %s\"), template_dir);\n \t\tgoto free_return;\n \t}\n \n \t/* Make sure that template is from the correct vintage */\n-\tstrcpy(template_path + template_len, \"config\");\n+\tstrbuf_addstr(&template_path, \"config\");\n \trepository_format_version = 0;\n \tgit_config_from_file(check_repository_format_version,\n-\t\t\t     template_path, NULL);\n-\ttemplate_path[template_len] = 0;\n+\t\t\t     template_path.buf, NULL);\n+\tstrbuf_setlen(&template_path, template_len);\n \n \tif (repository_format_version &&\n \t    repository_format_version != GIT_REPO_VERSION) {\n@@ -162,17 +147,15 @@ static void copy_templates(const char *template_dir)\n \t\tgoto close_free_return;\n \t}\n \n-\tmemcpy(path, git_dir, len);\n-\tif (len && path[len - 1] != '/')\n-\t\tpath[len++] = '/';\n-\tpath[len] = 0;\n-\tcopy_templates_1(path, len,\n-\t\t\t template_path, template_len,\n-\t\t\t dir);\n+\tstrbuf_addstr(&path, get_git_dir());\n+\tstrbuf_complete(&path, '/');\n+\tcopy_templates_1(&path, &template_path, dir);\n close_free_return:\n \tclosedir(dir);\n free_return:\n \tfree(to_free);\n+\tstrbuf_release(&path);\n+\tstrbuf_release(&template_path);\n }\n \n static int git_init_db_config(const char *k, const char *v, void *cb)\n@@ -199,28 +182,20 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)\n \n static int create_default_files(const char *template_path)\n {\n-\tconst char *git_dir = get_git_dir();\n-\tunsigned len = strlen(git_dir);\n-\tstatic char path[PATH_MAX];\n \tstruct stat st1;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tchar *path;\n \tchar repo_version_string[10];\n \tchar junk[2];\n \tint reinit;\n \tint filemode;\n \n-\tif (len > sizeof(path)-50)\n-\t\tdie(_(\"insane git directory %s\"), git_dir);\n-\tmemcpy(path, git_dir, len);\n-\n-\tif (len && path[len-1] != '/')\n-\t\tpath[len++] = '/';\n-\n \t/*\n \t * Create .git/refs/{heads,tags}\n \t */\n-\tsafe_create_dir(git_path(\"refs\"), 1);\n-\tsafe_create_dir(git_path(\"refs/heads\"), 1);\n-\tsafe_create_dir(git_path(\"refs/tags\"), 1);\n+\tsafe_create_dir(git_path_buf(&buf, \"refs\"), 1);\n+\tsafe_create_dir(git_path_buf(&buf, \"refs/heads\"), 1);\n+\tsafe_create_dir(git_path_buf(&buf, \"refs/tags\"), 1);\n \n \t/* Just look for `init.templatedir` */\n \tgit_config(git_init_db_config, NULL);\n@@ -244,16 +219,16 @@ static int create_default_files(const char *template_path)\n \t */\n \tif (shared_repository) {\n \t\tadjust_shared_perm(get_git_dir());\n-\t\tadjust_shared_perm(git_path(\"refs\"));\n-\t\tadjust_shared_perm(git_path(\"refs/heads\"));\n-\t\tadjust_shared_perm(git_path(\"refs/tags\"));\n+\t\tadjust_shared_perm(git_path_buf(&buf, \"refs\"));\n+\t\tadjust_shared_perm(git_path_buf(&buf, \"refs/heads\"));\n+\t\tadjust_shared_perm(git_path_buf(&buf, \"refs/tags\"));\n \t}\n \n \t/*\n \t * Create the default symlink from \".git/HEAD\" to the \"master\"\n \t * branch, if it does not exist yet.\n \t */\n-\tstrcpy(path + len, \"HEAD\");\n+\tpath = git_path_buf(&buf, \"HEAD\");\n \treinit = (!access(path, R_OK)\n \t\t  || readlink(path, junk, sizeof(junk)-1) != -1);\n \tif (!reinit) {\n@@ -266,10 +241,8 @@ static int create_default_files(const char *template_path)\n \t\t  \"%d\", GIT_REPO_VERSION);\n \tgit_config_set(\"core.repositoryformatversion\", repo_version_string);\n \n-\tpath[len] = 0;\n-\tstrcpy(path + len, \"config\");\n-\n \t/* Check filemode trustability */\n+\tpath = git_path_buf(&buf, \"config\");\n \tfilemode = TEST_FILEMODE;\n \tif (TEST_FILEMODE && !lstat(path, &st1)) {\n \t\tstruct stat st2;\n@@ -290,14 +263,13 @@ static int create_default_files(const char *template_path)\n \t\t/* allow template config file to override the default */\n \t\tif (log_all_ref_updates == -1)\n \t\t    git_config_set(\"core.logallrefupdates\", \"true\");\n-\t\tif (needs_work_tree_config(git_dir, work_tree))\n+\t\tif (needs_work_tree_config(get_git_dir(), work_tree))\n \t\t\tgit_config_set(\"core.worktree\", work_tree);\n \t}\n \n \tif (!reinit) {\n \t\t/* Check if symlink is supported in the work tree */\n-\t\tpath[len] = 0;\n-\t\tstrcpy(path + len, \"tXXXXXX\");\n+\t\tpath = git_path_buf(&buf, \"tXXXXXX\");\n \t\tif (!close(xmkstemp(path)) &&\n \t\t    !unlink(path) &&\n \t\t    !symlink(\"testing\", path) &&\n@@ -308,31 +280,35 @@ static int create_default_files(const char *template_path)\n \t\t\tgit_config_set(\"core.symlinks\", \"false\");\n \n \t\t/* Check if the filesystem is case-insensitive */\n-\t\tpath[len] = 0;\n-\t\tstrcpy(path + len, \"CoNfIg\");\n+\t\tpath = git_path_buf(&buf, \"CoNfIg\");\n \t\tif (!access(path, F_OK))\n \t\t\tgit_config_set(\"core.ignorecase\", \"true\");\n \t\tprobe_utf8_pathname_composition();\n \t}\n \n+\tstrbuf_release(&buf);\n \treturn reinit;\n }\n \n static void create_object_directory(void)\n {\n-\tconst char *object_directory = get_object_directory();\n-\tint len = strlen(object_directory);\n-\tchar *path = xmalloc(len + 40);\n+\tstruct strbuf path = STRBUF_INIT;\n+\tsize_t baselen;\n+\n+\tstrbuf_addstr(&path, get_object_directory());\n+\tbaselen = path.len;\n+\n+\tsafe_create_dir(path.buf, 1);\n \n-\tmemcpy(path, object_directory, len);\n+\tstrbuf_setlen(&path, baselen);\n+\tstrbuf_addstr(&path, \"/pack\");\n+\tsafe_create_dir(path.buf, 1);\n \n-\tsafe_create_dir(object_directory, 1);\n-\tstrcpy(path+len, \"/pack\");\n-\tsafe_create_dir(path, 1);\n-\tstrcpy(path+len, \"/info\");\n-\tsafe_create_dir(path, 1);\n+\tstrbuf_setlen(&path, baselen);\n+\tstrbuf_addstr(&path, \"/info\");\n+\tsafe_create_dir(path.buf, 1);\n \n-\tfree(path);\n+\tstrbuf_release(&path);\n }\n \n int set_git_dir_init(const char *git_dir, const char *real_git_dir,\ndiff --git a/t/t0001-init.sh b/t/t0001-init.sh\nindex 7de8d85..f91bbcf 100755\n--- a/t/t0001-init.sh\n+++ b/t/t0001-init.sh\n@@ -202,8 +202,8 @@ test_expect_success 'init honors global core.sharedRepository' '\n \tx$(git config -f shared-honor-global/.git/config core.sharedRepository)\n '\n \n-test_expect_success 'init rejects insanely long --template' '\n-\ttest_must_fail git init --template=$(printf \"x%09999dx\" 1) test\n+test_expect_success 'init allows insanely long --template' '\n+\tgit init --template=$(printf \"x%09999dx\" 1) test\n '\n \n test_expect_success 'init creates a new directory' '\n-- \n2.6.0.455.ga3f9923\n"},{"id":"271198","messageId":"56133EF1.4020700@web.de","threadId":"40424","inReplyTo":"20151005034313.GA25502@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] precompose_utf8: drop unused variable","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2015-10-06T03:24:33Z","receivedAt":"2015-10-06T03:24:33Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On 2015-10-05 05.43, Jeff King wrote:\n[]\nThe whole series looks good, cleans up my mess and passes t3910.\nThanks for that.\n"}]}