mirror of
https://github.com/git/git
synced 2024-08-25 02:35:46 +00:00
Merge branch 'hn/refs-errno-cleanup'
Futz with the way 'errno' is relied on in the refs API to carry the failure modes up the call chain. * hn/refs-errno-cleanup: refs: make errno output explicit for read_raw_ref_fn refs/files-backend: stop setting errno from lock_ref_oid_basic refs: remove EINVAL errno output from specification of read_raw_ref_fn refs file backend: move raceproof_create_file() here
This commit is contained in:
commit
92382d14cd
43
cache.h
43
cache.h
|
@ -1210,49 +1210,6 @@ enum scld_error safe_create_leading_directories(char *path);
|
|||
enum scld_error safe_create_leading_directories_const(const char *path);
|
||||
enum scld_error safe_create_leading_directories_no_share(char *path);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
|
||||
|
||||
int mkdir_in_gitdir(const char *path);
|
||||
char *interpolate_path(const char *path, int real_home);
|
||||
/* NEEDSWORK: remove this synonym once in-flight topics have migrated */
|
||||
|
|
|
@ -415,74 +415,6 @@ enum scld_error safe_create_leading_directories_const(const char *path)
|
|||
return result;
|
||||
}
|
||||
|
||||
int raceproof_create_file(const char *path, create_file_fn fn, void *cb)
|
||||
{
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
out:
|
||||
strbuf_release(&path_copy);
|
||||
errno = save_errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)
|
||||
{
|
||||
int i;
|
||||
|
|
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,
|
||||
type);
|
||||
type, &errno);
|
||||
}
|
||||
|
||||
/* This function needs to return a meaningful errno on failure */
|
||||
|
|
|
@ -239,15 +239,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,
|
||||
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;
|
||||
int res = 0;
|
||||
|
||||
oidcpy(oid, null_oid());
|
||||
errno = 0;
|
||||
res = drefs->refs->be->read_raw_ref(drefs->refs, refname, oid, referent,
|
||||
type);
|
||||
type, failure_errno);
|
||||
|
||||
if (res == 0) {
|
||||
trace_printf_key(&trace_refs, "read_raw_ref: %s: %s (=> %s) type %x: %d\n",
|
||||
|
@ -255,7 +254,7 @@ static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname,
|
|||
} else {
|
||||
trace_printf_key(&trace_refs,
|
||||
"read_raw_ref: %s: %d (errno %d)\n", refname,
|
||||
res, errno);
|
||||
res, *failure_errno);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -341,9 +341,9 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
|
|||
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 +354,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 +458,9 @@ static int files_read_raw_ref(struct ref_store *ref_store,
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -540,6 +538,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");
|
||||
|
@ -610,7 +609,9 @@ static int lock_raw_ref(struct files_ref_store *refs,
|
|||
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
|
||||
|
@ -618,7 +619,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
|
|||
*/
|
||||
goto retry;
|
||||
} else {
|
||||
unable_to_lock_message(ref_file.buf, errno, err);
|
||||
unable_to_lock_message(ref_file.buf, myerr, err);
|
||||
goto error_return;
|
||||
}
|
||||
}
|
||||
|
@ -628,9 +629,9 @@ static int lock_raw_ref(struct files_ref_store *refs,
|
|||
* 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'",
|
||||
|
@ -654,7 +655,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
|
|||
* 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
|
||||
|
@ -692,13 +693,13 @@ static int lock_raw_ref(struct files_ref_store *refs,
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -852,6 +853,115 @@ static struct ref_iterator *files_ref_iterator_begin(
|
|||
return ref_iterator;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 raceproof_create_file(const char *path, create_file_fn fn, void *cb)
|
||||
{
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
out:
|
||||
strbuf_release(&path_copy);
|
||||
errno = save_errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int remove_empty_directories(struct strbuf *path)
|
||||
{
|
||||
/*
|
||||
|
@ -873,7 +983,6 @@ 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, int *type,
|
||||
|
@ -881,7 +990,6 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
|
|||
{
|
||||
struct strbuf ref_file = STRBUF_INIT;
|
||||
struct ref_lock *lock;
|
||||
int last_errno = 0;
|
||||
|
||||
files_assert_main_repository(refs, "lock_ref_oid_basic");
|
||||
assert(err);
|
||||
|
@ -892,11 +1000,10 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
|
|||
if (!refs_resolve_ref_unsafe(&refs->base, refname,
|
||||
RESOLVE_REF_NO_RECURSE,
|
||||
&lock->old_oid, type)) {
|
||||
last_errno = errno;
|
||||
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;
|
||||
}
|
||||
|
@ -909,15 +1016,12 @@ 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,
|
||||
NULL, NULL, 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;
|
||||
}
|
||||
|
@ -934,7 +1038,6 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
|
|||
|
||||
out:
|
||||
strbuf_release(&ref_file);
|
||||
errno = last_errno;
|
||||
return lock;
|
||||
}
|
||||
|
||||
|
|
|
@ -724,9 +724,9 @@ static struct snapshot *get_snapshot(struct packed_ref_store *refs)
|
|||
return refs->snapshot;
|
||||
}
|
||||
|
||||
static int packed_read_raw_ref(struct ref_store *ref_store,
|
||||
const char *refname, struct object_id *oid,
|
||||
struct strbuf *referent, unsigned int *type)
|
||||
static int packed_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 packed_ref_store *refs =
|
||||
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) {
|
||||
/* refname is not a packed reference. */
|
||||
errno = ENOENT;
|
||||
*failure_errno = ENOENT;
|
||||
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
|
||||
* OUTPUT REFERENCE NAMES ARE VALIDATED WITHIN THIS FUNCTION.
|
||||
*
|
||||
* Return 0 on success. If the ref doesn't exist, set errno to ENOENT
|
||||
* and return -1. If the ref exists but is neither a symbolic ref nor
|
||||
* an object ID, it is broken; set REF_ISBROKEN in type, set errno to
|
||||
* EINVAL, and return -1. If there is another error reading the ref,
|
||||
* set errno appropriately and return -1.
|
||||
* Return 0 on success, or -1 on failure. If the ref exists but is neither a
|
||||
* symbolic ref nor an object ID, it is broken. In this case set REF_ISBROKEN in
|
||||
* type, and return -1 (failure_errno should not be ENOENT)
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
* refname will still be valid and unchanged.
|
||||
*/
|
||||
typedef int read_raw_ref_fn(struct ref_store *ref_store,
|
||||
const char *refname, struct object_id *oid,
|
||||
struct strbuf *referent, unsigned int *type);
|
||||
typedef int read_raw_ref_fn(struct ref_store *ref_store, const char *refname,
|
||||
struct object_id *oid, struct strbuf *referent,
|
||||
unsigned int *type, int *failure_errno);
|
||||
|
||||
struct ref_storage_be {
|
||||
struct ref_storage_be *next;
|
||||
|
|
Loading…
Reference in a new issue