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_UNUSED(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_UNUSED(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_UNUSED(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_puts(&path, 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 if (error == GIT_ENOTFOUND && old_id && git_oid_is_zero(old_id))
1178 return 0;
1179 goto out;
1180 }
1181
1182 /* If the types don't match, there's no way the values do */
1183 if (old_id && old_ref->type != GIT_REFERENCE_DIRECT) {
1184 *cmp = -1;
1185 goto out;
1186 }
1187 if (old_target && old_ref->type != GIT_REFERENCE_SYMBOLIC) {
1188 *cmp = 1;
1189 goto out;
1190 }
1191
1192 if (old_id && old_ref->type == GIT_REFERENCE_DIRECT)
1193 *cmp = git_oid_cmp(old_id, &old_ref->target.oid);
1194
1195 if (old_target && old_ref->type == GIT_REFERENCE_SYMBOLIC)
1196 *cmp = git__strcmp(old_target, old_ref->target.symbolic);
1197
1198 out:
1199 git_reference_free(old_ref);
1200
1201 return error;
1202 }
1203
1204 /*
1205 * The git.git comment regarding this, for your viewing pleasure:
1206 *
1207 * Special hack: If a branch is updated directly and HEAD
1208 * points to it (may happen on the remote side of a push
1209 * for example) then logically the HEAD reflog should be
1210 * updated too.
1211 * A generic solution implies reverse symref information,
1212 * but finding all symrefs pointing to the given branch
1213 * would be rather costly for this rare event (the direct
1214 * update of a branch) to be worth it. So let's cheat and
1215 * check with HEAD only which should cover 99% of all usage
1216 * scenarios (even 100% of the default ones).
1217 */
maybe_append_head(refdb_fs_backend * backend,const git_reference * ref,const git_signature * who,const char * message)1218 static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message)
1219 {
1220 git_reference *head = NULL;
1221 git_refdb *refdb = NULL;
1222 int error, write_reflog;
1223 git_oid old_id;
1224
1225 if ((error = git_repository_refdb(&refdb, backend->repo)) < 0 ||
1226 (error = git_refdb_should_write_head_reflog(&write_reflog, refdb, ref)) < 0)
1227 goto out;
1228 if (!write_reflog)
1229 goto out;
1230
1231 /* if we can't resolve, we use {0}*40 as old id */
1232 if (git_reference_name_to_id(&old_id, backend->repo, ref->name) < 0)
1233 memset(&old_id, 0, sizeof(old_id));
1234
1235 if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0 ||
1236 (error = reflog_append(backend, head, &old_id, git_reference_target(ref), who, message)) < 0)
1237 goto out;
1238
1239 out:
1240 git_reference_free(head);
1241 git_refdb_free(refdb);
1242 return error;
1243 }
1244
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)1245 static int refdb_fs_backend__write(
1246 git_refdb_backend *_backend,
1247 const git_reference *ref,
1248 int force,
1249 const git_signature *who,
1250 const char *message,
1251 const git_oid *old_id,
1252 const char *old_target)
1253 {
1254 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1255 git_filebuf file = GIT_FILEBUF_INIT;
1256 int error = 0;
1257
1258 GIT_ASSERT_ARG(backend);
1259
1260 if ((error = reference_path_available(backend, ref->name, NULL, force)) < 0)
1261 return error;
1262
1263 /* We need to perform the reflog append and old value check under the ref's lock */
1264 if ((error = loose_lock(&file, backend, ref->name)) < 0)
1265 return error;
1266
1267 return refdb_fs_backend__write_tail(_backend, ref, &file, true, old_id, old_target, who, message);
1268 }
1269
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)1270 static int refdb_fs_backend__write_tail(
1271 git_refdb_backend *_backend,
1272 const git_reference *ref,
1273 git_filebuf *file,
1274 int update_reflog,
1275 const git_oid *old_id,
1276 const char *old_target,
1277 const git_signature *who,
1278 const char *message)
1279 {
1280 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1281 int error = 0, cmp = 0, should_write;
1282 const char *new_target = NULL;
1283 const git_oid *new_id = NULL;
1284
1285 if ((error = cmp_old_ref(&cmp, _backend, ref->name, old_id, old_target)) < 0)
1286 goto on_error;
1287
1288 if (cmp) {
1289 git_error_set(GIT_ERROR_REFERENCE, "old reference value does not match");
1290 error = GIT_EMODIFIED;
1291 goto on_error;
1292 }
1293
1294 if (ref->type == GIT_REFERENCE_SYMBOLIC)
1295 new_target = ref->target.symbolic;
1296 else
1297 new_id = &ref->target.oid;
1298
1299 error = cmp_old_ref(&cmp, _backend, ref->name, new_id, new_target);
1300 if (error < 0 && error != GIT_ENOTFOUND)
1301 goto on_error;
1302
1303 /* Don't update if we have the same value */
1304 if (!error && !cmp) {
1305 error = 0;
1306 goto on_error; /* not really error */
1307 }
1308
1309 if (update_reflog) {
1310 git_refdb *refdb;
1311
1312 if ((error = git_repository_refdb__weakptr(&refdb, backend->repo)) < 0 ||
1313 (error = git_refdb_should_write_reflog(&should_write, refdb, ref)) < 0)
1314 goto on_error;
1315
1316 if (should_write) {
1317 if ((error = reflog_append(backend, ref, NULL, NULL, who, message)) < 0)
1318 goto on_error;
1319 if ((error = maybe_append_head(backend, ref, who, message)) < 0)
1320 goto on_error;
1321 }
1322 }
1323
1324 return loose_commit(file, ref);
1325
1326 on_error:
1327 git_filebuf_cleanup(file);
1328 return error;
1329 }
1330
refdb_fs_backend__prune_refs(refdb_fs_backend * backend,const char * ref_name,const char * prefix)1331 static int refdb_fs_backend__prune_refs(
1332 refdb_fs_backend *backend,
1333 const char *ref_name,
1334 const char *prefix)
1335 {
1336 git_buf relative_path = GIT_BUF_INIT;
1337 git_buf base_path = GIT_BUF_INIT;
1338 size_t commonlen;
1339 int error;
1340
1341 GIT_ASSERT_ARG(backend);
1342 GIT_ASSERT_ARG(ref_name);
1343
1344 if ((error = git_buf_sets(&relative_path, ref_name)) < 0)
1345 goto cleanup;
1346
1347 git_path_squash_slashes(&relative_path);
1348 if ((commonlen = git_path_common_dirlen("refs/heads/", git_buf_cstr(&relative_path))) == strlen("refs/heads/") ||
1349 (commonlen = git_path_common_dirlen("refs/tags/", git_buf_cstr(&relative_path))) == strlen("refs/tags/") ||
1350 (commonlen = git_path_common_dirlen("refs/remotes/", git_buf_cstr(&relative_path))) == strlen("refs/remotes/")) {
1351
1352 git_buf_truncate(&relative_path, commonlen);
1353
1354 if (prefix)
1355 error = git_buf_join3(&base_path, '/',
1356 backend->commonpath, prefix,
1357 git_buf_cstr(&relative_path));
1358 else
1359 error = git_buf_joinpath(&base_path,
1360 backend->commonpath,
1361 git_buf_cstr(&relative_path));
1362
1363 if (!error)
1364 error = git_path_validate_filesystem(base_path.ptr, base_path.size);
1365
1366 if (error < 0)
1367 goto cleanup;
1368
1369 error = git_futils_rmdir_r(ref_name + commonlen,
1370 git_buf_cstr(&base_path),
1371 GIT_RMDIR_EMPTY_PARENTS | GIT_RMDIR_SKIP_ROOT);
1372
1373 if (error == GIT_ENOTFOUND)
1374 error = 0;
1375 }
1376
1377 cleanup:
1378 git_buf_dispose(&relative_path);
1379 git_buf_dispose(&base_path);
1380 return error;
1381 }
1382
refdb_fs_backend__delete(git_refdb_backend * _backend,const char * ref_name,const git_oid * old_id,const char * old_target)1383 static int refdb_fs_backend__delete(
1384 git_refdb_backend *_backend,
1385 const char *ref_name,
1386 const git_oid *old_id, const char *old_target)
1387 {
1388 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1389 git_filebuf file = GIT_FILEBUF_INIT;
1390 int error = 0;
1391
1392 GIT_ASSERT_ARG(backend);
1393 GIT_ASSERT_ARG(ref_name);
1394
1395 if ((error = loose_lock(&file, backend, ref_name)) < 0)
1396 return error;
1397
1398 if ((error = refdb_reflog_fs__delete(_backend, ref_name)) < 0) {
1399 git_filebuf_cleanup(&file);
1400 return error;
1401 }
1402
1403 return refdb_fs_backend__delete_tail(_backend, &file, ref_name, old_id, old_target);
1404 }
1405
loose_delete(refdb_fs_backend * backend,const char * ref_name)1406 static int loose_delete(refdb_fs_backend *backend, const char *ref_name)
1407 {
1408 git_buf path = GIT_BUF_INIT;
1409 int error = 0;
1410
1411 if ((error = loose_path(&path, backend->commonpath, ref_name)) < 0)
1412 return error;
1413
1414 error = p_unlink(path.ptr);
1415 if (error < 0 && errno == ENOENT)
1416 error = GIT_ENOTFOUND;
1417 else if (error != 0)
1418 error = -1;
1419
1420 git_buf_dispose(&path);
1421
1422 return error;
1423 }
1424
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)1425 static int refdb_fs_backend__delete_tail(
1426 git_refdb_backend *_backend,
1427 git_filebuf *file,
1428 const char *ref_name,
1429 const git_oid *old_id, const char *old_target)
1430 {
1431 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1432 int error = 0, cmp = 0;
1433 bool packed_deleted = 0;
1434
1435 error = cmp_old_ref(&cmp, _backend, ref_name, old_id, old_target);
1436 if (error < 0)
1437 goto cleanup;
1438
1439 if (cmp) {
1440 git_error_set(GIT_ERROR_REFERENCE, "old reference value does not match");
1441 error = GIT_EMODIFIED;
1442 goto cleanup;
1443 }
1444
1445 /*
1446 * To ensure that an external observer will see either the current ref value
1447 * (because the loose ref still exists), or a missing ref (after the packed-file is
1448 * unlocked, there will be nothing left), we must ensure things happen in the
1449 * following order:
1450 *
1451 * - the packed-ref file is locked and loaded, as well as a loose one, if it exists
1452 * - we optimistically delete a packed ref, keeping track of whether it existed
1453 * - we delete the loose ref, note that we have its .lock
1454 * - the loose ref is "unlocked", then the packed-ref file is rewritten and unlocked
1455 * - we should prune the path components if a loose ref was deleted
1456 *
1457 * Note that, because our packed backend doesn't expose its filesystem lock,
1458 * we might not be able to guarantee that this is what actually happens (ie.
1459 * as our current code never write packed-refs.lock, nothing stops observers
1460 * from grabbing a "stale" value from there).
1461 */
1462 if ((error = packed_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND)
1463 goto cleanup;
1464
1465 if (error == 0)
1466 packed_deleted = 1;
1467
1468 if ((error = loose_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND)
1469 goto cleanup;
1470
1471 if (error == GIT_ENOTFOUND) {
1472 error = packed_deleted ? 0 : ref_error_notfound(ref_name);
1473 goto cleanup;
1474 }
1475
1476 cleanup:
1477 git_filebuf_cleanup(file);
1478 if (error == 0)
1479 error = refdb_fs_backend__prune_refs(backend, ref_name, "");
1480 return error;
1481 }
1482
1483 static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name);
1484
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)1485 static int refdb_fs_backend__rename(
1486 git_reference **out,
1487 git_refdb_backend *_backend,
1488 const char *old_name,
1489 const char *new_name,
1490 int force,
1491 const git_signature *who,
1492 const char *message)
1493 {
1494 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1495 git_reference *old, *new = NULL;
1496 git_filebuf file = GIT_FILEBUF_INIT;
1497 int error;
1498
1499 GIT_ASSERT_ARG(backend);
1500
1501 if ((error = reference_path_available(
1502 backend, new_name, old_name, force)) < 0 ||
1503 (error = refdb_fs_backend__lookup(&old, _backend, old_name)) < 0)
1504 return error;
1505
1506 if ((error = refdb_fs_backend__delete(_backend, old_name, NULL, NULL)) < 0) {
1507 git_reference_free(old);
1508 return error;
1509 }
1510
1511 new = git_reference__realloc(&old, new_name);
1512 if (!new) {
1513 git_reference_free(old);
1514 return -1;
1515 }
1516
1517 if ((error = loose_lock(&file, backend, new->name)) < 0) {
1518 git_reference_free(new);
1519 return error;
1520 }
1521
1522 /* Try to rename the refog; it's ok if the old doesn't exist */
1523 error = refdb_reflog_fs__rename(_backend, old_name, new_name);
1524 if (((error == 0) || (error == GIT_ENOTFOUND)) &&
1525 ((error = reflog_append(backend, new, git_reference_target(new), NULL, who, message)) < 0)) {
1526 git_reference_free(new);
1527 git_filebuf_cleanup(&file);
1528 return error;
1529 }
1530
1531 if (error < 0) {
1532 git_reference_free(new);
1533 git_filebuf_cleanup(&file);
1534 return error;
1535 }
1536
1537
1538 if ((error = loose_commit(&file, new)) < 0 || out == NULL) {
1539 git_reference_free(new);
1540 return error;
1541 }
1542
1543 *out = new;
1544 return 0;
1545 }
1546
refdb_fs_backend__compress(git_refdb_backend * _backend)1547 static int refdb_fs_backend__compress(git_refdb_backend *_backend)
1548 {
1549 int error;
1550 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1551
1552 GIT_ASSERT_ARG(backend);
1553
1554 if ((error = packed_reload(backend)) < 0 || /* load the existing packfile */
1555 (error = packed_loadloose(backend)) < 0 || /* add all the loose refs */
1556 (error = packed_write(backend)) < 0) /* write back to disk */
1557 return error;
1558
1559 return 0;
1560 }
1561
refdb_fs_backend__free(git_refdb_backend * _backend)1562 static void refdb_fs_backend__free(git_refdb_backend *_backend)
1563 {
1564 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1565
1566 if (!backend)
1567 return;
1568
1569 git_sortedcache_free(backend->refcache);
1570 git__free(backend->gitpath);
1571 git__free(backend->commonpath);
1572 git__free(backend);
1573 }
1574
setup_namespace(git_repository * repo,const char * in)1575 static char *setup_namespace(git_repository *repo, const char *in)
1576 {
1577 git_buf path = GIT_BUF_INIT;
1578 char *parts, *start, *end, *out = NULL;
1579
1580 if (!in)
1581 goto done;
1582
1583 git_buf_puts(&path, in);
1584
1585 /* if the repo is not namespaced, nothing else to do */
1586 if (repo->namespace == NULL) {
1587 out = git_buf_detach(&path);
1588 goto done;
1589 }
1590
1591 parts = end = git__strdup(repo->namespace);
1592 if (parts == NULL)
1593 goto done;
1594
1595 /*
1596 * From `man gitnamespaces`:
1597 * namespaces which include a / will expand to a hierarchy
1598 * of namespaces; for example, GIT_NAMESPACE=foo/bar will store
1599 * refs under refs/namespaces/foo/refs/namespaces/bar/
1600 */
1601 while ((start = git__strsep(&end, "/")) != NULL)
1602 git_buf_printf(&path, "refs/namespaces/%s/", start);
1603
1604 git_buf_printf(&path, "refs/namespaces/%s/refs", end);
1605 git__free(parts);
1606
1607 /* Make sure that the folder with the namespace exists */
1608 if (git_futils_mkdir_relative(git_buf_cstr(&path), in, 0777,
1609 GIT_MKDIR_PATH, NULL) < 0)
1610 goto done;
1611
1612 /* Return root of the namespaced gitpath, i.e. without the trailing 'refs' */
1613 git_buf_rtruncate_at_char(&path, '/');
1614 git_buf_putc(&path, '/');
1615 out = git_buf_detach(&path);
1616
1617 done:
1618 git_buf_dispose(&path);
1619 return out;
1620 }
1621
reflog_alloc(git_reflog ** reflog,const char * name)1622 static int reflog_alloc(git_reflog **reflog, const char *name)
1623 {
1624 git_reflog *log;
1625
1626 *reflog = NULL;
1627
1628 log = git__calloc(1, sizeof(git_reflog));
1629 GIT_ERROR_CHECK_ALLOC(log);
1630
1631 log->ref_name = git__strdup(name);
1632 GIT_ERROR_CHECK_ALLOC(log->ref_name);
1633
1634 if (git_vector_init(&log->entries, 0, NULL) < 0) {
1635 git__free(log->ref_name);
1636 git__free(log);
1637 return -1;
1638 }
1639
1640 *reflog = log;
1641
1642 return 0;
1643 }
1644
reflog_parse(git_reflog * log,const char * buf,size_t buf_size)1645 static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
1646 {
1647 git_parse_ctx parser = GIT_PARSE_CTX_INIT;
1648
1649 if ((git_parse_ctx_init(&parser, buf, buf_size)) < 0)
1650 return -1;
1651
1652 for (; parser.remain_len; git_parse_advance_line(&parser)) {
1653 git_reflog_entry *entry;
1654 const char *sig;
1655 char c;
1656
1657 entry = git__calloc(1, sizeof(*entry));
1658 GIT_ERROR_CHECK_ALLOC(entry);
1659 entry->committer = git__calloc(1, sizeof(*entry->committer));
1660 GIT_ERROR_CHECK_ALLOC(entry->committer);
1661
1662 if (git_parse_advance_oid(&entry->oid_old, &parser) < 0 ||
1663 git_parse_advance_expected(&parser, " ", 1) < 0 ||
1664 git_parse_advance_oid(&entry->oid_cur, &parser) < 0)
1665 goto next;
1666
1667 sig = parser.line;
1668 while (git_parse_peek(&c, &parser, 0) == 0 && c != '\t' && c != '\n')
1669 git_parse_advance_chars(&parser, 1);
1670
1671 if (git_signature__parse(entry->committer, &sig, parser.line, NULL, 0) < 0)
1672 goto next;
1673
1674 if (c == '\t') {
1675 size_t len;
1676 git_parse_advance_chars(&parser, 1);
1677
1678 len = parser.line_len;
1679 if (parser.line[len - 1] == '\n')
1680 len--;
1681
1682 entry->msg = git__strndup(parser.line, len);
1683 GIT_ERROR_CHECK_ALLOC(entry->msg);
1684 }
1685
1686 if ((git_vector_insert(&log->entries, entry)) < 0) {
1687 git_reflog_entry__free(entry);
1688 return -1;
1689 }
1690
1691 continue;
1692
1693 next:
1694 git_reflog_entry__free(entry);
1695 }
1696
1697 return 0;
1698 }
1699
create_new_reflog_file(const char * filepath)1700 static int create_new_reflog_file(const char *filepath)
1701 {
1702 int fd, error;
1703
1704 if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
1705 return error;
1706
1707 if ((fd = p_open(filepath,
1708 O_WRONLY | O_CREAT,
1709 GIT_REFLOG_FILE_MODE)) < 0)
1710 return -1;
1711
1712 return p_close(fd);
1713 }
1714
refdb_reflog_fs__ensure_log(git_refdb_backend * _backend,const char * name)1715 static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name)
1716 {
1717 refdb_fs_backend *backend;
1718 git_repository *repo;
1719 git_buf path = GIT_BUF_INIT;
1720 int error;
1721
1722 GIT_ASSERT_ARG(_backend && name);
1723
1724 backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1725 repo = backend->repo;
1726
1727 if ((error = reflog_path(&path, repo, name)) < 0)
1728 return error;
1729
1730 error = create_new_reflog_file(git_buf_cstr(&path));
1731 git_buf_dispose(&path);
1732
1733 return error;
1734 }
1735
has_reflog(git_repository * repo,const char * name)1736 static int has_reflog(git_repository *repo, const char *name)
1737 {
1738 int ret = 0;
1739 git_buf path = GIT_BUF_INIT;
1740
1741 if (reflog_path(&path, repo, name) < 0)
1742 goto cleanup;
1743
1744 ret = git_path_isfile(git_buf_cstr(&path));
1745
1746 cleanup:
1747 git_buf_dispose(&path);
1748 return ret;
1749 }
1750
refdb_reflog_fs__has_log(git_refdb_backend * _backend,const char * name)1751 static int refdb_reflog_fs__has_log(git_refdb_backend *_backend, const char *name)
1752 {
1753 refdb_fs_backend *backend;
1754
1755 GIT_ASSERT_ARG(_backend);
1756 GIT_ASSERT_ARG(name);
1757
1758 backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1759
1760 return has_reflog(backend->repo, name);
1761 }
1762
refdb_reflog_fs__read(git_reflog ** out,git_refdb_backend * _backend,const char * name)1763 static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
1764 {
1765 int error = -1;
1766 git_buf log_path = GIT_BUF_INIT;
1767 git_buf log_file = GIT_BUF_INIT;
1768 git_reflog *log = NULL;
1769 git_repository *repo;
1770 refdb_fs_backend *backend;
1771
1772 GIT_ASSERT_ARG(out);
1773 GIT_ASSERT_ARG(_backend);
1774 GIT_ASSERT_ARG(name);
1775
1776 backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1777 repo = backend->repo;
1778
1779 if (reflog_alloc(&log, name) < 0)
1780 return -1;
1781
1782 if (reflog_path(&log_path, repo, name) < 0)
1783 goto cleanup;
1784
1785 error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
1786 if (error < 0 && error != GIT_ENOTFOUND)
1787 goto cleanup;
1788
1789 if ((error == GIT_ENOTFOUND) &&
1790 ((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
1791 goto cleanup;
1792
1793 if ((error = reflog_parse(log,
1794 git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
1795 goto cleanup;
1796
1797 *out = log;
1798 goto success;
1799
1800 cleanup:
1801 git_reflog_free(log);
1802
1803 success:
1804 git_buf_dispose(&log_file);
1805 git_buf_dispose(&log_path);
1806
1807 return error;
1808 }
1809
serialize_reflog_entry(git_buf * buf,const git_oid * oid_old,const git_oid * oid_new,const git_signature * committer,const char * msg)1810 static int serialize_reflog_entry(
1811 git_buf *buf,
1812 const git_oid *oid_old,
1813 const git_oid *oid_new,
1814 const git_signature *committer,
1815 const char *msg)
1816 {
1817 char raw_old[GIT_OID_HEXSZ+1];
1818 char raw_new[GIT_OID_HEXSZ+1];
1819
1820 git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
1821 git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
1822
1823 git_buf_clear(buf);
1824
1825 git_buf_puts(buf, raw_old);
1826 git_buf_putc(buf, ' ');
1827 git_buf_puts(buf, raw_new);
1828
1829 git_signature__writebuf(buf, " ", committer);
1830
1831 /* drop trailing LF */
1832 git_buf_rtrim(buf);
1833
1834 if (msg) {
1835 size_t i;
1836
1837 git_buf_putc(buf, '\t');
1838 git_buf_puts(buf, msg);
1839
1840 for (i = 0; i < buf->size - 2; i++)
1841 if (buf->ptr[i] == '\n')
1842 buf->ptr[i] = ' ';
1843 git_buf_rtrim(buf);
1844 }
1845
1846 git_buf_putc(buf, '\n');
1847
1848 return git_buf_oom(buf);
1849 }
1850
lock_reflog(git_filebuf * file,refdb_fs_backend * backend,const char * refname)1851 static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char *refname)
1852 {
1853 git_repository *repo;
1854 git_buf log_path = GIT_BUF_INIT;
1855 int error;
1856
1857 repo = backend->repo;
1858
1859 if (!git_path_validate(backend->repo, refname, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
1860 git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", refname);
1861 return GIT_EINVALIDSPEC;
1862 }
1863
1864 if (reflog_path(&log_path, repo, refname) < 0)
1865 return -1;
1866
1867 if (!git_path_isfile(git_buf_cstr(&log_path))) {
1868 git_error_set(GIT_ERROR_INVALID,
1869 "log file for reference '%s' doesn't exist", refname);
1870 error = -1;
1871 goto cleanup;
1872 }
1873
1874 error = git_filebuf_open(file, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE);
1875
1876 cleanup:
1877 git_buf_dispose(&log_path);
1878
1879 return error;
1880 }
1881
refdb_reflog_fs__write(git_refdb_backend * _backend,git_reflog * reflog)1882 static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog)
1883 {
1884 int error = -1;
1885 unsigned int i;
1886 git_reflog_entry *entry;
1887 refdb_fs_backend *backend;
1888 git_buf log = GIT_BUF_INIT;
1889 git_filebuf fbuf = GIT_FILEBUF_INIT;
1890
1891 GIT_ASSERT_ARG(_backend);
1892 GIT_ASSERT_ARG(reflog);
1893
1894 backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1895
1896 if ((error = lock_reflog(&fbuf, backend, reflog->ref_name)) < 0)
1897 return -1;
1898
1899 git_vector_foreach(&reflog->entries, i, entry) {
1900 if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
1901 goto cleanup;
1902
1903 if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
1904 goto cleanup;
1905 }
1906
1907 error = git_filebuf_commit(&fbuf);
1908 goto success;
1909
1910 cleanup:
1911 git_filebuf_cleanup(&fbuf);
1912
1913 success:
1914 git_buf_dispose(&log);
1915
1916 return error;
1917 }
1918
1919 /* 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)1920 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)
1921 {
1922 int error, is_symbolic, open_flags;
1923 git_oid old_id = {{0}}, new_id = {{0}};
1924 git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
1925 git_repository *repo = backend->repo;
1926
1927 is_symbolic = ref->type == GIT_REFERENCE_SYMBOLIC;
1928
1929 /* "normal" symbolic updates do not write */
1930 if (is_symbolic &&
1931 strcmp(ref->name, GIT_HEAD_FILE) &&
1932 !(old && new))
1933 return 0;
1934
1935 /* From here on is_symbolic also means that it's HEAD */
1936
1937 if (old) {
1938 git_oid_cpy(&old_id, old);
1939 } else {
1940 error = git_reference_name_to_id(&old_id, repo, ref->name);
1941 if (error < 0 && error != GIT_ENOTFOUND)
1942 return error;
1943 }
1944
1945 if (new) {
1946 git_oid_cpy(&new_id, new);
1947 } else {
1948 if (!is_symbolic) {
1949 git_oid_cpy(&new_id, git_reference_target(ref));
1950 } else {
1951 error = git_reference_name_to_id(&new_id, repo, git_reference_symbolic_target(ref));
1952 if (error < 0 && error != GIT_ENOTFOUND)
1953 return error;
1954 /* detaching HEAD does not create an entry */
1955 if (error == GIT_ENOTFOUND)
1956 return 0;
1957
1958 git_error_clear();
1959 }
1960 }
1961
1962 if ((error = serialize_reflog_entry(&buf, &old_id, &new_id, who, message)) < 0)
1963 goto cleanup;
1964
1965 if ((error = reflog_path(&path, repo, ref->name)) < 0)
1966 goto cleanup;
1967
1968 if (((error = git_futils_mkpath2file(git_buf_cstr(&path), 0777)) < 0) &&
1969 (error != GIT_EEXISTS)) {
1970 goto cleanup;
1971 }
1972
1973 /* If the new branch matches part of the namespace of a previously deleted branch,
1974 * there maybe an obsolete/unused directory (or directory hierarchy) in the way.
1975 */
1976 if (git_path_isdir(git_buf_cstr(&path))) {
1977 if ((error = git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY)) < 0) {
1978 if (error == GIT_ENOTFOUND)
1979 error = 0;
1980 } else if (git_path_isdir(git_buf_cstr(&path))) {
1981 git_error_set(GIT_ERROR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder",
1982 ref->name);
1983 error = GIT_EDIRECTORY;
1984 }
1985
1986 if (error != 0)
1987 goto cleanup;
1988 }
1989
1990 open_flags = O_WRONLY | O_CREAT | O_APPEND;
1991
1992 if (backend->fsync)
1993 open_flags |= O_FSYNC;
1994
1995 error = git_futils_writebuffer(&buf, git_buf_cstr(&path), open_flags, GIT_REFLOG_FILE_MODE);
1996
1997 cleanup:
1998 git_buf_dispose(&buf);
1999 git_buf_dispose(&path);
2000
2001 return error;
2002 }
2003
refdb_reflog_fs__rename(git_refdb_backend * _backend,const char * old_name,const char * new_name)2004 static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name)
2005 {
2006 int error = 0, fd;
2007 git_buf old_path = GIT_BUF_INIT;
2008 git_buf new_path = GIT_BUF_INIT;
2009 git_buf temp_path = GIT_BUF_INIT;
2010 git_buf normalized = GIT_BUF_INIT;
2011 git_repository *repo;
2012 refdb_fs_backend *backend;
2013
2014 GIT_ASSERT_ARG(_backend);
2015 GIT_ASSERT_ARG(old_name);
2016 GIT_ASSERT_ARG(new_name);
2017
2018 backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
2019 repo = backend->repo;
2020
2021 if ((error = git_reference__normalize_name(
2022 &normalized, new_name, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL)) < 0)
2023 return error;
2024
2025 if (git_buf_joinpath(&temp_path, repo->gitdir, GIT_REFLOG_DIR) < 0)
2026 return -1;
2027
2028 if ((error = loose_path(&old_path, git_buf_cstr(&temp_path), old_name)) < 0)
2029 return error;
2030
2031 if ((error = loose_path(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized))) < 0)
2032 return error;
2033
2034 if (!git_path_exists(git_buf_cstr(&old_path))) {
2035 error = GIT_ENOTFOUND;
2036 goto cleanup;
2037 }
2038
2039 /*
2040 * Move the reflog to a temporary place. This two-phase renaming is required
2041 * in order to cope with funny renaming use cases when one tries to move a reference
2042 * to a partially colliding namespace:
2043 * - a/b -> a/b/c
2044 * - a/b/c/d -> a/b/c
2045 */
2046 if ((error = loose_path(&temp_path, git_buf_cstr(&temp_path), "temp_reflog")) < 0)
2047 return error;
2048
2049 if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path), GIT_REFLOG_FILE_MODE)) < 0) {
2050 error = -1;
2051 goto cleanup;
2052 }
2053
2054 p_close(fd);
2055
2056 if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
2057 git_error_set(GIT_ERROR_OS, "failed to rename reflog for %s", new_name);
2058 error = -1;
2059 goto cleanup;
2060 }
2061
2062 if (git_path_isdir(git_buf_cstr(&new_path)) &&
2063 (git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
2064 error = -1;
2065 goto cleanup;
2066 }
2067
2068 if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
2069 error = -1;
2070 goto cleanup;
2071 }
2072
2073 if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
2074 git_error_set(GIT_ERROR_OS, "failed to rename reflog for %s", new_name);
2075 error = -1;
2076 }
2077
2078 cleanup:
2079 git_buf_dispose(&temp_path);
2080 git_buf_dispose(&old_path);
2081 git_buf_dispose(&new_path);
2082 git_buf_dispose(&normalized);
2083
2084 return error;
2085 }
2086
refdb_reflog_fs__delete(git_refdb_backend * _backend,const char * name)2087 static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name)
2088 {
2089 refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
2090 git_buf path = GIT_BUF_INIT;
2091 int error;
2092
2093 GIT_ASSERT_ARG(_backend);
2094 GIT_ASSERT_ARG(name);
2095
2096 if ((error = reflog_path(&path, backend->repo, name)) < 0)
2097 goto out;
2098
2099 if (!git_path_exists(path.ptr))
2100 goto out;
2101
2102 if ((error = p_unlink(path.ptr)) < 0)
2103 goto out;
2104
2105 error = refdb_fs_backend__prune_refs(backend, name, GIT_REFLOG_DIR);
2106
2107 out:
2108 git_buf_dispose(&path);
2109
2110 return error;
2111 }
2112
git_refdb_backend_fs(git_refdb_backend ** backend_out,git_repository * repository)2113 int git_refdb_backend_fs(
2114 git_refdb_backend **backend_out,
2115 git_repository *repository)
2116 {
2117 int t = 0;
2118 git_buf gitpath = GIT_BUF_INIT;
2119 refdb_fs_backend *backend;
2120
2121 backend = git__calloc(1, sizeof(refdb_fs_backend));
2122 GIT_ERROR_CHECK_ALLOC(backend);
2123
2124 if (git_refdb_init_backend(&backend->parent, GIT_REFDB_BACKEND_VERSION) < 0)
2125 goto fail;
2126
2127 backend->repo = repository;
2128
2129 if (repository->gitdir) {
2130 backend->gitpath = setup_namespace(repository, repository->gitdir);
2131
2132 if (backend->gitpath == NULL)
2133 goto fail;
2134 }
2135
2136 if (repository->commondir) {
2137 backend->commonpath = setup_namespace(repository, repository->commondir);
2138
2139 if (backend->commonpath == NULL)
2140 goto fail;
2141 }
2142
2143 if (git_buf_joinpath(&gitpath, backend->commonpath, GIT_PACKEDREFS_FILE) < 0 ||
2144 git_sortedcache_new(
2145 &backend->refcache, offsetof(struct packref, name),
2146 NULL, NULL, packref_cmp, git_buf_cstr(&gitpath)) < 0)
2147 goto fail;
2148
2149 git_buf_dispose(&gitpath);
2150
2151 if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_IGNORECASE) && t) {
2152 backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE;
2153 backend->direach_flags |= GIT_PATH_DIR_IGNORE_CASE;
2154 }
2155 if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_PRECOMPOSE) && t) {
2156 backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
2157 backend->direach_flags |= GIT_PATH_DIR_PRECOMPOSE_UNICODE;
2158 }
2159 if ((!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_FSYNCOBJECTFILES) && t) ||
2160 git_repository__fsync_gitdir)
2161 backend->fsync = 1;
2162 backend->iterator_flags |= GIT_ITERATOR_DESCEND_SYMLINKS;
2163
2164 backend->parent.exists = &refdb_fs_backend__exists;
2165 backend->parent.lookup = &refdb_fs_backend__lookup;
2166 backend->parent.iterator = &refdb_fs_backend__iterator;
2167 backend->parent.write = &refdb_fs_backend__write;
2168 backend->parent.del = &refdb_fs_backend__delete;
2169 backend->parent.rename = &refdb_fs_backend__rename;
2170 backend->parent.compress = &refdb_fs_backend__compress;
2171 backend->parent.lock = &refdb_fs_backend__lock;
2172 backend->parent.unlock = &refdb_fs_backend__unlock;
2173 backend->parent.has_log = &refdb_reflog_fs__has_log;
2174 backend->parent.ensure_log = &refdb_reflog_fs__ensure_log;
2175 backend->parent.free = &refdb_fs_backend__free;
2176 backend->parent.reflog_read = &refdb_reflog_fs__read;
2177 backend->parent.reflog_write = &refdb_reflog_fs__write;
2178 backend->parent.reflog_rename = &refdb_reflog_fs__rename;
2179 backend->parent.reflog_delete = &refdb_reflog_fs__delete;
2180
2181 *backend_out = (git_refdb_backend *)backend;
2182 return 0;
2183
2184 fail:
2185 git_buf_dispose(&gitpath);
2186 git__free(backend->gitpath);
2187 git__free(backend->commonpath);
2188 git__free(backend);
2189 return -1;
2190 }
2191