Commit 0862ec2eb9529573ab46f3975defc0b7632bede4

Edward Thomson 2015-09-17T09:58:38

core::mkdir tests: ensure we don't stomp symlinks in mkdir In `mkdir` and `mkdir_r`, ensure that we don't try to remove symlinks that are in our way.

diff --git a/src/path.c b/src/path.c
index cb11ace..72cb289 100644
--- a/src/path.c
+++ b/src/path.c
@@ -526,6 +526,17 @@ bool git_path_isfile(const char *path)
 	return S_ISREG(st.st_mode) != 0;
 }
 
+bool git_path_islink(const char *path)
+{
+	struct stat st;
+
+	assert(path);
+	if (p_lstat(path, &st) < 0)
+		return false;
+
+	return S_ISLNK(st.st_mode) != 0;
+}
+
 #ifdef GIT_WIN32
 
 bool git_path_is_empty_dir(const char *path)
diff --git a/src/path.h b/src/path.h
index 971603e..c76e903 100644
--- a/src/path.h
+++ b/src/path.h
@@ -169,6 +169,12 @@ extern bool git_path_isdir(const char *path);
 extern bool git_path_isfile(const char *path);
 
 /**
+ * Check if the given path points to a symbolic link.
+ * @return true or false
+ */
+extern bool git_path_islink(const char *path);
+
+/**
  * Check if the given path is a directory, and is empty.
  */
 extern bool git_path_is_empty_dir(const char *path);
diff --git a/tests/core/mkdir.c b/tests/core/mkdir.c
index 11fecb1..8d487e5 100644
--- a/tests/core/mkdir.c
+++ b/tests/core/mkdir.c
@@ -222,6 +222,40 @@ void test_core_mkdir__chmods(void)
 	check_mode(0777, st.st_mode);
 }
 
+void test_core_mkdir__keeps_parent_symlinks(void)
+{
+#ifndef GIT_WIN32
+	git_buf path = GIT_BUF_INIT;
+
+	cl_set_cleanup(cleanup_basic_dirs, NULL);
+
+	/* make a directory */
+	cl_assert(!git_path_isdir("d0"));
+	cl_git_pass(git_futils_mkdir("d0", 0755, 0));
+	cl_assert(git_path_isdir("d0"));
+
+	cl_must_pass(symlink("d0", "d1"));
+	cl_assert(git_path_islink("d1"));
+
+	cl_git_pass(git_futils_mkdir("d1/foo/bar", 0755, GIT_MKDIR_PATH|GIT_MKDIR_REMOVE_SYMLINKS));
+	cl_assert(git_path_islink("d1"));
+	cl_assert(git_path_isdir("d1/foo/bar"));
+	cl_assert(git_path_isdir("d0/foo/bar"));
+
+	cl_must_pass(symlink("d0", "d2"));
+	cl_assert(git_path_islink("d2"));
+
+	git_buf_joinpath(&path, clar_sandbox_path(), "d2/other/dir");
+
+	cl_git_pass(git_futils_mkdir(path.ptr, 0755, GIT_MKDIR_PATH|GIT_MKDIR_REMOVE_SYMLINKS));
+	cl_assert(git_path_islink("d2"));
+	cl_assert(git_path_isdir("d2/other/dir"));
+	cl_assert(git_path_isdir("d0/other/dir"));
+
+	git_buf_free(&path);
+#endif
+}
+
 void test_core_mkdir__mkdir_path_inside_unwriteable_parent(void)
 {
 	struct stat st;