files-backend: break out ref reading

Refactor resolve_ref_1 in terms of a new function read_raw_ref, which
is responsible for reading ref data from the ref storage.

Later, we will make read_raw_ref a pluggable backend function, and make
resolve_ref_unsafe common.

Signed-off-by: David Turner <dturner@twopensource.com>
Helped-by: Duy Nguyen <pclouds@gmail.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
David Turner 2016-04-07 15:03:01 -04:00 committed by Junio C Hamano
parent afbe782fa3
commit 7048653a73

View File

@ -1390,6 +1390,141 @@ static int resolve_missing_loose_ref(const char *refname,
return -1;
}
/*
* Read a raw ref from the filesystem or packed refs file.
*
* If the ref is a sha1, fill in sha1 and return 0.
*
* If the ref is symbolic, fill in *symref with the referrent
* (e.g. "refs/heads/master") and return 0. The caller is responsible
* for validating the referrent. Set REF_ISSYMREF in flags.
*
* If the ref doesn't exist, set errno to ENOENT and return -1.
*
* If the ref exists but is neither a symbolic ref nor a sha1, it is
* broken. Set REF_ISBROKEN in flags, set errno to EINVAL, and return
* -1.
*
* If there is another error reading the ref, set errno appropriately and
* return -1.
*
* Backend-specific flags might be set in flags as well, regardless of
* outcome.
*
* sb_path is workspace: the caller should allocate and free it.
*
* It is OK for refname to point into symref. In this case:
* - if the function succeeds with REF_ISSYMREF, symref will be
* overwritten and the memory pointed to by refname might be changed
* or even freed.
* - in all other cases, symref will be untouched, and therefore
* refname will still be valid and unchanged.
*/
static int read_raw_ref(const char *refname, unsigned char *sha1,
struct strbuf *symref, struct strbuf *sb_path,
struct strbuf *sb_contents, int *flags)
{
const char *path;
const char *buf;
struct stat st;
int fd;
strbuf_reset(sb_path);
strbuf_git_path(sb_path, "%s", refname);
path = sb_path->buf;
stat_ref:
/*
* We might have to loop back here to avoid a race
* condition: first we lstat() the file, then we try
* to read it as a link or as a file. But if somebody
* changes the type of the file (file <-> directory
* <-> symlink) between the lstat() and reading, then
* we don't want to report that as an error but rather
* try again starting with the lstat().
*/
if (lstat(path, &st) < 0) {
if (errno != ENOENT)
return -1;
if (resolve_missing_loose_ref(refname, sha1, flags)) {
errno = ENOENT;
return -1;
}
return 0;
}
/* Follow "normalized" - ie "refs/.." symlinks by hand */
if (S_ISLNK(st.st_mode)) {
strbuf_reset(sb_contents);
if (strbuf_readlink(sb_contents, path, 0) < 0) {
if (errno == ENOENT || errno == EINVAL)
/* inconsistent with lstat; retry */
goto stat_ref;
else
return -1;
}
if (starts_with(sb_contents->buf, "refs/") &&
!check_refname_format(sb_contents->buf, 0)) {
strbuf_swap(sb_contents, symref);
*flags |= REF_ISSYMREF;
return 0;
}
}
/* Is it a directory? */
if (S_ISDIR(st.st_mode)) {
errno = EISDIR;
return -1;
}
/*
* Anything else, just open it and try to use it as
* a ref
*/
fd = open(path, O_RDONLY);
if (fd < 0) {
if (errno == ENOENT)
/* inconsistent with lstat; retry */
goto stat_ref;
else
return -1;
}
strbuf_reset(sb_contents);
if (strbuf_read(sb_contents, fd, 256) < 0) {
int save_errno = errno;
close(fd);
errno = save_errno;
return -1;
}
close(fd);
strbuf_rtrim(sb_contents);
buf = sb_contents->buf;
if (starts_with(buf, "ref:")) {
buf += 4;
while (isspace(*buf))
buf++;
strbuf_reset(symref);
strbuf_addstr(symref, buf);
*flags |= REF_ISSYMREF;
return 0;
}
/*
* Please note that FETCH_HEAD has additional
* data after the sha.
*/
if (get_sha1_hex(buf, sha1) ||
(buf[40] != '\0' && !isspace(buf[40]))) {
*flags |= REF_ISBROKEN;
errno = EINVAL;
return -1;
}
return 0;
}
/* This function needs to return a meaningful errno on failure */
static const char *resolve_ref_1(const char *refname,
int resolve_flags,
@ -1422,34 +1557,22 @@ static const char *resolve_ref_1(const char *refname,
}
for (symref_count = 0; symref_count < MAXDEPTH; symref_count++) {
const char *path;
struct stat st;
int fd;
int read_flags = 0;
strbuf_reset(sb_path);
strbuf_git_path(sb_path, "%s", refname);
path = sb_path->buf;
/*
* We might have to loop back here to avoid a race
* condition: first we lstat() the file, then we try
* to read it as a link or as a file. But if somebody
* changes the type of the file (file <-> directory
* <-> symlink) between the lstat() and reading, then
* we don't want to report that as an error but rather
* try again starting with the lstat().
*/
stat_ref:
if (lstat(path, &st) < 0) {
if (errno != ENOENT)
if (read_raw_ref(refname, sha1, sb_refname,
sb_path, sb_contents, &read_flags)) {
*flags |= read_flags;
if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING))
return NULL;
if (resolve_missing_loose_ref(refname, sha1, flags)) {
if (resolve_flags & RESOLVE_REF_READING) {
errno = ENOENT;
return NULL;
}
hashclr(sha1);
}
hashclr(sha1);
if (*flags & REF_BAD_NAME)
*flags |= REF_ISBROKEN;
return refname;
}
*flags |= read_flags;
if (!(read_flags & REF_ISSYMREF)) {
if (*flags & REF_BAD_NAME) {
hashclr(sha1);
*flags |= REF_ISBROKEN;
@ -1457,83 +1580,6 @@ static const char *resolve_ref_1(const char *refname,
return refname;
}
/* Follow "normalized" - ie "refs/.." symlinks by hand */
if (S_ISLNK(st.st_mode)) {
strbuf_reset(sb_contents);
if (strbuf_readlink(sb_contents, path, 0) < 0) {
if (errno == ENOENT || errno == EINVAL)
/* inconsistent with lstat; retry */
goto stat_ref;
else
return NULL;
}
if (starts_with(sb_contents->buf, "refs/") &&
!check_refname_format(sb_contents->buf, 0)) {
strbuf_swap(sb_refname, sb_contents);
refname = sb_refname->buf;
*flags |= REF_ISSYMREF;
if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
hashclr(sha1);
return refname;
}
continue;
}
}
/* Is it a directory? */
if (S_ISDIR(st.st_mode)) {
errno = EISDIR;
return NULL;
}
/*
* Anything else, just open it and try to use it as
* a ref
*/
fd = open(path, O_RDONLY);
if (fd < 0) {
if (errno == ENOENT)
/* inconsistent with lstat; retry */
goto stat_ref;
else
return NULL;
}
strbuf_reset(sb_contents);
if (strbuf_read(sb_contents, fd, 256) < 0) {
int save_errno = errno;
close(fd);
errno = save_errno;
return NULL;
}
close(fd);
strbuf_rtrim(sb_contents);
/*
* Is it a symbolic ref?
*/
if (!starts_with(sb_contents->buf, "ref:")) {
/*
* Please note that FETCH_HEAD has a second
* line containing other data.
*/
if (get_sha1_hex(sb_contents->buf, sha1) ||
(sb_contents->buf[40] != '\0' && !isspace(sb_contents->buf[40]))) {
*flags |= REF_ISBROKEN;
errno = EINVAL;
return NULL;
}
if (*flags & REF_BAD_NAME) {
hashclr(sha1);
*flags |= REF_ISBROKEN;
}
return refname;
}
*flags |= REF_ISSYMREF;
refname = sb_contents->buf + 4;
while (isspace(*refname))
refname++;
strbuf_reset(sb_refname);
strbuf_addstr(sb_refname, refname);
refname = sb_refname->buf;
if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
hashclr(sha1);