Merge branch 'jk/quarantine-received-objects'
authorJunio C Hamano <gitster@pobox.com>
Mon, 17 Oct 2016 20:25:20 +0000 (13:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 17 Oct 2016 20:25:20 +0000 (13:25 -0700)
In order for the receiving end of "git push" to inspect the
received history and decide to reject the push, the objects sent
from the sending end need to be made available to the hook and
the mechanism for the connectivity check, and this was done
traditionally by storing the objects in the receiving repository
and letting "git gc" to expire it. Instead, store the newly
received objects in a temporary area, and make them available by
reusing the alternate object store mechanism to them only while we
decide if we accept the check, and once we decide, either migrate
them to the repository or purge them immediately.

* jk/quarantine-received-objects:
tmp-objdir: do not migrate files starting with '.'
tmp-objdir: put quarantine information in the environment
receive-pack: quarantine objects until pre-receive accepts
tmp-objdir: introduce API for temporary object directories
check_connected: accept an env argument

1  2 
Makefile
builtin/receive-pack.c
cache.h
diff --combined Makefile
index d15bf8de9d36065865781caf5adb069c25657432,4e3becb4980b1d83384eea2c3a67926101e4a044..9d6c24503155471cc66882f6ba2040456a1cf470
+++ b/Makefile
@@@ -467,7 -467,6 +467,7 @@@ SPATCH = spatc
  export TCL_PATH TCLTK_PATH
  
  SPARSE_FLAGS =
 +SPATCH_FLAGS = --all-includes
  
  
  
@@@ -832,6 -831,7 +832,7 @@@ LIB_OBJS += submodule-config.
  LIB_OBJS += symlinks.o
  LIB_OBJS += tag.o
  LIB_OBJS += tempfile.o
+ LIB_OBJS += tmp-objdir.o
  LIB_OBJS += trace.o
  LIB_OBJS += trailer.o
  LIB_OBJS += transport.o
@@@ -2315,7 -2315,7 +2316,7 @@@ C_SOURCES = $(patsubst %.o,%.c,$(C_OBJ)
  %.cocci.patch: %.cocci $(C_SOURCES)
        @echo '    ' SPATCH $<; \
        for f in $(C_SOURCES); do \
 -              $(SPATCH) --sp-file $< $$f; \
 +              $(SPATCH) --sp-file $< $$f $(SPATCH_FLAGS); \
        done >$@ 2>$@.log; \
        if test -s $@; \
        then \
diff --combined builtin/receive-pack.c
index f7cd1802524e2ba41a41d6479e69534dc042b404,267d3204c209bb0816004dac3009589f08f66074..04ed38e17db67845ec5da34031f10a894d933bdc
@@@ -20,6 -20,7 +20,7 @@@
  #include "gpg-interface.h"
  #include "sigchain.h"
  #include "fsck.h"
+ #include "tmp-objdir.h"
  
  static const char * const receive_pack_usage[] = {
        N_("git receive-pack <git-dir>"),
@@@ -86,6 -87,8 +87,8 @@@ static enum 
  } use_keepalive;
  static int keepalive_in_sec = 5;
  
+ static struct tmp_objdir *tmp_objdir;
  static enum deny_action parse_deny_action(const char *var, const char *value)
  {
        if (value) {
@@@ -268,10 -271,9 +271,10 @@@ static int show_ref_cb(const char *path
        return 0;
  }
  
 -static void show_one_alternate_sha1(const unsigned char sha1[20], void *unused)
 +static int show_one_alternate_sha1(const unsigned char sha1[20], void *unused)
  {
        show_ref(".have", sha1);
 +      return 0;
  }
  
  static void collect_one_alternate_ref(const struct ref *ref, void *data)
@@@ -664,6 -666,9 +667,9 @@@ static int run_and_feed_hook(const cha
        } else
                argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT");
  
+       if (tmp_objdir)
+               argv_array_pushv(&proc.env_array, tmp_objdir_env(tmp_objdir));
        if (use_sideband) {
                memset(&muxer, 0, sizeof(muxer));
                muxer.proc = copy_to_sideband;
@@@ -763,6 -768,7 +769,7 @@@ static int run_update_hook(struct comma
        proc.stdout_to_stderr = 1;
        proc.err = use_sideband ? -1 : 0;
        proc.argv = argv;
+       proc.env = tmp_objdir_env(tmp_objdir);
  
        code = start_command(&proc);
        if (code)
@@@ -834,6 -840,7 +841,7 @@@ static int update_shallow_ref(struct co
                    !delayed_reachability_test(si, i))
                        sha1_array_append(&extra, si->shallow->sha1[i]);
  
+       opt.env = tmp_objdir_env(tmp_objdir);
        setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra);
        if (check_connected(command_singleton_iterator, cmd, &opt)) {
                rollback_lock_file(&shallow_lock);
@@@ -1241,12 -1248,17 +1249,17 @@@ static void set_connectivity_errors(str
  
        for (cmd = commands; cmd; cmd = cmd->next) {
                struct command *singleton = cmd;
+               struct check_connected_options opt = CHECK_CONNECTED_INIT;
                if (shallow_update && si->shallow_ref[cmd->index])
                        /* to be checked in update_shallow_ref() */
                        continue;
+               opt.env = tmp_objdir_env(tmp_objdir);
                if (!check_connected(command_singleton_iterator, &singleton,
-                                    NULL))
+                                    &opt))
                        continue;
                cmd->error_string = "missing necessary objects";
        }
  }
@@@ -1429,6 -1441,7 +1442,7 @@@ static void execute_commands(struct com
        data.si = si;
        opt.err_fd = err_fd;
        opt.progress = err_fd && !quiet;
+       opt.env = tmp_objdir_env(tmp_objdir);
        if (check_connected(iterate_receive_command_list, &data, &opt))
                set_connectivity_errors(commands, si);
  
                return;
        }
  
+       /*
+        * Now we'll start writing out refs, which means the objects need
+        * to be in their final positions so that other processes can see them.
+        */
+       if (tmp_objdir_migrate(tmp_objdir) < 0) {
+               for (cmd = commands; cmd; cmd = cmd->next) {
+                       if (!cmd->error_string)
+                               cmd->error_string = "unable to migrate objects to permanent storage";
+               }
+               return;
+       }
+       tmp_objdir = NULL;
        check_aliased_updates(commands);
  
        free(head_name_to_free);
@@@ -1640,6 -1666,18 +1667,18 @@@ static const char *unpack(int err_fd, s
                argv_array_push(&child.args, alt_shallow_file);
        }
  
+       tmp_objdir = tmp_objdir_create();
+       if (!tmp_objdir)
+               return "unable to create temporary object directory";
+       child.env = tmp_objdir_env(tmp_objdir);
+       /*
+        * Normally we just pass the tmp_objdir environment to the child
+        * processes that do the heavy lifting, but we may need to see these
+        * objects ourselves to set up shallow information.
+        */
+       tmp_objdir_add_as_alternate(tmp_objdir);
        if (ntohl(hdr.hdr_entries) < unpack_limit) {
                argv_array_pushl(&child.args, "unpack-objects", hdr_arg, NULL);
                if (quiet)
diff --combined cache.h
index 0dc39a998cb246b8eb22bc6def8a0900a14e2ee2,c898a1dd7d5f5dd20ee8830bb7e91b620fb18423..05ecb889ebd7c33386a9c3b4c32b32a4ce1f32da
+++ b/cache.h
@@@ -433,6 -433,7 +433,7 @@@ static inline enum object_type object_t
  #define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS"
  #define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
  #define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
+ #define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
  
  /*
   * This environment variable is expected to contain a boolean indicating
@@@ -1206,11 -1207,6 +1207,11 @@@ struct object_context 
  #define GET_SHA1_FOLLOW_SYMLINKS 0100
  #define GET_SHA1_ONLY_TO_DIE    04000
  
 +#define GET_SHA1_DISAMBIGUATORS \
 +      (GET_SHA1_COMMIT | GET_SHA1_COMMITTISH | \
 +      GET_SHA1_TREE | GET_SHA1_TREEISH | \
 +      GET_SHA1_BLOB)
 +
  extern int get_sha1(const char *str, unsigned char *sha1);
  extern int get_sha1_commit(const char *str, unsigned char *sha1);
  extern int get_sha1_committish(const char *str, unsigned char *sha1);
@@@ -1225,8 -1221,6 +1226,8 @@@ extern int get_oid(const char *str, str
  typedef int each_abbrev_fn(const unsigned char *sha1, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
  
 +extern int set_disambiguate_hint_config(const char *var, const char *value);
 +
  /*
   * Try to read a SHA1 in hexadecimal format from the 40 characters
   * starting at hex.  Write the 20-byte result to sha1 in binary form.
@@@ -1632,15 -1626,7 +1633,15 @@@ struct object_info 
                } packed;
        } u;
  };
 +
 +/*
 + * Initializer for a "struct object_info" that wants no items. You may
 + * also memset() the memory to all-zeroes.
 + */
 +#define OBJECT_INFO_INIT {NULL}
 +
  extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 +extern int packed_object_info(struct packed_git *pack, off_t offset, struct object_info *);
  
  /* Dumb servers support */
  extern int update_server_info(int);