{"thread":{"id":"44625","subject":"[PATCH] making real_path thread-safe","startedAt":"2016-12-05T18:59:17Z","lastAt":"2017-01-09T19:26:35Z","messageCount":83,"participants":["Brandon Williams","Stefan Beller","Junio C Hamano","Ramsay Jones","Torsten Bögershausen","Johannes Sixt","Duy Nguyen","Jacob Keller","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"306945","messageId":"1480964316-99305-1-git-send-email-bmwill@google.com","threadId":"44625","inReplyTo":null,"subject":"[PATCH] making real_path thread-safe","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-05T18:58:35Z","receivedAt":"2016-12-05T18:59:17Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"The goal of this patch is to make the function real_path thread-safe.  To do,\nthe current logic which uses chdir() to do the symlink resolution needed to be\nrewritten to perform the symlink resolution by hand.\n\nI wanted to get the bulk of the work out there for review quickly so\ntechnically this patch doesn't make real_path thread safe yet as it still uses\na static buffer to serve up the resultant path.  On a future iteration I plan\non removing this and pushing ownership of the returned string to the callers.\n\nFor more information on why this change is desirable you can see the following\nthreads:\n\nhttps://public-inbox.org/git/20161129010538.GA121643@google.com/\nhttps://public-inbox.org/git/1480555714-186183-1-git-send-email-bmwill@google.com/\n\nBrandon Williams (1):\n  real_path: make real_path thread-safe\n\n abspath.c | 183 +++++++++++++++++++++++++++++++++++++++++---------------------\n 1 file changed, 122 insertions(+), 61 deletions(-)\n\n-- \n2.8.0.rc3.226.g39d4020\n\n"},{"id":"306946","messageId":"1480964316-99305-2-git-send-email-bmwill@google.com","threadId":"44625","inReplyTo":"1480964316-99305-1-git-send-email-bmwill@google.com","subject":"[PATCH] real_path: make real_path thread-safe","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-05T18:58:36Z","receivedAt":"2016-12-05T18:59:23Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"The current implementation of real_path uses chdir() in order to resolve\nsymlinks.  Unfortunately this isn't thread-safe as chdir() affects a\nprocess as a whole and not just an individual thread.  Instead perform\nthe symlink resolution by hand so that the calls to chdir() can be\nremoved, making real_path reentrant.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 183 +++++++++++++++++++++++++++++++++++++++++---------------------\n 1 file changed, 122 insertions(+), 61 deletions(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex 2825de8..6f546e0 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -11,8 +11,38 @@ int is_directory(const char *path)\n \treturn (!stat(path, &st) && S_ISDIR(st.st_mode));\n }\n \n+/* removes the last path component from 'path' except if 'path' is root */\n+static void strip_last_component(struct strbuf *path)\n+{\n+\tif (path->len > 1) {\n+\t\tchar *last_slash = find_last_dir_sep(path->buf);\n+\t\tstrbuf_setlen(path, last_slash - path->buf);\n+\t}\n+}\n+\n+/* gets the next component in 'remaining' and places it in 'next' */\n+static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n+{\n+\tchar *start = NULL;\n+\tchar *end = NULL;\n+\n+\tstrbuf_reset(next);\n+\n+\t/* look for the next component */\n+\t/* Skip sequences of multiple path-separators */\n+\tfor (start = remaining->buf; is_dir_sep(*start); start++)\n+\t\t/* nothing */;\n+\t/* Find end of the path component */\n+\tfor (end = start; *end && !is_dir_sep(*end); end++)\n+\t\t/* nothing */;\n+\n+\tstrbuf_add(next, start, end - start);\n+\t/* remove the component from 'remaining' */\n+\tstrbuf_remove(remaining, 0, end - remaining->buf);\n+}\n+\n /* We allow \"recursive\" symbolic links. Only within reason, though. */\n-#define MAXDEPTH 5\n+#define MAXSYMLINKS 5\n \n /*\n  * Return the real path (i.e., absolute path, with symlinks resolved\n@@ -21,7 +51,6 @@ int is_directory(const char *path)\n  * absolute_path().)  The return value is a pointer to a static\n  * buffer.\n  *\n- * The input and all intermediate paths must be shorter than MAX_PATH.\n  * The directory part of path (i.e., everything up to the last\n  * dir_sep) must denote a valid, existing directory, but the last\n  * component need not exist.  If die_on_error is set, then die with an\n@@ -33,22 +62,16 @@ int is_directory(const char *path)\n  */\n static const char *real_path_internal(const char *path, int die_on_error)\n {\n-\tstatic struct strbuf sb = STRBUF_INIT;\n+\tstatic struct strbuf resolved = STRBUF_INIT;\n+\tstruct strbuf remaining = STRBUF_INIT;\n+\tstruct strbuf next = STRBUF_INIT;\n+\tstruct strbuf symlink = STRBUF_INIT;\n \tchar *retval = NULL;\n-\n-\t/*\n-\t * If we have to temporarily chdir(), store the original CWD\n-\t * here so that we can chdir() back to it at the end of the\n-\t * function:\n-\t */\n-\tstruct strbuf cwd = STRBUF_INIT;\n-\n-\tint depth = MAXDEPTH;\n-\tchar *last_elem = NULL;\n+\tint num_symlinks = 0;\n \tstruct stat st;\n \n \t/* We've already done it */\n-\tif (path == sb.buf)\n+\tif (path == resolved.buf)\n \t\treturn path;\n \n \tif (!*path) {\n@@ -58,74 +81,112 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tgoto error_out;\n \t}\n \n-\tstrbuf_reset(&sb);\n-\tstrbuf_addstr(&sb, path);\n-\n-\twhile (depth--) {\n-\t\tif (!is_directory(sb.buf)) {\n-\t\t\tchar *last_slash = find_last_dir_sep(sb.buf);\n-\t\t\tif (last_slash) {\n-\t\t\t\tlast_elem = xstrdup(last_slash + 1);\n-\t\t\t\tstrbuf_setlen(&sb, last_slash - sb.buf + 1);\n-\t\t\t} else {\n-\t\t\t\tlast_elem = xmemdupz(sb.buf, sb.len);\n-\t\t\t\tstrbuf_reset(&sb);\n-\t\t\t}\n+\tstrbuf_reset(&resolved);\n+\n+\tif (is_absolute_path(path)) {\n+\t\t/* absolute path; start with only root as being resolved */\n+\t\tstrbuf_addch(&resolved, '/');\n+\t\tstrbuf_addstr(&remaining, path + 1);\n+\t} else {\n+\t\t/* relative path; can use CWD as the initial resolved path */\n+\t\tif (strbuf_getcwd(&resolved)) {\n+\t\t\tif (die_on_error)\n+\t\t\t\tdie_errno(\"Could not get current working directory\");\n+\t\t\telse\n+\t\t\t\tgoto error_out;\n+\t\t}\n+\t\tstrbuf_addstr(&remaining, path);\n+\t}\n+\n+\t/* Iterate over the remaining path components */\n+\twhile (remaining.len > 0) {\n+\t\tget_next_component(&next, &remaining);\n+\n+\t\tif (next.len == 0) {\n+\t\t\tcontinue; /* empty component */\n+\t\t} else if (next.len == 1 && !strcmp(next.buf, \".\")) {\n+\t\t\tcontinue; /* '.' component */\n+\t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n+\t\t\t/* '..' component; strip the last path component */\n+\t\t\tstrip_last_component(&resolved);\n+\t\t\tcontinue;\n \t\t}\n \n-\t\tif (sb.len) {\n-\t\t\tif (!cwd.len && strbuf_getcwd(&cwd)) {\n+\t\t/* append the next component and resolve resultant path */\n+\t\tif (resolved.buf[resolved.len - 1] != '/')\n+\t\t\tstrbuf_addch(&resolved, '/');\n+\t\tstrbuf_addbuf(&resolved, &next);\n+\n+\t\tif (lstat(resolved.buf, &st)) {\n+\t\t\t/* error out unless this was the last component */\n+\t\t\tif (!(errno == ENOENT && !remaining.len)) {\n \t\t\t\tif (die_on_error)\n-\t\t\t\t\tdie_errno(\"Could not get current working directory\");\n+\t\t\t\t\tdie_errno(\"Invalid path '%s'\",\n+\t\t\t\t\t\t  resolved.buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n+\t\t} else if (S_ISLNK(st.st_mode)) {\n+\t\t\tssize_t len;\n+\t\t\tstrbuf_reset(&symlink);\n \n-\t\t\tif (chdir(sb.buf)) {\n+\t\t\tif (num_symlinks++ > MAXSYMLINKS) {\n \t\t\t\tif (die_on_error)\n-\t\t\t\t\tdie_errno(\"Could not switch to '%s'\",\n-\t\t\t\t\t\t  sb.buf);\n+\t\t\t\t\tdie(\"More than %d nested symlinks \"\n+\t\t\t\t\t    \"on path '%s'\", MAXSYMLINKS, path);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n-\t\t}\n-\t\tif (strbuf_getcwd(&sb)) {\n-\t\t\tif (die_on_error)\n-\t\t\t\tdie_errno(\"Could not get current working directory\");\n-\t\t\telse\n-\t\t\t\tgoto error_out;\n-\t\t}\n \n-\t\tif (last_elem) {\n-\t\t\tif (sb.len && !is_dir_sep(sb.buf[sb.len - 1]))\n-\t\t\t\tstrbuf_addch(&sb, '/');\n-\t\t\tstrbuf_addstr(&sb, last_elem);\n-\t\t\tfree(last_elem);\n-\t\t\tlast_elem = NULL;\n-\t\t}\n-\n-\t\tif (!lstat(sb.buf, &st) && S_ISLNK(st.st_mode)) {\n-\t\t\tstruct strbuf next_sb = STRBUF_INIT;\n-\t\t\tssize_t len = strbuf_readlink(&next_sb, sb.buf, 0);\n+\t\t\tlen = strbuf_readlink(&symlink, resolved.buf,\n+\t\t\t\t\t      st.st_size);\n \t\t\tif (len < 0) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid symlink '%s'\",\n-\t\t\t\t\t\t  sb.buf);\n+\t\t\t\t\t\t  resolved.buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n-\t\t\tstrbuf_swap(&sb, &next_sb);\n-\t\t\tstrbuf_release(&next_sb);\n-\t\t} else\n-\t\t\tbreak;\n+\n+\t\t\tif (is_absolute_path(symlink.buf)) {\n+\t\t\t\t/*\n+\t\t\t\t * absolute symlink\n+\t\t\t\t * reset resolved path to root\n+\t\t\t\t */\n+\t\t\t\tstrbuf_setlen(&resolved, 1);\n+\t\t\t} else {\n+\t\t\t\t/*\n+\t\t\t\t * relative symlink\n+\t\t\t\t * strip off the last component since it will\n+\t\t\t\t * be replaced with the contents of the symlink\n+\t\t\t\t */\n+\t\t\t\tstrip_last_component(&resolved);\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * if there are still remaining components to resolve\n+\t\t\t * then append them to symlink\n+\t\t\t */\n+\t\t\tif (remaining.len) {\n+\t\t\t\tstrbuf_addch(&symlink, '/');\n+\t\t\t\tstrbuf_addbuf(&symlink, &remaining);\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * use the symlink as the remaining components that\n+\t\t\t * need to be resloved\n+\t\t\t */\n+\t\t\tstrbuf_swap(&symlink, &remaining);\n+\t\t}\n \t}\n \n-\tretval = sb.buf;\n+\tretval = resolved.buf;\n+\n error_out:\n-\tfree(last_elem);\n-\tif (cwd.len && chdir(cwd.buf))\n-\t\tdie_errno(\"Could not change back to '%s'\", cwd.buf);\n-\tstrbuf_release(&cwd);\n+\t//strbuf_release(&resolved);\n+\tstrbuf_release(&remaining);\n+\tstrbuf_release(&next);\n+\tstrbuf_release(&symlink);\n \n \treturn retval;\n }\n-- \n2.8.0.rc3.226.g39d4020\n\n"},{"id":"306951","messageId":"CAGZ79kauPdE1uiFSvBALkNiwXbnV6d6xhwLdWNQwRir_8rTG6Q@mail.gmail.com","threadId":"44625","inReplyTo":"1480964316-99305-2-git-send-email-bmwill@google.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-12-05T19:57:32Z","receivedAt":"2016-12-05T19:57:39Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, Dec 5, 2016 at 10:58 AM, Brandon Williams <bmwill@google.com> wrote:\n> The current implementation of real_path uses chdir() in order to resolve\n> symlinks.  Unfortunately this isn't thread-safe as chdir() affects a\n> process as a whole and not just an individual thread.  Instead perform\n> the symlink resolution by hand so that the calls to chdir() can be\n> removed, making real_path reentrant.\n>\n> Signed-off-by: Brandon Williams <bmwill@google.com>\n\nThanks for working on this, some comments below:\n\n>\n> +/* removes the last path component from 'path' except if 'path' is root */\n> +static void strip_last_component(struct strbuf *path)\n> +{\n> +       if (path->len > 1) {\n> +               char *last_slash = find_last_dir_sep(path->buf);\n\nWhat happens when there is no dir_sep?\n\n> +/* gets the next component in 'remaining' and places it in 'next' */\n> +static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n> +{\n\nIt's more than just getting it, it also chops it off of 'remaining' ?\n\n\n\n> +       strbuf_reset(&resolved);\n> +\n> +       if (is_absolute_path(path)) {\n> +               /* absolute path; start with only root as being resolved */\n> +               strbuf_addch(&resolved, '/');\n> +               strbuf_addstr(&remaining, path + 1);\n\nThis is where we would wait for input of Windows savy people.\n\n> +       } else {\n> +               /* relative path; can use CWD as the initial resolved path */\n> +               if (strbuf_getcwd(&resolved)) {\n> +                       if (die_on_error)\n> +                               die_errno(\"Could not get current working directory\");\n\nI am looking at xgetcwd, which words it slightly differently.\n\n    if (strbuf_getcwd(&sb))\n        die_errno(_(\"unable to get current working directory\"));\n\nNot sure if aligning them would be a good idea?\n\nGoing by \"git grep die_errno\" as well as our Coding guidelines,\nwe don't want to see capitalized error messages.\n\n> +\n> +               if (next.len == 0) {\n> +                       continue; /* empty component */\n\nwhich means we resolve over path//with//double//slashes just fine,\nas well as /./ parts. :)\n\n>                 }\n>\n> -               if (sb.len) {\n> -                       if (!cwd.len && strbuf_getcwd(&cwd)) {\n> +               /* append the next component and resolve resultant path */\n\n\"resultant\" indicates you have a math background. :)\nBut I had to look it up, I guess it is fine that way,\nthough \"resulting\" may cause less mental friction\nfor non native speakers.\n\n\n> +                       if (!(errno == ENOENT && !remaining.len)) {\n>                                 if (die_on_error)\n> -                                       die_errno(\"Could not get current working directory\");\n> +                                       die_errno(\"Invalid path '%s'\",\n> +                                                 resolved.buf);\n>                                 else\n>                                         goto error_out;\n>                         }\n> +               } else if (S_ISLNK(st.st_mode)) {\n\nAs far as I can tell, we could keep the symlink strbuf\nat a smaller scope here? (I was surprised how many strbufs\nare declared at the beginning of the function)\n\n> +       //strbuf_release(&resolved);\n\nThis is why the cover letter toned down expectations ?\n(no // as comment, maybe remove that line?)\n\nThanks,\nStefan\n"},{"id":"306953","messageId":"CAGZ79katWewdwU3ZDYDj-QZEeersx9XDPZuTdMJG_u_62YwMsg@mail.gmail.com","threadId":"44625","inReplyTo":"1480964316-99305-2-git-send-email-bmwill@google.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-12-05T20:14:16Z","receivedAt":"2016-12-05T20:14:58Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":">  static const char *real_path_internal(const char *path, int die_on_error)\n>  {\n> -       static struct strbuf sb = STRBUF_INIT;\n> +       static struct strbuf resolved = STRBUF_INIT;\n\nAlso by having this static here, it is not quite thread safe, yet.\n\nBy removing the static here we cannot do the early cheap check as:\n\n>         /* We've already done it */\n> -       if (path == sb.buf)\n> +       if (path == resolved.buf)\n>                 return path;\n\nI wonder how often we run into this case; are there some callers explicitly\nrelying on real_path_internal being cheap for repeated calls?\n(Maybe run the test suite with this early return instrumented? Not sure how\nto assess the impact of removing the cheap out return optimization)\n\nThe long tail (i.e. the actual functionality) should actually be\nfaster, I'd imagine\nas we do less than with using chdir.\n\nThanks,\nStefan\n"},{"id":"306954","messageId":"20161205201619.GE68588@google.com","threadId":"44625","inReplyTo":"CAGZ79katWewdwU3ZDYDj-QZEeersx9XDPZuTdMJG_u_62YwMsg@mail.gmail.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-05T20:16:19Z","receivedAt":"2016-12-05T20:16:26Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 12/05, Stefan Beller wrote:\n> >  static const char *real_path_internal(const char *path, int die_on_error)\n> >  {\n> > -       static struct strbuf sb = STRBUF_INIT;\n> > +       static struct strbuf resolved = STRBUF_INIT;\n> \n> Also by having this static here, it is not quite thread safe, yet.\n> \n> By removing the static here we cannot do the early cheap check as:\n> \n> >         /* We've already done it */\n> > -       if (path == sb.buf)\n> > +       if (path == resolved.buf)\n> >                 return path;\n> \n> I wonder how often we run into this case; are there some callers explicitly\n> relying on real_path_internal being cheap for repeated calls?\n> (Maybe run the test suite with this early return instrumented? Not sure how\n> to assess the impact of removing the cheap out return optimization)\n> \n> The long tail (i.e. the actual functionality) should actually be\n> faster, I'd imagine\n> as we do less than with using chdir.\n\nDepends on how expensive the chdir calls were.  And I'm working to get\nrid of the static buffer.  Just need have the callers own the memory\nfirst.\n\n-- \nBrandon Williams\n"},{"id":"306955","messageId":"20161205201237.GD68588@google.com","threadId":"44625","inReplyTo":"CAGZ79kauPdE1uiFSvBALkNiwXbnV6d6xhwLdWNQwRir_8rTG6Q@mail.gmail.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-05T20:12:37Z","receivedAt":"2016-12-05T20:21:50Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 12/05, Stefan Beller wrote:\n> > +/* removes the last path component from 'path' except if 'path' is root */\n> > +static void strip_last_component(struct strbuf *path)\n> > +{\n> > +       if (path->len > 1) {\n> > +               char *last_slash = find_last_dir_sep(path->buf);\n> \n> What happens when there is no dir_sep?\n\nThere should always be a dir_sep since that only gets run if the passed\nin path at least contains root '/'\n\n> \n> > +/* gets the next component in 'remaining' and places it in 'next' */\n> > +static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n> > +{\n> \n> It's more than just getting it, it also chops it off of 'remaining' ?\n\nTrue, I can update the comment to reflect that.\n\n> > +       } else {\n> > +               /* relative path; can use CWD as the initial resolved path */\n> > +               if (strbuf_getcwd(&resolved)) {\n> > +                       if (die_on_error)\n> > +                               die_errno(\"Could not get current working directory\");\n> \n> I am looking at xgetcwd, which words it slightly differently.\n> \n>     if (strbuf_getcwd(&sb))\n>         die_errno(_(\"unable to get current working directory\"));\n> \n> Not sure if aligning them would be a good idea?\n> \n> Going by \"git grep die_errno\" as well as our Coding guidelines,\n> we don't want to see capitalized error messages.\n\nK I can use the other msg.\n\n> >\n> > -               if (sb.len) {\n> > -                       if (!cwd.len && strbuf_getcwd(&cwd)) {\n> > +               /* append the next component and resolve resultant path */\n> \n> \"resultant\" indicates you have a math background. :)\n> But I had to look it up, I guess it is fine that way,\n> though \"resulting\" may cause less mental friction\n> for non native speakers.\n> \n> \n> > +                       if (!(errno == ENOENT && !remaining.len)) {\n> >                                 if (die_on_error)\n> > -                                       die_errno(\"Could not get current working directory\");\n> > +                                       die_errno(\"Invalid path '%s'\",\n> > +                                                 resolved.buf);\n> >                                 else\n> >                                         goto error_out;\n> >                         }\n> > +               } else if (S_ISLNK(st.st_mode)) {\n> \n> As far as I can tell, we could keep the symlink strbuf\n> at a smaller scope here? (I was surprised how many strbufs\n> are declared at the beginning of the function)\n\nYeah I can push it down in scope.  There will be a bit more allocation\nchurn with the smaller scope but multiple symlinks should be rare?\nAlternatively the 'next' buffer can be reused...I decided against that\ninitially due to readability.  And yes, lots of string manipulation\nrequires lots of strbufs :)\n\n> > +       //strbuf_release(&resolved);\n> \n> This is why the cover letter toned down expectations ?\n> (no // as comment, maybe remove that line?)\n\nyep.  It will be added back in though once the callers to real_path take\nownership of the memory.\n\n-- \nBrandon Williams\n"},{"id":"306960","messageId":"CAGZ79kbNMS-Hy=6y6EupcnwL58bhM=pesA8Mk_G+tjkL4yNNVA@mail.gmail.com","threadId":"44625","inReplyTo":"20161205201237.GD68588@google.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-12-05T20:38:25Z","receivedAt":"2016-12-05T20:38:31Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, Dec 5, 2016 at 12:12 PM, Brandon Williams <bmwill@google.com> wrote:\n\n>> > +       if (path->len > 1) {\n>> > +               char *last_slash = find_last_dir_sep(path->buf);\n>>\n>> What happens when there is no dir_sep?\n>\n> There should always be a dir_sep since that only gets run if the passed\n> in path at least contains root '/'\n\nOh, sure, that makes sense. When porting/running this on Windows, does\nthe assumption still hold?\n\n>>     if (strbuf_getcwd(&sb))\n>>         die_errno(_(\"unable to get current working directory\"));\n>>\n>> Not sure if aligning them would be a good idea?\n>>\n>> Going by \"git grep die_errno\" as well as our Coding guidelines,\n>> we don't want to see capitalized error messages.\n>\n> K I can use the other msg.\n\nWell this wasn't a rhetorical question, but I was genuine wondering\nif that was worth it.\n\nWhen having different error messages in different places,\nit makes debugging easier, because you have fewer starting points.\n\nBut this function is deep down in the stack, such that you would expect\nother error messages to also show up , so I dunno.\n\n>> > +               } else if (S_ISLNK(st.st_mode)) {\n>>\n>> As far as I can tell, we could keep the symlink strbuf\n>> at a smaller scope here? (I was surprised how many strbufs\n>> are declared at the beginning of the function)\n>\n> Yeah I can push it down in scope.  There will be a bit more allocation\n> churn with the smaller scope but multiple symlinks should be rare?\n> Alternatively the 'next' buffer can be reused...I decided against that\n> initially due to readability.\n\nI'd second to not reuse 'next'. :)\nI guess we could keep the less churn-y version then.\n\n>  And yes, lots of string manipulation\n> requires lots of strbufs :)\n"},{"id":"307085","messageId":"xmqqtwagy65q.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"1480964316-99305-2-git-send-email-bmwill@google.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-06T23:44:17Z","receivedAt":"2016-12-06T23:44:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> +/* removes the last path component from 'path' except if 'path' is root */\n> +static void strip_last_component(struct strbuf *path)\n> +{\n> +\tif (path->len > 1) {\n> +\t\tchar *last_slash = find_last_dir_sep(path->buf);\n> +\t\tstrbuf_setlen(path, last_slash - path->buf);\n> +\t}\n> +}\n\nYou use find_last_dir_sep() which takes care of \"Windows uses\nbackslash\" issue.  Is this function expected to be fed something\nlike \"C:\\My Files\\foo.txt\" and more importantly \"C:\\My Files\"?  Or\nis that handled by a lot higher level up in the callchain?  I am\nreacting the comparison of path->len and 1 here.\n\nAlso is the input expected to be normalized?  Are we expected to be\nfed something like \"/a//b/./c/../d/e\" and react sensibly, or is that\nhandled by a lot higher level up in the callchain?\n\n> +/* gets the next component in 'remaining' and places it in 'next' */\n> +static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n> +{\n> +\tchar *start = NULL;\n> +\tchar *end = NULL;\n> +\n> +\tstrbuf_reset(next);\n> +\n> +\t/* look for the next component */\n> +\t/* Skip sequences of multiple path-separators */\n> +\tfor (start = remaining->buf; is_dir_sep(*start); start++)\n> +\t\t/* nothing */;\n\nStyle:\n\t\t; /* nothing */\n\n> +\t/* Find end of the path component */\n> +\tfor (end = start; *end && !is_dir_sep(*end); end++)\n> +\t\t/* nothing */;\n> +\n> +\tstrbuf_add(next, start, end - start);\n\nOK, so this was given \"///foo/bar\" in \"remaining\" and appended\n'foo/' to \"next\".  I.e. deduping of slashes is handled here.\n\nPOSIX cares about treating \"//\" at the very beginning of the path\nspecially.  Is that supposed to be handled here, or by a lot higher\nlevel up in the callchain?\n\n> +\t/* remove the component from 'remaining' */\n> +\tstrbuf_remove(remaining, 0, end - remaining->buf);\n> +}\n> +\n>  /* We allow \"recursive\" symbolic links. Only within reason, though. */\n> -#define MAXDEPTH 5\n> +#define MAXSYMLINKS 5\n>  \n>  /*\n>   * Return the real path (i.e., absolute path, with symlinks resolved\n> @@ -21,7 +51,6 @@ int is_directory(const char *path)\n>   * absolute_path().)  The return value is a pointer to a static\n>   * buffer.\n>   *\n>   * The directory part of path (i.e., everything up to the last\n>   * dir_sep) must denote a valid, existing directory, but the last\n>   * component need not exist.  If die_on_error is set, then die with an\n> @@ -33,22 +62,16 @@ int is_directory(const char *path)\n>   */\n>  static const char *real_path_internal(const char *path, int die_on_error)\n>  {\n> +\tstatic struct strbuf resolved = STRBUF_INIT;\n\nThis being 'static' would probably mean that this is not reentrant,\nwhich goes against the title of the patch.\n"},{"id":"307090","messageId":"20161207001018.GD103573@google.com","threadId":"44625","inReplyTo":"xmqqtwagy65q.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-07T00:10:18Z","receivedAt":"2016-12-07T00:10:25Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 12/06, Junio C Hamano wrote:\n> Brandon Williams <bmwill@google.com> writes:\n> \n> > +/* removes the last path component from 'path' except if 'path' is root */\n> > +static void strip_last_component(struct strbuf *path)\n> > +{\n> > +\tif (path->len > 1) {\n> > +\t\tchar *last_slash = find_last_dir_sep(path->buf);\n> > +\t\tstrbuf_setlen(path, last_slash - path->buf);\n> > +\t}\n> > +}\n> \n> You use find_last_dir_sep() which takes care of \"Windows uses\n> backslash\" issue.  Is this function expected to be fed something\n> like \"C:\\My Files\\foo.txt\" and more importantly \"C:\\My Files\"?  Or\n> is that handled by a lot higher level up in the callchain?  I am\n> reacting the comparison of path->len and 1 here.\n\nThis function should accept both absolute and relative paths, which\nmeans it should probably accept \"C:\\My Files\".  I wasn't thinking about\nwindows 100% of the time while writing this so I'm hoping that a windows\nexpert will point things like this out to me :).  So in reality this\ncheck shouldn't be (path->len > 1) but rather some function is_only_root\nwhich would check if the strbuf only contains a string which looks like\nroot.\n\n> \n> Also is the input expected to be normalized?  Are we expected to be\n> fed something like \"/a//b/./c/../d/e\" and react sensibly, or is that\n> handled by a lot higher level up in the callchain?\n\nYes it should handle paths like that sensibly.\n\n> > +/* gets the next component in 'remaining' and places it in 'next' */\n> > +static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n> > +{\n> > +\tchar *start = NULL;\n> > +\tchar *end = NULL;\n> > +\n> > +\tstrbuf_reset(next);\n> > +\n> > +\t/* look for the next component */\n> > +\t/* Skip sequences of multiple path-separators */\n> > +\tfor (start = remaining->buf; is_dir_sep(*start); start++)\n> > +\t\t/* nothing */;\n> \n> Style:\n> \t\t; /* nothing */\n\nk will fix.\n\n> \n> > +\t/* Find end of the path component */\n> > +\tfor (end = start; *end && !is_dir_sep(*end); end++)\n> > +\t\t/* nothing */;\n> > +\n> > +\tstrbuf_add(next, start, end - start);\n> \n> OK, so this was given \"///foo/bar\" in \"remaining\" and appended\n> 'foo/' to \"next\".  I.e. deduping of slashes is handled here.\n> \n> POSIX cares about treating \"//\" at the very beginning of the path\n> specially.  Is that supposed to be handled here, or by a lot higher\n> level up in the callchain?\n\nWhat exactly does \"//\" mean in this context? (I'm just naive in this\narea)\n\n> \n> > +\t/* remove the component from 'remaining' */\n> > +\tstrbuf_remove(remaining, 0, end - remaining->buf);\n> > +}\n> > +\n> >  /* We allow \"recursive\" symbolic links. Only within reason, though. */\n> > -#define MAXDEPTH 5\n> > +#define MAXSYMLINKS 5\n> >  \n> >  /*\n> >   * Return the real path (i.e., absolute path, with symlinks resolved\n> > @@ -21,7 +51,6 @@ int is_directory(const char *path)\n> >   * absolute_path().)  The return value is a pointer to a static\n> >   * buffer.\n> >   *\n> >   * The directory part of path (i.e., everything up to the last\n> >   * dir_sep) must denote a valid, existing directory, but the last\n> >   * component need not exist.  If die_on_error is set, then die with an\n> > @@ -33,22 +62,16 @@ int is_directory(const char *path)\n> >   */\n> >  static const char *real_path_internal(const char *path, int die_on_error)\n> >  {\n> > +\tstatic struct strbuf resolved = STRBUF_INIT;\n> \n> This being 'static' would probably mean that this is not reentrant,\n> which goes against the title of the patch.\n\nYes I mentioned in the cover letter that this was the one last thing\nthat needed to be changed to make it reentrant.  I wanted to get this\nout for review sooner since this is the biggest change (getting rid of\nthe chdir() calls) and dropping the static here would just require\nmaking the callers own the memory which should hopefully be an easy\nrefactor.  It just would have taken a bit more time to actually do that\nrefactor now.  I'm slowly working on it while this is being reviewed and\ncould be ready to send out when rerolling this patch.  Sorry for having\na slightly misleading patch title :)\n\n-- \nBrandon Williams\n"},{"id":"307093","messageId":"b73e61f8-0cff-b33e-118a-e530d367c94c@ramsayjones.plus.com","threadId":"44625","inReplyTo":"20161207001018.GD103573@google.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2016-12-07T01:12:25Z","receivedAt":"2016-12-07T01:12:38Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 07/12/16 00:10, Brandon Williams wrote:\n> On 12/06, Junio C Hamano wrote:\n>> POSIX cares about treating \"//\" at the very beginning of the path\n>> specially.  Is that supposed to be handled here, or by a lot higher\n>> level up in the callchain?\n> \n> What exactly does \"//\" mean in this context? (I'm just naive in this\n> area)\n\nThis refers to a UNC path (ie Universal Naming Convention) which\nis used to refer to servers, printers and other 'network resources'.\nAlthough this started (many moons ago) in unix, it isn't used too\nmuch outside of windows networks! (where it is usually denoted by\n\\\\servername\\path).\n\nYou can see the relics of unix UNC paths if you look at the wording\nfor basename() in the POSIX standard. Note the special treatment of\nthe path which 'is exactly \"//\"', see http://pubs.opengroup.org/onlinepubs/009695399/functions/basename.html\n\nATB,\nRamsay Jones\n"},{"id":"307151","messageId":"20161207201409.GA19743@tb-raspi","threadId":"44625","inReplyTo":"b73e61f8-0cff-b33e-118a-e530d367c94c@ramsayjones.plus.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2016-12-07T20:14:09Z","receivedAt":"2016-12-07T20:15:31Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On Wed, Dec 07, 2016 at 01:12:25AM +0000, Ramsay Jones wrote:\n> \n> \n> On 07/12/16 00:10, Brandon Williams wrote:\n> > On 12/06, Junio C Hamano wrote:\n> >> POSIX cares about treating \"//\" at the very beginning of the path\n> >> specially.  Is that supposed to be handled here, or by a lot higher\n> >> level up in the callchain?\n> > \n> > What exactly does \"//\" mean in this context? (I'm just naive in this\n> > area)\n> \n> This refers to a UNC path (ie Universal Naming Convention) which\n> is used to refer to servers, printers and other 'network resources'.\n> Although this started (many moons ago) in unix, it isn't used too\n> much outside of windows networks! (where it is usually denoted by\n> \\\\servername\\path).\n> \n> You can see the relics of unix UNC paths if you look at the wording\n> for basename() in the POSIX standard. Note the special treatment of\n> the path which 'is exactly \"//\"', see http://pubs.opengroup.org/onlinepubs/009695399/functions/basename.html\n> \n> ATB,\n> Ramsay Jones\n\nPlease allow one more comment about UNC:\nThey are used under Windows, and typically wotk together with Git.\nOne breakage between 2.10 and 2.11 has been observed, saying that\npushing to \\\\SERVER\\SHARE\\DIRECTORY does not work any more.\n\nIt has been reported under\n \"git 2.11.0 error when pushing to remote located on a windows share\"\nboth here and \nhere:\nhttps://github.com/git-for-windows/git/issues/979#issuecomment-264816175\n\nI don't have a Windows box at the moment, and I don't know if the\nbreakage was introduced by changes in real_patyh().\n\nBut in any case it seems that e.g.\n//SEFVER/SHARE/DIR1/DIR2/..\nmust be converted into\n//SEFVER/SHARE/DIR1\n\nand \n\\\\SEFVER\\SHARE\\DIR1\\DIR2\\..\nmust be converted into\n\\\\SEFVER\\SHARE\\DIR1\n\n\n\n\n"},{"id":"307156","messageId":"xmqqtwafwkdt.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"20161207201409.GA19743@tb-raspi","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-07T20:32:14Z","receivedAt":"2016-12-07T20:37:01Z","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> But in any case it seems that e.g.\n> //SEFVER/SHARE/DIR1/DIR2/..\n> must be converted into\n> //SEFVER/SHARE/DIR1\n>\n> and \n> \\\\SEFVER\\SHARE\\DIR1\\DIR2\\..\n> must be converted into\n> \\\\SEFVER\\SHARE\\DIR1\n\nAdditional questions that may be interesting are:\n\n    //A/B/../C\t\tis it //A/C?  is it an error?\n    //A/B/../../C/D\tis it //C/D?  is it an error?\n\n"},{"id":"307157","messageId":"7d968fd8-a92d-efd3-ce67-7de6049b6d56@kdbg.org","threadId":"44625","inReplyTo":"20161207001018.GD103573@google.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2016-12-07T20:43:07Z","receivedAt":"2016-12-07T20:43:15Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 07.12.2016 um 01:10 schrieb Brandon Williams:\n> This function should accept both absolute and relative paths, which\n> means it should probably accept \"C:\\My Files\".  I wasn't thinking about\n> windows 100% of the time while writing this so I'm hoping that a windows\n> expert will point things like this out to me :).\n\n;)\n\nWith this patch, the test suite fails at the very first git init call:\n\nD:\\Src\\mingw-git\\t>sh t0000-basic.sh -v -i -x\nfatal: Invalid path '/:': No such file or directory\nerror: cannot run git init -- have you built things yet?\nFATAL: Unexpected exit with code 1\n\nI haven't dug further, yet.\n\n-- Hannes\n\n"},{"id":"307179","messageId":"20161207221335.GA116201@google.com","threadId":"44625","inReplyTo":"xmqqtwafwkdt.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-07T22:13:35Z","receivedAt":"2016-12-07T22:13:56Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 12/07, Junio C Hamano wrote:\n> Torsten Bögershausen <tboegi@web.de> writes:\n> \n> > But in any case it seems that e.g.\n> > //SEFVER/SHARE/DIR1/DIR2/..\n> > must be converted into\n> > //SEFVER/SHARE/DIR1\n> >\n> > and \n> > \\\\SEFVER\\SHARE\\DIR1\\DIR2\\..\n> > must be converted into\n> > \\\\SEFVER\\SHARE\\DIR1\n> \n> Additional questions that may be interesting are:\n> \n>     //A/B/../C\t\tis it //A/C?  is it an error?\n>     //A/B/../../C/D\tis it //C/D?  is it an error?\n> \n\n\nAlso is //.. the same as //?  I would assume so since /.. is /\n\n-- \nBrandon Williams\n"},{"id":"307180","messageId":"20161207222927.GB116201@google.com","threadId":"44625","inReplyTo":"7d968fd8-a92d-efd3-ce67-7de6049b6d56@kdbg.org","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-07T22:29:27Z","receivedAt":"2016-12-07T22:29:58Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 12/07, Johannes Sixt wrote:\n> Am 07.12.2016 um 01:10 schrieb Brandon Williams:\n> >This function should accept both absolute and relative paths, which\n> >means it should probably accept \"C:\\My Files\".  I wasn't thinking about\n> >windows 100% of the time while writing this so I'm hoping that a windows\n> >expert will point things like this out to me :).\n> \n> ;)\n> \n> With this patch, the test suite fails at the very first git init call:\n> \n> D:\\Src\\mingw-git\\t>sh t0000-basic.sh -v -i -x\n> fatal: Invalid path '/:': No such file or directory\n> error: cannot run git init -- have you built things yet?\n> FATAL: Unexpected exit with code 1\n> \n> I haven't dug further, yet.\n> \n> -- Hannes\n> \n\nThanks for providing me with the error.  Instead of assuming root is \"/\"\nI'll need to extract what root is from an absolute path.  Aside from\nwhat root looks like, do most other path constructs behave similarly in\nunix and windows? (like \"..\" and \".\" as examples)\n\nSince I don't really have a windows machine to test things it might be\nslightly difficult to get everything correct quickly but hopefully we can\nget this working :)\n\n-- \nBrandon Williams\n"},{"id":"307221","messageId":"20161208075555.GA23595@tb-raspi","threadId":"44625","inReplyTo":"20161207221335.GA116201@google.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2016-12-08T07:55:55Z","receivedAt":"2016-12-08T07:56:17Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On Wed, Dec 07, 2016 at 02:13:35PM -0800, Brandon Williams wrote:\n> On 12/07, Junio C Hamano wrote:\n> > Torsten Bögershausen <tboegi@web.de> writes:\n> > \n> > > But in any case it seems that e.g.\n> > > //SEFVER/SHARE/DIR1/DIR2/..\n> > > must be converted into\n> > > //SEFVER/SHARE/DIR1\n> > >\n> > > and \n> > > \\\\SEFVER\\SHARE\\DIR1\\DIR2\\..\n> > > must be converted into\n> > > \\\\SEFVER\\SHARE\\DIR1\n> > \n> > Additional questions that may be interesting are:\n> > \n> >     //A/B/../C\t\tis it //A/C?  is it an error?\nYes, at least under Windows.\nIf I have e.g. a Raspi with SAMBA, I can put a git Repository here: \n\n//raspi/torsten/projects/git\nIf I use\ngit push //raspi/torsten/../junio/projects/git\nthat should be an error.\n\n> >     //A/B/../../C/D\tis it //C/D?  is it an error?\n> > \n\nSame for\ngit push /raspi/../raspi2/torsten//projects/git\n\n\n> \n> \n> Also is //.. the same as //?  I would assume so since /.. is /\n> \nUnder Windows //.. is simply illegal, I would say.\nThe documentation here\nhttps://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx\n\nMentions these 2 examples, what to feed into the WIN32 file API:\n\na)\n\\\\?\\D:\\very-long-path\n\nb)\n\\\\server\\share\\path\\file\"\n\nc)\n\"\\\\?\\UNC\\server\\share\\path\" \n\nSo whatever we do, the \"..\" resoltion is only allowed to look at \n\"very-long-path\" or \"path\".\n\nSome conversion may be done in mingw.c:\nhttps://github.com/github/git-msysgit/blob/master/compat/mingw.c\nSo what I understand, '/' in Git are already converted into '\\' if needed ?\n\nIt seams that we may wnat a function get_start_of_path(uncpath),\nwhich returns:\n\nget_start_of_path_win(\"//?/D:/very-long-path\")         \"/very-long-path\" \nget_start_of_path_win(\"//server/share/path/file\")      \"/path/file\"\nget_start_of_path_win(\"//?/UNC/server/share/path\")     \"/path\"\n(I don't know if we need the variant with '\\', but is would'n hurt):\nget_start_of_path_win(\"\\\\\\\\?\\\\D:\\\\very-long-path\")         \"\\\\very-long-path\" \nget_start_of_path_win(\"\\\\\\\\server\\\\share\\\\path\\\\file\")      \"\\\\path\\\\file\"\nget_start_of_path_win(\"\\\\\\\\?\\\\UNC\\\\server\\\\share\\\\path\")     \"\\\\path\"\n\nThen the non-windows version could simply return\nget_start_of_path_non_win(something)     something\n\nDoes this make sense ?\n\n\n \n\n\n"},{"id":"307224","messageId":"CACsJy8BdUsGD-Bx=-qiP3cWt2AjwD1P3NTbqEnjnKa1v0TvySQ@mail.gmail.com","threadId":"44625","inReplyTo":"20161205201619.GE68588@google.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-12-08T09:41:39Z","receivedAt":"2016-12-08T09:42:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Dec 6, 2016 at 3:16 AM, Brandon Williams <bmwill@google.com> wrote:\n> On 12/05, Stefan Beller wrote:\n>> >  static const char *real_path_internal(const char *path, int die_on_error)\n>> >  {\n>> > -       static struct strbuf sb = STRBUF_INIT;\n>> > +       static struct strbuf resolved = STRBUF_INIT;\n>>\n>> Also by having this static here, it is not quite thread safe, yet.\n>>\n>> By removing the static here we cannot do the early cheap check as:\n>>\n>> >         /* We've already done it */\n>> > -       if (path == sb.buf)\n>> > +       if (path == resolved.buf)\n>> >                 return path;\n>>\n>> I wonder how often we run into this case; are there some callers explicitly\n>> relying on real_path_internal being cheap for repeated calls?\n>> (Maybe run the test suite with this early return instrumented? Not sure how\n>> to assess the impact of removing the cheap out return optimization)\n>>\n>> The long tail (i.e. the actual functionality) should actually be\n>> faster, I'd imagine\n>> as we do less than with using chdir.\n>\n> Depends on how expensive the chdir calls were.  And I'm working to get\n> rid of the static buffer.  Just need have the callers own the memory\n> first.\n\nI suggest you turn this real_path_internal into strbuf_real_path. In\nother words, it takes a strbuf and writes the result there, allocating\nmemory if needed.\n\nThis function can replace the two strbuf_addstr(..., real_path(..));\nwe have in setup.c and sha1_file.c. real_path() can own a static\nstrbuf buffer to retain current behavior. We could also have a new\nwrapper real_pathdup() around strbuf_real_path(), which can replace 9\ninstances of xstrdup(real_path(...)) (and Stefan is adding a few more;\nthat's what led me back to these mails)\n-- \nDuy\n"},{"id":"307236","messageId":"1767f01a-4125-d99b-37db-3f4a56aaa28a@kdbg.org","threadId":"44625","inReplyTo":"20161207222927.GB116201@google.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2016-12-08T11:32:46Z","receivedAt":"2016-12-08T11:33:38Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 07.12.2016 um 23:29 schrieb Brandon Williams:\n> Instead of assuming root is \"/\"\n> I'll need to extract what root is from an absolute path.  Aside from\n> what root looks like, do most other path constructs behave similarly in\n> unix and windows? (like \"..\" and \".\" as examples)\n\nYes, .. and . work the same way, except that they cannot appear in the \n\\\\server\\share part. I also think that .. does not cancel these parts.\n\nAs long as you use is_absolute_path() and do not simplify path \ncomponents before offset_1st_component(), you should be on the safe side.\n\n> Since I don't really have a windows machine to test things it might be\n> slightly difficult to get everything correct quickly but hopefully we can\n> get this working :)\n\nI'll lend a hand, of course, as time permits.\n\n-- Hannes\n\n"},{"id":"307264","messageId":"xmqqwpfatl8a.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"1767f01a-4125-d99b-37db-3f4a56aaa28a@kdbg.org","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-08T16:54:29Z","receivedAt":"2016-12-08T16:54:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n> Am 07.12.2016 um 23:29 schrieb Brandon Williams:\n>> Instead of assuming root is \"/\"\n>> I'll need to extract what root is from an absolute path.  Aside from\n>> what root looks like, do most other path constructs behave similarly in\n>> unix and windows? (like \"..\" and \".\" as examples)\n>\n> Yes, .. and . work the same way, except that they cannot appear in the\n> \\\\server\\share part. I also think that .. does not cancel these parts.\n>\n> As long as you use is_absolute_path() and do not simplify path\n> components before offset_1st_component(), you should be on the safe\n> side.\n>\n>> Since I don't really have a windows machine to test things it might be\n>> slightly difficult to get everything correct quickly but hopefully we can\n>> get this working :)\n>\n> I'll lend a hand, of course, as time permits.\n\nThanks, as always, for working well together ;-).\n"},{"id":"307268","messageId":"20161208175048.GN116201@google.com","threadId":"44625","inReplyTo":"CACsJy8BdUsGD-Bx=-qiP3cWt2AjwD1P3NTbqEnjnKa1v0TvySQ@mail.gmail.com","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-08T17:50:48Z","receivedAt":"2016-12-08T17:51:01Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 12/08, Duy Nguyen wrote:\n> On Tue, Dec 6, 2016 at 3:16 AM, Brandon Williams <bmwill@google.com> wrote:\n> > On 12/05, Stefan Beller wrote:\n> >> >  static const char *real_path_internal(const char *path, int die_on_error)\n> >> >  {\n> >> > -       static struct strbuf sb = STRBUF_INIT;\n> >> > +       static struct strbuf resolved = STRBUF_INIT;\n> >>\n> >> Also by having this static here, it is not quite thread safe, yet.\n> >>\n> >> By removing the static here we cannot do the early cheap check as:\n> >>\n> >> >         /* We've already done it */\n> >> > -       if (path == sb.buf)\n> >> > +       if (path == resolved.buf)\n> >> >                 return path;\n> >>\n> >> I wonder how often we run into this case; are there some callers explicitly\n> >> relying on real_path_internal being cheap for repeated calls?\n> >> (Maybe run the test suite with this early return instrumented? Not sure how\n> >> to assess the impact of removing the cheap out return optimization)\n> >>\n> >> The long tail (i.e. the actual functionality) should actually be\n> >> faster, I'd imagine\n> >> as we do less than with using chdir.\n> >\n> > Depends on how expensive the chdir calls were.  And I'm working to get\n> > rid of the static buffer.  Just need have the callers own the memory\n> > first.\n> \n> I suggest you turn this real_path_internal into strbuf_real_path. In\n> other words, it takes a strbuf and writes the result there, allocating\n> memory if needed.\n> \n> This function can replace the two strbuf_addstr(..., real_path(..));\n> we have in setup.c and sha1_file.c. real_path() can own a static\n> strbuf buffer to retain current behavior. We could also have a new\n> wrapper real_pathdup() around strbuf_real_path(), which can replace 9\n> instances of xstrdup(real_path(...)) (and Stefan is adding a few more;\n> that's what led me back to these mails)\n\nSounds like a plan, thanks for the advice.\n\n-- \nBrandon Williams\n"},{"id":"307277","messageId":"6cae7549-72f2-e591-ad48-28d449a62caf@kdbg.org","threadId":"44625","inReplyTo":"20161208075555.GA23595@tb-raspi","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2016-12-08T18:41:45Z","receivedAt":"2016-12-08T18:41:52Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 08.12.2016 um 08:55 schrieb Torsten Bögershausen:\n> Some conversion may be done in mingw.c:\n> https://github.com/github/git-msysgit/blob/master/compat/mingw.c\n> So what I understand, '/' in Git are already converted into '\\' if needed ?\n\nOnly if needed, and there are not many places where this is the case. \n(Actually, I can't find one place where we do.) In particular, typical \nfile accesses does not require the conversion.\n\n> It seams that we may wnat a function get_start_of_path(uncpath),\n> which returns:\n>\n> get_start_of_path_win(\"//?/D:/very-long-path\")         \"/very-long-path\"\n\nWe have offset_1st_component().\n\n-- Hannes\n\n"},{"id":"307299","messageId":"20161208190242.GA63154@google.com","threadId":"44625","inReplyTo":"6cae7549-72f2-e591-ad48-28d449a62caf@kdbg.org","subject":"Re: [PATCH] real_path: make real_path thread-safe","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-08T19:02:42Z","receivedAt":"2016-12-08T19:02:49Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 12/08, Johannes Sixt wrote:\n> Am 08.12.2016 um 08:55 schrieb Torsten Bögershausen:\n> >Some conversion may be done in mingw.c:\n> >https://github.com/github/git-msysgit/blob/master/compat/mingw.c\n> >So what I understand, '/' in Git are already converted into '\\' if needed ?\n> \n> Only if needed, and there are not many places where this is the\n> case. (Actually, I can't find one place where we do.) In particular,\n> typical file accesses does not require the conversion.\n\nThat's convenient, that way I don't have to worry about using '\\' as a\ndirectory separator instead of '/'.\n\n> \n> >It seams that we may wnat a function get_start_of_path(uncpath),\n> >which returns:\n> >\n> >get_start_of_path_win(\"//?/D:/very-long-path\")         \"/very-long-path\"\n> \n> We have offset_1st_component().\n\nThanks for letting me know this function exists, that makes my job a bit\neasier.\n\n-- \nBrandon Williams\n"},{"id":"307313","messageId":"1481241494-6861-1-git-send-email-bmwill@google.com","threadId":"44625","inReplyTo":"1480964316-99305-1-git-send-email-bmwill@google.com","subject":"[PATCH v2 0/4] road to reentrant real_path","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-08T23:58:10Z","receivedAt":"2016-12-08T23:58:39Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Thanks for all of the comments on v1 of the series.  Hopefully this series\naddresses the issues with windows and actually passes the first test :)\n\nSome changes in v2:\n* the 1st component of a path should now be handled correctly on windows as well\n  as unix.\n* Pushed the static strbuf to the callers of real_path_internal\n* renamed real_path_internal to strbuf_realpath\n* added real_pathdup\n* migrated some callers of real_path to real_pathdup and strbuf_realpath\n\nBrandon Williams (4):\n  real_path: resolve symlinks by hand\n  real_path: convert real_path_internal to strbuf_realpath\n  real_path: create real_pathdup\n  real_path: have callers use real_pathdup and strbuf_realpath\n\n abspath.c         | 215 ++++++++++++++++++++++++++++++++++++------------------\n builtin/init-db.c |   6 +-\n cache.h           |   3 +\n environment.c     |   2 +-\n setup.c           |  15 ++--\n sha1_file.c       |   2 +-\n submodule.c       |   2 +-\n transport.c       |   2 +-\n worktree.c        |   2 +-\n 9 files changed, 163 insertions(+), 86 deletions(-)\n\n--- interdiff from v1\n\ndiff --git a/abspath.c b/abspath.c\nindex 6f546e0..df37356 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -14,13 +14,13 @@ int is_directory(const char *path)\n /* removes the last path component from 'path' except if 'path' is root */\n static void strip_last_component(struct strbuf *path)\n {\n-\tif (path->len > 1) {\n+\tif (path->len > offset_1st_component(path->buf)) {\n \t\tchar *last_slash = find_last_dir_sep(path->buf);\n \t\tstrbuf_setlen(path, last_slash - path->buf);\n \t}\n }\n \n-/* gets the next component in 'remaining' and places it in 'next' */\n+/* get (and remove) the next component in 'remaining' and place it in 'next' */\n static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n {\n \tchar *start = NULL;\n@@ -31,10 +31,10 @@ static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n \t/* look for the next component */\n \t/* Skip sequences of multiple path-separators */\n \tfor (start = remaining->buf; is_dir_sep(*start); start++)\n-\t\t/* nothing */;\n+\t\t; /* nothing */\n \t/* Find end of the path component */\n \tfor (end = start; *end && !is_dir_sep(*end); end++)\n-\t\t/* nothing */;\n+\t\t; /* nothing */\n \n \tstrbuf_add(next, start, end - start);\n \t/* remove the component from 'remaining' */\n@@ -48,21 +48,17 @@ static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n  * Return the real path (i.e., absolute path, with symlinks resolved\n  * and extra slashes removed) equivalent to the specified path.  (If\n  * you want an absolute path but don't mind links, use\n- * absolute_path().)  The return value is a pointer to a static\n- * buffer.\n+ * absolute_path().)  Places the resolved realpath in the provided strbuf.\n  *\n  * The directory part of path (i.e., everything up to the last\n  * dir_sep) must denote a valid, existing directory, but the last\n  * component need not exist.  If die_on_error is set, then die with an\n  * informative error message if there is a problem.  Otherwise, return\n  * NULL on errors (without generating any output).\n- *\n- * If path is our buffer, then return path, as it's already what the\n- * user wants.\n  */\n-static const char *real_path_internal(const char *path, int die_on_error)\n+char *strbuf_realpath(struct strbuf *resolved, const char *path,\n+\t\t      int die_on_error)\n {\n-\tstatic struct strbuf resolved = STRBUF_INIT;\n \tstruct strbuf remaining = STRBUF_INIT;\n \tstruct strbuf next = STRBUF_INIT;\n \tstruct strbuf symlink = STRBUF_INIT;\n@@ -70,10 +66,6 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \tint num_symlinks = 0;\n \tstruct stat st;\n \n-\t/* We've already done it */\n-\tif (path == resolved.buf)\n-\t\treturn path;\n-\n \tif (!*path) {\n \t\tif (die_on_error)\n \t\t\tdie(\"The empty string is not a valid path\");\n@@ -81,17 +73,18 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tgoto error_out;\n \t}\n \n-\tstrbuf_reset(&resolved);\n+\tstrbuf_reset(resolved);\n \n \tif (is_absolute_path(path)) {\n \t\t/* absolute path; start with only root as being resolved */\n-\t\tstrbuf_addch(&resolved, '/');\n-\t\tstrbuf_addstr(&remaining, path + 1);\n+\t\tint offset = offset_1st_component(path);\n+\t\tstrbuf_add(resolved, path, offset);\n+\t\tstrbuf_addstr(&remaining, path + offset);\n \t} else {\n \t\t/* relative path; can use CWD as the initial resolved path */\n-\t\tif (strbuf_getcwd(&resolved)) {\n+\t\tif (strbuf_getcwd(resolved)) {\n \t\t\tif (die_on_error)\n-\t\t\t\tdie_errno(\"Could not get current working directory\");\n+\t\t\t\tdie_errno(\"unable to get current working directory\");\n \t\t\telse\n \t\t\t\tgoto error_out;\n \t\t}\n@@ -108,21 +101,21 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tcontinue; /* '.' component */\n \t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n \t\t\t/* '..' component; strip the last path component */\n-\t\t\tstrip_last_component(&resolved);\n+\t\t\tstrip_last_component(resolved);\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* append the next component and resolve resultant path */\n-\t\tif (resolved.buf[resolved.len - 1] != '/')\n-\t\t\tstrbuf_addch(&resolved, '/');\n-\t\tstrbuf_addbuf(&resolved, &next);\n+\t\tif (!is_dir_sep(resolved->buf[resolved->len - 1]))\n+\t\t\tstrbuf_addch(resolved, '/');\n+\t\tstrbuf_addbuf(resolved, &next);\n \n-\t\tif (lstat(resolved.buf, &st)) {\n+\t\tif (lstat(resolved->buf, &st)) {\n \t\t\t/* error out unless this was the last component */\n \t\t\tif (!(errno == ENOENT && !remaining.len)) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid path '%s'\",\n-\t\t\t\t\t\t  resolved.buf);\n+\t\t\t\t\t\t  resolved->buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n@@ -138,29 +131,29 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n \n-\t\t\tlen = strbuf_readlink(&symlink, resolved.buf,\n+\t\t\tlen = strbuf_readlink(&symlink, resolved->buf,\n \t\t\t\t\t      st.st_size);\n \t\t\tif (len < 0) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid symlink '%s'\",\n-\t\t\t\t\t\t  resolved.buf);\n+\t\t\t\t\t\t  resolved->buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n \n \t\t\tif (is_absolute_path(symlink.buf)) {\n-\t\t\t\t/*\n-\t\t\t\t * absolute symlink\n-\t\t\t\t * reset resolved path to root\n-\t\t\t\t */\n-\t\t\t\tstrbuf_setlen(&resolved, 1);\n+\t\t\t\t/* absolute symlink; set resolved to root */\n+\t\t\t\tint offset = offset_1st_component(symlink.buf);\n+\t\t\t\tstrbuf_reset(resolved);\n+\t\t\t\tstrbuf_add(resolved, symlink.buf, offset);\n+\t\t\t\tstrbuf_remove(&symlink, 0, offset);\n \t\t\t} else {\n \t\t\t\t/*\n \t\t\t\t * relative symlink\n \t\t\t\t * strip off the last component since it will\n \t\t\t\t * be replaced with the contents of the symlink\n \t\t\t\t */\n-\t\t\t\tstrip_last_component(&resolved);\n+\t\t\t\tstrip_last_component(resolved);\n \t\t\t}\n \n \t\t\t/*\n@@ -180,25 +173,42 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t}\n \t}\n \n-\tretval = resolved.buf;\n+\tretval = resolved->buf;\n \n error_out:\n-\t//strbuf_release(&resolved);\n \tstrbuf_release(&remaining);\n \tstrbuf_release(&next);\n \tstrbuf_release(&symlink);\n \n+\tif (!retval)\n+\t\tstrbuf_reset(resolved);\n+\n \treturn retval;\n }\n \n const char *real_path(const char *path)\n {\n-\treturn real_path_internal(path, 1);\n+\tstatic struct strbuf realpath = STRBUF_INIT;\n+\treturn strbuf_realpath(&realpath, path, 1);\n }\n \n const char *real_path_if_valid(const char *path)\n {\n-\treturn real_path_internal(path, 0);\n+\tstatic struct strbuf realpath = STRBUF_INIT;\n+\treturn strbuf_realpath(&realpath, path, 0);\n+}\n+\n+char *real_pathdup(const char *path)\n+{\n+\tstruct strbuf realpath = STRBUF_INIT;\n+\tchar *retval = NULL;\n+\n+\tif(strbuf_realpath(&realpath, path, 0))\n+\t\tretval = strbuf_detach(&realpath, NULL);\n+\n+\tstrbuf_release(&realpath);\n+\n+\treturn retval;\n }\n \n /*\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 2399b97..76d68fa 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -338,7 +338,7 @@ int init_db(const char *git_dir, const char *real_git_dir,\n {\n \tint reinit;\n \tint exist_ok = flags & INIT_DB_EXIST_OK;\n-\tchar *original_git_dir = xstrdup(real_path(git_dir));\n+\tchar *original_git_dir = real_pathdup(git_dir);\n \n \tif (real_git_dir) {\n \t\tstruct stat st;\n@@ -489,7 +489,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);\n \n \tif (real_git_dir && !is_absolute_path(real_git_dir))\n-\t\treal_git_dir = xstrdup(real_path(real_git_dir));\n+\t\treal_git_dir = real_pathdup(real_git_dir);\n \n \tif (argc == 1) {\n \t\tint mkdir_tried = 0;\n@@ -560,7 +560,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t\tconst char *git_dir_parent = strrchr(git_dir, '/');\n \t\tif (git_dir_parent) {\n \t\t\tchar *rel = xstrndup(git_dir, git_dir_parent - git_dir);\n-\t\t\tgit_work_tree_cfg = xstrdup(real_path(rel));\n+\t\t\tgit_work_tree_cfg = real_pathdup(rel);\n \t\t\tfree(rel);\n \t\t}\n \t\tif (!git_work_tree_cfg)\ndiff --git a/cache.h b/cache.h\nindex a50a61a..e12a5d9 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1064,8 +1064,11 @@ static inline int is_absolute_path(const char *path)\n \treturn is_dir_sep(path[0]) || has_dos_drive_prefix(path);\n }\n int is_directory(const char *);\n+char *strbuf_realpath(struct strbuf *resolved, const char *path,\n+\t\t      int die_on_error);\n const char *real_path(const char *path);\n const char *real_path_if_valid(const char *path);\n+char *real_pathdup(const char *path);\n const char *absolute_path(const char *path);\n const char *remove_leading_path(const char *in, const char *prefix);\n const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);\ndiff --git a/environment.c b/environment.c\nindex 0935ec6..9b943d2 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -259,7 +259,7 @@ void set_git_work_tree(const char *new_work_tree)\n \t\treturn;\n \t}\n \tgit_work_tree_initialized = 1;\n-\twork_tree = xstrdup(real_path(new_work_tree));\n+\twork_tree = real_pathdup(new_work_tree);\n }\n \n const char *get_git_work_tree(void)\ndiff --git a/setup.c b/setup.c\nindex fe572b8..0d9fdd0 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -254,10 +254,12 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)\n \t\tif (!is_absolute_path(data.buf))\n \t\t\tstrbuf_addf(&path, \"%s/\", gitdir);\n \t\tstrbuf_addbuf(&path, &data);\n-\t\tstrbuf_addstr(sb, real_path(path.buf));\n+\t\tstrbuf_realpath(sb, path.buf, 1);\n \t\tret = 1;\n-\t} else\n+\t} else {\n \t\tstrbuf_addstr(sb, gitdir);\n+\t}\n+\n \tstrbuf_release(&data);\n \tstrbuf_release(&path);\n \treturn ret;\n@@ -692,7 +694,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,\n \t/* --work-tree is set without --git-dir; use discovered one */\n \tif (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {\n \t\tif (offset != cwd->len && !is_absolute_path(gitdir))\n-\t\t\tgitdir = xstrdup(real_path(gitdir));\n+\t\t\tgitdir = real_pathdup(gitdir);\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(\"Could not come back to cwd\");\n \t\treturn setup_explicit_git_dir(gitdir, cwd, nongit_ok);\n@@ -800,11 +802,12 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,\n \t\t/* Keep entry but do not canonicalize it */\n \t\treturn 1;\n \t} else {\n-\t\tconst char *real_path = real_path_if_valid(ceil);\n-\t\tif (!real_path)\n+\t\tchar *real_path = real_pathdup(ceil);\n+\t\tif (!real_path) {\n \t\t\treturn 0;\n+\t\t}\n \t\tfree(item->string);\n-\t\titem->string = xstrdup(real_path);\n+\t\titem->string = real_path;\n \t\treturn 1;\n \t}\n }\ndiff --git a/sha1_file.c b/sha1_file.c\nindex 9c86d19..6a03cc3 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -291,7 +291,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,\n \tstruct strbuf pathbuf = STRBUF_INIT;\n \n \tif (!is_absolute_path(entry) && relative_base) {\n-\t\tstrbuf_addstr(&pathbuf, real_path(relative_base));\n+\t\tstrbuf_realpath(&pathbuf, relative_base, 1);\n \t\tstrbuf_addch(&pathbuf, '/');\n \t}\n \tstrbuf_addstr(&pathbuf, entry);\ndiff --git a/submodule.c b/submodule.c\nindex 6f7d883..c85ba50 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1227,7 +1227,7 @@ void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)\n {\n \tstruct strbuf file_name = STRBUF_INIT;\n \tstruct strbuf rel_path = STRBUF_INIT;\n-\tconst char *real_work_tree = xstrdup(real_path(work_tree));\n+\tconst char *real_work_tree = real_pathdup(work_tree);\n \n \t/* Update gitfile */\n \tstrbuf_addf(&file_name, \"%s/.git\", work_tree);\ndiff --git a/transport.c b/transport.c\nindex d57e8de..236c6f6 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1130,7 +1130,7 @@ static int refs_from_alternate_cb(struct alternate_object_database *e,\n \tconst struct ref *extra;\n \tstruct alternate_refs_data *cb = data;\n \n-\tother = xstrdup(real_path(e->path));\n+\tother = real_pathdup(e->path);\n \tlen = strlen(other);\n \n \twhile (other[len-1] == '/')\ndiff --git a/worktree.c b/worktree.c\nindex f7869f8..c90e013 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -255,7 +255,7 @@ struct worktree *find_worktree(struct worktree **list,\n \t\treturn wt;\n \n \targ = prefix_filename(prefix, strlen(prefix), arg);\n-\tpath = xstrdup(real_path(arg));\n+\tpath = real_pathdup(arg);\n \tfor (; *list; list++)\n \t\tif (!fspathcmp(path, real_path((*list)->path)))\n \t\t\tbreak;\n\n-- \n2.8.0.rc3.226.g39d4020\n\n"},{"id":"307314","messageId":"1481241494-6861-2-git-send-email-bmwill@google.com","threadId":"44625","inReplyTo":"1481241494-6861-1-git-send-email-bmwill@google.com","subject":"[PATCH v2 1/4] real_path: resolve symlinks by hand","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-08T23:58:11Z","receivedAt":"2016-12-08T23:58:42Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"The current implementation of real_path uses chdir() in order to resolve\nsymlinks.  Unfortunately this isn't thread-safe as chdir() affects a\nprocess as a whole and not just an individual thread.  Instead perform\nthe symlink resolution by hand so that the calls to chdir() can be\nremoved, making real_path one step closer to being reentrant.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 183 +++++++++++++++++++++++++++++++++++++++++---------------------\n 1 file changed, 122 insertions(+), 61 deletions(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex 2825de8..92f2a29 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -11,8 +11,38 @@ int is_directory(const char *path)\n \treturn (!stat(path, &st) && S_ISDIR(st.st_mode));\n }\n \n+/* removes the last path component from 'path' except if 'path' is root */\n+static void strip_last_component(struct strbuf *path)\n+{\n+\tif (path->len > offset_1st_component(path->buf)) {\n+\t\tchar *last_slash = find_last_dir_sep(path->buf);\n+\t\tstrbuf_setlen(path, last_slash - path->buf);\n+\t}\n+}\n+\n+/* get (and remove) the next component in 'remaining' and place it in 'next' */\n+static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n+{\n+\tchar *start = NULL;\n+\tchar *end = NULL;\n+\n+\tstrbuf_reset(next);\n+\n+\t/* look for the next component */\n+\t/* Skip sequences of multiple path-separators */\n+\tfor (start = remaining->buf; is_dir_sep(*start); start++)\n+\t\t; /* nothing */\n+\t/* Find end of the path component */\n+\tfor (end = start; *end && !is_dir_sep(*end); end++)\n+\t\t; /* nothing */\n+\n+\tstrbuf_add(next, start, end - start);\n+\t/* remove the component from 'remaining' */\n+\tstrbuf_remove(remaining, 0, end - remaining->buf);\n+}\n+\n /* We allow \"recursive\" symbolic links. Only within reason, though. */\n-#define MAXDEPTH 5\n+#define MAXSYMLINKS 5\n \n /*\n  * Return the real path (i.e., absolute path, with symlinks resolved\n@@ -21,7 +51,6 @@ int is_directory(const char *path)\n  * absolute_path().)  The return value is a pointer to a static\n  * buffer.\n  *\n- * The input and all intermediate paths must be shorter than MAX_PATH.\n  * The directory part of path (i.e., everything up to the last\n  * dir_sep) must denote a valid, existing directory, but the last\n  * component need not exist.  If die_on_error is set, then die with an\n@@ -33,22 +62,16 @@ int is_directory(const char *path)\n  */\n static const char *real_path_internal(const char *path, int die_on_error)\n {\n-\tstatic struct strbuf sb = STRBUF_INIT;\n+\tstatic struct strbuf resolved = STRBUF_INIT;\n+\tstruct strbuf remaining = STRBUF_INIT;\n+\tstruct strbuf next = STRBUF_INIT;\n+\tstruct strbuf symlink = STRBUF_INIT;\n \tchar *retval = NULL;\n-\n-\t/*\n-\t * If we have to temporarily chdir(), store the original CWD\n-\t * here so that we can chdir() back to it at the end of the\n-\t * function:\n-\t */\n-\tstruct strbuf cwd = STRBUF_INIT;\n-\n-\tint depth = MAXDEPTH;\n-\tchar *last_elem = NULL;\n+\tint num_symlinks = 0;\n \tstruct stat st;\n \n \t/* We've already done it */\n-\tif (path == sb.buf)\n+\tif (path == resolved.buf)\n \t\treturn path;\n \n \tif (!*path) {\n@@ -58,74 +81,112 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tgoto error_out;\n \t}\n \n-\tstrbuf_reset(&sb);\n-\tstrbuf_addstr(&sb, path);\n-\n-\twhile (depth--) {\n-\t\tif (!is_directory(sb.buf)) {\n-\t\t\tchar *last_slash = find_last_dir_sep(sb.buf);\n-\t\t\tif (last_slash) {\n-\t\t\t\tlast_elem = xstrdup(last_slash + 1);\n-\t\t\t\tstrbuf_setlen(&sb, last_slash - sb.buf + 1);\n-\t\t\t} else {\n-\t\t\t\tlast_elem = xmemdupz(sb.buf, sb.len);\n-\t\t\t\tstrbuf_reset(&sb);\n-\t\t\t}\n+\tstrbuf_reset(&resolved);\n+\n+\tif (is_absolute_path(path)) {\n+\t\t/* absolute path; start with only root as being resolved */\n+\t\tint offset = offset_1st_component(path);\n+\t\tstrbuf_add(&resolved, path, offset);\n+\t\tstrbuf_addstr(&remaining, path + offset);\n+\t} else {\n+\t\t/* relative path; can use CWD as the initial resolved path */\n+\t\tif (strbuf_getcwd(&resolved)) {\n+\t\t\tif (die_on_error)\n+\t\t\t\tdie_errno(\"unable to get current working directory\");\n+\t\t\telse\n+\t\t\t\tgoto error_out;\n+\t\t}\n+\t\tstrbuf_addstr(&remaining, path);\n+\t}\n+\n+\t/* Iterate over the remaining path components */\n+\twhile (remaining.len > 0) {\n+\t\tget_next_component(&next, &remaining);\n+\n+\t\tif (next.len == 0) {\n+\t\t\tcontinue; /* empty component */\n+\t\t} else if (next.len == 1 && !strcmp(next.buf, \".\")) {\n+\t\t\tcontinue; /* '.' component */\n+\t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n+\t\t\t/* '..' component; strip the last path component */\n+\t\t\tstrip_last_component(&resolved);\n+\t\t\tcontinue;\n \t\t}\n \n-\t\tif (sb.len) {\n-\t\t\tif (!cwd.len && strbuf_getcwd(&cwd)) {\n+\t\t/* append the next component and resolve resultant path */\n+\t\tif (!is_dir_sep(resolved.buf[resolved.len - 1]))\n+\t\t\tstrbuf_addch(&resolved, '/');\n+\t\tstrbuf_addbuf(&resolved, &next);\n+\n+\t\tif (lstat(resolved.buf, &st)) {\n+\t\t\t/* error out unless this was the last component */\n+\t\t\tif (!(errno == ENOENT && !remaining.len)) {\n \t\t\t\tif (die_on_error)\n-\t\t\t\t\tdie_errno(\"Could not get current working directory\");\n+\t\t\t\t\tdie_errno(\"Invalid path '%s'\",\n+\t\t\t\t\t\t  resolved.buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n+\t\t} else if (S_ISLNK(st.st_mode)) {\n+\t\t\tssize_t len;\n+\t\t\tstrbuf_reset(&symlink);\n \n-\t\t\tif (chdir(sb.buf)) {\n+\t\t\tif (num_symlinks++ > MAXSYMLINKS) {\n \t\t\t\tif (die_on_error)\n-\t\t\t\t\tdie_errno(\"Could not switch to '%s'\",\n-\t\t\t\t\t\t  sb.buf);\n+\t\t\t\t\tdie(\"More than %d nested symlinks \"\n+\t\t\t\t\t    \"on path '%s'\", MAXSYMLINKS, path);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n-\t\t}\n-\t\tif (strbuf_getcwd(&sb)) {\n-\t\t\tif (die_on_error)\n-\t\t\t\tdie_errno(\"Could not get current working directory\");\n-\t\t\telse\n-\t\t\t\tgoto error_out;\n-\t\t}\n-\n-\t\tif (last_elem) {\n-\t\t\tif (sb.len && !is_dir_sep(sb.buf[sb.len - 1]))\n-\t\t\t\tstrbuf_addch(&sb, '/');\n-\t\t\tstrbuf_addstr(&sb, last_elem);\n-\t\t\tfree(last_elem);\n-\t\t\tlast_elem = NULL;\n-\t\t}\n \n-\t\tif (!lstat(sb.buf, &st) && S_ISLNK(st.st_mode)) {\n-\t\t\tstruct strbuf next_sb = STRBUF_INIT;\n-\t\t\tssize_t len = strbuf_readlink(&next_sb, sb.buf, 0);\n+\t\t\tlen = strbuf_readlink(&symlink, resolved.buf,\n+\t\t\t\t\t      st.st_size);\n \t\t\tif (len < 0) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid symlink '%s'\",\n-\t\t\t\t\t\t  sb.buf);\n+\t\t\t\t\t\t  resolved.buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n-\t\t\tstrbuf_swap(&sb, &next_sb);\n-\t\t\tstrbuf_release(&next_sb);\n-\t\t} else\n-\t\t\tbreak;\n+\n+\t\t\tif (is_absolute_path(symlink.buf)) {\n+\t\t\t\t/* absolute symlink; set resolved to root */\n+\t\t\t\tint offset = offset_1st_component(symlink.buf);\n+\t\t\t\tstrbuf_reset(&resolved);\n+\t\t\t\tstrbuf_add(&resolved, symlink.buf, offset);\n+\t\t\t\tstrbuf_remove(&symlink, 0, offset);\n+\t\t\t} else {\n+\t\t\t\t/*\n+\t\t\t\t * relative symlink\n+\t\t\t\t * strip off the last component since it will\n+\t\t\t\t * be replaced with the contents of the symlink\n+\t\t\t\t */\n+\t\t\t\tstrip_last_component(&resolved);\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * if there are still remaining components to resolve\n+\t\t\t * then append them to symlink\n+\t\t\t */\n+\t\t\tif (remaining.len) {\n+\t\t\t\tstrbuf_addch(&symlink, '/');\n+\t\t\t\tstrbuf_addbuf(&symlink, &remaining);\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * use the symlink as the remaining components that\n+\t\t\t * need to be resloved\n+\t\t\t */\n+\t\t\tstrbuf_swap(&symlink, &remaining);\n+\t\t}\n \t}\n \n-\tretval = sb.buf;\n+\tretval = resolved.buf;\n+\n error_out:\n-\tfree(last_elem);\n-\tif (cwd.len && chdir(cwd.buf))\n-\t\tdie_errno(\"Could not change back to '%s'\", cwd.buf);\n-\tstrbuf_release(&cwd);\n+\tstrbuf_release(&remaining);\n+\tstrbuf_release(&next);\n+\tstrbuf_release(&symlink);\n \n \treturn retval;\n }\n-- \n2.8.0.rc3.226.g39d4020\n\n"},{"id":"307315","messageId":"1481241494-6861-3-git-send-email-bmwill@google.com","threadId":"44625","inReplyTo":"1481241494-6861-1-git-send-email-bmwill@google.com","subject":"[PATCH v2 2/4] real_path: convert real_path_internal to strbuf_realpath","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-08T23:58:12Z","receivedAt":"2016-12-08T23:58:45Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Change the name of real_path_internal to strbuf_realpath.  In addition\npush the static strbuf up to its callers and instead take as a\nparameter a pointer to a strbuf to use for the final result.\n\nThis change makes strbuf_realpath reentrant.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 53 +++++++++++++++++++++++++----------------------------\n cache.h   |  2 ++\n 2 files changed, 27 insertions(+), 28 deletions(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex 92f2a29..b0d4c1b 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -48,21 +48,17 @@ static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n  * Return the real path (i.e., absolute path, with symlinks resolved\n  * and extra slashes removed) equivalent to the specified path.  (If\n  * you want an absolute path but don't mind links, use\n- * absolute_path().)  The return value is a pointer to a static\n- * buffer.\n+ * absolute_path().)  Places the resolved realpath in the provided strbuf.\n  *\n  * The directory part of path (i.e., everything up to the last\n  * dir_sep) must denote a valid, existing directory, but the last\n  * component need not exist.  If die_on_error is set, then die with an\n  * informative error message if there is a problem.  Otherwise, return\n  * NULL on errors (without generating any output).\n- *\n- * If path is our buffer, then return path, as it's already what the\n- * user wants.\n  */\n-static const char *real_path_internal(const char *path, int die_on_error)\n+char *strbuf_realpath(struct strbuf *resolved, const char *path,\n+\t\t      int die_on_error)\n {\n-\tstatic struct strbuf resolved = STRBUF_INIT;\n \tstruct strbuf remaining = STRBUF_INIT;\n \tstruct strbuf next = STRBUF_INIT;\n \tstruct strbuf symlink = STRBUF_INIT;\n@@ -70,10 +66,6 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \tint num_symlinks = 0;\n \tstruct stat st;\n \n-\t/* We've already done it */\n-\tif (path == resolved.buf)\n-\t\treturn path;\n-\n \tif (!*path) {\n \t\tif (die_on_error)\n \t\t\tdie(\"The empty string is not a valid path\");\n@@ -81,16 +73,16 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tgoto error_out;\n \t}\n \n-\tstrbuf_reset(&resolved);\n+\tstrbuf_reset(resolved);\n \n \tif (is_absolute_path(path)) {\n \t\t/* absolute path; start with only root as being resolved */\n \t\tint offset = offset_1st_component(path);\n-\t\tstrbuf_add(&resolved, path, offset);\n+\t\tstrbuf_add(resolved, path, offset);\n \t\tstrbuf_addstr(&remaining, path + offset);\n \t} else {\n \t\t/* relative path; can use CWD as the initial resolved path */\n-\t\tif (strbuf_getcwd(&resolved)) {\n+\t\tif (strbuf_getcwd(resolved)) {\n \t\t\tif (die_on_error)\n \t\t\t\tdie_errno(\"unable to get current working directory\");\n \t\t\telse\n@@ -109,21 +101,21 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tcontinue; /* '.' component */\n \t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n \t\t\t/* '..' component; strip the last path component */\n-\t\t\tstrip_last_component(&resolved);\n+\t\t\tstrip_last_component(resolved);\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* append the next component and resolve resultant path */\n-\t\tif (!is_dir_sep(resolved.buf[resolved.len - 1]))\n-\t\t\tstrbuf_addch(&resolved, '/');\n-\t\tstrbuf_addbuf(&resolved, &next);\n+\t\tif (!is_dir_sep(resolved->buf[resolved->len - 1]))\n+\t\t\tstrbuf_addch(resolved, '/');\n+\t\tstrbuf_addbuf(resolved, &next);\n \n-\t\tif (lstat(resolved.buf, &st)) {\n+\t\tif (lstat(resolved->buf, &st)) {\n \t\t\t/* error out unless this was the last component */\n \t\t\tif (!(errno == ENOENT && !remaining.len)) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid path '%s'\",\n-\t\t\t\t\t\t  resolved.buf);\n+\t\t\t\t\t\t  resolved->buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n@@ -139,12 +131,12 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n \n-\t\t\tlen = strbuf_readlink(&symlink, resolved.buf,\n+\t\t\tlen = strbuf_readlink(&symlink, resolved->buf,\n \t\t\t\t\t      st.st_size);\n \t\t\tif (len < 0) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid symlink '%s'\",\n-\t\t\t\t\t\t  resolved.buf);\n+\t\t\t\t\t\t  resolved->buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n@@ -152,8 +144,8 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tif (is_absolute_path(symlink.buf)) {\n \t\t\t\t/* absolute symlink; set resolved to root */\n \t\t\t\tint offset = offset_1st_component(symlink.buf);\n-\t\t\t\tstrbuf_reset(&resolved);\n-\t\t\t\tstrbuf_add(&resolved, symlink.buf, offset);\n+\t\t\t\tstrbuf_reset(resolved);\n+\t\t\t\tstrbuf_add(resolved, symlink.buf, offset);\n \t\t\t\tstrbuf_remove(&symlink, 0, offset);\n \t\t\t} else {\n \t\t\t\t/*\n@@ -161,7 +153,7 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\t\t * strip off the last component since it will\n \t\t\t\t * be replaced with the contents of the symlink\n \t\t\t\t */\n-\t\t\t\tstrip_last_component(&resolved);\n+\t\t\t\tstrip_last_component(resolved);\n \t\t\t}\n \n \t\t\t/*\n@@ -181,24 +173,29 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t}\n \t}\n \n-\tretval = resolved.buf;\n+\tretval = resolved->buf;\n \n error_out:\n \tstrbuf_release(&remaining);\n \tstrbuf_release(&next);\n \tstrbuf_release(&symlink);\n \n+\tif (!retval)\n+\t\tstrbuf_reset(resolved);\n+\n \treturn retval;\n }\n \n const char *real_path(const char *path)\n {\n-\treturn real_path_internal(path, 1);\n+\tstatic struct strbuf realpath = STRBUF_INIT;\n+\treturn strbuf_realpath(&realpath, path, 1);\n }\n \n const char *real_path_if_valid(const char *path)\n {\n-\treturn real_path_internal(path, 0);\n+\tstatic struct strbuf realpath = STRBUF_INIT;\n+\treturn strbuf_realpath(&realpath, path, 0);\n }\n \n /*\ndiff --git a/cache.h b/cache.h\nindex a50a61a..7a81294 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1064,6 +1064,8 @@ static inline int is_absolute_path(const char *path)\n \treturn is_dir_sep(path[0]) || has_dos_drive_prefix(path);\n }\n int is_directory(const char *);\n+char *strbuf_realpath(struct strbuf *resolved, const char *path,\n+\t\t      int die_on_error);\n const char *real_path(const char *path);\n const char *real_path_if_valid(const char *path);\n const char *absolute_path(const char *path);\n-- \n2.8.0.rc3.226.g39d4020\n\n"},{"id":"307317","messageId":"1481241494-6861-4-git-send-email-bmwill@google.com","threadId":"44625","inReplyTo":"1481241494-6861-1-git-send-email-bmwill@google.com","subject":"[PATCH v2 3/4] real_path: create real_pathdup","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-08T23:58:13Z","receivedAt":"2016-12-08T23:58:51Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Create real_pathdup which returns a caller owned string of the resolved\nrealpath based on the provide path.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 13 +++++++++++++\n cache.h   |  1 +\n 2 files changed, 14 insertions(+)\n\ndiff --git a/abspath.c b/abspath.c\nindex b0d4c1b..df37356 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -198,6 +198,19 @@ const char *real_path_if_valid(const char *path)\n \treturn strbuf_realpath(&realpath, path, 0);\n }\n \n+char *real_pathdup(const char *path)\n+{\n+\tstruct strbuf realpath = STRBUF_INIT;\n+\tchar *retval = NULL;\n+\n+\tif(strbuf_realpath(&realpath, path, 0))\n+\t\tretval = strbuf_detach(&realpath, NULL);\n+\n+\tstrbuf_release(&realpath);\n+\n+\treturn retval;\n+}\n+\n /*\n  * Use this to get an absolute path from a relative one. If you want\n  * to resolve links, you should use real_path.\ndiff --git a/cache.h b/cache.h\nindex 7a81294..e12a5d9 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1068,6 +1068,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t      int die_on_error);\n const char *real_path(const char *path);\n const char *real_path_if_valid(const char *path);\n+char *real_pathdup(const char *path);\n const char *absolute_path(const char *path);\n const char *remove_leading_path(const char *in, const char *prefix);\n const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);\n-- \n2.8.0.rc3.226.g39d4020\n\n"},{"id":"307318","messageId":"1481241494-6861-5-git-send-email-bmwill@google.com","threadId":"44625","inReplyTo":"1481241494-6861-1-git-send-email-bmwill@google.com","subject":"[PATCH v2 4/4] real_path: have callers use real_pathdup and strbuf_realpath","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-08T23:58:14Z","receivedAt":"2016-12-08T23:58:52Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Migrate callers of real_path() who duplicate the retern value to use\nreal_pathdup or strbuf_realpath.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/init-db.c |  6 +++---\n environment.c     |  2 +-\n setup.c           | 15 +++++++++------\n sha1_file.c       |  2 +-\n submodule.c       |  2 +-\n transport.c       |  2 +-\n worktree.c        |  2 +-\n 7 files changed, 17 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 2399b97..76d68fa 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -338,7 +338,7 @@ int init_db(const char *git_dir, const char *real_git_dir,\n {\n \tint reinit;\n \tint exist_ok = flags & INIT_DB_EXIST_OK;\n-\tchar *original_git_dir = xstrdup(real_path(git_dir));\n+\tchar *original_git_dir = real_pathdup(git_dir);\n \n \tif (real_git_dir) {\n \t\tstruct stat st;\n@@ -489,7 +489,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);\n \n \tif (real_git_dir && !is_absolute_path(real_git_dir))\n-\t\treal_git_dir = xstrdup(real_path(real_git_dir));\n+\t\treal_git_dir = real_pathdup(real_git_dir);\n \n \tif (argc == 1) {\n \t\tint mkdir_tried = 0;\n@@ -560,7 +560,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t\tconst char *git_dir_parent = strrchr(git_dir, '/');\n \t\tif (git_dir_parent) {\n \t\t\tchar *rel = xstrndup(git_dir, git_dir_parent - git_dir);\n-\t\t\tgit_work_tree_cfg = xstrdup(real_path(rel));\n+\t\t\tgit_work_tree_cfg = real_pathdup(rel);\n \t\t\tfree(rel);\n \t\t}\n \t\tif (!git_work_tree_cfg)\ndiff --git a/environment.c b/environment.c\nindex 0935ec6..9b943d2 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -259,7 +259,7 @@ void set_git_work_tree(const char *new_work_tree)\n \t\treturn;\n \t}\n \tgit_work_tree_initialized = 1;\n-\twork_tree = xstrdup(real_path(new_work_tree));\n+\twork_tree = real_pathdup(new_work_tree);\n }\n \n const char *get_git_work_tree(void)\ndiff --git a/setup.c b/setup.c\nindex fe572b8..0d9fdd0 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -254,10 +254,12 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)\n \t\tif (!is_absolute_path(data.buf))\n \t\t\tstrbuf_addf(&path, \"%s/\", gitdir);\n \t\tstrbuf_addbuf(&path, &data);\n-\t\tstrbuf_addstr(sb, real_path(path.buf));\n+\t\tstrbuf_realpath(sb, path.buf, 1);\n \t\tret = 1;\n-\t} else\n+\t} else {\n \t\tstrbuf_addstr(sb, gitdir);\n+\t}\n+\n \tstrbuf_release(&data);\n \tstrbuf_release(&path);\n \treturn ret;\n@@ -692,7 +694,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,\n \t/* --work-tree is set without --git-dir; use discovered one */\n \tif (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {\n \t\tif (offset != cwd->len && !is_absolute_path(gitdir))\n-\t\t\tgitdir = xstrdup(real_path(gitdir));\n+\t\t\tgitdir = real_pathdup(gitdir);\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(\"Could not come back to cwd\");\n \t\treturn setup_explicit_git_dir(gitdir, cwd, nongit_ok);\n@@ -800,11 +802,12 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,\n \t\t/* Keep entry but do not canonicalize it */\n \t\treturn 1;\n \t} else {\n-\t\tconst char *real_path = real_path_if_valid(ceil);\n-\t\tif (!real_path)\n+\t\tchar *real_path = real_pathdup(ceil);\n+\t\tif (!real_path) {\n \t\t\treturn 0;\n+\t\t}\n \t\tfree(item->string);\n-\t\titem->string = xstrdup(real_path);\n+\t\titem->string = real_path;\n \t\treturn 1;\n \t}\n }\ndiff --git a/sha1_file.c b/sha1_file.c\nindex 9c86d19..6a03cc3 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -291,7 +291,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,\n \tstruct strbuf pathbuf = STRBUF_INIT;\n \n \tif (!is_absolute_path(entry) && relative_base) {\n-\t\tstrbuf_addstr(&pathbuf, real_path(relative_base));\n+\t\tstrbuf_realpath(&pathbuf, relative_base, 1);\n \t\tstrbuf_addch(&pathbuf, '/');\n \t}\n \tstrbuf_addstr(&pathbuf, entry);\ndiff --git a/submodule.c b/submodule.c\nindex 6f7d883..c85ba50 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1227,7 +1227,7 @@ void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)\n {\n \tstruct strbuf file_name = STRBUF_INIT;\n \tstruct strbuf rel_path = STRBUF_INIT;\n-\tconst char *real_work_tree = xstrdup(real_path(work_tree));\n+\tconst char *real_work_tree = real_pathdup(work_tree);\n \n \t/* Update gitfile */\n \tstrbuf_addf(&file_name, \"%s/.git\", work_tree);\ndiff --git a/transport.c b/transport.c\nindex d57e8de..236c6f6 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1130,7 +1130,7 @@ static int refs_from_alternate_cb(struct alternate_object_database *e,\n \tconst struct ref *extra;\n \tstruct alternate_refs_data *cb = data;\n \n-\tother = xstrdup(real_path(e->path));\n+\tother = real_pathdup(e->path);\n \tlen = strlen(other);\n \n \twhile (other[len-1] == '/')\ndiff --git a/worktree.c b/worktree.c\nindex f7869f8..c90e013 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -255,7 +255,7 @@ struct worktree *find_worktree(struct worktree **list,\n \t\treturn wt;\n \n \targ = prefix_filename(prefix, strlen(prefix), arg);\n-\tpath = xstrdup(real_path(arg));\n+\tpath = real_pathdup(arg);\n \tfor (; *list; list++)\n \t\tif (!fspathcmp(path, real_path((*list)->path)))\n \t\t\tbreak;\n-- \n2.8.0.rc3.226.g39d4020\n\n"},{"id":"307324","messageId":"CA+P7+xpjpGhk0MzyZ5VjKD_bORL9WQJvcFm8hi_ptxJpL_+NJQ@mail.gmail.com","threadId":"44625","inReplyTo":"1481241494-6861-2-git-send-email-bmwill@google.com","subject":"Re: [PATCH v2 1/4] real_path: resolve symlinks by hand","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-12-09T01:49:40Z","receivedAt":"2016-12-09T01:50:30Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Thu, Dec 8, 2016 at 3:58 PM, Brandon Williams <bmwill@google.com> wrote:\n> The current implementation of real_path uses chdir() in order to resolve\n> symlinks.  Unfortunately this isn't thread-safe as chdir() affects a\n> process as a whole and not just an individual thread.  Instead perform\n> the symlink resolution by hand so that the calls to chdir() can be\n> removed, making real_path one step closer to being reentrant.\n>\n\nBetter description for this part of the change. I like the\nimprovement, as it clearly indicates what this particular patch is\nabout, and why, but doesn't over-state what we gain here.\n\nThe rest of this seems reasonable, though I'm not super familiar with\nthe code, so I didn't have any comments.\n\nThanks,\nJake\n"},{"id":"307346","messageId":"CACsJy8A2M_G34MeHh6vGsrf5ePOOduM6u=n17_EZLtu31uDAYg@mail.gmail.com","threadId":"44625","inReplyTo":"1481241494-6861-1-git-send-email-bmwill@google.com","subject":"Re: [PATCH v2 0/4] road to reentrant real_path","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-12-09T12:33:34Z","receivedAt":"2016-12-09T12:34:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Dec 9, 2016 at 6:58 AM, Brandon Williams <bmwill@google.com> wrote:\n> diff --git a/setup.c b/setup.c\n> index fe572b8..0d9fdd0 100644\n> --- a/setup.c\n> +++ b/setup.c\n> @@ -254,10 +254,12 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)\n>                 if (!is_absolute_path(data.buf))\n>                         strbuf_addf(&path, \"%s/\", gitdir);\n>                 strbuf_addbuf(&path, &data);\n> -               strbuf_addstr(sb, real_path(path.buf));\n> +               strbuf_realpath(sb, path.buf, 1);\n\nThis is not the same because of this hunk in strbuf_realpath()\n\n> @@ -81,17 +73,18 @@ static const char *real_path_internal(const char *path, int die_on_error)\n>                         goto error_out;\n>         }\n>\n> -       strbuf_reset(&resolved);\n> +       strbuf_reset(resolved);\n>\n>         if (is_absolute_path(path)) {\n\nBut if you you remove that, then all (old) callers of\nstrbuf_realpath() must do a strbuf_reset() in advance if needed\n(probably just real_path does) which sounds reasonable to me. You're\nprobably want to be careful about the strbuf_reset() at the end of the\nfunction too.\n\nOther than that, I think this diff looks nice.\n-- \nDuy\n"},{"id":"307356","messageId":"944a3e91-934a-7923-2b2a-639af81e707d@kdbg.org","threadId":"44625","inReplyTo":"1481241494-6861-2-git-send-email-bmwill@google.com","subject":"Re: [PATCH v2 1/4] real_path: resolve symlinks by hand","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2016-12-09T14:33:28Z","receivedAt":"2016-12-09T14:33:39Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 09.12.2016 um 00:58 schrieb Brandon Williams:\n> The current implementation of real_path uses chdir() in order to resolve\n> symlinks.  Unfortunately this isn't thread-safe as chdir() affects a\n> process as a whole and not just an individual thread.  Instead perform\n> the symlink resolution by hand so that the calls to chdir() can be\n> removed, making real_path one step closer to being reentrant.\n>\n> Signed-off-by: Brandon Williams <bmwill@google.com>\n> ---\n>  abspath.c | 183 +++++++++++++++++++++++++++++++++++++++++---------------------\n>  1 file changed, 122 insertions(+), 61 deletions(-)\n>\n> diff --git a/abspath.c b/abspath.c\n> index 2825de8..92f2a29 100644\n> --- a/abspath.c\n> +++ b/abspath.c\n> @@ -11,8 +11,38 @@ int is_directory(const char *path)\n>  \treturn (!stat(path, &st) && S_ISDIR(st.st_mode));\n>  }\n>\n> +/* removes the last path component from 'path' except if 'path' is root */\n> +static void strip_last_component(struct strbuf *path)\n> +{\n> +\tif (path->len > offset_1st_component(path->buf)) {\n> +\t\tchar *last_slash = find_last_dir_sep(path->buf);\n> +\t\tstrbuf_setlen(path, last_slash - path->buf);\n> +\t}\n> +}\n\nThis implementation is not correct because when the input is \"/foo\", the \nresult is \"\" when it should be \"/\". Also, can the input be a \nnon-normalized path? When the input is \"foo///bar\", should the result be \n\"foo\" or would \"foo//\" be an acceptable result? I think it should be the \nformer. find_last_dir_sep() returns the last of the three slashes, not \nthe first one. Therefore, I've rewritten the function thusly:\n\nstatic void strip_last_component(struct strbuf *path)\n{\n\tsize_t offset = offset_1st_component(path->buf);\n\tsize_t len = path->len;\n\twhile (offset < len && !is_dir_sep(path->buf[len - 1]))\n\t\tlen--;\n\twhile (offset < len && is_dir_sep(path->buf[len - 1]))\n\t\tlen--;\n\tstrbuf_setlen(path, len);\n}\n\n> +\n> +/* get (and remove) the next component in 'remaining' and place it in 'next' */\n> +static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n> +{\n> +\tchar *start = NULL;\n> +\tchar *end = NULL;\n> +\n> +\tstrbuf_reset(next);\n> +\n> +\t/* look for the next component */\n> +\t/* Skip sequences of multiple path-separators */\n> +\tfor (start = remaining->buf; is_dir_sep(*start); start++)\n> +\t\t; /* nothing */\n> +\t/* Find end of the path component */\n> +\tfor (end = start; *end && !is_dir_sep(*end); end++)\n> +\t\t; /* nothing */\n> +\n> +\tstrbuf_add(next, start, end - start);\n> +\t/* remove the component from 'remaining' */\n> +\tstrbuf_remove(remaining, 0, end - remaining->buf);\n> +}\n\nMental note: This function strips leading slashes and the first path \ncomponent; post-condition: remaining is empty or has leading slashes.\n\n> +\n>  /* We allow \"recursive\" symbolic links. Only within reason, though. */\n> -#define MAXDEPTH 5\n> +#define MAXSYMLINKS 5\n>\n>  /*\n>   * Return the real path (i.e., absolute path, with symlinks resolved\n> @@ -21,7 +51,6 @@ int is_directory(const char *path)\n>   * absolute_path().)  The return value is a pointer to a static\n>   * buffer.\n>   *\n> - * The input and all intermediate paths must be shorter than MAX_PATH.\n>   * The directory part of path (i.e., everything up to the last\n>   * dir_sep) must denote a valid, existing directory, but the last\n>   * component need not exist.  If die_on_error is set, then die with an\n> @@ -33,22 +62,16 @@ int is_directory(const char *path)\n>   */\n>  static const char *real_path_internal(const char *path, int die_on_error)\n>  {\n> -\tstatic struct strbuf sb = STRBUF_INIT;\n> +\tstatic struct strbuf resolved = STRBUF_INIT;\n> +\tstruct strbuf remaining = STRBUF_INIT;\n> +\tstruct strbuf next = STRBUF_INIT;\n> +\tstruct strbuf symlink = STRBUF_INIT;\n>  \tchar *retval = NULL;\n> -\n> -\t/*\n> -\t * If we have to temporarily chdir(), store the original CWD\n> -\t * here so that we can chdir() back to it at the end of the\n> -\t * function:\n> -\t */\n> -\tstruct strbuf cwd = STRBUF_INIT;\n> -\n> -\tint depth = MAXDEPTH;\n> -\tchar *last_elem = NULL;\n> +\tint num_symlinks = 0;\n>  \tstruct stat st;\n>\n>  \t/* We've already done it */\n> -\tif (path == sb.buf)\n> +\tif (path == resolved.buf)\n>  \t\treturn path;\n>\n>  \tif (!*path) {\n> @@ -58,74 +81,112 @@ static const char *real_path_internal(const char *path, int die_on_error)\n>  \t\t\tgoto error_out;\n>  \t}\n>\n> -\tstrbuf_reset(&sb);\n> -\tstrbuf_addstr(&sb, path);\n> -\n> -\twhile (depth--) {\n> -\t\tif (!is_directory(sb.buf)) {\n> -\t\t\tchar *last_slash = find_last_dir_sep(sb.buf);\n> -\t\t\tif (last_slash) {\n> -\t\t\t\tlast_elem = xstrdup(last_slash + 1);\n> -\t\t\t\tstrbuf_setlen(&sb, last_slash - sb.buf + 1);\n> -\t\t\t} else {\n> -\t\t\t\tlast_elem = xmemdupz(sb.buf, sb.len);\n> -\t\t\t\tstrbuf_reset(&sb);\n> -\t\t\t}\n> +\tstrbuf_reset(&resolved);\n> +\n> +\tif (is_absolute_path(path)) {\n> +\t\t/* absolute path; start with only root as being resolved */\n> +\t\tint offset = offset_1st_component(path);\n> +\t\tstrbuf_add(&resolved, path, offset);\n> +\t\tstrbuf_addstr(&remaining, path + offset);\n\nOK.\n\n> +\t} else {\n> +\t\t/* relative path; can use CWD as the initial resolved path */\n> +\t\tif (strbuf_getcwd(&resolved)) {\n> +\t\t\tif (die_on_error)\n> +\t\t\t\tdie_errno(\"unable to get current working directory\");\n> +\t\t\telse\n> +\t\t\t\tgoto error_out;\n> +\t\t}\n> +\t\tstrbuf_addstr(&remaining, path);\n> +\t}\n> +\n> +\t/* Iterate over the remaining path components */\n> +\twhile (remaining.len > 0) {\n> +\t\tget_next_component(&next, &remaining);\n> +\n> +\t\tif (next.len == 0) {\n> +\t\t\tcontinue; /* empty component */\n\nCan this happen? I think it can: when 'path' is a root directory, or \nends with path separators.\n\n> +\t\t} else if (next.len == 1 && !strcmp(next.buf, \".\")) {\n> +\t\t\tcontinue; /* '.' component */\n\nGood.\n\nI don't mind strcmp(), but others might point out that a single \ncharacter comparison should be sufficient.\n\n> +\t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n> +\t\t\t/* '..' component; strip the last path component */\n> +\t\t\tstrip_last_component(&resolved);\n> +\t\t\tcontinue;\n\nGood.\n\n>  \t\t}\n>\n> -\t\tif (sb.len) {\n> -\t\t\tif (!cwd.len && strbuf_getcwd(&cwd)) {\n> +\t\t/* append the next component and resolve resultant path */\n> +\t\tif (!is_dir_sep(resolved.buf[resolved.len - 1]))\n> +\t\t\tstrbuf_addch(&resolved, '/');\n\nCan we access resolved.buf[resolved.len - 1] here? At this point, \nresolved has been filled with an absolute path or from getcwd(); it \ncannot be empty in the first iteration. In subsequent iterations, it \ncannot be empty as long as strip_last_component() does not make it \nempty. Your original version of strip_last_component() could make it \nempty; my rewrite should not.\n\nDo we have to check for the path separator at all? Typically, resolved \ndoes not end with a slash, but if we went all the way up to the root, \nthere is a trailing slash. Good; the condition is required.\n\n> +\t\tstrbuf_addbuf(&resolved, &next);\n> +\n> +\t\tif (lstat(resolved.buf, &st)) {\n> +\t\t\t/* error out unless this was the last component */\n> +\t\t\tif (!(errno == ENOENT && !remaining.len)) {\n\nPerhaps it was easier to _write_ the condition in this way, but I would \nhave an easier time to _read_ it when it is\n\n\t\t\tif (errno != ENOENT || remaining.len) {\n\n>  \t\t\t\tif (die_on_error)\n> -\t\t\t\t\tdie_errno(\"Could not get current working directory\");\n> +\t\t\t\t\tdie_errno(\"Invalid path '%s'\",\n> +\t\t\t\t\t\t  resolved.buf);\n>  \t\t\t\telse\n>  \t\t\t\t\tgoto error_out;\n>  \t\t\t}\n> +\t\t} else if (S_ISLNK(st.st_mode)) {\n> +\t\t\tssize_t len;\n> +\t\t\tstrbuf_reset(&symlink);\n>\n> -\t\t\tif (chdir(sb.buf)) {\n> +\t\t\tif (num_symlinks++ > MAXSYMLINKS) {\n>  \t\t\t\tif (die_on_error)\n> -\t\t\t\t\tdie_errno(\"Could not switch to '%s'\",\n> -\t\t\t\t\t\t  sb.buf);\n> +\t\t\t\t\tdie(\"More than %d nested symlinks \"\n> +\t\t\t\t\t    \"on path '%s'\", MAXSYMLINKS, path);\n>  \t\t\t\telse\n>  \t\t\t\t\tgoto error_out;\n>  \t\t\t}\n> -\t\t}\n> -\t\tif (strbuf_getcwd(&sb)) {\n> -\t\t\tif (die_on_error)\n> -\t\t\t\tdie_errno(\"Could not get current working directory\");\n> -\t\t\telse\n> -\t\t\t\tgoto error_out;\n> -\t\t}\n> -\n> -\t\tif (last_elem) {\n> -\t\t\tif (sb.len && !is_dir_sep(sb.buf[sb.len - 1]))\n> -\t\t\t\tstrbuf_addch(&sb, '/');\n> -\t\t\tstrbuf_addstr(&sb, last_elem);\n> -\t\t\tfree(last_elem);\n> -\t\t\tlast_elem = NULL;\n> -\t\t}\n>\n> -\t\tif (!lstat(sb.buf, &st) && S_ISLNK(st.st_mode)) {\n> -\t\t\tstruct strbuf next_sb = STRBUF_INIT;\n> -\t\t\tssize_t len = strbuf_readlink(&next_sb, sb.buf, 0);\n> +\t\t\tlen = strbuf_readlink(&symlink, resolved.buf,\n> +\t\t\t\t\t      st.st_size);\n>  \t\t\tif (len < 0) {\n>  \t\t\t\tif (die_on_error)\n>  \t\t\t\t\tdie_errno(\"Invalid symlink '%s'\",\n> -\t\t\t\t\t\t  sb.buf);\n> +\t\t\t\t\t\t  resolved.buf);\n>  \t\t\t\telse\n>  \t\t\t\t\tgoto error_out;\n>  \t\t\t}\n> -\t\t\tstrbuf_swap(&sb, &next_sb);\n> -\t\t\tstrbuf_release(&next_sb);\n> -\t\t} else\n> -\t\t\tbreak;\n> +\n> +\t\t\tif (is_absolute_path(symlink.buf)) {\n> +\t\t\t\t/* absolute symlink; set resolved to root */\n> +\t\t\t\tint offset = offset_1st_component(symlink.buf);\n> +\t\t\t\tstrbuf_reset(&resolved);\n> +\t\t\t\tstrbuf_add(&resolved, symlink.buf, offset);\n> +\t\t\t\tstrbuf_remove(&symlink, 0, offset);\n\nGood. I would have expected some kind of \"goto repeat;\" because when we \nencounter a symlink to an absolute path, most, if not all, work done so \nfar is obsoleted. But I haven't thought too deeply about this.\n\n> +\t\t\t} else {\n> +\t\t\t\t/*\n> +\t\t\t\t * relative symlink\n> +\t\t\t\t * strip off the last component since it will\n> +\t\t\t\t * be replaced with the contents of the symlink\n> +\t\t\t\t */\n> +\t\t\t\tstrip_last_component(&resolved);\n> +\t\t\t}\n> +\n> +\t\t\t/*\n> +\t\t\t * if there are still remaining components to resolve\n> +\t\t\t * then append them to symlink\n> +\t\t\t */\n> +\t\t\tif (remaining.len) {\n> +\t\t\t\tstrbuf_addch(&symlink, '/');\n> +\t\t\t\tstrbuf_addbuf(&symlink, &remaining);\n> +\t\t\t}\n> +\n> +\t\t\t/*\n> +\t\t\t * use the symlink as the remaining components that\n> +\t\t\t * need to be resloved\n> +\t\t\t */\n> +\t\t\tstrbuf_swap(&symlink, &remaining);\n\nGood. This looks very reasonable.\n\n> +\t\t}\n>  \t}\n>\n> -\tretval = sb.buf;\n> +\tretval = resolved.buf;\n> +\n>  error_out:\n> -\tfree(last_elem);\n> -\tif (cwd.len && chdir(cwd.buf))\n> -\t\tdie_errno(\"Could not change back to '%s'\", cwd.buf);\n> -\tstrbuf_release(&cwd);\n> +\tstrbuf_release(&remaining);\n> +\tstrbuf_release(&next);\n> +\tstrbuf_release(&symlink);\n>\n>  \treturn retval;\n>  }\n>\n\n"},{"id":"307357","messageId":"6deba49f-52f7-7fb5-2f93-24aa411a7710@kdbg.org","threadId":"44625","inReplyTo":"1481241494-6861-4-git-send-email-bmwill@google.com","subject":"Re: [PATCH v2 3/4] real_path: create real_pathdup","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2016-12-09T14:35:21Z","receivedAt":"2016-12-09T14:35:30Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 09.12.2016 um 00:58 schrieb Brandon Williams:\n> +char *real_pathdup(const char *path)\n> +{\n> +\tstruct strbuf realpath = STRBUF_INIT;\n> +\tchar *retval = NULL;\n> +\n> +\tif(strbuf_realpath(&realpath, path, 0))\n\nStyle nit: blank after if is missing.\n\n-- Hannes\n\n"},{"id":"307392","messageId":"20161209194232.GC88637@google.com","threadId":"44625","inReplyTo":"CACsJy8A2M_G34MeHh6vGsrf5ePOOduM6u=n17_EZLtu31uDAYg@mail.gmail.com","subject":"Re: [PATCH v2 0/4] road to reentrant real_path","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-09T19:42:32Z","receivedAt":"2016-12-09T19:42:41Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 12/09, Duy Nguyen wrote:\n> On Fri, Dec 9, 2016 at 6:58 AM, Brandon Williams <bmwill@google.com> wrote:\n> > diff --git a/setup.c b/setup.c\n> > index fe572b8..0d9fdd0 100644\n> > --- a/setup.c\n> > +++ b/setup.c\n> > @@ -254,10 +254,12 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)\n> >                 if (!is_absolute_path(data.buf))\n> >                         strbuf_addf(&path, \"%s/\", gitdir);\n> >                 strbuf_addbuf(&path, &data);\n> > -               strbuf_addstr(sb, real_path(path.buf));\n> > +               strbuf_realpath(sb, path.buf, 1);\n> \n> This is not the same because of this hunk in strbuf_realpath()\n\nThen perhaps I shouldn't make this change (and just leave it as is)\nsince the way real_path_internal/strbuf_realpath is written requires\nthat the strbuf being used for the resolved path only contains the\nresolved path (see the lstat(resolved->buf &st) call).  Sidenote it\nlooks like strbuf_getcwd() also does a reset, though more subtlety,\nsince it just passes its buffer to getcwd().\n\n> \n> > @@ -81,17 +73,18 @@ static const char *real_path_internal(const char *path, int die_on_error)\n> >                         goto error_out;\n> >         }\n> >\n> > -       strbuf_reset(&resolved);\n> > +       strbuf_reset(resolved);\n> >\n> >         if (is_absolute_path(path)) {\n> \n> But if you you remove that, then all (old) callers of\n> strbuf_realpath() must do a strbuf_reset() in advance if needed\n> (probably just real_path does) which sounds reasonable to me. You're\n> probably want to be careful about the strbuf_reset() at the end of the\n> function too.\n> \n> Other than that, I think this diff looks nice.\n> -- \n> Duy\n\n-- \nBrandon Williams\n"},{"id":"307393","messageId":"20161209200436.GD88637@google.com","threadId":"44625","inReplyTo":"944a3e91-934a-7923-2b2a-639af81e707d@kdbg.org","subject":"Re: [PATCH v2 1/4] real_path: resolve symlinks by hand","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-09T20:04:36Z","receivedAt":"2016-12-09T20:04:45Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 12/09, Johannes Sixt wrote:\n> Am 09.12.2016 um 00:58 schrieb Brandon Williams:\n> >The current implementation of real_path uses chdir() in order to resolve\n> >symlinks.  Unfortunately this isn't thread-safe as chdir() affects a\n> >process as a whole and not just an individual thread.  Instead perform\n> >the symlink resolution by hand so that the calls to chdir() can be\n> >removed, making real_path one step closer to being reentrant.\n> >\n> >Signed-off-by: Brandon Williams <bmwill@google.com>\n> >---\n> > abspath.c | 183 +++++++++++++++++++++++++++++++++++++++++---------------------\n> > 1 file changed, 122 insertions(+), 61 deletions(-)\n> >\n> >diff --git a/abspath.c b/abspath.c\n> >index 2825de8..92f2a29 100644\n> >--- a/abspath.c\n> >+++ b/abspath.c\n> >@@ -11,8 +11,38 @@ int is_directory(const char *path)\n> > \treturn (!stat(path, &st) && S_ISDIR(st.st_mode));\n> > }\n> >\n> >+/* removes the last path component from 'path' except if 'path' is root */\n> >+static void strip_last_component(struct strbuf *path)\n> >+{\n> >+\tif (path->len > offset_1st_component(path->buf)) {\n> >+\t\tchar *last_slash = find_last_dir_sep(path->buf);\n> >+\t\tstrbuf_setlen(path, last_slash - path->buf);\n> >+\t}\n> >+}\n> \n> This implementation is not correct because when the input is \"/foo\",\n> the result is \"\" when it should be \"/\". Also, can the input be a\n> non-normalized path? When the input is \"foo///bar\", should the\n> result be \"foo\" or would \"foo//\" be an acceptable result? I think it\n> should be the former. find_last_dir_sep() returns the last of the\n> three slashes, not the first one. Therefore, I've rewritten the\n> function thusly:\n> \n> static void strip_last_component(struct strbuf *path)\n> {\n> \tsize_t offset = offset_1st_component(path->buf);\n> \tsize_t len = path->len;\n> \twhile (offset < len && !is_dir_sep(path->buf[len - 1]))\n> \t\tlen--;\n> \twhile (offset < len && is_dir_sep(path->buf[len - 1]))\n> \t\tlen--;\n> \tstrbuf_setlen(path, len);\n> }\n> \n\nThanks for that catch.  So your rewrite takes the offset of the 1st\ncomponent and ensures that we don't cut that part off.  It first strips\nall non directory separators and then all directory separators.  This\nway \"/foo////bar\" becomes \"/foo\" and as you pointed out \"/foo\" would\nbecome \"/\".  The offset would also take care of windows drive letters\nand the like.  Looks good.  Thanks!\n\n> >+\t\tstrbuf_addbuf(&resolved, &next);\n> >+\n> >+\t\tif (lstat(resolved.buf, &st)) {\n> >+\t\t\t/* error out unless this was the last component */\n> >+\t\t\tif (!(errno == ENOENT && !remaining.len)) {\n> \n> Perhaps it was easier to _write_ the condition in this way, but I\n> would have an easier time to _read_ it when it is\n> \n> \t\t\tif (errno != ENOENT || remaining.len) {\n> \n\nYes I did write it out weird, mostly because it made the most sense for\nwhat I was trying to accomplish (add path components must exist, except\nfor the very last one).  I'm fine applying DeMorgan's since it looks a\nlittle cleaner.\n\n> >+\n> >+\t\t\tif (is_absolute_path(symlink.buf)) {\n> >+\t\t\t\t/* absolute symlink; set resolved to root */\n> >+\t\t\t\tint offset = offset_1st_component(symlink.buf);\n> >+\t\t\t\tstrbuf_reset(&resolved);\n> >+\t\t\t\tstrbuf_add(&resolved, symlink.buf, offset);\n> >+\t\t\t\tstrbuf_remove(&symlink, 0, offset);\n> \n> Good. I would have expected some kind of \"goto repeat;\" because when\n> we encounter a symlink to an absolute path, most, if not all, work\n> done so far is obsoleted. But I haven't thought too deeply about\n> this.\n\nIt made the most sense to just reuse the same looping condition that\nI already had in place.  Resetting the resolved string to be the root\ncomponent of the absolute symlink made it easy to \"throw away\" all the\nold work to allow us to start from scratch again.\n\n-- \nBrandon Williams\n"},{"id":"307435","messageId":"CACsJy8C=0SPYcwC34zTzfVg=U8xiN=XYWuiEVqP_OK5dJF_SfQ@mail.gmail.com","threadId":"44625","inReplyTo":"20161209194232.GC88637@google.com","subject":"Re: [PATCH v2 0/4] road to reentrant real_path","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-12-10T11:02:44Z","receivedAt":"2016-12-10T11:03:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Dec 10, 2016 at 2:42 AM, Brandon Williams <bmwill@google.com> wrote:\n> On 12/09, Duy Nguyen wrote:\n>> On Fri, Dec 9, 2016 at 6:58 AM, Brandon Williams <bmwill@google.com> wrote:\n>> > diff --git a/setup.c b/setup.c\n>> > index fe572b8..0d9fdd0 100644\n>> > --- a/setup.c\n>> > +++ b/setup.c\n>> > @@ -254,10 +254,12 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)\n>> >                 if (!is_absolute_path(data.buf))\n>> >                         strbuf_addf(&path, \"%s/\", gitdir);\n>> >                 strbuf_addbuf(&path, &data);\n>> > -               strbuf_addstr(sb, real_path(path.buf));\n>> > +               strbuf_realpath(sb, path.buf, 1);\n>>\n>> This is not the same because of this hunk in strbuf_realpath()\n>\n> Then perhaps I shouldn't make this change (and just leave it as is)\n> since the way real_path_internal/strbuf_realpath is written requires\n> that the strbuf being used for the resolved path only contains the\n> resolved path (see the lstat(resolved->buf &st) call).  Sidenote it\n> looks like strbuf_getcwd() also does a reset, though more subtlety,\n> since it just passes its buffer to getcwd().\n\nYeah that's ok too (I did not see this subtlety when I suggested the change).\n-- \nDuy\n"},{"id":"307515","messageId":"1481566615-75299-1-git-send-email-bmwill@google.com","threadId":"44625","inReplyTo":"1481241494-6861-1-git-send-email-bmwill@google.com","subject":"[PATCH v3 0/4] road to reentrant real_path","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-12T18:16:51Z","receivedAt":"2016-12-12T18:17:20Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Changes in v3:\n\n* Rewrite of `strip_last_component()` as the v2 verison didn't properly handle\n  inputs like '/foo'.  Thanks to Johannes for pointing this out and suggesting\n  a solution.\n* Small style changes\n* Revert the call in `get_common_dir_noenv()` to maintain proper functionality.\n\nBrandon Williams (4):\n  real_path: resolve symlinks by hand\n  real_path: convert real_path_internal to strbuf_realpath\n  real_path: create real_pathdup\n  real_path: have callers use real_pathdup and strbuf_realpath\n\n abspath.c         | 222 ++++++++++++++++++++++++++++++++++++------------------\n builtin/init-db.c |   6 +-\n cache.h           |   3 +\n environment.c     |   2 +-\n setup.c           |  13 ++--\n sha1_file.c       |   2 +-\n submodule.c       |   2 +-\n transport.c       |   2 +-\n worktree.c        |   2 +-\n 9 files changed, 169 insertions(+), 85 deletions(-)\n\n--- interdiff from v2\n\ndiff --git a/abspath.c b/abspath.c\nindex df37356..79ee310 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -14,10 +14,17 @@ int is_directory(const char *path)\n /* removes the last path component from 'path' except if 'path' is root */\n static void strip_last_component(struct strbuf *path)\n {\n-\tif (path->len > offset_1st_component(path->buf)) {\n-\t\tchar *last_slash = find_last_dir_sep(path->buf);\n-\t\tstrbuf_setlen(path, last_slash - path->buf);\n-\t}\n+\tsize_t offset = offset_1st_component(path->buf);\n+\tsize_t len = path->len;\n+\n+\t/* Find start of the last component */\n+\twhile (offset < len && !is_dir_sep(path->buf[len - 1]))\n+\t\tlen--;\n+\t/* Skip sequences of multiple path-separators */\n+\twhile (offset < len && is_dir_sep(path->buf[len - 1]))\n+\t\tlen--;\n+\n+\tstrbuf_setlen(path, len);\n }\n \n /* get (and remove) the next component in 'remaining' and place it in 'next' */\n@@ -112,7 +119,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \n \t\tif (lstat(resolved->buf, &st)) {\n \t\t\t/* error out unless this was the last component */\n-\t\t\tif (!(errno == ENOENT && !remaining.len)) {\n+\t\t\tif (errno != ENOENT || remaining.len) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid path '%s'\",\n \t\t\t\t\t\t  resolved->buf);\n@@ -203,7 +210,7 @@ char *real_pathdup(const char *path)\n \tstruct strbuf realpath = STRBUF_INIT;\n \tchar *retval = NULL;\n \n-\tif(strbuf_realpath(&realpath, path, 0))\n+\tif (strbuf_realpath(&realpath, path, 0))\n \t\tretval = strbuf_detach(&realpath, NULL);\n \n \tstrbuf_release(&realpath);\ndiff --git a/setup.c b/setup.c\nindex 0d9fdd0..1b534a7 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -254,7 +254,7 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)\n \t\tif (!is_absolute_path(data.buf))\n \t\t\tstrbuf_addf(&path, \"%s/\", gitdir);\n \t\tstrbuf_addbuf(&path, &data);\n-\t\tstrbuf_realpath(sb, path.buf, 1);\n+\t\tstrbuf_addstr(sb, real_path(path.buf));\n \t\tret = 1;\n \t} else {\n \t\tstrbuf_addstr(sb, gitdir);\n\n-- \n2.8.0.rc3.226.g39d4020\n\n"},{"id":"307516","messageId":"1481566615-75299-2-git-send-email-bmwill@google.com","threadId":"44625","inReplyTo":"1481566615-75299-1-git-send-email-bmwill@google.com","subject":"[PATCH v3 1/4] real_path: resolve symlinks by hand","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-12T18:16:52Z","receivedAt":"2016-12-12T18:17:24Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"The current implementation of real_path uses chdir() in order to resolve\nsymlinks.  Unfortunately this isn't thread-safe as chdir() affects a\nprocess as a whole and not just an individual thread.  Instead perform\nthe symlink resolution by hand so that the calls to chdir() can be\nremoved, making real_path one step closer to being reentrant.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 190 ++++++++++++++++++++++++++++++++++++++++++--------------------\n 1 file changed, 129 insertions(+), 61 deletions(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex 2825de8..cafcae0 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -11,8 +11,45 @@ int is_directory(const char *path)\n \treturn (!stat(path, &st) && S_ISDIR(st.st_mode));\n }\n \n+/* removes the last path component from 'path' except if 'path' is root */\n+static void strip_last_component(struct strbuf *path)\n+{\n+\tsize_t offset = offset_1st_component(path->buf);\n+\tsize_t len = path->len;\n+\n+\t/* Find start of the last component */\n+\twhile (offset < len && !is_dir_sep(path->buf[len - 1]))\n+\t\tlen--;\n+\t/* Skip sequences of multiple path-separators */\n+\twhile (offset < len && is_dir_sep(path->buf[len - 1]))\n+\t\tlen--;\n+\n+\tstrbuf_setlen(path, len);\n+}\n+\n+/* get (and remove) the next component in 'remaining' and place it in 'next' */\n+static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n+{\n+\tchar *start = NULL;\n+\tchar *end = NULL;\n+\n+\tstrbuf_reset(next);\n+\n+\t/* look for the next component */\n+\t/* Skip sequences of multiple path-separators */\n+\tfor (start = remaining->buf; is_dir_sep(*start); start++)\n+\t\t; /* nothing */\n+\t/* Find end of the path component */\n+\tfor (end = start; *end && !is_dir_sep(*end); end++)\n+\t\t; /* nothing */\n+\n+\tstrbuf_add(next, start, end - start);\n+\t/* remove the component from 'remaining' */\n+\tstrbuf_remove(remaining, 0, end - remaining->buf);\n+}\n+\n /* We allow \"recursive\" symbolic links. Only within reason, though. */\n-#define MAXDEPTH 5\n+#define MAXSYMLINKS 5\n \n /*\n  * Return the real path (i.e., absolute path, with symlinks resolved\n@@ -21,7 +58,6 @@ int is_directory(const char *path)\n  * absolute_path().)  The return value is a pointer to a static\n  * buffer.\n  *\n- * The input and all intermediate paths must be shorter than MAX_PATH.\n  * The directory part of path (i.e., everything up to the last\n  * dir_sep) must denote a valid, existing directory, but the last\n  * component need not exist.  If die_on_error is set, then die with an\n@@ -33,22 +69,16 @@ int is_directory(const char *path)\n  */\n static const char *real_path_internal(const char *path, int die_on_error)\n {\n-\tstatic struct strbuf sb = STRBUF_INIT;\n+\tstatic struct strbuf resolved = STRBUF_INIT;\n+\tstruct strbuf remaining = STRBUF_INIT;\n+\tstruct strbuf next = STRBUF_INIT;\n+\tstruct strbuf symlink = STRBUF_INIT;\n \tchar *retval = NULL;\n-\n-\t/*\n-\t * If we have to temporarily chdir(), store the original CWD\n-\t * here so that we can chdir() back to it at the end of the\n-\t * function:\n-\t */\n-\tstruct strbuf cwd = STRBUF_INIT;\n-\n-\tint depth = MAXDEPTH;\n-\tchar *last_elem = NULL;\n+\tint num_symlinks = 0;\n \tstruct stat st;\n \n \t/* We've already done it */\n-\tif (path == sb.buf)\n+\tif (path == resolved.buf)\n \t\treturn path;\n \n \tif (!*path) {\n@@ -58,74 +88,112 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tgoto error_out;\n \t}\n \n-\tstrbuf_reset(&sb);\n-\tstrbuf_addstr(&sb, path);\n-\n-\twhile (depth--) {\n-\t\tif (!is_directory(sb.buf)) {\n-\t\t\tchar *last_slash = find_last_dir_sep(sb.buf);\n-\t\t\tif (last_slash) {\n-\t\t\t\tlast_elem = xstrdup(last_slash + 1);\n-\t\t\t\tstrbuf_setlen(&sb, last_slash - sb.buf + 1);\n-\t\t\t} else {\n-\t\t\t\tlast_elem = xmemdupz(sb.buf, sb.len);\n-\t\t\t\tstrbuf_reset(&sb);\n-\t\t\t}\n+\tstrbuf_reset(&resolved);\n+\n+\tif (is_absolute_path(path)) {\n+\t\t/* absolute path; start with only root as being resolved */\n+\t\tint offset = offset_1st_component(path);\n+\t\tstrbuf_add(&resolved, path, offset);\n+\t\tstrbuf_addstr(&remaining, path + offset);\n+\t} else {\n+\t\t/* relative path; can use CWD as the initial resolved path */\n+\t\tif (strbuf_getcwd(&resolved)) {\n+\t\t\tif (die_on_error)\n+\t\t\t\tdie_errno(\"unable to get current working directory\");\n+\t\t\telse\n+\t\t\t\tgoto error_out;\n \t\t}\n+\t\tstrbuf_addstr(&remaining, path);\n+\t}\n \n-\t\tif (sb.len) {\n-\t\t\tif (!cwd.len && strbuf_getcwd(&cwd)) {\n+\t/* Iterate over the remaining path components */\n+\twhile (remaining.len > 0) {\n+\t\tget_next_component(&next, &remaining);\n+\n+\t\tif (next.len == 0) {\n+\t\t\tcontinue; /* empty component */\n+\t\t} else if (next.len == 1 && !strcmp(next.buf, \".\")) {\n+\t\t\tcontinue; /* '.' component */\n+\t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n+\t\t\t/* '..' component; strip the last path component */\n+\t\t\tstrip_last_component(&resolved);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* append the next component and resolve resultant path */\n+\t\tif (!is_dir_sep(resolved.buf[resolved.len - 1]))\n+\t\t\tstrbuf_addch(&resolved, '/');\n+\t\tstrbuf_addbuf(&resolved, &next);\n+\n+\t\tif (lstat(resolved.buf, &st)) {\n+\t\t\t/* error out unless this was the last component */\n+\t\t\tif (errno != ENOENT || remaining.len) {\n \t\t\t\tif (die_on_error)\n-\t\t\t\t\tdie_errno(\"Could not get current working directory\");\n+\t\t\t\t\tdie_errno(\"Invalid path '%s'\",\n+\t\t\t\t\t\t  resolved.buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n+\t\t} else if (S_ISLNK(st.st_mode)) {\n+\t\t\tssize_t len;\n+\t\t\tstrbuf_reset(&symlink);\n \n-\t\t\tif (chdir(sb.buf)) {\n+\t\t\tif (num_symlinks++ > MAXSYMLINKS) {\n \t\t\t\tif (die_on_error)\n-\t\t\t\t\tdie_errno(\"Could not switch to '%s'\",\n-\t\t\t\t\t\t  sb.buf);\n+\t\t\t\t\tdie(\"More than %d nested symlinks \"\n+\t\t\t\t\t    \"on path '%s'\", MAXSYMLINKS, path);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n-\t\t}\n-\t\tif (strbuf_getcwd(&sb)) {\n-\t\t\tif (die_on_error)\n-\t\t\t\tdie_errno(\"Could not get current working directory\");\n-\t\t\telse\n-\t\t\t\tgoto error_out;\n-\t\t}\n-\n-\t\tif (last_elem) {\n-\t\t\tif (sb.len && !is_dir_sep(sb.buf[sb.len - 1]))\n-\t\t\t\tstrbuf_addch(&sb, '/');\n-\t\t\tstrbuf_addstr(&sb, last_elem);\n-\t\t\tfree(last_elem);\n-\t\t\tlast_elem = NULL;\n-\t\t}\n \n-\t\tif (!lstat(sb.buf, &st) && S_ISLNK(st.st_mode)) {\n-\t\t\tstruct strbuf next_sb = STRBUF_INIT;\n-\t\t\tssize_t len = strbuf_readlink(&next_sb, sb.buf, 0);\n+\t\t\tlen = strbuf_readlink(&symlink, resolved.buf,\n+\t\t\t\t\t      st.st_size);\n \t\t\tif (len < 0) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid symlink '%s'\",\n-\t\t\t\t\t\t  sb.buf);\n+\t\t\t\t\t\t  resolved.buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n-\t\t\tstrbuf_swap(&sb, &next_sb);\n-\t\t\tstrbuf_release(&next_sb);\n-\t\t} else\n-\t\t\tbreak;\n+\n+\t\t\tif (is_absolute_path(symlink.buf)) {\n+\t\t\t\t/* absolute symlink; set resolved to root */\n+\t\t\t\tint offset = offset_1st_component(symlink.buf);\n+\t\t\t\tstrbuf_reset(&resolved);\n+\t\t\t\tstrbuf_add(&resolved, symlink.buf, offset);\n+\t\t\t\tstrbuf_remove(&symlink, 0, offset);\n+\t\t\t} else {\n+\t\t\t\t/*\n+\t\t\t\t * relative symlink\n+\t\t\t\t * strip off the last component since it will\n+\t\t\t\t * be replaced with the contents of the symlink\n+\t\t\t\t */\n+\t\t\t\tstrip_last_component(&resolved);\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * if there are still remaining components to resolve\n+\t\t\t * then append them to symlink\n+\t\t\t */\n+\t\t\tif (remaining.len) {\n+\t\t\t\tstrbuf_addch(&symlink, '/');\n+\t\t\t\tstrbuf_addbuf(&symlink, &remaining);\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * use the symlink as the remaining components that\n+\t\t\t * need to be resloved\n+\t\t\t */\n+\t\t\tstrbuf_swap(&symlink, &remaining);\n+\t\t}\n \t}\n \n-\tretval = sb.buf;\n+\tretval = resolved.buf;\n+\n error_out:\n-\tfree(last_elem);\n-\tif (cwd.len && chdir(cwd.buf))\n-\t\tdie_errno(\"Could not change back to '%s'\", cwd.buf);\n-\tstrbuf_release(&cwd);\n+\tstrbuf_release(&remaining);\n+\tstrbuf_release(&next);\n+\tstrbuf_release(&symlink);\n \n \treturn retval;\n }\n-- \n2.8.0.rc3.226.g39d4020\n\n"},{"id":"307517","messageId":"1481566615-75299-5-git-send-email-bmwill@google.com","threadId":"44625","inReplyTo":"1481566615-75299-1-git-send-email-bmwill@google.com","subject":"[PATCH v3 4/4] real_path: have callers use real_pathdup and strbuf_realpath","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-12T18:16:55Z","receivedAt":"2016-12-12T18:17:25Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Migrate callers of real_path() who duplicate the retern value to use\nreal_pathdup or strbuf_realpath.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/init-db.c |  6 +++---\n environment.c     |  2 +-\n setup.c           | 13 ++++++++-----\n sha1_file.c       |  2 +-\n submodule.c       |  2 +-\n transport.c       |  2 +-\n worktree.c        |  2 +-\n 7 files changed, 16 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 2399b97..76d68fa 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -338,7 +338,7 @@ int init_db(const char *git_dir, const char *real_git_dir,\n {\n \tint reinit;\n \tint exist_ok = flags & INIT_DB_EXIST_OK;\n-\tchar *original_git_dir = xstrdup(real_path(git_dir));\n+\tchar *original_git_dir = real_pathdup(git_dir);\n \n \tif (real_git_dir) {\n \t\tstruct stat st;\n@@ -489,7 +489,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);\n \n \tif (real_git_dir && !is_absolute_path(real_git_dir))\n-\t\treal_git_dir = xstrdup(real_path(real_git_dir));\n+\t\treal_git_dir = real_pathdup(real_git_dir);\n \n \tif (argc == 1) {\n \t\tint mkdir_tried = 0;\n@@ -560,7 +560,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t\tconst char *git_dir_parent = strrchr(git_dir, '/');\n \t\tif (git_dir_parent) {\n \t\t\tchar *rel = xstrndup(git_dir, git_dir_parent - git_dir);\n-\t\t\tgit_work_tree_cfg = xstrdup(real_path(rel));\n+\t\t\tgit_work_tree_cfg = real_pathdup(rel);\n \t\t\tfree(rel);\n \t\t}\n \t\tif (!git_work_tree_cfg)\ndiff --git a/environment.c b/environment.c\nindex 0935ec6..9b943d2 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -259,7 +259,7 @@ void set_git_work_tree(const char *new_work_tree)\n \t\treturn;\n \t}\n \tgit_work_tree_initialized = 1;\n-\twork_tree = xstrdup(real_path(new_work_tree));\n+\twork_tree = real_pathdup(new_work_tree);\n }\n \n const char *get_git_work_tree(void)\ndiff --git a/setup.c b/setup.c\nindex fe572b8..1b534a7 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -256,8 +256,10 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)\n \t\tstrbuf_addbuf(&path, &data);\n \t\tstrbuf_addstr(sb, real_path(path.buf));\n \t\tret = 1;\n-\t} else\n+\t} else {\n \t\tstrbuf_addstr(sb, gitdir);\n+\t}\n+\n \tstrbuf_release(&data);\n \tstrbuf_release(&path);\n \treturn ret;\n@@ -692,7 +694,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,\n \t/* --work-tree is set without --git-dir; use discovered one */\n \tif (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {\n \t\tif (offset != cwd->len && !is_absolute_path(gitdir))\n-\t\t\tgitdir = xstrdup(real_path(gitdir));\n+\t\t\tgitdir = real_pathdup(gitdir);\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(\"Could not come back to cwd\");\n \t\treturn setup_explicit_git_dir(gitdir, cwd, nongit_ok);\n@@ -800,11 +802,12 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,\n \t\t/* Keep entry but do not canonicalize it */\n \t\treturn 1;\n \t} else {\n-\t\tconst char *real_path = real_path_if_valid(ceil);\n-\t\tif (!real_path)\n+\t\tchar *real_path = real_pathdup(ceil);\n+\t\tif (!real_path) {\n \t\t\treturn 0;\n+\t\t}\n \t\tfree(item->string);\n-\t\titem->string = xstrdup(real_path);\n+\t\titem->string = real_path;\n \t\treturn 1;\n \t}\n }\ndiff --git a/sha1_file.c b/sha1_file.c\nindex 9c86d19..6a03cc3 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -291,7 +291,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,\n \tstruct strbuf pathbuf = STRBUF_INIT;\n \n \tif (!is_absolute_path(entry) && relative_base) {\n-\t\tstrbuf_addstr(&pathbuf, real_path(relative_base));\n+\t\tstrbuf_realpath(&pathbuf, relative_base, 1);\n \t\tstrbuf_addch(&pathbuf, '/');\n \t}\n \tstrbuf_addstr(&pathbuf, entry);\ndiff --git a/submodule.c b/submodule.c\nindex 6f7d883..c85ba50 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1227,7 +1227,7 @@ void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)\n {\n \tstruct strbuf file_name = STRBUF_INIT;\n \tstruct strbuf rel_path = STRBUF_INIT;\n-\tconst char *real_work_tree = xstrdup(real_path(work_tree));\n+\tconst char *real_work_tree = real_pathdup(work_tree);\n \n \t/* Update gitfile */\n \tstrbuf_addf(&file_name, \"%s/.git\", work_tree);\ndiff --git a/transport.c b/transport.c\nindex d57e8de..236c6f6 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1130,7 +1130,7 @@ static int refs_from_alternate_cb(struct alternate_object_database *e,\n \tconst struct ref *extra;\n \tstruct alternate_refs_data *cb = data;\n \n-\tother = xstrdup(real_path(e->path));\n+\tother = real_pathdup(e->path);\n \tlen = strlen(other);\n \n \twhile (other[len-1] == '/')\ndiff --git a/worktree.c b/worktree.c\nindex f7869f8..c90e013 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -255,7 +255,7 @@ struct worktree *find_worktree(struct worktree **list,\n \t\treturn wt;\n \n \targ = prefix_filename(prefix, strlen(prefix), arg);\n-\tpath = xstrdup(real_path(arg));\n+\tpath = real_pathdup(arg);\n \tfor (; *list; list++)\n \t\tif (!fspathcmp(path, real_path((*list)->path)))\n \t\t\tbreak;\n-- \n2.8.0.rc3.226.g39d4020\n\n"},{"id":"307518","messageId":"1481566615-75299-3-git-send-email-bmwill@google.com","threadId":"44625","inReplyTo":"1481566615-75299-1-git-send-email-bmwill@google.com","subject":"[PATCH v3 2/4] real_path: convert real_path_internal to strbuf_realpath","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-12T18:16:53Z","receivedAt":"2016-12-12T18:17:26Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Change the name of real_path_internal to strbuf_realpath.  In addition\npush the static strbuf up to its callers and instead take as a\nparameter a pointer to a strbuf to use for the final result.\n\nThis change makes strbuf_realpath reentrant.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 53 +++++++++++++++++++++++++----------------------------\n cache.h   |  2 ++\n 2 files changed, 27 insertions(+), 28 deletions(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex cafcae0..8c6c76b 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -55,21 +55,17 @@ static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n  * Return the real path (i.e., absolute path, with symlinks resolved\n  * and extra slashes removed) equivalent to the specified path.  (If\n  * you want an absolute path but don't mind links, use\n- * absolute_path().)  The return value is a pointer to a static\n- * buffer.\n+ * absolute_path().)  Places the resolved realpath in the provided strbuf.\n  *\n  * The directory part of path (i.e., everything up to the last\n  * dir_sep) must denote a valid, existing directory, but the last\n  * component need not exist.  If die_on_error is set, then die with an\n  * informative error message if there is a problem.  Otherwise, return\n  * NULL on errors (without generating any output).\n- *\n- * If path is our buffer, then return path, as it's already what the\n- * user wants.\n  */\n-static const char *real_path_internal(const char *path, int die_on_error)\n+char *strbuf_realpath(struct strbuf *resolved, const char *path,\n+\t\t      int die_on_error)\n {\n-\tstatic struct strbuf resolved = STRBUF_INIT;\n \tstruct strbuf remaining = STRBUF_INIT;\n \tstruct strbuf next = STRBUF_INIT;\n \tstruct strbuf symlink = STRBUF_INIT;\n@@ -77,10 +73,6 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \tint num_symlinks = 0;\n \tstruct stat st;\n \n-\t/* We've already done it */\n-\tif (path == resolved.buf)\n-\t\treturn path;\n-\n \tif (!*path) {\n \t\tif (die_on_error)\n \t\t\tdie(\"The empty string is not a valid path\");\n@@ -88,16 +80,16 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tgoto error_out;\n \t}\n \n-\tstrbuf_reset(&resolved);\n+\tstrbuf_reset(resolved);\n \n \tif (is_absolute_path(path)) {\n \t\t/* absolute path; start with only root as being resolved */\n \t\tint offset = offset_1st_component(path);\n-\t\tstrbuf_add(&resolved, path, offset);\n+\t\tstrbuf_add(resolved, path, offset);\n \t\tstrbuf_addstr(&remaining, path + offset);\n \t} else {\n \t\t/* relative path; can use CWD as the initial resolved path */\n-\t\tif (strbuf_getcwd(&resolved)) {\n+\t\tif (strbuf_getcwd(resolved)) {\n \t\t\tif (die_on_error)\n \t\t\t\tdie_errno(\"unable to get current working directory\");\n \t\t\telse\n@@ -116,21 +108,21 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tcontinue; /* '.' component */\n \t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n \t\t\t/* '..' component; strip the last path component */\n-\t\t\tstrip_last_component(&resolved);\n+\t\t\tstrip_last_component(resolved);\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* append the next component and resolve resultant path */\n-\t\tif (!is_dir_sep(resolved.buf[resolved.len - 1]))\n-\t\t\tstrbuf_addch(&resolved, '/');\n-\t\tstrbuf_addbuf(&resolved, &next);\n+\t\tif (!is_dir_sep(resolved->buf[resolved->len - 1]))\n+\t\t\tstrbuf_addch(resolved, '/');\n+\t\tstrbuf_addbuf(resolved, &next);\n \n-\t\tif (lstat(resolved.buf, &st)) {\n+\t\tif (lstat(resolved->buf, &st)) {\n \t\t\t/* error out unless this was the last component */\n \t\t\tif (errno != ENOENT || remaining.len) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid path '%s'\",\n-\t\t\t\t\t\t  resolved.buf);\n+\t\t\t\t\t\t  resolved->buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n@@ -146,12 +138,12 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n \n-\t\t\tlen = strbuf_readlink(&symlink, resolved.buf,\n+\t\t\tlen = strbuf_readlink(&symlink, resolved->buf,\n \t\t\t\t\t      st.st_size);\n \t\t\tif (len < 0) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid symlink '%s'\",\n-\t\t\t\t\t\t  resolved.buf);\n+\t\t\t\t\t\t  resolved->buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n@@ -159,8 +151,8 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tif (is_absolute_path(symlink.buf)) {\n \t\t\t\t/* absolute symlink; set resolved to root */\n \t\t\t\tint offset = offset_1st_component(symlink.buf);\n-\t\t\t\tstrbuf_reset(&resolved);\n-\t\t\t\tstrbuf_add(&resolved, symlink.buf, offset);\n+\t\t\t\tstrbuf_reset(resolved);\n+\t\t\t\tstrbuf_add(resolved, symlink.buf, offset);\n \t\t\t\tstrbuf_remove(&symlink, 0, offset);\n \t\t\t} else {\n \t\t\t\t/*\n@@ -168,7 +160,7 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\t\t * strip off the last component since it will\n \t\t\t\t * be replaced with the contents of the symlink\n \t\t\t\t */\n-\t\t\t\tstrip_last_component(&resolved);\n+\t\t\t\tstrip_last_component(resolved);\n \t\t\t}\n \n \t\t\t/*\n@@ -188,24 +180,29 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t}\n \t}\n \n-\tretval = resolved.buf;\n+\tretval = resolved->buf;\n \n error_out:\n \tstrbuf_release(&remaining);\n \tstrbuf_release(&next);\n \tstrbuf_release(&symlink);\n \n+\tif (!retval)\n+\t\tstrbuf_reset(resolved);\n+\n \treturn retval;\n }\n \n const char *real_path(const char *path)\n {\n-\treturn real_path_internal(path, 1);\n+\tstatic struct strbuf realpath = STRBUF_INIT;\n+\treturn strbuf_realpath(&realpath, path, 1);\n }\n \n const char *real_path_if_valid(const char *path)\n {\n-\treturn real_path_internal(path, 0);\n+\tstatic struct strbuf realpath = STRBUF_INIT;\n+\treturn strbuf_realpath(&realpath, path, 0);\n }\n \n /*\ndiff --git a/cache.h b/cache.h\nindex a50a61a..7a81294 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1064,6 +1064,8 @@ static inline int is_absolute_path(const char *path)\n \treturn is_dir_sep(path[0]) || has_dos_drive_prefix(path);\n }\n int is_directory(const char *);\n+char *strbuf_realpath(struct strbuf *resolved, const char *path,\n+\t\t      int die_on_error);\n const char *real_path(const char *path);\n const char *real_path_if_valid(const char *path);\n const char *absolute_path(const char *path);\n-- \n2.8.0.rc3.226.g39d4020\n\n"},{"id":"307519","messageId":"1481566615-75299-4-git-send-email-bmwill@google.com","threadId":"44625","inReplyTo":"1481566615-75299-1-git-send-email-bmwill@google.com","subject":"[PATCH v3 3/4] real_path: create real_pathdup","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-12T18:16:54Z","receivedAt":"2016-12-12T18:17:27Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Create real_pathdup which returns a caller owned string of the resolved\nrealpath based on the provide path.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 13 +++++++++++++\n cache.h   |  1 +\n 2 files changed, 14 insertions(+)\n\ndiff --git a/abspath.c b/abspath.c\nindex 8c6c76b..79ee310 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -205,6 +205,19 @@ const char *real_path_if_valid(const char *path)\n \treturn strbuf_realpath(&realpath, path, 0);\n }\n \n+char *real_pathdup(const char *path)\n+{\n+\tstruct strbuf realpath = STRBUF_INIT;\n+\tchar *retval = NULL;\n+\n+\tif (strbuf_realpath(&realpath, path, 0))\n+\t\tretval = strbuf_detach(&realpath, NULL);\n+\n+\tstrbuf_release(&realpath);\n+\n+\treturn retval;\n+}\n+\n /*\n  * Use this to get an absolute path from a relative one. If you want\n  * to resolve links, you should use real_path.\ndiff --git a/cache.h b/cache.h\nindex 7a81294..e12a5d9 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1068,6 +1068,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t      int die_on_error);\n const char *real_path(const char *path);\n const char *real_path_if_valid(const char *path);\n+char *real_pathdup(const char *path);\n const char *absolute_path(const char *path);\n const char *remove_leading_path(const char *in, const char *prefix);\n const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);\n-- \n2.8.0.rc3.226.g39d4020\n\n"},{"id":"307550","messageId":"xmqqd1gw94f1.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"1481566615-75299-2-git-send-email-bmwill@google.com","subject":"Re: [PATCH v3 1/4] real_path: resolve symlinks by hand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-12T22:19:14Z","receivedAt":"2016-12-12T22:19:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> +/* removes the last path component from 'path' except if 'path' is root */\n> +static void strip_last_component(struct strbuf *path)\n> +{\n> +\tsize_t offset = offset_1st_component(path->buf);\n> +\tsize_t len = path->len;\n> +\n> +\t/* Find start of the last component */\n> +\twhile (offset < len && !is_dir_sep(path->buf[len - 1]))\n> +\t\tlen--;\n\nIf somebody at a higher level in the callchain has already\nnormalized path, this is not a problem, but this will behave\n\"unexpectedly\" when path ends with a dir_sep byte (or more).\n\nE.g. for input path \"foo/bar/\", the above loop runs zero times and\nthen ...\n\n> +\t/* Skip sequences of multiple path-separators */\n> +\twhile (offset < len && is_dir_sep(path->buf[len - 1]))\n> +\t\tlen--;\n\n... the slash at the end is removed, leaving \"foo/bar\" in path.\n\n> +\tstrbuf_setlen(path, len);\n> +}\n> ...\n> +/* get (and remove) the next component in 'remaining' and place it in 'next' */\n> +static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n> +{\n> +\tchar *start = NULL;\n> +\tchar *end = NULL;\n> +\n> +\tstrbuf_reset(next);\n> +\n> +\t/* look for the next component */\n> +\t/* Skip sequences of multiple path-separators */\n> +\tfor (start = remaining->buf; is_dir_sep(*start); start++)\n> +\t\t; /* nothing */\n> +\t/* Find end of the path component */\n> +\tfor (end = start; *end && !is_dir_sep(*end); end++)\n> +\t\t; /* nothing */\n> +\n> +\tstrbuf_add(next, start, end - start);\n> +\t/* remove the component from 'remaining' */\n> +\tstrbuf_remove(remaining, 0, end - remaining->buf);\n> +}\n\nUnlike the strip_last_component(), I think this one is more\ncarefully done and avoids getting fooled by //extra//slashes// at\nthe beginning or at the end, which does help in the correctness of\nthe loop we see below.\n\n> @@ -58,74 +88,112 @@ static const char *real_path_internal(const char *path, int die_on_error)\n>  \t\t\tgoto error_out;\n>  \t}\n>  \n> +\tstrbuf_reset(&resolved);\n> +\n> +\tif (is_absolute_path(path)) {\n> +\t\t/* absolute path; start with only root as being resolved */\n> +\t\tint offset = offset_1st_component(path);\n> +\t\tstrbuf_add(&resolved, path, offset);\n> +\t\tstrbuf_addstr(&remaining, path + offset);\n> +\t} else {\n> +\t\t/* relative path; can use CWD as the initial resolved path */\n> +\t\tif (strbuf_getcwd(&resolved)) {\n> +\t\t\tif (die_on_error)\n> +\t\t\t\tdie_errno(\"unable to get current working directory\");\n> +\t\t\telse\n> +\t\t\t\tgoto error_out;\n>  \t\t}\n> +\t\tstrbuf_addstr(&remaining, path);\n> +\t}\n>  \n> +\t/* Iterate over the remaining path components */\n> +\twhile (remaining.len > 0) {\n> +\t\tget_next_component(&next, &remaining);\n> +\n> +\t\tif (next.len == 0) {\n> +\t\t\tcontinue; /* empty component */\n> +\t\t} else if (next.len == 1 && !strcmp(next.buf, \".\")) {\n> +\t\t\tcontinue; /* '.' component */\n> +\t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n> +\t\t\t/* '..' component; strip the last path component */\n> +\t\t\tstrip_last_component(&resolved);\n\nWouldn't this let \"resolved\" eventually run out of the path\ncomponents to strip for a malformed input e.g. \"/a/../../b\"?\n\n> + ...\n> +\t\t\t/*\n> +\t\t\t * if there are still remaining components to resolve\n> +\t\t\t * then append them to symlink\n> +\t\t\t */\n> +\t\t\tif (remaining.len) {\n> +\t\t\t\tstrbuf_addch(&symlink, '/');\n\nThis can add duplicate dir_sep if readlink(2)'ed contents of the\nsymbolic link already ends with a slash, but I think it (together\nwith the fact that the code does nothing to normalize what is read\nfrom the symbolic link) probably does not matter, given the way how\nget_next_component() is implemented.\n\n> +\t\t\t\tstrbuf_addbuf(&symlink, &remaining);\n> +\t\t\t}\n> +\n> +\t\t\t/*\n> +\t\t\t * use the symlink as the remaining components that\n> +\t\t\t * need to be resloved\n> +\t\t\t */\n> +\t\t\tstrbuf_swap(&symlink, &remaining);\n> +\t\t}\n>  \t}\n>  \n> +\tretval = resolved.buf;\n> +\n>  error_out:\n> +\tstrbuf_release(&remaining);\n> +\tstrbuf_release(&next);\n> +\tstrbuf_release(&symlink);\n>  \n>  \treturn retval;\n>  }\n\n"},{"id":"307551","messageId":"xmqq8trk94ck.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"1481566615-75299-3-git-send-email-bmwill@google.com","subject":"Re: [PATCH v3 2/4] real_path: convert real_path_internal to strbuf_realpath","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-12T22:20:43Z","receivedAt":"2016-12-12T22:20:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> Change the name of real_path_internal to strbuf_realpath.  In addition\n> push the static strbuf up to its callers and instead take as a\n> parameter a pointer to a strbuf to use for the final result.\n>\n> This change makes strbuf_realpath reentrant.\n\nYup, this step makes sense.\n"},{"id":"307552","messageId":"xmqq4m289445.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"1481566615-75299-4-git-send-email-bmwill@google.com","subject":"Re: [PATCH v3 3/4] real_path: create real_pathdup","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-12T22:25:46Z","receivedAt":"2016-12-12T22:25:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> Create real_pathdup which returns a caller owned string of the resolved\n> realpath based on the provide path.\n>\n> Signed-off-by: Brandon Williams <bmwill@google.com>\n> ---\n>  abspath.c | 13 +++++++++++++\n>  cache.h   |  1 +\n>  2 files changed, 14 insertions(+)\n>\n> diff --git a/abspath.c b/abspath.c\n> index 8c6c76b..79ee310 100644\n> --- a/abspath.c\n> +++ b/abspath.c\n> @@ -205,6 +205,19 @@ const char *real_path_if_valid(const char *path)\n>  \treturn strbuf_realpath(&realpath, path, 0);\n>  }\n>  \n> +char *real_pathdup(const char *path)\n> +{\n> +\tstruct strbuf realpath = STRBUF_INIT;\n> +\tchar *retval = NULL;\n> +\n> +\tif (strbuf_realpath(&realpath, path, 0))\n> +\t\tretval = strbuf_detach(&realpath, NULL);\n> +\n> +\tstrbuf_release(&realpath);\n> +\n> +\treturn retval;\n> +}\n\nOK.  Taken alone, the above makes a reader wonder if it still makes\nsense for strbuf_realpath() to return realpath.buf (or NULL upon\nerror).  An obvious alternative is to return 0 on success and -1 on\nfailure.\n\nBut other callers of strbuf_realpath() are mimicking the historical\n\"give a path in 'const char *', receive a path in 'const char *'\nthat you have to xstrdup() if you want to keep it\" interface, and\nthis interface may be easier for them.  I dunno.\n\n>  /*\n>   * Use this to get an absolute path from a relative one. If you want\n>   * to resolve links, you should use real_path.\n> diff --git a/cache.h b/cache.h\n> index 7a81294..e12a5d9 100644\n> --- a/cache.h\n> +++ b/cache.h\n> @@ -1068,6 +1068,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n>  \t\t      int die_on_error);\n>  const char *real_path(const char *path);\n>  const char *real_path_if_valid(const char *path);\n> +char *real_pathdup(const char *path);\n>  const char *absolute_path(const char *path);\n>  const char *remove_leading_path(const char *in, const char *prefix);\n>  const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);\n"},{"id":"307553","messageId":"xmqqzik07pin.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"1481566615-75299-5-git-send-email-bmwill@google.com","subject":"Re: [PATCH v3 4/4] real_path: have callers use real_pathdup and strbuf_realpath","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-12T22:26:24Z","receivedAt":"2016-12-12T22:26:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> Migrate callers of real_path() who duplicate the retern value to use\n> real_pathdup or strbuf_realpath.\n\nLooks good.\n"},{"id":"307556","messageId":"20161212225006.GB193413@google.com","threadId":"44625","inReplyTo":"xmqqd1gw94f1.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 1/4] real_path: resolve symlinks by hand","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-12T22:50:06Z","receivedAt":"2016-12-12T22:50:14Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 12/12, Junio C Hamano wrote:\n> Brandon Williams <bmwill@google.com> writes:\n> \n> > +/* removes the last path component from 'path' except if 'path' is root */\n> > +static void strip_last_component(struct strbuf *path)\n> > +{\n> > +\tsize_t offset = offset_1st_component(path->buf);\n> > +\tsize_t len = path->len;\n> > +\n> > +\t/* Find start of the last component */\n> > +\twhile (offset < len && !is_dir_sep(path->buf[len - 1]))\n> > +\t\tlen--;\n> \n> If somebody at a higher level in the callchain has already\n> normalized path, this is not a problem, but this will behave\n> \"unexpectedly\" when path ends with a dir_sep byte (or more).\n> \n> E.g. for input path \"foo/bar/\", the above loop runs zero times and\n> then ...\n> \n> > +\t/* Skip sequences of multiple path-separators */\n> > +\twhile (offset < len && is_dir_sep(path->buf[len - 1]))\n> > +\t\tlen--;\n> \n> ... the slash at the end is removed, leaving \"foo/bar\" in path.\n> \n\nThe way this is currently used I don't believe this scenario can happen\n(since input to this shouldn't have trailing slashes), but if others\nbegin to use this function then yes, that is an implicit assumption.  I\nthink it may be an ok assumption though since this is only called on\n\"resolved\" which is the ouput and needs to be normalized to begin with. To\nfix this we could simply add the while loop that strips dir_sep at the\nbeginning as well as at the end, like so:\n\n  /* Skip sequences of multiple path-separators */\n  while (offset < len && is_dir_sep(path->buf[len - 1]))\n  \tlen--;\n  /* Skip sequences of multiple path-separators */\n  while (offset < len && !is_dir_sep(path->buf[len - 1]))\n  \tlen--;\n  /* Skip sequences of multiple path-separators */\n  while (offset < len && is_dir_sep(path->buf[len - 1]))\n  \tlen--;\n\n> > +\tstrbuf_setlen(path, len);\n> > +}\n> > ...\n> > +/* get (and remove) the next component in 'remaining' and place it in 'next' */\n> > +static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n> > +{\n> > +\tchar *start = NULL;\n> > +\tchar *end = NULL;\n> > +\n> > +\tstrbuf_reset(next);\n> > +\n> > +\t/* look for the next component */\n> > +\t/* Skip sequences of multiple path-separators */\n> > +\tfor (start = remaining->buf; is_dir_sep(*start); start++)\n> > +\t\t; /* nothing */\n> > +\t/* Find end of the path component */\n> > +\tfor (end = start; *end && !is_dir_sep(*end); end++)\n> > +\t\t; /* nothing */\n> > +\n> > +\tstrbuf_add(next, start, end - start);\n> > +\t/* remove the component from 'remaining' */\n> > +\tstrbuf_remove(remaining, 0, end - remaining->buf);\n> > +}\n> \n> Unlike the strip_last_component(), I think this one is more\n> carefully done and avoids getting fooled by //extra//slashes// at\n> the beginning or at the end, which does help in the correctness of\n> the loop we see below.\n> \n> > @@ -58,74 +88,112 @@ static const char *real_path_internal(const char *path, int die_on_error)\n> >  \t\t\tgoto error_out;\n> >  \t}\n> >  \n> > +\tstrbuf_reset(&resolved);\n> > +\n> > +\tif (is_absolute_path(path)) {\n> > +\t\t/* absolute path; start with only root as being resolved */\n> > +\t\tint offset = offset_1st_component(path);\n> > +\t\tstrbuf_add(&resolved, path, offset);\n> > +\t\tstrbuf_addstr(&remaining, path + offset);\n> > +\t} else {\n> > +\t\t/* relative path; can use CWD as the initial resolved path */\n> > +\t\tif (strbuf_getcwd(&resolved)) {\n> > +\t\t\tif (die_on_error)\n> > +\t\t\t\tdie_errno(\"unable to get current working directory\");\n> > +\t\t\telse\n> > +\t\t\t\tgoto error_out;\n> >  \t\t}\n> > +\t\tstrbuf_addstr(&remaining, path);\n> > +\t}\n> >  \n> > +\t/* Iterate over the remaining path components */\n> > +\twhile (remaining.len > 0) {\n> > +\t\tget_next_component(&next, &remaining);\n> > +\n> > +\t\tif (next.len == 0) {\n> > +\t\t\tcontinue; /* empty component */\n> > +\t\t} else if (next.len == 1 && !strcmp(next.buf, \".\")) {\n> > +\t\t\tcontinue; /* '.' component */\n> > +\t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n> > +\t\t\t/* '..' component; strip the last path component */\n> > +\t\t\tstrip_last_component(&resolved);\n> \n> Wouldn't this let \"resolved\" eventually run out of the path\n> components to strip for a malformed input e.g. \"/a/../../b\"?\n> \n\nAs I understand it, that path is correct and would resolve to \"/b\".  The\nstrip_last_component function doesn't allow striping the \"1st\" component\nor the \"root\" component.  So if resolved is \"/\" and we encounter a \"..\"\nwhich requires striping of the last component, the result would be \"/\".\n\n> > + ...\n> > +\t\t\t/*\n> > +\t\t\t * if there are still remaining components to resolve\n> > +\t\t\t * then append them to symlink\n> > +\t\t\t */\n> > +\t\t\tif (remaining.len) {\n> > +\t\t\t\tstrbuf_addch(&symlink, '/');\n> \n> This can add duplicate dir_sep if readlink(2)'ed contents of the\n> symbolic link already ends with a slash, but I think it (together\n> with the fact that the code does nothing to normalize what is read\n> from the symbolic link) probably does not matter, given the way how\n> get_next_component() is implemented.\n> \n\nYes, I think the way get_next_component() is written will account for\nnon-normalized symlink contents.  This way we only have to worry about\nnormalizing input in one location (maybe two with\nstrip_last_component()).\n\n> > +\t\t\t\tstrbuf_addbuf(&symlink, &remaining);\n> > +\t\t\t}\n> > +\n> > +\t\t\t/*\n> > +\t\t\t * use the symlink as the remaining components that\n> > +\t\t\t * need to be resloved\n> > +\t\t\t */\n> > +\t\t\tstrbuf_swap(&symlink, &remaining);\n> > +\t\t}\n> >  \t}\n> >  \n> > +\tretval = resolved.buf;\n> > +\n> >  error_out:\n> > +\tstrbuf_release(&remaining);\n> > +\tstrbuf_release(&next);\n> > +\tstrbuf_release(&symlink);\n> >  \n> >  \treturn retval;\n> >  }\n> \n\n-- \nBrandon Williams\n"},{"id":"307557","messageId":"xmqqk2b47mgz.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"20161212225006.GB193413@google.com","subject":"Re: [PATCH v3 1/4] real_path: resolve symlinks by hand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-12T23:32:12Z","receivedAt":"2016-12-12T23:32:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> On 12/12, Junio C Hamano wrote:\n> ...\n>> E.g. for input path \"foo/bar/\", the above loop runs zero times and\n>> then ...\n>> \n>> > +\t/* Skip sequences of multiple path-separators */\n>> > +\twhile (offset < len && is_dir_sep(path->buf[len - 1]))\n>> > +\t\tlen--;\n>> \n>> ... the slash at the end is removed, leaving \"foo/bar\" in path.\n>\n> The way this is currently used I don't believe this scenario can happen\n> (since input to this shouldn't have trailing slashes), but if others\n> begin to use this function then yes, that is an implicit assumption.\n\nOK.\n\n>> > +\t/* Iterate over the remaining path components */\n>> > +\twhile (remaining.len > 0) {\n>> > +\t\tget_next_component(&next, &remaining);\n>> > +\n>> > +\t\tif (next.len == 0) {\n>> > +\t\t\tcontinue; /* empty component */\n>> > +\t\t} else if (next.len == 1 && !strcmp(next.buf, \".\")) {\n>> > +\t\t\tcontinue; /* '.' component */\n>> > +\t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n>> > +\t\t\t/* '..' component; strip the last path component */\n>> > +\t\t\tstrip_last_component(&resolved);\n>> \n>> Wouldn't this let \"resolved\" eventually run out of the path\n>> components to strip for a malformed input e.g. \"/a/../../b\"?\n>\n> As I understand it, that path is correct and would resolve to \"/b\".\n\nThat is OK on the traditional UNIX end.  \n\nI am not sure if we want to handle the \"//host/share/$remaining\" in\nthis codepath, though.  If we do, then this is still an issue (IIRC,\nsomebody explained that we do not want to step into //host/share/\npart when seeing \"..\"---we'd at least need to leave a NEEDSWORK\ncomment so that interested parties can later take a look into it.\n"},{"id":"307558","messageId":"xmqqfuls7lri.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"xmqqzik07pin.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 4/4] real_path: have callers use real_pathdup and strbuf_realpath","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-12T23:47:29Z","receivedAt":"2016-12-12T23:47:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Brandon Williams <bmwill@google.com> writes:\n>\n>> Migrate callers of real_path() who duplicate the retern value to use\n>> real_pathdup or strbuf_realpath.\n>\n> Looks good.\n\nThis has small non-textual conflicts with Stefan's embed^Wabsorption\ntopic; please holler if you spot my mismerge.  Thanks.\n\n"},{"id":"307561","messageId":"CAGZ79kbYAyGwTb7AkymoaMo+TPGVRiv8kn00fXGS=S_ZVFG0Jw@mail.gmail.com","threadId":"44625","inReplyTo":"xmqqfuls7lri.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 4/4] real_path: have callers use real_pathdup and strbuf_realpath","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-12-12T23:58:49Z","receivedAt":"2016-12-12T23:58:58Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, Dec 12, 2016 at 3:47 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> Brandon Williams <bmwill@google.com> writes:\n>>\n>>> Migrate callers of real_path() who duplicate the retern value to use\n>>> real_pathdup or strbuf_realpath.\n>>\n>> Looks good.\n>\n> This has small non-textual conflicts with Stefan's embed^Wabsorption\n> topic; please holler if you spot my mismerge.  Thanks.\n>\n\n5f6a003727 (Merge branch 'bw/realpath-wo-chdir' into jch)\nlooks good to me.\n"},{"id":"307563","messageId":"20161213011529.GC193413@google.com","threadId":"44625","inReplyTo":"CAGZ79kbYAyGwTb7AkymoaMo+TPGVRiv8kn00fXGS=S_ZVFG0Jw@mail.gmail.com","subject":"Re: [PATCH v3 4/4] real_path: have callers use real_pathdup and strbuf_realpath","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-13T01:15:29Z","receivedAt":"2016-12-13T01:15:38Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 12/12, Stefan Beller wrote:\n> On Mon, Dec 12, 2016 at 3:47 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> > Junio C Hamano <gitster@pobox.com> writes:\n> >\n> >> Brandon Williams <bmwill@google.com> writes:\n> >>\n> >>> Migrate callers of real_path() who duplicate the retern value to use\n> >>> real_pathdup or strbuf_realpath.\n> >>\n> >> Looks good.\n> >\n> > This has small non-textual conflicts with Stefan's embed^Wabsorption\n> > topic; please holler if you spot my mismerge.  Thanks.\n> >\n> \n> 5f6a003727 (Merge branch 'bw/realpath-wo-chdir' into jch)\n> looks good to me.\n\nLooks good to me as well.\n\n-- \nBrandon Williams\n"},{"id":"307574","messageId":"xmqq7f7472o7.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"20161213011529.GC193413@google.com","subject":"Re: [PATCH v3 4/4] real_path: have callers use real_pathdup and strbuf_realpath","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-13T06:39:52Z","receivedAt":"2016-12-13T06:40:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> On 12/12, Stefan Beller wrote:\n>> On Mon, Dec 12, 2016 at 3:47 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> > Junio C Hamano <gitster@pobox.com> writes:\n>> >\n>> >> Brandon Williams <bmwill@google.com> writes:\n>> >>\n>> >>> Migrate callers of real_path() who duplicate the retern value to use\n>> >>> real_pathdup or strbuf_realpath.\n>> >>\n>> >> Looks good.\n>> >\n>> > This has small non-textual conflicts with Stefan's embed^Wabsorption\n>> > topic; please holler if you spot my mismerge.  Thanks.\n>> \n>> 5f6a003727 (Merge branch 'bw/realpath-wo-chdir' into jch)\n>> looks good to me.\n>\n> Looks good to me as well.\n\nThanks.\n"},{"id":"308223","messageId":"097e3e2e-f46d-b0aa-be9c-68c274c5e3dc@kdbg.org","threadId":"44625","inReplyTo":"1481566615-75299-1-git-send-email-bmwill@google.com","subject":"[PATCH bw/realpath-wo-chdir] real_path: canonicalize directory separators in root parts","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2016-12-21T21:51:35Z","receivedAt":"2016-12-21T21:51:46Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"When an absolute path is resolved, resolution begins at the first path\ncomponent after the root part. The root part is just copied verbatim,\nbecause it must not be inspected for symbolic links. For POSIX paths,\nthis is just the initial slash, but on Windows, the root part has the\nforms c:\\ or \\\\server\\share. We do want to canonicalize the back-slashes\nin the root part because these parts are compared to the result of\ngetcwd(), which does return a fully canonicalized path.\n\nFactor out a helper that splits off the root part, and have it\ncanonicalize the copied part.\n\nThis change was prompted because t1504-ceiling-dirs.sh caught a breakage\nin GIT_CEILING_DIRECTORIES handling on Windows.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n This introduces the second #ifdef GIT_WINDOWS_NATIVE in this file.\n It could be avoided if convert_slashes were defined as a do-nothing\n on POSIX, but that would not help the other occurrence. Therefore,\n I suggest to leave it at this.\n\n abspath.c | 29 +++++++++++++++++------------\n 1 file changed, 17 insertions(+), 12 deletions(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex 79ee310867..1d56f5ed9f 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -48,6 +48,19 @@ static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n \tstrbuf_remove(remaining, 0, end - remaining->buf);\n }\n \n+/* copies root part from remaining to resolved, canonicalizing it on the way */\n+static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)\n+{\n+\tint offset = offset_1st_component(remaining->buf);\n+\n+\tstrbuf_reset(resolved);\n+\tstrbuf_add(resolved, remaining->buf, offset);\n+#ifdef GIT_WINDOWS_NATIVE\n+\tconvert_slashes(resolved->buf);\n+#endif\n+\tstrbuf_remove(remaining, 0, offset);\n+}\n+\n /* We allow \"recursive\" symbolic links. Only within reason, though. */\n #define MAXSYMLINKS 5\n \n@@ -80,14 +93,10 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t\tgoto error_out;\n \t}\n \n-\tstrbuf_reset(resolved);\n+\tstrbuf_addstr(&remaining, path);\n+\tget_root_part(resolved, &remaining);\n \n-\tif (is_absolute_path(path)) {\n-\t\t/* absolute path; start with only root as being resolved */\n-\t\tint offset = offset_1st_component(path);\n-\t\tstrbuf_add(resolved, path, offset);\n-\t\tstrbuf_addstr(&remaining, path + offset);\n-\t} else {\n+\tif (!resolved->len) {\n \t\t/* relative path; can use CWD as the initial resolved path */\n \t\tif (strbuf_getcwd(resolved)) {\n \t\t\tif (die_on_error)\n@@ -95,7 +104,6 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t\telse\n \t\t\t\tgoto error_out;\n \t\t}\n-\t\tstrbuf_addstr(&remaining, path);\n \t}\n \n \t/* Iterate over the remaining path components */\n@@ -150,10 +158,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \n \t\t\tif (is_absolute_path(symlink.buf)) {\n \t\t\t\t/* absolute symlink; set resolved to root */\n-\t\t\t\tint offset = offset_1st_component(symlink.buf);\n-\t\t\t\tstrbuf_reset(resolved);\n-\t\t\t\tstrbuf_add(resolved, symlink.buf, offset);\n-\t\t\t\tstrbuf_remove(&symlink, 0, offset);\n+\t\t\t\tget_root_part(resolved, &symlink);\n \t\t\t} else {\n \t\t\t\t/*\n \t\t\t\t * relative symlink\n-- \n2.11.0.79.gf6b77ca\n\n"},{"id":"308225","messageId":"20161221223304.GA119874@google.com","threadId":"44625","inReplyTo":"097e3e2e-f46d-b0aa-be9c-68c274c5e3dc@kdbg.org","subject":"Re: [PATCH bw/realpath-wo-chdir] real_path: canonicalize directory separators in root parts","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-21T22:33:04Z","receivedAt":"2016-12-21T22:40:04Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 12/21, Johannes Sixt wrote:\n> When an absolute path is resolved, resolution begins at the first path\n> component after the root part. The root part is just copied verbatim,\n> because it must not be inspected for symbolic links. For POSIX paths,\n> this is just the initial slash, but on Windows, the root part has the\n> forms c:\\ or \\\\server\\share. We do want to canonicalize the back-slashes\n> in the root part because these parts are compared to the result of\n> getcwd(), which does return a fully canonicalized path.\n> \n> Factor out a helper that splits off the root part, and have it\n> canonicalize the copied part.\n> \n> This change was prompted because t1504-ceiling-dirs.sh caught a breakage\n> in GIT_CEILING_DIRECTORIES handling on Windows.\n> \n> Signed-off-by: Johannes Sixt <j6t@kdbg.org>\n> ---\n>  This introduces the second #ifdef GIT_WINDOWS_NATIVE in this file.\n>  It could be avoided if convert_slashes were defined as a do-nothing\n>  on POSIX, but that would not help the other occurrence. Therefore,\n>  I suggest to leave it at this.\n> \n>  abspath.c | 29 +++++++++++++++++------------\n>  1 file changed, 17 insertions(+), 12 deletions(-)\n> \n> diff --git a/abspath.c b/abspath.c\n> index 79ee310867..1d56f5ed9f 100644\n> --- a/abspath.c\n> +++ b/abspath.c\n> @@ -48,6 +48,19 @@ static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n>  \tstrbuf_remove(remaining, 0, end - remaining->buf);\n>  }\n>  \n> +/* copies root part from remaining to resolved, canonicalizing it on the way */\n> +static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)\n> +{\n> +\tint offset = offset_1st_component(remaining->buf);\n> +\n> +\tstrbuf_reset(resolved);\n> +\tstrbuf_add(resolved, remaining->buf, offset);\n> +#ifdef GIT_WINDOWS_NATIVE\n> +\tconvert_slashes(resolved->buf);\n> +#endif\n\nSo then the only extra cononicalization that is happening here is\nconverting '\\\\server\\share' to '//server/share'? (or 'c:\\' to 'c:/')\n\n-- \nBrandon Williams\n"},{"id":"308238","messageId":"0c9aa347-d64e-b7d7-9b07-52d844d76252@kdbg.org","threadId":"44625","inReplyTo":"20161221223304.GA119874@google.com","subject":"Re: [PATCH bw/realpath-wo-chdir] real_path: canonicalize directory separators in root parts","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2016-12-22T06:07:44Z","receivedAt":"2016-12-22T06:07:53Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 21.12.2016 um 23:33 schrieb Brandon Williams:\n> On 12/21, Johannes Sixt wrote:\n>> +/* copies root part from remaining to resolved, canonicalizing it on the way */\n>> +static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)\n>> +{\n>> +\tint offset = offset_1st_component(remaining->buf);\n>> +\n>> +\tstrbuf_reset(resolved);\n>> +\tstrbuf_add(resolved, remaining->buf, offset);\n>> +#ifdef GIT_WINDOWS_NATIVE\n>> +\tconvert_slashes(resolved->buf);\n>> +#endif\n>\n> So then the only extra cononicalization that is happening here is\n> converting '\\\\server\\share' to '//server/share'? (or 'c:\\' to 'c:/')\n\nCorrect. All other directory separators are canonicalized by the primary \nfunction, strbuf_realpath.\n\n-- Hannes\n\n"},{"id":"308250","messageId":"20161222173301.GB119874@google.com","threadId":"44625","inReplyTo":"0c9aa347-d64e-b7d7-9b07-52d844d76252@kdbg.org","subject":"Re: [PATCH bw/realpath-wo-chdir] real_path: canonicalize directory separators in root parts","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2016-12-22T17:33:01Z","receivedAt":"2016-12-22T17:33:09Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 12/22, Johannes Sixt wrote:\n> Am 21.12.2016 um 23:33 schrieb Brandon Williams:\n> >On 12/21, Johannes Sixt wrote:\n> >>+/* copies root part from remaining to resolved, canonicalizing it on the way */\n> >>+static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)\n> >>+{\n> >>+\tint offset = offset_1st_component(remaining->buf);\n> >>+\n> >>+\tstrbuf_reset(resolved);\n> >>+\tstrbuf_add(resolved, remaining->buf, offset);\n> >>+#ifdef GIT_WINDOWS_NATIVE\n> >>+\tconvert_slashes(resolved->buf);\n> >>+#endif\n> >\n> >So then the only extra cononicalization that is happening here is\n> >converting '\\\\server\\share' to '//server/share'? (or 'c:\\' to 'c:/')\n> \n> Correct. All other directory separators are canonicalized by the\n> primary function, strbuf_realpath.\n\nSounds good. Logically everything looks good to me.  And I like that\nsetting 'resolved' to the root of an abs path is pulled out into a\nhelper function.  It took me a couple extra seconds to realize that\noffset_1st_component returns 0 with a relative path, which makes causes\nthe call to get_root_part to essentially be a noop (ie nothing is\nresolved).\n\nThanks for helping get this to work on windows!\n\n-- \nBrandon Williams\n"},{"id":"308257","messageId":"05333505-63ca-8733-e247-a57d4a9dac4d@kdbg.org","threadId":"44625","inReplyTo":"20161222173301.GB119874@google.com","subject":"Re: [PATCH bw/realpath-wo-chdir] real_path: canonicalize directory separators in root parts","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2016-12-22T18:54:17Z","receivedAt":"2016-12-22T18:54:26Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 22.12.2016 um 18:33 schrieb Brandon Williams:\n> It took me a couple extra seconds to realize that\n> offset_1st_component returns 0 with a relative path, which makes causes\n> the call to get_root_part to essentially be a noop (ie nothing is\n> resolved).\n\nYeah, I am still unsure whether it is a good idea to optimize away the \nis_absolute_path() call, because we lose the symmetry to the symlink \ncase, where we cannot get rid of the call...\n\nBut I think the condition plus comment\n\n    if (!resolved->len) {\n        /* relative path; can use CWD as the initial resolved path */\n\nmakes things fairly clear.\n\n-- Hannes\n\n"},{"id":"308261","messageId":"xmqqmvfng3mt.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"20161222173301.GB119874@google.com","subject":"Re: [PATCH bw/realpath-wo-chdir] real_path: canonicalize directory separators in root parts","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-22T19:33:46Z","receivedAt":"2016-12-22T19:33:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> On 12/22, Johannes Sixt wrote:\n>> Am 21.12.2016 um 23:33 schrieb Brandon Williams:\n>> >On 12/21, Johannes Sixt wrote:\n>> >>+/* copies root part from remaining to resolved, canonicalizing it on the way */\n>> >>+static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)\n>> >>+{\n>> >>+\tint offset = offset_1st_component(remaining->buf);\n>> >>+\n>> >>+\tstrbuf_reset(resolved);\n>> >>+\tstrbuf_add(resolved, remaining->buf, offset);\n>> >>+#ifdef GIT_WINDOWS_NATIVE\n>> >>+\tconvert_slashes(resolved->buf);\n>> >>+#endif\n>> >\n>> >So then the only extra cononicalization that is happening here is\n>> >converting '\\\\server\\share' to '//server/share'? (or 'c:\\' to 'c:/')\n>> \n>> Correct. All other directory separators are canonicalized by the\n>> primary function, strbuf_realpath.\n>\n> Sounds good. Logically everything looks good to me.  And I like that\n> setting 'resolved' to the root of an abs path is pulled out into a\n> helper function.  It took me a couple extra seconds to realize that\n> offset_1st_component returns 0 with a relative path, which makes causes\n> the call to get_root_part to essentially be a noop (ie nothing is\n> resolved).\n>\n> Thanks for helping get this to work on windows!\n\nThanks, both.  \n\nLet's move the topic with this patch to 'next'.  Further\nmicro-optimization can be done incrementally if desired.\n\n\n\n"},{"id":"308729","messageId":"20170103190923.11882-1-bmwill@google.com","threadId":"44625","inReplyTo":"1481566615-75299-1-git-send-email-bmwill@google.com","subject":"[PATCH v4 0/5] road to reentrant real_path","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-03T19:09:18Z","receivedAt":"2017-01-03T19:11:54Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Only change with v4 is in [1/5] renaming the #define MAXSYMLINKS back to\nMAXDEPTH due to a naming conflict brought up by Lars Schneider.\n\nBrandon Williams (4):\n  real_path: resolve symlinks by hand\n  real_path: convert real_path_internal to strbuf_realpath\n  real_path: create real_pathdup\n  real_path: have callers use real_pathdup and strbuf_realpath\n\nJohannes Sixt (1):\n  real_path: canonicalize directory separators in root parts\n\n abspath.c         | 225 +++++++++++++++++++++++++++++++++++++-----------------\n builtin/init-db.c |   6 +-\n cache.h           |   3 +\n environment.c     |   2 +-\n setup.c           |  13 ++--\n sha1_file.c       |   2 +-\n submodule.c       |   2 +-\n transport.c       |   2 +-\n worktree.c        |   2 +-\n 9 files changed, 173 insertions(+), 84 deletions(-)\n\n--- interdiff with v3\n\ndiff --git a/abspath.c b/abspath.c\nindex 1d56f5ed9..3562d17bf 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -62,7 +62,7 @@ static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)\n }\n \n /* We allow \"recursive\" symbolic links. Only within reason, though. */\n-#define MAXSYMLINKS 5\n+#define MAXDEPTH 5\n \n /*\n  * Return the real path (i.e., absolute path, with symlinks resolved\n@@ -138,10 +138,10 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t\tssize_t len;\n \t\t\tstrbuf_reset(&symlink);\n \n-\t\t\tif (num_symlinks++ > MAXSYMLINKS) {\n+\t\t\tif (num_symlinks++ > MAXDEPTH) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie(\"More than %d nested symlinks \"\n-\t\t\t\t\t    \"on path '%s'\", MAXSYMLINKS, path);\n+\t\t\t\t\t    \"on path '%s'\", MAXDEPTH, path);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"308730","messageId":"20170103190923.11882-3-bmwill@google.com","threadId":"44625","inReplyTo":"20170103190923.11882-1-bmwill@google.com","subject":"[PATCH v4 2/5] real_path: convert real_path_internal to strbuf_realpath","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-03T19:09:20Z","receivedAt":"2017-01-03T19:11:57Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Change the name of real_path_internal to strbuf_realpath.  In addition\npush the static strbuf up to its callers and instead take as a\nparameter a pointer to a strbuf to use for the final result.\n\nThis change makes strbuf_realpath reentrant.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 53 +++++++++++++++++++++++++----------------------------\n cache.h   |  2 ++\n 2 files changed, 27 insertions(+), 28 deletions(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex 0f34636a8..c3a6acd4d 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -55,21 +55,17 @@ static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n  * Return the real path (i.e., absolute path, with symlinks resolved\n  * and extra slashes removed) equivalent to the specified path.  (If\n  * you want an absolute path but don't mind links, use\n- * absolute_path().)  The return value is a pointer to a static\n- * buffer.\n+ * absolute_path().)  Places the resolved realpath in the provided strbuf.\n  *\n  * The directory part of path (i.e., everything up to the last\n  * dir_sep) must denote a valid, existing directory, but the last\n  * component need not exist.  If die_on_error is set, then die with an\n  * informative error message if there is a problem.  Otherwise, return\n  * NULL on errors (without generating any output).\n- *\n- * If path is our buffer, then return path, as it's already what the\n- * user wants.\n  */\n-static const char *real_path_internal(const char *path, int die_on_error)\n+char *strbuf_realpath(struct strbuf *resolved, const char *path,\n+\t\t      int die_on_error)\n {\n-\tstatic struct strbuf resolved = STRBUF_INIT;\n \tstruct strbuf remaining = STRBUF_INIT;\n \tstruct strbuf next = STRBUF_INIT;\n \tstruct strbuf symlink = STRBUF_INIT;\n@@ -77,10 +73,6 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \tint num_symlinks = 0;\n \tstruct stat st;\n \n-\t/* We've already done it */\n-\tif (path == resolved.buf)\n-\t\treturn path;\n-\n \tif (!*path) {\n \t\tif (die_on_error)\n \t\t\tdie(\"The empty string is not a valid path\");\n@@ -88,16 +80,16 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tgoto error_out;\n \t}\n \n-\tstrbuf_reset(&resolved);\n+\tstrbuf_reset(resolved);\n \n \tif (is_absolute_path(path)) {\n \t\t/* absolute path; start with only root as being resolved */\n \t\tint offset = offset_1st_component(path);\n-\t\tstrbuf_add(&resolved, path, offset);\n+\t\tstrbuf_add(resolved, path, offset);\n \t\tstrbuf_addstr(&remaining, path + offset);\n \t} else {\n \t\t/* relative path; can use CWD as the initial resolved path */\n-\t\tif (strbuf_getcwd(&resolved)) {\n+\t\tif (strbuf_getcwd(resolved)) {\n \t\t\tif (die_on_error)\n \t\t\t\tdie_errno(\"unable to get current working directory\");\n \t\t\telse\n@@ -116,21 +108,21 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tcontinue; /* '.' component */\n \t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n \t\t\t/* '..' component; strip the last path component */\n-\t\t\tstrip_last_component(&resolved);\n+\t\t\tstrip_last_component(resolved);\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* append the next component and resolve resultant path */\n-\t\tif (!is_dir_sep(resolved.buf[resolved.len - 1]))\n-\t\t\tstrbuf_addch(&resolved, '/');\n-\t\tstrbuf_addbuf(&resolved, &next);\n+\t\tif (!is_dir_sep(resolved->buf[resolved->len - 1]))\n+\t\t\tstrbuf_addch(resolved, '/');\n+\t\tstrbuf_addbuf(resolved, &next);\n \n-\t\tif (lstat(resolved.buf, &st)) {\n+\t\tif (lstat(resolved->buf, &st)) {\n \t\t\t/* error out unless this was the last component */\n \t\t\tif (errno != ENOENT || remaining.len) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid path '%s'\",\n-\t\t\t\t\t\t  resolved.buf);\n+\t\t\t\t\t\t  resolved->buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n@@ -146,12 +138,12 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n \n-\t\t\tlen = strbuf_readlink(&symlink, resolved.buf,\n+\t\t\tlen = strbuf_readlink(&symlink, resolved->buf,\n \t\t\t\t\t      st.st_size);\n \t\t\tif (len < 0) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid symlink '%s'\",\n-\t\t\t\t\t\t  resolved.buf);\n+\t\t\t\t\t\t  resolved->buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n@@ -159,8 +151,8 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tif (is_absolute_path(symlink.buf)) {\n \t\t\t\t/* absolute symlink; set resolved to root */\n \t\t\t\tint offset = offset_1st_component(symlink.buf);\n-\t\t\t\tstrbuf_reset(&resolved);\n-\t\t\t\tstrbuf_add(&resolved, symlink.buf, offset);\n+\t\t\t\tstrbuf_reset(resolved);\n+\t\t\t\tstrbuf_add(resolved, symlink.buf, offset);\n \t\t\t\tstrbuf_remove(&symlink, 0, offset);\n \t\t\t} else {\n \t\t\t\t/*\n@@ -168,7 +160,7 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\t\t * strip off the last component since it will\n \t\t\t\t * be replaced with the contents of the symlink\n \t\t\t\t */\n-\t\t\t\tstrip_last_component(&resolved);\n+\t\t\t\tstrip_last_component(resolved);\n \t\t\t}\n \n \t\t\t/*\n@@ -188,24 +180,29 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t}\n \t}\n \n-\tretval = resolved.buf;\n+\tretval = resolved->buf;\n \n error_out:\n \tstrbuf_release(&remaining);\n \tstrbuf_release(&next);\n \tstrbuf_release(&symlink);\n \n+\tif (!retval)\n+\t\tstrbuf_reset(resolved);\n+\n \treturn retval;\n }\n \n const char *real_path(const char *path)\n {\n-\treturn real_path_internal(path, 1);\n+\tstatic struct strbuf realpath = STRBUF_INIT;\n+\treturn strbuf_realpath(&realpath, path, 1);\n }\n \n const char *real_path_if_valid(const char *path)\n {\n-\treturn real_path_internal(path, 0);\n+\tstatic struct strbuf realpath = STRBUF_INIT;\n+\treturn strbuf_realpath(&realpath, path, 0);\n }\n \n /*\ndiff --git a/cache.h b/cache.h\nindex a50a61a19..7a8129403 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1064,6 +1064,8 @@ static inline int is_absolute_path(const char *path)\n \treturn is_dir_sep(path[0]) || has_dos_drive_prefix(path);\n }\n int is_directory(const char *);\n+char *strbuf_realpath(struct strbuf *resolved, const char *path,\n+\t\t      int die_on_error);\n const char *real_path(const char *path);\n const char *real_path_if_valid(const char *path);\n const char *absolute_path(const char *path);\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"308731","messageId":"20170103190923.11882-5-bmwill@google.com","threadId":"44625","inReplyTo":"20170103190923.11882-1-bmwill@google.com","subject":"[PATCH v4 4/5] real_path: have callers use real_pathdup and strbuf_realpath","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-03T19:09:22Z","receivedAt":"2017-01-03T19:11:58Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Migrate callers of real_path() who duplicate the retern value to use\nreal_pathdup or strbuf_realpath.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/init-db.c |  6 +++---\n environment.c     |  2 +-\n setup.c           | 13 ++++++++-----\n sha1_file.c       |  2 +-\n submodule.c       |  2 +-\n transport.c       |  2 +-\n worktree.c        |  2 +-\n 7 files changed, 16 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 2399b97d9..76d68fad0 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -338,7 +338,7 @@ int init_db(const char *git_dir, const char *real_git_dir,\n {\n \tint reinit;\n \tint exist_ok = flags & INIT_DB_EXIST_OK;\n-\tchar *original_git_dir = xstrdup(real_path(git_dir));\n+\tchar *original_git_dir = real_pathdup(git_dir);\n \n \tif (real_git_dir) {\n \t\tstruct stat st;\n@@ -489,7 +489,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);\n \n \tif (real_git_dir && !is_absolute_path(real_git_dir))\n-\t\treal_git_dir = xstrdup(real_path(real_git_dir));\n+\t\treal_git_dir = real_pathdup(real_git_dir);\n \n \tif (argc == 1) {\n \t\tint mkdir_tried = 0;\n@@ -560,7 +560,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t\tconst char *git_dir_parent = strrchr(git_dir, '/');\n \t\tif (git_dir_parent) {\n \t\t\tchar *rel = xstrndup(git_dir, git_dir_parent - git_dir);\n-\t\t\tgit_work_tree_cfg = xstrdup(real_path(rel));\n+\t\t\tgit_work_tree_cfg = real_pathdup(rel);\n \t\t\tfree(rel);\n \t\t}\n \t\tif (!git_work_tree_cfg)\ndiff --git a/environment.c b/environment.c\nindex 0935ec696..9b943d2d5 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -259,7 +259,7 @@ void set_git_work_tree(const char *new_work_tree)\n \t\treturn;\n \t}\n \tgit_work_tree_initialized = 1;\n-\twork_tree = xstrdup(real_path(new_work_tree));\n+\twork_tree = real_pathdup(new_work_tree);\n }\n \n const char *get_git_work_tree(void)\ndiff --git a/setup.c b/setup.c\nindex fe572b82c..1b534a750 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -256,8 +256,10 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)\n \t\tstrbuf_addbuf(&path, &data);\n \t\tstrbuf_addstr(sb, real_path(path.buf));\n \t\tret = 1;\n-\t} else\n+\t} else {\n \t\tstrbuf_addstr(sb, gitdir);\n+\t}\n+\n \tstrbuf_release(&data);\n \tstrbuf_release(&path);\n \treturn ret;\n@@ -692,7 +694,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,\n \t/* --work-tree is set without --git-dir; use discovered one */\n \tif (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {\n \t\tif (offset != cwd->len && !is_absolute_path(gitdir))\n-\t\t\tgitdir = xstrdup(real_path(gitdir));\n+\t\t\tgitdir = real_pathdup(gitdir);\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(\"Could not come back to cwd\");\n \t\treturn setup_explicit_git_dir(gitdir, cwd, nongit_ok);\n@@ -800,11 +802,12 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,\n \t\t/* Keep entry but do not canonicalize it */\n \t\treturn 1;\n \t} else {\n-\t\tconst char *real_path = real_path_if_valid(ceil);\n-\t\tif (!real_path)\n+\t\tchar *real_path = real_pathdup(ceil);\n+\t\tif (!real_path) {\n \t\t\treturn 0;\n+\t\t}\n \t\tfree(item->string);\n-\t\titem->string = xstrdup(real_path);\n+\t\titem->string = real_path;\n \t\treturn 1;\n \t}\n }\ndiff --git a/sha1_file.c b/sha1_file.c\nindex 9c86d1924..6a03cc393 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -291,7 +291,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,\n \tstruct strbuf pathbuf = STRBUF_INIT;\n \n \tif (!is_absolute_path(entry) && relative_base) {\n-\t\tstrbuf_addstr(&pathbuf, real_path(relative_base));\n+\t\tstrbuf_realpath(&pathbuf, relative_base, 1);\n \t\tstrbuf_addch(&pathbuf, '/');\n \t}\n \tstrbuf_addstr(&pathbuf, entry);\ndiff --git a/submodule.c b/submodule.c\nindex ece17315d..55819ba9c 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1301,7 +1301,7 @@ void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)\n {\n \tstruct strbuf file_name = STRBUF_INIT;\n \tstruct strbuf rel_path = STRBUF_INIT;\n-\tconst char *real_work_tree = xstrdup(real_path(work_tree));\n+\tconst char *real_work_tree = real_pathdup(work_tree);\n \n \t/* Update gitfile */\n \tstrbuf_addf(&file_name, \"%s/.git\", work_tree);\ndiff --git a/transport.c b/transport.c\nindex 04e5d6623..a3b98f198 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1146,7 +1146,7 @@ static int refs_from_alternate_cb(struct alternate_object_database *e,\n \tconst struct ref *extra;\n \tstruct alternate_refs_data *cb = data;\n \n-\tother = xstrdup(real_path(e->path));\n+\tother = real_pathdup(e->path);\n \tlen = strlen(other);\n \n \twhile (other[len-1] == '/')\ndiff --git a/worktree.c b/worktree.c\nindex eb6121263..edf14daf9 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -255,7 +255,7 @@ struct worktree *find_worktree(struct worktree **list,\n \t\treturn wt;\n \n \targ = prefix_filename(prefix, strlen(prefix), arg);\n-\tpath = xstrdup(real_path(arg));\n+\tpath = real_pathdup(arg);\n \tfor (; *list; list++)\n \t\tif (!fspathcmp(path, real_path((*list)->path)))\n \t\t\tbreak;\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"308732","messageId":"20170103190923.11882-6-bmwill@google.com","threadId":"44625","inReplyTo":"20170103190923.11882-1-bmwill@google.com","subject":"[PATCH v4 5/5] real_path: canonicalize directory separators in root parts","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-03T19:09:23Z","receivedAt":"2017-01-03T19:12:01Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"From: Johannes Sixt <j6t@kdbg.org>\n\nWhen an absolute path is resolved, resolution begins at the first path\ncomponent after the root part. The root part is just copied verbatim,\nbecause it must not be inspected for symbolic links. For POSIX paths,\nthis is just the initial slash, but on Windows, the root part has the\nforms c:\\ or \\\\server\\share. We do want to canonicalize the back-slashes\nin the root part because these parts are compared to the result of\ngetcwd(), which does return a fully canonicalized path.\n\nFactor out a helper that splits off the root part, and have it\ncanonicalize the copied part.\n\nThis change was prompted because t1504-ceiling-dirs.sh caught a breakage\nin GIT_CEILING_DIRECTORIES handling on Windows.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n abspath.c | 29 +++++++++++++++++------------\n 1 file changed, 17 insertions(+), 12 deletions(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex f4283f465..3562d17bf 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -48,6 +48,19 @@ static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n \tstrbuf_remove(remaining, 0, end - remaining->buf);\n }\n \n+/* copies root part from remaining to resolved, canonicalizing it on the way */\n+static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)\n+{\n+\tint offset = offset_1st_component(remaining->buf);\n+\n+\tstrbuf_reset(resolved);\n+\tstrbuf_add(resolved, remaining->buf, offset);\n+#ifdef GIT_WINDOWS_NATIVE\n+\tconvert_slashes(resolved->buf);\n+#endif\n+\tstrbuf_remove(remaining, 0, offset);\n+}\n+\n /* We allow \"recursive\" symbolic links. Only within reason, though. */\n #define MAXDEPTH 5\n \n@@ -80,14 +93,10 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t\tgoto error_out;\n \t}\n \n-\tstrbuf_reset(resolved);\n+\tstrbuf_addstr(&remaining, path);\n+\tget_root_part(resolved, &remaining);\n \n-\tif (is_absolute_path(path)) {\n-\t\t/* absolute path; start with only root as being resolved */\n-\t\tint offset = offset_1st_component(path);\n-\t\tstrbuf_add(resolved, path, offset);\n-\t\tstrbuf_addstr(&remaining, path + offset);\n-\t} else {\n+\tif (!resolved->len) {\n \t\t/* relative path; can use CWD as the initial resolved path */\n \t\tif (strbuf_getcwd(resolved)) {\n \t\t\tif (die_on_error)\n@@ -95,7 +104,6 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t\telse\n \t\t\t\tgoto error_out;\n \t\t}\n-\t\tstrbuf_addstr(&remaining, path);\n \t}\n \n \t/* Iterate over the remaining path components */\n@@ -150,10 +158,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \n \t\t\tif (is_absolute_path(symlink.buf)) {\n \t\t\t\t/* absolute symlink; set resolved to root */\n-\t\t\t\tint offset = offset_1st_component(symlink.buf);\n-\t\t\t\tstrbuf_reset(resolved);\n-\t\t\t\tstrbuf_add(resolved, symlink.buf, offset);\n-\t\t\t\tstrbuf_remove(&symlink, 0, offset);\n+\t\t\t\tget_root_part(resolved, &symlink);\n \t\t\t} else {\n \t\t\t\t/*\n \t\t\t\t * relative symlink\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"308733","messageId":"20170103190923.11882-2-bmwill@google.com","threadId":"44625","inReplyTo":"20170103190923.11882-1-bmwill@google.com","subject":"[PATCH v4 1/5] real_path: resolve symlinks by hand","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-03T19:09:19Z","receivedAt":"2017-01-03T19:12:03Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"The current implementation of real_path uses chdir() in order to resolve\nsymlinks.  Unfortunately this isn't thread-safe as chdir() affects a\nprocess as a whole and not just an individual thread.  Instead perform\nthe symlink resolution by hand so that the calls to chdir() can be\nremoved, making real_path one step closer to being reentrant.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 188 ++++++++++++++++++++++++++++++++++++++++++--------------------\n 1 file changed, 128 insertions(+), 60 deletions(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex 2825de859..0f34636a8 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -11,6 +11,43 @@ int is_directory(const char *path)\n \treturn (!stat(path, &st) && S_ISDIR(st.st_mode));\n }\n \n+/* removes the last path component from 'path' except if 'path' is root */\n+static void strip_last_component(struct strbuf *path)\n+{\n+\tsize_t offset = offset_1st_component(path->buf);\n+\tsize_t len = path->len;\n+\n+\t/* Find start of the last component */\n+\twhile (offset < len && !is_dir_sep(path->buf[len - 1]))\n+\t\tlen--;\n+\t/* Skip sequences of multiple path-separators */\n+\twhile (offset < len && is_dir_sep(path->buf[len - 1]))\n+\t\tlen--;\n+\n+\tstrbuf_setlen(path, len);\n+}\n+\n+/* get (and remove) the next component in 'remaining' and place it in 'next' */\n+static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n+{\n+\tchar *start = NULL;\n+\tchar *end = NULL;\n+\n+\tstrbuf_reset(next);\n+\n+\t/* look for the next component */\n+\t/* Skip sequences of multiple path-separators */\n+\tfor (start = remaining->buf; is_dir_sep(*start); start++)\n+\t\t; /* nothing */\n+\t/* Find end of the path component */\n+\tfor (end = start; *end && !is_dir_sep(*end); end++)\n+\t\t; /* nothing */\n+\n+\tstrbuf_add(next, start, end - start);\n+\t/* remove the component from 'remaining' */\n+\tstrbuf_remove(remaining, 0, end - remaining->buf);\n+}\n+\n /* We allow \"recursive\" symbolic links. Only within reason, though. */\n #define MAXDEPTH 5\n \n@@ -21,7 +58,6 @@ int is_directory(const char *path)\n  * absolute_path().)  The return value is a pointer to a static\n  * buffer.\n  *\n- * The input and all intermediate paths must be shorter than MAX_PATH.\n  * The directory part of path (i.e., everything up to the last\n  * dir_sep) must denote a valid, existing directory, but the last\n  * component need not exist.  If die_on_error is set, then die with an\n@@ -33,22 +69,16 @@ int is_directory(const char *path)\n  */\n static const char *real_path_internal(const char *path, int die_on_error)\n {\n-\tstatic struct strbuf sb = STRBUF_INIT;\n+\tstatic struct strbuf resolved = STRBUF_INIT;\n+\tstruct strbuf remaining = STRBUF_INIT;\n+\tstruct strbuf next = STRBUF_INIT;\n+\tstruct strbuf symlink = STRBUF_INIT;\n \tchar *retval = NULL;\n-\n-\t/*\n-\t * If we have to temporarily chdir(), store the original CWD\n-\t * here so that we can chdir() back to it at the end of the\n-\t * function:\n-\t */\n-\tstruct strbuf cwd = STRBUF_INIT;\n-\n-\tint depth = MAXDEPTH;\n-\tchar *last_elem = NULL;\n+\tint num_symlinks = 0;\n \tstruct stat st;\n \n \t/* We've already done it */\n-\tif (path == sb.buf)\n+\tif (path == resolved.buf)\n \t\treturn path;\n \n \tif (!*path) {\n@@ -58,74 +88,112 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tgoto error_out;\n \t}\n \n-\tstrbuf_reset(&sb);\n-\tstrbuf_addstr(&sb, path);\n-\n-\twhile (depth--) {\n-\t\tif (!is_directory(sb.buf)) {\n-\t\t\tchar *last_slash = find_last_dir_sep(sb.buf);\n-\t\t\tif (last_slash) {\n-\t\t\t\tlast_elem = xstrdup(last_slash + 1);\n-\t\t\t\tstrbuf_setlen(&sb, last_slash - sb.buf + 1);\n-\t\t\t} else {\n-\t\t\t\tlast_elem = xmemdupz(sb.buf, sb.len);\n-\t\t\t\tstrbuf_reset(&sb);\n-\t\t\t}\n+\tstrbuf_reset(&resolved);\n+\n+\tif (is_absolute_path(path)) {\n+\t\t/* absolute path; start with only root as being resolved */\n+\t\tint offset = offset_1st_component(path);\n+\t\tstrbuf_add(&resolved, path, offset);\n+\t\tstrbuf_addstr(&remaining, path + offset);\n+\t} else {\n+\t\t/* relative path; can use CWD as the initial resolved path */\n+\t\tif (strbuf_getcwd(&resolved)) {\n+\t\t\tif (die_on_error)\n+\t\t\t\tdie_errno(\"unable to get current working directory\");\n+\t\t\telse\n+\t\t\t\tgoto error_out;\n \t\t}\n+\t\tstrbuf_addstr(&remaining, path);\n+\t}\n \n-\t\tif (sb.len) {\n-\t\t\tif (!cwd.len && strbuf_getcwd(&cwd)) {\n+\t/* Iterate over the remaining path components */\n+\twhile (remaining.len > 0) {\n+\t\tget_next_component(&next, &remaining);\n+\n+\t\tif (next.len == 0) {\n+\t\t\tcontinue; /* empty component */\n+\t\t} else if (next.len == 1 && !strcmp(next.buf, \".\")) {\n+\t\t\tcontinue; /* '.' component */\n+\t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n+\t\t\t/* '..' component; strip the last path component */\n+\t\t\tstrip_last_component(&resolved);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* append the next component and resolve resultant path */\n+\t\tif (!is_dir_sep(resolved.buf[resolved.len - 1]))\n+\t\t\tstrbuf_addch(&resolved, '/');\n+\t\tstrbuf_addbuf(&resolved, &next);\n+\n+\t\tif (lstat(resolved.buf, &st)) {\n+\t\t\t/* error out unless this was the last component */\n+\t\t\tif (errno != ENOENT || remaining.len) {\n \t\t\t\tif (die_on_error)\n-\t\t\t\t\tdie_errno(\"Could not get current working directory\");\n+\t\t\t\t\tdie_errno(\"Invalid path '%s'\",\n+\t\t\t\t\t\t  resolved.buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n+\t\t} else if (S_ISLNK(st.st_mode)) {\n+\t\t\tssize_t len;\n+\t\t\tstrbuf_reset(&symlink);\n \n-\t\t\tif (chdir(sb.buf)) {\n+\t\t\tif (num_symlinks++ > MAXDEPTH) {\n \t\t\t\tif (die_on_error)\n-\t\t\t\t\tdie_errno(\"Could not switch to '%s'\",\n-\t\t\t\t\t\t  sb.buf);\n+\t\t\t\t\tdie(\"More than %d nested symlinks \"\n+\t\t\t\t\t    \"on path '%s'\", MAXDEPTH, path);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n-\t\t}\n-\t\tif (strbuf_getcwd(&sb)) {\n-\t\t\tif (die_on_error)\n-\t\t\t\tdie_errno(\"Could not get current working directory\");\n-\t\t\telse\n-\t\t\t\tgoto error_out;\n-\t\t}\n-\n-\t\tif (last_elem) {\n-\t\t\tif (sb.len && !is_dir_sep(sb.buf[sb.len - 1]))\n-\t\t\t\tstrbuf_addch(&sb, '/');\n-\t\t\tstrbuf_addstr(&sb, last_elem);\n-\t\t\tfree(last_elem);\n-\t\t\tlast_elem = NULL;\n-\t\t}\n \n-\t\tif (!lstat(sb.buf, &st) && S_ISLNK(st.st_mode)) {\n-\t\t\tstruct strbuf next_sb = STRBUF_INIT;\n-\t\t\tssize_t len = strbuf_readlink(&next_sb, sb.buf, 0);\n+\t\t\tlen = strbuf_readlink(&symlink, resolved.buf,\n+\t\t\t\t\t      st.st_size);\n \t\t\tif (len < 0) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid symlink '%s'\",\n-\t\t\t\t\t\t  sb.buf);\n+\t\t\t\t\t\t  resolved.buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n-\t\t\tstrbuf_swap(&sb, &next_sb);\n-\t\t\tstrbuf_release(&next_sb);\n-\t\t} else\n-\t\t\tbreak;\n+\n+\t\t\tif (is_absolute_path(symlink.buf)) {\n+\t\t\t\t/* absolute symlink; set resolved to root */\n+\t\t\t\tint offset = offset_1st_component(symlink.buf);\n+\t\t\t\tstrbuf_reset(&resolved);\n+\t\t\t\tstrbuf_add(&resolved, symlink.buf, offset);\n+\t\t\t\tstrbuf_remove(&symlink, 0, offset);\n+\t\t\t} else {\n+\t\t\t\t/*\n+\t\t\t\t * relative symlink\n+\t\t\t\t * strip off the last component since it will\n+\t\t\t\t * be replaced with the contents of the symlink\n+\t\t\t\t */\n+\t\t\t\tstrip_last_component(&resolved);\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * if there are still remaining components to resolve\n+\t\t\t * then append them to symlink\n+\t\t\t */\n+\t\t\tif (remaining.len) {\n+\t\t\t\tstrbuf_addch(&symlink, '/');\n+\t\t\t\tstrbuf_addbuf(&symlink, &remaining);\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * use the symlink as the remaining components that\n+\t\t\t * need to be resloved\n+\t\t\t */\n+\t\t\tstrbuf_swap(&symlink, &remaining);\n+\t\t}\n \t}\n \n-\tretval = sb.buf;\n+\tretval = resolved.buf;\n+\n error_out:\n-\tfree(last_elem);\n-\tif (cwd.len && chdir(cwd.buf))\n-\t\tdie_errno(\"Could not change back to '%s'\", cwd.buf);\n-\tstrbuf_release(&cwd);\n+\tstrbuf_release(&remaining);\n+\tstrbuf_release(&next);\n+\tstrbuf_release(&symlink);\n \n \treturn retval;\n }\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"308734","messageId":"20170103190923.11882-4-bmwill@google.com","threadId":"44625","inReplyTo":"20170103190923.11882-1-bmwill@google.com","subject":"[PATCH v4 3/5] real_path: create real_pathdup","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-03T19:09:21Z","receivedAt":"2017-01-03T19:12:04Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Create real_pathdup which returns a caller owned string of the resolved\nrealpath based on the provide path.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 13 +++++++++++++\n cache.h   |  1 +\n 2 files changed, 14 insertions(+)\n\ndiff --git a/abspath.c b/abspath.c\nindex c3a6acd4d..f4283f465 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -205,6 +205,19 @@ const char *real_path_if_valid(const char *path)\n \treturn strbuf_realpath(&realpath, path, 0);\n }\n \n+char *real_pathdup(const char *path)\n+{\n+\tstruct strbuf realpath = STRBUF_INIT;\n+\tchar *retval = NULL;\n+\n+\tif (strbuf_realpath(&realpath, path, 0))\n+\t\tretval = strbuf_detach(&realpath, NULL);\n+\n+\tstrbuf_release(&realpath);\n+\n+\treturn retval;\n+}\n+\n /*\n  * Use this to get an absolute path from a relative one. If you want\n  * to resolve links, you should use real_path.\ndiff --git a/cache.h b/cache.h\nindex 7a8129403..e12a5d912 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1068,6 +1068,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t      int die_on_error);\n const char *real_path(const char *path);\n const char *real_path_if_valid(const char *path);\n+char *real_pathdup(const char *path);\n const char *absolute_path(const char *path);\n const char *remove_leading_path(const char *in, const char *prefix);\n const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"308746","messageId":"20170104004825.3s27dsircdp5lqte@sigill.intra.peff.net","threadId":"44625","inReplyTo":"20170103190923.11882-1-bmwill@google.com","subject":"Re: [PATCH v4 0/5] road to reentrant real_path","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-01-04T00:48:25Z","receivedAt":"2017-01-04T00:48:59Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Jan 03, 2017 at 11:09:18AM -0800, Brandon Williams wrote:\n\n> Only change with v4 is in [1/5] renaming the #define MAXSYMLINKS back to\n> MAXDEPTH due to a naming conflict brought up by Lars Schneider.\n\nHmm. Isn't MAXSYMLINKS basically what you want here, though? It what's\nwhat all other similar functions will be using.\n\nThe only problem was that we were redefining the macro. So maybe:\n\n  #ifndef MAXSYMLINKS\n  #define MAXSYMLINKS 5\n  #endif\n\nwould be a good solution?\n\nIt looks like the \"usual\" value is more like 20 or 30 on most systems,\nthough.  We should probably also set errno to ELOOP when we hit the\nlimit, which is what other symlink-resolving functions would do.\n\nI'm surprised we didn't hit this on Linux, which has MAXSYMLINKS, too.\nWe should be picking it up from <sys/param.h>.\n\n-Peff\n"},{"id":"308752","messageId":"CA+P7+xrgt+aTF4ibJ139=WihwHwG_m01bjAaF5-VW=Rk8u1ykA@mail.gmail.com","threadId":"44625","inReplyTo":"20170103190923.11882-5-bmwill@google.com","subject":"Re: [PATCH v4 4/5] real_path: have callers use real_pathdup and strbuf_realpath","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2017-01-04T01:07:26Z","receivedAt":"2017-01-04T01:07:53Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Jan 3, 2017 at 11:09 AM, Brandon Williams <bmwill@google.com> wrote:\n> Migrate callers of real_path() who duplicate the retern value to use\n> real_pathdup or strbuf_realpath.\n\nNit: s/retern/return\n"},{"id":"308760","messageId":"3f9a530c-402f-f276-4721-fa6a8a6fef41@web.de","threadId":"44625","inReplyTo":"20170104004825.3s27dsircdp5lqte@sigill.intra.peff.net","subject":"Re: [PATCH v4 0/5] road to reentrant real_path","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2017-01-04T06:56:02Z","receivedAt":"2017-01-04T06:56:45Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On 04.01.17 01:48, Jeff King wrote:\n> On Tue, Jan 03, 2017 at 11:09:18AM -0800, Brandon Williams wrote:\n> \n>> Only change with v4 is in [1/5] renaming the #define MAXSYMLINKS back to\n>> MAXDEPTH due to a naming conflict brought up by Lars Schneider.\n> \n> Hmm. Isn't MAXSYMLINKS basically what you want here, though? It what's\n> what all other similar functions will be using.\n> \n> The only problem was that we were redefining the macro. So maybe:\n> \n>   #ifndef MAXSYMLINKS\n>   #define MAXSYMLINKS 5\n>   #endif\n> \n> would be a good solution?\nWhy 5  ? (looking at the  20..30 below)\nAnd why 5 on one system and e.g. on my Mac OS\n#define MAXSYMLINKS     32  \n\nWould the same value value for all Git installations on all platforms make sense?\n#define GITMAXSYMLINKS 20\n\n> \n> It looks like the \"usual\" value is more like 20 or 30 on most systems,\n> though.  We should probably also set errno to ELOOP when we hit the\n> limit, which is what other symlink-resolving functions would do.\n> \n> I'm surprised we didn't hit this on Linux, which has MAXSYMLINKS, too.\n> We should be picking it up from <sys/param.h>.\n> \n> -Peff\n> \n\n"},{"id":"308761","messageId":"20170104070107.huse2a6thz737epv@sigill.intra.peff.net","threadId":"44625","inReplyTo":"3f9a530c-402f-f276-4721-fa6a8a6fef41@web.de","subject":"Re: [PATCH v4 0/5] road to reentrant real_path","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-01-04T07:01:08Z","receivedAt":"2017-01-04T07:01:18Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jan 04, 2017 at 07:56:02AM +0100, Torsten Bögershausen wrote:\n\n> On 04.01.17 01:48, Jeff King wrote:\n> > On Tue, Jan 03, 2017 at 11:09:18AM -0800, Brandon Williams wrote:\n> > \n> >> Only change with v4 is in [1/5] renaming the #define MAXSYMLINKS back to\n> >> MAXDEPTH due to a naming conflict brought up by Lars Schneider.\n> > \n> > Hmm. Isn't MAXSYMLINKS basically what you want here, though? It what's\n> > what all other similar functions will be using.\n> > \n> > The only problem was that we were redefining the macro. So maybe:\n> > \n> >   #ifndef MAXSYMLINKS\n> >   #define MAXSYMLINKS 5\n> >   #endif\n> > \n> > would be a good solution?\n> Why 5  ? (looking at the  20..30 below)\n> And why 5 on one system and e.g. on my Mac OS\n> #define MAXSYMLINKS     32  \n\nI mentioned \"5\" because that is the current value of MAXDEPTH. I do\nthink it would be reasonable to bump it to something higher.\n\n> Would the same value value for all Git installations on all platforms make sense?\n> #define GITMAXSYMLINKS 20\n\nI think it's probably more important to match the rest of the OS, so\nthat open(\"foo\") and realpath(\"foo\") behave similarly on the same\nsystem. Though I think even that isn't always possible, as the limit is\ndynamic on some systems.\n\nI think the idea of the 20-30 range is that it's small enough to catch\nan infinite loop quickly, and large enough that nobody will ever hit it\nin practice. :)\n\n-Peff\n"},{"id":"308793","messageId":"20170104181318.GC69227@google.com","threadId":"44625","inReplyTo":"20170104070107.huse2a6thz737epv@sigill.intra.peff.net","subject":"Re: [PATCH v4 0/5] road to reentrant real_path","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-04T18:13:18Z","receivedAt":"2017-01-04T18:13:43Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 01/04, Jeff King wrote:\n> On Wed, Jan 04, 2017 at 07:56:02AM +0100, Torsten Bögershausen wrote:\n> \n> > On 04.01.17 01:48, Jeff King wrote:\n> > > On Tue, Jan 03, 2017 at 11:09:18AM -0800, Brandon Williams wrote:\n> > > \n> > >> Only change with v4 is in [1/5] renaming the #define MAXSYMLINKS back to\n> > >> MAXDEPTH due to a naming conflict brought up by Lars Schneider.\n> > > \n> > > Hmm. Isn't MAXSYMLINKS basically what you want here, though? It what's\n> > > what all other similar functions will be using.\n> > > \n> > > The only problem was that we were redefining the macro. So maybe:\n> > > \n> > >   #ifndef MAXSYMLINKS\n> > >   #define MAXSYMLINKS 5\n> > >   #endif\n> > > \n> > > would be a good solution?\n> > Why 5  ? (looking at the  20..30 below)\n> > And why 5 on one system and e.g. on my Mac OS\n> > #define MAXSYMLINKS     32  \n> \n> I mentioned \"5\" because that is the current value of MAXDEPTH. I do\n> think it would be reasonable to bump it to something higher.\n> \n> > Would the same value value for all Git installations on all platforms make sense?\n> > #define GITMAXSYMLINKS 20\n> \n> I think it's probably more important to match the rest of the OS, so\n> that open(\"foo\") and realpath(\"foo\") behave similarly on the same\n> system. Though I think even that isn't always possible, as the limit is\n> dynamic on some systems.\n> \n> I think the idea of the 20-30 range is that it's small enough to catch\n> an infinite loop quickly, and large enough that nobody will ever hit it\n> in practice. :)\n\nI agree that we should have similar guarantees as the OS provides,\nespecially if the OS already has MAXSYMLINKS defined.  What then, should\nthe fall back value be if the OS doesn't have this defined?  5 like we\nhave done historically, or something around the 20-30 range as Torsten\nsuggests?\n\n-- \nBrandon Williams\n"},{"id":"308795","messageId":"20170104181449.GD69227@google.com","threadId":"44625","inReplyTo":"CA+P7+xrgt+aTF4ibJ139=WihwHwG_m01bjAaF5-VW=Rk8u1ykA@mail.gmail.com","subject":"Re: [PATCH v4 4/5] real_path: have callers use real_pathdup and strbuf_realpath","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-04T18:14:49Z","receivedAt":"2017-01-04T18:25:09Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 01/03, Jacob Keller wrote:\n> On Tue, Jan 3, 2017 at 11:09 AM, Brandon Williams <bmwill@google.com> wrote:\n> > Migrate callers of real_path() who duplicate the retern value to use\n> > real_pathdup or strbuf_realpath.\n> \n> Nit: s/retern/return\n\nThanks for catching that, I'll fix that in the next reroll.\n\n-- \nBrandon Williams\n"},{"id":"308796","messageId":"CAGZ79kbdNuGe038Wb9OR1SKq-XYtsPrLsn6XueO6zsKKGFYiNg@mail.gmail.com","threadId":"44625","inReplyTo":"20170104181318.GC69227@google.com","subject":"Re: [PATCH v4 0/5] road to reentrant real_path","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-01-04T18:22:38Z","receivedAt":"2017-01-04T18:25:30Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Wed, Jan 4, 2017 at 10:13 AM, Brandon Williams <bmwill@google.com> wrote:\n> On 01/04, Jeff King wrote:\n>> On Wed, Jan 04, 2017 at 07:56:02AM +0100, Torsten Bögershausen wrote:\n>>\n>> > On 04.01.17 01:48, Jeff King wrote:\n>> > > On Tue, Jan 03, 2017 at 11:09:18AM -0800, Brandon Williams wrote:\n>> > >\n>> > >> Only change with v4 is in [1/5] renaming the #define MAXSYMLINKS back to\n>> > >> MAXDEPTH due to a naming conflict brought up by Lars Schneider.\n>> > >\n>> > > Hmm. Isn't MAXSYMLINKS basically what you want here, though? It what's\n>> > > what all other similar functions will be using.\n>> > >\n>> > > The only problem was that we were redefining the macro. So maybe:\n>> > >\n>> > >   #ifndef MAXSYMLINKS\n>> > >   #define MAXSYMLINKS 5\n>> > >   #endif\n>> > >\n>> > > would be a good solution?\n>> > Why 5  ? (looking at the  20..30 below)\n>> > And why 5 on one system and e.g. on my Mac OS\n>> > #define MAXSYMLINKS     32\n>>\n>> I mentioned \"5\" because that is the current value of MAXDEPTH. I do\n>> think it would be reasonable to bump it to something higher.\n>>\n>> > Would the same value value for all Git installations on all platforms make sense?\n>> > #define GITMAXSYMLINKS 20\n>>\n>> I think it's probably more important to match the rest of the OS, so\n>> that open(\"foo\") and realpath(\"foo\") behave similarly on the same\n>> system. Though I think even that isn't always possible, as the limit is\n>> dynamic on some systems.\n>>\n>> I think the idea of the 20-30 range is that it's small enough to catch\n>> an infinite loop quickly, and large enough that nobody will ever hit it\n>> in practice. :)\n>\n> I agree that we should have similar guarantees as the OS provides,\n> especially if the OS already has MAXSYMLINKS defined.  What then, should\n> the fall back value be if the OS doesn't have this defined?  5 like we\n> have done historically, or something around the 20-30 range as Torsten\n> suggests?\n\nAs a fallback I'd rather go for a larger number than too small.\nThe reason for the existence is just to break an infinite loop\n(and report an error? which the current code doesn't quite do,\nbut your series actually does).\n\nIf the number is too large, then it takes a bit longer to generate the error\nmessage, but the error path is no big deal w.r.t. performance, so it's fine\nfor it taking a bit longer.\n\nIf the number is too low, then we may hinder people from getting actual\nwork done, (i.e. they have to figure out what the problem is and recompile\ngit), so I'd think a larger number is not harmful. So 32?\n\n>\n> --\n> Brandon Williams\n"},{"id":"308800","messageId":"CA+P7+xp+j1ajPLjE-RukSmp33_bRkD7J65X-++frkYd9LWLSkQ@mail.gmail.com","threadId":"44625","inReplyTo":"CAGZ79kbdNuGe038Wb9OR1SKq-XYtsPrLsn6XueO6zsKKGFYiNg@mail.gmail.com","subject":"Re: [PATCH v4 0/5] road to reentrant real_path","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2017-01-04T21:46:07Z","receivedAt":"2017-01-04T21:46:36Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Wed, Jan 4, 2017 at 10:22 AM, Stefan Beller <sbeller@google.com> wrote:\n> On Wed, Jan 4, 2017 at 10:13 AM, Brandon Williams <bmwill@google.com> wrote:\n>> On 01/04, Jeff King wrote:\n>>> On Wed, Jan 04, 2017 at 07:56:02AM +0100, Torsten Bögershausen wrote:\n>>>\n>>> > On 04.01.17 01:48, Jeff King wrote:\n>>> > > On Tue, Jan 03, 2017 at 11:09:18AM -0800, Brandon Williams wrote:\n>>> > >\n>>> > >> Only change with v4 is in [1/5] renaming the #define MAXSYMLINKS back to\n>>> > >> MAXDEPTH due to a naming conflict brought up by Lars Schneider.\n>>> > >\n>>> > > Hmm. Isn't MAXSYMLINKS basically what you want here, though? It what's\n>>> > > what all other similar functions will be using.\n>>> > >\n>>> > > The only problem was that we were redefining the macro. So maybe:\n>>> > >\n>>> > >   #ifndef MAXSYMLINKS\n>>> > >   #define MAXSYMLINKS 5\n>>> > >   #endif\n>>> > >\n>>> > > would be a good solution?\n>>> > Why 5  ? (looking at the  20..30 below)\n>>> > And why 5 on one system and e.g. on my Mac OS\n>>> > #define MAXSYMLINKS     32\n>>>\n>>> I mentioned \"5\" because that is the current value of MAXDEPTH. I do\n>>> think it would be reasonable to bump it to something higher.\n>>>\n>>> > Would the same value value for all Git installations on all platforms make sense?\n>>> > #define GITMAXSYMLINKS 20\n>>>\n>>> I think it's probably more important to match the rest of the OS, so\n>>> that open(\"foo\") and realpath(\"foo\") behave similarly on the same\n>>> system. Though I think even that isn't always possible, as the limit is\n>>> dynamic on some systems.\n>>>\n>>> I think the idea of the 20-30 range is that it's small enough to catch\n>>> an infinite loop quickly, and large enough that nobody will ever hit it\n>>> in practice. :)\n>>\n>> I agree that we should have similar guarantees as the OS provides,\n>> especially if the OS already has MAXSYMLINKS defined.  What then, should\n>> the fall back value be if the OS doesn't have this defined?  5 like we\n>> have done historically, or something around the 20-30 range as Torsten\n>> suggests?\n>\n> As a fallback I'd rather go for a larger number than too small.\n> The reason for the existence is just to break an infinite loop\n> (and report an error? which the current code doesn't quite do,\n> but your series actually does).\n>\n> If the number is too large, then it takes a bit longer to generate the error\n> message, but the error path is no big deal w.r.t. performance, so it's fine\n> for it taking a bit longer.\n>\n> If the number is too low, then we may hinder people from getting actual\n> work done, (i.e. they have to figure out what the problem is and recompile\n> git), so I'd think a larger number is not harmful. So 32?\n>\n\nI think I agree as well.\n\nThanks,\nJake\n\n>>\n>> --\n>> Brandon Williams\n"},{"id":"308801","messageId":"20170104220124.145808-1-bmwill@google.com","threadId":"44625","inReplyTo":"20170103190923.11882-1-bmwill@google.com","subject":"[PATCH v5 0/5] road to reentrant real_path","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-04T22:01:19Z","receivedAt":"2017-01-04T22:01:45Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"changes in v5:\n* set errno to ELOOP when MAXSYMLINKS is exceded.\n* revert to use MAXSYMLINKS instead of MAXDEPTH.\n* If the OS hasn't defined MAXSYMLINKS, use a fallback value of 32.\n\nBrandon Williams (4):\n  real_path: resolve symlinks by hand\n  real_path: convert real_path_internal to strbuf_realpath\n  real_path: create real_pathdup\n  real_path: have callers use real_pathdup and strbuf_realpath\n\nJohannes Sixt (1):\n  real_path: canonicalize directory separators in root parts\n\n abspath.c         | 231 +++++++++++++++++++++++++++++++++++++-----------------\n builtin/init-db.c |   6 +-\n cache.h           |   3 +\n environment.c     |   2 +-\n setup.c           |  13 +--\n sha1_file.c       |   2 +-\n submodule.c       |   2 +-\n transport.c       |   2 +-\n worktree.c        |   2 +-\n 9 files changed, 178 insertions(+), 85 deletions(-)\n\n--- interdiff with v4\n\ndiff --git a/abspath.c b/abspath.c\nindex 3562d17bf..fce40fddc 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -62,7 +62,9 @@ static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)\n }\n \n /* We allow \"recursive\" symbolic links. Only within reason, though. */\n-#define MAXDEPTH 5\n+#ifndef MAXSYMLINKS\n+#define MAXSYMLINKS 32\n+#endif\n \n /*\n  * Return the real path (i.e., absolute path, with symlinks resolved\n@@ -138,10 +140,12 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t\tssize_t len;\n \t\t\tstrbuf_reset(&symlink);\n \n-\t\t\tif (num_symlinks++ > MAXDEPTH) {\n+\t\t\tif (num_symlinks++ > MAXSYMLINKS) {\n+\t\t\t\terrno = ELOOP;\n+\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie(\"More than %d nested symlinks \"\n-\t\t\t\t\t    \"on path '%s'\", MAXDEPTH, path);\n+\t\t\t\t\t    \"on path '%s'\", MAXSYMLINKS, path);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"308802","messageId":"20170104220124.145808-4-bmwill@google.com","threadId":"44625","inReplyTo":"20170104220124.145808-1-bmwill@google.com","subject":"[PATCH v5 3/5] real_path: create real_pathdup","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-04T22:01:22Z","receivedAt":"2017-01-04T22:01:50Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Create real_pathdup which returns a caller owned string of the resolved\nrealpath based on the provide path.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 13 +++++++++++++\n cache.h   |  1 +\n 2 files changed, 14 insertions(+)\n\ndiff --git a/abspath.c b/abspath.c\nindex a200d4220..72f716f80 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -209,6 +209,19 @@ const char *real_path_if_valid(const char *path)\n \treturn strbuf_realpath(&realpath, path, 0);\n }\n \n+char *real_pathdup(const char *path)\n+{\n+\tstruct strbuf realpath = STRBUF_INIT;\n+\tchar *retval = NULL;\n+\n+\tif (strbuf_realpath(&realpath, path, 0))\n+\t\tretval = strbuf_detach(&realpath, NULL);\n+\n+\tstrbuf_release(&realpath);\n+\n+\treturn retval;\n+}\n+\n /*\n  * Use this to get an absolute path from a relative one. If you want\n  * to resolve links, you should use real_path.\ndiff --git a/cache.h b/cache.h\nindex 7a8129403..e12a5d912 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1068,6 +1068,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t      int die_on_error);\n const char *real_path(const char *path);\n const char *real_path_if_valid(const char *path);\n+char *real_pathdup(const char *path);\n const char *absolute_path(const char *path);\n const char *remove_leading_path(const char *in, const char *prefix);\n const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"308803","messageId":"20170104220124.145808-5-bmwill@google.com","threadId":"44625","inReplyTo":"20170104220124.145808-1-bmwill@google.com","subject":"[PATCH v5 4/5] real_path: have callers use real_pathdup and strbuf_realpath","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-04T22:01:23Z","receivedAt":"2017-01-04T22:01:53Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Migrate callers of real_path() who duplicate the return value to use\nreal_pathdup or strbuf_realpath.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/init-db.c |  6 +++---\n environment.c     |  2 +-\n setup.c           | 13 ++++++++-----\n sha1_file.c       |  2 +-\n submodule.c       |  2 +-\n transport.c       |  2 +-\n worktree.c        |  2 +-\n 7 files changed, 16 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 2399b97d9..76d68fad0 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -338,7 +338,7 @@ int init_db(const char *git_dir, const char *real_git_dir,\n {\n \tint reinit;\n \tint exist_ok = flags & INIT_DB_EXIST_OK;\n-\tchar *original_git_dir = xstrdup(real_path(git_dir));\n+\tchar *original_git_dir = real_pathdup(git_dir);\n \n \tif (real_git_dir) {\n \t\tstruct stat st;\n@@ -489,7 +489,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);\n \n \tif (real_git_dir && !is_absolute_path(real_git_dir))\n-\t\treal_git_dir = xstrdup(real_path(real_git_dir));\n+\t\treal_git_dir = real_pathdup(real_git_dir);\n \n \tif (argc == 1) {\n \t\tint mkdir_tried = 0;\n@@ -560,7 +560,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t\tconst char *git_dir_parent = strrchr(git_dir, '/');\n \t\tif (git_dir_parent) {\n \t\t\tchar *rel = xstrndup(git_dir, git_dir_parent - git_dir);\n-\t\t\tgit_work_tree_cfg = xstrdup(real_path(rel));\n+\t\t\tgit_work_tree_cfg = real_pathdup(rel);\n \t\t\tfree(rel);\n \t\t}\n \t\tif (!git_work_tree_cfg)\ndiff --git a/environment.c b/environment.c\nindex 0935ec696..9b943d2d5 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -259,7 +259,7 @@ void set_git_work_tree(const char *new_work_tree)\n \t\treturn;\n \t}\n \tgit_work_tree_initialized = 1;\n-\twork_tree = xstrdup(real_path(new_work_tree));\n+\twork_tree = real_pathdup(new_work_tree);\n }\n \n const char *get_git_work_tree(void)\ndiff --git a/setup.c b/setup.c\nindex fe572b82c..1b534a750 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -256,8 +256,10 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)\n \t\tstrbuf_addbuf(&path, &data);\n \t\tstrbuf_addstr(sb, real_path(path.buf));\n \t\tret = 1;\n-\t} else\n+\t} else {\n \t\tstrbuf_addstr(sb, gitdir);\n+\t}\n+\n \tstrbuf_release(&data);\n \tstrbuf_release(&path);\n \treturn ret;\n@@ -692,7 +694,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,\n \t/* --work-tree is set without --git-dir; use discovered one */\n \tif (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {\n \t\tif (offset != cwd->len && !is_absolute_path(gitdir))\n-\t\t\tgitdir = xstrdup(real_path(gitdir));\n+\t\t\tgitdir = real_pathdup(gitdir);\n \t\tif (chdir(cwd->buf))\n \t\t\tdie_errno(\"Could not come back to cwd\");\n \t\treturn setup_explicit_git_dir(gitdir, cwd, nongit_ok);\n@@ -800,11 +802,12 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,\n \t\t/* Keep entry but do not canonicalize it */\n \t\treturn 1;\n \t} else {\n-\t\tconst char *real_path = real_path_if_valid(ceil);\n-\t\tif (!real_path)\n+\t\tchar *real_path = real_pathdup(ceil);\n+\t\tif (!real_path) {\n \t\t\treturn 0;\n+\t\t}\n \t\tfree(item->string);\n-\t\titem->string = xstrdup(real_path);\n+\t\titem->string = real_path;\n \t\treturn 1;\n \t}\n }\ndiff --git a/sha1_file.c b/sha1_file.c\nindex 9c86d1924..6a03cc393 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -291,7 +291,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,\n \tstruct strbuf pathbuf = STRBUF_INIT;\n \n \tif (!is_absolute_path(entry) && relative_base) {\n-\t\tstrbuf_addstr(&pathbuf, real_path(relative_base));\n+\t\tstrbuf_realpath(&pathbuf, relative_base, 1);\n \t\tstrbuf_addch(&pathbuf, '/');\n \t}\n \tstrbuf_addstr(&pathbuf, entry);\ndiff --git a/submodule.c b/submodule.c\nindex ece17315d..55819ba9c 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1301,7 +1301,7 @@ void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)\n {\n \tstruct strbuf file_name = STRBUF_INIT;\n \tstruct strbuf rel_path = STRBUF_INIT;\n-\tconst char *real_work_tree = xstrdup(real_path(work_tree));\n+\tconst char *real_work_tree = real_pathdup(work_tree);\n \n \t/* Update gitfile */\n \tstrbuf_addf(&file_name, \"%s/.git\", work_tree);\ndiff --git a/transport.c b/transport.c\nindex 04e5d6623..a3b98f198 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1146,7 +1146,7 @@ static int refs_from_alternate_cb(struct alternate_object_database *e,\n \tconst struct ref *extra;\n \tstruct alternate_refs_data *cb = data;\n \n-\tother = xstrdup(real_path(e->path));\n+\tother = real_pathdup(e->path);\n \tlen = strlen(other);\n \n \twhile (other[len-1] == '/')\ndiff --git a/worktree.c b/worktree.c\nindex eb6121263..edf14daf9 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -255,7 +255,7 @@ struct worktree *find_worktree(struct worktree **list,\n \t\treturn wt;\n \n \targ = prefix_filename(prefix, strlen(prefix), arg);\n-\tpath = xstrdup(real_path(arg));\n+\tpath = real_pathdup(arg);\n \tfor (; *list; list++)\n \t\tif (!fspathcmp(path, real_path((*list)->path)))\n \t\t\tbreak;\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"308804","messageId":"20170104220124.145808-6-bmwill@google.com","threadId":"44625","inReplyTo":"20170104220124.145808-1-bmwill@google.com","subject":"[PATCH v5 5/5] real_path: canonicalize directory separators in root parts","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-04T22:01:24Z","receivedAt":"2017-01-04T22:01:57Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"From: Johannes Sixt <j6t@kdbg.org>\n\nWhen an absolute path is resolved, resolution begins at the first path\ncomponent after the root part. The root part is just copied verbatim,\nbecause it must not be inspected for symbolic links. For POSIX paths,\nthis is just the initial slash, but on Windows, the root part has the\nforms c:\\ or \\\\server\\share. We do want to canonicalize the back-slashes\nin the root part because these parts are compared to the result of\ngetcwd(), which does return a fully canonicalized path.\n\nFactor out a helper that splits off the root part, and have it\ncanonicalize the copied part.\n\nThis change was prompted because t1504-ceiling-dirs.sh caught a breakage\nin GIT_CEILING_DIRECTORIES handling on Windows.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n abspath.c | 29 +++++++++++++++++------------\n 1 file changed, 17 insertions(+), 12 deletions(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex 72f716f80..fce40fddc 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -48,6 +48,19 @@ static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n \tstrbuf_remove(remaining, 0, end - remaining->buf);\n }\n \n+/* copies root part from remaining to resolved, canonicalizing it on the way */\n+static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)\n+{\n+\tint offset = offset_1st_component(remaining->buf);\n+\n+\tstrbuf_reset(resolved);\n+\tstrbuf_add(resolved, remaining->buf, offset);\n+#ifdef GIT_WINDOWS_NATIVE\n+\tconvert_slashes(resolved->buf);\n+#endif\n+\tstrbuf_remove(remaining, 0, offset);\n+}\n+\n /* We allow \"recursive\" symbolic links. Only within reason, though. */\n #ifndef MAXSYMLINKS\n #define MAXSYMLINKS 32\n@@ -82,14 +95,10 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t\tgoto error_out;\n \t}\n \n-\tstrbuf_reset(resolved);\n+\tstrbuf_addstr(&remaining, path);\n+\tget_root_part(resolved, &remaining);\n \n-\tif (is_absolute_path(path)) {\n-\t\t/* absolute path; start with only root as being resolved */\n-\t\tint offset = offset_1st_component(path);\n-\t\tstrbuf_add(resolved, path, offset);\n-\t\tstrbuf_addstr(&remaining, path + offset);\n-\t} else {\n+\tif (!resolved->len) {\n \t\t/* relative path; can use CWD as the initial resolved path */\n \t\tif (strbuf_getcwd(resolved)) {\n \t\t\tif (die_on_error)\n@@ -97,7 +106,6 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t\telse\n \t\t\t\tgoto error_out;\n \t\t}\n-\t\tstrbuf_addstr(&remaining, path);\n \t}\n \n \t/* Iterate over the remaining path components */\n@@ -154,10 +162,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \n \t\t\tif (is_absolute_path(symlink.buf)) {\n \t\t\t\t/* absolute symlink; set resolved to root */\n-\t\t\t\tint offset = offset_1st_component(symlink.buf);\n-\t\t\t\tstrbuf_reset(resolved);\n-\t\t\t\tstrbuf_add(resolved, symlink.buf, offset);\n-\t\t\t\tstrbuf_remove(&symlink, 0, offset);\n+\t\t\t\tget_root_part(resolved, &symlink);\n \t\t\t} else {\n \t\t\t\t/*\n \t\t\t\t * relative symlink\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"308805","messageId":"20170104220124.145808-3-bmwill@google.com","threadId":"44625","inReplyTo":"20170104220124.145808-1-bmwill@google.com","subject":"[PATCH v5 2/5] real_path: convert real_path_internal to strbuf_realpath","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-04T22:01:21Z","receivedAt":"2017-01-04T22:02:01Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Change the name of real_path_internal to strbuf_realpath.  In addition\npush the static strbuf up to its callers and instead take as a\nparameter a pointer to a strbuf to use for the final result.\n\nThis change makes strbuf_realpath reentrant.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 53 +++++++++++++++++++++++++----------------------------\n cache.h   |  2 ++\n 2 files changed, 27 insertions(+), 28 deletions(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex 629201e48..a200d4220 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -57,21 +57,17 @@ static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n  * Return the real path (i.e., absolute path, with symlinks resolved\n  * and extra slashes removed) equivalent to the specified path.  (If\n  * you want an absolute path but don't mind links, use\n- * absolute_path().)  The return value is a pointer to a static\n- * buffer.\n+ * absolute_path().)  Places the resolved realpath in the provided strbuf.\n  *\n  * The directory part of path (i.e., everything up to the last\n  * dir_sep) must denote a valid, existing directory, but the last\n  * component need not exist.  If die_on_error is set, then die with an\n  * informative error message if there is a problem.  Otherwise, return\n  * NULL on errors (without generating any output).\n- *\n- * If path is our buffer, then return path, as it's already what the\n- * user wants.\n  */\n-static const char *real_path_internal(const char *path, int die_on_error)\n+char *strbuf_realpath(struct strbuf *resolved, const char *path,\n+\t\t      int die_on_error)\n {\n-\tstatic struct strbuf resolved = STRBUF_INIT;\n \tstruct strbuf remaining = STRBUF_INIT;\n \tstruct strbuf next = STRBUF_INIT;\n \tstruct strbuf symlink = STRBUF_INIT;\n@@ -79,10 +75,6 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \tint num_symlinks = 0;\n \tstruct stat st;\n \n-\t/* We've already done it */\n-\tif (path == resolved.buf)\n-\t\treturn path;\n-\n \tif (!*path) {\n \t\tif (die_on_error)\n \t\t\tdie(\"The empty string is not a valid path\");\n@@ -90,16 +82,16 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tgoto error_out;\n \t}\n \n-\tstrbuf_reset(&resolved);\n+\tstrbuf_reset(resolved);\n \n \tif (is_absolute_path(path)) {\n \t\t/* absolute path; start with only root as being resolved */\n \t\tint offset = offset_1st_component(path);\n-\t\tstrbuf_add(&resolved, path, offset);\n+\t\tstrbuf_add(resolved, path, offset);\n \t\tstrbuf_addstr(&remaining, path + offset);\n \t} else {\n \t\t/* relative path; can use CWD as the initial resolved path */\n-\t\tif (strbuf_getcwd(&resolved)) {\n+\t\tif (strbuf_getcwd(resolved)) {\n \t\t\tif (die_on_error)\n \t\t\t\tdie_errno(\"unable to get current working directory\");\n \t\t\telse\n@@ -118,21 +110,21 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tcontinue; /* '.' component */\n \t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n \t\t\t/* '..' component; strip the last path component */\n-\t\t\tstrip_last_component(&resolved);\n+\t\t\tstrip_last_component(resolved);\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* append the next component and resolve resultant path */\n-\t\tif (!is_dir_sep(resolved.buf[resolved.len - 1]))\n-\t\t\tstrbuf_addch(&resolved, '/');\n-\t\tstrbuf_addbuf(&resolved, &next);\n+\t\tif (!is_dir_sep(resolved->buf[resolved->len - 1]))\n+\t\t\tstrbuf_addch(resolved, '/');\n+\t\tstrbuf_addbuf(resolved, &next);\n \n-\t\tif (lstat(resolved.buf, &st)) {\n+\t\tif (lstat(resolved->buf, &st)) {\n \t\t\t/* error out unless this was the last component */\n \t\t\tif (errno != ENOENT || remaining.len) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid path '%s'\",\n-\t\t\t\t\t\t  resolved.buf);\n+\t\t\t\t\t\t  resolved->buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n@@ -150,12 +142,12 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n \n-\t\t\tlen = strbuf_readlink(&symlink, resolved.buf,\n+\t\t\tlen = strbuf_readlink(&symlink, resolved->buf,\n \t\t\t\t\t      st.st_size);\n \t\t\tif (len < 0) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid symlink '%s'\",\n-\t\t\t\t\t\t  resolved.buf);\n+\t\t\t\t\t\t  resolved->buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n@@ -163,8 +155,8 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tif (is_absolute_path(symlink.buf)) {\n \t\t\t\t/* absolute symlink; set resolved to root */\n \t\t\t\tint offset = offset_1st_component(symlink.buf);\n-\t\t\t\tstrbuf_reset(&resolved);\n-\t\t\t\tstrbuf_add(&resolved, symlink.buf, offset);\n+\t\t\t\tstrbuf_reset(resolved);\n+\t\t\t\tstrbuf_add(resolved, symlink.buf, offset);\n \t\t\t\tstrbuf_remove(&symlink, 0, offset);\n \t\t\t} else {\n \t\t\t\t/*\n@@ -172,7 +164,7 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\t\t * strip off the last component since it will\n \t\t\t\t * be replaced with the contents of the symlink\n \t\t\t\t */\n-\t\t\t\tstrip_last_component(&resolved);\n+\t\t\t\tstrip_last_component(resolved);\n \t\t\t}\n \n \t\t\t/*\n@@ -192,24 +184,29 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t}\n \t}\n \n-\tretval = resolved.buf;\n+\tretval = resolved->buf;\n \n error_out:\n \tstrbuf_release(&remaining);\n \tstrbuf_release(&next);\n \tstrbuf_release(&symlink);\n \n+\tif (!retval)\n+\t\tstrbuf_reset(resolved);\n+\n \treturn retval;\n }\n \n const char *real_path(const char *path)\n {\n-\treturn real_path_internal(path, 1);\n+\tstatic struct strbuf realpath = STRBUF_INIT;\n+\treturn strbuf_realpath(&realpath, path, 1);\n }\n \n const char *real_path_if_valid(const char *path)\n {\n-\treturn real_path_internal(path, 0);\n+\tstatic struct strbuf realpath = STRBUF_INIT;\n+\treturn strbuf_realpath(&realpath, path, 0);\n }\n \n /*\ndiff --git a/cache.h b/cache.h\nindex a50a61a19..7a8129403 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1064,6 +1064,8 @@ static inline int is_absolute_path(const char *path)\n \treturn is_dir_sep(path[0]) || has_dos_drive_prefix(path);\n }\n int is_directory(const char *);\n+char *strbuf_realpath(struct strbuf *resolved, const char *path,\n+\t\t      int die_on_error);\n const char *real_path(const char *path);\n const char *real_path_if_valid(const char *path);\n const char *absolute_path(const char *path);\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"308806","messageId":"20170104220124.145808-2-bmwill@google.com","threadId":"44625","inReplyTo":"20170104220124.145808-1-bmwill@google.com","subject":"[PATCH v5 1/5] real_path: resolve symlinks by hand","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-04T22:01:20Z","receivedAt":"2017-01-04T22:02:05Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"The current implementation of real_path uses chdir() in order to resolve\nsymlinks.  Unfortunately this isn't thread-safe as chdir() affects a\nprocess as a whole and not just an individual thread.  Instead perform\nthe symlink resolution by hand so that the calls to chdir() can be\nremoved, making real_path one step closer to being reentrant.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 194 ++++++++++++++++++++++++++++++++++++++++++--------------------\n 1 file changed, 133 insertions(+), 61 deletions(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex 2825de859..629201e48 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -11,8 +11,47 @@ int is_directory(const char *path)\n \treturn (!stat(path, &st) && S_ISDIR(st.st_mode));\n }\n \n+/* removes the last path component from 'path' except if 'path' is root */\n+static void strip_last_component(struct strbuf *path)\n+{\n+\tsize_t offset = offset_1st_component(path->buf);\n+\tsize_t len = path->len;\n+\n+\t/* Find start of the last component */\n+\twhile (offset < len && !is_dir_sep(path->buf[len - 1]))\n+\t\tlen--;\n+\t/* Skip sequences of multiple path-separators */\n+\twhile (offset < len && is_dir_sep(path->buf[len - 1]))\n+\t\tlen--;\n+\n+\tstrbuf_setlen(path, len);\n+}\n+\n+/* get (and remove) the next component in 'remaining' and place it in 'next' */\n+static void get_next_component(struct strbuf *next, struct strbuf *remaining)\n+{\n+\tchar *start = NULL;\n+\tchar *end = NULL;\n+\n+\tstrbuf_reset(next);\n+\n+\t/* look for the next component */\n+\t/* Skip sequences of multiple path-separators */\n+\tfor (start = remaining->buf; is_dir_sep(*start); start++)\n+\t\t; /* nothing */\n+\t/* Find end of the path component */\n+\tfor (end = start; *end && !is_dir_sep(*end); end++)\n+\t\t; /* nothing */\n+\n+\tstrbuf_add(next, start, end - start);\n+\t/* remove the component from 'remaining' */\n+\tstrbuf_remove(remaining, 0, end - remaining->buf);\n+}\n+\n /* We allow \"recursive\" symbolic links. Only within reason, though. */\n-#define MAXDEPTH 5\n+#ifndef MAXSYMLINKS\n+#define MAXSYMLINKS 32\n+#endif\n \n /*\n  * Return the real path (i.e., absolute path, with symlinks resolved\n@@ -21,7 +60,6 @@ int is_directory(const char *path)\n  * absolute_path().)  The return value is a pointer to a static\n  * buffer.\n  *\n- * The input and all intermediate paths must be shorter than MAX_PATH.\n  * The directory part of path (i.e., everything up to the last\n  * dir_sep) must denote a valid, existing directory, but the last\n  * component need not exist.  If die_on_error is set, then die with an\n@@ -33,22 +71,16 @@ int is_directory(const char *path)\n  */\n static const char *real_path_internal(const char *path, int die_on_error)\n {\n-\tstatic struct strbuf sb = STRBUF_INIT;\n+\tstatic struct strbuf resolved = STRBUF_INIT;\n+\tstruct strbuf remaining = STRBUF_INIT;\n+\tstruct strbuf next = STRBUF_INIT;\n+\tstruct strbuf symlink = STRBUF_INIT;\n \tchar *retval = NULL;\n-\n-\t/*\n-\t * If we have to temporarily chdir(), store the original CWD\n-\t * here so that we can chdir() back to it at the end of the\n-\t * function:\n-\t */\n-\tstruct strbuf cwd = STRBUF_INIT;\n-\n-\tint depth = MAXDEPTH;\n-\tchar *last_elem = NULL;\n+\tint num_symlinks = 0;\n \tstruct stat st;\n \n \t/* We've already done it */\n-\tif (path == sb.buf)\n+\tif (path == resolved.buf)\n \t\treturn path;\n \n \tif (!*path) {\n@@ -58,74 +90,114 @@ static const char *real_path_internal(const char *path, int die_on_error)\n \t\t\tgoto error_out;\n \t}\n \n-\tstrbuf_reset(&sb);\n-\tstrbuf_addstr(&sb, path);\n-\n-\twhile (depth--) {\n-\t\tif (!is_directory(sb.buf)) {\n-\t\t\tchar *last_slash = find_last_dir_sep(sb.buf);\n-\t\t\tif (last_slash) {\n-\t\t\t\tlast_elem = xstrdup(last_slash + 1);\n-\t\t\t\tstrbuf_setlen(&sb, last_slash - sb.buf + 1);\n-\t\t\t} else {\n-\t\t\t\tlast_elem = xmemdupz(sb.buf, sb.len);\n-\t\t\t\tstrbuf_reset(&sb);\n-\t\t\t}\n+\tstrbuf_reset(&resolved);\n+\n+\tif (is_absolute_path(path)) {\n+\t\t/* absolute path; start with only root as being resolved */\n+\t\tint offset = offset_1st_component(path);\n+\t\tstrbuf_add(&resolved, path, offset);\n+\t\tstrbuf_addstr(&remaining, path + offset);\n+\t} else {\n+\t\t/* relative path; can use CWD as the initial resolved path */\n+\t\tif (strbuf_getcwd(&resolved)) {\n+\t\t\tif (die_on_error)\n+\t\t\t\tdie_errno(\"unable to get current working directory\");\n+\t\t\telse\n+\t\t\t\tgoto error_out;\n \t\t}\n+\t\tstrbuf_addstr(&remaining, path);\n+\t}\n \n-\t\tif (sb.len) {\n-\t\t\tif (!cwd.len && strbuf_getcwd(&cwd)) {\n+\t/* Iterate over the remaining path components */\n+\twhile (remaining.len > 0) {\n+\t\tget_next_component(&next, &remaining);\n+\n+\t\tif (next.len == 0) {\n+\t\t\tcontinue; /* empty component */\n+\t\t} else if (next.len == 1 && !strcmp(next.buf, \".\")) {\n+\t\t\tcontinue; /* '.' component */\n+\t\t} else if (next.len == 2 && !strcmp(next.buf, \"..\")) {\n+\t\t\t/* '..' component; strip the last path component */\n+\t\t\tstrip_last_component(&resolved);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* append the next component and resolve resultant path */\n+\t\tif (!is_dir_sep(resolved.buf[resolved.len - 1]))\n+\t\t\tstrbuf_addch(&resolved, '/');\n+\t\tstrbuf_addbuf(&resolved, &next);\n+\n+\t\tif (lstat(resolved.buf, &st)) {\n+\t\t\t/* error out unless this was the last component */\n+\t\t\tif (errno != ENOENT || remaining.len) {\n \t\t\t\tif (die_on_error)\n-\t\t\t\t\tdie_errno(\"Could not get current working directory\");\n+\t\t\t\t\tdie_errno(\"Invalid path '%s'\",\n+\t\t\t\t\t\t  resolved.buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n+\t\t} else if (S_ISLNK(st.st_mode)) {\n+\t\t\tssize_t len;\n+\t\t\tstrbuf_reset(&symlink);\n+\n+\t\t\tif (num_symlinks++ > MAXSYMLINKS) {\n+\t\t\t\terrno = ELOOP;\n \n-\t\t\tif (chdir(sb.buf)) {\n \t\t\t\tif (die_on_error)\n-\t\t\t\t\tdie_errno(\"Could not switch to '%s'\",\n-\t\t\t\t\t\t  sb.buf);\n+\t\t\t\t\tdie(\"More than %d nested symlinks \"\n+\t\t\t\t\t    \"on path '%s'\", MAXSYMLINKS, path);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n-\t\t}\n-\t\tif (strbuf_getcwd(&sb)) {\n-\t\t\tif (die_on_error)\n-\t\t\t\tdie_errno(\"Could not get current working directory\");\n-\t\t\telse\n-\t\t\t\tgoto error_out;\n-\t\t}\n \n-\t\tif (last_elem) {\n-\t\t\tif (sb.len && !is_dir_sep(sb.buf[sb.len - 1]))\n-\t\t\t\tstrbuf_addch(&sb, '/');\n-\t\t\tstrbuf_addstr(&sb, last_elem);\n-\t\t\tfree(last_elem);\n-\t\t\tlast_elem = NULL;\n-\t\t}\n-\n-\t\tif (!lstat(sb.buf, &st) && S_ISLNK(st.st_mode)) {\n-\t\t\tstruct strbuf next_sb = STRBUF_INIT;\n-\t\t\tssize_t len = strbuf_readlink(&next_sb, sb.buf, 0);\n+\t\t\tlen = strbuf_readlink(&symlink, resolved.buf,\n+\t\t\t\t\t      st.st_size);\n \t\t\tif (len < 0) {\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie_errno(\"Invalid symlink '%s'\",\n-\t\t\t\t\t\t  sb.buf);\n+\t\t\t\t\t\t  resolved.buf);\n \t\t\t\telse\n \t\t\t\t\tgoto error_out;\n \t\t\t}\n-\t\t\tstrbuf_swap(&sb, &next_sb);\n-\t\t\tstrbuf_release(&next_sb);\n-\t\t} else\n-\t\t\tbreak;\n+\n+\t\t\tif (is_absolute_path(symlink.buf)) {\n+\t\t\t\t/* absolute symlink; set resolved to root */\n+\t\t\t\tint offset = offset_1st_component(symlink.buf);\n+\t\t\t\tstrbuf_reset(&resolved);\n+\t\t\t\tstrbuf_add(&resolved, symlink.buf, offset);\n+\t\t\t\tstrbuf_remove(&symlink, 0, offset);\n+\t\t\t} else {\n+\t\t\t\t/*\n+\t\t\t\t * relative symlink\n+\t\t\t\t * strip off the last component since it will\n+\t\t\t\t * be replaced with the contents of the symlink\n+\t\t\t\t */\n+\t\t\t\tstrip_last_component(&resolved);\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * if there are still remaining components to resolve\n+\t\t\t * then append them to symlink\n+\t\t\t */\n+\t\t\tif (remaining.len) {\n+\t\t\t\tstrbuf_addch(&symlink, '/');\n+\t\t\t\tstrbuf_addbuf(&symlink, &remaining);\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * use the symlink as the remaining components that\n+\t\t\t * need to be resloved\n+\t\t\t */\n+\t\t\tstrbuf_swap(&symlink, &remaining);\n+\t\t}\n \t}\n \n-\tretval = sb.buf;\n+\tretval = resolved.buf;\n+\n error_out:\n-\tfree(last_elem);\n-\tif (cwd.len && chdir(cwd.buf))\n-\t\tdie_errno(\"Could not change back to '%s'\", cwd.buf);\n-\tstrbuf_release(&cwd);\n+\tstrbuf_release(&remaining);\n+\tstrbuf_release(&next);\n+\tstrbuf_release(&symlink);\n \n \treturn retval;\n }\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"308807","messageId":"20170104215520.GA68241@google.com","threadId":"44625","inReplyTo":"CA+P7+xp+j1ajPLjE-RukSmp33_bRkD7J65X-++frkYd9LWLSkQ@mail.gmail.com","subject":"Re: [PATCH v4 0/5] road to reentrant real_path","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-04T21:55:20Z","receivedAt":"2017-01-04T22:02:06Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 01/04, Jacob Keller wrote:\n> On Wed, Jan 4, 2017 at 10:22 AM, Stefan Beller <sbeller@google.com> wrote:\n> > On Wed, Jan 4, 2017 at 10:13 AM, Brandon Williams <bmwill@google.com> wrote:\n> >> On 01/04, Jeff King wrote:\n> >>> On Wed, Jan 04, 2017 at 07:56:02AM +0100, Torsten Bögershausen wrote:\n> >>>\n> >>> > On 04.01.17 01:48, Jeff King wrote:\n> >>> > > On Tue, Jan 03, 2017 at 11:09:18AM -0800, Brandon Williams wrote:\n> >>> > >\n> >>> > >> Only change with v4 is in [1/5] renaming the #define MAXSYMLINKS back to\n> >>> > >> MAXDEPTH due to a naming conflict brought up by Lars Schneider.\n> >>> > >\n> >>> > > Hmm. Isn't MAXSYMLINKS basically what you want here, though? It what's\n> >>> > > what all other similar functions will be using.\n> >>> > >\n> >>> > > The only problem was that we were redefining the macro. So maybe:\n> >>> > >\n> >>> > >   #ifndef MAXSYMLINKS\n> >>> > >   #define MAXSYMLINKS 5\n> >>> > >   #endif\n> >>> > >\n> >>> > > would be a good solution?\n> >>> > Why 5  ? (looking at the  20..30 below)\n> >>> > And why 5 on one system and e.g. on my Mac OS\n> >>> > #define MAXSYMLINKS     32\n> >>>\n> >>> I mentioned \"5\" because that is the current value of MAXDEPTH. I do\n> >>> think it would be reasonable to bump it to something higher.\n> >>>\n> >>> > Would the same value value for all Git installations on all platforms make sense?\n> >>> > #define GITMAXSYMLINKS 20\n> >>>\n> >>> I think it's probably more important to match the rest of the OS, so\n> >>> that open(\"foo\") and realpath(\"foo\") behave similarly on the same\n> >>> system. Though I think even that isn't always possible, as the limit is\n> >>> dynamic on some systems.\n> >>>\n> >>> I think the idea of the 20-30 range is that it's small enough to catch\n> >>> an infinite loop quickly, and large enough that nobody will ever hit it\n> >>> in practice. :)\n> >>\n> >> I agree that we should have similar guarantees as the OS provides,\n> >> especially if the OS already has MAXSYMLINKS defined.  What then, should\n> >> the fall back value be if the OS doesn't have this defined?  5 like we\n> >> have done historically, or something around the 20-30 range as Torsten\n> >> suggests?\n> >\n> > As a fallback I'd rather go for a larger number than too small.\n> > The reason for the existence is just to break an infinite loop\n> > (and report an error? which the current code doesn't quite do,\n> > but your series actually does).\n> >\n> > If the number is too large, then it takes a bit longer to generate the error\n> > message, but the error path is no big deal w.r.t. performance, so it's fine\n> > for it taking a bit longer.\n> >\n> > If the number is too low, then we may hinder people from getting actual\n> > work done, (i.e. they have to figure out what the problem is and recompile\n> > git), so I'd think a larger number is not harmful. So 32?\n> >\n> \n> I think I agree as well.\n> \n> Thanks,\n> Jake\n> \n> >>\n> >> --\n> >> Brandon Williams\n\nThat's two people for 32.  I'll use that as the fallback and resend the\nseries.\n\n-- \nBrandon Williams\n"},{"id":"308946","messageId":"xmqqpojy1c49.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"20170104220124.145808-1-bmwill@google.com","subject":"Re: [PATCH v5 0/5] road to reentrant real_path","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-08T03:09:26Z","receivedAt":"2017-01-08T03:09:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> changes in v5:\n> * set errno to ELOOP when MAXSYMLINKS is exceded.\n> * revert to use MAXSYMLINKS instead of MAXDEPTH.\n> * If the OS hasn't defined MAXSYMLINKS, use a fallback value of 32.\n>\n> Brandon Williams (4):\n>   real_path: resolve symlinks by hand\n>   real_path: convert real_path_internal to strbuf_realpath\n>   real_path: create real_pathdup\n>   real_path: have callers use real_pathdup and strbuf_realpath\n>\n> Johannes Sixt (1):\n>   real_path: canonicalize directory separators in root parts\n>\n\nHow does this relate to the 5-patch real_path: series that has been\non 'next' since last year?\n"},{"id":"309045","messageId":"20170109180418.GB62878@google.com","threadId":"44625","inReplyTo":"xmqqpojy1c49.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 0/5] road to reentrant real_path","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-09T18:04:18Z","receivedAt":"2017-01-09T18:04:26Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 01/07, Junio C Hamano wrote:\n> Brandon Williams <bmwill@google.com> writes:\n> \n> > changes in v5:\n> > * set errno to ELOOP when MAXSYMLINKS is exceded.\n> > * revert to use MAXSYMLINKS instead of MAXDEPTH.\n> > * If the OS hasn't defined MAXSYMLINKS, use a fallback value of 32.\n> >\n> > Brandon Williams (4):\n> >   real_path: resolve symlinks by hand\n> >   real_path: convert real_path_internal to strbuf_realpath\n> >   real_path: create real_pathdup\n> >   real_path: have callers use real_pathdup and strbuf_realpath\n> >\n> > Johannes Sixt (1):\n> >   real_path: canonicalize directory separators in root parts\n> >\n> \n> How does this relate to the 5-patch real_path: series that has been\n> on 'next' since last year?\n\nThe only difference should be in the first patch of the series which\nhandles the #define a bit differently due to the discussion that\nhappened last week.\n\nHere is the interdiff between v5 and 'origin/bw/realpath-wo-chdir':\n\ndiff --git a/abspath.c b/abspath.c\nindex 1d56f5ed9..fce40fddc 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -62,7 +62,9 @@ static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)\n }\n \n /* We allow \"recursive\" symbolic links. Only within reason, though. */\n-#define MAXSYMLINKS 5\n+#ifndef MAXSYMLINKS\n+#define MAXSYMLINKS 32\n+#endif\n \n /*\n  * Return the real path (i.e., absolute path, with symlinks resolved\n@@ -139,6 +141,8 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t\tstrbuf_reset(&symlink);\n \n \t\t\tif (num_symlinks++ > MAXSYMLINKS) {\n+\t\t\t\terrno = ELOOP;\n+\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie(\"More than %d nested symlinks \"\n \t\t\t\t\t    \"on path '%s'\", MAXSYMLINKS, path);\n\n-- \nBrandon Williams\n"},{"id":"309047","messageId":"xmqqzij0t7uh.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"20170109180418.GB62878@google.com","subject":"Re: [PATCH v5 0/5] road to reentrant real_path","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-09T18:18:46Z","receivedAt":"2017-01-09T18:18:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n>> How does this relate to the 5-patch real_path: series that has been\n>> on 'next' since last year?\n>\n> The only difference should be in the first patch of the series which\n> handles the #define a bit differently due to the discussion that\n> happened last week.\n>\n> Here is the interdiff between v5 and 'origin/bw/realpath-wo-chdir':\n\nThen can you make that an incremental patch (or two) with its own\nlog message instead?  It (or they) would look and smell like a\nbugfix patch that follows up a change that has already landed.  As\nyou know, we won't eject and replace patches that are already in\n'next' during a development cycle.\n\nThanks.\n\n>\n> diff --git a/abspath.c b/abspath.c\n> index 1d56f5ed9..fce40fddc 100644\n> --- a/abspath.c\n> +++ b/abspath.c\n> @@ -62,7 +62,9 @@ static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)\n>  }\n>  \n>  /* We allow \"recursive\" symbolic links. Only within reason, though. */\n> -#define MAXSYMLINKS 5\n> +#ifndef MAXSYMLINKS\n> +#define MAXSYMLINKS 32\n> +#endif\n>  \n>  /*\n>   * Return the real path (i.e., absolute path, with symlinks resolved\n> @@ -139,6 +141,8 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n>  \t\t\tstrbuf_reset(&symlink);\n>  \n>  \t\t\tif (num_symlinks++ > MAXSYMLINKS) {\n> +\t\t\t\terrno = ELOOP;\n> +\n>  \t\t\t\tif (die_on_error)\n>  \t\t\t\t\tdie(\"More than %d nested symlinks \"\n>  \t\t\t\t\t    \"on path '%s'\", MAXSYMLINKS, path);\n"},{"id":"309048","messageId":"20170109182433.GC62878@google.com","threadId":"44625","inReplyTo":"xmqqzij0t7uh.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 0/5] road to reentrant real_path","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-09T18:24:33Z","receivedAt":"2017-01-09T18:24:42Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 01/09, Junio C Hamano wrote:\n> Brandon Williams <bmwill@google.com> writes:\n> \n> >> How does this relate to the 5-patch real_path: series that has been\n> >> on 'next' since last year?\n> >\n> > The only difference should be in the first patch of the series which\n> > handles the #define a bit differently due to the discussion that\n> > happened last week.\n> >\n> > Here is the interdiff between v5 and 'origin/bw/realpath-wo-chdir':\n> \n> Then can you make that an incremental patch (or two) with its own\n> log message instead?  It (or they) would look and smell like a\n> bugfix patch that follows up a change that has already landed.  As\n> you know, we won't eject and replace patches that are already in\n> 'next' during a development cycle.\n> \n> Thanks.\n\nYes I'll get right on that.\n\n-- \nBrandon Williams\n"},{"id":"309050","messageId":"20170109185024.73006-1-bmwill@google.com","threadId":"44625","inReplyTo":"xmqqzij0t7uh.fsf@gitster.mtv.corp.google.com","subject":"[PATCH 1/2] real_path: prevent redefinition of MAXSYMLINKS","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-09T18:50:23Z","receivedAt":"2017-01-09T18:50:43Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"The macro 'MAXSYMLINKS' is already defined on macOS and Linux in\n<sys/param.h>.  If 'MAXSYMLINKS' has already been defined, use the value\ndefined by the OS otherwise default to a value of 32 which is more\ninline with what is allowed by many systems.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/abspath.c b/abspath.c\nindex 1d56f5ed9..0393213e5 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -62,7 +62,9 @@ static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)\n }\n \n /* We allow \"recursive\" symbolic links. Only within reason, though. */\n-#define MAXSYMLINKS 5\n+#ifndef MAXSYMLINKS\n+#define MAXSYMLINKS 32\n+#endif\n \n /*\n  * Return the real path (i.e., absolute path, with symlinks resolved\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"309051","messageId":"20170109185024.73006-2-bmwill@google.com","threadId":"44625","inReplyTo":"20170109185024.73006-1-bmwill@google.com","subject":"[PATCH 2/2] real_path: set errno when max number of symlinks is exceeded","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-01-09T18:50:24Z","receivedAt":"2017-01-09T18:50:51Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Set errno to ELOOP when the maximum number of symlinks is exceeded, as\nwould be done by other symlink-resolving functions.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n abspath.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/abspath.c b/abspath.c\nindex 0393213e5..fce40fddc 100644\n--- a/abspath.c\n+++ b/abspath.c\n@@ -141,6 +141,8 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,\n \t\t\tstrbuf_reset(&symlink);\n \n \t\t\tif (num_symlinks++ > MAXSYMLINKS) {\n+\t\t\t\terrno = ELOOP;\n+\n \t\t\t\tif (die_on_error)\n \t\t\t\t\tdie(\"More than %d nested symlinks \"\n \t\t\t\t\t    \"on path '%s'\", MAXSYMLINKS, path);\n-- \n2.11.0.390.gc69c2f50cf-goog\n\n"},{"id":"309055","messageId":"xmqqr34ct4po.fsf@gitster.mtv.corp.google.com","threadId":"44625","inReplyTo":"20170109182433.GC62878@google.com","subject":"Re: [PATCH v5 0/5] road to reentrant real_path","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-09T19:26:27Z","receivedAt":"2017-01-09T19:26:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> On 01/09, Junio C Hamano wrote:\n>> Brandon Williams <bmwill@google.com> writes:\n>> \n>> >> How does this relate to the 5-patch real_path: series that has been\n>> >> on 'next' since last year?\n>> >\n>> > The only difference should be in the first patch of the series which\n>> > handles the #define a bit differently due to the discussion that\n>> > happened last week.\n>> >\n>> > Here is the interdiff between v5 and 'origin/bw/realpath-wo-chdir':\n>> \n>> Then can you make that an incremental patch (or two) with its own\n>> log message instead?  It (or they) would look and smell like a\n>> bugfix patch that follows up a change that has already landed.  As\n>> you know, we won't eject and replace patches that are already in\n>> 'next' during a development cycle.\n>> \n>> Thanks.\n>\n> Yes I'll get right on that.\n\nThanks.  Will queue.\n"}]}