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@xxxxxxxxxxxxxxxx> Helped-by: Duy Nguyen <pclouds@xxxxxxxxx> Signed-off-by: Junio C Hamano <gitster@xxxxxxxxx> Signed-off-by: Michael Haggerty <mhagger@xxxxxxxxxxxx> --- refs/files-backend.c | 244 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 145 insertions(+), 99 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index b865ba5..d51e778 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -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,118 +1557,29 @@ 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); - } - if (*flags & REF_BAD_NAME) { - hashclr(sha1); + hashclr(sha1); + if (*flags & REF_BAD_NAME) *flags |= REF_ISBROKEN; - } 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); + *flags |= read_flags; - /* - * 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 (!(read_flags & REF_ISSYMREF)) { 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); -- 2.4.2.767.g62658d5-twtrsrc -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html