From 9224b73be03845a99f8171c57dc282f806b70f4c Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:24 +0200 Subject: Change bad_ref_char() to return a boolean value Previously most bad characters were indicated by returning 1, but "*" was special-cased to return 2 instead of 1. One caller examined the return value to see whether the special case occurred. But it is easier (to document and understand) for bad_ref_char() simply to return a boolean value, treating "*" like any other bad character. Special-case the handling of "*" (which only occurs in very specific circumstances) at the caller. The resulting calling code thereby also becomes more transparent. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index a615043b34..fd29d894dc 100644 --- a/refs.c +++ b/refs.c @@ -860,22 +860,21 @@ int for_each_rawref(each_ref_fn fn, void *cb_data) * - it contains a "\" (backslash) */ +/* Return true iff ch is not allowed in reference names. */ static inline int bad_ref_char(int ch) { if (((unsigned) ch) <= ' ' || ch == 0x7f || ch == '~' || ch == '^' || ch == ':' || ch == '\\') return 1; /* 2.13 Pattern Matching Notation */ - if (ch == '?' || ch == '[') /* Unsupported */ + if (ch == '*' || ch == '?' || ch == '[') /* Unsupported */ return 1; - if (ch == '*') /* Supported at the end */ - return 2; return 0; } int check_ref_format(const char *ref) { - int ch, level, bad_type, last; + int ch, level, last; int ret = CHECK_REF_FORMAT_OK; const char *cp = ref; @@ -890,9 +889,8 @@ int check_ref_format(const char *ref) /* we are at the beginning of the path component */ if (ch == '.') return CHECK_REF_FORMAT_ERROR; - bad_type = bad_ref_char(ch); - if (bad_type) { - if (bad_type == 2 && (!*cp || *cp == '/') && + if (bad_ref_char(ch)) { + if (ch == '*' && (!*cp || *cp == '/') && ret == CHECK_REF_FORMAT_OK) ret = CHECK_REF_FORMAT_WILDCARD; else @@ -902,8 +900,7 @@ int check_ref_format(const char *ref) last = ch; /* scan the rest of the path component */ while ((ch = *cp++) != 0) { - bad_type = bad_ref_char(ch); - if (bad_type) + if (bad_ref_char(ch)) return CHECK_REF_FORMAT_ERROR; if (ch == '/') break; -- cgit v1.2.3 From 8d9c50105f908b2adde4b7c77537cf95f19cd893 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:25 +0200 Subject: Change check_ref_format() to take a flags argument Change check_ref_format() to take a flags argument that indicates what is acceptable in the reference name (analogous to "git check-ref-format"'s "--allow-onelevel" and "--refspec-pattern"). This is more convenient for callers and also fixes a failure in the test suite (and likely elsewhere in the code) by enabling "onelevel" and "refspec-pattern" to be allowed independently of each other. Also rename check_ref_format() to check_refname_format() to make it obvious that it deals with refnames rather than references themselves. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index fd29d894dc..aaa87304a4 100644 --- a/refs.c +++ b/refs.c @@ -872,10 +872,9 @@ static inline int bad_ref_char(int ch) return 0; } -int check_ref_format(const char *ref) +int check_refname_format(const char *ref, int flags) { int ch, level, last; - int ret = CHECK_REF_FORMAT_OK; const char *cp = ref; level = 0; @@ -884,41 +883,42 @@ int check_ref_format(const char *ref) ; /* tolerate duplicated slashes */ if (!ch) /* should not end with slashes */ - return CHECK_REF_FORMAT_ERROR; + return -1; /* we are at the beginning of the path component */ if (ch == '.') - return CHECK_REF_FORMAT_ERROR; + return -1; if (bad_ref_char(ch)) { - if (ch == '*' && (!*cp || *cp == '/') && - ret == CHECK_REF_FORMAT_OK) - ret = CHECK_REF_FORMAT_WILDCARD; + if ((flags & REFNAME_REFSPEC_PATTERN) && ch == '*' && + (!*cp || *cp == '/')) + /* Accept one wildcard as a full refname component. */ + flags &= ~REFNAME_REFSPEC_PATTERN; else - return CHECK_REF_FORMAT_ERROR; + return -1; } last = ch; /* scan the rest of the path component */ while ((ch = *cp++) != 0) { if (bad_ref_char(ch)) - return CHECK_REF_FORMAT_ERROR; + return -1; if (ch == '/') break; if (last == '.' && ch == '.') - return CHECK_REF_FORMAT_ERROR; + return -1; if (last == '@' && ch == '{') - return CHECK_REF_FORMAT_ERROR; + return -1; last = ch; } level++; if (!ch) { if (ref <= cp - 2 && cp[-2] == '.') - return CHECK_REF_FORMAT_ERROR; - if (level < 2) - return CHECK_REF_FORMAT_ONELEVEL; + return -1; + if (level < 2 && !(flags & REFNAME_ALLOW_ONELEVEL)) + return -1; if (has_extension(ref, ".lock")) - return CHECK_REF_FORMAT_ERROR; - return ret; + return -1; + return 0; } } } @@ -1103,7 +1103,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1) { char refpath[PATH_MAX]; - if (check_ref_format(ref)) + if (check_refname_format(ref, 0)) return NULL; strcpy(refpath, mkpath("refs/%s", ref)); return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL); @@ -1111,13 +1111,9 @@ struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1) struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags) { - switch (check_ref_format(ref)) { - default: + if (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL)) return NULL; - case 0: - case CHECK_REF_FORMAT_ONELEVEL: - return lock_ref_sha1_basic(ref, old_sha1, flags, NULL); - } + return lock_ref_sha1_basic(ref, old_sha1, flags, NULL); } static struct lock_file packlock; -- cgit v1.2.3 From 49295d4e3fb58c6179b7434cd87805f86cb1f7b7 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:26 +0200 Subject: Refactor check_refname_format() Among other things, extract a function check_refname_component(). Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 95 ++++++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 55 insertions(+), 40 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index aaa87304a4..52597244b8 100644 --- a/refs.c +++ b/refs.c @@ -872,55 +872,70 @@ static inline int bad_ref_char(int ch) return 0; } +/* + * Try to read one refname component from the front of ref. Return + * the length of the component found, or -1 if the component is not + * legal. + */ +static int check_refname_component(const char *ref) +{ + const char *cp; + char last = '\0'; + + for (cp = ref; ; cp++) { + char ch = *cp; + if (ch == '\0' || ch == '/') + break; + if (bad_ref_char(ch)) + return -1; /* Illegal character in refname. */ + if (last == '.' && ch == '.') + return -1; /* Refname contains "..". */ + if (last == '@' && ch == '{') + return -1; /* Refname contains "@{". */ + last = ch; + } + if (cp == ref) + return -1; /* Component has zero length. */ + if (ref[0] == '.') + return -1; /* Component starts with '.'. */ + return cp - ref; +} + int check_refname_format(const char *ref, int flags) { - int ch, level, last; - const char *cp = ref; + int component_len, component_count = 0; - level = 0; while (1) { - while ((ch = *cp++) == '/') - ; /* tolerate duplicated slashes */ - if (!ch) - /* should not end with slashes */ - return -1; - - /* we are at the beginning of the path component */ - if (ch == '.') - return -1; - if (bad_ref_char(ch)) { - if ((flags & REFNAME_REFSPEC_PATTERN) && ch == '*' && - (!*cp || *cp == '/')) + while (*ref == '/') + ref++; /* tolerate leading and repeated slashes */ + + /* We are at the start of a path component. */ + component_len = check_refname_component(ref); + if (component_len < 0) { + if ((flags & REFNAME_REFSPEC_PATTERN) && + ref[0] == '*' && + (ref[1] == '\0' || ref[1] == '/')) { /* Accept one wildcard as a full refname component. */ flags &= ~REFNAME_REFSPEC_PATTERN; - else - return -1; - } - - last = ch; - /* scan the rest of the path component */ - while ((ch = *cp++) != 0) { - if (bad_ref_char(ch)) - return -1; - if (ch == '/') - break; - if (last == '.' && ch == '.') - return -1; - if (last == '@' && ch == '{') - return -1; - last = ch; - } - level++; - if (!ch) { - if (ref <= cp - 2 && cp[-2] == '.') - return -1; - if (level < 2 && !(flags & REFNAME_ALLOW_ONELEVEL)) - return -1; - if (has_extension(ref, ".lock")) + component_len = 1; + } else { return -1; - return 0; + } } + component_count++; + if (ref[component_len] == '\0') + break; + /* Skip to next component. */ + ref += component_len + 1; } + + if (ref[component_len - 1] == '.') + return -1; /* Refname ends with '.'. */ + if (component_len >= 5 && !memcmp(&ref[component_len - 5], ".lock", 5)) + return -1; /* Refname ends with ".lock". */ + if (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2) + return -1; /* Refname has only one component. */ + return 0; } const char *prettify_refname(const char *name) -- cgit v1.2.3 From 7e9d2fe96060957d90870d2fac9b85608423e277 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:27 +0200 Subject: Do not allow ".lock" at the end of any refname component Allowing any refname component to end with ".lock" is looking for trouble; for example, $ git br foo.lock/bar $ git br foo fatal: Unable to create '[...]/.git/refs/heads/foo.lock': File exists. Therefore, do not allow any refname component to end with ".lock". Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 52597244b8..5a0bd0f7af 100644 --- a/refs.c +++ b/refs.c @@ -898,6 +898,8 @@ static int check_refname_component(const char *ref) return -1; /* Component has zero length. */ if (ref[0] == '.') return -1; /* Component starts with '.'. */ + if (cp - ref >= 5 && !memcmp(cp - 5, ".lock", 5)) + return -1; /* Refname ends with ".lock". */ return cp - ref; } @@ -931,8 +933,6 @@ int check_refname_format(const char *ref, int flags) if (ref[component_len - 1] == '.') return -1; /* Refname ends with '.'. */ - if (component_len >= 5 && !memcmp(&ref[component_len - 5], ".lock", 5)) - return -1; /* Refname ends with ".lock". */ if (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2) return -1; /* Refname has only one component. */ return 0; -- cgit v1.2.3 From a40e6fb67a4aed2d5ffde5792bf7f1996d9666e1 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:30 +0200 Subject: Change check_refname_format() to reject unnormalized refnames Since much of the infrastructure does not work correctly with unnormalized refnames, change check_refname_format() to reject them. Similarly, change "git check-ref-format" to reject unnormalized refnames by default. But add an option --normalize, which causes "git check-ref-format" to normalize the refname before checking its format, and print the normalized refname. This is exactly the behavior of the old --print option, which is retained but deprecated. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 5a0bd0f7af..d2aac24a36 100644 --- a/refs.c +++ b/refs.c @@ -908,9 +908,6 @@ int check_refname_format(const char *ref, int flags) int component_len, component_count = 0; while (1) { - while (*ref == '/') - ref++; /* tolerate leading and repeated slashes */ - /* We are at the start of a path component. */ component_len = check_refname_component(ref); if (component_len < 0) { -- cgit v1.2.3 From 7bb2bf8e5cc47f7731c9c012ecc943b14f99ee5a Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:31 +0200 Subject: resolve_ref(): explicitly fail if a symlink is not readable Previously the failure came later, after a few steps in which the length was treated like the actual length of a string. Even though the old code gave the same answers, it was somewhat misleading. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'refs.c') diff --git a/refs.c b/refs.c index d2aac24a36..c51fd45f99 100644 --- a/refs.c +++ b/refs.c @@ -518,6 +518,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * /* Follow "normalized" - ie "refs/.." symlinks by hand */ if (S_ISLNK(st.st_mode)) { len = readlink(path, buffer, sizeof(buffer)-1); + if (len < 0) + return NULL; if (len >= 5 && !memcmp("refs/", buffer, 5)) { buffer[len] = 0; strcpy(ref_buffer, buffer); -- cgit v1.2.3 From b54cb795970628714a3a9fa0f4a73cd117b0207f Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:32 +0200 Subject: resolve_ref(): use prefixcmp() Terminate the link content string one step earlier, allowing prefixcmp() to be used instead of the less clear memcmp(). Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index c51fd45f99..da9737f992 100644 --- a/refs.c +++ b/refs.c @@ -520,8 +520,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * len = readlink(path, buffer, sizeof(buffer)-1); if (len < 0) return NULL; - if (len >= 5 && !memcmp("refs/", buffer, 5)) { - buffer[len] = 0; + buffer[len] = 0; + if (!prefixcmp(buffer, "refs/")) { strcpy(ref_buffer, buffer); ref = ref_buffer; if (flag) -- cgit v1.2.3 From 1f58a0383857e5328e3e4d248d6c4a3485098679 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:33 +0200 Subject: resolve_ref(): only follow a symlink that contains a valid, normalized refname Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index da9737f992..8f0b87184b 100644 --- a/refs.c +++ b/refs.c @@ -521,7 +521,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * if (len < 0) return NULL; buffer[len] = 0; - if (!prefixcmp(buffer, "refs/")) { + if (!prefixcmp(buffer, "refs/") && + !check_refname_format(buffer, 0)) { strcpy(ref_buffer, buffer); ref = ref_buffer; if (flag) -- cgit v1.2.3 From 287750507d3859ff65a7b0baac5ba1fdf30e9604 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:34 +0200 Subject: resolve_ref(): turn buffer into a proper string as soon as possible Immediately strip off trailing spaces and null-terminate the string holding the contents of the reference file; this allows the use of string functions and avoids the need to keep separate track of the string's length. (get_sha1_hex() fails automatically if the string is too short.) Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 8f0b87184b..79ab0eb95f 100644 --- a/refs.c +++ b/refs.c @@ -546,25 +546,25 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * return NULL; len = read_in_full(fd, buffer, sizeof(buffer)-1); close(fd); + if (len < 0) + return NULL; + while (len && isspace(buffer[len-1])) + len--; + buffer[len] = '\0'; /* * Is it a symbolic ref? */ - if (len < 4 || memcmp("ref:", buffer, 4)) + if (prefixcmp(buffer, "ref:")) break; buf = buffer + 4; - len -= 4; - while (len && isspace(*buf)) - buf++, len--; - while (len && isspace(buf[len-1])) - len--; - buf[len] = 0; - memcpy(ref_buffer, buf, len + 1); - ref = ref_buffer; + while (isspace(*buf)) + buf++; + ref = strcpy(ref_buffer, buf); if (flag) *flag |= REF_ISSYMREF; } - if (len < 40 || get_sha1_hex(buffer, sha1)) + if (get_sha1_hex(buffer, sha1)) return NULL; return ref; } -- cgit v1.2.3 From c224ca7f66bf88bf933d05ecbc163b5bbb152098 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:35 +0200 Subject: resolve_ref(): extract a function get_packed_ref() Making it a function and giving it a name makes the code clearer. I also have a strong suspicion that the function will find other uses in the future. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 79ab0eb95f..473f7f6bc6 100644 --- a/refs.c +++ b/refs.c @@ -465,6 +465,23 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re return retval; } +/* + * Try to read ref from the packed references. On success, set sha1 + * and return 0; otherwise, return -1. + */ +static int get_packed_ref(const char *ref, unsigned char *sha1) +{ + struct ref_list *list = get_packed_refs(NULL); + while (list) { + if (!strcmp(ref, list->name)) { + hashcpy(sha1, list->sha1); + return 0; + } + list = list->next; + } + return -1; +} + /* * If the "reading" argument is set, this function finds out what _object_ * the ref points at by "reading" the ref. The ref, if it is not symbolic, @@ -497,22 +514,26 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * return NULL; git_snpath(path, sizeof(path), "%s", ref); - /* Special case: non-existing file. */ + if (lstat(path, &st) < 0) { - struct ref_list *list = get_packed_refs(NULL); - while (list) { - if (!strcmp(ref, list->name)) { - hashcpy(sha1, list->sha1); - if (flag) - *flag |= REF_ISPACKED; - return ref; - } - list = list->next; + if (errno != ENOENT) + return NULL; + /* + * The loose reference file does not exist; + * check for a packed reference. + */ + if (!get_packed_ref(ref, sha1)) { + if (flag) + *flag |= REF_ISPACKED; + return ref; } - if (reading || errno != ENOENT) + /* The reference is not a packed reference, either. */ + if (reading) { return NULL; - hashclr(sha1); - return ref; + } else { + hashclr(sha1); + return ref; + } } /* Follow "normalized" - ie "refs/.." symlinks by hand */ -- cgit v1.2.3 From 313fb010da4343eca22ee48a2cc18048d999de53 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:36 +0200 Subject: resolve_ref(): do not follow incorrectly-formatted symbolic refs Emit a warning and fail if a symbolic reference refers to an incorrectly-formatted refname. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 473f7f6bc6..b055501899 100644 --- a/refs.c +++ b/refs.c @@ -581,6 +581,11 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * buf = buffer + 4; while (isspace(*buf)) buf++; + if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) { + warning("symbolic reference in %s is formatted incorrectly", + path); + return NULL; + } ref = strcpy(ref_buffer, buf); if (flag) *flag |= REF_ISSYMREF; -- cgit v1.2.3 From 8384d78886eca05cae2a4c1bccaee379d76c1e06 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:39 +0200 Subject: resolve_ref(): verify that the input refname has the right format Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'refs.c') diff --git a/refs.c b/refs.c index b055501899..ee3e0cc560 100644 --- a/refs.c +++ b/refs.c @@ -504,6 +504,9 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * if (flag) *flag = 0; + if (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL)) + return NULL; + for (;;) { char path[PATH_MAX]; struct stat st; -- cgit v1.2.3 From 629cd3ac6d081b43c8cdb045845be1fedbc89615 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:40 +0200 Subject: resolve_ref(): emit warnings for improperly-formatted references While resolving references, if a reference is found that is in an unrecognized format, emit a warning (and then fail, as before). Wouldn't *you* want to know? Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index ee3e0cc560..2387f4e735 100644 --- a/refs.c +++ b/refs.c @@ -500,6 +500,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * ssize_t len; char buffer[256]; static char ref_buffer[256]; + char path[PATH_MAX]; if (flag) *flag = 0; @@ -508,7 +509,6 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * return NULL; for (;;) { - char path[PATH_MAX]; struct stat st; char *buf; int fd; @@ -593,8 +593,10 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * if (flag) *flag |= REF_ISSYMREF; } - if (get_sha1_hex(buffer, sha1)) + if (get_sha1_hex(buffer, sha1)) { + warning("reference in %s is formatted incorrectly", path); return NULL; + } return ref; } -- cgit v1.2.3 From f989fea0e0b47873de62a355f4766f03a88fb01b Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:41 +0200 Subject: resolve_ref(): also treat a too-long SHA1 as invalid If the SHA1 in a reference file is not terminated by a space or end-of-file, consider it malformed and emit a warning. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 2387f4e735..0baa500cbb 100644 --- a/refs.c +++ b/refs.c @@ -593,7 +593,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * if (flag) *flag |= REF_ISSYMREF; } - if (get_sha1_hex(buffer, sha1)) { + /* Please note that FETCH_HEAD has a second line containing other data. */ + if (get_sha1_hex(buffer, sha1) || (buffer[40] != '\0' && !isspace(buffer[40]))) { warning("reference in %s is formatted incorrectly", path); return NULL; } -- cgit v1.2.3 From 7cb368421f62318f2c0f0e19a83ca34c201aebaa Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:42 +0200 Subject: resolve_ref(): expand documentation Record information about resolve_ref(), hard-won via reverse engineering, in a comment for future spelunkers. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 0baa500cbb..096b42c5e9 100644 --- a/refs.c +++ b/refs.c @@ -482,18 +482,6 @@ static int get_packed_ref(const char *ref, unsigned char *sha1) return -1; } -/* - * If the "reading" argument is set, this function finds out what _object_ - * the ref points at by "reading" the ref. The ref, if it is not symbolic, - * has to exist, and if it is symbolic, it has to point at an existing ref, - * because the "read" goes through the symref to the ref it points at. - * - * The access that is not "reading" may often be "writing", but does not - * have to; it can be merely checking _where it leads to_. If it is a - * prelude to "writing" to the ref, a write to a symref that points at - * yet-to-be-born ref will create the real ref pointed by the symref. - * reading=0 allows the caller to check where such a symref leads to. - */ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag) { int depth = MAXDEPTH; -- cgit v1.2.3 From dce4bab6567de7c458b334e029e3dedcab5f2648 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 15 Sep 2011 23:10:43 +0200 Subject: add_ref(): verify that the refname is formatted correctly In add_ref(), verify that the refname is formatted correctly before adding it to the ref_list. Here we have to allow refname components that start with ".", since (for example) the remote protocol uses synthetic reference name ".have". So add a new REFNAME_DOT_COMPONENT flag that can be passed to check_refname_format() to allow leading dots. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 096b42c5e9..832a52f781 100644 --- a/refs.c +++ b/refs.c @@ -56,6 +56,8 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1, entry = xmalloc(sizeof(struct ref_list) + len); hashcpy(entry->sha1, sha1); hashclr(entry->peeled); + if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT)) + die("Reference has invalid format: '%s'", name); memcpy(entry->name, name, len); entry->flag = flag; entry->next = list; @@ -900,7 +902,7 @@ static inline int bad_ref_char(int ch) * the length of the component found, or -1 if the component is not * legal. */ -static int check_refname_component(const char *ref) +static int check_refname_component(const char *ref, int flags) { const char *cp; char last = '\0'; @@ -919,8 +921,16 @@ static int check_refname_component(const char *ref) } if (cp == ref) return -1; /* Component has zero length. */ - if (ref[0] == '.') - return -1; /* Component starts with '.'. */ + if (ref[0] == '.') { + if (!(flags & REFNAME_DOT_COMPONENT)) + return -1; /* Component starts with '.'. */ + /* + * Even if leading dots are allowed, don't allow "." + * as a component (".." is prevented by a rule above). + */ + if (ref[1] == '\0') + return -1; /* Component equals ".". */ + } if (cp - ref >= 5 && !memcmp(cp - 5, ".lock", 5)) return -1; /* Refname ends with ".lock". */ return cp - ref; @@ -932,7 +942,7 @@ int check_refname_format(const char *ref, int flags) while (1) { /* We are at the start of a path component. */ - component_len = check_refname_component(ref); + component_len = check_refname_component(ref, flags); if (component_len < 0) { if ((flags & REFNAME_REFSPEC_PATTERN) && ref[0] == '*' && -- cgit v1.2.3