* [PATCH 0/2] another attempt at make_absolute_path()
@ 2007-07-27 19:10 Bradford C Smith
2007-07-27 19:10 ` [PATCH 1/2] added file path helper routines Bradford C Smith
2007-07-27 19:22 ` [PATCH 0/2] another attempt at make_absolute_path() Johannes Schindelin
0 siblings, 2 replies; 4+ messages in thread
From: Bradford C Smith @ 2007-07-27 19:10 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Johannes Schindelin
Here's my attempt at make_absolute_path() and friends. I think this
version handles symlinks cleanly to avoid problems with '..' path
elements Junio pointed out recently.
I built these with another patch I previously submitted to make
git-config consistently use lockfile.c routines and tested it with the
regular test suite plus some extra tests Junio sent to the list for
checking git-config symlink handling.
I also built a separate executable with just the path handling routines
in it and spot-checked several cases to make sure it appeared to be
working as expected. ('/', loop of symlinks, lots of extra slashes, .
and .. elements, etc.)
Best Regards,
Bradford
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/2] added file path helper routines
2007-07-27 19:10 [PATCH 0/2] another attempt at make_absolute_path() Bradford C Smith
@ 2007-07-27 19:10 ` Bradford C Smith
2007-07-27 19:10 ` [PATCH 2/2] use make_absolute_path() in lock_file() Bradford C Smith
2007-07-27 19:22 ` [PATCH 0/2] another attempt at make_absolute_path() Johannes Schindelin
1 sibling, 1 reply; 4+ messages in thread
From: Bradford C Smith @ 2007-07-27 19:10 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Johannes Schindelin, Bradford C. Smith
From: Bradford C. Smith <bradford.carl.smith@gmail.com>
Added the following routines:
is_absolute_path()
split_path()
join_path()
is_valid_path()
make_absolute_path()
Signed-off-by: Bradford C. Smith <bradford.carl.smith@gmail.com>
---
cache.h | 5 ++
path.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 252 insertions(+), 0 deletions(-)
diff --git a/cache.h b/cache.h
index 53801b8..8480716 100644
--- a/cache.h
+++ b/cache.h
@@ -356,6 +356,11 @@ enum sharedrepo {
};
int git_config_perm(const char *var, const char *value);
int adjust_shared_perm(const char *path);
+int is_absolute_path(char *p);
+void split_path(const char *p, char *start, char *rest);
+void join_path(char *p, const char *start, const char *rest);
+int is_valid_path(const char *p);
+char *make_absolute_path(char *p);
int safe_create_leading_directories(char *path);
char *enter_repo(char *path, int strict);
diff --git a/path.c b/path.c
index dfff41f..2d14677 100644
--- a/path.c
+++ b/path.c
@@ -288,3 +288,250 @@ int adjust_shared_perm(const char *path)
return -2;
return 0;
}
+
+
+int is_absolute_path(char *p)
+{
+ return p[0] == '/';
+}
+
+static void strip_trailing_slashes(char *p)
+{
+ char *r = strrchr(p, '/');
+
+ if (!r)
+ return; /* no slashes at all */
+ if (*(r + 1) != '\0')
+ return; /* last slash is not at the end */
+ /*
+ * last character is a slash, back up overwriting slashes with
+ * nulls until I find a non-null or the beginning of p, but
+ * don't overwrite a '/' at the beginning of p.
+ */
+ while (r > p && *r == '/') {
+ *r = '\0';
+ r--;
+ }
+}
+
+/*
+ * p = path name that will fit in a PATH_MAX size buffer
+ * start = NULL or PATH_MAX size buffer
+ * rest = NULL or PATH_MAX size buffer
+ *
+ * split p on the last slash that isn't a trailing slash.
+ * Copy everything before the slash into start if it is not NULL. Copy
+ * everything after the slash, except trailing slashes, into rest if it
+ * is not NULL. The slash itself isn't put in either one.
+ *
+ * If p contains no non-trailing slashes, all of p will be put into
+ * start and rest will be an empty string.
+ *
+ * This routine is meant to be the exact reverse of join_path() as long
+ * as p has no trailing slashes. If p has trailing slashes, spliting
+ * and rejoining will cause them to disappear.
+ *
+ * NOTE: p is copied into a temporary buffer, so it is safe for start or
+ * rest to point into p.
+ * NOTE: if p is too big to fit in a PATH_MAX size buffer, it will be
+ * silently truncated when copied to the temporary buffer.
+ */
+void split_path(const char *p, char *start, char *rest)
+{
+ char buf[PATH_MAX];
+ char * last_slash;
+ const char * after_slash;
+
+ strncpy(buf, p, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+ strip_trailing_slashes(buf);
+ last_slash = strrchr(buf, '/');
+ if (last_slash) {
+ *last_slash = '\0';
+ after_slash = last_slash + 1;
+ } else {
+ after_slash = "";
+ }
+ if (start) {
+ strcpy(start, buf);
+ }
+ if (rest) {
+ strcpy(rest, after_slash);
+ }
+}
+
+/*
+ * p = PATH_MAX size buffer to hold result
+ * start = beginning of a path (shorter than PATH_MAX)
+ * rest = end of a path (shorter than PATH_MAX)
+ *
+ * fill p with start + '/' + rest, removing any trailing slashes from
+ * the result. If the result is too big to fit in a PATH_MAX size
+ * buffer, it will be silently truncated.
+ *
+ * NOTE: This routine uses a temporary buffer to hold the result, so it
+ * is safe to have start or rest pointing into p.
+ */
+void join_path(char *p, const char *start, const char *rest)
+{
+ char buf[PATH_MAX];
+
+ snprintf(buf, sizeof(buf), "%s/%s", start, rest);
+ strip_trailing_slashes(buf);
+ strcpy(p, buf);
+}
+
+/*
+ * p = path that will fit in a PATH_MAX size buffer
+ *
+ * return true if p is a valid path, false otherwise
+ *
+ * p is considered valid if
+ * 1. stat(p) succeeds
+ * OR
+ * 2. stat(p) fails with ENOENT and I can successfully stat() the
+ * directory part of p and see that it is a directory.
+ *
+ * NOTE: The caller must ensure that p will fit in a PATH_MAX size
+ * buffer.
+ */
+int is_valid_path(const char *p)
+{
+ char dir[PATH_MAX];
+ struct stat st;
+
+ if (stat(p, &st) == 0) {
+ return 1;
+ }
+ if (errno != ENOENT) {
+ /*
+ * there's something wrong with p other than it just not
+ * existing
+ */
+ return 0;
+ }
+ split_path(p, dir, NULL);
+ if (dir[0] == '\0') {
+ /* path is '/something' and '/' always exists */
+ return 1;
+ }
+ return (stat(dir, &st) == 0) && S_ISDIR(st.st_mode);
+}
+
+/*
+ * p = PATH_MAX size buffer containing a path that may specify a symlink
+ *
+ * If p is a symlink, overwrite p with the target of the symlink. If
+ * the target would be too big to fit in a PATH_MAX size buffer, p will
+ * not be overwritten.
+ *
+ * Returns true if p is overwritten, false otherwise.
+ */
+static int expand_symlink(char *p)
+{
+ char buf[PATH_MAX];
+ size_t len;
+
+ /* don't try to expand a symlink in an invalid path */
+ if (!is_valid_path(p)) {
+ return 0;
+ }
+ len = readlink(p, buf, sizeof(buf));
+ if (len < 0) {
+ return 0; /* not a symlink or couldn't read it */
+ }
+ if (len >= sizeof(buf)) {
+ return 0; /* link too long to expand */
+ }
+ buf[len] = '\0'; /* readlink() doesn't null terminate */
+ if (is_absolute_path(buf)) {
+ strcpy(p, buf);
+ return 1;
+ } else {
+ /* replace basename with relative symlink */
+ char dir[PATH_MAX];
+
+ split_path(p, dir, NULL);
+ if ((strlen(dir) + 1 + strlen(buf)) < PATH_MAX) {
+ join_path(p, dir, buf);
+ return 1;
+ } else {
+ /* link too big to fit in p */
+ return 0;
+ }
+ }
+}
+
+/*
+ * p = absolute path in a PATH_MAX size buffer
+ *
+ * Attempt to replace contents of p with an equivalent absolute path
+ * containing no extra slashes, symlinks, '.', or '..' elements. This
+ * is done recursively beginning with '/'. Resolution of symlinks will
+ * stop at the first element in the path that doesn't exist or cannot be
+ * read/searched for some reason, but extra slashes, '.', and '..'
+ * elements will still be resolved after that point.
+ *
+ * Always returns p.
+ */
+static char *normalize_path(char *p)
+{
+ char start[PATH_MAX];
+ char rest[PATH_MAX];
+
+ /*
+ * recursion stopping case: nothing to normalize in an empty
+ * string (represents root directory)
+ */
+ if (*p == '\0') {
+ return p;
+ }
+ split_path(p, start, rest);
+ normalize_path(start);
+ if (!strcmp(rest, ".")) {
+ /* "self" expands to nothing */
+ rest[0] = '\0';
+ }
+ if (!strcmp(rest, "..")) {
+ /*
+ * "parent" expands to nothing and removes the last
+ * element from start.
+ */
+ rest[0] = '\0';
+ split_path(start, start, NULL);
+ }
+
+ /* put the path back together */
+ join_path(p, start, rest);
+
+ if (expand_symlink(p)) {
+ /* p was a symlink, so I must normalize its expansion */
+ normalize_path(p);
+ }
+ return p;
+}
+
+
+/*
+ * p = absolute or partial path in a PATH_MAX size buffer
+ *
+ * normalize p to an absolute path containing no symlinks and no . or ..
+ * directories.
+ *
+ * NOTE: If any of the path components do not exist or cannot be read/searched
+ * for some reason, this routine will only standardize the parts of the
+ * path up to the "bad" component.
+ *
+ * Always returns p.
+ */
+char *make_absolute_path(char *p)
+{
+ if (!is_absolute_path(p)) {
+ char cwd[PATH_MAX];
+
+ if (NULL == getcwd(cwd, sizeof(cwd)))
+ die("cannot get working directory");
+ join_path(p, cwd, p);
+ }
+ return normalize_path(p);
+}
--
1.5.3.rc3.9.g9ef91
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] use make_absolute_path() in lock_file()
2007-07-27 19:10 ` [PATCH 1/2] added file path helper routines Bradford C Smith
@ 2007-07-27 19:10 ` Bradford C Smith
0 siblings, 0 replies; 4+ messages in thread
From: Bradford C Smith @ 2007-07-27 19:10 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Johannes Schindelin, Bradford C. Smith
From: Bradford C. Smith <bradford.carl.smith@gmail.com>
Use make_absolute_path() to get fully resolved path name for creating
the lock file.
Signed-off-by: Bradford C. Smith <bradford.carl.smith@gmail.com>
---
lockfile.c | 16 +++-------------
1 files changed, 3 insertions(+), 13 deletions(-)
diff --git a/lockfile.c b/lockfile.c
index 9202472..57f850f 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -28,20 +28,10 @@ static void remove_lock_file_on_signal(int signo)
static int lock_file(struct lock_file *lk, const char *path)
{
int fd;
- struct stat st;
- if ((!lstat(path, &st)) && S_ISLNK(st.st_mode)) {
- ssize_t sz;
- static char target[PATH_MAX];
- sz = readlink(path, target, sizeof(target));
- if (sz < 0)
- warning("Cannot readlink %s", path);
- else if (target[0] != '/')
- warning("Cannot lock target of relative symlink %s", path);
- else
- path = target;
- }
- sprintf(lk->filename, "%s.lock", path);
+ strcpy(lk->filename, path);
+ make_absolute_path(lk->filename);
+ strcat(lk->filename, ".lock");
fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
if (0 <= fd) {
if (!lock_file_list) {
--
1.5.3.rc3.9.g9ef91
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 0/2] another attempt at make_absolute_path()
2007-07-27 19:10 [PATCH 0/2] another attempt at make_absolute_path() Bradford C Smith
2007-07-27 19:10 ` [PATCH 1/2] added file path helper routines Bradford C Smith
@ 2007-07-27 19:22 ` Johannes Schindelin
1 sibling, 0 replies; 4+ messages in thread
From: Johannes Schindelin @ 2007-07-27 19:22 UTC (permalink / raw)
To: Bradford C Smith; +Cc: git, Junio C Hamano
Hi,
On Fri, 27 Jul 2007, Bradford C Smith wrote:
> Here's my attempt at make_absolute_path() and friends. I think this
> version handles symlinks cleanly to avoid problems with '..' path
> elements Junio pointed out recently.
>
> I built these with another patch I previously submitted to make
> git-config consistently use lockfile.c routines and tested it with the
> regular test suite plus some extra tests Junio sent to the list for
> checking git-config symlink handling.
>
> I also built a separate executable with just the path handling routines
> in it and spot-checked several cases to make sure it appeared to be
> working as expected. ('/', loop of symlinks, lots of extra slashes, .
> and .. elements, etc.)
Heh. Just a couple of minutes after I sent my version...
Although I added tests, and you did not...
Ciao,
Dscho
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2007-07-27 19:22 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-07-27 19:10 [PATCH 0/2] another attempt at make_absolute_path() Bradford C Smith
2007-07-27 19:10 ` [PATCH 1/2] added file path helper routines Bradford C Smith
2007-07-27 19:10 ` [PATCH 2/2] use make_absolute_path() in lock_file() Bradford C Smith
2007-07-27 19:22 ` [PATCH 0/2] another attempt at make_absolute_path() Johannes Schindelin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).