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