File Coverage

deps/libgit2/src/refdb_fs.c
Criterion Covered Total %
statement 611 1014 60.2
branch 296 682 43.4
condition n/a
subroutine n/a
pod n/a
total 907 1696 53.4


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 334           static int packed_reload(refdb_fs_backend *backend)
78             {
79             int error;
80 334           git_buf packedrefs = GIT_BUF_INIT;
81             char *scan, *eof, *eol;
82              
83 334 50         if (!backend->gitpath)
84 0           return 0;
85              
86 334           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 334 50         if (error <= 0) {
95 334 50         if (error == GIT_ENOTFOUND) {
96 334           git_sortedcache_clear(backend->refcache, true);
97 334           git_error_clear();
98 334           error = 0;
99             }
100 334           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 334           return -1;
198             }
199              
200 801           static int loose_parse_oid(
201             git_oid *oid, const char *filename, git_buf *file_content)
202             {
203 801           const char *str = git_buf_cstr(file_content);
204              
205 801 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 801 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 801           str += GIT_OID_HEXSZ;
214 801 50         if (*str == '\0' || git__isspace(*str))
    50          
215 801           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 1451           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 1451 50         if ((error = git_buf_joinpath(buf, base, path)) < 0 ||
    100          
228 1451           (error = git_futils_readbuffer(buf, buf->ptr)) < 0)
229 166           git_buf_dispose(buf);
230              
231 1451           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 484           static const char *loose_parse_symbolic(git_buf *file_content)
360             {
361 484           const unsigned int header_len = (unsigned int)strlen(GIT_SYMREF);
362             const char *refname_start;
363              
364 484           refname_start = (const char *)file_content->ptr;
365              
366 484 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 484           refname_start += header_len;
376              
377 484           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 1559           static bool is_per_worktree_ref(const char *ref_name)
388             {
389 2561           return git__prefixcmp(ref_name, "refs/") != 0 ||
390 1002           git__prefixcmp(ref_name, "refs/bisect/") == 0;
391             }
392              
393 1451           static int loose_lookup(
394             git_reference **out,
395             refdb_fs_backend *backend,
396             const char *ref_name)
397             {
398 1451           git_buf ref_file = GIT_BUF_INIT;
399 1451           int error = 0;
400             const char *ref_dir;
401              
402 1451 100         if (out)
403 1334           *out = NULL;
404              
405 1451 100         if (is_per_worktree_ref(ref_name))
406 530           ref_dir = backend->gitpath;
407             else
408 921           ref_dir = backend->commonpath;
409              
410 1451 100         if ((error = loose_readbuffer(&ref_file, ref_dir, ref_name)) < 0)
411             /* cannot read loose ref file - gah */;
412 1285 100         else if (git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF) == 0) {
413             const char *target;
414              
415 484           git_buf_rtrim(&ref_file);
416              
417 484 50         if (!(target = loose_parse_symbolic(&ref_file)))
418 0           error = -1;
419 484 50         else if (out != NULL)
420 484           *out = git_reference__alloc_symbolic(ref_name, target);
421             } else {
422             git_oid oid;
423              
424 801 50         if (!(error = loose_parse_oid(&oid, ref_name, &ref_file)) &&
    100          
425             out != NULL)
426 801           *out = git_reference__alloc(ref_name, &oid, NULL);
427             }
428              
429 1451           git_buf_dispose(&ref_file);
430 1451           return error;
431             }
432              
433 166           static int ref_error_notfound(const char *name)
434             {
435 166           git_error_set(GIT_ERROR_REFERENCE, "reference '%s' not found", name);
436 166           return GIT_ENOTFOUND;
437             }
438              
439 166           static int packed_lookup(
440             git_reference **out,
441             refdb_fs_backend *backend,
442             const char *ref_name)
443             {
444 166           int error = 0;
445             struct packref *entry;
446              
447 166 50         if ((error = packed_reload(backend)) < 0)
448 0           return error;
449              
450 166 50         if (git_sortedcache_rlock(backend->refcache) < 0)
451 0           return -1;
452              
453 166           entry = git_sortedcache_lookup(backend->refcache, ref_name);
454 166 50         if (!entry) {
455 166           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 166           git_sortedcache_runlock(backend->refcache);
463              
464 166           return error;
465             }
466              
467 1323           static int refdb_fs_backend__lookup(
468             git_reference **out,
469             git_refdb_backend *_backend,
470             const char *ref_name)
471             {
472 1323           refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
473             int error;
474              
475 1323 50         assert(backend);
476              
477 1323 100         if (!(error = loose_lookup(out, backend, ref_name)))
478 1157           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 166 50         if (error == GIT_ENOTFOUND) {
483 166           git_error_clear();
484 166           error = packed_lookup(out, backend, ref_name);
485             }
486              
487 166           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 50         if ((error = git_pool_init(&iter->pool, 1)) < 0)
687 0           goto out;
688              
689 32 50         if ((error = git_vector_init(&iter->loose, 8, NULL)) < 0)
690 0           goto out;
691              
692 35           if (glob != NULL &&
693 3           (iter->glob = git_pool_strdup(&iter->pool, glob)) == NULL) {
694 0           error = GIT_ERROR_NOMEMORY;
695 0           goto out;
696             }
697              
698 32 50         if ((error = iter_load_loose_paths(backend, iter)) < 0)
699 0           goto out;
700              
701 32 50         if ((error = packed_reload(backend)) < 0)
702 0           goto out;
703              
704 32 50         if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
705 0           goto out;
706              
707 32           iter->parent.next = refdb_fs_backend__iterator_next;
708 32           iter->parent.next_name = refdb_fs_backend__iterator_next_name;
709 32           iter->parent.free = refdb_fs_backend__iterator_free;
710              
711 32           *out = (git_reference_iterator *)iter;
712             out:
713 32 50         if (error)
714 0           refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
715 32           return error;
716             }
717              
718 0           static bool ref_is_available(
719             const char *old_ref, const char *new_ref, const char *this_ref)
720             {
721 0 0         if (old_ref == NULL || strcmp(old_ref, this_ref)) {
    0          
722 0           size_t reflen = strlen(this_ref);
723 0           size_t newlen = strlen(new_ref);
724 0           size_t cmplen = reflen < newlen ? reflen : newlen;
725 0 0         const char *lead = reflen < newlen ? new_ref : this_ref;
726              
727 0 0         if (!strncmp(new_ref, this_ref, cmplen) && lead[cmplen] == '/') {
    0          
728 0           return false;
729             }
730             }
731              
732 0           return true;
733             }
734              
735 100           static int reference_path_available(
736             refdb_fs_backend *backend,
737             const char *new_ref,
738             const char* old_ref,
739             int force)
740             {
741             size_t i;
742             int error;
743              
744 100 50         if ((error = packed_reload(backend)) < 0)
745 0           return error;
746              
747 100 100         if (!force) {
748             int exists;
749              
750 30 50         if ((error = refdb_fs_backend__exists(
751             &exists, (git_refdb_backend *)backend, new_ref)) < 0) {
752 2           return error;
753             }
754              
755 30 100         if (exists) {
756 2           git_error_set(GIT_ERROR_REFERENCE,
757             "failed to write reference '%s': a reference with "
758             "that name already exists.", new_ref);
759 30           return GIT_EEXISTS;
760             }
761             }
762              
763 98           git_sortedcache_rlock(backend->refcache);
764              
765 98 50         for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
766 0           struct packref *ref = git_sortedcache_entry(backend->refcache, i);
767              
768 0 0         if (ref && !ref_is_available(old_ref, new_ref, ref->name)) {
    0          
769 0           git_sortedcache_runlock(backend->refcache);
770 0           git_error_set(GIT_ERROR_REFERENCE,
771             "path to reference '%s' collides with existing one", new_ref);
772 0           return -1;
773             }
774             }
775              
776 98           git_sortedcache_runlock(backend->refcache);
777 98           return 0;
778             }
779              
780 108           static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name)
781             {
782             int error, filebuf_flags;
783 108           git_buf ref_path = GIT_BUF_INIT;
784             const char *basedir;
785              
786 108 50         assert(file && backend && name);
    50          
    50          
787              
788 108 50         if (!git_path_isvalid(backend->repo, name, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
789 0           git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", name);
790 0           return GIT_EINVALIDSPEC;
791             }
792              
793 108 100         if (is_per_worktree_ref(name))
794 27           basedir = backend->gitpath;
795             else
796 81           basedir = backend->commonpath;
797              
798             /* Remove a possibly existing empty directory hierarchy
799             * which name would collide with the reference name
800             */
801 108 50         if ((error = git_futils_rmdir_r(name, basedir, GIT_RMDIR_SKIP_NONEMPTY)) < 0)
802 0           return error;
803              
804 108 50         if (git_buf_joinpath(&ref_path, basedir, name) < 0)
805 0           return -1;
806              
807 108           filebuf_flags = GIT_FILEBUF_CREATE_LEADING_DIRS;
808 108 50         if (backend->fsync)
809 0           filebuf_flags |= GIT_FILEBUF_FSYNC;
810              
811 108           error = git_filebuf_open(file, ref_path.ptr, filebuf_flags, GIT_REFS_FILE_MODE);
812              
813 108 50         if (error == GIT_EDIRECTORY)
814 0           git_error_set(GIT_ERROR_REFERENCE, "cannot lock ref '%s', there are refs beneath that folder", name);
815              
816 108           git_buf_dispose(&ref_path);
817 108           return error;
818             }
819              
820 93           static int loose_commit(git_filebuf *file, const git_reference *ref)
821             {
822 93 50         assert(file && ref);
    50          
823              
824 93 100         if (ref->type == GIT_REFERENCE_DIRECT) {
825             char oid[GIT_OID_HEXSZ + 1];
826 73           git_oid_nfmt(oid, sizeof(oid), &ref->target.oid);
827              
828 73           git_filebuf_printf(file, "%s\n", oid);
829 20 50         } else if (ref->type == GIT_REFERENCE_SYMBOLIC) {
830 20           git_filebuf_printf(file, GIT_SYMREF "%s\n", ref->target.symbolic);
831             } else {
832 0           assert(0); /* don't let this happen */
833             }
834              
835 93           return git_filebuf_commit(file);
836             }
837              
838 3           static int refdb_fs_backend__lock(void **out, git_refdb_backend *_backend, const char *refname)
839             {
840             int error;
841             git_filebuf *lock;
842 3           refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
843              
844 3           lock = git__calloc(1, sizeof(git_filebuf));
845 3 50         GIT_ERROR_CHECK_ALLOC(lock);
846              
847 3 50         if ((error = loose_lock(lock, backend, refname)) < 0) {
848 0           git__free(lock);
849 0           return error;
850             }
851              
852 3           *out = lock;
853 3           return 0;
854             }
855              
856             static int refdb_fs_backend__write_tail(
857             git_refdb_backend *_backend,
858             const git_reference *ref,
859             git_filebuf *file,
860             int update_reflog,
861             const git_oid *old_id,
862             const char *old_target,
863             const git_signature *who,
864             const char *message);
865              
866             static int refdb_fs_backend__delete_tail(
867             git_refdb_backend *_backend,
868             git_filebuf *file,
869             const char *ref_name,
870             const git_oid *old_id,
871             const char *old_target);
872              
873 3           static int refdb_fs_backend__unlock(git_refdb_backend *backend, void *payload, int success, int update_reflog,
874             const git_reference *ref, const git_signature *sig, const char *message)
875             {
876 3           git_filebuf *lock = (git_filebuf *) payload;
877 3           int error = 0;
878              
879 3 100         if (success == 2)
880 1           error = refdb_fs_backend__delete_tail(backend, lock, ref->name, NULL, NULL);
881 2 50         else if (success)
882 2           error = refdb_fs_backend__write_tail(backend, ref, lock, update_reflog, NULL, NULL, sig, message);
883             else
884 0           git_filebuf_cleanup(lock);
885              
886 3           git__free(lock);
887 3           return error;
888             }
889              
890             /*
891             * Find out what object this reference resolves to.
892             *
893             * For references that point to a 'big' tag (e.g. an
894             * actual tag object on the repository), we need to
895             * cache on the packfile the OID of the object to
896             * which that 'big tag' is pointing to.
897             */
898 0           static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref)
899             {
900             git_object *object;
901              
902 0 0         if (ref->flags & PACKREF_HAS_PEEL || ref->flags & PACKREF_CANNOT_PEEL)
    0          
903 0           return 0;
904              
905             /*
906             * Find the tagged object in the repository
907             */
908 0 0         if (git_object_lookup(&object, backend->repo, &ref->oid, GIT_OBJECT_ANY) < 0)
909 0           return -1;
910              
911             /*
912             * If the tagged object is a Tag object, we need to resolve it;
913             * if the ref is actually a 'weak' ref, we don't need to resolve
914             * anything.
915             */
916 0 0         if (git_object_type(object) == GIT_OBJECT_TAG) {
917 0           git_tag *tag = (git_tag *)object;
918              
919             /*
920             * Find the object pointed at by this tag
921             */
922 0           git_oid_cpy(&ref->peel, git_tag_target_id(tag));
923 0           ref->flags |= PACKREF_HAS_PEEL;
924              
925             /*
926             * The reference has now cached the resolved OID, and is
927             * marked at such. When written to the packfile, it'll be
928             * accompanied by this resolved oid
929             */
930             }
931              
932 0           git_object_free(object);
933 0           return 0;
934             }
935              
936             /*
937             * Write a single reference into a packfile
938             */
939 0           static int packed_write_ref(struct packref *ref, git_filebuf *file)
940             {
941             char oid[GIT_OID_HEXSZ + 1];
942 0           git_oid_nfmt(oid, sizeof(oid), &ref->oid);
943              
944             /*
945             * For references that peel to an object in the repo, we must
946             * write the resulting peel on a separate line, e.g.
947             *
948             * 6fa8a902cc1d18527e1355773c86721945475d37 refs/tags/libgit2-0.4
949             * ^2ec0cb7959b0bf965d54f95453f5b4b34e8d3100
950             *
951             * This obviously only applies to tags.
952             * The required peels have already been loaded into `ref->peel_target`.
953             */
954 0 0         if (ref->flags & PACKREF_HAS_PEEL) {
955             char peel[GIT_OID_HEXSZ + 1];
956 0           git_oid_nfmt(peel, sizeof(peel), &ref->peel);
957              
958 0 0         if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
959 0           return -1;
960             } else {
961 0 0         if (git_filebuf_printf(file, "%s %s\n", oid, ref->name) < 0)
962 0           return -1;
963             }
964              
965 0           return 0;
966             }
967              
968             /*
969             * Remove all loose references
970             *
971             * Once we have successfully written a packfile,
972             * all the loose references that were packed must be
973             * removed from disk.
974             *
975             * This is a dangerous method; make sure the packfile
976             * is well-written, because we are destructing references
977             * here otherwise.
978             */
979 0           static int packed_remove_loose(refdb_fs_backend *backend)
980             {
981             size_t i;
982 0           git_filebuf lock = GIT_FILEBUF_INIT;
983 0           git_buf ref_content = GIT_BUF_INIT;
984 0           int error = 0;
985              
986             /* backend->refcache is already locked when this is called */
987              
988 0 0         for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
989 0           struct packref *ref = git_sortedcache_entry(backend->refcache, i);
990             git_oid current_id;
991              
992 0 0         if (!ref || !(ref->flags & PACKREF_WAS_LOOSE))
    0          
993 0           continue;
994              
995 0           git_filebuf_cleanup(&lock);
996              
997             /* We need to stop anybody from updating the ref while we try to do a safe delete */
998 0           error = loose_lock(&lock, backend, ref->name);
999             /* If someone else is updating it, let them do it */
1000 0 0         if (error == GIT_EEXISTS || error == GIT_ENOTFOUND)
    0          
1001 0           continue;
1002              
1003 0 0         if (error < 0) {
1004 0           git_buf_dispose(&ref_content);
1005 0           git_error_set(GIT_ERROR_REFERENCE, "failed to lock loose reference '%s'", ref->name);
1006 0           return error;
1007             }
1008              
1009 0           error = git_futils_readbuffer(&ref_content, lock.path_original);
1010             /* Someone else beat us to cleaning up the ref, let's simply continue */
1011 0 0         if (error == GIT_ENOTFOUND)
1012 0           continue;
1013              
1014             /* This became a symref between us packing and trying to delete it, so ignore it */
1015 0 0         if (!git__prefixcmp(ref_content.ptr, GIT_SYMREF))
1016 0           continue;
1017              
1018             /* Figure out the current id; if we find a bad ref file, skip it so we can do the rest */
1019 0 0         if (loose_parse_oid(¤t_id, lock.path_original, &ref_content) < 0)
1020 0           continue;
1021              
1022             /* If the ref moved since we packed it, we must not delete it */
1023 0 0         if (!git_oid_equal(¤t_id, &ref->oid))
1024 0           continue;
1025              
1026             /*
1027             * if we fail to remove a single file, this is *not* good,
1028             * but we should keep going and remove as many as possible.
1029             * If we fail to remove, the ref is still in the old state, so
1030             * we haven't lost information.
1031             */
1032 0           p_unlink(lock.path_original);
1033             }
1034              
1035 0           git_buf_dispose(&ref_content);
1036 0           git_filebuf_cleanup(&lock);
1037 0           return 0;
1038             }
1039              
1040             /*
1041             * Write all the contents in the in-memory packfile to disk.
1042             */
1043 0           static int packed_write(refdb_fs_backend *backend)
1044             {
1045 0           git_sortedcache *refcache = backend->refcache;
1046 0           git_filebuf pack_file = GIT_FILEBUF_INIT;
1047 0           int error, open_flags = 0;
1048             size_t i;
1049              
1050             /* lock the cache to updates while we do this */
1051 0 0         if ((error = git_sortedcache_wlock(refcache)) < 0)
1052 0           return error;
1053              
1054 0 0         if (backend->fsync)
1055 0           open_flags = GIT_FILEBUF_FSYNC;
1056              
1057             /* Open the file! */
1058 0 0         if ((error = git_filebuf_open(&pack_file, git_sortedcache_path(refcache), open_flags, GIT_PACKEDREFS_FILE_MODE)) < 0)
1059 0           goto fail;
1060              
1061             /* Packfiles have a header... apparently
1062             * This is in fact not required, but we might as well print it
1063             * just for kicks */
1064 0 0         if ((error = git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < 0)
1065 0           goto fail;
1066              
1067 0 0         for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) {
1068 0           struct packref *ref = git_sortedcache_entry(refcache, i);
1069 0 0         assert(ref);
1070              
1071 0 0         if ((error = packed_find_peel(backend, ref)) < 0)
1072 0           goto fail;
1073              
1074 0 0         if ((error = packed_write_ref(ref, &pack_file)) < 0)
1075 0           goto fail;
1076             }
1077              
1078             /* if we've written all the references properly, we can commit
1079             * the packfile to make the changes effective */
1080 0 0         if ((error = git_filebuf_commit(&pack_file)) < 0)
1081 0           goto fail;
1082              
1083             /* when and only when the packfile has been properly written,
1084             * we can go ahead and remove the loose refs */
1085 0 0         if ((error = packed_remove_loose(backend)) < 0)
1086 0           goto fail;
1087              
1088 0           git_sortedcache_updated(refcache);
1089 0           git_sortedcache_wunlock(refcache);
1090              
1091             /* we're good now */
1092 0           return 0;
1093              
1094             fail:
1095 0           git_filebuf_cleanup(&pack_file);
1096 0           git_sortedcache_wunlock(refcache);
1097              
1098 0           return error;
1099             }
1100              
1101 8           static int packed_delete(refdb_fs_backend *backend, const char *ref_name)
1102             {
1103             size_t pack_pos;
1104 8           int error, found = 0;
1105              
1106 8 50         if ((error = packed_reload(backend)) < 0)
1107 0           goto cleanup;
1108              
1109 8 50         if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
1110 0           goto cleanup;
1111              
1112             /* If a packed reference exists, remove it from the packfile and repack if necessary */
1113 8           error = git_sortedcache_lookup_index(&pack_pos, backend->refcache, ref_name);
1114 8 50         if (error == 0) {
1115 0           error = git_sortedcache_remove(backend->refcache, pack_pos);
1116 0           found = 1;
1117             }
1118 8 50         if (error == GIT_ENOTFOUND)
1119 8           error = 0;
1120              
1121 8           git_sortedcache_wunlock(backend->refcache);
1122              
1123 8 50         if (found)
1124 0           error = packed_write(backend);
1125              
1126             cleanup:
1127 8           return error;
1128             }
1129              
1130             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);
1131              
1132 206           static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const char *name,
1133             const git_oid *old_id, const char *old_target)
1134             {
1135 206           int error = 0;
1136 206           git_reference *old_ref = NULL;
1137              
1138 206           *cmp = 0;
1139             /* It "matches" if there is no old value to compare against */
1140 206 100         if (!old_id && !old_target)
    100          
1141 69           return 0;
1142              
1143 137 100         if ((error = refdb_fs_backend__lookup(&old_ref, backend, name)) < 0)
1144 31           goto out;
1145              
1146             /* If the types don't match, there's no way the values do */
1147 106 100         if (old_id && old_ref->type != GIT_REFERENCE_DIRECT) {
    100          
1148 2           *cmp = -1;
1149 2           goto out;
1150             }
1151 104 100         if (old_target && old_ref->type != GIT_REFERENCE_SYMBOLIC) {
    100          
1152 1           *cmp = 1;
1153 1           goto out;
1154             }
1155              
1156 103 100         if (old_id && old_ref->type == GIT_REFERENCE_DIRECT)
    50          
1157 82           *cmp = git_oid_cmp(old_id, &old_ref->target.oid);
1158              
1159 103 100         if (old_target && old_ref->type == GIT_REFERENCE_SYMBOLIC)
    50          
1160 21           *cmp = git__strcmp(old_target, old_ref->target.symbolic);
1161              
1162             out:
1163 137           git_reference_free(old_ref);
1164              
1165 206           return error;
1166             }
1167              
1168             /*
1169             * The git.git comment regarding this, for your viewing pleasure:
1170             *
1171             * Special hack: If a branch is updated directly and HEAD
1172             * points to it (may happen on the remote side of a push
1173             * for example) then logically the HEAD reflog should be
1174             * updated too.
1175             * A generic solution implies reverse symref information,
1176             * but finding all symrefs pointing to the given branch
1177             * would be rather costly for this rare event (the direct
1178             * update of a branch) to be worth it. So let's cheat and
1179             * check with HEAD only which should cover 99% of all usage
1180             * scenarios (even 100% of the default ones).
1181             */
1182 80           static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message)
1183             {
1184 80           git_reference *head = NULL;
1185 80           git_refdb *refdb = NULL;
1186             int error, write_reflog;
1187             git_oid old_id;
1188              
1189 80 50         if ((error = git_repository_refdb(&refdb, backend->repo)) < 0 ||
    50          
1190 80           (error = git_refdb_should_write_head_reflog(&write_reflog, refdb, ref)) < 0)
1191             goto out;
1192 80 100         if (!write_reflog)
1193 50           goto out;
1194              
1195             /* if we can't resolve, we use {0}*40 as old id */
1196 30 100         if (git_reference_name_to_id(&old_id, backend->repo, ref->name) < 0)
1197 3           memset(&old_id, 0, sizeof(old_id));
1198              
1199 60 50         if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0 ||
1200 30           (error = reflog_append(backend, head, &old_id, git_reference_target(ref), who, message)) < 0)
1201             goto out;
1202              
1203             out:
1204 80           git_reference_free(head);
1205 80           git_refdb_free(refdb);
1206 80           return error;
1207             }
1208              
1209 99           static int refdb_fs_backend__write(
1210             git_refdb_backend *_backend,
1211             const git_reference *ref,
1212             int force,
1213             const git_signature *who,
1214             const char *message,
1215             const git_oid *old_id,
1216             const char *old_target)
1217             {
1218 99           refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1219 99           git_filebuf file = GIT_FILEBUF_INIT;
1220 99           int error = 0;
1221              
1222 99 50         assert(backend);
1223              
1224 99 100         if ((error = reference_path_available(backend, ref->name, NULL, force)) < 0)
1225 2           return error;
1226              
1227             /* We need to perform the reflog append and old value check under the ref's lock */
1228 97 50         if ((error = loose_lock(&file, backend, ref->name)) < 0)
1229 0           return error;
1230              
1231 99           return refdb_fs_backend__write_tail(_backend, ref, &file, true, old_id, old_target, who, message);
1232             }
1233              
1234 99           static int refdb_fs_backend__write_tail(
1235             git_refdb_backend *_backend,
1236             const git_reference *ref,
1237             git_filebuf *file,
1238             int update_reflog,
1239             const git_oid *old_id,
1240             const char *old_target,
1241             const git_signature *who,
1242             const char *message)
1243             {
1244 99           refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1245 99           int error = 0, cmp = 0, should_write;
1246 99           const char *new_target = NULL;
1247 99           const git_oid *new_id = NULL;
1248              
1249 99 50         if ((error = cmp_old_ref(&cmp, _backend, ref->name, old_id, old_target)) < 0)
1250 0           goto on_error;
1251              
1252 99 50         if (cmp) {
1253 0           git_error_set(GIT_ERROR_REFERENCE, "old reference value does not match");
1254 0           error = GIT_EMODIFIED;
1255 0           goto on_error;
1256             }
1257              
1258 99 100         if (ref->type == GIT_REFERENCE_SYMBOLIC)
1259 22           new_target = ref->target.symbolic;
1260             else
1261 77           new_id = &ref->target.oid;
1262              
1263 99           error = cmp_old_ref(&cmp, _backend, ref->name, new_id, new_target);
1264 99 100         if (error < 0 && error != GIT_ENOTFOUND)
    50          
1265 0           goto on_error;
1266              
1267             /* Don't update if we have the same value */
1268 99 100         if (!error && !cmp) {
    100          
1269 7           error = 0;
1270 7           goto on_error; /* not really error */
1271             }
1272              
1273 92 100         if (update_reflog) {
1274             git_refdb *refdb;
1275              
1276 90 50         if ((error = git_repository_refdb__weakptr(&refdb, backend->repo)) < 0 ||
    50          
1277 90           (error = git_refdb_should_write_reflog(&should_write, refdb, ref)) < 0)
1278             goto on_error;
1279              
1280 90 100         if (should_write) {
1281 80 50         if ((error = reflog_append(backend, ref, NULL, NULL, who, message)) < 0)
1282 0           goto on_error;
1283 80 50         if ((error = maybe_append_head(backend, ref, who, message)) < 0)
1284 90           goto on_error;
1285             }
1286             }
1287              
1288 92           return loose_commit(file, ref);
1289              
1290             on_error:
1291 7           git_filebuf_cleanup(file);
1292 99           return error;
1293             }
1294              
1295 11           static void refdb_fs_backend__prune_refs(
1296             refdb_fs_backend *backend,
1297             const char *ref_name,
1298             const char *prefix)
1299             {
1300 11           git_buf relative_path = GIT_BUF_INIT;
1301 11           git_buf base_path = GIT_BUF_INIT;
1302             size_t commonlen;
1303              
1304 11 50         assert(backend && ref_name);
    50          
1305              
1306 11 50         if (git_buf_sets(&relative_path, ref_name) < 0)
1307 0           goto cleanup;
1308              
1309 11           git_path_squash_slashes(&relative_path);
1310 17 100         if ((commonlen = git_path_common_dirlen("refs/heads/", git_buf_cstr(&relative_path))) == strlen("refs/heads/") ||
    100          
1311 10 50         (commonlen = git_path_common_dirlen("refs/tags/", git_buf_cstr(&relative_path))) == strlen("refs/tags/") ||
1312 4           (commonlen = git_path_common_dirlen("refs/remotes/", git_buf_cstr(&relative_path))) == strlen("refs/remotes/")) {
1313              
1314 7           git_buf_truncate(&relative_path, commonlen);
1315              
1316 7 50         if (prefix) {
1317 7 50         if (git_buf_join3(&base_path, '/', backend->commonpath, prefix, git_buf_cstr(&relative_path)) < 0)
1318 0           goto cleanup;
1319             } else {
1320 0 0         if (git_buf_joinpath(&base_path, backend->commonpath, git_buf_cstr(&relative_path)) < 0)
1321 0           goto cleanup;
1322             }
1323              
1324 7           git_futils_rmdir_r(ref_name + commonlen, git_buf_cstr(&base_path), GIT_RMDIR_EMPTY_PARENTS | GIT_RMDIR_SKIP_ROOT);
1325             }
1326              
1327             cleanup:
1328 11           git_buf_dispose(&relative_path);
1329 11           git_buf_dispose(&base_path);
1330 11           }
1331              
1332 7           static int refdb_fs_backend__delete(
1333             git_refdb_backend *_backend,
1334             const char *ref_name,
1335             const git_oid *old_id, const char *old_target)
1336             {
1337 7           refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1338 7           git_filebuf file = GIT_FILEBUF_INIT;
1339 7           int error = 0;
1340              
1341 7 50         assert(backend && ref_name);
    50          
1342              
1343 7 50         if ((error = loose_lock(&file, backend, ref_name)) < 0)
1344 0           return error;
1345              
1346 7 50         if ((error = refdb_reflog_fs__delete(_backend, ref_name)) < 0) {
1347 0           git_filebuf_cleanup(&file);
1348 0           return error;
1349             }
1350              
1351 7           return refdb_fs_backend__delete_tail(_backend, &file, ref_name, old_id, old_target);
1352             }
1353              
1354 8           static int loose_delete(refdb_fs_backend *backend, const char *ref_name)
1355             {
1356 8           git_buf loose_path = GIT_BUF_INIT;
1357 8           int error = 0;
1358              
1359 8 50         if (git_buf_joinpath(&loose_path, backend->commonpath, ref_name) < 0)
1360 0           return -1;
1361              
1362 8           error = p_unlink(loose_path.ptr);
1363 8 50         if (error < 0 && errno == ENOENT)
    0          
1364 0           error = GIT_ENOTFOUND;
1365 8 50         else if (error != 0)
1366 0           error = -1;
1367              
1368 8           git_buf_dispose(&loose_path);
1369              
1370 8           return error;
1371             }
1372              
1373 8           static int refdb_fs_backend__delete_tail(
1374             git_refdb_backend *_backend,
1375             git_filebuf *file,
1376             const char *ref_name,
1377             const git_oid *old_id, const char *old_target)
1378             {
1379 8           refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1380 8           int error = 0, cmp = 0;
1381 8           bool packed_deleted = 0;
1382              
1383 8           error = cmp_old_ref(&cmp, _backend, ref_name, old_id, old_target);
1384 8 50         if (error < 0)
1385 0           goto cleanup;
1386              
1387 8 50         if (cmp) {
1388 0           git_error_set(GIT_ERROR_REFERENCE, "old reference value does not match");
1389 0           error = GIT_EMODIFIED;
1390 0           goto cleanup;
1391             }
1392              
1393             /*
1394             * To ensure that an external observer will see either the current ref value
1395             * (because the loose ref still exists), or a missing ref (after the packed-file is
1396             * unlocked, there will be nothing left), we must ensure things happen in the
1397             * following order:
1398             *
1399             * - the packed-ref file is locked and loaded, as well as a loose one, if it exists
1400             * - we optimistically delete a packed ref, keeping track of whether it existed
1401             * - we delete the loose ref, note that we have its .lock
1402             * - the loose ref is "unlocked", then the packed-ref file is rewritten and unlocked
1403             * - we should prune the path components if a loose ref was deleted
1404             *
1405             * Note that, because our packed backend doesn't expose its filesystem lock,
1406             * we might not be able to guarantee that this is what actually happens (ie.
1407             * as our current code never write packed-refs.lock, nothing stops observers
1408             * from grabbing a "stale" value from there).
1409             */
1410 8 50         if ((error = packed_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND)
    0          
1411 0           goto cleanup;
1412              
1413 8 50         if (error == 0)
1414 8           packed_deleted = 1;
1415              
1416 8 50         if ((error = loose_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND)
    0          
1417 0           goto cleanup;
1418              
1419 8 50         if (error == GIT_ENOTFOUND) {
1420 0 0         error = packed_deleted ? 0 : ref_error_notfound(ref_name);
1421 0           goto cleanup;
1422             }
1423              
1424             cleanup:
1425 8           git_filebuf_cleanup(file);
1426 8 50         if (error == 0)
1427 8           refdb_fs_backend__prune_refs(backend, ref_name, "");
1428 8           return error;
1429             }
1430              
1431             static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name);
1432              
1433 1           static int refdb_fs_backend__rename(
1434             git_reference **out,
1435             git_refdb_backend *_backend,
1436             const char *old_name,
1437             const char *new_name,
1438             int force,
1439             const git_signature *who,
1440             const char *message)
1441             {
1442 1           refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1443 1           git_reference *old, *new = NULL;
1444 1           git_filebuf file = GIT_FILEBUF_INIT;
1445             int error;
1446              
1447 1 50         assert(backend);
1448              
1449 1 50         if ((error = reference_path_available(
1450 1 50         backend, new_name, old_name, force)) < 0 ||
1451             (error = refdb_fs_backend__lookup(&old, _backend, old_name)) < 0)
1452 0           return error;
1453              
1454 1 50         if ((error = refdb_fs_backend__delete(_backend, old_name, NULL, NULL)) < 0) {
1455 0           git_reference_free(old);
1456 0           return error;
1457             }
1458              
1459 1           new = git_reference__realloc(&old, new_name);
1460 1 50         if (!new) {
1461 0           git_reference_free(old);
1462 0           return -1;
1463             }
1464              
1465 1 50         if ((error = loose_lock(&file, backend, new->name)) < 0) {
1466 0           git_reference_free(new);
1467 0           return error;
1468             }
1469              
1470             /* Try to rename the refog; it's ok if the old doesn't exist */
1471 1           error = refdb_reflog_fs__rename(_backend, old_name, new_name);
1472 2 50         if (((error == 0) || (error == GIT_ENOTFOUND)) &&
    50          
    50          
1473 1           ((error = reflog_append(backend, new, git_reference_target(new), NULL, who, message)) < 0)) {
1474 0           git_reference_free(new);
1475 0           git_filebuf_cleanup(&file);
1476 0           return error;
1477             }
1478              
1479 1 50         if (error < 0) {
1480 0           git_reference_free(new);
1481 0           git_filebuf_cleanup(&file);
1482 0           return error;
1483             }
1484              
1485              
1486 1 50         if ((error = loose_commit(&file, new)) < 0 || out == NULL) {
    50          
1487 0           git_reference_free(new);
1488 0           return error;
1489             }
1490              
1491 1           *out = new;
1492 1           return 0;
1493             }
1494              
1495 0           static int refdb_fs_backend__compress(git_refdb_backend *_backend)
1496             {
1497             int error;
1498 0           refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1499              
1500 0 0         assert(backend);
1501              
1502 0 0         if ((error = packed_reload(backend)) < 0 || /* load the existing packfile */
    0          
1503 0 0         (error = packed_loadloose(backend)) < 0 || /* add all the loose refs */
1504             (error = packed_write(backend)) < 0) /* write back to disk */
1505 0           return error;
1506              
1507 0           return 0;
1508             }
1509              
1510 49           static void refdb_fs_backend__free(git_refdb_backend *_backend)
1511             {
1512 49           refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1513              
1514 49 50         assert(backend);
1515              
1516 49           git_sortedcache_free(backend->refcache);
1517 49           git__free(backend->gitpath);
1518 49           git__free(backend->commonpath);
1519 49           git__free(backend);
1520 49           }
1521              
1522 108           static char *setup_namespace(git_repository *repo, const char *in)
1523             {
1524 108           git_buf path = GIT_BUF_INIT;
1525 108           char *parts, *start, *end, *out = NULL;
1526              
1527 108 50         if (!in)
1528 0           goto done;
1529              
1530 108           git_buf_puts(&path, in);
1531              
1532             /* if the repo is not namespaced, nothing else to do */
1533 108 50         if (repo->namespace == NULL) {
1534 108           out = git_buf_detach(&path);
1535 108           goto done;
1536             }
1537              
1538 0           parts = end = git__strdup(repo->namespace);
1539 0 0         if (parts == NULL)
1540 0           goto done;
1541              
1542             /*
1543             * From `man gitnamespaces`:
1544             * namespaces which include a / will expand to a hierarchy
1545             * of namespaces; for example, GIT_NAMESPACE=foo/bar will store
1546             * refs under refs/namespaces/foo/refs/namespaces/bar/
1547             */
1548 0 0         while ((start = git__strsep(&end, "/")) != NULL)
1549 0           git_buf_printf(&path, "refs/namespaces/%s/", start);
1550              
1551 0           git_buf_printf(&path, "refs/namespaces/%s/refs", end);
1552 0           git__free(parts);
1553              
1554             /* Make sure that the folder with the namespace exists */
1555 0 0         if (git_futils_mkdir_relative(git_buf_cstr(&path), in, 0777,
1556             GIT_MKDIR_PATH, NULL) < 0)
1557 0           goto done;
1558              
1559             /* Return root of the namespaced gitpath, i.e. without the trailing '/refs' */
1560 0           git_buf_rtruncate_at_char(&path, '/');
1561 0           out = git_buf_detach(&path);
1562              
1563             done:
1564 108           git_buf_dispose(&path);
1565 108           return out;
1566             }
1567              
1568 20           static int reflog_alloc(git_reflog **reflog, const char *name)
1569             {
1570             git_reflog *log;
1571              
1572 20           *reflog = NULL;
1573              
1574 20           log = git__calloc(1, sizeof(git_reflog));
1575 20 50         GIT_ERROR_CHECK_ALLOC(log);
1576              
1577 20           log->ref_name = git__strdup(name);
1578 20 50         GIT_ERROR_CHECK_ALLOC(log->ref_name);
1579              
1580 20 50         if (git_vector_init(&log->entries, 0, NULL) < 0) {
1581 0           git__free(log->ref_name);
1582 0           git__free(log);
1583 0           return -1;
1584             }
1585              
1586 20           *reflog = log;
1587              
1588 20           return 0;
1589             }
1590              
1591 20           static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
1592             {
1593 20           git_parse_ctx parser = GIT_PARSE_CTX_INIT;
1594              
1595 20 50         if ((git_parse_ctx_init(&parser, buf, buf_size)) < 0)
1596 0           return -1;
1597              
1598 59 100         for (; parser.remain_len; git_parse_advance_line(&parser)) {
1599             git_reflog_entry *entry;
1600             const char *sig;
1601             char c;
1602              
1603 39           entry = git__calloc(1, sizeof(*entry));
1604 39 50         GIT_ERROR_CHECK_ALLOC(entry);
1605 39           entry->committer = git__calloc(1, sizeof(*entry->committer));
1606 39 50         GIT_ERROR_CHECK_ALLOC(entry->committer);
1607              
1608 78           if (git_parse_advance_oid(&entry->oid_old, &parser) < 0 ||
1609 78 50         git_parse_advance_expected(&parser, " ", 1) < 0 ||
1610 39           git_parse_advance_oid(&entry->oid_cur, &parser) < 0)
1611             goto next;
1612              
1613 39           sig = parser.line;
1614 2145 50         while (git_parse_peek(&c, &parser, 0) == 0 && c != '\t' && c != '\n')
    100          
    50          
1615 2106           git_parse_advance_chars(&parser, 1);
1616              
1617 39 50         if (git_signature__parse(entry->committer, &sig, parser.line, NULL, 0) < 0)
1618 0           goto next;
1619              
1620 39 50         if (c == '\t') {
1621             size_t len;
1622 39           git_parse_advance_chars(&parser, 1);
1623              
1624 39           len = parser.line_len;
1625 39 50         if (parser.line[len - 1] == '\n')
1626 39           len--;
1627              
1628 39           entry->msg = git__strndup(parser.line, len);
1629 39 50         GIT_ERROR_CHECK_ALLOC(entry->msg);
1630             }
1631              
1632 39 50         if ((git_vector_insert(&log->entries, entry)) < 0) {
1633 0           git_reflog_entry__free(entry);
1634 0           return -1;
1635             }
1636              
1637 39           continue;
1638              
1639             next:
1640 0           git_reflog_entry__free(entry);
1641             }
1642              
1643 20           return 0;
1644             }
1645              
1646 7           static int create_new_reflog_file(const char *filepath)
1647             {
1648             int fd, error;
1649              
1650 7 50         if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
1651 0           return error;
1652              
1653 7 50         if ((fd = p_open(filepath,
1654             O_WRONLY | O_CREAT,
1655             GIT_REFLOG_FILE_MODE)) < 0)
1656 0           return -1;
1657              
1658 7           return p_close(fd);
1659             }
1660              
1661 240           GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const char *name)
1662             {
1663 240 100         if (strcmp(name, GIT_HEAD_FILE) == 0)
1664 77           return git_buf_join3(path, '/', repo->gitdir, GIT_REFLOG_DIR, name);
1665 163           return git_buf_join3(path, '/', repo->commondir, GIT_REFLOG_DIR, name);
1666             }
1667              
1668 6           static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name)
1669             {
1670             refdb_fs_backend *backend;
1671             git_repository *repo;
1672 6           git_buf path = GIT_BUF_INIT;
1673             int error;
1674              
1675 6 50         assert(_backend && name);
    50          
1676              
1677 6           backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1678 6           repo = backend->repo;
1679              
1680 6 50         if ((error = retrieve_reflog_path(&path, repo, name)) < 0)
1681 0           return error;
1682              
1683 6           error = create_new_reflog_file(git_buf_cstr(&path));
1684 6           git_buf_dispose(&path);
1685              
1686 6           return error;
1687             }
1688              
1689 88           static int has_reflog(git_repository *repo, const char *name)
1690             {
1691 88           int ret = 0;
1692 88           git_buf path = GIT_BUF_INIT;
1693              
1694 88 50         if (retrieve_reflog_path(&path, repo, name) < 0)
1695 0           goto cleanup;
1696              
1697 88           ret = git_path_isfile(git_buf_cstr(&path));
1698              
1699             cleanup:
1700 88           git_buf_dispose(&path);
1701 88           return ret;
1702             }
1703              
1704 88           static int refdb_reflog_fs__has_log(git_refdb_backend *_backend, const char *name)
1705             {
1706             refdb_fs_backend *backend;
1707              
1708 88 50         assert(_backend && name);
    50          
1709              
1710 88           backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1711              
1712 88           return has_reflog(backend->repo, name);
1713             }
1714              
1715 20           static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
1716             {
1717 20           int error = -1;
1718 20           git_buf log_path = GIT_BUF_INIT;
1719 20           git_buf log_file = GIT_BUF_INIT;
1720 20           git_reflog *log = NULL;
1721             git_repository *repo;
1722             refdb_fs_backend *backend;
1723              
1724 20 50         assert(out && _backend && name);
    50          
    50          
1725              
1726 20           backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1727 20           repo = backend->repo;
1728              
1729 20 50         if (reflog_alloc(&log, name) < 0)
1730 0           return -1;
1731              
1732 20 50         if (retrieve_reflog_path(&log_path, repo, name) < 0)
1733 0           goto cleanup;
1734              
1735 20           error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
1736 20 100         if (error < 0 && error != GIT_ENOTFOUND)
    50          
1737 0           goto cleanup;
1738              
1739 21 100         if ((error == GIT_ENOTFOUND) &&
    50          
1740 1           ((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
1741 0           goto cleanup;
1742              
1743 20 50         if ((error = reflog_parse(log,
1744             git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
1745 0           goto cleanup;
1746              
1747 20           *out = log;
1748 20           goto success;
1749              
1750             cleanup:
1751 0           git_reflog_free(log);
1752              
1753             success:
1754 20           git_buf_dispose(&log_file);
1755 20           git_buf_dispose(&log_path);
1756              
1757 20           return error;
1758             }
1759              
1760 121           static int serialize_reflog_entry(
1761             git_buf *buf,
1762             const git_oid *oid_old,
1763             const git_oid *oid_new,
1764             const git_signature *committer,
1765             const char *msg)
1766             {
1767             char raw_old[GIT_OID_HEXSZ+1];
1768             char raw_new[GIT_OID_HEXSZ+1];
1769              
1770 121           git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
1771 121           git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
1772              
1773 121           git_buf_clear(buf);
1774              
1775 121           git_buf_puts(buf, raw_old);
1776 121           git_buf_putc(buf, ' ');
1777 121           git_buf_puts(buf, raw_new);
1778              
1779 121           git_signature__writebuf(buf, " ", committer);
1780              
1781             /* drop trailing LF */
1782 121           git_buf_rtrim(buf);
1783              
1784 121 100         if (msg) {
1785             size_t i;
1786              
1787 114           git_buf_putc(buf, '\t');
1788 114           git_buf_puts(buf, msg);
1789              
1790 19307 100         for (i = 0; i < buf->size - 2; i++)
1791 19193 50         if (buf->ptr[i] == '\n')
1792 0           buf->ptr[i] = ' ';
1793 114           git_buf_rtrim(buf);
1794             }
1795              
1796 121           git_buf_putc(buf, '\n');
1797              
1798 121           return git_buf_oom(buf);
1799             }
1800              
1801 7           static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char *refname)
1802             {
1803             git_repository *repo;
1804 7           git_buf log_path = GIT_BUF_INIT;
1805             int error;
1806              
1807 7           repo = backend->repo;
1808              
1809 7 50         if (!git_path_isvalid(backend->repo, refname, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
1810 0           git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", refname);
1811 0           return GIT_EINVALIDSPEC;
1812             }
1813              
1814 7 50         if (retrieve_reflog_path(&log_path, repo, refname) < 0)
1815 0           return -1;
1816              
1817 7 50         if (!git_path_isfile(git_buf_cstr(&log_path))) {
1818 0           git_error_set(GIT_ERROR_INVALID,
1819             "log file for reference '%s' doesn't exist", refname);
1820 0           error = -1;
1821 0           goto cleanup;
1822             }
1823              
1824 7           error = git_filebuf_open(file, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE);
1825              
1826             cleanup:
1827 7           git_buf_dispose(&log_path);
1828              
1829 7           return error;
1830             }
1831              
1832 7           static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog)
1833             {
1834 7           int error = -1;
1835             unsigned int i;
1836             git_reflog_entry *entry;
1837             refdb_fs_backend *backend;
1838 7           git_buf log = GIT_BUF_INIT;
1839 7           git_filebuf fbuf = GIT_FILEBUF_INIT;
1840              
1841 7 50         assert(_backend && reflog);
    50          
1842              
1843 7           backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1844              
1845 7 50         if ((error = lock_reflog(&fbuf, backend, reflog->ref_name)) < 0)
1846 0           return -1;
1847              
1848 17 100         git_vector_foreach(&reflog->entries, i, entry) {
1849 10 50         if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
1850 0           goto cleanup;
1851              
1852 10 50         if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
1853 0           goto cleanup;
1854             }
1855              
1856 7           error = git_filebuf_commit(&fbuf);
1857 7           goto success;
1858              
1859             cleanup:
1860 0           git_filebuf_cleanup(&fbuf);
1861              
1862             success:
1863 7           git_buf_dispose(&log);
1864              
1865 7           return error;
1866             }
1867              
1868             /* Append to the reflog, must be called under reference lock */
1869 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)
1870             {
1871             int error, is_symbolic, open_flags;
1872 111           git_oid old_id = {{0}}, new_id = {{0}};
1873 111           git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
1874 111           git_repository *repo = backend->repo;
1875              
1876 111           is_symbolic = ref->type == GIT_REFERENCE_SYMBOLIC;
1877              
1878             /* "normal" symbolic updates do not write */
1879 111 100         if (is_symbolic &&
    50          
1880 0 0         strcmp(ref->name, GIT_HEAD_FILE) &&
1881 0 0         !(old && new))
1882 0           return 0;
1883              
1884             /* From here on is_symbolic also means that it's HEAD */
1885              
1886 111 100         if (old) {
1887 31           git_oid_cpy(&old_id, old);
1888             } else {
1889 80           error = git_reference_name_to_id(&old_id, repo, ref->name);
1890 80 100         if (error < 0 && error != GIT_ENOTFOUND)
    50          
1891 0           return error;
1892             }
1893              
1894 111 100         if (new) {
1895 30           git_oid_cpy(&new_id, new);
1896             } else {
1897 81 100         if (!is_symbolic) {
1898 63           git_oid_cpy(&new_id, git_reference_target(ref));
1899             } else {
1900 18           error = git_reference_name_to_id(&new_id, repo, git_reference_symbolic_target(ref));
1901 18 50         if (error < 0 && error != GIT_ENOTFOUND)
    0          
1902 0           return error;
1903             /* detaching HEAD does not create an entry */
1904 18 50         if (error == GIT_ENOTFOUND)
1905 0           return 0;
1906              
1907 18           git_error_clear();
1908             }
1909             }
1910              
1911 111 50         if ((error = serialize_reflog_entry(&buf, &old_id, &new_id, who, message)) < 0)
1912 0           goto cleanup;
1913              
1914 111 50         if ((error = retrieve_reflog_path(&path, repo, ref->name)) < 0)
1915 0           goto cleanup;
1916              
1917 111 50         if (((error = git_futils_mkpath2file(git_buf_cstr(&path), 0777)) < 0) &&
    0          
1918             (error != GIT_EEXISTS)) {
1919 0           goto cleanup;
1920             }
1921              
1922             /* If the new branch matches part of the namespace of a previously deleted branch,
1923             * there maybe an obsolete/unused directory (or directory hierarchy) in the way.
1924             */
1925 111 50         if (git_path_isdir(git_buf_cstr(&path))) {
1926 0 0         if ((error = git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY)) < 0) {
1927 0 0         if (error == GIT_ENOTFOUND)
1928 0           error = 0;
1929 0 0         } else if (git_path_isdir(git_buf_cstr(&path))) {
1930 0           git_error_set(GIT_ERROR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder",
1931 0           ref->name);
1932 0           error = GIT_EDIRECTORY;
1933             }
1934              
1935 0 0         if (error != 0)
1936 0           goto cleanup;
1937             }
1938              
1939 111           open_flags = O_WRONLY | O_CREAT | O_APPEND;
1940              
1941 111 50         if (backend->fsync)
1942 0           open_flags |= O_FSYNC;
1943              
1944 111           error = git_futils_writebuffer(&buf, git_buf_cstr(&path), open_flags, GIT_REFLOG_FILE_MODE);
1945              
1946             cleanup:
1947 111           git_buf_dispose(&buf);
1948 111           git_buf_dispose(&path);
1949              
1950 111           return error;
1951             }
1952              
1953 1           static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name)
1954             {
1955 1           int error = 0, fd;
1956 1           git_buf old_path = GIT_BUF_INIT;
1957 1           git_buf new_path = GIT_BUF_INIT;
1958 1           git_buf temp_path = GIT_BUF_INIT;
1959 1           git_buf normalized = GIT_BUF_INIT;
1960             git_repository *repo;
1961             refdb_fs_backend *backend;
1962              
1963 1 50         assert(_backend && old_name && new_name);
    50          
    50          
1964              
1965 1           backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1966 1           repo = backend->repo;
1967              
1968 1 50         if ((error = git_reference__normalize_name(
1969             &normalized, new_name, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL)) < 0)
1970 0           return error;
1971              
1972 1 50         if (git_buf_joinpath(&temp_path, repo->gitdir, GIT_REFLOG_DIR) < 0)
1973 0           return -1;
1974              
1975 1 50         if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), old_name) < 0)
1976 0           return -1;
1977              
1978 1 50         if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0)
1979 0           return -1;
1980              
1981 1 50         if (!git_path_exists(git_buf_cstr(&old_path))) {
1982 1           error = GIT_ENOTFOUND;
1983 1           goto cleanup;
1984             }
1985              
1986             /*
1987             * Move the reflog to a temporary place. This two-phase renaming is required
1988             * in order to cope with funny renaming use cases when one tries to move a reference
1989             * to a partially colliding namespace:
1990             * - a/b -> a/b/c
1991             * - a/b/c/d -> a/b/c
1992             */
1993 0 0         if (git_buf_joinpath(&temp_path, git_buf_cstr(&temp_path), "temp_reflog") < 0)
1994 0           return -1;
1995              
1996 0 0         if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path), GIT_REFLOG_FILE_MODE)) < 0) {
1997 0           error = -1;
1998 0           goto cleanup;
1999             }
2000              
2001 0           p_close(fd);
2002              
2003 0 0         if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
2004 0           git_error_set(GIT_ERROR_OS, "failed to rename reflog for %s", new_name);
2005 0           error = -1;
2006 0           goto cleanup;
2007             }
2008              
2009 0           if (git_path_isdir(git_buf_cstr(&new_path)) &&
2010 0           (git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
2011 0           error = -1;
2012 0           goto cleanup;
2013             }
2014              
2015 0 0         if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
2016 0           error = -1;
2017 0           goto cleanup;
2018             }
2019              
2020 0 0         if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
2021 0           git_error_set(GIT_ERROR_OS, "failed to rename reflog for %s", new_name);
2022 0           error = -1;
2023             }
2024              
2025             cleanup:
2026 1           git_buf_dispose(&temp_path);
2027 1           git_buf_dispose(&old_path);
2028 1           git_buf_dispose(&new_path);
2029 1           git_buf_dispose(&normalized);
2030              
2031 1           return error;
2032             }
2033              
2034 8           static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name)
2035             {
2036 8           refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
2037 8           git_buf path = GIT_BUF_INIT;
2038             int error;
2039              
2040 8 50         assert(_backend && name);
    50          
2041              
2042 8 50         if ((error = retrieve_reflog_path(&path, backend->repo, name)) < 0)
2043 0           goto out;
2044              
2045 8 100         if (!git_path_exists(path.ptr))
2046 5           goto out;
2047              
2048 3 50         if ((error = p_unlink(path.ptr)) < 0)
2049 0           goto out;
2050              
2051 3           refdb_fs_backend__prune_refs(backend, name, GIT_REFLOG_DIR);
2052              
2053             out:
2054 8           git_buf_dispose(&path);
2055              
2056 8           return error;
2057             }
2058              
2059 54           int git_refdb_backend_fs(
2060             git_refdb_backend **backend_out,
2061             git_repository *repository)
2062             {
2063 54           int t = 0;
2064 54           git_buf gitpath = GIT_BUF_INIT;
2065             refdb_fs_backend *backend;
2066              
2067 54           backend = git__calloc(1, sizeof(refdb_fs_backend));
2068 54 50         GIT_ERROR_CHECK_ALLOC(backend);
2069              
2070 54 50         if (git_refdb_init_backend(&backend->parent, GIT_REFDB_BACKEND_VERSION) < 0)
2071 0           goto fail;
2072              
2073 54           backend->repo = repository;
2074              
2075 54 50         if (repository->gitdir) {
2076 54           backend->gitpath = setup_namespace(repository, repository->gitdir);
2077              
2078 54 50         if (backend->gitpath == NULL)
2079 0           goto fail;
2080             }
2081              
2082 54 50         if (repository->commondir) {
2083 54           backend->commonpath = setup_namespace(repository, repository->commondir);
2084              
2085 54 50         if (backend->commonpath == NULL)
2086 0           goto fail;
2087             }
2088              
2089 108           if (git_buf_joinpath(&gitpath, backend->commonpath, GIT_PACKEDREFS_FILE) < 0 ||
2090 54           git_sortedcache_new(
2091             &backend->refcache, offsetof(struct packref, name),
2092             NULL, NULL, packref_cmp, git_buf_cstr(&gitpath)) < 0)
2093             goto fail;
2094              
2095 54           git_buf_dispose(&gitpath);
2096              
2097 54 50         if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_IGNORECASE) && t) {
    50          
2098 0           backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE;
2099 0           backend->direach_flags |= GIT_PATH_DIR_IGNORE_CASE;
2100             }
2101 54 50         if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_PRECOMPOSE) && t) {
    50          
2102 0           backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
2103 0           backend->direach_flags |= GIT_PATH_DIR_PRECOMPOSE_UNICODE;
2104             }
2105 54 50         if ((!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_FSYNCOBJECTFILES) && t) ||
    50          
    50          
2106             git_repository__fsync_gitdir)
2107 0           backend->fsync = 1;
2108 54           backend->iterator_flags |= GIT_ITERATOR_DESCEND_SYMLINKS;
2109              
2110 54           backend->parent.exists = &refdb_fs_backend__exists;
2111 54           backend->parent.lookup = &refdb_fs_backend__lookup;
2112 54           backend->parent.iterator = &refdb_fs_backend__iterator;
2113 54           backend->parent.write = &refdb_fs_backend__write;
2114 54           backend->parent.del = &refdb_fs_backend__delete;
2115 54           backend->parent.rename = &refdb_fs_backend__rename;
2116 54           backend->parent.compress = &refdb_fs_backend__compress;
2117 54           backend->parent.lock = &refdb_fs_backend__lock;
2118 54           backend->parent.unlock = &refdb_fs_backend__unlock;
2119 54           backend->parent.has_log = &refdb_reflog_fs__has_log;
2120 54           backend->parent.ensure_log = &refdb_reflog_fs__ensure_log;
2121 54           backend->parent.free = &refdb_fs_backend__free;
2122 54           backend->parent.reflog_read = &refdb_reflog_fs__read;
2123 54           backend->parent.reflog_write = &refdb_reflog_fs__write;
2124 54           backend->parent.reflog_rename = &refdb_reflog_fs__rename;
2125 54           backend->parent.reflog_delete = &refdb_reflog_fs__delete;
2126              
2127 54           *backend_out = (git_refdb_backend *)backend;
2128 54           return 0;
2129              
2130             fail:
2131 0           git_buf_dispose(&gitpath);
2132 0           git__free(backend->gitpath);
2133 0           git__free(backend->commonpath);
2134 0           git__free(backend);
2135 54           return -1;
2136             }