Coverage Report

Created: 2024-09-08 06:24

/src/git/tmp-objdir.c
Line
Count
Source (jump to first uncovered line)
1
#define USE_THE_REPOSITORY_VARIABLE
2
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
17
struct tmp_objdir {
18
  struct strbuf path;
19
  struct strvec env;
20
  struct object_directory *prev_odb;
21
  int 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
 */
30
static struct tmp_objdir *the_tmp_objdir;
31
32
static void tmp_objdir_free(struct tmp_objdir *t)
33
0
{
34
0
  strbuf_release(&t->path);
35
0
  strvec_clear(&t->env);
36
0
  free(t);
37
0
}
38
39
int tmp_objdir_destroy(struct tmp_objdir *t)
40
0
{
41
0
  int err;
42
43
0
  if (!t)
44
0
    return 0;
45
46
0
  if (t == the_tmp_objdir)
47
0
    the_tmp_objdir = NULL;
48
49
0
  if (t->prev_odb)
50
0
    restore_primary_odb(t->prev_odb, t->path.buf);
51
52
0
  err = remove_dir_recursively(&t->path, 0);
53
54
0
  tmp_objdir_free(t);
55
56
0
  return err;
57
0
}
58
59
static void remove_tmp_objdir(void)
60
0
{
61
0
  tmp_objdir_destroy(the_tmp_objdir);
62
0
}
63
64
void tmp_objdir_discard_objects(struct tmp_objdir *t)
65
0
{
66
0
  remove_dir_recursively(&t->path, REMOVE_DIR_KEEP_TOPLEVEL);
67
0
}
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
 */
76
static void env_append(struct strvec *env, const char *key, const char *val)
77
0
{
78
0
  struct strbuf quoted = STRBUF_INIT;
79
0
  const 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
   */
85
0
  if (*val == '"' || strchr(val, PATH_SEP)) {
86
0
    strbuf_addch(&quoted, '"');
87
0
    quote_c_style(val, &quoted, NULL, 1);
88
0
    strbuf_addch(&quoted, '"');
89
0
    val = quoted.buf;
90
0
  }
91
92
0
  old = getenv(key);
93
0
  if (!old)
94
0
    strvec_pushf(env, "%s=%s", key, val);
95
0
  else
96
0
    strvec_pushf(env, "%s=%s%c%s", key, old, PATH_SEP, val);
97
98
0
  strbuf_release(&quoted);
99
0
}
100
101
static void env_replace(struct strvec *env, const char *key, const char *val)
102
0
{
103
0
  strvec_pushf(env, "%s=%s", key, val);
104
0
}
105
106
static int setup_tmp_objdir(const char *root)
107
0
{
108
0
  char *path;
109
0
  int ret = 0;
110
111
0
  path = xstrfmt("%s/pack", root);
112
0
  ret = mkdir(path, 0777);
113
0
  free(path);
114
115
0
  return ret;
116
0
}
117
118
struct tmp_objdir *tmp_objdir_create(const char *prefix)
119
0
{
120
0
  static int installed_handlers;
121
0
  struct tmp_objdir *t;
122
123
0
  if (the_tmp_objdir)
124
0
    BUG("only one tmp_objdir can be used at a time");
125
126
0
  t = xcalloc(1, sizeof(*t));
127
0
  strbuf_init(&t->path, 0);
128
0
  strvec_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
   */
135
0
  strbuf_addf(&t->path, "%s/tmp_objdir-%s-XXXXXX", get_object_directory(), prefix);
136
137
0
  if (!mkdtemp(t->path.buf)) {
138
    /* free, not destroy, as we never touched the filesystem */
139
0
    tmp_objdir_free(t);
140
0
    return NULL;
141
0
  }
142
143
0
  the_tmp_objdir = t;
144
0
  if (!installed_handlers) {
145
0
    atexit(remove_tmp_objdir);
146
0
    installed_handlers++;
147
0
  }
148
149
0
  if (setup_tmp_objdir(t->path.buf)) {
150
0
    tmp_objdir_destroy(t);
151
0
    return NULL;
152
0
  }
153
154
0
  env_append(&t->env, ALTERNATE_DB_ENVIRONMENT,
155
0
       absolute_path(get_object_directory()));
156
0
  env_replace(&t->env, DB_ENVIRONMENT, absolute_path(t->path.buf));
157
0
  env_replace(&t->env, GIT_QUARANTINE_ENVIRONMENT,
158
0
        absolute_path(t->path.buf));
159
160
0
  return t;
161
0
}
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
 */
170
static int pack_copy_priority(const char *name)
171
0
{
172
0
  if (!starts_with(name, "pack"))
173
0
    return 0;
174
0
  if (ends_with(name, ".keep"))
175
0
    return 1;
176
0
  if (ends_with(name, ".pack"))
177
0
    return 2;
178
0
  if (ends_with(name, ".rev"))
179
0
    return 3;
180
0
  if (ends_with(name, ".idx"))
181
0
    return 4;
182
0
  return 5;
183
0
}
184
185
static int pack_copy_cmp(const char *a, const char *b)
186
0
{
187
0
  return pack_copy_priority(a) - pack_copy_priority(b);
188
0
}
189
190
static int read_dir_paths(struct string_list *out, const char *path)
191
0
{
192
0
  DIR *dh;
193
0
  struct dirent *de;
194
195
0
  dh = opendir(path);
196
0
  if (!dh)
197
0
    return -1;
198
199
0
  while ((de = readdir(dh)))
200
0
    if (de->d_name[0] != '.')
201
0
      string_list_append(out, de->d_name);
202
203
0
  closedir(dh);
204
0
  return 0;
205
0
}
206
207
static int migrate_paths(struct strbuf *src, struct strbuf *dst);
208
209
static int migrate_one(struct strbuf *src, struct strbuf *dst)
210
0
{
211
0
  struct stat st;
212
213
0
  if (stat(src->buf, &st) < 0)
214
0
    return -1;
215
0
  if (S_ISDIR(st.st_mode)) {
216
0
    if (!mkdir(dst->buf, 0777)) {
217
0
      if (adjust_shared_perm(dst->buf))
218
0
        return -1;
219
0
    } else if (errno != EEXIST)
220
0
      return -1;
221
0
    return migrate_paths(src, dst);
222
0
  }
223
0
  return finalize_object_file(src->buf, dst->buf);
224
0
}
225
226
static int migrate_paths(struct strbuf *src, struct strbuf *dst)
227
0
{
228
0
  size_t src_len = src->len, dst_len = dst->len;
229
0
  struct string_list paths = STRING_LIST_INIT_DUP;
230
0
  int i;
231
0
  int ret = 0;
232
233
0
  if (read_dir_paths(&paths, src->buf) < 0)
234
0
    return -1;
235
0
  paths.cmp = pack_copy_cmp;
236
0
  string_list_sort(&paths);
237
238
0
  for (i = 0; i < paths.nr; i++) {
239
0
    const char *name = paths.items[i].string;
240
241
0
    strbuf_addf(src, "/%s", name);
242
0
    strbuf_addf(dst, "/%s", name);
243
244
0
    ret |= migrate_one(src, dst);
245
246
0
    strbuf_setlen(src, src_len);
247
0
    strbuf_setlen(dst, dst_len);
248
0
  }
249
250
0
  string_list_clear(&paths, 0);
251
0
  return ret;
252
0
}
253
254
int tmp_objdir_migrate(struct tmp_objdir *t)
255
0
{
256
0
  struct strbuf src = STRBUF_INIT, dst = STRBUF_INIT;
257
0
  int ret;
258
259
0
  if (!t)
260
0
    return 0;
261
262
0
  if (t->prev_odb) {
263
0
    if (the_repository->objects->odb->will_destroy)
264
0
      BUG("migrating an ODB that was marked for destruction");
265
0
    restore_primary_odb(t->prev_odb, t->path.buf);
266
0
    t->prev_odb = NULL;
267
0
  }
268
269
0
  strbuf_addbuf(&src, &t->path);
270
0
  strbuf_addstr(&dst, get_object_directory());
271
272
0
  ret = migrate_paths(&src, &dst);
273
274
0
  strbuf_release(&src);
275
0
  strbuf_release(&dst);
276
277
0
  tmp_objdir_destroy(t);
278
0
  return ret;
279
0
}
280
281
const char **tmp_objdir_env(const struct tmp_objdir *t)
282
0
{
283
0
  if (!t)
284
0
    return NULL;
285
0
  return t->env.v;
286
0
}
287
288
void tmp_objdir_add_as_alternate(const struct tmp_objdir *t)
289
0
{
290
0
  add_to_alternates_memory(t->path.buf);
291
0
}
292
293
void tmp_objdir_replace_primary_odb(struct tmp_objdir *t, int will_destroy)
294
0
{
295
0
  if (t->prev_odb)
296
0
    BUG("the primary object database is already replaced");
297
0
  t->prev_odb = set_temporary_primary_odb(t->path.buf, will_destroy);
298
0
  t->will_destroy = will_destroy;
299
0
}
300
301
struct tmp_objdir *tmp_objdir_unapply_primary_odb(void)
302
0
{
303
0
  if (!the_tmp_objdir || !the_tmp_objdir->prev_odb)
304
0
    return NULL;
305
306
0
  restore_primary_odb(the_tmp_objdir->prev_odb, the_tmp_objdir->path.buf);
307
0
  the_tmp_objdir->prev_odb = NULL;
308
0
  return the_tmp_objdir;
309
0
}
310
311
void tmp_objdir_reapply_primary_odb(struct tmp_objdir *t, const char *old_cwd,
312
    const char *new_cwd)
313
0
{
314
0
  char *path;
315
316
0
  path = reparent_relative_path(old_cwd, new_cwd, t->path.buf);
317
0
  strbuf_reset(&t->path);
318
0
  strbuf_addstr(&t->path, path);
319
0
  free(path);
320
0
  tmp_objdir_replace_primary_odb(t, t->will_destroy);
321
0
}