1#include"cache.h" 2#include"tmp-objdir.h" 3#include"dir.h" 4#include"sigchain.h" 5#include"string-list.h" 6#include"strbuf.h" 7#include"argv-array.h" 8#include"quote.h" 9 10struct tmp_objdir { 11struct strbuf path; 12struct argv_array env; 13}; 14 15/* 16 * Allow only one tmp_objdir at a time in a running process, which simplifies 17 * our signal/atexit cleanup routines. It's doubtful callers will ever need 18 * more than one, and we can expand later if so. You can have many such 19 * tmp_objdirs simultaneously in many processes, of course. 20 */ 21static struct tmp_objdir *the_tmp_objdir; 22 23static voidtmp_objdir_free(struct tmp_objdir *t) 24{ 25strbuf_release(&t->path); 26argv_array_clear(&t->env); 27free(t); 28} 29 30static inttmp_objdir_destroy_1(struct tmp_objdir *t,int on_signal) 31{ 32int err; 33 34if(!t) 35return0; 36 37if(t == the_tmp_objdir) 38 the_tmp_objdir = NULL; 39 40/* 41 * This may use malloc via strbuf_grow(), but we should 42 * have pre-grown t->path sufficiently so that this 43 * doesn't happen in practice. 44 */ 45 err =remove_dir_recursively(&t->path,0); 46 47/* 48 * When we are cleaning up due to a signal, we won't bother 49 * freeing memory; it may cause a deadlock if the signal 50 * arrived while libc's allocator lock is held. 51 */ 52if(!on_signal) 53tmp_objdir_free(t); 54return err; 55} 56 57inttmp_objdir_destroy(struct tmp_objdir *t) 58{ 59returntmp_objdir_destroy_1(t,0); 60} 61 62static voidremove_tmp_objdir(void) 63{ 64tmp_objdir_destroy(the_tmp_objdir); 65} 66 67static voidremove_tmp_objdir_on_signal(int signo) 68{ 69tmp_objdir_destroy_1(the_tmp_objdir,1); 70sigchain_pop(signo); 71raise(signo); 72} 73 74/* 75 * These env_* functions are for setting up the child environment; the 76 * "replace" variant overrides the value of any existing variable with that 77 * "key". The "append" variant puts our new value at the end of a list, 78 * separated by PATH_SEP (which is what separate values in 79 * GIT_ALTERNATE_OBJECT_DIRECTORIES). 80 */ 81static voidenv_append(struct argv_array *env,const char*key,const char*val) 82{ 83struct strbuf quoted = STRBUF_INIT; 84const char*old; 85 86/* 87 * Avoid quoting if it's not necessary, for maximum compatibility 88 * with older parsers which don't understand the quoting. 89 */ 90if(*val =='"'||strchr(val, PATH_SEP)) { 91strbuf_addch("ed,'"'); 92quote_c_style(val, "ed, NULL,1); 93strbuf_addch("ed,'"'); 94 val = quoted.buf; 95} 96 97 old =getenv(key); 98if(!old) 99argv_array_pushf(env,"%s=%s", key, val); 100else 101argv_array_pushf(env,"%s=%s%c%s", key, old, PATH_SEP, val); 102 103strbuf_release("ed); 104} 105 106static voidenv_replace(struct argv_array *env,const char*key,const char*val) 107{ 108argv_array_pushf(env,"%s=%s", key, val); 109} 110 111static intsetup_tmp_objdir(const char*root) 112{ 113char*path; 114int ret =0; 115 116 path =xstrfmt("%s/pack", root); 117 ret =mkdir(path,0777); 118free(path); 119 120return ret; 121} 122 123struct tmp_objdir *tmp_objdir_create(void) 124{ 125static int installed_handlers; 126struct tmp_objdir *t; 127 128if(the_tmp_objdir) 129die("BUG: only one tmp_objdir can be used at a time"); 130 131 t =xmalloc(sizeof(*t)); 132strbuf_init(&t->path,0); 133argv_array_init(&t->env); 134 135strbuf_addf(&t->path,"%s/incoming-XXXXXX",get_object_directory()); 136 137/* 138 * Grow the strbuf beyond any filename we expect to be placed in it. 139 * If tmp_objdir_destroy() is called by a signal handler, then 140 * we should be able to use the strbuf to remove files without 141 * having to call malloc. 142 */ 143strbuf_grow(&t->path,1024); 144 145if(!mkdtemp(t->path.buf)) { 146/* free, not destroy, as we never touched the filesystem */ 147tmp_objdir_free(t); 148return NULL; 149} 150 151 the_tmp_objdir = t; 152if(!installed_handlers) { 153atexit(remove_tmp_objdir); 154sigchain_push_common(remove_tmp_objdir_on_signal); 155 installed_handlers++; 156} 157 158if(setup_tmp_objdir(t->path.buf)) { 159tmp_objdir_destroy(t); 160return NULL; 161} 162 163env_append(&t->env, ALTERNATE_DB_ENVIRONMENT, 164absolute_path(get_object_directory())); 165env_replace(&t->env, DB_ENVIRONMENT,absolute_path(t->path.buf)); 166env_replace(&t->env, GIT_QUARANTINE_ENVIRONMENT, 167absolute_path(t->path.buf)); 168 169return t; 170} 171 172/* 173 * Make sure we copy packfiles and their associated metafiles in the correct 174 * order. All of these ends_with checks are slightly expensive to do in 175 * the midst of a sorting routine, but in practice it shouldn't matter. 176 * We will have a relatively small number of packfiles to order, and loose 177 * objects exit early in the first line. 178 */ 179static intpack_copy_priority(const char*name) 180{ 181if(!starts_with(name,"pack")) 182return0; 183if(ends_with(name,".keep")) 184return1; 185if(ends_with(name,".pack")) 186return2; 187if(ends_with(name,".idx")) 188return3; 189return4; 190} 191 192static intpack_copy_cmp(const char*a,const char*b) 193{ 194returnpack_copy_priority(a) -pack_copy_priority(b); 195} 196 197static intread_dir_paths(struct string_list *out,const char*path) 198{ 199DIR*dh; 200struct dirent *de; 201 202 dh =opendir(path); 203if(!dh) 204return-1; 205 206while((de =readdir(dh))) 207if(de->d_name[0] !='.') 208string_list_append(out, de->d_name); 209 210closedir(dh); 211return0; 212} 213 214static intmigrate_paths(struct strbuf *src,struct strbuf *dst); 215 216static intmigrate_one(struct strbuf *src,struct strbuf *dst) 217{ 218struct stat st; 219 220if(stat(src->buf, &st) <0) 221return-1; 222if(S_ISDIR(st.st_mode)) { 223if(!mkdir(dst->buf,0777)) { 224if(adjust_shared_perm(dst->buf)) 225return-1; 226}else if(errno != EEXIST) 227return-1; 228returnmigrate_paths(src, dst); 229} 230returnfinalize_object_file(src->buf, dst->buf); 231} 232 233static intmigrate_paths(struct strbuf *src,struct strbuf *dst) 234{ 235size_t src_len = src->len, dst_len = dst->len; 236struct string_list paths = STRING_LIST_INIT_DUP; 237int i; 238int ret =0; 239 240if(read_dir_paths(&paths, src->buf) <0) 241return-1; 242 paths.cmp = pack_copy_cmp; 243string_list_sort(&paths); 244 245for(i =0; i < paths.nr; i++) { 246const char*name = paths.items[i].string; 247 248strbuf_addf(src,"/%s", name); 249strbuf_addf(dst,"/%s", name); 250 251 ret |=migrate_one(src, dst); 252 253strbuf_setlen(src, src_len); 254strbuf_setlen(dst, dst_len); 255} 256 257string_list_clear(&paths,0); 258return ret; 259} 260 261inttmp_objdir_migrate(struct tmp_objdir *t) 262{ 263struct strbuf src = STRBUF_INIT, dst = STRBUF_INIT; 264int ret; 265 266if(!t) 267return0; 268 269strbuf_addbuf(&src, &t->path); 270strbuf_addstr(&dst,get_object_directory()); 271 272 ret =migrate_paths(&src, &dst); 273 274strbuf_release(&src); 275strbuf_release(&dst); 276 277tmp_objdir_destroy(t); 278return ret; 279} 280 281const char**tmp_objdir_env(const struct tmp_objdir *t) 282{ 283if(!t) 284return NULL; 285return t->env.argv; 286} 287 288voidtmp_objdir_add_as_alternate(const struct tmp_objdir *t) 289{ 290add_to_alternates_memory(t->path.buf); 291}