diff -r b1e51ef4e536 -r d0ef8c1dddd4 mercurial/cext/manifest.c --- a/mercurial/cext/manifest.c Wed Jul 08 00:15:15 2020 +0200 +++ b/mercurial/cext/manifest.c Mon Jul 06 03:43:32 2020 +0200 @@ -49,23 +49,35 @@ } /* get the node value of a single line */ -static PyObject *nodeof(line *l) +static PyObject *nodeof(line *l, char *flag) { char *s = l->start; Py_ssize_t llen = pathlen(l); Py_ssize_t hlen = l->len - llen - 2; - Py_ssize_t hlen_raw = 20; + Py_ssize_t hlen_raw; PyObject *hash; if (llen + 1 + 40 + 1 > l->len) { /* path '\0' hash '\n' */ PyErr_SetString(PyExc_ValueError, "manifest line too short"); return NULL; } + /* Detect flags after the hash first. */ + switch (s[llen + hlen]) { + case 'l': + case 't': + case 'x': + *flag = s[llen + hlen]; + --hlen; + break; + default: + *flag = '\0'; + break; + } + switch (hlen) { case 40: /* sha1 */ - case 41: /* sha1 with cruft for a merge */ + hlen_raw = 20; break; case 64: /* new hash */ - case 65: /* new hash with cruft for a merge */ hlen_raw = 32; break; default: @@ -89,9 +101,8 @@ /* get the node hash and flags of a line as a tuple */ static PyObject *hashflags(line *l) { - char *s = l->start; - Py_ssize_t plen = pathlen(l); - PyObject *hash = nodeof(l); + char flag; + PyObject *hash = nodeof(l, &flag); ssize_t hlen; Py_ssize_t hplen, flen; PyObject *flags; @@ -99,14 +110,7 @@ if (!hash) return NULL; - /* hash is either 20 or 21 bytes for an old hash, so we use a - ternary here to get the "real" hexlified sha length. */ - hlen = PyBytes_GET_SIZE(hash) < 22 ? 40 : 64; - /* 1 for null byte, 1 for newline */ - hplen = plen + hlen + 2; - flen = l->len - hplen; - - flags = PyBytes_FromStringAndSize(s + hplen - 1, flen); + flags = PyBytes_FromStringAndSize(&flag, flag ? 1 : 0); if (!flags) { Py_DECREF(hash); return NULL; @@ -291,6 +295,7 @@ { Py_ssize_t pl; line *l; + char flag; Py_ssize_t consumed; PyObject *ret = NULL, *path = NULL, *hash = NULL, *flags = NULL; l = lmiter_nextline((lmIter *)o); @@ -299,13 +304,11 @@ } pl = pathlen(l); path = PyBytes_FromStringAndSize(l->start, pl); - hash = nodeof(l); + hash = nodeof(l, &flag); if (!path || !hash) { goto done; } - consumed = pl + 41; - flags = PyBytes_FromStringAndSize(l->start + consumed, - l->len - consumed - 1); + flags = PyBytes_FromStringAndSize(&flag, flag ? 1 : 0); if (!flags) { goto done; } @@ -568,19 +571,13 @@ pyhash = PyTuple_GetItem(value, 0); if (!PyBytes_Check(pyhash)) { PyErr_Format(PyExc_TypeError, - "node must be a 20-byte string"); + "node must be a 20 or 32 bytes string"); return -1; } hlen = PyBytes_Size(pyhash); - /* Some parts of the codebase try and set 21 or 22 - * byte "hash" values in order to perturb things for - * status. We have to preserve at least the 21st - * byte. Sigh. If there's a 22nd byte, we drop it on - * the floor, which works fine. - */ - if (hlen != 20 && hlen != 21 && hlen != 22) { + if (hlen != 20 && hlen != 32) { PyErr_Format(PyExc_TypeError, - "node must be a 20-byte string"); + "node must be a 20 or 32 bytes string"); return -1; } hash = PyBytes_AsString(pyhash); @@ -588,28 +585,39 @@ pyflags = PyTuple_GetItem(value, 1); if (!PyBytes_Check(pyflags) || PyBytes_Size(pyflags) > 1) { PyErr_Format(PyExc_TypeError, - "flags must a 0 or 1 byte string"); + "flags must a 0 or 1 bytes string"); return -1; } if (PyBytes_AsStringAndSize(pyflags, &flags, &flen) == -1) { return -1; } + if (flen == 1) { + switch (*flags) { + case 'l': + case 't': + case 'x': + break; + default: + PyErr_Format(PyExc_TypeError, "invalid manifest flag"); + return -1; + } + } /* one null byte and one newline */ - dlen = plen + 41 + flen + 1; + dlen = plen + hlen * 2 + 1 + flen + 1; dest = malloc(dlen); if (!dest) { PyErr_NoMemory(); return -1; } memcpy(dest, path, plen + 1); - for (i = 0; i < 20; i++) { + for (i = 0; i < hlen; i++) { /* Cast to unsigned, so it will not get sign-extended when promoted * to int (as is done when passing to a variadic function) */ sprintf(dest + plen + 1 + (i * 2), "%02x", (unsigned char)hash[i]); } - memcpy(dest + plen + 41, flags, flen); - dest[plen + 41 + flen] = '\n'; + memcpy(dest + plen + 2 * hlen + 1, flags, flen); + dest[plen + 2 * hlen + 1 + flen] = '\n'; new.start = dest; new.len = dlen; new.hash_suffix = '\0';