##// END OF EJS Templates
revlog: add a mechanism to verify expected file position before appending...
revlog: add a mechanism to verify expected file position before appending If someone uses `hg debuglocks`, or some non-hg process writes to the .hg directory without respecting the locks, or if the repo's on a networked filesystem, it's possible for the revlog code to write out corrupted data. The form of this corruption can vary depending on what data was written and how that happened. We are in the "networked filesystem" case (though I've had users also do this to themselves with the "`hg debuglocks`" scenario), and most often see this with the changelog. What ends up happening is we produce two items (let's call them rev1 and rev2) in the .i file that have the same linkrev, baserev, and offset into the .d file, while the data in the .d file is appended properly. rev2's compressed_size is accurate for rev2, but when we go to decompress the data in the .d file, we use the offset that's recorded in the index file, which is the same as rev1, and attempt to decompress rev2.compressed_size bytes of rev1's data. This usually does not succeed. :) When using inline data, this also fails, though I haven't investigated why too closely. This shows up as a "patch decode" error. I believe what's happening there is that we're basically ignoring the offset field, getting the data properly, but since baserev != rev, it thinks this is a delta based on rev (instead of a full text) and can't actually apply it as such. For now, I'm going to make this an optional component and default it to entirely off. I may increase the default severity of this in the future, once I've enabled it for my users and we gain more experience with it. Luckily, most of my users have a versioned filesystem and can roll back to before the corruption has been written, it's just a hassle to do so and not everyone knows how (so it's a support burden). Users on other filesystems will not have that luxury, and this can cause them to have a corrupted repository that they are unlikely to know how to resolve, and they'll see this as a data-loss event. Refusing to create the corruption is a much better user experience. This mechanism is not perfect. There may be false-negatives (racy writes that are not detected). There should not be any false-positives (non-racy writes that are detected as such). This is not a mechanism that makes putting a repo on a networked filesystem "safe" or "supported", just *less* likely to cause corruption. Differential Revision: https://phab.mercurial-scm.org/D9952

File last commit:

r45691:61e74644 default
r47349:e9901d01 default
Show More
parsers.c
762 lines | 19.9 KiB | text/x-c | CLexer
Yuya Nishihara
parsers: switch to policy importer...
r32372 /*
parsers.c - efficient content parsing
Copyright 2008 Matt Mackall <mpm@selenic.com> and others
This software may be used and distributed according to the terms of
the GNU General Public License, incorporated herein by reference.
*/
Gregory Szorc
cext: make parsers.c PY_SSIZE_T_CLEAN...
r42235 #define PY_SSIZE_T_CLEAN
Yuya Nishihara
parsers: switch to policy importer...
r32372 #include <Python.h>
#include <ctype.h>
#include <stddef.h>
#include <string.h>
Gregory Szorc
cext: reorder #include...
r34439 #include "bitmanipulation.h"
Yuya Nishihara
cext: factor out header for charencode.c...
r33753 #include "charencode.h"
Yuya Nishihara
parsers: switch to policy importer...
r32372 #include "util.h"
#ifdef IS_PY3K
/* The mapping of Python types is meant to be temporary to get Python
* 3 to compile. We should remove this once Python 3 support is fully
* supported and proper types are used in the extensions themselves. */
#define PyInt_Check PyLong_Check
#define PyInt_FromLong PyLong_FromLong
#define PyInt_FromSsize_t PyLong_FromSsize_t
#define PyInt_AsLong PyLong_AsLong
#endif
Yuya Nishihara
cext: mark constant variables
r32385 static const char *const versionerrortext = "Python minor version mismatch";
Yuya Nishihara
parsers: switch to policy importer...
r32372
static PyObject *dict_new_presized(PyObject *self, PyObject *args)
{
Py_ssize_t expected_size;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
return _dict_new_presized(expected_size);
}
static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
Augie Fackler
parsers: allow clang-format here...
r34863 int size, int mtime)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
Augie Fackler
parsers: allow clang-format here...
r34863 dirstateTupleObject *t =
PyObject_New(dirstateTupleObject, &dirstateTupleType);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!t) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 t->state = state;
t->mode = mode;
t->size = size;
t->mtime = mtime;
return t;
}
static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
Augie Fackler
parsers: allow clang-format here...
r34863 PyObject *kwds)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
/* We do all the initialization here and not a tp_init function because
* dirstate_tuple is immutable. */
dirstateTupleObject *t;
char state;
int size, mode, mtime;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!t) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 t->state = state;
t->mode = mode;
t->size = size;
t->mtime = mtime;
return (PyObject *)t;
}
static void dirstate_tuple_dealloc(PyObject *o)
{
PyObject_Del(o);
}
static Py_ssize_t dirstate_tuple_length(PyObject *o)
{
return 4;
}
static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
{
dirstateTupleObject *t = (dirstateTupleObject *)o;
switch (i) {
case 0:
return PyBytes_FromStringAndSize(&t->state, 1);
case 1:
return PyInt_FromLong(t->mode);
case 2:
return PyInt_FromLong(t->size);
case 3:
return PyInt_FromLong(t->mtime);
default:
PyErr_SetString(PyExc_IndexError, "index out of range");
return NULL;
}
}
static PySequenceMethods dirstate_tuple_sq = {
Augie Fackler
parsers: allow clang-format here...
r34863 dirstate_tuple_length, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
dirstate_tuple_item, /* sq_item */
0, /* sq_ass_item */
0, /* sq_contains */
0, /* sq_inplace_concat */
0 /* sq_inplace_repeat */
Yuya Nishihara
parsers: switch to policy importer...
r32372 };
PyTypeObject dirstateTupleType = {
Augie Fackler
parsers: allow clang-format here...
r34863 PyVarObject_HEAD_INIT(NULL, 0) /* header */
"dirstate_tuple", /* tp_name */
sizeof(dirstateTupleObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)dirstate_tuple_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
&dirstate_tuple_sq, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"dirstate tuple", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
dirstate_tuple_new, /* tp_new */
Yuya Nishihara
parsers: switch to policy importer...
r32372 };
static PyObject *parse_dirstate(PyObject *self, PyObject *args)
{
PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
PyObject *fname = NULL, *cname = NULL, *entry = NULL;
char state, *cur, *str, *cpos;
int mode, size, mtime;
Gregory Szorc
cext: make parsers.c PY_SSIZE_T_CLEAN...
r42235 unsigned int flen, pos = 40;
Py_ssize_t len = 40;
Py_ssize_t readlen;
Yuya Nishihara
parsers: switch to policy importer...
r32372
Yuya Nishihara
py3: bulk-replace 'const char*' format specifier passed to PyArg_ParseTuple*()...
r36638 if (!PyArg_ParseTuple(
args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
len = readlen;
/* read parents */
if (len < 40) {
Augie Fackler
parsers: allow clang-format here...
r34863 PyErr_SetString(PyExc_ValueError,
"too little data for parents");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
}
Yuya Nishihara
cext: cast s# arguments of Py_BuildValue() to Py_ssize_t...
r42265 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
str + 20, (Py_ssize_t)20);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!parents) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
/* read filenames */
while (pos >= 40 && pos < len) {
if (pos + 17 > len) {
PyErr_SetString(PyExc_ValueError,
Augie Fackler
parsers: allow clang-format here...
r34863 "overflow in dirstate");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
}
cur = str + pos;
/* unpack header */
state = *cur;
mode = getbe32(cur + 1);
size = getbe32(cur + 5);
mtime = getbe32(cur + 9);
flen = getbe32(cur + 13);
pos += 17;
cur += 17;
if (flen > len - pos) {
Augie Fackler
parsers: allow clang-format here...
r34863 PyErr_SetString(PyExc_ValueError,
"overflow in dirstate");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
}
Augie Fackler
parsers: allow clang-format here...
r34863 entry =
(PyObject *)make_dirstate_tuple(state, mode, size, mtime);
Yuya Nishihara
parsers: switch to policy importer...
r32372 cpos = memchr(cur, 0, flen);
if (cpos) {
fname = PyBytes_FromStringAndSize(cur, cpos - cur);
Augie Fackler
parsers: allow clang-format here...
r34863 cname = PyBytes_FromStringAndSize(
cpos + 1, flen - (cpos - cur) - 1);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (!fname || !cname ||
PyDict_SetItem(cmap, fname, cname) == -1 ||
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 PyDict_SetItem(dmap, fname, entry) == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 Py_DECREF(cname);
} else {
fname = PyBytes_FromStringAndSize(cur, flen);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!fname ||
PyDict_SetItem(dmap, fname, entry) == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 }
Py_DECREF(fname);
Py_DECREF(entry);
fname = cname = entry = NULL;
pos += flen;
}
ret = parents;
Py_INCREF(ret);
quit:
Py_XDECREF(fname);
Py_XDECREF(cname);
Py_XDECREF(entry);
Py_XDECREF(parents);
return ret;
}
/*
* Build a set of non-normal and other parent entries from the dirstate dmap
Augie Fackler
parsers: allow clang-format here...
r34863 */
Gregory Szorc
cext: wrap before brace for functions...
r34441 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
{
Yuya Nishihara
parsers: switch to policy importer...
r32372 PyObject *dmap, *fname, *v;
PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
Py_ssize_t pos;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
&dmap)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
nonnset = PySet_New(NULL);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (nonnset == NULL) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
otherpset = PySet_New(NULL);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (otherpset == NULL) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
pos = 0;
while (PyDict_Next(dmap, &pos, &fname, &v)) {
dirstateTupleObject *t;
if (!dirstate_tuple_check(v)) {
PyErr_SetString(PyExc_TypeError,
Augie Fackler
parsers: allow clang-format here...
r34863 "expected a dirstate tuple");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
}
t = (dirstateTupleObject *)v;
if (t->state == 'n' && t->size == -2) {
if (PySet_Add(otherpset, fname) == -1) {
goto bail;
}
}
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (t->state == 'n' && t->mtime != -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 continue;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
if (PySet_Add(nonnset, fname) == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 }
result = Py_BuildValue("(OO)", nonnset, otherpset);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (result == NULL) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 Py_DECREF(nonnset);
Py_DECREF(otherpset);
return result;
bail:
Py_XDECREF(nonnset);
Py_XDECREF(otherpset);
Py_XDECREF(result);
return NULL;
}
/*
* Efficiently pack a dirstate object into its on-disk format.
*/
static PyObject *pack_dirstate(PyObject *self, PyObject *args)
{
PyObject *packobj = NULL;
PyObject *map, *copymap, *pl, *mtime_unset = NULL;
Py_ssize_t nbytes, pos, l;
PyObject *k, *v = NULL, *pn;
char *p, *s;
int now;
Yuya Nishihara
dirstate: use tuple interface to fix leak in pack_dirstate()...
r39485 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
&now)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
Yuya Nishihara
dirstate: use tuple interface to fix leak in pack_dirstate()...
r39485 if (PyTuple_Size(pl) != 2) {
PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
}
/* Figure out how much we need to allocate. */
for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
PyObject *c;
if (!PyBytes_Check(k)) {
PyErr_SetString(PyExc_TypeError, "expected string key");
goto bail;
}
nbytes += PyBytes_GET_SIZE(k) + 17;
c = PyDict_GetItem(copymap, k);
if (c) {
if (!PyBytes_Check(c)) {
PyErr_SetString(PyExc_TypeError,
Augie Fackler
parsers: allow clang-format here...
r34863 "expected string key");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
}
nbytes += PyBytes_GET_SIZE(c) + 1;
}
}
packobj = PyBytes_FromStringAndSize(NULL, nbytes);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (packobj == NULL) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
p = PyBytes_AS_STRING(packobj);
Yuya Nishihara
dirstate: use tuple interface to fix leak in pack_dirstate()...
r39485 pn = PyTuple_GET_ITEM(pl, 0);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
goto bail;
}
memcpy(p, s, l);
p += 20;
Yuya Nishihara
dirstate: use tuple interface to fix leak in pack_dirstate()...
r39485 pn = PyTuple_GET_ITEM(pl, 1);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
goto bail;
}
memcpy(p, s, l);
p += 20;
Augie Fackler
parsers: allow clang-format here...
r34863 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 dirstateTupleObject *tuple;
char state;
int mode, size, mtime;
Py_ssize_t len, l;
PyObject *o;
char *t;
if (!dirstate_tuple_check(v)) {
PyErr_SetString(PyExc_TypeError,
Augie Fackler
parsers: allow clang-format here...
r34863 "expected a dirstate tuple");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
}
tuple = (dirstateTupleObject *)v;
state = tuple->state;
mode = tuple->mode;
size = tuple->size;
mtime = tuple->mtime;
if (state == 'n' && mtime == now) {
/* See pure/parsers.py:pack_dirstate for why we do
* this. */
mtime = -1;
mtime_unset = (PyObject *)make_dirstate_tuple(
Augie Fackler
parsers: allow clang-format here...
r34863 state, mode, size, mtime);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!mtime_unset) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
if (PyDict_SetItem(map, k, mtime_unset) == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 Py_DECREF(mtime_unset);
mtime_unset = NULL;
}
*p++ = state;
putbe32((uint32_t)mode, p);
putbe32((uint32_t)size, p + 4);
putbe32((uint32_t)mtime, p + 8);
t = p + 12;
p += 16;
len = PyBytes_GET_SIZE(k);
memcpy(p, PyBytes_AS_STRING(k), len);
p += len;
o = PyDict_GetItem(copymap, k);
if (o) {
*p++ = '\0';
l = PyBytes_GET_SIZE(o);
memcpy(p, PyBytes_AS_STRING(o), l);
p += l;
len += l + 1;
}
putbe32((uint32_t)len, t);
}
pos = p - PyBytes_AS_STRING(packobj);
if (pos != nbytes) {
PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
Augie Fackler
parsers: allow clang-format here...
r34863 (long)pos, (long)nbytes);
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
}
return packobj;
bail:
Py_XDECREF(mtime_unset);
Py_XDECREF(packobj);
Py_XDECREF(v);
return NULL;
}
#define BUMPED_FIX 1
#define USING_SHA_256 2
#define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
Augie Fackler
parsers: allow clang-format here...
r34863 static PyObject *readshas(const char *source, unsigned char num,
Py_ssize_t hashwidth)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
int i;
PyObject *list = PyTuple_New(num);
if (list == NULL) {
return NULL;
}
for (i = 0; i < num; i++) {
PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
if (hash == NULL) {
Py_DECREF(list);
return NULL;
}
PyTuple_SET_ITEM(list, i, hash);
source += hashwidth;
}
return list;
}
static PyObject *fm1readmarker(const char *databegin, const char *dataend,
Augie Fackler
parsers: allow clang-format here...
r34863 uint32_t *msize)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
const char *data = databegin;
const char *meta;
double mtime;
int16_t tz;
uint16_t flags;
unsigned char nsuccs, nparents, nmetadata;
Py_ssize_t hashwidth = 20;
PyObject *prec = NULL, *parents = NULL, *succs = NULL;
PyObject *metadata = NULL, *ret = NULL;
int i;
if (data + FM1_HEADER_SIZE > dataend) {
goto overflow;
}
*msize = getbe32(data);
data += 4;
mtime = getbefloat64(data);
data += 8;
tz = getbeint16(data);
data += 2;
flags = getbeuint16(data);
data += 2;
if (flags & USING_SHA_256) {
hashwidth = 32;
}
nsuccs = (unsigned char)(*data++);
nparents = (unsigned char)(*data++);
nmetadata = (unsigned char)(*data++);
if (databegin + *msize > dataend) {
goto overflow;
}
Augie Fackler
parsers: allow clang-format here...
r34863 dataend = databegin + *msize; /* narrow down to marker size */
Yuya Nishihara
parsers: switch to policy importer...
r32372
if (data + hashwidth > dataend) {
goto overflow;
}
prec = PyBytes_FromStringAndSize(data, hashwidth);
data += hashwidth;
if (prec == NULL) {
goto bail;
}
if (data + nsuccs * hashwidth > dataend) {
goto overflow;
}
succs = readshas(data, nsuccs, hashwidth);
if (succs == NULL) {
goto bail;
}
data += nsuccs * hashwidth;
if (nparents == 1 || nparents == 2) {
if (data + nparents * hashwidth > dataend) {
goto overflow;
}
parents = readshas(data, nparents, hashwidth);
if (parents == NULL) {
goto bail;
}
data += nparents * hashwidth;
} else {
parents = Py_None;
Py_INCREF(parents);
}
if (data + 2 * nmetadata > dataend) {
goto overflow;
}
meta = data + (2 * nmetadata);
metadata = PyTuple_New(nmetadata);
if (metadata == NULL) {
goto bail;
}
for (i = 0; i < nmetadata; i++) {
PyObject *tmp, *left = NULL, *right = NULL;
Py_ssize_t leftsize = (unsigned char)(*data++);
Py_ssize_t rightsize = (unsigned char)(*data++);
if (meta + leftsize + rightsize > dataend) {
goto overflow;
}
left = PyBytes_FromStringAndSize(meta, leftsize);
meta += leftsize;
right = PyBytes_FromStringAndSize(meta, rightsize);
meta += rightsize;
tmp = PyTuple_New(2);
if (!left || !right || !tmp) {
Py_XDECREF(left);
Py_XDECREF(right);
Py_XDECREF(tmp);
goto bail;
}
PyTuple_SET_ITEM(tmp, 0, left);
PyTuple_SET_ITEM(tmp, 1, right);
PyTuple_SET_ITEM(metadata, i, tmp);
}
Augie Fackler
parsers: allow clang-format here...
r34863 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
(int)tz * 60, parents);
goto bail; /* return successfully */
Yuya Nishihara
parsers: switch to policy importer...
r32372
overflow:
PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
bail:
Py_XDECREF(prec);
Py_XDECREF(succs);
Py_XDECREF(metadata);
Py_XDECREF(parents);
return ret;
}
Gregory Szorc
cext: wrap before brace for functions...
r34441 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
{
Yuya Nishihara
parsers: switch to policy importer...
r32372 const char *data, *dataend;
Gregory Szorc
cext: make parsers.c PY_SSIZE_T_CLEAN...
r42235 Py_ssize_t datalen, offset, stop;
Yuya Nishihara
parsers: switch to policy importer...
r32372 PyObject *markers = NULL;
Yuya Nishihara
py3: bulk-replace 'const char*' format specifier passed to PyArg_ParseTuple*()...
r36638 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
&offset, &stop)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
}
Augie Fackler
parsers: better bounds checking in fm1readmarkers...
r41052 if (offset < 0) {
PyErr_SetString(PyExc_ValueError,
"invalid negative offset in fm1readmarkers");
return NULL;
}
if (stop > datalen) {
PyErr_SetString(
PyExc_ValueError,
"stop longer than data length in fm1readmarkers");
return NULL;
}
Yuya Nishihara
parsers: switch to policy importer...
r32372 dataend = data + datalen;
data += offset;
markers = PyList_New(0);
if (!markers) {
return NULL;
}
while (offset < stop) {
uint32_t msize;
int error;
PyObject *record = fm1readmarker(data, dataend, &msize);
if (!record) {
goto bail;
}
error = PyList_Append(markers, record);
Py_DECREF(record);
if (error) {
goto bail;
}
data += msize;
offset += msize;
}
return markers;
bail:
Py_DECREF(markers);
return NULL;
}
static char parsers_doc[] = "Efficient content parsing.";
PyObject *encodedir(PyObject *self, PyObject *args);
PyObject *pathencode(PyObject *self, PyObject *args);
PyObject *lowerencode(PyObject *self, PyObject *args);
Gregory Szorc
cext: extract revlog/index parsing code to own C file...
r32378 PyObject *parse_index2(PyObject *self, PyObject *args);
Yuya Nishihara
parsers: switch to policy importer...
r32372
static PyMethodDef methods[] = {
Augie Fackler
parsers: allow clang-format here...
r34863 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
{"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
"create a set containing non-normal and other parent entries of given "
"dirstate\n"},
{"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
{"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
{"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
{"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
{"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
{"dict_new_presized", dict_new_presized, METH_VARARGS,
"construct a dict with an expected size\n"},
{"make_file_foldmap", make_file_foldmap, METH_VARARGS,
"make file foldmap\n"},
{"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
"escape a UTF-8 byte string to JSON (fast path)\n"},
{"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
{"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
{"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
{"fm1readmarkers", fm1readmarkers, METH_VARARGS,
"parse v1 obsolete markers\n"},
{NULL, NULL}};
Yuya Nishihara
parsers: switch to policy importer...
r32372
void dirs_module_init(PyObject *mod);
void manifest_module_init(PyObject *mod);
Gregory Szorc
cext: extract revlog/index parsing code to own C file...
r32378 void revlog_module_init(PyObject *mod);
Yuya Nishihara
parsers: switch to policy importer...
r32372
Joerg Sonnenberger
phases: sparsify phaseroots and phasesets...
r45691 static const int version = 17;
Yuya Nishihara
parsers: switch to policy importer...
r32372
static void module_init(PyObject *mod)
{
Raphaël Gomès
rust-dirstate: add rust-cpython bindings to the new parse/pack functions...
r42489 PyObject *capsule = NULL;
Yuya Nishihara
parsers: switch to policy importer...
r32372 PyModule_AddIntConstant(mod, "version", version);
/* This module constant has two purposes. First, it lets us unit test
* the ImportError raised without hard-coding any error text. This
* means we can change the text in the future without breaking tests,
* even across changesets without a recompile. Second, its presence
* can be used to determine whether the version-checking logic is
* present, which also helps in testing across changesets without a
* recompile. Note that this means the pure-Python version of parsers
* should not have this module constant. */
PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
dirs_module_init(mod);
manifest_module_init(mod);
Gregory Szorc
cext: extract revlog/index parsing code to own C file...
r32378 revlog_module_init(mod);
Yuya Nishihara
parsers: switch to policy importer...
r32372
Raphaël Gomès
rust-dirstate: add rust-cpython bindings to the new parse/pack functions...
r42489 capsule = PyCapsule_New(
make_dirstate_tuple,
"mercurial.cext.parsers.make_dirstate_tuple_CAPI", NULL);
if (capsule != NULL)
PyModule_AddObject(mod, "make_dirstate_tuple_CAPI", capsule);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (PyType_Ready(&dirstateTupleType) < 0) {
Yuya Nishihara
cext: move back finalization of dirstateTupleType where it should be
r32383 return;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 Py_INCREF(&dirstateTupleType);
PyModule_AddObject(mod, "dirstatetuple",
Augie Fackler
parsers: allow clang-format here...
r34863 (PyObject *)&dirstateTupleType);
Yuya Nishihara
parsers: switch to policy importer...
r32372 }
static int check_python_version(void)
{
PyObject *sys = PyImport_ImportModule("sys"), *ver;
long hexversion;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!sys) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 ver = PyObject_GetAttrString(sys, "hexversion");
Py_DECREF(sys);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!ver) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 hexversion = PyInt_AsLong(ver);
Py_DECREF(ver);
/* sys.hexversion is a 32-bit number by default, so the -1 case
* should only occur in unusual circumstances (e.g. if sys.hexversion
* is manually set to an invalid value). */
if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
Augie Fackler
parsers: allow clang-format here...
r34863 PyErr_Format(PyExc_ImportError,
"%s: The Mercurial extension "
"modules were compiled with Python " PY_VERSION
", but "
"Mercurial is currently using Python with "
"sys.hexversion=%ld: "
"Python %s\n at: %s",
versionerrortext, hexversion, Py_GetVersion(),
Py_GetProgramFullPath());
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
}
return 0;
}
#ifdef IS_PY3K
Augie Fackler
parsers: allow clang-format here...
r34863 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
parsers_doc, -1, methods};
Yuya Nishihara
parsers: switch to policy importer...
r32372
PyMODINIT_FUNC PyInit_parsers(void)
{
PyObject *mod;
if (check_python_version() == -1)
return NULL;
mod = PyModule_Create(&parsers_module);
module_init(mod);
return mod;
}
#else
PyMODINIT_FUNC initparsers(void)
{
PyObject *mod;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (check_python_version() == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 mod = Py_InitModule3("parsers", methods, parsers_doc);
module_init(mod);
}
#endif