File Coverage

deps/libgit2/src/refdb_fs.c
Criterion Covered Total %
statement 634 1048 60.5
branch 313 702 44.5
condition n/a
subroutine n/a
pod n/a
total 947 1750 54.1


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