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