git
/
tmp-objdir.c
321 строка · 7.1 Кб
1#define USE_THE_REPOSITORY_VARIABLE2
3#include "git-compat-util.h"4#include "tmp-objdir.h"5#include "abspath.h"6#include "chdir-notify.h"7#include "dir.h"8#include "environment.h"9#include "object-file.h"10#include "path.h"11#include "string-list.h"12#include "strbuf.h"13#include "strvec.h"14#include "quote.h"15#include "object-store-ll.h"16
17struct tmp_objdir {18struct strbuf path;19struct strvec env;20struct object_directory *prev_odb;21int will_destroy;22};23
24/*
25* Allow only one tmp_objdir at a time in a running process, which simplifies
26* our atexit cleanup routines. It's doubtful callers will ever need
27* more than one, and we can expand later if so. You can have many such
28* tmp_objdirs simultaneously in many processes, of course.
29*/
30static struct tmp_objdir *the_tmp_objdir;31
32static void tmp_objdir_free(struct tmp_objdir *t)33{
34strbuf_release(&t->path);35strvec_clear(&t->env);36free(t);37}
38
39int tmp_objdir_destroy(struct tmp_objdir *t)40{
41int err;42
43if (!t)44return 0;45
46if (t == the_tmp_objdir)47the_tmp_objdir = NULL;48
49if (t->prev_odb)50restore_primary_odb(t->prev_odb, t->path.buf);51
52err = remove_dir_recursively(&t->path, 0);53
54tmp_objdir_free(t);55
56return err;57}
58
59static void remove_tmp_objdir(void)60{
61tmp_objdir_destroy(the_tmp_objdir);62}
63
64void tmp_objdir_discard_objects(struct tmp_objdir *t)65{
66remove_dir_recursively(&t->path, REMOVE_DIR_KEEP_TOPLEVEL);67}
68
69/*
70* These env_* functions are for setting up the child environment; the
71* "replace" variant overrides the value of any existing variable with that
72* "key". The "append" variant puts our new value at the end of a list,
73* separated by PATH_SEP (which is what separate values in
74* GIT_ALTERNATE_OBJECT_DIRECTORIES).
75*/
76static void env_append(struct strvec *env, const char *key, const char *val)77{
78struct strbuf quoted = STRBUF_INIT;79const char *old;80
81/*82* Avoid quoting if it's not necessary, for maximum compatibility
83* with older parsers which don't understand the quoting.
84*/
85if (*val == '"' || strchr(val, PATH_SEP)) {86strbuf_addch("ed, '"');87quote_c_style(val, "ed, NULL, 1);88strbuf_addch("ed, '"');89val = quoted.buf;90}91
92old = getenv(key);93if (!old)94strvec_pushf(env, "%s=%s", key, val);95else96strvec_pushf(env, "%s=%s%c%s", key, old, PATH_SEP, val);97
98strbuf_release("ed);99}
100
101static void env_replace(struct strvec *env, const char *key, const char *val)102{
103strvec_pushf(env, "%s=%s", key, val);104}
105
106static int setup_tmp_objdir(const char *root)107{
108char *path;109int ret = 0;110
111path = xstrfmt("%s/pack", root);112ret = mkdir(path, 0777);113free(path);114
115return ret;116}
117
118struct tmp_objdir *tmp_objdir_create(const char *prefix)119{
120static int installed_handlers;121struct tmp_objdir *t;122
123if (the_tmp_objdir)124BUG("only one tmp_objdir can be used at a time");125
126t = xcalloc(1, sizeof(*t));127strbuf_init(&t->path, 0);128strvec_init(&t->env);129
130/*131* Use a string starting with tmp_ so that the builtin/prune.c code
132* can recognize any stale objdirs left behind by a crash and delete
133* them.
134*/
135strbuf_addf(&t->path, "%s/tmp_objdir-%s-XXXXXX", get_object_directory(), prefix);136
137if (!mkdtemp(t->path.buf)) {138/* free, not destroy, as we never touched the filesystem */139tmp_objdir_free(t);140return NULL;141}142
143the_tmp_objdir = t;144if (!installed_handlers) {145atexit(remove_tmp_objdir);146installed_handlers++;147}148
149if (setup_tmp_objdir(t->path.buf)) {150tmp_objdir_destroy(t);151return NULL;152}153
154env_append(&t->env, ALTERNATE_DB_ENVIRONMENT,155absolute_path(get_object_directory()));156env_replace(&t->env, DB_ENVIRONMENT, absolute_path(t->path.buf));157env_replace(&t->env, GIT_QUARANTINE_ENVIRONMENT,158absolute_path(t->path.buf));159
160return t;161}
162
163/*
164* Make sure we copy packfiles and their associated metafiles in the correct
165* order. All of these ends_with checks are slightly expensive to do in
166* the midst of a sorting routine, but in practice it shouldn't matter.
167* We will have a relatively small number of packfiles to order, and loose
168* objects exit early in the first line.
169*/
170static int pack_copy_priority(const char *name)171{
172if (!starts_with(name, "pack"))173return 0;174if (ends_with(name, ".keep"))175return 1;176if (ends_with(name, ".pack"))177return 2;178if (ends_with(name, ".rev"))179return 3;180if (ends_with(name, ".idx"))181return 4;182return 5;183}
184
185static int pack_copy_cmp(const char *a, const char *b)186{
187return pack_copy_priority(a) - pack_copy_priority(b);188}
189
190static int read_dir_paths(struct string_list *out, const char *path)191{
192DIR *dh;193struct dirent *de;194
195dh = opendir(path);196if (!dh)197return -1;198
199while ((de = readdir(dh)))200if (de->d_name[0] != '.')201string_list_append(out, de->d_name);202
203closedir(dh);204return 0;205}
206
207static int migrate_paths(struct strbuf *src, struct strbuf *dst);208
209static int migrate_one(struct strbuf *src, struct strbuf *dst)210{
211struct stat st;212
213if (stat(src->buf, &st) < 0)214return -1;215if (S_ISDIR(st.st_mode)) {216if (!mkdir(dst->buf, 0777)) {217if (adjust_shared_perm(dst->buf))218return -1;219} else if (errno != EEXIST)220return -1;221return migrate_paths(src, dst);222}223return finalize_object_file(src->buf, dst->buf);224}
225
226static int migrate_paths(struct strbuf *src, struct strbuf *dst)227{
228size_t src_len = src->len, dst_len = dst->len;229struct string_list paths = STRING_LIST_INIT_DUP;230int i;231int ret = 0;232
233if (read_dir_paths(&paths, src->buf) < 0)234return -1;235paths.cmp = pack_copy_cmp;236string_list_sort(&paths);237
238for (i = 0; i < paths.nr; i++) {239const char *name = paths.items[i].string;240
241strbuf_addf(src, "/%s", name);242strbuf_addf(dst, "/%s", name);243
244ret |= migrate_one(src, dst);245
246strbuf_setlen(src, src_len);247strbuf_setlen(dst, dst_len);248}249
250string_list_clear(&paths, 0);251return ret;252}
253
254int tmp_objdir_migrate(struct tmp_objdir *t)255{
256struct strbuf src = STRBUF_INIT, dst = STRBUF_INIT;257int ret;258
259if (!t)260return 0;261
262if (t->prev_odb) {263if (the_repository->objects->odb->will_destroy)264BUG("migrating an ODB that was marked for destruction");265restore_primary_odb(t->prev_odb, t->path.buf);266t->prev_odb = NULL;267}268
269strbuf_addbuf(&src, &t->path);270strbuf_addstr(&dst, get_object_directory());271
272ret = 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.v;286}
287
288void tmp_objdir_add_as_alternate(const struct tmp_objdir *t)289{
290add_to_alternates_memory(t->path.buf);291}
292
293void tmp_objdir_replace_primary_odb(struct tmp_objdir *t, int will_destroy)294{
295if (t->prev_odb)296BUG("the primary object database is already replaced");297t->prev_odb = set_temporary_primary_odb(t->path.buf, will_destroy);298t->will_destroy = will_destroy;299}
300
301struct tmp_objdir *tmp_objdir_unapply_primary_odb(void)302{
303if (!the_tmp_objdir || !the_tmp_objdir->prev_odb)304return NULL;305
306restore_primary_odb(the_tmp_objdir->prev_odb, the_tmp_objdir->path.buf);307the_tmp_objdir->prev_odb = NULL;308return the_tmp_objdir;309}
310
311void tmp_objdir_reapply_primary_odb(struct tmp_objdir *t, const char *old_cwd,312const char *new_cwd)313{
314char *path;315
316path = reparent_relative_path(old_cwd, new_cwd, t->path.buf);317strbuf_reset(&t->path);318strbuf_addstr(&t->path, path);319free(path);320tmp_objdir_replace_primary_odb(t, t->will_destroy);321}
322