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