[WIP/RFC PATCH 1/2] Introduce GIT_INDEX_PREFIX

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

 



GIT_INDEX_PREFIX is used to limit write access to a specific directory.
Only "important" information is protected by index prefix (those will
be used to create tree objects)

When GIT_INDEX_PREFIX is set, any attempt to modify the index (refresh
it is okay though) will bail out. read-tree and merge, however, can
write to full index. For merge, no conflict is allowed outside index
prefix.
---
 builtin-merge-recursive.c |    4 ++-
 builtin-read-tree.c       |    5 +++
 builtin-rev-parse.c       |    5 +++
 builtin-update-index.c    |    3 ++
 cache.h                   |    2 +
 environment.c             |   12 ++++++
 read-cache.c              |   84 +++++++++++++++++++++++++++++++++++++++++++-
 unpack-trees.c            |    3 ++
 unpack-trees.h            |    3 +-
 9 files changed, 117 insertions(+), 4 deletions(-)

diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index 362c290..94ca808 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -209,8 +209,10 @@ static int git_merge_trees(int index_only,
 	memset(&opts, 0, sizeof(opts));
 	if (index_only)
 		opts.index_only = 1;
-	else
+	else {
 		opts.update = 1;
+		opts.check_index_prefix = 1;
+	}
 	opts.merge = 1;
 	opts.head_idx = 2;
 	opts.fn = threeway_merge;
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 5a09e17..ea0a929 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -242,6 +242,11 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
 			opts.head_idx = 1;
 	}
 
+	if (get_index_prefix()) {
+		read_cache();
+		opts.check_index_prefix = 1;
+	}
+
 	for (i = 0; i < nr_trees; i++) {
 		struct tree *tree = trees[i];
 		parse_tree(tree);
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index a7860ed..1888208 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -499,6 +499,11 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 					puts(prefix);
 				continue;
 			}
+			if (!strcmp(arg, "--show-index-prefix")) {
+				if (get_index_prefix())
+					puts(get_index_prefix());
+				continue;
+			}
 			if (!strcmp(arg, "--show-cdup")) {
 				const char *pfx = prefix;
 				if (!is_inside_work_tree()) {
diff --git a/builtin-update-index.c b/builtin-update-index.c
index 9e0d7ab..71c83ec 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -242,6 +242,9 @@ static void chmod_path(int flip, const char *path)
 	struct cache_entry *ce;
 	unsigned int mode;
 
+	if (get_index_prefix() && prefixcmp(path, get_index_prefix()))
+		die("%s: cannot update index outside %s", path, get_index_prefix());
+
 	pos = cache_name_pos(path, strlen(path));
 	if (pos < 0)
 		goto fail;
diff --git a/cache.h b/cache.h
index eab1a17..9f5a613 100644
--- a/cache.h
+++ b/cache.h
@@ -300,6 +300,7 @@ static inline enum object_type object_type(unsigned int mode)
 #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 #define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
 #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
+#define INDEX_PREFIX_ENVIRONMENT "GIT_INDEX_PREFIX"
 #define GITATTRIBUTES_FILE ".gitattributes"
 #define INFOATTRIBUTES_FILE "info/attributes"
 #define ATTRIBUTE_MACRO_PREFIX "[attr]"
@@ -318,6 +319,7 @@ extern int set_git_dir(const char *path);
 extern const char *get_git_work_tree(void);
 extern const char *read_gitfile_gently(const char *path);
 extern void set_git_work_tree(const char *tree);
+extern const char *get_index_prefix(void);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
diff --git a/environment.c b/environment.c
index 73feb2d..a8edaf5 100644
--- a/environment.c
+++ b/environment.c
@@ -46,6 +46,8 @@ enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 char *git_work_tree_cfg;
 static char *work_tree;
 
+static const char *index_prefix;
+
 static const char *git_dir;
 static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
 
@@ -71,6 +73,9 @@ static void setup_git_env(void)
 	git_graft_file = getenv(GRAFT_ENVIRONMENT);
 	if (!git_graft_file)
 		git_graft_file = xstrdup(git_path("info/grafts"));
+	index_prefix = getenv(INDEX_PREFIX_ENVIRONMENT);
+	if (index_prefix && (!*index_prefix || index_prefix[strlen(index_prefix)-1] != '/'))
+		die("GIT_INDEX_PREFIX must end with a slash");
 }
 
 int is_bare_repository(void)
@@ -122,6 +127,13 @@ const char *get_git_work_tree(void)
 	return work_tree;
 }
 
+const char *get_index_prefix()
+{
+	if (!git_dir)
+		setup_git_env();
+	return index_prefix;
+}
+
 char *get_object_directory(void)
 {
 	if (!git_object_dir)
diff --git a/read-cache.c b/read-cache.c
index ac9a8e7..4f8d44b 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -23,6 +23,11 @@
 
 struct index_state the_index;
 
+static int outside_index_prefix(const struct index_state *istate, const char *ce_name)
+{
+	return istate == &the_index && get_index_prefix() && prefixcmp(ce_name, get_index_prefix());
+}
+
 static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
 {
 	istate->cache[nr] = ce;
@@ -380,6 +385,8 @@ int remove_index_entry_at(struct index_state *istate, int pos)
 {
 	struct cache_entry *ce = istate->cache[pos];
 
+	if (outside_index_prefix(istate, ce->name))
+		die("%s: cannot remove from index outside %s", ce->name, get_index_prefix());
 	remove_name_hash(ce);
 	istate->cache_changed = 1;
 	istate->cache_nr--;
@@ -394,6 +401,10 @@ int remove_index_entry_at(struct index_state *istate, int pos)
 int remove_file_from_index(struct index_state *istate, const char *path)
 {
 	int pos = index_name_pos(istate, path, strlen(path));
+
+	if (outside_index_prefix(istate, path))
+		die("%s: cannot remove from index from outside %s", path, get_index_prefix());
+
 	if (pos < 0)
 		pos = -pos-1;
 	cache_tree_invalidate_path(istate->cache_tree, path);
@@ -793,21 +804,35 @@ static int check_file_directory_conflict(struct index_state *istate,
 	return retval + has_dir_name(istate, ce, pos, ok_to_replace);
 }
 
+static int ce_compare(const struct cache_entry *ce1, const struct cache_entry *ce2)
+{
+	return ce1->ce_mode == ce2->ce_mode &&
+		((ce1->ce_flags ^ ce2->ce_flags) & ~(CE_HASHED | CE_UPDATE)) == 0 &&
+		!memcmp(ce1->sha1, ce2->sha1, 20) &&
+		!strcmp(ce1->name, ce2->name);
+}
+
 static int add_index_entry_with_check(struct index_state *istate, struct cache_entry *ce, int option)
 {
 	int pos;
 	int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
 	int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
 	int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
+	int is_outside_index_prefix = outside_index_prefix(istate, ce->name);
 
 	cache_tree_invalidate_path(istate->cache_tree, ce->name);
 	pos = index_name_pos(istate, ce->name, ce->ce_flags);
 
 	/* existing match? Just replace it. */
 	if (pos >= 0) {
+		if (is_outside_index_prefix && !ce_compare(istate->cache[pos], ce))
+			die("%s: cannot add to index outside %s", ce->name, get_index_prefix());
 		replace_index_entry(istate, pos, ce);
 		return 0;
 	}
+
+	if (is_outside_index_prefix)
+		die("%s: cannot add to index outside %s", ce->name, get_index_prefix());
 	pos = -pos-1;
 
 	/*
@@ -842,9 +867,11 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
 {
 	int pos;
 
-	if (option & ADD_CACHE_JUST_APPEND)
+	if (option & ADD_CACHE_JUST_APPEND) {
+		if (outside_index_prefix(istate, ce->name))
+			die("%s: cannot add to index outside %s", ce->name, get_index_prefix());
 		pos = istate->cache_nr;
-	else {
+	} else {
 		int ret;
 		ret = add_index_entry_with_check(istate, ce, option);
 		if (ret <= 0)
@@ -1385,3 +1412,56 @@ int write_index(const struct index_state *istate, int newfd)
 	}
 	return ce_flush(&c, newfd);
 }
+
+int check_index_prefix(const struct index_state *index, int is_merge)
+{
+	unsigned start,end1,end2,i;
+	struct cache_entry **cache1, **cache2;
+	const char *index_prefix = get_index_prefix();
+
+	if (!index_prefix)
+		return 0;
+
+	/* Check for unmerged entries first */
+	for (i = 0; i < index->cache_nr; i++) {
+		struct cache_entry *ce = index->cache[i];
+		if (ce_stage(ce) && prefixcmp(ce->name, index_prefix))
+			return 1;
+	}
+
+	if (is_merge)
+		return 0;
+
+	/* not a merge, no change is allowed outside index prefix */
+
+	cache1 = the_index.cache;
+	cache2 = index->cache;
+	start = 0;
+	end1 = the_index.cache_nr ? the_index.cache_nr - 1 : 0;
+	end2 = index->cache_nr ? index->cache_nr - 1 : 0;
+	while (start < end1 && start < end2 &&
+		ce_compare(cache1[start], cache2[start]))
+		start ++;
+
+	while (end1 > start && end2 > start &&
+		ce_compare(cache1[end1], cache2[end2])) {
+		end1 --;
+		end2 --;
+	}
+
+	/*
+	 * everything in start..end1 and start..end2 must
+	 * be prefixed by get_index_prefix()
+	 */
+	if (start < end1 &&
+		(prefixcmp(cache1[start]->name, index_prefix) ||
+		prefixcmp(cache1[end1]->name, index_prefix)))
+		return 1;
+
+	if (start < end2 &&
+		(prefixcmp(cache2[start]->name, index_prefix) ||
+		prefixcmp(cache2[end2]->name, index_prefix)))
+		return 1;
+
+	return 0;
+}
diff --git a/unpack-trees.c b/unpack-trees.c
index 0de5a31..2ef4827 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -403,6 +403,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 	if (o->trivial_merges_only && o->nontrivial_merge)
 		return unpack_failed(o, "Merge requires file-level merging");
 
+	if (o->check_index_prefix && check_index_prefix(&o->result, o->merge && !o->prefix))
+		return unpack_failed(o, "Merge outside index prefix");
+
 	o->src_index = NULL;
 	if (check_updates(o))
 		return -1;
diff --git a/unpack-trees.h b/unpack-trees.h
index 94e5672..a1b46f9 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -26,7 +26,8 @@ struct unpack_trees_options {
 		     verbose_update:1,
 		     aggressive:1,
 		     skip_unmerged:1,
-		     gently:1;
+		     gently:1,
+		     check_index_prefix:1;
 	const char *prefix;
 	int pos;
 	struct dir_struct *dir;
-- 
1.5.5.GIT
--
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