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