summaryrefslogtreecommitdiff
path: root/refs/files-backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'refs/files-backend.c')
-rw-r--r--refs/files-backend.c292
1 files changed, 172 insertions, 120 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 74c0385873..151b0056fe 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -79,13 +79,15 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
* Create a new submodule ref cache and add it to the internal
* set of caches.
*/
-static struct ref_store *files_ref_store_create(const char *gitdir,
+static struct ref_store *files_ref_store_create(struct repository *repo,
+ const char *gitdir,
unsigned int flags)
{
struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
struct ref_store *ref_store = (struct ref_store *)refs;
struct strbuf sb = STRBUF_INIT;
+ ref_store->repo = repo;
ref_store->gitdir = xstrdup(gitdir);
base_ref_store_init(ref_store, &refs_be_files);
refs->store_flags = flags;
@@ -93,7 +95,7 @@ static struct ref_store *files_ref_store_create(const char *gitdir,
get_common_dir_noenv(&sb, gitdir);
refs->gitcommondir = strbuf_detach(&sb, NULL);
strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
- refs->packed_ref_store = packed_ref_store_create(sb.buf, flags);
+ refs->packed_ref_store = packed_ref_store_create(repo, sb.buf, flags);
strbuf_release(&sb);
chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir);
@@ -227,7 +229,7 @@ static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dir
pos = search_ref_dir(dir, prefix, prefix_len);
if (pos >= 0)
continue;
- child_entry = create_dir_entry(dir->cache, prefix, prefix_len, 1);
+ child_entry = create_dir_entry(dir->cache, prefix, prefix_len);
add_entry_to_dir(dir, child_entry);
}
}
@@ -278,7 +280,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
strbuf_addch(&refname, '/');
add_entry_to_dir(dir,
create_dir_entry(dir->cache, refname.buf,
- refname.len, 1));
+ refname.len));
} else {
if (!refs_resolve_ref_unsafe(&refs->base,
refname.buf,
@@ -336,14 +338,14 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
* lazily):
*/
add_entry_to_dir(get_ref_dir(refs->loose->root),
- create_dir_entry(refs->loose, "refs/", 5, 1));
+ create_dir_entry(refs->loose, "refs/", 5));
}
return refs->loose;
}
-static int files_read_raw_ref(struct ref_store *ref_store,
- const char *refname, struct object_id *oid,
- struct strbuf *referent, unsigned int *type)
+static int files_read_raw_ref(struct ref_store *ref_store, const char *refname,
+ struct object_id *oid, struct strbuf *referent,
+ unsigned int *type, int *failure_errno)
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
@@ -354,7 +356,6 @@ static int files_read_raw_ref(struct ref_store *ref_store,
struct stat st;
int fd;
int ret = -1;
- int save_errno;
int remaining_retries = 3;
*type = 0;
@@ -459,10 +460,9 @@ stat_ref:
ret = parse_loose_ref_contents(buf, oid, referent, type);
out:
- save_errno = errno;
+ *failure_errno = errno;
strbuf_release(&sb_path);
strbuf_release(&sb_contents);
- errno = save_errno;
return ret;
}
@@ -531,7 +531,6 @@ static void unlock_ref(struct ref_lock *lock)
static int lock_raw_ref(struct files_ref_store *refs,
const char *refname, int mustexist,
const struct string_list *extras,
- const struct string_list *skip,
struct ref_lock **lock_p,
struct strbuf *referent,
unsigned int *type,
@@ -541,6 +540,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
struct strbuf ref_file = STRBUF_INIT;
int attempts_remaining = 3;
int ret = TRANSACTION_GENERIC_ERROR;
+ int failure_errno;
assert(err);
files_assert_main_repository(refs, "lock_raw_ref");
@@ -568,7 +568,7 @@ retry:
* reason to expect this error to be transitory.
*/
if (refs_verify_refname_available(&refs->base, refname,
- extras, skip, err)) {
+ extras, NULL, err)) {
if (mustexist) {
/*
* To the user the relevant error is
@@ -611,7 +611,9 @@ retry:
if (hold_lock_file_for_update_timeout(
&lock->lk, ref_file.buf, LOCK_NO_DEREF,
get_files_ref_lock_timeout_ms()) < 0) {
- if (errno == ENOENT && --attempts_remaining > 0) {
+ int myerr = errno;
+ errno = 0;
+ if (myerr == ENOENT && --attempts_remaining > 0) {
/*
* Maybe somebody just deleted one of the
* directories leading to ref_file. Try
@@ -619,7 +621,7 @@ retry:
*/
goto retry;
} else {
- unable_to_lock_message(ref_file.buf, errno, err);
+ unable_to_lock_message(ref_file.buf, myerr, err);
goto error_return;
}
}
@@ -629,9 +631,9 @@ retry:
* fear that its value will change.
*/
- if (files_read_raw_ref(&refs->base, refname,
- &lock->old_oid, referent, type)) {
- if (errno == ENOENT) {
+ if (files_read_raw_ref(&refs->base, refname, &lock->old_oid, referent,
+ type, &failure_errno)) {
+ if (failure_errno == ENOENT) {
if (mustexist) {
/* Garden variety missing reference. */
strbuf_addf(err, "unable to resolve reference '%s'",
@@ -655,7 +657,7 @@ retry:
* reference named "refs/foo/bar/baz".
*/
}
- } else if (errno == EISDIR) {
+ } else if (failure_errno == EISDIR) {
/*
* There is a directory in the way. It might have
* contained references that have been deleted. If
@@ -673,7 +675,7 @@ retry:
REMOVE_DIR_EMPTY_ONLY)) {
if (refs_verify_refname_available(
&refs->base, refname,
- extras, skip, err)) {
+ extras, NULL, err)) {
/*
* The error message set by
* verify_refname_available() is OK.
@@ -693,13 +695,13 @@ retry:
goto error_return;
}
}
- } else if (errno == EINVAL && (*type & REF_ISBROKEN)) {
+ } else if (failure_errno == EINVAL && (*type & REF_ISBROKEN)) {
strbuf_addf(err, "unable to resolve reference '%s': "
"reference broken", refname);
goto error_return;
} else {
strbuf_addf(err, "unable to resolve reference '%s': %s",
- refname, strerror(errno));
+ refname, strerror(failure_errno));
goto error_return;
}
@@ -710,7 +712,7 @@ retry:
*/
if (refs_verify_refname_available(
refs->packed_ref_store, refname,
- extras, skip, err))
+ extras, NULL, err))
goto error_return;
}
@@ -730,6 +732,7 @@ struct files_ref_iterator {
struct ref_iterator base;
struct ref_iterator *iter0;
+ struct repository *repo;
unsigned int flags;
};
@@ -744,8 +747,14 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE)
continue;
+ if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
+ (iter->iter0->flags & REF_ISSYMREF) &&
+ (iter->iter0->flags & REF_ISBROKEN))
+ continue;
+
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
!ref_resolves_to_object(iter->iter0->refname,
+ iter->repo,
iter->iter0->oid,
iter->iter0->flags))
continue;
@@ -824,7 +833,7 @@ static struct ref_iterator *files_ref_iterator_begin(
*/
loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs),
- prefix, 1);
+ prefix, ref_store->repo, 1);
/*
* The packed-refs file might contain broken references, for
@@ -848,45 +857,119 @@ static struct ref_iterator *files_ref_iterator_begin(
base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable,
overlay_iter->ordered);
iter->iter0 = overlay_iter;
+ iter->repo = ref_store->repo;
iter->flags = flags;
return ref_iterator;
}
/*
- * Verify that the reference locked by lock has the value old_oid
- * (unless it is NULL). Fail if the reference doesn't exist and
- * mustexist is set. Return 0 on success. On error, write an error
- * message to err, set errno, and return a negative value.
+ * Callback function for raceproof_create_file(). This function is
+ * expected to do something that makes dirname(path) permanent despite
+ * the fact that other processes might be cleaning up empty
+ * directories at the same time. Usually it will create a file named
+ * path, but alternatively it could create another file in that
+ * directory, or even chdir() into that directory. The function should
+ * return 0 if the action was completed successfully. On error, it
+ * should return a nonzero result and set errno.
+ * raceproof_create_file() treats two errno values specially:
+ *
+ * - ENOENT -- dirname(path) does not exist. In this case,
+ * raceproof_create_file() tries creating dirname(path)
+ * (and any parent directories, if necessary) and calls
+ * the function again.
+ *
+ * - EISDIR -- the file already exists and is a directory. In this
+ * case, raceproof_create_file() removes the directory if
+ * it is empty (and recursively any empty directories that
+ * it contains) and calls the function again.
+ *
+ * Any other errno causes raceproof_create_file() to fail with the
+ * callback's return value and errno.
+ *
+ * Obviously, this function should be OK with being called again if it
+ * fails with ENOENT or EISDIR. In other scenarios it will not be
+ * called again.
+ */
+typedef int create_file_fn(const char *path, void *cb);
+
+/*
+ * Create a file in dirname(path) by calling fn, creating leading
+ * directories if necessary. Retry a few times in case we are racing
+ * with another process that is trying to clean up the directory that
+ * contains path. See the documentation for create_file_fn for more
+ * details.
+ *
+ * Return the value and set the errno that resulted from the most
+ * recent call of fn. fn is always called at least once, and will be
+ * called more than once if it returns ENOENT or EISDIR.
*/
-static int verify_lock(struct ref_store *ref_store, struct ref_lock *lock,
- const struct object_id *old_oid, int mustexist,
- struct strbuf *err)
+static int raceproof_create_file(const char *path, create_file_fn fn, void *cb)
{
- assert(err);
+ /*
+ * The number of times we will try to remove empty directories
+ * in the way of path. This is only 1 because if another
+ * process is racily creating directories that conflict with
+ * us, we don't want to fight against them.
+ */
+ int remove_directories_remaining = 1;
- if (refs_read_ref_full(ref_store, lock->ref_name,
- mustexist ? RESOLVE_REF_READING : 0,
- &lock->old_oid, NULL)) {
- if (old_oid) {
- int save_errno = errno;
- strbuf_addf(err, "can't verify ref '%s'", lock->ref_name);
- errno = save_errno;
- return -1;
- } else {
- oidclr(&lock->old_oid);
- return 0;
- }
- }
- if (old_oid && !oideq(&lock->old_oid, old_oid)) {
- strbuf_addf(err, "ref '%s' is at %s but expected %s",
- lock->ref_name,
- oid_to_hex(&lock->old_oid),
- oid_to_hex(old_oid));
- errno = EBUSY;
- return -1;
+ /*
+ * The number of times that we will try to create the
+ * directories containing path. We are willing to attempt this
+ * more than once, because another process could be trying to
+ * clean up empty directories at the same time as we are
+ * trying to create them.
+ */
+ int create_directories_remaining = 3;
+
+ /* A scratch copy of path, filled lazily if we need it: */
+ struct strbuf path_copy = STRBUF_INIT;
+
+ int ret, save_errno;
+
+ /* Sanity check: */
+ assert(*path);
+
+retry_fn:
+ ret = fn(path, cb);
+ save_errno = errno;
+ if (!ret)
+ goto out;
+
+ if (errno == EISDIR && remove_directories_remaining-- > 0) {
+ /*
+ * A directory is in the way. Maybe it is empty; try
+ * to remove it:
+ */
+ if (!path_copy.len)
+ strbuf_addstr(&path_copy, path);
+
+ if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY))
+ goto retry_fn;
+ } else if (errno == ENOENT && create_directories_remaining-- > 0) {
+ /*
+ * Maybe the containing directory didn't exist, or
+ * maybe it was just deleted by a process that is
+ * racing with us to clean up empty directories. Try
+ * to create it:
+ */
+ enum scld_error scld_result;
+
+ if (!path_copy.len)
+ strbuf_addstr(&path_copy, path);
+
+ do {
+ scld_result = safe_create_leading_directories(path_copy.buf);
+ if (scld_result == SCLD_OK)
+ goto retry_fn;
+ } while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0);
}
- return 0;
+
+out:
+ strbuf_release(&path_copy);
+ errno = save_errno;
+ return ret;
}
static int remove_empty_directories(struct strbuf *path)
@@ -910,64 +993,27 @@ static int create_reflock(const char *path, void *cb)
/*
* Locks a ref returning the lock on success and NULL on failure.
- * On failure errno is set to something meaningful.
*/
static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
- const char *refname,
- const struct object_id *old_oid,
- const struct string_list *extras,
- const struct string_list *skip,
- unsigned int flags, int *type,
+ const char *refname, int *type,
struct strbuf *err)
{
struct strbuf ref_file = STRBUF_INIT;
struct ref_lock *lock;
- int last_errno = 0;
- int mustexist = (old_oid && !is_null_oid(old_oid));
- int resolve_flags = RESOLVE_REF_NO_RECURSE;
- int resolved;
files_assert_main_repository(refs, "lock_ref_oid_basic");
assert(err);
CALLOC_ARRAY(lock, 1);
- if (mustexist)
- resolve_flags |= RESOLVE_REF_READING;
- if (flags & REF_DELETING)
- resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
-
files_ref_path(refs, &ref_file, refname);
- resolved = !!refs_resolve_ref_unsafe(&refs->base,
- refname, resolve_flags,
- &lock->old_oid, type);
- if (!resolved && errno == EISDIR) {
- /*
- * we are trying to lock foo but we used to
- * have foo/bar which now does not exist;
- * it is normal for the empty directory 'foo'
- * to remain.
- */
- if (remove_empty_directories(&ref_file)) {
- last_errno = errno;
- if (!refs_verify_refname_available(
- &refs->base,
- refname, extras, skip, err))
- strbuf_addf(err, "there are still refs under '%s'",
- refname);
- goto error_return;
- }
- resolved = !!refs_resolve_ref_unsafe(&refs->base,
- refname, resolve_flags,
- &lock->old_oid, type);
- }
- if (!resolved) {
- last_errno = errno;
- if (last_errno != ENOTDIR ||
- !refs_verify_refname_available(&refs->base, refname,
- extras, skip, err))
+ if (!refs_resolve_ref_unsafe(&refs->base, refname,
+ RESOLVE_REF_NO_RECURSE,
+ &lock->old_oid, type)) {
+ if (!refs_verify_refname_available(&refs->base, refname,
+ NULL, NULL, err))
strbuf_addf(err, "unable to resolve reference '%s': %s",
- refname, strerror(last_errno));
+ refname, strerror(errno));
goto error_return;
}
@@ -980,23 +1026,20 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
*/
if (is_null_oid(&lock->old_oid) &&
refs_verify_refname_available(refs->packed_ref_store, refname,
- extras, skip, err)) {
- last_errno = ENOTDIR;
+ NULL, NULL, err))
goto error_return;
- }
lock->ref_name = xstrdup(refname);
if (raceproof_create_file(ref_file.buf, create_reflock, &lock->lk)) {
- last_errno = errno;
unable_to_lock_message(ref_file.buf, errno, err);
goto error_return;
}
- if (verify_lock(&refs->base, lock, old_oid, mustexist, err)) {
- last_errno = errno;
- goto error_return;
- }
+ if (refs_read_ref_full(&refs->base, lock->ref_name,
+ 0,
+ &lock->old_oid, NULL))
+ oidclr(&lock->old_oid);
goto out;
error_return:
@@ -1005,7 +1048,6 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
out:
strbuf_release(&ref_file);
- errno = last_errno;
return lock;
}
@@ -1132,7 +1174,7 @@ static int should_pack_ref(const char *refname,
return 0;
/* Do not pack broken refs: */
- if (!ref_resolves_to_object(refname, oid, ref_flags))
+ if (!ref_resolves_to_object(refname, the_repository, oid, ref_flags))
return 0;
return 1;
@@ -1155,7 +1197,8 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
- iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, 0);
+ iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL,
+ the_repository, 0);
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
/*
* If the loose reference can be packed, add an entry
@@ -1415,8 +1458,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
logmoved = log;
- lock = lock_ref_oid_basic(refs, newrefname, NULL, NULL, NULL,
- REF_NO_DEREF, NULL, &err);
+ lock = lock_ref_oid_basic(refs, newrefname, NULL, &err);
if (!lock) {
if (copy)
error("unable to copy '%s' to '%s': %s", oldrefname, newrefname, err.buf);
@@ -1438,8 +1480,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
goto out;
rollback:
- lock = lock_ref_oid_basic(refs, oldrefname, NULL, NULL, NULL,
- REF_NO_DEREF, NULL, &err);
+ lock = lock_ref_oid_basic(refs, oldrefname, NULL, &err);
if (!lock) {
error("unable to lock %s for rollback: %s", oldrefname, err.buf);
strbuf_release(&err);
@@ -1846,9 +1887,7 @@ static int files_create_symref(struct ref_store *ref_store,
struct ref_lock *lock;
int ret;
- lock = lock_ref_oid_basic(refs, refname, NULL,
- NULL, NULL, REF_NO_DEREF, NULL,
- &err);
+ lock = lock_ref_oid_basic(refs, refname, NULL, &err);
if (!lock) {
error("%s", err.buf);
strbuf_release(&err);
@@ -2416,7 +2455,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
}
ret = lock_raw_ref(refs, update->refname, mustexist,
- affected_refnames, NULL,
+ affected_refnames,
&lock, &referent,
&update->type, err);
if (ret) {
@@ -3037,7 +3076,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
}
static int files_reflog_expire(struct ref_store *ref_store,
- const char *refname, const struct object_id *oid,
+ const char *refname,
unsigned int flags,
reflog_expiry_prepare_fn prepare_fn,
reflog_expiry_should_prune_fn should_prune_fn,
@@ -3054,6 +3093,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
int status = 0;
int type;
struct strbuf err = STRBUF_INIT;
+ const struct object_id *oid;
memset(&cb, 0, sizeof(cb));
cb.flags = flags;
@@ -3065,14 +3105,26 @@ static int files_reflog_expire(struct ref_store *ref_store,
* reference itself, plus we might need to update the
* reference if --updateref was specified:
*/
- lock = lock_ref_oid_basic(refs, refname, oid,
- NULL, NULL, REF_NO_DEREF,
- &type, &err);
+ lock = lock_ref_oid_basic(refs, refname, &type, &err);
if (!lock) {
error("cannot lock ref '%s': %s", refname, err.buf);
strbuf_release(&err);
return -1;
}
+ oid = &lock->old_oid;
+
+ /*
+ * When refs are deleted, their reflog is deleted before the
+ * ref itself is deleted. This is because there is no separate
+ * lock for reflog; instead we take a lock on the ref with
+ * lock_ref_oid_basic().
+ *
+ * If a race happens and the reflog doesn't exist after we've
+ * acquired the lock that's OK. We've got nothing more to do;
+ * We were asked to delete the reflog, but someone else
+ * deleted it! The caller doesn't care that we deleted it,
+ * just that it is deleted. So we can return successfully.
+ */
if (!refs_reflog_exists(ref_store, refname)) {
unlock_ref(lock);
return 0;