refs: make errno output explicit for read_raw_ref_fn
This makes it explicit how alternative ref backends should report errors in read_raw_ref_fn. read_raw_ref_fn needs to supply a credible errno for a number of cases. These are primarily: 1) The files backend calls read_raw_ref from lock_raw_ref, and uses the resulting error codes to create/remove directories as needed. 2) ENOENT should be translated in a zero OID, optionally with REF_ISBROKEN set, returning the last successfully resolved symref. This is necessary so read_raw_ref("HEAD") on an empty repo returns refs/heads/main (or the default branch du-jour), and we know on which branch to create the first commit. Make this information flow explicit by adding a failure_errno to the signature of read_raw_ref. All errnos from the files backend are still propagated unchanged, even though inspection suggests only ENOTDIR, EISDIR and ENOENT are relevant. Signed-off-by: Han-Wen Nienhuys <hanwen@google.com> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
1ae6ed230a
commit
5b12e16bb1
2
refs.c
2
refs.c
@ -1682,7 +1682,7 @@ int refs_read_raw_ref(struct ref_store *ref_store,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ref_store->be->read_raw_ref(ref_store, refname, oid, referent,
|
return ref_store->be->read_raw_ref(ref_store, refname, oid, referent,
|
||||||
type);
|
type, &errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function needs to return a meaningful errno on failure */
|
/* This function needs to return a meaningful errno on failure */
|
||||||
|
@ -238,15 +238,14 @@ debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
|
|||||||
|
|
||||||
static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname,
|
static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname,
|
||||||
struct object_id *oid, struct strbuf *referent,
|
struct object_id *oid, struct strbuf *referent,
|
||||||
unsigned int *type)
|
unsigned int *type, int *failure_errno)
|
||||||
{
|
{
|
||||||
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
|
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
oidcpy(oid, null_oid());
|
oidcpy(oid, null_oid());
|
||||||
errno = 0;
|
|
||||||
res = drefs->refs->be->read_raw_ref(drefs->refs, refname, oid, referent,
|
res = drefs->refs->be->read_raw_ref(drefs->refs, refname, oid, referent,
|
||||||
type);
|
type, failure_errno);
|
||||||
|
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
trace_printf_key(&trace_refs, "read_raw_ref: %s: %s (=> %s) type %x: %d\n",
|
trace_printf_key(&trace_refs, "read_raw_ref: %s: %s (=> %s) type %x: %d\n",
|
||||||
@ -254,7 +253,7 @@ static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname,
|
|||||||
} else {
|
} else {
|
||||||
trace_printf_key(&trace_refs,
|
trace_printf_key(&trace_refs,
|
||||||
"read_raw_ref: %s: %d (errno %d)\n", refname,
|
"read_raw_ref: %s: %d (errno %d)\n", refname,
|
||||||
res, errno);
|
res, *failure_errno);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -341,9 +341,9 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
|
|||||||
return refs->loose;
|
return refs->loose;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int files_read_raw_ref(struct ref_store *ref_store,
|
static int files_read_raw_ref(struct ref_store *ref_store, const char *refname,
|
||||||
const char *refname, struct object_id *oid,
|
struct object_id *oid, struct strbuf *referent,
|
||||||
struct strbuf *referent, unsigned int *type)
|
unsigned int *type, int *failure_errno)
|
||||||
{
|
{
|
||||||
struct files_ref_store *refs =
|
struct files_ref_store *refs =
|
||||||
files_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
|
files_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
|
||||||
@ -354,7 +354,6 @@ static int files_read_raw_ref(struct ref_store *ref_store,
|
|||||||
struct stat st;
|
struct stat st;
|
||||||
int fd;
|
int fd;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
int save_errno;
|
|
||||||
int remaining_retries = 3;
|
int remaining_retries = 3;
|
||||||
|
|
||||||
*type = 0;
|
*type = 0;
|
||||||
@ -459,10 +458,9 @@ stat_ref:
|
|||||||
ret = parse_loose_ref_contents(buf, oid, referent, type);
|
ret = parse_loose_ref_contents(buf, oid, referent, type);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
save_errno = errno;
|
*failure_errno = errno;
|
||||||
strbuf_release(&sb_path);
|
strbuf_release(&sb_path);
|
||||||
strbuf_release(&sb_contents);
|
strbuf_release(&sb_contents);
|
||||||
errno = save_errno;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,6 +538,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
|
|||||||
struct strbuf ref_file = STRBUF_INIT;
|
struct strbuf ref_file = STRBUF_INIT;
|
||||||
int attempts_remaining = 3;
|
int attempts_remaining = 3;
|
||||||
int ret = TRANSACTION_GENERIC_ERROR;
|
int ret = TRANSACTION_GENERIC_ERROR;
|
||||||
|
int failure_errno;
|
||||||
|
|
||||||
assert(err);
|
assert(err);
|
||||||
files_assert_main_repository(refs, "lock_raw_ref");
|
files_assert_main_repository(refs, "lock_raw_ref");
|
||||||
@ -610,7 +609,9 @@ retry:
|
|||||||
if (hold_lock_file_for_update_timeout(
|
if (hold_lock_file_for_update_timeout(
|
||||||
&lock->lk, ref_file.buf, LOCK_NO_DEREF,
|
&lock->lk, ref_file.buf, LOCK_NO_DEREF,
|
||||||
get_files_ref_lock_timeout_ms()) < 0) {
|
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
|
* Maybe somebody just deleted one of the
|
||||||
* directories leading to ref_file. Try
|
* directories leading to ref_file. Try
|
||||||
@ -618,7 +619,7 @@ retry:
|
|||||||
*/
|
*/
|
||||||
goto retry;
|
goto retry;
|
||||||
} else {
|
} else {
|
||||||
unable_to_lock_message(ref_file.buf, errno, err);
|
unable_to_lock_message(ref_file.buf, myerr, err);
|
||||||
goto error_return;
|
goto error_return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -628,9 +629,9 @@ retry:
|
|||||||
* fear that its value will change.
|
* fear that its value will change.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (files_read_raw_ref(&refs->base, refname,
|
if (files_read_raw_ref(&refs->base, refname, &lock->old_oid, referent,
|
||||||
&lock->old_oid, referent, type)) {
|
type, &failure_errno)) {
|
||||||
if (errno == ENOENT) {
|
if (failure_errno == ENOENT) {
|
||||||
if (mustexist) {
|
if (mustexist) {
|
||||||
/* Garden variety missing reference. */
|
/* Garden variety missing reference. */
|
||||||
strbuf_addf(err, "unable to resolve reference '%s'",
|
strbuf_addf(err, "unable to resolve reference '%s'",
|
||||||
@ -654,7 +655,7 @@ retry:
|
|||||||
* reference named "refs/foo/bar/baz".
|
* 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
|
* There is a directory in the way. It might have
|
||||||
* contained references that have been deleted. If
|
* contained references that have been deleted. If
|
||||||
@ -692,13 +693,13 @@ retry:
|
|||||||
goto error_return;
|
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': "
|
strbuf_addf(err, "unable to resolve reference '%s': "
|
||||||
"reference broken", refname);
|
"reference broken", refname);
|
||||||
goto error_return;
|
goto error_return;
|
||||||
} else {
|
} else {
|
||||||
strbuf_addf(err, "unable to resolve reference '%s': %s",
|
strbuf_addf(err, "unable to resolve reference '%s': %s",
|
||||||
refname, strerror(errno));
|
refname, strerror(failure_errno));
|
||||||
goto error_return;
|
goto error_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,9 +724,9 @@ static struct snapshot *get_snapshot(struct packed_ref_store *refs)
|
|||||||
return refs->snapshot;
|
return refs->snapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int packed_read_raw_ref(struct ref_store *ref_store,
|
static int packed_read_raw_ref(struct ref_store *ref_store, const char *refname,
|
||||||
const char *refname, struct object_id *oid,
|
struct object_id *oid, struct strbuf *referent,
|
||||||
struct strbuf *referent, unsigned int *type)
|
unsigned int *type, int *failure_errno)
|
||||||
{
|
{
|
||||||
struct packed_ref_store *refs =
|
struct packed_ref_store *refs =
|
||||||
packed_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
|
packed_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
|
||||||
@ -739,7 +739,7 @@ static int packed_read_raw_ref(struct ref_store *ref_store,
|
|||||||
|
|
||||||
if (!rec) {
|
if (!rec) {
|
||||||
/* refname is not a packed reference. */
|
/* refname is not a packed reference. */
|
||||||
errno = ENOENT;
|
*failure_errno = ENOENT;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,11 +620,15 @@ typedef int reflog_expire_fn(struct ref_store *ref_store,
|
|||||||
* properly-formatted or even safe reference name. NEITHER INPUT NOR
|
* properly-formatted or even safe reference name. NEITHER INPUT NOR
|
||||||
* OUTPUT REFERENCE NAMES ARE VALIDATED WITHIN THIS FUNCTION.
|
* OUTPUT REFERENCE NAMES ARE VALIDATED WITHIN THIS FUNCTION.
|
||||||
*
|
*
|
||||||
* Return 0 on success. If the ref doesn't exist, set errno to ENOENT
|
* Return 0 on success, or -1 on failure. If the ref exists but is neither a
|
||||||
* and return -1. If the ref exists but is neither a symbolic ref nor
|
* symbolic ref nor an object ID, it is broken. In this case set REF_ISBROKEN in
|
||||||
* an object ID, it is broken; set REF_ISBROKEN in type, and return -1
|
* type, and return -1 (failure_errno should not be ENOENT)
|
||||||
* (errno should not be ENOENT) If there is another error reading the
|
*
|
||||||
* ref, set errno appropriately and return -1.
|
* failure_errno provides errno codes that are interpreted beyond error
|
||||||
|
* reporting. The following error codes have special meaning:
|
||||||
|
* * ENOENT: the ref doesn't exist
|
||||||
|
* * EISDIR: ref name is a directory
|
||||||
|
* * ENOTDIR: ref prefix is not a directory
|
||||||
*
|
*
|
||||||
* Backend-specific flags might be set in type as well, regardless of
|
* Backend-specific flags might be set in type as well, regardless of
|
||||||
* outcome.
|
* outcome.
|
||||||
@ -638,9 +642,9 @@ typedef int reflog_expire_fn(struct ref_store *ref_store,
|
|||||||
* - in all other cases, referent will be untouched, and therefore
|
* - in all other cases, referent will be untouched, and therefore
|
||||||
* refname will still be valid and unchanged.
|
* refname will still be valid and unchanged.
|
||||||
*/
|
*/
|
||||||
typedef int read_raw_ref_fn(struct ref_store *ref_store,
|
typedef int read_raw_ref_fn(struct ref_store *ref_store, const char *refname,
|
||||||
const char *refname, struct object_id *oid,
|
struct object_id *oid, struct strbuf *referent,
|
||||||
struct strbuf *referent, unsigned int *type);
|
unsigned int *type, int *failure_errno);
|
||||||
|
|
||||||
struct ref_storage_be {
|
struct ref_storage_be {
|
||||||
struct ref_storage_be *next;
|
struct ref_storage_be *next;
|
||||||
|
Loading…
Reference in New Issue
Block a user