1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8 #include "refs.h"
9 #include "hash.h"
10 #include "repository.h"
11 #include "futils.h"
12 #include "filebuf.h"
13 #include "pack.h"
14 #include "parse.h"
15 #include "reflog.h"
16 #include "refdb.h"
17 #include "iterator.h"
18 #include "sortedcache.h"
19 #include "signature.h"
20 #include "wildmatch.h"
21
22 #include <git2/tag.h>
23 #include <git2/object.h>
24 #include <git2/refdb.h>
25 #include <git2/branch.h>
26 #include <git2/sys/refdb_backend.h>
27 #include <git2/sys/refs.h>
28 #include <git2/sys/reflog.h>
29
30 #define DEFAULT_NESTING_LEVEL 5
31 #define MAX_NESTING_LEVEL 10
32
33 enum {
34 PACKREF_HAS_PEEL = 1,
35 PACKREF_WAS_LOOSE = 2,
36 PACKREF_CANNOT_PEEL = 4,
37 PACKREF_SHADOWED = 8,
38 };
39
40 enum {
41 PEELING_NONE = 0,
42 PEELING_STANDARD,
43 PEELING_FULL
44 };
45
46 struct packref {
47 git_oid oid;
48 git_oid peel;
49 char flags;
50 char name[GIT_FLEX_ARRAY];
51 };
52
53 typedef struct refdb_fs_backend {
54 git_refdb_backend parent;
55
56 git_repository *repo;
57 /* path to git directory */
58 char *gitpath;
59 /* path to common objects' directory */
60 char *commonpath;
61
62 git_sortedcache *refcache;
63 int peeling_mode;
64 git_iterator_flag_t iterator_flags;
65 uint32_t direach_flags;
66 int fsync;
67 } refdb_fs_backend;
68
69 static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name);
70
loose_path(git_buf * out,const char * base,const char * refname)71 GIT_INLINE(int) loose_path(
72 git_buf *out,
73 const char *base,
74 const char *refname)
75 {
76 if (git_buf_joinpath(out, base, refname) < 0)
77 return -1;
78
79 return git_path_validate_filesystem_with_suffix(out->ptr, out->size,
80 CONST_STRLEN(".lock"));
81 }
82
reflog_path(git_buf * out,git_repository * repo,const char * refname)83 GIT_INLINE(int) reflog_path(
84 git_buf *out,
85 git_repository *repo,
86 const char *refname)
87 {
88 const char *base;
89 int error;
90
91 base = (strcmp(refname, GIT_HEAD_FILE) == 0) ? repo->gitdir :
92 repo->commondir;
93
94 if ((error = git_buf_joinpath(out, base, GIT_REFLOG_DIR)) < 0)
95 return error;
96
97 return loose_path(out, out->ptr, refname);
98 }
99
packref_cmp(const void * a_,const void * b_)100 static int packref_cmp(const void *a_, const void *b_)
101 {
102 const struct packref *a = a_, *b = b_;
103 return strcmp(a->name, b->name);
104 }
105
packed_reload(refdb_fs_backend * backend)106 static int packed_reload(refdb_fs_backend *backend)
107 {
108 int error;
109 git_buf packedrefs = GIT_BUF_INIT;
110 char *scan, *eof, *eol;
111
112 if (!backend->gitpath)
113 return 0;
114
115 error = git_sortedcache_lockandload(backend->refcache, &packedrefs);
116
117 /*
118 * If we can't find the packed-refs, clear table and return.
119 * Any other error just gets passed through.
120 * If no error, and file wasn't changed, just return.
121 * Anything else means we need to refresh the packed refs.
122 */
123 if (error <= 0) {
124 if (error == GIT_ENOTFOUND) {
125 git_sortedcache_clear(backend->refcache, true);
126 git_error_clear();
127 error = 0;
128 }
129 return error;
130 }
131
132 /* At this point, refresh the packed refs from the loaded buffer. */
133
134 git_sortedcache_clear(backend->refcache, false);
135
136 scan = (char *)packedrefs.ptr;
137 eof = scan + packedrefs.size;
138
139 backend->peeling_mode = PEELING_NONE;
140
141 if (*scan == '#') {
142 static const char *traits_header = "# pack-refs with: ";
143
144 if (git__prefixcmp(scan, traits_header) == 0) {
145 scan += strlen(traits_header);
146 eol = strchr(scan, '\n');
147
148 if (!eol)
149 goto parse_failed;
150 *eol = '\0';
151
152 if (strstr(scan, " fully-peeled ") != NULL) {
153 backend->peeling_mode = PEELING_FULL;
154 } else if (strstr(scan, " peeled ") != NULL) {
155 backend->peeling_mode = PEELING_STANDARD;
156 }
157
158 scan = eol + 1;
159 }
160 }
161
162 while (scan < eof && *scan == '#') {
163 if (!(eol = strchr(scan, '\n')))
164 goto parse_failed;
165 scan = eol + 1;
166 }
167
168 while (scan < eof) {
169 struct packref *ref;
170 git_oid oid;
171
172 /* parse "<OID> <refname>\n" */
173
174 if (git_oid_fromstr(&oid, scan) < 0)
175 goto parse_failed;
176 scan += GIT_OID_HEXSZ;
177
178 if (*scan++ != ' ')
179 goto parse_failed;
180 if (!(eol = strchr(scan, '\n')))
181 goto parse_failed;
182 *eol = '\0';
183 if (eol[-1] == '\r')
184 eol[-1] = '\0';
185
186 if (git_sortedcache_upsert((void **)&ref, backend->refcache, scan) < 0)
187 goto parse_failed;
188 scan = eol + 1;
189
190 git_oid_cpy(&ref->oid, &oid);
191
192 /* look for optional "^<OID>\n" */
193
194 if (*scan == '^') {
195 if (git_oid_fromstr(&oid, scan + 1) < 0)
196 goto parse_failed;
197 scan += GIT_OID_HEXSZ + 1;
198
199 if (scan < eof) {
200 if (!(eol = strchr(scan, '\n')))
201 goto parse_failed;
202 scan = eol + 1;
203 }
204
205 git_oid_cpy(&ref->peel, &oid);
206 ref->flags |= PACKREF_HAS_PEEL;
207 }
208 else if (backend->peeling_mode == PEELING_FULL ||
209 (backend->peeling_mode == PEELING_STANDARD &&
210 git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0))
211 ref->flags |= PACKREF_CANNOT_PEEL;
212 }
213
214 git_sortedcache_wunlock(backend->refcache);
215 git_buf_dispose(&packedrefs);
216
217 return 0;
218
219 parse_failed:
220 git_error_set(GIT_ERROR_REFERENCE, "corrupted packed references file");
221
222 git_sortedcache_clear(backend->refcache, false);
223 git_sortedcache_wunlock(backend->refcache);
224 git_buf_dispose(&packedrefs);
225
226 return -1;
227 }
228
loose_parse_oid(git_oid * oid,const char * filename,git_buf * file_content)229 static int loose_parse_oid(
230 git_oid *oid, const char *filename, git_buf *file_content)
231 {
232 const char *str = git_buf_cstr(file_content);
233
234 if (git_buf_len(file_content) < GIT_OID_HEXSZ)
235 goto corrupted;
236
237 /* we need to get 40 OID characters from the file */
238 if (git_oid_fromstr(oid, str) < 0)
239 goto corrupted;
240
241 /* If the file is longer than 40 chars, the 41st must be a space */
242 str += GIT_OID_HEXSZ;
243 if (*str == '\0' || git__isspace(*str))
244 return 0;
245
246 corrupted:
247 git_error_set(GIT_ERROR_REFERENCE, "corrupted loose reference file: %s", filename);
248 return -1;
249 }
250
loose_readbuffer(git_buf * buf,const char * base,const char * path)251 static int loose_readbuffer(git_buf *buf, const char *base, const char *path)
252 {
253 int error;
254
255 if ((error = loose_path(buf, base, path)) < 0 ||
256 (error = git_futils_readbuffer(buf, buf->ptr)) < 0)
257 git_buf_dispose(buf);
258
259 return error;
260 }
261
loose_lookup_to_packfile(refdb_fs_backend * backend,const char * name)262 static int loose_lookup_to_packfile(refdb_fs_backend *backend, const char *name)
263 {
264 int error = 0;
265 git_buf ref_file = GIT_BUF_INIT;
266 struct packref *ref = NULL;
267 git_oid oid;
268
269 /* if we fail to load the loose reference, assume someone changed
270 * the filesystem under us and skip it...
271 */
272 if (loose_readbuffer(&ref_file, backend->gitpath, name) < 0) {
273 git_error_clear();
274 goto done;
275 }
276
277 /* skip symbolic refs */
278 if (!git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF))
279 goto done;
280
281 /* parse OID from file */
282 if ((error = loose_parse_oid(&oid, name, &ref_file)) < 0)
283 goto done;
284
285 if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
286 goto done;
287
288 if (!(error = git_sortedcache_upsert(
289 (void **)&ref, backend->refcache, name))) {
290
291 git_oid_cpy(&ref->oid, &oid);
292 ref->flags = PACKREF_WAS_LOOSE;
293 }
294
295 git_sortedcache_wunlock(backend->refcache);
296
297 done:
298 git_buf_dispose(&ref_file);
299 return error;
300 }
301
_dirent_loose_load(void * payload,git_buf * full_path)302 static int _dirent_loose_load(void *payload, git_buf *full_path)
303 {
304 refdb_fs_backend *backend = payload;
305 const char *file_path;
306
307 if (git__suffixcmp(full_path->ptr, ".lock") == 0)
308 return 0;
309
310 if (git_path_isdir(full_path->ptr)) {
311 int error = git_path_direach(
312 full_path, backend->direach_flags, _dirent_loose_load, backend);
313 /* Race with the filesystem, ignore it */
314 if (error == GIT_ENOTFOUND) {
315 git_error_clear();
316 return 0;
317 }
318
319 return error;
320 }
321
322 file_path = full_path->ptr + strlen(backend->gitpath);
323
324 return loose_lookup_to_packfile(backend, file_path);
325 }
326
327 /*
328 * Load all the loose references from the repository
329 * into the in-memory Packfile, and build a vector with
330 * all the references so it can be written back to
331 * disk.
332 */
packed_loadloose(refdb_fs_backend * backend)333 static int packed_loadloose(refdb_fs_backend *backend)
334 {
335 int error;
336 git_buf refs_path = GIT_BUF_INIT;
337
338 if (git_buf_joinpath(&refs_path, backend->gitpath, GIT_REFS_DIR) < 0)
339 return -1;
340
341 /*
342 * Load all the loose files from disk into the Packfile table.
343 * This will overwrite any old packed entries with their
344 * updated loose versions
345 */
346 error = git_path_direach(
347 &refs_path, backend->direach_flags, _dirent_loose_load, backend);
348
349 git_buf_dispose(&refs_path);
350
351 return error;
352 }
353
refdb_fs_backend__exists(int * exists,git_refdb_backend * _backend,const char * ref_name)354 static int refdb_fs_backend__exists(
355 int *exists,
356 git_refdb_backend *_backend,
357 const char *ref_name)
358 {
359 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
360 git_buf ref_path = GIT_BUF_INIT;
361 int error;
362
363 GIT_ASSERT_ARG(backend);
364
365 *exists = 0;
366
367 if ((error = loose_path(&ref_path, backend->gitpath, ref_name)) < 0)
368 goto out;
369
370 if (git_path_isfile(ref_path.ptr)) {
371 *exists = 1;
372 goto out;
373 }
374
375 if ((error = packed_reload(backend)) < 0)
376 goto out;
377
378 if (git_sortedcache_lookup(backend->refcache, ref_name) != NULL) {
379 *exists = 1;
380 goto out;
381 }
382
383 out:
384 git_buf_dispose(&ref_path);
385 return error;
386 }
387
loose_parse_symbolic(git_buf * file_content)388 static const char *loose_parse_symbolic(git_buf *file_content)
389 {
390 const unsigned int header_len = (unsigned int)strlen(GIT_SYMREF);
391 const char *refname_start;
392
393 refname_start = (const char *)file_content->ptr;
394
395 if (git_buf_len(file_content) < header_len + 1) {
396 git_error_set(GIT_ERROR_REFERENCE, "corrupted loose reference file");
397 return NULL;
398 }
399
400 /*
401 * Assume we have already checked for the header
402 * before calling this function
403 */
404 refname_start += header_len;
405
406 return refname_start;
407 }
408
409 /*
410 * Returns whether a reference is stored per worktree or not.
411 * Per-worktree references are:
412 *
413 * - all pseudorefs, e.g. HEAD and MERGE_HEAD
414 * - all references stored inside of "refs/bisect/"
415 */
is_per_worktree_ref(const char * ref_name)416 static bool is_per_worktree_ref(const char *ref_name)
417 {
418 return git__prefixcmp(ref_name, "refs/") != 0 ||
419 git__prefixcmp(ref_name, "refs/bisect/") == 0;
420 }
421
loose_lookup(git_reference ** out,refdb_fs_backend * backend,const char * ref_name)422 static int loose_lookup(
423 git_reference **out,
424 refdb_fs_backend *backend,
425 const char *ref_name)
426 {
427 git_buf ref_file = GIT_BUF_INIT;
428 int error = 0;
429 const char *ref_dir;
430
431 if (out)
432 *out = NULL;
433
434 if (is_per_worktree_ref(ref_name))
435 ref_dir = backend->gitpath;
436 else
437 ref_dir = backend->commonpath;
438
439 if ((error = loose_readbuffer(&ref_file, ref_dir, ref_name)) < 0)
440 /* cannot read loose ref file - gah */;
441 else if (git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF) == 0) {
442 const char *target;
443
444 git_buf_rtrim(&ref_file);
445
446 if (!(target = loose_parse_symbolic(&ref_file)))
447 error = -1;
448 else if (out != NULL)
449 *out = git_reference__alloc_symbolic(ref_name, target);
450 } else {
451 git_oid oid;
452
453 if (!(error = loose_parse_oid(&oid, ref_name, &ref_file)) &&
454 out != NULL)
455 *out = git_reference__alloc(ref_name, &oid, NULL);
456 }
457
458 git_buf_dispose(&ref_file);
459 return error;
460 }
461
ref_error_notfound(const char * name)462 static int ref_error_notfound(const char *name)
463 {
464 git_error_set(GIT_ERROR_REFERENCE, "reference '%s' not found", name);
465 return GIT_ENOTFOUND;
466 }
467
packed_lookup(git_reference ** out,refdb_fs_backend * backend,const char * ref_name)468 static int packed_lookup(
469 git_reference **out,
470 refdb_fs_backend *backend,
471 const char *ref_name)
472 {
473 int error = 0;
474 struct packref *entry;
475
476 if ((error = packed_reload(backend)) < 0)
477 return error;
478
479 if (git_sortedcache_rlock(backend->refcache) < 0)
480 return -1;
481
482 entry = git_sortedcache_lookup(backend->refcache, ref_name);
483 if (!entry) {
484 error = ref_error_notfound(ref_name);
485 } else {
486 *out = git_reference__alloc(ref_name, &entry->oid, &entry->peel);
487 if (!*out)
488 error = -1;
489 }
490
491 git_sortedcache_runlock(backend->refcache);
492
493 return error;
494 }
495
refdb_fs_backend__lookup(git_reference ** out,git_refdb_backend * _backend,const char * ref_name)496 static int refdb_fs_backend__lookup(
497 git_reference **out,
498 git_refdb_backend *_backend,
499 const char *ref_name)
500 {
501 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
502 int error;
503
504 GIT_ASSERT_ARG(backend);
505
506 if (!(error = loose_lookup(out, backend, ref_name)))
507 return 0;
508
509 /* only try to lookup this reference on the packfile if it
510 * wasn't found on the loose refs; not if there was a critical error */
511 if (error == GIT_ENOTFOUND) {
512 git_error_clear();
513 error = packed_lookup(out, backend, ref_name);
514 }
515
516 return error;
517 }
518
519 typedef struct {
520 git_reference_iterator parent;
521
522 char *glob;
523
524 git_pool pool;
525 git_vector loose;
526
527 git_sortedcache *cache;
528 size_t loose_pos;
529 size_t packed_pos;
530 } refdb_fs_iter;
531
refdb_fs_backend__iterator_free(git_reference_iterator * _iter)532 static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
533 {
534 refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent);
535
536 git_vector_free(&iter->loose);
537 git_pool_clear(&iter->pool);
538 git_sortedcache_free(iter->cache);
539 git__free(iter);
540 }
541
iter_load_loose_paths(refdb_fs_backend * backend,refdb_fs_iter * iter)542 static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
543 {
544 int error = 0;
545 git_buf path = GIT_BUF_INIT;
546 git_iterator *fsit = NULL;
547 git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT;
548 const git_index_entry *entry = NULL;
549 const char *ref_prefix = GIT_REFS_DIR;
550 size_t ref_prefix_len = strlen(ref_prefix);
551
552 if (!backend->commonpath) /* do nothing if no commonpath for loose refs */
553 return 0;
554
555 fsit_opts.flags = backend->iterator_flags;
556
557 if (iter->glob) {
558 const char *last_sep = NULL;
559 const char *pos;
560 for (pos = iter->glob; *pos; ++pos) {
561 switch (*pos) {
562 case '?':
563 case '*':
564 case '[':
565 case '\\':
566 break;
567 case '/':
568 last_sep = pos;
569 /* FALLTHROUGH */
570 default:
571 continue;
572 }
573 break;
574 }
575 if (last_sep) {
576 ref_prefix = iter->glob;
577 ref_prefix_len = (last_sep - ref_prefix) + 1;
578 }
579 }
580
581 if ((error = git_buf_printf(&path, "%s/", backend->commonpath)) < 0 ||
582 (error = git_buf_put(&path, ref_prefix, ref_prefix_len)) < 0) {
583 git_buf_dispose(&path);
584 return error;
585 }
586
587 if ((error = git_iterator_for_filesystem(&fsit, path.ptr, &fsit_opts)) < 0) {
588 git_buf_dispose(&path);
589 return (iter->glob && error == GIT_ENOTFOUND)? 0 : error;
590 }
591
592 error = git_buf_sets(&path, ref_prefix);
593
594 while (!error && !git_iterator_advance(&entry, fsit)) {
595 const char *ref_name;
596 char *ref_dup;
597
598 git_buf_truncate(&path, ref_prefix_len);
599 git_buf_puts(&path, entry->path);
600 ref_name = git_buf_cstr(&path);
601
602 if (git__suffixcmp(ref_name, ".lock") == 0 ||
603 (iter->glob && wildmatch(iter->glob, ref_name, 0) != 0))
604 continue;
605
606 ref_dup = git_pool_strdup(&iter->pool, ref_name);
607 if (!ref_dup)
608 error = -1;
609 else
610 error = git_vector_insert(&iter->loose, ref_dup);
611 }
612
613 git_iterator_free(fsit);
614 git_buf_dispose(&path);
615
616 return error;
617 }
618
refdb_fs_backend__iterator_next(git_reference ** out,git_reference_iterator * _iter)619 static int refdb_fs_backend__iterator_next(
620 git_reference **out, git_reference_iterator *_iter)
621 {
622 int error = GIT_ITEROVER;
623 refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent);
624 refdb_fs_backend *backend = GIT_CONTAINER_OF(iter->parent.db->backend, refdb_fs_backend, parent);
625 struct packref *ref;
626
627 while (iter->loose_pos < iter->loose.length) {
628 const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
629
630 if (loose_lookup(out, backend, path) == 0) {
631 ref = git_sortedcache_lookup(iter->cache, path);
632 if (ref)
633 ref->flags |= PACKREF_SHADOWED;
634
635 return 0;
636 }
637
638 git_error_clear();
639 }
640
641 error = GIT_ITEROVER;
642 while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
643 ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
644 if (!ref) /* stop now if another thread deleted refs and we past end */
645 break;
646
647 if (ref->flags & PACKREF_SHADOWED)
648 continue;
649 if (iter->glob && wildmatch(iter->glob, ref->name, 0) != 0)
650 continue;
651
652 *out = git_reference__alloc(ref->name, &ref->oid, &ref->peel);
653 error = (*out != NULL) ? 0 : -1;
654 break;
655 }
656
657 return error;
658 }
659
refdb_fs_backend__iterator_next_name(const char ** out,git_reference_iterator * _iter)660 static int refdb_fs_backend__iterator_next_name(
661 const char **out, git_reference_iterator *_iter)
662 {
663 int error = GIT_ITEROVER;
664 refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent);
665 refdb_fs_backend *backend = GIT_CONTAINER_OF(iter->parent.db->backend, refdb_fs_backend, parent);
666 struct packref *ref;
667
668 while (iter->loose_pos < iter->loose.length) {
669 const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
670 struct packref *ref;
671
672 if (loose_lookup(NULL, backend, path) == 0) {
673 ref = git_sortedcache_lookup(iter->cache, path);
674 if (ref)
675 ref->flags |= PACKREF_SHADOWED;
676
677 *out = path;
678 return 0;
679 }
680
681 git_error_clear();
682 }
683
684 error = GIT_ITEROVER;
685 while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
686 ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
687 if (!ref) /* stop now if another thread deleted refs and we past end */
688 break;
689
690 if (ref->flags & PACKREF_SHADOWED)
691 continue;
692 if (iter->glob && wildmatch(iter->glob, ref->name, 0) != 0)
693 continue;
694
695 *out = ref->name;
696 error = 0;
697 break;
698 }
699
700 return error;
701 }
702
refdb_fs_backend__iterator(git_reference_iterator ** out,git_refdb_backend * _backend,const char * glob)703 static int refdb_fs_backend__iterator(
704 git_reference_iterator **out, git_refdb_backend *_backend, const char *glob)
705 {
706 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
707 refdb_fs_iter *iter = NULL;
708 int error;
709
710 GIT_ASSERT_ARG(backend);
711
712 iter = git__calloc(1, sizeof(refdb_fs_iter));
713 GIT_ERROR_CHECK_ALLOC(iter);
714
715 if ((error = git_pool_init(&iter->pool, 1)) < 0)
716 goto out;
717
718 if ((error = git_vector_init(&iter->loose, 8, NULL)) < 0)
719 goto out;
720
721 if (glob != NULL &&
722 (iter->glob = git_pool_strdup(&iter->pool, glob)) == NULL) {
723 error = GIT_ERROR_NOMEMORY;
724 goto out;
725 }
726
727 if ((error = iter_load_loose_paths(backend, iter)) < 0)
728 goto out;
729
730 if ((error = packed_reload(backend)) < 0)
731 goto out;
732
733 if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
734 goto out;
735
736 iter->parent.next = refdb_fs_backend__iterator_next;
737 iter->parent.next_name = refdb_fs_backend__iterator_next_name;
738 iter->parent.free = refdb_fs_backend__iterator_free;
739
740 *out = (git_reference_iterator *)iter;
741 out:
742 if (error)
743 refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
744 return error;
745 }
746
ref_is_available(const char * old_ref,const char * new_ref,const char * this_ref)747 static bool ref_is_available(
748 const char *old_ref, const char *new_ref, const char *this_ref)
749 {
750 if (old_ref == NULL || strcmp(old_ref, this_ref)) {
751 size_t reflen = strlen(this_ref);
752 size_t newlen = strlen(new_ref);
753 size_t cmplen = reflen < newlen ? reflen : newlen;
754 const char *lead = reflen < newlen ? new_ref : this_ref;
755
756 if (!strncmp(new_ref, this_ref, cmplen) && lead[cmplen] == '/') {
757 return false;
758 }
759 }
760
761 return true;
762 }
763
reference_path_available(refdb_fs_backend * backend,const char * new_ref,const char * old_ref,int force)764 static int reference_path_available(
765 refdb_fs_backend *backend,
766 const char *new_ref,
767 const char* old_ref,
768 int force)
769 {
770 size_t i;
771 int error;
772
773 if ((error = packed_reload(backend)) < 0)
774 return error;
775
776 if (!force) {
777 int exists;
778
779 if ((error = refdb_fs_backend__exists(
780 &exists, (git_refdb_backend *)backend, new_ref)) < 0) {
781 return error;
782 }
783
784 if (exists) {
785 git_error_set(GIT_ERROR_REFERENCE,
786 "failed to write reference '%s': a reference with "
787 "that name already exists.", new_ref);
788 return GIT_EEXISTS;
789 }
790 }
791
792 if ((error = git_sortedcache_rlock(backend->refcache)) < 0)
793 return error;
794
795 for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
796 struct packref *ref = git_sortedcache_entry(backend->refcache, i);
797
798 if (ref && !ref_is_available(old_ref, new_ref, ref->name)) {
799 git_sortedcache_runlock(backend->refcache);
800 git_error_set(GIT_ERROR_REFERENCE,
801 "path to reference '%s' collides with existing one", new_ref);
802 return -1;
803 }
804 }
805
806 git_sortedcache_runlock(backend->refcache);
807 return 0;
808 }
809
loose_lock(git_filebuf * file,refdb_fs_backend * backend,const char * name)810 static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name)
811 {
812 int error, filebuf_flags;
813 git_buf ref_path = GIT_BUF_INIT;
814 const char *basedir;
815
816 GIT_ASSERT_ARG(file);
817 GIT_ASSERT_ARG(backend);
818 GIT_ASSERT_ARG(name);
819
820 if (!git_path_validate(backend->repo, name, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
821 git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", name);
822 return GIT_EINVALIDSPEC;
823 }
824
825 if (is_per_worktree_ref(name))
826 basedir = backend->gitpath;
827 else
828 basedir = backend->commonpath;
829
830 /* Remove a possibly existing empty directory hierarchy
831 * which name would collide with the reference name
832 */
833 if ((error = git_futils_rmdir_r(name, basedir, GIT_RMDIR_SKIP_NONEMPTY)) < 0)
834 return error;
835
836 if ((error = loose_path(&ref_path, basedir, name)) < 0)
837 return error;
838
839 filebuf_flags = GIT_FILEBUF_CREATE_LEADING_DIRS;
840 if (backend->fsync)
841 filebuf_flags |= GIT_FILEBUF_FSYNC;
842
843 error = git_filebuf_open(file, ref_path.ptr, filebuf_flags, GIT_REFS_FILE_MODE);
844
845 if (error == GIT_EDIRECTORY)
846 git_error_set(GIT_ERROR_REFERENCE, "cannot lock ref '%s', there are refs beneath that folder", name);
847
848 git_buf_dispose(&ref_path);
849 return error;
850 }
851
loose_commit(git_filebuf * file,const git_reference * ref)852 static int loose_commit(git_filebuf *file, const git_reference *ref)
853 {
854 GIT_ASSERT_ARG(file);
855 GIT_ASSERT_ARG(ref);
856
857 if (ref->type == GIT_REFERENCE_DIRECT) {
858 char oid[GIT_OID_HEXSZ + 1];
859 git_oid_nfmt(oid, sizeof(oid), &ref->target.oid);
860
861 git_filebuf_printf(file, "%s\n", oid);
862 } else if (ref->type == GIT_REFERENCE_SYMBOLIC) {
863 git_filebuf_printf(file, GIT_SYMREF "%s\n", ref->target.symbolic);
864 } else {
865 GIT_ASSERT(0);
866 }
867
868 return git_filebuf_commit(file);
869 }
870
refdb_fs_backend__lock(void ** out,git_refdb_backend * _backend,const char * refname)871 static int refdb_fs_backend__lock(void **out, git_refdb_backend *_backend, const char *refname)
872 {
873 int error;
874 git_filebuf *lock;
875 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
876
877 lock = git__calloc(1, sizeof(git_filebuf));
878 GIT_ERROR_CHECK_ALLOC(lock);
879
880 if ((error = loose_lock(lock, backend, refname)) < 0) {
881 git__free(lock);
882 return error;
883 }
884
885 *out = lock;
886 return 0;
887 }
888
889 static int refdb_fs_backend__write_tail(
890 git_refdb_backend *_backend,
891 const git_reference *ref,
892 git_filebuf *file,
893 int update_reflog,
894 const git_oid *old_id,
895 const char *old_target,
896 const git_signature *who,
897 const char *message);
898
899 static int refdb_fs_backend__delete_tail(
900 git_refdb_backend *_backend,
901 git_filebuf *file,
902 const char *ref_name,
903 const git_oid *old_id,
904 const char *old_target);
905
refdb_fs_backend__unlock(git_refdb_backend * backend,void * payload,int success,int update_reflog,const git_reference * ref,const git_signature * sig,const char * message)906 static int refdb_fs_backend__unlock(git_refdb_backend *backend, void *payload, int success, int update_reflog,
907 const git_reference *ref, const git_signature *sig, const char *message)
908 {
909 git_filebuf *lock = (git_filebuf *) payload;
910 int error = 0;
911
912 if (success == 2)
913 error = refdb_fs_backend__delete_tail(backend, lock, ref->name, NULL, NULL);
914 else if (success)
915 error = refdb_fs_backend__write_tail(backend, ref, lock, update_reflog, NULL, NULL, sig, message);
916 else
917 git_filebuf_cleanup(lock);
918
919 git__free(lock);
920 return error;
921 }
922
923 /*
924 * Find out what object this reference resolves to.
925 *
926 * For references that point to a 'big' tag (e.g. an
927 * actual tag object on the repository), we need to
928 * cache on the packfile the OID of the object to
929 * which that 'big tag' is pointing to.
930 */
packed_find_peel(refdb_fs_backend * backend,struct packref * ref)931 static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref)
932 {
933 git_object *object;
934
935 if (ref->flags & PACKREF_HAS_PEEL || ref->flags & PACKREF_CANNOT_PEEL)
936 return 0;
937
938 /*
939 * Find the tagged object in the repository
940 */
941 if (git_object_lookup(&object, backend->repo, &ref->oid, GIT_OBJECT_ANY) < 0)
942 return -1;
943
944 /*
945 * If the tagged object is a Tag object, we need to resolve it;
946 * if the ref is actually a 'weak' ref, we don't need to resolve
947 * anything.
948 */
949 if (git_object_type(object) == GIT_OBJECT_TAG) {
950 git_tag *tag = (git_tag *)object;
951
952 /*
953 * Find the object pointed at by this tag
954 */
955 git_oid_cpy(&ref->peel, git_tag_target_id(tag));
956 ref->flags |= PACKREF_HAS_PEEL;
957
958 /*
959 * The reference has now cached the resolved OID, and is
960 * marked at such. When written to the packfile, it'll be
961 * accompanied by this resolved oid
962 */
963 }
964
965 git_object_free(object);
966 return 0;
967 }
968
969 /*
970 * Write a single reference into a packfile
971 */
packed_write_ref(struct packref * ref,git_filebuf * file)972 static int packed_write_ref(struct packref *ref, git_filebuf *file)
973 {
974 char oid[GIT_OID_HEXSZ + 1];
975 git_oid_nfmt(oid, sizeof(oid), &ref->oid);
976
977 /*
978 * For references that peel to an object in the repo, we must
979 * write the resulting peel on a separate line, e.g.
980 *
981 * 6fa8a902cc1d18527e1355773c86721945475d37 refs/tags/libgit2-0.4
982 * ^2ec0cb7959b0bf965d54f95453f5b4b34e8d3100
983 *
984 * This obviously only applies to tags.
985 * The required peels have already been loaded into `ref->peel_target`.
986 */
987 if (ref->flags & PACKREF_HAS_PEEL) {
988 char peel[GIT_OID_HEXSZ + 1];
989 git_oid_nfmt(peel, sizeof(peel), &ref->peel);
990
991 if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
992 return -1;
993 } else {
994 if (git_filebuf_printf(file, "%s %s\n", oid, ref->name) < 0)
995 return -1;
996 }
997
998 return 0;
999 }
1000
1001 /*
1002 * Remove all loose references
1003 *
1004 * Once we have successfully written a packfile,
1005 * all the loose references that were packed must be
1006 * removed from disk.
1007 *
1008 * This is a dangerous method; make sure the packfile
1009 * is well-written, because we are destructing references
1010 * here otherwise.
1011 */
packed_remove_loose(refdb_fs_backend * backend)1012 static int packed_remove_loose(refdb_fs_backend *backend)
1013 {
1014 size_t i;
1015 git_filebuf lock = GIT_FILEBUF_INIT;
1016 git_buf ref_content = GIT_BUF_INIT;
1017 int error = 0;
1018
1019 /* backend->refcache is already locked when this is called */
1020
1021 for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
1022 struct packref *ref = git_sortedcache_entry(backend->refcache, i);
1023 git_oid current_id;
1024
1025 if (!ref || !(ref->flags & PACKREF_WAS_LOOSE))
1026 continue;
1027
1028 git_filebuf_cleanup(&lock);
1029
1030 /* We need to stop anybody from updating the ref while we try to do a safe delete */
1031 error = loose_lock(&lock, backend, ref->name);
1032 /* If someone else is updating it, let them do it */
1033 if (error == GIT_EEXISTS || error == GIT_ENOTFOUND)
1034 continue;
1035
1036 if (error < 0) {
1037 git_buf_dispose(&ref_content);
1038 git_error_set(GIT_ERROR_REFERENCE, "failed to lock loose reference '%s'", ref->name);
1039 return error;
1040 }
1041
1042 error = git_futils_readbuffer(&ref_content, lock.path_original);
1043 /* Someone else beat us to cleaning up the ref, let's simply continue */
1044 if (error == GIT_ENOTFOUND)
1045 continue;
1046
1047 /* This became a symref between us packing and trying to delete it, so ignore it */
1048 if (!git__prefixcmp(ref_content.ptr, GIT_SYMREF))
1049 continue;
1050
1051 /* Figure out the current id; if we find a bad ref file, skip it so we can do the rest */
1052 if (loose_parse_oid(¤t_id, lock.path_original, &ref_content) < 0)
1053 continue;
1054
1055 /* If the ref moved since we packed it, we must not delete it */
1056 if (!git_oid_equal(¤t_id, &ref->oid))
1057 continue;
1058
1059 /*
1060 * if we fail to remove a single file, this is *not* good,
1061 * but we should keep going and remove as many as possible.
1062 * If we fail to remove, the ref is still in the old state, so
1063 * we haven't lost information.
1064 */
1065 p_unlink(lock.path_original);
1066 }
1067
1068 git_buf_dispose(&ref_content);
1069 git_filebuf_cleanup(&lock);
1070 return 0;
1071 }
1072
1073 /*
1074 * Write all the contents in the in-memory packfile to disk.
1075 */
packed_write(refdb_fs_backend * backend)1076 static int packed_write(refdb_fs_backend *backend)
1077 {
1078 git_sortedcache *refcache = backend->refcache;
1079 git_filebuf pack_file = GIT_FILEBUF_INIT;
1080 int error, open_flags = 0;
1081 size_t i;
1082
1083 /* lock the cache to updates while we do this */
1084 if ((error = git_sortedcache_wlock(refcache)) < 0)
1085 return error;
1086
1087 if (backend->fsync)
1088 open_flags = GIT_FILEBUF_FSYNC;
1089
1090 /* Open the file! */
1091 if ((error = git_filebuf_open(&pack_file, git_sortedcache_path(refcache), open_flags, GIT_PACKEDREFS_FILE_MODE)) < 0)
1092 goto fail;
1093
1094 /* Packfiles have a header... apparently
1095 * This is in fact not required, but we might as well print it
1096 * just for kicks */
1097 if ((error = git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < 0)
1098 goto fail;
1099
1100 for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) {
1101 struct packref *ref = git_sortedcache_entry(refcache, i);
1102 GIT_ASSERT(ref);
1103
1104 if ((error = packed_find_peel(backend, ref)) < 0)
1105 goto fail;
1106
1107 if ((error = packed_write_ref(ref, &pack_file)) < 0)
1108 goto fail;
1109 }
1110
1111 /* if we've written all the references properly, we can commit
1112 * the packfile to make the changes effective */
1113 if ((error = git_filebuf_commit(&pack_file)) < 0)
1114 goto fail;
1115
1116 /* when and only when the packfile has been properly written,
1117 * we can go ahead and remove the loose refs */
1118 if ((error = packed_remove_loose(backend)) < 0)
1119 goto fail;
1120
1121 git_sortedcache_updated(refcache);
1122 git_sortedcache_wunlock(refcache);
1123
1124 /* we're good now */
1125 return 0;
1126
1127 fail:
1128 git_filebuf_cleanup(&pack_file);
1129 git_sortedcache_wunlock(refcache);
1130
1131 return error;
1132 }
1133
packed_delete(refdb_fs_backend * backend,const char * ref_name)1134 static int packed_delete(refdb_fs_backend *backend, const char *ref_name)
1135 {
1136 size_t pack_pos;
1137 int error, found = 0;
1138
1139 if ((error = packed_reload(backend)) < 0)
1140 goto cleanup;
1141
1142 if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
1143 goto cleanup;
1144
1145 /* If a packed reference exists, remove it from the packfile and repack if necessary */
1146 error = git_sortedcache_lookup_index(&pack_pos, backend->refcache, ref_name);
1147 if (error == 0) {
1148 error = git_sortedcache_remove(backend->refcache, pack_pos);
1149 found = 1;
1150 }
1151 if (error == GIT_ENOTFOUND)
1152 error = 0;
1153
1154 git_sortedcache_wunlock(backend->refcache);
1155
1156 if (found)
1157 error = packed_write(backend);
1158
1159 cleanup:
1160 return error;
1161 }
1162
1163 static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *author, const char *message);
1164
cmp_old_ref(int * cmp,git_refdb_backend * backend,const char * name,const git_oid * old_id,const char * old_target)1165 static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const char *name,
1166 const git_oid *old_id, const char *old_target)
1167 {
1168 int error = 0;
1169 git_reference *old_ref = NULL;
1170
1171 *cmp = 0;
1172 /* It "matches" if there is no old value to compare against */
1173 if (!old_id && !old_target)
1174 return 0;
1175
1176 if ((error = refdb_fs_backend__lookup(&old_ref, backend, name)) < 0)
1177 goto out;
1178
1179 /* If the types don't match, there's no way the values do */
1180 if (old_id && old_ref->type != GIT_REFERENCE_DIRECT) {
1181 *cmp = -1;
1182 goto out;
1183 }
1184 if (old_target && old_ref->type != GIT_REFERENCE_SYMBOLIC) {
1185 *cmp = 1;
1186 goto out;
1187 }
1188
1189 if (old_id && old_ref->type == GIT_REFERENCE_DIRECT)
1190 *cmp = git_oid_cmp(old_id, &old_ref->target.oid);
1191
1192 if (old_target && old_ref->type == GIT_REFERENCE_SYMBOLIC)
1193 *cmp = git__strcmp(old_target, old_ref->target.symbolic);
1194
1195 out:
1196 git_reference_free(old_ref);
1197
1198 return error;
1199 }
1200
1201 /*
1202 * The git.git comment regarding this, for your viewing pleasure:
1203 *
1204 * Special hack: If a branch is updated directly and HEAD
1205 * points to it (may happen on the remote side of a push
1206 * for example) then logically the HEAD reflog should be
1207 * updated too.
1208 * A generic solution implies reverse symref information,
1209 * but finding all symrefs pointing to the given branch
1210 * would be rather costly for this rare event (the direct
1211 * update of a branch) to be worth it. So let's cheat and
1212 * check with HEAD only which should cover 99% of all usage
1213 * scenarios (even 100% of the default ones).
1214 */
maybe_append_head(refdb_fs_backend * backend,const git_reference * ref,const git_signature * who,const char * message)1215 static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message)
1216 {
1217 git_reference *head = NULL;
1218 git_refdb *refdb = NULL;
1219 int error, write_reflog;
1220 git_oid old_id;
1221
1222 if ((error = git_repository_refdb(&refdb, backend->repo)) < 0 ||
1223 (error = git_refdb_should_write_head_reflog(&write_reflog, refdb, ref)) < 0)
1224 goto out;
1225 if (!write_reflog)
1226 goto out;
1227
1228 /* if we can't resolve, we use {0}*40 as old id */
1229 if (git_reference_name_to_id(&old_id, backend->repo, ref->name) < 0)
1230 memset(&old_id, 0, sizeof(old_id));
1231
1232 if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0 ||
1233 (error = reflog_append(backend, head, &old_id, git_reference_target(ref), who, message)) < 0)
1234 goto out;
1235
1236 out:
1237 git_reference_free(head);
1238 git_refdb_free(refdb);
1239 return error;
1240 }
1241
refdb_fs_backend__write(git_refdb_backend * _backend,const git_reference * ref,int force,const git_signature * who,const char * message,const git_oid * old_id,const char * old_target)1242 static int refdb_fs_backend__write(
1243 git_refdb_backend *_backend,
1244 const git_reference *ref,
1245 int force,
1246 const git_signature *who,
1247 const char *message,
1248 const git_oid *old_id,
1249 const char *old_target)
1250 {
1251 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1252 git_filebuf file = GIT_FILEBUF_INIT;
1253 int error = 0;
1254
1255 GIT_ASSERT_ARG(backend);
1256
1257 if ((error = reference_path_available(backend, ref->name, NULL, force)) < 0)
1258 return error;
1259
1260 /* We need to perform the reflog append and old value check under the ref's lock */
1261 if ((error = loose_lock(&file, backend, ref->name)) < 0)
1262 return error;
1263
1264 return refdb_fs_backend__write_tail(_backend, ref, &file, true, old_id, old_target, who, message);
1265 }
1266
refdb_fs_backend__write_tail(git_refdb_backend * _backend,const git_reference * ref,git_filebuf * file,int update_reflog,const git_oid * old_id,const char * old_target,const git_signature * who,const char * message)1267 static int refdb_fs_backend__write_tail(
1268 git_refdb_backend *_backend,
1269 const git_reference *ref,
1270 git_filebuf *file,
1271 int update_reflog,
1272 const git_oid *old_id,
1273 const char *old_target,
1274 const git_signature *who,
1275 const char *message)
1276 {
1277 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1278 int error = 0, cmp = 0, should_write;
1279 const char *new_target = NULL;
1280 const git_oid *new_id = NULL;
1281
1282 if ((error = cmp_old_ref(&cmp, _backend, ref->name, old_id, old_target)) < 0)
1283 goto on_error;
1284
1285 if (cmp) {
1286 git_error_set(GIT_ERROR_REFERENCE, "old reference value does not match");
1287 error = GIT_EMODIFIED;
1288 goto on_error;
1289 }
1290
1291 if (ref->type == GIT_REFERENCE_SYMBOLIC)
1292 new_target = ref->target.symbolic;
1293 else
1294 new_id = &ref->target.oid;
1295
1296 error = cmp_old_ref(&cmp, _backend, ref->name, new_id, new_target);
1297 if (error < 0 && error != GIT_ENOTFOUND)
1298 goto on_error;
1299
1300 /* Don't update if we have the same value */
1301 if (!error && !cmp) {
1302 error = 0;
1303 goto on_error; /* not really error */
1304 }
1305
1306 if (update_reflog) {
1307 git_refdb *refdb;
1308
1309 if ((error = git_repository_refdb__weakptr(&refdb, backend->repo)) < 0 ||
1310 (error = git_refdb_should_write_reflog(&should_write, refdb, ref)) < 0)
1311 goto on_error;
1312
1313 if (should_write) {
1314 if ((error = reflog_append(backend, ref, NULL, NULL, who, message)) < 0)
1315 goto on_error;
1316 if ((error = maybe_append_head(backend, ref, who, message)) < 0)
1317 goto on_error;
1318 }
1319 }
1320
1321 return loose_commit(file, ref);
1322
1323 on_error:
1324 git_filebuf_cleanup(file);
1325 return error;
1326 }
1327
refdb_fs_backend__prune_refs(refdb_fs_backend * backend,const char * ref_name,const char * prefix)1328 static int refdb_fs_backend__prune_refs(
1329 refdb_fs_backend *backend,
1330 const char *ref_name,
1331 const char *prefix)
1332 {
1333 git_buf relative_path = GIT_BUF_INIT;
1334 git_buf base_path = GIT_BUF_INIT;
1335 size_t commonlen;
1336 int error;
1337
1338 GIT_ASSERT_ARG(backend);
1339 GIT_ASSERT_ARG(ref_name);
1340
1341 if ((error = git_buf_sets(&relative_path, ref_name)) < 0)
1342 goto cleanup;
1343
1344 git_path_squash_slashes(&relative_path);
1345 if ((commonlen = git_path_common_dirlen("refs/heads/", git_buf_cstr(&relative_path))) == strlen("refs/heads/") ||
1346 (commonlen = git_path_common_dirlen("refs/tags/", git_buf_cstr(&relative_path))) == strlen("refs/tags/") ||
1347 (commonlen = git_path_common_dirlen("refs/remotes/", git_buf_cstr(&relative_path))) == strlen("refs/remotes/")) {
1348
1349 git_buf_truncate(&relative_path, commonlen);
1350
1351 if (prefix)
1352 error = git_buf_join3(&base_path, '/',
1353 backend->commonpath, prefix,
1354 git_buf_cstr(&relative_path));
1355 else
1356 error = git_buf_joinpath(&base_path,
1357 backend->commonpath,
1358 git_buf_cstr(&relative_path));
1359
1360 if (!error)
1361 error = git_path_validate_filesystem(base_path.ptr, base_path.size);
1362
1363 if (error < 0)
1364 goto cleanup;
1365
1366 error = git_futils_rmdir_r(ref_name + commonlen,
1367 git_buf_cstr(&base_path),
1368 GIT_RMDIR_EMPTY_PARENTS | GIT_RMDIR_SKIP_ROOT);
1369
1370 if (error == GIT_ENOTFOUND)
1371 error = 0;
1372 }
1373
1374 cleanup:
1375 git_buf_dispose(&relative_path);
1376 git_buf_dispose(&base_path);
1377 return error;
1378 }
1379
refdb_fs_backend__delete(git_refdb_backend * _backend,const char * ref_name,const git_oid * old_id,const char * old_target)1380 static int refdb_fs_backend__delete(
1381 git_refdb_backend *_backend,
1382 const char *ref_name,
1383 const git_oid *old_id, const char *old_target)
1384 {
1385 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1386 git_filebuf file = GIT_FILEBUF_INIT;
1387 int error = 0;
1388
1389 GIT_ASSERT_ARG(backend);
1390 GIT_ASSERT_ARG(ref_name);
1391
1392 if ((error = loose_lock(&file, backend, ref_name)) < 0)
1393 return error;
1394
1395 if ((error = refdb_reflog_fs__delete(_backend, ref_name)) < 0) {
1396 git_filebuf_cleanup(&file);
1397 return error;
1398 }
1399
1400 return refdb_fs_backend__delete_tail(_backend, &file, ref_name, old_id, old_target);
1401 }
1402
loose_delete(refdb_fs_backend * backend,const char * ref_name)1403 static int loose_delete(refdb_fs_backend *backend, const char *ref_name)
1404 {
1405 git_buf path = GIT_BUF_INIT;
1406 int error = 0;
1407
1408 if ((error = loose_path(&path, backend->commonpath, ref_name)) < 0)
1409 return error;
1410
1411 error = p_unlink(path.ptr);
1412 if (error < 0 && errno == ENOENT)
1413 error = GIT_ENOTFOUND;
1414 else if (error != 0)
1415 error = -1;
1416
1417 git_buf_dispose(&path);
1418
1419 return error;
1420 }
1421
refdb_fs_backend__delete_tail(git_refdb_backend * _backend,git_filebuf * file,const char * ref_name,const git_oid * old_id,const char * old_target)1422 static int refdb_fs_backend__delete_tail(
1423 git_refdb_backend *_backend,
1424 git_filebuf *file,
1425 const char *ref_name,
1426 const git_oid *old_id, const char *old_target)
1427 {
1428 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1429 int error = 0, cmp = 0;
1430 bool packed_deleted = 0;
1431
1432 error = cmp_old_ref(&cmp, _backend, ref_name, old_id, old_target);
1433 if (error < 0)
1434 goto cleanup;
1435
1436 if (cmp) {
1437 git_error_set(GIT_ERROR_REFERENCE, "old reference value does not match");
1438 error = GIT_EMODIFIED;
1439 goto cleanup;
1440 }
1441
1442 /*
1443 * To ensure that an external observer will see either the current ref value
1444 * (because the loose ref still exists), or a missing ref (after the packed-file is
1445 * unlocked, there will be nothing left), we must ensure things happen in the
1446 * following order:
1447 *
1448 * - the packed-ref file is locked and loaded, as well as a loose one, if it exists
1449 * - we optimistically delete a packed ref, keeping track of whether it existed
1450 * - we delete the loose ref, note that we have its .lock
1451 * - the loose ref is "unlocked", then the packed-ref file is rewritten and unlocked
1452 * - we should prune the path components if a loose ref was deleted
1453 *
1454 * Note that, because our packed backend doesn't expose its filesystem lock,
1455 * we might not be able to guarantee that this is what actually happens (ie.
1456 * as our current code never write packed-refs.lock, nothing stops observers
1457 * from grabbing a "stale" value from there).
1458 */
1459 if ((error = packed_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND)
1460 goto cleanup;
1461
1462 if (error == 0)
1463 packed_deleted = 1;
1464
1465 if ((error = loose_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND)
1466 goto cleanup;
1467
1468 if (error == GIT_ENOTFOUND) {
1469 error = packed_deleted ? 0 : ref_error_notfound(ref_name);
1470 goto cleanup;
1471 }
1472
1473 cleanup:
1474 git_filebuf_cleanup(file);
1475 if (error == 0)
1476 error = refdb_fs_backend__prune_refs(backend, ref_name, "");
1477 return error;
1478 }
1479
1480 static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name);
1481
refdb_fs_backend__rename(git_reference ** out,git_refdb_backend * _backend,const char * old_name,const char * new_name,int force,const git_signature * who,const char * message)1482 static int refdb_fs_backend__rename(
1483 git_reference **out,
1484 git_refdb_backend *_backend,
1485 const char *old_name,
1486 const char *new_name,
1487 int force,
1488 const git_signature *who,
1489 const char *message)
1490 {
1491 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1492 git_reference *old, *new = NULL;
1493 git_filebuf file = GIT_FILEBUF_INIT;
1494 int error;
1495
1496 GIT_ASSERT_ARG(backend);
1497
1498 if ((error = reference_path_available(
1499 backend, new_name, old_name, force)) < 0 ||
1500 (error = refdb_fs_backend__lookup(&old, _backend, old_name)) < 0)
1501 return error;
1502
1503 if ((error = refdb_fs_backend__delete(_backend, old_name, NULL, NULL)) < 0) {
1504 git_reference_free(old);
1505 return error;
1506 }
1507
1508 new = git_reference__realloc(&old, new_name);
1509 if (!new) {
1510 git_reference_free(old);
1511 return -1;
1512 }
1513
1514 if ((error = loose_lock(&file, backend, new->name)) < 0) {
1515 git_reference_free(new);
1516 return error;
1517 }
1518
1519 /* Try to rename the refog; it's ok if the old doesn't exist */
1520 error = refdb_reflog_fs__rename(_backend, old_name, new_name);
1521 if (((error == 0) || (error == GIT_ENOTFOUND)) &&
1522 ((error = reflog_append(backend, new, git_reference_target(new), NULL, who, message)) < 0)) {
1523 git_reference_free(new);
1524 git_filebuf_cleanup(&file);
1525 return error;
1526 }
1527
1528 if (error < 0) {
1529 git_reference_free(new);
1530 git_filebuf_cleanup(&file);
1531 return error;
1532 }
1533
1534
1535 if ((error = loose_commit(&file, new)) < 0 || out == NULL) {
1536 git_reference_free(new);
1537 return error;
1538 }
1539
1540 *out = new;
1541 return 0;
1542 }
1543
refdb_fs_backend__compress(git_refdb_backend * _backend)1544 static int refdb_fs_backend__compress(git_refdb_backend *_backend)
1545 {
1546 int error;
1547 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1548
1549 GIT_ASSERT_ARG(backend);
1550
1551 if ((error = packed_reload(backend)) < 0 || /* load the existing packfile */
1552 (error = packed_loadloose(backend)) < 0 || /* add all the loose refs */
1553 (error = packed_write(backend)) < 0) /* write back to disk */
1554 return error;
1555
1556 return 0;
1557 }
1558
refdb_fs_backend__free(git_refdb_backend * _backend)1559 static void refdb_fs_backend__free(git_refdb_backend *_backend)
1560 {
1561 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1562
1563 if (!backend)
1564 return;
1565
1566 git_sortedcache_free(backend->refcache);
1567 git__free(backend->gitpath);
1568 git__free(backend->commonpath);
1569 git__free(backend);
1570 }
1571
setup_namespace(git_repository * repo,const char * in)1572 static char *setup_namespace(git_repository *repo, const char *in)
1573 {
1574 git_buf path = GIT_BUF_INIT;
1575 char *parts, *start, *end, *out = NULL;
1576
1577 if (!in)
1578 goto done;
1579
1580 git_buf_puts(&path, in);
1581
1582 /* if the repo is not namespaced, nothing else to do */
1583 if (repo->namespace == NULL) {
1584 out = git_buf_detach(&path);
1585 goto done;
1586 }
1587
1588 parts = end = git__strdup(repo->namespace);
1589 if (parts == NULL)
1590 goto done;
1591
1592 /*
1593 * From `man gitnamespaces`:
1594 * namespaces which include a / will expand to a hierarchy
1595 * of namespaces; for example, GIT_NAMESPACE=foo/bar will store
1596 * refs under refs/namespaces/foo/refs/namespaces/bar/
1597 */
1598 while ((start = git__strsep(&end, "/")) != NULL)
1599 git_buf_printf(&path, "refs/namespaces/%s/", start);
1600
1601 git_buf_printf(&path, "refs/namespaces/%s/refs", end);
1602 git__free(parts);
1603
1604 /* Make sure that the folder with the namespace exists */
1605 if (git_futils_mkdir_relative(git_buf_cstr(&path), in, 0777,
1606 GIT_MKDIR_PATH, NULL) < 0)
1607 goto done;
1608
1609 /* Return root of the namespaced gitpath, i.e. without the trailing '/refs' */
1610 git_buf_rtruncate_at_char(&path, '/');
1611 out = git_buf_detach(&path);
1612
1613 done:
1614 git_buf_dispose(&path);
1615 return out;
1616 }
1617
reflog_alloc(git_reflog ** reflog,const char * name)1618 static int reflog_alloc(git_reflog **reflog, const char *name)
1619 {
1620 git_reflog *log;
1621
1622 *reflog = NULL;
1623
1624 log = git__calloc(1, sizeof(git_reflog));
1625 GIT_ERROR_CHECK_ALLOC(log);
1626
1627 log->ref_name = git__strdup(name);
1628 GIT_ERROR_CHECK_ALLOC(log->ref_name);
1629
1630 if (git_vector_init(&log->entries, 0, NULL) < 0) {
1631 git__free(log->ref_name);
1632 git__free(log);
1633 return -1;
1634 }
1635
1636 *reflog = log;
1637
1638 return 0;
1639 }
1640
reflog_parse(git_reflog * log,const char * buf,size_t buf_size)1641 static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
1642 {
1643 git_parse_ctx parser = GIT_PARSE_CTX_INIT;
1644
1645 if ((git_parse_ctx_init(&parser, buf, buf_size)) < 0)
1646 return -1;
1647
1648 for (; parser.remain_len; git_parse_advance_line(&parser)) {
1649 git_reflog_entry *entry;
1650 const char *sig;
1651 char c;
1652
1653 entry = git__calloc(1, sizeof(*entry));
1654 GIT_ERROR_CHECK_ALLOC(entry);
1655 entry->committer = git__calloc(1, sizeof(*entry->committer));
1656 GIT_ERROR_CHECK_ALLOC(entry->committer);
1657
1658 if (git_parse_advance_oid(&entry->oid_old, &parser) < 0 ||
1659 git_parse_advance_expected(&parser, " ", 1) < 0 ||
1660 git_parse_advance_oid(&entry->oid_cur, &parser) < 0)
1661 goto next;
1662
1663 sig = parser.line;
1664 while (git_parse_peek(&c, &parser, 0) == 0 && c != '\t' && c != '\n')
1665 git_parse_advance_chars(&parser, 1);
1666
1667 if (git_signature__parse(entry->committer, &sig, parser.line, NULL, 0) < 0)
1668 goto next;
1669
1670 if (c == '\t') {
1671 size_t len;
1672 git_parse_advance_chars(&parser, 1);
1673
1674 len = parser.line_len;
1675 if (parser.line[len - 1] == '\n')
1676 len--;
1677
1678 entry->msg = git__strndup(parser.line, len);
1679 GIT_ERROR_CHECK_ALLOC(entry->msg);
1680 }
1681
1682 if ((git_vector_insert(&log->entries, entry)) < 0) {
1683 git_reflog_entry__free(entry);
1684 return -1;
1685 }
1686
1687 continue;
1688
1689 next:
1690 git_reflog_entry__free(entry);
1691 }
1692
1693 return 0;
1694 }
1695
create_new_reflog_file(const char * filepath)1696 static int create_new_reflog_file(const char *filepath)
1697 {
1698 int fd, error;
1699
1700 if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
1701 return error;
1702
1703 if ((fd = p_open(filepath,
1704 O_WRONLY | O_CREAT,
1705 GIT_REFLOG_FILE_MODE)) < 0)
1706 return -1;
1707
1708 return p_close(fd);
1709 }
1710
refdb_reflog_fs__ensure_log(git_refdb_backend * _backend,const char * name)1711 static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name)
1712 {
1713 refdb_fs_backend *backend;
1714 git_repository *repo;
1715 git_buf path = GIT_BUF_INIT;
1716 int error;
1717
1718 GIT_ASSERT_ARG(_backend && name);
1719
1720 backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1721 repo = backend->repo;
1722
1723 if ((error = reflog_path(&path, repo, name)) < 0)
1724 return error;
1725
1726 error = create_new_reflog_file(git_buf_cstr(&path));
1727 git_buf_dispose(&path);
1728
1729 return error;
1730 }
1731
has_reflog(git_repository * repo,const char * name)1732 static int has_reflog(git_repository *repo, const char *name)
1733 {
1734 int ret = 0;
1735 git_buf path = GIT_BUF_INIT;
1736
1737 if (reflog_path(&path, repo, name) < 0)
1738 goto cleanup;
1739
1740 ret = git_path_isfile(git_buf_cstr(&path));
1741
1742 cleanup:
1743 git_buf_dispose(&path);
1744 return ret;
1745 }
1746
refdb_reflog_fs__has_log(git_refdb_backend * _backend,const char * name)1747 static int refdb_reflog_fs__has_log(git_refdb_backend *_backend, const char *name)
1748 {
1749 refdb_fs_backend *backend;
1750
1751 GIT_ASSERT_ARG(_backend);
1752 GIT_ASSERT_ARG(name);
1753
1754 backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1755
1756 return has_reflog(backend->repo, name);
1757 }
1758
refdb_reflog_fs__read(git_reflog ** out,git_refdb_backend * _backend,const char * name)1759 static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
1760 {
1761 int error = -1;
1762 git_buf log_path = GIT_BUF_INIT;
1763 git_buf log_file = GIT_BUF_INIT;
1764 git_reflog *log = NULL;
1765 git_repository *repo;
1766 refdb_fs_backend *backend;
1767
1768 GIT_ASSERT_ARG(out);
1769 GIT_ASSERT_ARG(_backend);
1770 GIT_ASSERT_ARG(name);
1771
1772 backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1773 repo = backend->repo;
1774
1775 if (reflog_alloc(&log, name) < 0)
1776 return -1;
1777
1778 if (reflog_path(&log_path, repo, name) < 0)
1779 goto cleanup;
1780
1781 error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
1782 if (error < 0 && error != GIT_ENOTFOUND)
1783 goto cleanup;
1784
1785 if ((error == GIT_ENOTFOUND) &&
1786 ((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
1787 goto cleanup;
1788
1789 if ((error = reflog_parse(log,
1790 git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
1791 goto cleanup;
1792
1793 *out = log;
1794 goto success;
1795
1796 cleanup:
1797 git_reflog_free(log);
1798
1799 success:
1800 git_buf_dispose(&log_file);
1801 git_buf_dispose(&log_path);
1802
1803 return error;
1804 }
1805
serialize_reflog_entry(git_buf * buf,const git_oid * oid_old,const git_oid * oid_new,const git_signature * committer,const char * msg)1806 static int serialize_reflog_entry(
1807 git_buf *buf,
1808 const git_oid *oid_old,
1809 const git_oid *oid_new,
1810 const git_signature *committer,
1811 const char *msg)
1812 {
1813 char raw_old[GIT_OID_HEXSZ+1];
1814 char raw_new[GIT_OID_HEXSZ+1];
1815
1816 git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
1817 git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
1818
1819 git_buf_clear(buf);
1820
1821 git_buf_puts(buf, raw_old);
1822 git_buf_putc(buf, ' ');
1823 git_buf_puts(buf, raw_new);
1824
1825 git_signature__writebuf(buf, " ", committer);
1826
1827 /* drop trailing LF */
1828 git_buf_rtrim(buf);
1829
1830 if (msg) {
1831 size_t i;
1832
1833 git_buf_putc(buf, '\t');
1834 git_buf_puts(buf, msg);
1835
1836 for (i = 0; i < buf->size - 2; i++)
1837 if (buf->ptr[i] == '\n')
1838 buf->ptr[i] = ' ';
1839 git_buf_rtrim(buf);
1840 }
1841
1842 git_buf_putc(buf, '\n');
1843
1844 return git_buf_oom(buf);
1845 }
1846
lock_reflog(git_filebuf * file,refdb_fs_backend * backend,const char * refname)1847 static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char *refname)
1848 {
1849 git_repository *repo;
1850 git_buf log_path = GIT_BUF_INIT;
1851 int error;
1852
1853 repo = backend->repo;
1854
1855 if (!git_path_validate(backend->repo, refname, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
1856 git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", refname);
1857 return GIT_EINVALIDSPEC;
1858 }
1859
1860 if (reflog_path(&log_path, repo, refname) < 0)
1861 return -1;
1862
1863 if (!git_path_isfile(git_buf_cstr(&log_path))) {
1864 git_error_set(GIT_ERROR_INVALID,
1865 "log file for reference '%s' doesn't exist", refname);
1866 error = -1;
1867 goto cleanup;
1868 }
1869
1870 error = git_filebuf_open(file, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE);
1871
1872 cleanup:
1873 git_buf_dispose(&log_path);
1874
1875 return error;
1876 }
1877
refdb_reflog_fs__write(git_refdb_backend * _backend,git_reflog * reflog)1878 static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog)
1879 {
1880 int error = -1;
1881 unsigned int i;
1882 git_reflog_entry *entry;
1883 refdb_fs_backend *backend;
1884 git_buf log = GIT_BUF_INIT;
1885 git_filebuf fbuf = GIT_FILEBUF_INIT;
1886
1887 GIT_ASSERT_ARG(_backend);
1888 GIT_ASSERT_ARG(reflog);
1889
1890 backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1891
1892 if ((error = lock_reflog(&fbuf, backend, reflog->ref_name)) < 0)
1893 return -1;
1894
1895 git_vector_foreach(&reflog->entries, i, entry) {
1896 if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
1897 goto cleanup;
1898
1899 if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
1900 goto cleanup;
1901 }
1902
1903 error = git_filebuf_commit(&fbuf);
1904 goto success;
1905
1906 cleanup:
1907 git_filebuf_cleanup(&fbuf);
1908
1909 success:
1910 git_buf_dispose(&log);
1911
1912 return error;
1913 }
1914
1915 /* Append to the reflog, must be called under reference lock */
reflog_append(refdb_fs_backend * backend,const git_reference * ref,const git_oid * old,const git_oid * new,const git_signature * who,const char * message)1916 static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *who, const char *message)
1917 {
1918 int error, is_symbolic, open_flags;
1919 git_oid old_id = {{0}}, new_id = {{0}};
1920 git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
1921 git_repository *repo = backend->repo;
1922
1923 is_symbolic = ref->type == GIT_REFERENCE_SYMBOLIC;
1924
1925 /* "normal" symbolic updates do not write */
1926 if (is_symbolic &&
1927 strcmp(ref->name, GIT_HEAD_FILE) &&
1928 !(old && new))
1929 return 0;
1930
1931 /* From here on is_symbolic also means that it's HEAD */
1932
1933 if (old) {
1934 git_oid_cpy(&old_id, old);
1935 } else {
1936 error = git_reference_name_to_id(&old_id, repo, ref->name);
1937 if (error < 0 && error != GIT_ENOTFOUND)
1938 return error;
1939 }
1940
1941 if (new) {
1942 git_oid_cpy(&new_id, new);
1943 } else {
1944 if (!is_symbolic) {
1945 git_oid_cpy(&new_id, git_reference_target(ref));
1946 } else {
1947 error = git_reference_name_to_id(&new_id, repo, git_reference_symbolic_target(ref));
1948 if (error < 0 && error != GIT_ENOTFOUND)
1949 return error;
1950 /* detaching HEAD does not create an entry */
1951 if (error == GIT_ENOTFOUND)
1952 return 0;
1953
1954 git_error_clear();
1955 }
1956 }
1957
1958 if ((error = serialize_reflog_entry(&buf, &old_id, &new_id, who, message)) < 0)
1959 goto cleanup;
1960
1961 if ((error = reflog_path(&path, repo, ref->name)) < 0)
1962 goto cleanup;
1963
1964 if (((error = git_futils_mkpath2file(git_buf_cstr(&path), 0777)) < 0) &&
1965 (error != GIT_EEXISTS)) {
1966 goto cleanup;
1967 }
1968
1969 /* If the new branch matches part of the namespace of a previously deleted branch,
1970 * there maybe an obsolete/unused directory (or directory hierarchy) in the way.
1971 */
1972 if (git_path_isdir(git_buf_cstr(&path))) {
1973 if ((error = git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY)) < 0) {
1974 if (error == GIT_ENOTFOUND)
1975 error = 0;
1976 } else if (git_path_isdir(git_buf_cstr(&path))) {
1977 git_error_set(GIT_ERROR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder",
1978 ref->name);
1979 error = GIT_EDIRECTORY;
1980 }
1981
1982 if (error != 0)
1983 goto cleanup;
1984 }
1985
1986 open_flags = O_WRONLY | O_CREAT | O_APPEND;
1987
1988 if (backend->fsync)
1989 open_flags |= O_FSYNC;
1990
1991 error = git_futils_writebuffer(&buf, git_buf_cstr(&path), open_flags, GIT_REFLOG_FILE_MODE);
1992
1993 cleanup:
1994 git_buf_dispose(&buf);
1995 git_buf_dispose(&path);
1996
1997 return error;
1998 }
1999
refdb_reflog_fs__rename(git_refdb_backend * _backend,const char * old_name,const char * new_name)2000 static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name)
2001 {
2002 int error = 0, fd;
2003 git_buf old_path = GIT_BUF_INIT;
2004 git_buf new_path = GIT_BUF_INIT;
2005 git_buf temp_path = GIT_BUF_INIT;
2006 git_buf normalized = GIT_BUF_INIT;
2007 git_repository *repo;
2008 refdb_fs_backend *backend;
2009
2010 GIT_ASSERT_ARG(_backend);
2011 GIT_ASSERT_ARG(old_name);
2012 GIT_ASSERT_ARG(new_name);
2013
2014 backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
2015 repo = backend->repo;
2016
2017 if ((error = git_reference__normalize_name(
2018 &normalized, new_name, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL)) < 0)
2019 return error;
2020
2021 if (git_buf_joinpath(&temp_path, repo->gitdir, GIT_REFLOG_DIR) < 0)
2022 return -1;
2023
2024 if ((error = loose_path(&old_path, git_buf_cstr(&temp_path), old_name)) < 0)
2025 return error;
2026
2027 if ((error = loose_path(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized))) < 0)
2028 return error;
2029
2030 if (!git_path_exists(git_buf_cstr(&old_path))) {
2031 error = GIT_ENOTFOUND;
2032 goto cleanup;
2033 }
2034
2035 /*
2036 * Move the reflog to a temporary place. This two-phase renaming is required
2037 * in order to cope with funny renaming use cases when one tries to move a reference
2038 * to a partially colliding namespace:
2039 * - a/b -> a/b/c
2040 * - a/b/c/d -> a/b/c
2041 */
2042 if ((error = loose_path(&temp_path, git_buf_cstr(&temp_path), "temp_reflog")) < 0)
2043 return error;
2044
2045 if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path), GIT_REFLOG_FILE_MODE)) < 0) {
2046 error = -1;
2047 goto cleanup;
2048 }
2049
2050 p_close(fd);
2051
2052 if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
2053 git_error_set(GIT_ERROR_OS, "failed to rename reflog for %s", new_name);
2054 error = -1;
2055 goto cleanup;
2056 }
2057
2058 if (git_path_isdir(git_buf_cstr(&new_path)) &&
2059 (git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
2060 error = -1;
2061 goto cleanup;
2062 }
2063
2064 if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
2065 error = -1;
2066 goto cleanup;
2067 }
2068
2069 if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
2070 git_error_set(GIT_ERROR_OS, "failed to rename reflog for %s", new_name);
2071 error = -1;
2072 }
2073
2074 cleanup:
2075 git_buf_dispose(&temp_path);
2076 git_buf_dispose(&old_path);
2077 git_buf_dispose(&new_path);
2078 git_buf_dispose(&normalized);
2079
2080 return error;
2081 }
2082
refdb_reflog_fs__delete(git_refdb_backend * _backend,const char * name)2083 static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name)
2084 {
2085 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
2086 git_buf path = GIT_BUF_INIT;
2087 int error;
2088
2089 GIT_ASSERT_ARG(_backend);
2090 GIT_ASSERT_ARG(name);
2091
2092 if ((error = reflog_path(&path, backend->repo, name)) < 0)
2093 goto out;
2094
2095 if (!git_path_exists(path.ptr))
2096 goto out;
2097
2098 if ((error = p_unlink(path.ptr)) < 0)
2099 goto out;
2100
2101 error = refdb_fs_backend__prune_refs(backend, name, GIT_REFLOG_DIR);
2102
2103 out:
2104 git_buf_dispose(&path);
2105
2106 return error;
2107 }
2108
git_refdb_backend_fs(git_refdb_backend ** backend_out,git_repository * repository)2109 int git_refdb_backend_fs(
2110 git_refdb_backend **backend_out,
2111 git_repository *repository)
2112 {
2113 int t = 0;
2114 git_buf gitpath = GIT_BUF_INIT;
2115 refdb_fs_backend *backend;
2116
2117 backend = git__calloc(1, sizeof(refdb_fs_backend));
2118 GIT_ERROR_CHECK_ALLOC(backend);
2119
2120 if (git_refdb_init_backend(&backend->parent, GIT_REFDB_BACKEND_VERSION) < 0)
2121 goto fail;
2122
2123 backend->repo = repository;
2124
2125 if (repository->gitdir) {
2126 backend->gitpath = setup_namespace(repository, repository->gitdir);
2127
2128 if (backend->gitpath == NULL)
2129 goto fail;
2130 }
2131
2132 if (repository->commondir) {
2133 backend->commonpath = setup_namespace(repository, repository->commondir);
2134
2135 if (backend->commonpath == NULL)
2136 goto fail;
2137 }
2138
2139 if (git_buf_joinpath(&gitpath, backend->commonpath, GIT_PACKEDREFS_FILE) < 0 ||
2140 git_sortedcache_new(
2141 &backend->refcache, offsetof(struct packref, name),
2142 NULL, NULL, packref_cmp, git_buf_cstr(&gitpath)) < 0)
2143 goto fail;
2144
2145 git_buf_dispose(&gitpath);
2146
2147 if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_IGNORECASE) && t) {
2148 backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE;
2149 backend->direach_flags |= GIT_PATH_DIR_IGNORE_CASE;
2150 }
2151 if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_PRECOMPOSE) && t) {
2152 backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
2153 backend->direach_flags |= GIT_PATH_DIR_PRECOMPOSE_UNICODE;
2154 }
2155 if ((!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_FSYNCOBJECTFILES) && t) ||
2156 git_repository__fsync_gitdir)
2157 backend->fsync = 1;
2158 backend->iterator_flags |= GIT_ITERATOR_DESCEND_SYMLINKS;
2159
2160 backend->parent.exists = &refdb_fs_backend__exists;
2161 backend->parent.lookup = &refdb_fs_backend__lookup;
2162 backend->parent.iterator = &refdb_fs_backend__iterator;
2163 backend->parent.write = &refdb_fs_backend__write;
2164 backend->parent.del = &refdb_fs_backend__delete;
2165 backend->parent.rename = &refdb_fs_backend__rename;
2166 backend->parent.compress = &refdb_fs_backend__compress;
2167 backend->parent.lock = &refdb_fs_backend__lock;
2168 backend->parent.unlock = &refdb_fs_backend__unlock;
2169 backend->parent.has_log = &refdb_reflog_fs__has_log;
2170 backend->parent.ensure_log = &refdb_reflog_fs__ensure_log;
2171 backend->parent.free = &refdb_fs_backend__free;
2172 backend->parent.reflog_read = &refdb_reflog_fs__read;
2173 backend->parent.reflog_write = &refdb_reflog_fs__write;
2174 backend->parent.reflog_rename = &refdb_reflog_fs__rename;
2175 backend->parent.reflog_delete = &refdb_reflog_fs__delete;
2176
2177 *backend_out = (git_refdb_backend *)backend;
2178 return 0;
2179
2180 fail:
2181 git_buf_dispose(&gitpath);
2182 git__free(backend->gitpath);
2183 git__free(backend->commonpath);
2184 git__free(backend);
2185 return -1;
2186 }
2187