{"thread":{"id":"9264","subject":"[PATCH 0/2] another attempt at make_absolute_path()","startedAt":"2007-07-27T19:10:54Z","lastAt":"2007-07-27T19:22:30Z","messageCount":4,"participants":["Bradford C Smith","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"48821","messageId":"11855634561516-git-send-email-bradford.carl.smith@gmail.com","threadId":"9264","inReplyTo":null,"subject":"[PATCH 0/2] another attempt at make_absolute_path()","fromName":"Bradford C Smith","fromEmail":"bradford.carl.smith@gmail.com","sentAt":"2007-07-27T19:10:54Z","receivedAt":"2007-07-27T19:10:54Z","isPatch":true,"sender":{"key":"bradford.carl.smith@gmail.com","avatar":"https://gravatar.com/avatar/699930ad8ca562e38156ee60270b2fee830cc0cb5d94c2f70c6db4a705723c54?d=mp&s=160"},"body":"Here's my attempt at make_absolute_path() and friends.  I think this\nversion handles symlinks cleanly to avoid problems with '..' path\nelements Junio pointed out recently.\n\nI built these with another patch I previously submitted to make\ngit-config consistently use lockfile.c routines and tested it with the\nregular test suite plus some extra tests Junio sent to the list for\nchecking git-config symlink handling.\n\nI also built a separate executable with just the path handling routines\nin it and spot-checked several cases to make sure it appeared to be\nworking as expected.  ('/', loop of symlinks, lots of extra slashes, .\nand .. elements, etc.)\n\nBest Regards,\n\nBradford\n"},{"id":"48822","messageId":"11855634582686-git-send-email-bradford.carl.smith@gmail.com","threadId":"9264","inReplyTo":"11855634561516-git-send-email-bradford.carl.smith@gmail.com","subject":"[PATCH 1/2] added file path helper routines","fromName":"Bradford C Smith","fromEmail":"bradford.carl.smith@gmail.com","sentAt":"2007-07-27T19:10:55Z","receivedAt":"2007-07-27T19:10:55Z","isPatch":true,"sender":{"key":"bradford.carl.smith@gmail.com","avatar":"https://gravatar.com/avatar/699930ad8ca562e38156ee60270b2fee830cc0cb5d94c2f70c6db4a705723c54?d=mp&s=160"},"body":"From: Bradford C. Smith <bradford.carl.smith@gmail.com>\n\nAdded the following routines:\n\nis_absolute_path()\nsplit_path()\njoin_path()\nis_valid_path()\nmake_absolute_path()\n\nSigned-off-by: Bradford C. Smith <bradford.carl.smith@gmail.com>\n---\n cache.h |    5 ++\n path.c  |  247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 252 insertions(+), 0 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 53801b8..8480716 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -356,6 +356,11 @@ enum sharedrepo {\n };\n int git_config_perm(const char *var, const char *value);\n int adjust_shared_perm(const char *path);\n+int is_absolute_path(char *p);\n+void split_path(const char *p, char *start, char *rest);\n+void join_path(char *p, const char *start, const char *rest);\n+int is_valid_path(const char *p);\n+char *make_absolute_path(char *p);\n int safe_create_leading_directories(char *path);\n char *enter_repo(char *path, int strict);\n \ndiff --git a/path.c b/path.c\nindex dfff41f..2d14677 100644\n--- a/path.c\n+++ b/path.c\n@@ -288,3 +288,250 @@ int adjust_shared_perm(const char *path)\n \t\treturn -2;\n \treturn 0;\n }\n+\n+\n+int is_absolute_path(char *p)\n+{\n+\treturn p[0] == '/';\n+}\n+\n+static void strip_trailing_slashes(char *p)\n+{\n+\tchar *r = strrchr(p, '/');\n+\n+\tif (!r)\n+\t\treturn; /* no slashes at all */\n+\tif (*(r + 1) != '\\0')\n+\t\treturn; /* last slash is not at the end */\n+\t/*\n+\t * last character is a slash, back up overwriting slashes with\n+\t * nulls until I find a non-null or the beginning of p, but\n+\t * don't overwrite a '/' at the beginning of p.\n+\t */\n+\twhile (r > p && *r == '/') {\n+\t\t*r = '\\0';\n+\t\tr--;\n+\t}\n+}\n+\n+/*\n+ * p = path name that will fit in a PATH_MAX size buffer\n+ * start = NULL or PATH_MAX size buffer\n+ * rest = NULL or PATH_MAX size buffer\n+ *\n+ * split p on the last slash that isn't a trailing slash.\n+ * Copy everything before the slash into start if it is not NULL.  Copy\n+ * everything after the slash, except trailing slashes, into rest if it\n+ * is not NULL.  The slash itself isn't put in either one.\n+ *\n+ * If p contains no non-trailing slashes, all of p will be put into\n+ * start and rest will be an empty string.\n+ *\n+ * This routine is meant to be the exact reverse of join_path() as long\n+ * as p has no trailing slashes.  If p has trailing slashes, spliting\n+ * and rejoining will cause them to disappear.\n+ *\n+ * NOTE: p is copied into a temporary buffer, so it is safe for start or\n+ * rest to point into p.\n+ * NOTE: if p is too big to fit in a PATH_MAX size buffer, it will be\n+ * silently truncated when copied to the temporary buffer.\n+ */\n+void split_path(const char *p, char *start, char *rest)\n+{\n+\tchar buf[PATH_MAX];\n+\tchar * last_slash;\n+\tconst char * after_slash;\n+\n+\tstrncpy(buf, p, sizeof(buf));\n+\tbuf[sizeof(buf) - 1] = '\\0';\n+\tstrip_trailing_slashes(buf);\n+\tlast_slash = strrchr(buf, '/');\n+\tif (last_slash) {\n+\t\t*last_slash = '\\0';\n+\t\tafter_slash = last_slash + 1;\n+\t} else {\n+\t\tafter_slash = \"\";\n+\t}\n+\tif (start) {\n+\t\tstrcpy(start, buf);\n+\t}\n+\tif (rest) {\n+\t\tstrcpy(rest, after_slash);\n+\t}\n+}\n+\n+/*\n+ * p = PATH_MAX size buffer to hold result\n+ * start = beginning of a path (shorter than PATH_MAX)\n+ * rest = end of a path (shorter than PATH_MAX)\n+ *\n+ * fill p with start + '/' + rest, removing any trailing slashes from\n+ * the result.  If the result is too big to fit in a PATH_MAX size\n+ * buffer, it will be silently truncated.\n+ *\n+ * NOTE: This routine uses a temporary buffer to hold the result, so it\n+ *       is safe to have start or rest pointing into p.\n+ */\n+void join_path(char *p, const char *start, const char *rest)\n+{\n+\tchar buf[PATH_MAX];\n+\n+\tsnprintf(buf, sizeof(buf), \"%s/%s\", start, rest);\n+\tstrip_trailing_slashes(buf);\n+\tstrcpy(p, buf);\n+}\n+\n+/*\n+ * p = path that will fit in a PATH_MAX size buffer\n+ *\n+ * return true if p is a valid path, false otherwise\n+ *\n+ * p is considered valid if\n+ * 1. stat(p) succeeds\n+ *    OR\n+ * 2. stat(p) fails with ENOENT and I can successfully stat() the\n+ * directory part of p and see that it is a directory.\n+ *\n+ * NOTE: The caller must ensure that p will fit in a PATH_MAX size\n+ *       buffer.\n+ */\n+int is_valid_path(const char *p)\n+{\n+\tchar dir[PATH_MAX];\n+\tstruct stat st;\n+\n+\tif (stat(p, &st) == 0) {\n+\t\treturn 1;\n+\t}\n+\tif (errno != ENOENT) {\n+\t\t/*\n+\t\t * there's something wrong with p other than it just not\n+\t\t * existing\n+\t\t */\n+\t\treturn 0;\n+\t}\n+\tsplit_path(p, dir, NULL);\n+\tif (dir[0] == '\\0') {\n+\t\t/* path is '/something' and '/' always exists */\n+\t\treturn 1;\n+\t}\n+\treturn (stat(dir, &st) == 0) && S_ISDIR(st.st_mode);\n+}\n+\n+/*\n+ * p = PATH_MAX size buffer containing a path that may specify a symlink\n+ *\n+ * If p is a symlink, overwrite p with the target of the symlink.  If\n+ * the target would be too big to fit in a PATH_MAX size buffer, p will\n+ * not be overwritten.\n+ *\n+ * Returns true if p is overwritten, false otherwise.\n+ */\n+static int expand_symlink(char *p)\n+{\n+\tchar buf[PATH_MAX];\n+\tsize_t len;\n+\n+\t/* don't try to expand a symlink in an invalid path */\n+\tif (!is_valid_path(p)) {\n+\t\treturn 0;\n+\t}\n+\tlen = readlink(p, buf, sizeof(buf));\n+\tif (len < 0) {\n+\t\treturn 0; /* not a symlink or couldn't read it */\n+\t}\n+\tif (len >= sizeof(buf)) {\n+\t\treturn 0; /* link too long to expand */\n+\t}\n+\tbuf[len] = '\\0'; /* readlink() doesn't null terminate */\n+\tif (is_absolute_path(buf)) {\n+\t\tstrcpy(p, buf);\n+\t\treturn 1;\n+\t} else {\n+\t\t/* replace basename with relative symlink */\n+\t\tchar dir[PATH_MAX];\n+\n+\t\tsplit_path(p, dir, NULL);\n+\t\tif ((strlen(dir) + 1 + strlen(buf)) < PATH_MAX) {\n+\t\t\tjoin_path(p, dir, buf);\n+\t\t\treturn 1;\n+\t\t} else {\n+\t\t\t/* link too big to fit in p */\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+}\n+\n+/*\n+ * p = absolute path in a PATH_MAX size buffer\n+ *\n+ * Attempt to replace contents of p with an equivalent absolute path\n+ * containing no extra slashes, symlinks, '.', or '..' elements.  This\n+ * is done recursively beginning with '/'.  Resolution of symlinks will\n+ * stop at the first element in the path that doesn't exist or cannot be\n+ * read/searched for some reason, but extra slashes, '.', and '..'\n+ * elements will still be resolved after that point.\n+ *\n+ * Always returns p.\n+ */\n+static char *normalize_path(char *p)\n+{\n+\tchar start[PATH_MAX];\n+\tchar rest[PATH_MAX];\n+\n+\t/*\n+\t * recursion stopping case: nothing to normalize in an empty\n+\t * string (represents root directory)\n+\t */\n+\tif (*p == '\\0') {\n+\t\treturn p;\n+\t}\n+\tsplit_path(p, start, rest);\n+\tnormalize_path(start);\n+\tif (!strcmp(rest, \".\")) {\n+\t\t/* \"self\" expands to nothing */\n+\t\trest[0] = '\\0';\n+\t}\n+\tif (!strcmp(rest, \"..\")) {\n+\t\t/*\n+\t\t * \"parent\" expands to nothing and removes the last\n+\t\t * element from start.\n+\t\t */\n+\t\trest[0] = '\\0';\n+\t\tsplit_path(start, start, NULL);\n+\t}\n+\n+\t/* put the path back together */\n+\tjoin_path(p, start, rest);\n+\n+\tif (expand_symlink(p)) {\n+\t\t/* p was a symlink, so I must normalize its expansion */\n+\t\tnormalize_path(p);\n+\t}\n+\treturn p;\n+}\n+\n+\n+/*\n+ * p = absolute or partial path in a PATH_MAX size buffer\n+ *\n+ * normalize p to an absolute path containing no symlinks and no . or ..\n+ * directories.\n+ *\n+ * NOTE: If any of the path components do not exist or cannot be read/searched\n+ * for some reason, this routine will only standardize the parts of the\n+ * path up to the \"bad\" component.\n+ *\n+ * Always returns p.\n+ */\n+char *make_absolute_path(char *p)\n+{\n+\tif (!is_absolute_path(p)) {\n+\t\tchar cwd[PATH_MAX];\n+\n+\t\tif (NULL == getcwd(cwd, sizeof(cwd)))\n+\t\t\tdie(\"cannot get working directory\");\n+\t\tjoin_path(p, cwd, p);\n+\t}\n+\treturn normalize_path(p);\n+}\n-- \n1.5.3.rc3.9.g9ef91\n"},{"id":"48823","messageId":"1185563460165-git-send-email-bradford.carl.smith@gmail.com","threadId":"9264","inReplyTo":"11855634582686-git-send-email-bradford.carl.smith@gmail.com","subject":"[PATCH 2/2] use make_absolute_path() in lock_file()","fromName":"Bradford C Smith","fromEmail":"bradford.carl.smith@gmail.com","sentAt":"2007-07-27T19:10:56Z","receivedAt":"2007-07-27T19:10:56Z","isPatch":true,"sender":{"key":"bradford.carl.smith@gmail.com","avatar":"https://gravatar.com/avatar/699930ad8ca562e38156ee60270b2fee830cc0cb5d94c2f70c6db4a705723c54?d=mp&s=160"},"body":"From: Bradford C. Smith <bradford.carl.smith@gmail.com>\n\nUse make_absolute_path() to get fully resolved path name for creating\nthe lock file.\n\nSigned-off-by: Bradford C. Smith <bradford.carl.smith@gmail.com>\n---\n lockfile.c |   16 +++-------------\n 1 files changed, 3 insertions(+), 13 deletions(-)\n\ndiff --git a/lockfile.c b/lockfile.c\nindex 9202472..57f850f 100644\n--- a/lockfile.c\n+++ b/lockfile.c\n@@ -28,20 +28,10 @@ static void remove_lock_file_on_signal(int signo)\n static int lock_file(struct lock_file *lk, const char *path)\n {\n \tint fd;\n-\tstruct stat st;\n \n-\tif ((!lstat(path, &st)) && S_ISLNK(st.st_mode)) {\n-\t\tssize_t sz;\n-\t\tstatic char target[PATH_MAX];\n-\t\tsz = readlink(path, target, sizeof(target));\n-\t\tif (sz < 0)\n-\t\t\twarning(\"Cannot readlink %s\", path);\n-\t\telse if (target[0] != '/')\n-\t\t\twarning(\"Cannot lock target of relative symlink %s\", path);\n-\t\telse\n-\t\t\tpath = target;\n-\t}\n-\tsprintf(lk->filename, \"%s.lock\", path);\n+\tstrcpy(lk->filename, path);\n+\tmake_absolute_path(lk->filename);\n+\tstrcat(lk->filename, \".lock\");\n \tfd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);\n \tif (0 <= fd) {\n \t\tif (!lock_file_list) {\n-- \n1.5.3.rc3.9.g9ef91\n"},{"id":"48826","messageId":"Pine.LNX.4.64.0707272022090.14781@racer.site","threadId":"9264","inReplyTo":"11855634561516-git-send-email-bradford.carl.smith@gmail.com","subject":"Re: [PATCH 0/2] another attempt at make_absolute_path()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-27T19:22:30Z","receivedAt":"2007-07-27T19:22:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 27 Jul 2007, Bradford C Smith wrote:\n\n> Here's my attempt at make_absolute_path() and friends.  I think this\n> version handles symlinks cleanly to avoid problems with '..' path\n> elements Junio pointed out recently.\n> \n> I built these with another patch I previously submitted to make\n> git-config consistently use lockfile.c routines and tested it with the\n> regular test suite plus some extra tests Junio sent to the list for\n> checking git-config symlink handling.\n> \n> I also built a separate executable with just the path handling routines\n> in it and spot-checked several cases to make sure it appeared to be\n> working as expected.  ('/', loop of symlinks, lots of extra slashes, .\n> and .. elements, etc.)\n\nHeh.  Just a couple of minutes after I sent my version...\n\nAlthough I added tests, and you did not...\n\nCiao,\nDscho\n"}]}