All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks
@ 2015-05-08 18:13 dturner
  2015-05-08 18:13 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks dturner
                   ` (3 more replies)
  0 siblings, 4 replies; 20+ messages in thread
From: dturner @ 2015-05-08 18:13 UTC (permalink / raw)
  To: git; +Cc: David Turner

From: David Turner <dturner@twitter.com>

Add a new function, get_tree_entry_follow_symlinks, to tree-walk.[ch].
The function is not yet used.  It will be used to implement git
cat-file --batch --follow-symlinks.

The function locates an object by path, following symlinks in the
repository.  If the symlinks lead outside the repository, the function
reports this to the caller.

Signed-off-by: David Turner <dturner@twitter.com>
---
 tree-walk.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tree-walk.h |   2 +
 2 files changed, 224 insertions(+)

diff --git a/tree-walk.c b/tree-walk.c
index 5dd9a71..6fb4b7d 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -415,6 +415,228 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
 	return error;
 }
 
+static int find_tree_entry_nonrecursive(struct tree_desc *t, char *name, unsigned char *result, unsigned *mode) {
+	int namelen = strlen(name);
+
+	while (t->size) {
+		const char *entry;
+		const unsigned char *sha1;
+		int entrylen, cmp;
+
+		sha1 = tree_entry_extract(t, &entry, mode);
+		entrylen = tree_entry_len(&t->entry);
+		update_tree_entry(t);
+		if (entrylen > namelen)
+			continue;
+		cmp = memcmp(name, entry, entrylen);
+		if (cmp > 0)
+			continue;
+		if (cmp < 0)
+			break;
+		if (entrylen == namelen) {
+			hashcpy(result, sha1);
+			return 0;
+		}
+		if (name[entrylen] != '/')
+			continue;
+		if (!S_ISDIR(*mode))
+			break;
+		hashcpy(result, sha1);
+		return 0;
+	}
+	return -1;
+}
+
+struct dir_state {
+	void *tree;
+	unsigned long size;
+	unsigned char sha1[20];
+};
+
+#define GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS 40
+
+/**
+ * Find a tree entry by following symlinks in tree_sha (which is
+ * assumed to be the root of the repository).  In the event that a
+ * symlink points outside the repository (e.g. a link to /foo or a
+ * root-level link to ../foo), the portion of the link which is
+ * outside the repository will be copied into result_path (which is
+ * assumed to hold at least PATH_MAX bytes), and *mode will be set to
+ * 0.  Otherwise, result will be filled in with the sha1 of the found
+ * object, and *mode will hold the mode of the object.
+ */
+int get_tree_enty_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, unsigned char *result_path, unsigned *mode)
+{
+	int retval = -1;
+	void *tree;
+	struct dir_state *parents = NULL;
+	size_t parents_cap = 0;
+	ssize_t parents_len = 0;
+	unsigned long size;
+	unsigned char root[20];
+	unsigned char current_tree_sha1[20];
+	struct strbuf namebuf = STRBUF_INIT;
+	enum object_type type;
+	int already_have_tree = 0;
+	struct tree_desc t = {0};
+	int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
+	int i;
+
+	strbuf_addstr(&namebuf, name);
+	hashcpy(current_tree_sha1, tree_sha1);
+
+	while (1) {
+		char *first_slash;
+		char *remainder = NULL;
+		int find_result;
+
+		if (!t.buffer) {
+			tree = read_object_with_reference(current_tree_sha1,
+							  tree_type, &size,
+							  root);
+			if (!tree)
+				goto done;
+
+			ALLOC_GROW(parents, parents_len + 1, parents_cap);
+			parents[parents_len].tree = tree;
+			parents[parents_len].size = size;
+			hashcpy(parents[parents_len].sha1, root);
+
+			parents_len++;
+
+			if (namebuf.buf[0] == '\0') {
+				hashcpy(result, root);
+				retval = 0;
+				goto done;
+			}
+
+			if (!size)
+				goto done;
+
+			/* descend */
+			init_tree_desc(&t, tree, size);
+		}
+
+		/* Handle symlinks to e.g. a//b by removing leading slashes */
+		while (namebuf.buf[0] == '/') {
+			strbuf_remove(&namebuf, 0, 1);
+		}
+
+		/* Split namebuf into a first component and a
+		 * remainder */
+		if ((first_slash = strchr(namebuf.buf, '/'))) {
+			*first_slash = 0;
+			remainder = first_slash + 1;
+		}
+
+		if (!strcmp(namebuf.buf, "..")) {
+			struct dir_state *parent;
+			/* We could end up with .. in the namebuf if
+			 * it appears in a symlink. */
+
+			if (parents_len == 1) {
+				if (remainder)
+					*first_slash = '/';
+				if (strlcpy(result_path, namebuf.buf,
+					    PATH_MAX) < PATH_MAX) {
+					*mode = 0;
+					retval = 0;
+				}
+				goto done;
+			}
+			parent = &parents[parents_len - 1];
+			free(parent->tree);
+			parents_len--;
+			parent = &parents[parents_len - 1];
+			init_tree_desc(&t, parent->tree, parent->size);
+			strbuf_remove(&namebuf, 0, remainder ? 3 : 2);
+			continue;
+		}
+
+		/* We could end up here via a symlink to dir/.. */
+		if (namebuf.buf[0] == '\0') {
+			hashcpy(result, parents[parents_len - 1].sha1);
+			retval = 0;
+			goto done;
+		}
+
+		/* Look up the first (or only) path component
+		 * in the tree. */
+		find_result = find_tree_entry_nonrecursive(&t, namebuf.buf,
+							   current_tree_sha1,
+							   mode);
+		if (find_result) {
+			retval = find_result;
+			goto done;
+		}
+
+		if (S_ISDIR(*mode)) {
+			if (!remainder) {
+				hashcpy(result, current_tree_sha1);
+				retval = 0;
+				goto done;
+			}
+			/* Descend the tree */
+			t.buffer = NULL;
+			strbuf_remove(&namebuf, 0,
+				      1 + first_slash - namebuf.buf);
+		} else if (S_ISREG(*mode)) {
+			if (!remainder) {
+				hashcpy(result, current_tree_sha1);
+				retval = 0;
+			}
+			goto done;
+		} else if (S_ISLNK(*mode)) {
+			/* Follow a symlink */
+			size_t link_len, len;
+			char *contents, *contents_start;
+			struct dir_state *parent;
+
+			if (follows_remaining-- == 0)
+				/* Too many symlinks followed */
+				goto done;
+
+			contents = read_sha1_file(current_tree_sha1, &type,
+						  &link_len);
+
+			if (!contents)
+				goto done;
+
+			if (contents[0] == '/') {
+				if (strlcpy(result_path,
+					    contents, PATH_MAX) < PATH_MAX) {
+					*mode = 0;
+					retval = 0;
+				}
+				goto done;
+			}
+
+			if (remainder)
+				len = first_slash - namebuf.buf;
+			else
+				len = namebuf.len;
+
+			contents_start = contents;
+
+			parent = &parents[parents_len - 1];
+			init_tree_desc(&t, parent->tree, parent->size);
+			strbuf_splice(&namebuf, 0, len,
+				      contents_start, link_len);
+			if (remainder)
+				namebuf.buf[link_len] = '/';
+			free(contents);
+		}
+	}
+done:
+	for (i = 0; i < parents_len; ++i) {
+		free(parents[i].tree);
+	}
+	free(parents);
+
+	strbuf_release(&namebuf);
+	return retval;
+}
+
 static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
 {
 	int namelen = strlen(name);
diff --git a/tree-walk.h b/tree-walk.h
index ae7fb3a..002e5a9 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -40,6 +40,8 @@ struct traverse_info;
 typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
 int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info);
 
+int get_tree_enty_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, unsigned char *result_path, unsigned *mode);
+
 struct traverse_info {
 	struct traverse_info *prev;
 	struct name_entry name;
-- 
2.0.4.315.gad8727a-twtrsrc

^ permalink raw reply related	[flat|nested] 20+ messages in thread
* [PATCH v8 0/3] git cat-file --follow-symlinks
@ 2015-05-13 18:21 David Turner
  2015-05-13 18:21 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks David Turner
  0 siblings, 1 reply; 20+ messages in thread
From: David Turner @ 2015-05-13 18:21 UTC (permalink / raw)
  To: git

This version includes the following:
(a) fix leak in tree-walk.c, thanks to Jeff King
(b) asciidoc now correctly indented when rendered as HTML and when
rendered as a man page.
(c) type casting is now correct
(d) Added a default to switch statement to shut up stupid compilers.

^ permalink raw reply	[flat|nested] 20+ messages in thread
* [PATCH v9 0/3]
@ 2015-05-13 18:23 David Turner
  2015-05-13 18:23 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks David Turner
  0 siblings, 1 reply; 20+ messages in thread
From: David Turner @ 2015-05-13 18:23 UTC (permalink / raw)
  To: git

Oops, forgot to ammend commit before patch v8.  This patch *really*
includes the switch and type casting fixes.

^ permalink raw reply	[flat|nested] 20+ messages in thread
* [PATCH v10 0/3]
@ 2015-05-14 20:17 David Turner
  2015-05-14 20:17 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks David Turner
  0 siblings, 1 reply; 20+ messages in thread
From: David Turner @ 2015-05-14 20:17 UTC (permalink / raw)
  To: git

Change text explaining that --follow-symlinks only works with
--batch{-check,}; now it is presented as a missing feature rather than
a necessary evil.

^ permalink raw reply	[flat|nested] 20+ messages in thread
* [PATCH v11 0/3] cat-file --follow-symlinks
@ 2015-05-14 20:38 David Turner
  2015-05-14 20:38 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks David Turner
  0 siblings, 1 reply; 20+ messages in thread
From: David Turner @ 2015-05-14 20:38 UTC (permalink / raw)
  To: git

Squashes Junio's formatting and text to cat-file.c on top of my doco
fixes from v10.

^ permalink raw reply	[flat|nested] 20+ messages in thread
* [PATCH v12 0/3] git cat-file --follow-symlinks
@ 2015-05-14 21:58 David Turner
  2015-05-14 21:58 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks David Turner
  0 siblings, 1 reply; 20+ messages in thread
From: David Turner @ 2015-05-14 21:58 UTC (permalink / raw)
  To: git

Adjust asciidoc to use +-continuation with non-indented sections
instead of [normal].

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2015-05-14 21:58 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-05-08 18:13 [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks dturner
2015-05-08 18:13 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks dturner
2015-05-08 19:45   ` Eric Sunshine
2015-05-08 20:17     ` Junio C Hamano
2015-05-08 20:25       ` Junio C Hamano
2015-05-08 20:39         ` Jeff King
2015-05-08 21:22           ` Junio C Hamano
2015-05-08 20:27       ` David Turner
2015-05-08 20:38       ` Philip Oakley
2015-05-08 21:31   ` Junio C Hamano
2015-05-08 18:13 ` [PATCH 3/3] cat-file: add --follow-symlinks to --batch dturner
2015-05-08 19:51   ` Eric Sunshine
2015-05-08 19:26 ` [PATCH 1/3] tree-walk: learn get_tree_enty_follow_symlinks Junio C Hamano
2015-05-08 20:02   ` David Turner
2015-05-08 19:43 ` Eric Sunshine
  -- strict thread matches above, loose matches on Subject: below --
2015-05-13 18:21 [PATCH v8 0/3] git cat-file --follow-symlinks David Turner
2015-05-13 18:21 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks David Turner
2015-05-13 18:23 [PATCH v9 0/3] David Turner
2015-05-13 18:23 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks David Turner
2015-05-14 20:17 [PATCH v10 0/3] David Turner
2015-05-14 20:17 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks David Turner
2015-05-14 20:38 [PATCH v11 0/3] cat-file --follow-symlinks David Turner
2015-05-14 20:38 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks David Turner
2015-05-14 21:58 [PATCH v12 0/3] git cat-file --follow-symlinks David Turner
2015-05-14 21:58 ` [PATCH 2/3] sha1_name: get_sha1_with_context learns to follow symlinks David Turner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.