[RFH] cleaning up "add across symlinks"

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



If you have this structure in your work tree:

	lrwxrwxrwx a -> c
	drwxrwxrwx c
	-rw-rw-rw- c/b

and let million monkeys give random paths to "git-update-index --add" or
"git add", you should end up with the index with two entries, a symlink
"a" and file "c/b".

Not so.  If an unfortunate monkey says "git add a/b", we happily add it to
the index, because we notice lstat("a/b") succeeds and assume that there
is such a path.  There isn't, as far as git is concerned, because we track
symbolic links.

The attached is still rough, in that while I think the commands prevent
such bogosities from entering the index, their reporting of the failure is
suboptimal.

For example, if you run the new test script added with this commit, the
error message from "update-index" would say "a/b does not exist", while it
might be true from the point of view of git, it is not from the end user's
point of view (IOW, the message may need to be reworded to read "not
adding a/b because leading path component a is a symlink --- add a itself,
or not add anything at all").  Similarly, the "git add a/b" should report
that it did not add a/b, but it doesn't.

Signed-off-by: Junio C Hamano <gitster@xxxxxxxxx>
---
 builtin-update-index.c            |   10 ++++++
 dir.c                             |    5 +++
 symlinks.c                        |    4 +-
 t/t2210-add-not-across-symlink.sh |   64 +++++++++++++++++++++++++++++++++++++
 4 files changed, 81 insertions(+), 2 deletions(-)
 create mode 100755 t/t2210-add-not-across-symlink.sh

diff --git a/builtin-update-index.c b/builtin-update-index.c
index a8795d3..24f3180 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -27,6 +27,8 @@ static int mark_valid_only;
 #define MARK_VALID 1
 #define UNMARK_VALID 2
 
+static char last_symlink_cache[PATH_MAX];
+
 static void report(const char *fmt, ...)
 {
 	va_list vp;
@@ -201,6 +203,14 @@ static int process_path(const char *path)
 	if (lstat(path, &st) < 0)
 		return process_lstat_error(path, errno);
 
+	/*
+	 * If "a" is a symlink and the caller gives "a/b", that is
+	 * a nonsense.  As far as git is concerned, there is no such
+	 * file in the work tree.
+	 */
+	if (has_symlink_leading_path(path, last_symlink_cache))
+		return process_lstat_error(path, ENOENT);
+
 	len = strlen(path);
 	if (S_ISDIR(st.st_mode))
 		return process_directory(path, len, &st);
diff --git a/dir.c b/dir.c
index d79762c..e91fed6 100644
--- a/dir.c
+++ b/dir.c
@@ -49,6 +49,11 @@ int common_prefix(const char **pathspec)
 			break;
 		}
 	}
+	if (prefix) {
+		int sym = has_symlink_leading_path(path, NULL);
+		if (sym && sym < prefix)
+			return sym;
+	}
 	return prefix;
 }
 
diff --git a/symlinks.c b/symlinks.c
index be9ace6..9d9116e 100644
--- a/symlinks.c
+++ b/symlinks.c
@@ -15,7 +15,7 @@ int has_symlink_leading_path(const char *name, char *last_symlink)
 		if (last_len < len &&
 		    !strncmp(name, last_symlink, last_len) &&
 		    name[last_len] == '/')
-			return 1;
+			return last_len;
 		*last_symlink = '\0';
 	}
 
@@ -37,7 +37,7 @@ int has_symlink_leading_path(const char *name, char *last_symlink)
 		if (S_ISLNK(st.st_mode)) {
 			if (last_symlink)
 				strcpy(last_symlink, path);
-			return 1;
+			return len;
 		}
 
 		dp[len++] = '/';
diff --git a/t/t2210-add-not-across-symlink.sh b/t/t2210-add-not-across-symlink.sh
new file mode 100755
index 0000000..102d407
--- /dev/null
+++ b/t/t2210-add-not-across-symlink.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+test_description='index not getting confused by intermediate symlinks'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	( echo a; echo c/b ) >expect &&
+	mkdir c &&
+	>c/b &&
+	ln -s c a &&
+	git update-index --add a c/b &&
+
+	git ls-files >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'update-index confusion (1)' '
+	test_must_fail git update-index --add a/b &&
+
+	git ls-files >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'update-index confusion (2)' '
+	test_must_fail git update-index --add a/b &&
+
+	git ls-files >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add confusion (0)' '
+
+	git add a/b c
+
+	git ls-files >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add confusion (1)' '
+
+	git add a/b
+
+	git ls-files >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add confusion (2)' '
+
+	git add a
+
+	git ls-files >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add confusion (3)' '
+
+	test_must_fail git add "a/*" &&
+
+	git ls-files >actual &&
+	test_cmp expect actual
+'
+
+test_done
-- 
1.5.5.120.gea9a0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux