changeset 32406:a9c71d578a1c

osutil: switch to policy importer "make clean" is recommended to test this change, though C API compatibility should be preserved.
author Yuya Nishihara <yuya@tcha.org>
date Fri, 12 Aug 2016 11:35:17 +0900
parents 8e0327dae3f4
children 008d37c4d783
files contrib/check-py3-compat.py contrib/import-checker.py contrib/wix/dist.wxs mercurial/__init__.py mercurial/cext/osutil.c mercurial/debugcommands.py mercurial/osutil.c mercurial/pure/osutil.py mercurial/util.py mercurial/windows.py setup.py
diffstat 11 files changed, 1346 insertions(+), 1343 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/check-py3-compat.py	Fri Aug 12 11:30:17 2016 +0900
+++ b/contrib/check-py3-compat.py	Fri Aug 12 11:35:17 2016 +0900
@@ -21,7 +21,6 @@
     'bdiff.py',
     'diffhelpers.py',
     'mpatch.py',
-    'osutil.py',
     'parsers.py',
 )
 
--- a/contrib/import-checker.py	Fri Aug 12 11:30:17 2016 +0900
+++ b/contrib/import-checker.py	Fri Aug 12 11:35:17 2016 +0900
@@ -30,7 +30,6 @@
     'bdiff.py',
     'diffhelpers.py',
     'mpatch.py',
-    'osutil.py',
     'parsers.py',
 )
 
--- a/contrib/wix/dist.wxs	Fri Aug 12 11:30:17 2016 +0900
+++ b/contrib/wix/dist.wxs	Fri Aug 12 11:35:17 2016 +0900
@@ -16,7 +16,7 @@
           <File Name="mercurial.bdiff.pyd" />
           <File Name="mercurial.diffhelpers.pyd" />
           <File Name="mercurial.mpatch.pyd" />
-          <File Name="mercurial.osutil.pyd" />
+          <File Name="mercurial.cext.osutil.pyd" />
           <File Name="mercurial.parsers.pyd" />
           <File Name="pyexpat.pyd" />
           <File Name="bz2.pyd" />
--- a/mercurial/__init__.py	Fri Aug 12 11:30:17 2016 +0900
+++ b/mercurial/__init__.py	Fri Aug 12 11:35:17 2016 +0900
@@ -27,7 +27,6 @@
     'mercurial.bdiff',
     'mercurial.diffhelpers',
     'mercurial.mpatch',
-    'mercurial.osutil',
     'mercurial.parsers',
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/cext/osutil.c	Fri Aug 12 11:35:17 2016 +0900
@@ -0,0 +1,1335 @@
+/*
+ osutil.c - native operating system services
+
+ Copyright 2007 Matt Mackall and others
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License, incorporated herein by reference.
+*/
+
+#define _ATFILE_SOURCE
+#include <Python.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <io.h>
+#else
+#include <dirent.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef HAVE_LINUX_STATFS
+#include <linux/magic.h>
+#include <sys/vfs.h>
+#endif
+#ifdef HAVE_BSD_STATFS
+#include <sys/mount.h>
+#include <sys/param.h>
+#endif
+#endif
+
+#ifdef __APPLE__
+#include <sys/attr.h>
+#include <sys/vnode.h>
+#endif
+
+#include "util.h"
+
+/* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#ifdef _WIN32
+/*
+stat struct compatible with hg expectations
+Mercurial only uses st_mode, st_size and st_mtime
+the rest is kept to minimize changes between implementations
+*/
+struct hg_stat {
+	int st_dev;
+	int st_mode;
+	int st_nlink;
+	__int64 st_size;
+	int st_mtime;
+	int st_ctime;
+};
+struct listdir_stat {
+	PyObject_HEAD
+	struct hg_stat st;
+};
+#else
+struct listdir_stat {
+	PyObject_HEAD
+	struct stat st;
+};
+#endif
+
+#ifdef IS_PY3K
+#define listdir_slot(name) \
+	static PyObject *listdir_stat_##name(PyObject *self, void *x) \
+	{ \
+		return PyLong_FromLong(((struct listdir_stat *)self)->st.name); \
+	}
+#else
+#define listdir_slot(name) \
+	static PyObject *listdir_stat_##name(PyObject *self, void *x) \
+	{ \
+		return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
+	}
+#endif
+
+listdir_slot(st_dev)
+listdir_slot(st_mode)
+listdir_slot(st_nlink)
+#ifdef _WIN32
+static PyObject *listdir_stat_st_size(PyObject *self, void *x)
+{
+	return PyLong_FromLongLong(
+		(PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
+}
+#else
+listdir_slot(st_size)
+#endif
+listdir_slot(st_mtime)
+listdir_slot(st_ctime)
+
+static struct PyGetSetDef listdir_stat_getsets[] = {
+	{"st_dev", listdir_stat_st_dev, 0, 0, 0},
+	{"st_mode", listdir_stat_st_mode, 0, 0, 0},
+	{"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
+	{"st_size", listdir_stat_st_size, 0, 0, 0},
+	{"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
+	{"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
+	{0, 0, 0, 0, 0}
+};
+
+static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
+{
+	return t->tp_alloc(t, 0);
+}
+
+static void listdir_stat_dealloc(PyObject *o)
+{
+	o->ob_type->tp_free(o);
+}
+
+static PyTypeObject listdir_stat_type = {
+	PyVarObject_HEAD_INIT(NULL, 0)
+	"osutil.stat",             /*tp_name*/
+	sizeof(struct listdir_stat), /*tp_basicsize*/
+	0,                         /*tp_itemsize*/
+	(destructor)listdir_stat_dealloc, /*tp_dealloc*/
+	0,                         /*tp_print*/
+	0,                         /*tp_getattr*/
+	0,                         /*tp_setattr*/
+	0,                         /*tp_compare*/
+	0,                         /*tp_repr*/
+	0,                         /*tp_as_number*/
+	0,                         /*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 | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+	"stat objects",            /* 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 */
+	listdir_stat_getsets,      /* 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 */
+	listdir_stat_new,          /* tp_new */
+};
+
+#ifdef _WIN32
+
+static int to_python_time(const FILETIME *tm)
+{
+	/* number of seconds between epoch and January 1 1601 */
+	const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
+	/* conversion factor from 100ns to 1s */
+	const __int64 a1 = 10000000;
+	/* explicit (int) cast to suspend compiler warnings */
+	return (int)((((__int64)tm->dwHighDateTime << 32)
+			+ tm->dwLowDateTime) / a1 - a0);
+}
+
+static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
+{
+	PyObject *py_st;
+	struct hg_stat *stp;
+
+	int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+		? _S_IFDIR : _S_IFREG;
+
+	if (!wantstat)
+		return Py_BuildValue("si", fd->cFileName, kind);
+
+	py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
+	if (!py_st)
+		return NULL;
+
+	stp = &((struct listdir_stat *)py_st)->st;
+	/*
+	use kind as st_mode
+	rwx bits on Win32 are meaningless
+	and Hg does not use them anyway
+	*/
+	stp->st_mode  = kind;
+	stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
+	stp->st_ctime = to_python_time(&fd->ftCreationTime);
+	if (kind == _S_IFREG)
+		stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
+				+ fd->nFileSizeLow;
+	return Py_BuildValue("siN", fd->cFileName,
+		kind, py_st);
+}
+
+static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
+{
+	PyObject *rval = NULL; /* initialize - return value */
+	PyObject *list;
+	HANDLE fh;
+	WIN32_FIND_DATAA fd;
+	char *pattern;
+
+	/* build the path + \* pattern string */
+	pattern = PyMem_Malloc(plen + 3); /* path + \* + \0 */
+	if (!pattern) {
+		PyErr_NoMemory();
+		goto error_nomem;
+	}
+	memcpy(pattern, path, plen);
+
+	if (plen > 0) {
+		char c = path[plen-1];
+		if (c != ':' && c != '/' && c != '\\')
+			pattern[plen++] = '\\';
+	}
+	pattern[plen++] = '*';
+	pattern[plen] = '\0';
+
+	fh = FindFirstFileA(pattern, &fd);
+	if (fh == INVALID_HANDLE_VALUE) {
+		PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
+		goto error_file;
+	}
+
+	list = PyList_New(0);
+	if (!list)
+		goto error_list;
+
+	do {
+		PyObject *item;
+
+		if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+			if (!strcmp(fd.cFileName, ".")
+			|| !strcmp(fd.cFileName, ".."))
+				continue;
+
+			if (skip && !strcmp(fd.cFileName, skip)) {
+				rval = PyList_New(0);
+				goto error;
+			}
+		}
+
+		item = make_item(&fd, wantstat);
+		if (!item)
+			goto error;
+
+		if (PyList_Append(list, item)) {
+			Py_XDECREF(item);
+			goto error;
+		}
+
+		Py_XDECREF(item);
+	} while (FindNextFileA(fh, &fd));
+
+	if (GetLastError() != ERROR_NO_MORE_FILES) {
+		PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
+		goto error;
+	}
+
+	rval = list;
+	Py_XINCREF(rval);
+error:
+	Py_XDECREF(list);
+error_list:
+	FindClose(fh);
+error_file:
+	PyMem_Free(pattern);
+error_nomem:
+	return rval;
+}
+
+#else
+
+int entkind(struct dirent *ent)
+{
+#ifdef DT_REG
+	switch (ent->d_type) {
+	case DT_REG: return S_IFREG;
+	case DT_DIR: return S_IFDIR;
+	case DT_LNK: return S_IFLNK;
+	case DT_BLK: return S_IFBLK;
+	case DT_CHR: return S_IFCHR;
+	case DT_FIFO: return S_IFIFO;
+	case DT_SOCK: return S_IFSOCK;
+	}
+#endif
+	return -1;
+}
+
+static PyObject *makestat(const struct stat *st)
+{
+	PyObject *stat;
+
+	stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
+	if (stat)
+		memcpy(&((struct listdir_stat *)stat)->st, st, sizeof(*st));
+	return stat;
+}
+
+static PyObject *_listdir_stat(char *path, int pathlen, int keepstat,
+			       char *skip)
+{
+	PyObject *list, *elem, *stat = NULL, *ret = NULL;
+	char fullpath[PATH_MAX + 10];
+	int kind, err;
+	struct stat st;
+	struct dirent *ent;
+	DIR *dir;
+#ifdef AT_SYMLINK_NOFOLLOW
+	int dfd = -1;
+#endif
+
+	if (pathlen >= PATH_MAX) {
+		errno = ENAMETOOLONG;
+		PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+		goto error_value;
+	}
+	strncpy(fullpath, path, PATH_MAX);
+	fullpath[pathlen] = '/';
+
+#ifdef AT_SYMLINK_NOFOLLOW
+	dfd = open(path, O_RDONLY);
+	if (dfd == -1) {
+		PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+		goto error_value;
+	}
+	dir = fdopendir(dfd);
+#else
+	dir = opendir(path);
+#endif
+	if (!dir) {
+		PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+		goto error_dir;
+	}
+
+	list = PyList_New(0);
+	if (!list)
+		goto error_list;
+
+	while ((ent = readdir(dir))) {
+		if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
+			continue;
+
+		kind = entkind(ent);
+		if (kind == -1 || keepstat) {
+#ifdef AT_SYMLINK_NOFOLLOW
+			err = fstatat(dfd, ent->d_name, &st,
+				      AT_SYMLINK_NOFOLLOW);
+#else
+			strncpy(fullpath + pathlen + 1, ent->d_name,
+				PATH_MAX - pathlen);
+			fullpath[PATH_MAX] = '\0';
+			err = lstat(fullpath, &st);
+#endif
+			if (err == -1) {
+				/* race with file deletion? */
+				if (errno == ENOENT)
+					continue;
+				strncpy(fullpath + pathlen + 1, ent->d_name,
+					PATH_MAX - pathlen);
+				fullpath[PATH_MAX] = 0;
+				PyErr_SetFromErrnoWithFilename(PyExc_OSError,
+							       fullpath);
+				goto error;
+			}
+			kind = st.st_mode & S_IFMT;
+		}
+
+		/* quit early? */
+		if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
+			ret = PyList_New(0);
+			goto error;
+		}
+
+		if (keepstat) {
+			stat = makestat(&st);
+			if (!stat)
+				goto error;
+			elem = Py_BuildValue("siN", ent->d_name, kind, stat);
+		} else
+			elem = Py_BuildValue("si", ent->d_name, kind);
+		if (!elem)
+			goto error;
+		stat = NULL;
+
+		PyList_Append(list, elem);
+		Py_DECREF(elem);
+	}
+
+	ret = list;
+	Py_INCREF(ret);
+
+error:
+	Py_DECREF(list);
+	Py_XDECREF(stat);
+error_list:
+	closedir(dir);
+	/* closedir also closes its dirfd */
+	goto error_value;
+error_dir:
+#ifdef AT_SYMLINK_NOFOLLOW
+	close(dfd);
+#endif
+error_value:
+	return ret;
+}
+
+#ifdef __APPLE__
+
+typedef struct {
+	u_int32_t length;
+	attrreference_t name;
+	fsobj_type_t obj_type;
+	struct timespec mtime;
+#if __LITTLE_ENDIAN__
+	mode_t access_mask;
+	uint16_t padding;
+#else
+	uint16_t padding;
+	mode_t access_mask;
+#endif
+	off_t size;
+} __attribute__((packed)) attrbuf_entry;
+
+int attrkind(attrbuf_entry *entry)
+{
+	switch (entry->obj_type) {
+	case VREG: return S_IFREG;
+	case VDIR: return S_IFDIR;
+	case VLNK: return S_IFLNK;
+	case VBLK: return S_IFBLK;
+	case VCHR: return S_IFCHR;
+	case VFIFO: return S_IFIFO;
+	case VSOCK: return S_IFSOCK;
+	}
+	return -1;
+}
+
+/* get these many entries at a time */
+#define LISTDIR_BATCH_SIZE 50
+
+static PyObject *_listdir_batch(char *path, int pathlen, int keepstat,
+				char *skip, bool *fallback)
+{
+	PyObject *list, *elem, *stat = NULL, *ret = NULL;
+	int kind, err;
+	unsigned long index;
+	unsigned int count, old_state, new_state;
+	bool state_seen = false;
+	attrbuf_entry *entry;
+	/* from the getattrlist(2) man page: a path can be no longer than
+	   (NAME_MAX * 3 + 1) bytes. Also, "The getattrlist() function will
+	   silently truncate attribute data if attrBufSize is too small." So
+	   pass in a buffer big enough for the worst case. */
+	char attrbuf[LISTDIR_BATCH_SIZE * (sizeof(attrbuf_entry) + NAME_MAX * 3 + 1)];
+	unsigned int basep_unused;
+
+	struct stat st;
+	int dfd = -1;
+
+	/* these must match the attrbuf_entry struct, otherwise you'll end up
+	   with garbage */
+	struct attrlist requested_attr = {0};
+	requested_attr.bitmapcount = ATTR_BIT_MAP_COUNT;
+	requested_attr.commonattr = (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE |
+				     ATTR_CMN_MODTIME | ATTR_CMN_ACCESSMASK);
+	requested_attr.fileattr = ATTR_FILE_DATALENGTH;
+
+	*fallback = false;
+
+	if (pathlen >= PATH_MAX) {
+		errno = ENAMETOOLONG;
+		PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+		goto error_value;
+	}
+
+	dfd = open(path, O_RDONLY);
+	if (dfd == -1) {
+		PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+		goto error_value;
+	}
+
+	list = PyList_New(0);
+	if (!list)
+		goto error_dir;
+
+	do {
+		count = LISTDIR_BATCH_SIZE;
+		err = getdirentriesattr(dfd, &requested_attr, &attrbuf,
+					sizeof(attrbuf), &count, &basep_unused,
+					&new_state, 0);
+		if (err < 0) {
+			if (errno == ENOTSUP) {
+				/* We're on a filesystem that doesn't support
+				   getdirentriesattr. Fall back to the
+				   stat-based implementation. */
+				*fallback = true;
+			} else
+				PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+			goto error;
+		}
+
+		if (!state_seen) {
+			old_state = new_state;
+			state_seen = true;
+		} else if (old_state != new_state) {
+			/* There's an edge case with getdirentriesattr. Consider
+			   the following initial list of files:
+
+			   a
+			   b
+			   <--
+			   c
+			   d
+
+			   If the iteration is paused at the arrow, and b is
+			   deleted before it is resumed, getdirentriesattr will
+			   not return d at all!  Ordinarily we're expected to
+			   restart the iteration from the beginning. To avoid
+			   getting stuck in a retry loop here, fall back to
+			   stat. */
+			*fallback = true;
+			goto error;
+		}
+
+		entry = (attrbuf_entry *)attrbuf;
+
+		for (index = 0; index < count; index++) {
+			char *filename = ((char *)&entry->name) +
+				entry->name.attr_dataoffset;
+
+			if (!strcmp(filename, ".") || !strcmp(filename, ".."))
+				continue;
+
+			kind = attrkind(entry);
+			if (kind == -1) {
+				PyErr_Format(PyExc_OSError,
+					     "unknown object type %u for file "
+					     "%s%s!",
+					     entry->obj_type, path, filename);
+				goto error;
+			}
+
+			/* quit early? */
+			if (skip && kind == S_IFDIR && !strcmp(filename, skip)) {
+				ret = PyList_New(0);
+				goto error;
+			}
+
+			if (keepstat) {
+				/* from the getattrlist(2) man page: "Only the
+				   permission bits ... are valid". */
+				st.st_mode = (entry->access_mask & ~S_IFMT) | kind;
+				st.st_mtime = entry->mtime.tv_sec;
+				st.st_size = entry->size;
+				stat = makestat(&st);
+				if (!stat)
+					goto error;
+				elem = Py_BuildValue("siN", filename, kind, stat);
+			} else
+				elem = Py_BuildValue("si", filename, kind);
+			if (!elem)
+				goto error;
+			stat = NULL;
+
+			PyList_Append(list, elem);
+			Py_DECREF(elem);
+
+			entry = (attrbuf_entry *)((char *)entry + entry->length);
+		}
+	} while (err == 0);
+
+	ret = list;
+	Py_INCREF(ret);
+
+error:
+	Py_DECREF(list);
+	Py_XDECREF(stat);
+error_dir:
+	close(dfd);
+error_value:
+	return ret;
+}
+
+#endif /* __APPLE__ */
+
+static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
+{
+#ifdef __APPLE__
+	PyObject *ret;
+	bool fallback = false;
+
+	ret = _listdir_batch(path, pathlen, keepstat, skip, &fallback);
+	if (ret != NULL || !fallback)
+		return ret;
+#endif
+	return _listdir_stat(path, pathlen, keepstat, skip);
+}
+
+static PyObject *statfiles(PyObject *self, PyObject *args)
+{
+	PyObject *names, *stats;
+	Py_ssize_t i, count;
+
+	if (!PyArg_ParseTuple(args, "O:statfiles", &names))
+		return NULL;
+
+	count = PySequence_Length(names);
+	if (count == -1) {
+		PyErr_SetString(PyExc_TypeError, "not a sequence");
+		return NULL;
+	}
+
+	stats = PyList_New(count);
+	if (stats == NULL)
+		return NULL;
+
+	for (i = 0; i < count; i++) {
+		PyObject *stat, *pypath;
+		struct stat st;
+		int ret, kind;
+		char *path;
+
+		/* With a large file count or on a slow filesystem,
+		   don't block signals for long (issue4878). */
+		if ((i % 1000) == 999 && PyErr_CheckSignals() == -1)
+			goto bail;
+
+		pypath = PySequence_GetItem(names, i);
+		if (!pypath)
+			goto bail;
+		path = PyBytes_AsString(pypath);
+		if (path == NULL) {
+			Py_DECREF(pypath);
+			PyErr_SetString(PyExc_TypeError, "not a string");
+			goto bail;
+		}
+		ret = lstat(path, &st);
+		Py_DECREF(pypath);
+		kind = st.st_mode & S_IFMT;
+		if (ret != -1 && (kind == S_IFREG || kind == S_IFLNK)) {
+			stat = makestat(&st);
+			if (stat == NULL)
+				goto bail;
+			PyList_SET_ITEM(stats, i, stat);
+		} else {
+			Py_INCREF(Py_None);
+			PyList_SET_ITEM(stats, i, Py_None);
+		}
+	}
+
+	return stats;
+
+bail:
+	Py_DECREF(stats);
+	return NULL;
+}
+
+/*
+ * recvfds() simply does not release GIL during blocking io operation because
+ * command server is known to be single-threaded.
+ *
+ * Old systems such as Solaris don't provide CMSG_LEN, msg_control, etc.
+ * Currently, recvfds() is not supported on these platforms.
+ */
+#ifdef CMSG_LEN
+
+static ssize_t recvfdstobuf(int sockfd, int **rfds, void *cbuf, size_t cbufsize)
+{
+	char dummy[1];
+	struct iovec iov = {dummy, sizeof(dummy)};
+	struct msghdr msgh = {0};
+	struct cmsghdr *cmsg;
+
+	msgh.msg_iov = &iov;
+	msgh.msg_iovlen = 1;
+	msgh.msg_control = cbuf;
+	msgh.msg_controllen = (socklen_t)cbufsize;
+	if (recvmsg(sockfd, &msgh, 0) < 0)
+		return -1;
+
+	for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg;
+	     cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
+		if (cmsg->cmsg_level != SOL_SOCKET ||
+		    cmsg->cmsg_type != SCM_RIGHTS)
+			continue;
+		*rfds = (int *)CMSG_DATA(cmsg);
+		return (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+	}
+
+	*rfds = cbuf;
+	return 0;
+}
+
+static PyObject *recvfds(PyObject *self, PyObject *args)
+{
+	int sockfd;
+	int *rfds = NULL;
+	ssize_t rfdscount, i;
+	char cbuf[256];
+	PyObject *rfdslist = NULL;
+
+	if (!PyArg_ParseTuple(args, "i", &sockfd))
+		return NULL;
+
+	rfdscount = recvfdstobuf(sockfd, &rfds, cbuf, sizeof(cbuf));
+	if (rfdscount < 0)
+		return PyErr_SetFromErrno(PyExc_OSError);
+
+	rfdslist = PyList_New(rfdscount);
+	if (!rfdslist)
+		goto bail;
+	for (i = 0; i < rfdscount; i++) {
+		PyObject *obj = PyLong_FromLong(rfds[i]);
+		if (!obj)
+			goto bail;
+		PyList_SET_ITEM(rfdslist, i, obj);
+	}
+	return rfdslist;
+
+bail:
+	Py_XDECREF(rfdslist);
+	return NULL;
+}
+
+#endif /* CMSG_LEN */
+
+#if defined(HAVE_SETPROCTITLE)
+/* setproctitle is the first choice - available in FreeBSD */
+#define SETPROCNAME_USE_SETPROCTITLE
+#elif (defined(__linux__) || defined(__APPLE__)) && PY_MAJOR_VERSION == 2
+/* rewrite the argv buffer in place - works in Linux and OS X. Py_GetArgcArgv
+ * in Python 3 returns the copied wchar_t **argv, thus unsupported. */
+#define SETPROCNAME_USE_ARGVREWRITE
+#else
+#define SETPROCNAME_USE_NONE
+#endif
+
+#ifndef SETPROCNAME_USE_NONE
+static PyObject *setprocname(PyObject *self, PyObject *args)
+{
+	const char *name = NULL;
+	if (!PyArg_ParseTuple(args, "s", &name))
+		return NULL;
+
+#if defined(SETPROCNAME_USE_SETPROCTITLE)
+	setproctitle("%s", name);
+#elif defined(SETPROCNAME_USE_ARGVREWRITE)
+	{
+		static char *argvstart = NULL;
+		static size_t argvsize = 0;
+		if (argvstart == NULL) {
+			int argc = 0, i;
+			char **argv = NULL;
+			char *argvend;
+			extern void Py_GetArgcArgv(int *argc, char ***argv);
+			Py_GetArgcArgv(&argc, &argv);
+
+			/* Check the memory we can use. Typically, argv[i] and
+			 * argv[i + 1] are continuous. */
+			argvend = argvstart = argv[0];
+			for (i = 0; i < argc; ++i) {
+				if (argv[i] > argvend || argv[i] < argvstart)
+					break; /* not continuous */
+				size_t len = strlen(argv[i]);
+				argvend = argv[i] + len + 1 /* '\0' */;
+			}
+			if (argvend > argvstart) /* sanity check */
+				argvsize = argvend - argvstart;
+		}
+
+		if (argvstart && argvsize > 1) {
+			int n = snprintf(argvstart, argvsize, "%s", name);
+			if (n >= 0 && (size_t)n < argvsize)
+				memset(argvstart + n, 0, argvsize - n);
+		}
+	}
+#endif
+
+	Py_RETURN_NONE;
+}
+#endif /* ndef SETPROCNAME_USE_NONE */
+
+#if defined(HAVE_BSD_STATFS)
+static const char *describefstype(const struct statfs *pbuf)
+{
+	/* BSD or OSX provides a f_fstypename field */
+	return pbuf->f_fstypename;
+}
+#elif defined(HAVE_LINUX_STATFS)
+static const char *describefstype(const struct statfs *pbuf)
+{
+	/* Begin of Linux filesystems */
+#ifdef ADFS_SUPER_MAGIC
+	if (pbuf->f_type == ADFS_SUPER_MAGIC)
+		return "adfs";
+#endif
+#ifdef AFFS_SUPER_MAGIC
+	if (pbuf->f_type == AFFS_SUPER_MAGIC)
+		return "affs";
+#endif
+#ifdef AUTOFS_SUPER_MAGIC
+	if (pbuf->f_type == AUTOFS_SUPER_MAGIC)
+		return "autofs";
+#endif
+#ifdef BDEVFS_MAGIC
+	if (pbuf->f_type == BDEVFS_MAGIC)
+		return "bdevfs";
+#endif
+#ifdef BEFS_SUPER_MAGIC
+	if (pbuf->f_type == BEFS_SUPER_MAGIC)
+		return "befs";
+#endif
+#ifdef BFS_MAGIC
+	if (pbuf->f_type == BFS_MAGIC)
+		return "bfs";
+#endif
+#ifdef BINFMTFS_MAGIC
+	if (pbuf->f_type == BINFMTFS_MAGIC)
+		return "binfmtfs";
+#endif
+#ifdef BTRFS_SUPER_MAGIC
+	if (pbuf->f_type == BTRFS_SUPER_MAGIC)
+		return "btrfs";
+#endif
+#ifdef CGROUP_SUPER_MAGIC
+	if (pbuf->f_type == CGROUP_SUPER_MAGIC)
+		return "cgroup";
+#endif
+#ifdef CIFS_MAGIC_NUMBER
+	if (pbuf->f_type == CIFS_MAGIC_NUMBER)
+		return "cifs";
+#endif
+#ifdef CODA_SUPER_MAGIC
+	if (pbuf->f_type == CODA_SUPER_MAGIC)
+		return "coda";
+#endif
+#ifdef COH_SUPER_MAGIC
+	if (pbuf->f_type == COH_SUPER_MAGIC)
+		return "coh";
+#endif
+#ifdef CRAMFS_MAGIC
+	if (pbuf->f_type == CRAMFS_MAGIC)
+		return "cramfs";
+#endif
+#ifdef DEBUGFS_MAGIC
+	if (pbuf->f_type == DEBUGFS_MAGIC)
+		return "debugfs";
+#endif
+#ifdef DEVFS_SUPER_MAGIC
+	if (pbuf->f_type == DEVFS_SUPER_MAGIC)
+		return "devfs";
+#endif
+#ifdef DEVPTS_SUPER_MAGIC
+	if (pbuf->f_type == DEVPTS_SUPER_MAGIC)
+		return "devpts";
+#endif
+#ifdef EFIVARFS_MAGIC
+	if (pbuf->f_type == EFIVARFS_MAGIC)
+		return "efivarfs";
+#endif
+#ifdef EFS_SUPER_MAGIC
+	if (pbuf->f_type == EFS_SUPER_MAGIC)
+		return "efs";
+#endif
+#ifdef EXT_SUPER_MAGIC
+	if (pbuf->f_type == EXT_SUPER_MAGIC)
+		return "ext";
+#endif
+#ifdef EXT2_OLD_SUPER_MAGIC
+	if (pbuf->f_type == EXT2_OLD_SUPER_MAGIC)
+		return "ext2";
+#endif
+#ifdef EXT2_SUPER_MAGIC
+	if (pbuf->f_type == EXT2_SUPER_MAGIC)
+		return "ext2";
+#endif
+#ifdef EXT3_SUPER_MAGIC
+	if (pbuf->f_type == EXT3_SUPER_MAGIC)
+		return "ext3";
+#endif
+#ifdef EXT4_SUPER_MAGIC
+	if (pbuf->f_type == EXT4_SUPER_MAGIC)
+		return "ext4";
+#endif
+#ifdef F2FS_SUPER_MAGIC
+	if (pbuf->f_type == F2FS_SUPER_MAGIC)
+		return "f2fs";
+#endif
+#ifdef FUSE_SUPER_MAGIC
+	if (pbuf->f_type == FUSE_SUPER_MAGIC)
+		return "fuse";
+#endif
+#ifdef FUTEXFS_SUPER_MAGIC
+	if (pbuf->f_type == FUTEXFS_SUPER_MAGIC)
+		return "futexfs";
+#endif
+#ifdef HFS_SUPER_MAGIC
+	if (pbuf->f_type == HFS_SUPER_MAGIC)
+		return "hfs";
+#endif
+#ifdef HOSTFS_SUPER_MAGIC
+	if (pbuf->f_type == HOSTFS_SUPER_MAGIC)
+		return "hostfs";
+#endif
+#ifdef HPFS_SUPER_MAGIC
+	if (pbuf->f_type == HPFS_SUPER_MAGIC)
+		return "hpfs";
+#endif
+#ifdef HUGETLBFS_MAGIC
+	if (pbuf->f_type == HUGETLBFS_MAGIC)
+		return "hugetlbfs";
+#endif
+#ifdef ISOFS_SUPER_MAGIC
+	if (pbuf->f_type == ISOFS_SUPER_MAGIC)
+		return "isofs";
+#endif
+#ifdef JFFS2_SUPER_MAGIC
+	if (pbuf->f_type == JFFS2_SUPER_MAGIC)
+		return "jffs2";
+#endif
+#ifdef JFS_SUPER_MAGIC
+	if (pbuf->f_type == JFS_SUPER_MAGIC)
+		return "jfs";
+#endif
+#ifdef MINIX_SUPER_MAGIC
+	if (pbuf->f_type == MINIX_SUPER_MAGIC)
+		return "minix";
+#endif
+#ifdef MINIX2_SUPER_MAGIC
+	if (pbuf->f_type == MINIX2_SUPER_MAGIC)
+		return "minix2";
+#endif
+#ifdef MINIX3_SUPER_MAGIC
+	if (pbuf->f_type == MINIX3_SUPER_MAGIC)
+		return "minix3";
+#endif
+#ifdef MQUEUE_MAGIC
+	if (pbuf->f_type == MQUEUE_MAGIC)
+		return "mqueue";
+#endif
+#ifdef MSDOS_SUPER_MAGIC
+	if (pbuf->f_type == MSDOS_SUPER_MAGIC)
+		return "msdos";
+#endif
+#ifdef NCP_SUPER_MAGIC
+	if (pbuf->f_type == NCP_SUPER_MAGIC)
+		return "ncp";
+#endif
+#ifdef NFS_SUPER_MAGIC
+	if (pbuf->f_type == NFS_SUPER_MAGIC)
+		return "nfs";
+#endif
+#ifdef NILFS_SUPER_MAGIC
+	if (pbuf->f_type == NILFS_SUPER_MAGIC)
+		return "nilfs";
+#endif
+#ifdef NTFS_SB_MAGIC
+	if (pbuf->f_type == NTFS_SB_MAGIC)
+		return "ntfs-sb";
+#endif
+#ifdef OCFS2_SUPER_MAGIC
+	if (pbuf->f_type == OCFS2_SUPER_MAGIC)
+		return "ocfs2";
+#endif
+#ifdef OPENPROM_SUPER_MAGIC
+	if (pbuf->f_type == OPENPROM_SUPER_MAGIC)
+		return "openprom";
+#endif
+#ifdef OVERLAYFS_SUPER_MAGIC
+	if (pbuf->f_type == OVERLAYFS_SUPER_MAGIC)
+		return "overlay";
+#endif
+#ifdef PIPEFS_MAGIC
+	if (pbuf->f_type == PIPEFS_MAGIC)
+		return "pipefs";
+#endif
+#ifdef PROC_SUPER_MAGIC
+	if (pbuf->f_type == PROC_SUPER_MAGIC)
+		return "proc";
+#endif
+#ifdef PSTOREFS_MAGIC
+	if (pbuf->f_type == PSTOREFS_MAGIC)
+		return "pstorefs";
+#endif
+#ifdef QNX4_SUPER_MAGIC
+	if (pbuf->f_type == QNX4_SUPER_MAGIC)
+		return "qnx4";
+#endif
+#ifdef QNX6_SUPER_MAGIC
+	if (pbuf->f_type == QNX6_SUPER_MAGIC)
+		return "qnx6";
+#endif
+#ifdef RAMFS_MAGIC
+	if (pbuf->f_type == RAMFS_MAGIC)
+		return "ramfs";
+#endif
+#ifdef REISERFS_SUPER_MAGIC
+	if (pbuf->f_type == REISERFS_SUPER_MAGIC)
+		return "reiserfs";
+#endif
+#ifdef ROMFS_MAGIC
+	if (pbuf->f_type == ROMFS_MAGIC)
+		return "romfs";
+#endif
+#ifdef SECURITYFS_MAGIC
+	if (pbuf->f_type == SECURITYFS_MAGIC)
+		return "securityfs";
+#endif
+#ifdef SELINUX_MAGIC
+	if (pbuf->f_type == SELINUX_MAGIC)
+		return "selinux";
+#endif
+#ifdef SMACK_MAGIC
+	if (pbuf->f_type == SMACK_MAGIC)
+		return "smack";
+#endif
+#ifdef SMB_SUPER_MAGIC
+	if (pbuf->f_type == SMB_SUPER_MAGIC)
+		return "smb";
+#endif
+#ifdef SOCKFS_MAGIC
+	if (pbuf->f_type == SOCKFS_MAGIC)
+		return "sockfs";
+#endif
+#ifdef SQUASHFS_MAGIC
+	if (pbuf->f_type == SQUASHFS_MAGIC)
+		return "squashfs";
+#endif
+#ifdef SYSFS_MAGIC
+	if (pbuf->f_type == SYSFS_MAGIC)
+		return "sysfs";
+#endif
+#ifdef SYSV2_SUPER_MAGIC
+	if (pbuf->f_type == SYSV2_SUPER_MAGIC)
+		return "sysv2";
+#endif
+#ifdef SYSV4_SUPER_MAGIC
+	if (pbuf->f_type == SYSV4_SUPER_MAGIC)
+		return "sysv4";
+#endif
+#ifdef TMPFS_MAGIC
+	if (pbuf->f_type == TMPFS_MAGIC)
+		return "tmpfs";
+#endif
+#ifdef UDF_SUPER_MAGIC
+	if (pbuf->f_type == UDF_SUPER_MAGIC)
+		return "udf";
+#endif
+#ifdef UFS_MAGIC
+	if (pbuf->f_type == UFS_MAGIC)
+		return "ufs";
+#endif
+#ifdef USBDEVICE_SUPER_MAGIC
+	if (pbuf->f_type == USBDEVICE_SUPER_MAGIC)
+		return "usbdevice";
+#endif
+#ifdef V9FS_MAGIC
+	if (pbuf->f_type == V9FS_MAGIC)
+		return "v9fs";
+#endif
+#ifdef VXFS_SUPER_MAGIC
+	if (pbuf->f_type == VXFS_SUPER_MAGIC)
+		return "vxfs";
+#endif
+#ifdef XENFS_SUPER_MAGIC
+	if (pbuf->f_type == XENFS_SUPER_MAGIC)
+		return "xenfs";
+#endif
+#ifdef XENIX_SUPER_MAGIC
+	if (pbuf->f_type == XENIX_SUPER_MAGIC)
+		return "xenix";
+#endif
+#ifdef XFS_SUPER_MAGIC
+	if (pbuf->f_type == XFS_SUPER_MAGIC)
+		return "xfs";
+#endif
+	/* End of Linux filesystems */
+	return NULL;
+}
+#endif /* def HAVE_LINUX_STATFS */
+
+#if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS)
+/* given a directory path, return filesystem type name (best-effort) */
+static PyObject *getfstype(PyObject *self, PyObject *args)
+{
+	const char *path = NULL;
+	struct statfs buf;
+	int r;
+	if (!PyArg_ParseTuple(args, "s", &path))
+		return NULL;
+
+	memset(&buf, 0, sizeof(buf));
+	r = statfs(path, &buf);
+	if (r != 0)
+		return PyErr_SetFromErrno(PyExc_OSError);
+	return Py_BuildValue("s", describefstype(&buf));
+}
+#endif /* defined(HAVE_LINUX_STATFS) || defined(HAVE_BSD_STATFS) */
+
+#endif /* ndef _WIN32 */
+
+static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+	PyObject *statobj = NULL; /* initialize - optional arg */
+	PyObject *skipobj = NULL; /* initialize - optional arg */
+	char *path, *skip = NULL;
+	int wantstat, plen;
+
+	static char *kwlist[] = {"path", "stat", "skip", NULL};
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
+			kwlist, &path, &plen, &statobj, &skipobj))
+		return NULL;
+
+	wantstat = statobj && PyObject_IsTrue(statobj);
+
+	if (skipobj && skipobj != Py_None) {
+		skip = PyBytes_AsString(skipobj);
+		if (!skip)
+			return NULL;
+	}
+
+	return _listdir(path, plen, wantstat, skip);
+}
+
+#ifdef _WIN32
+static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	static char *kwlist[] = {"name", "mode", "buffering", NULL};
+	PyObject *file_obj = NULL;
+	char *name = NULL;
+	char *mode = "rb";
+	DWORD access = 0;
+	DWORD creation;
+	HANDLE handle;
+	int fd, flags = 0;
+	int bufsize = -1;
+	char m0, m1, m2;
+	char fpmode[4];
+	int fppos = 0;
+	int plus;
+	FILE *fp;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
+					 Py_FileSystemDefaultEncoding,
+					 &name, &mode, &bufsize))
+		return NULL;
+
+	m0 = mode[0];
+	m1 = m0 ? mode[1] : '\0';
+	m2 = m1 ? mode[2] : '\0';
+	plus = m1 == '+' || m2 == '+';
+
+	fpmode[fppos++] = m0;
+	if (m1 == 'b' || m2 == 'b') {
+		flags = _O_BINARY;
+		fpmode[fppos++] = 'b';
+	}
+	else
+		flags = _O_TEXT;
+	if (m0 == 'r' && !plus) {
+		flags |= _O_RDONLY;
+		access = GENERIC_READ;
+	} else {
+		/*
+		work around http://support.microsoft.com/kb/899149 and
+		set _O_RDWR for 'w' and 'a', even if mode has no '+'
+		*/
+		flags |= _O_RDWR;
+		access = GENERIC_READ | GENERIC_WRITE;
+		fpmode[fppos++] = '+';
+	}
+	fpmode[fppos++] = '\0';
+
+	switch (m0) {
+	case 'r':
+		creation = OPEN_EXISTING;
+		break;
+	case 'w':
+		creation = CREATE_ALWAYS;
+		break;
+	case 'a':
+		creation = OPEN_ALWAYS;
+		flags |= _O_APPEND;
+		break;
+	default:
+		PyErr_Format(PyExc_ValueError,
+			     "mode string must begin with one of 'r', 'w', "
+			     "or 'a', not '%c'", m0);
+		goto bail;
+	}
+
+	handle = CreateFile(name, access,
+			    FILE_SHARE_READ | FILE_SHARE_WRITE |
+			    FILE_SHARE_DELETE,
+			    NULL,
+			    creation,
+			    FILE_ATTRIBUTE_NORMAL,
+			    0);
+
+	if (handle == INVALID_HANDLE_VALUE) {
+		PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
+		goto bail;
+	}
+
+	fd = _open_osfhandle((intptr_t)handle, flags);
+
+	if (fd == -1) {
+		CloseHandle(handle);
+		PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
+		goto bail;
+	}
+#ifndef IS_PY3K
+	fp = _fdopen(fd, fpmode);
+	if (fp == NULL) {
+		_close(fd);
+		PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
+		goto bail;
+	}
+
+	file_obj = PyFile_FromFile(fp, name, mode, fclose);
+	if (file_obj == NULL) {
+		fclose(fp);
+		goto bail;
+	}
+
+	PyFile_SetBufSize(file_obj, bufsize);
+#else
+	file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
+	if (file_obj == NULL)
+		goto bail;
+#endif
+bail:
+	PyMem_Free(name);
+	return file_obj;
+}
+#endif
+
+#ifdef __APPLE__
+#include <ApplicationServices/ApplicationServices.h>
+
+static PyObject *isgui(PyObject *self)
+{
+	CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
+
+	if (dict != NULL) {
+		CFRelease(dict);
+		Py_RETURN_TRUE;
+	} else {
+		Py_RETURN_FALSE;
+	}
+}
+#endif
+
+static char osutil_doc[] = "Native operating system services.";
+
+static PyMethodDef methods[] = {
+	{"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
+	 "list a directory\n"},
+#ifdef _WIN32
+	{"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
+	 "Open a file with POSIX-like semantics.\n"
+"On error, this function may raise either a WindowsError or an IOError."},
+#else
+	{"statfiles", (PyCFunction)statfiles, METH_VARARGS | METH_KEYWORDS,
+	 "stat a series of files or symlinks\n"
+"Returns None for non-existent entries and entries of other types.\n"},
+#ifdef CMSG_LEN
+	{"recvfds", (PyCFunction)recvfds, METH_VARARGS,
+	 "receive list of file descriptors via socket\n"},
+#endif
+#ifndef SETPROCNAME_USE_NONE
+	{"setprocname", (PyCFunction)setprocname, METH_VARARGS,
+	 "set process title (best-effort)\n"},
+#endif
+#if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS)
+	{"getfstype", (PyCFunction)getfstype, METH_VARARGS,
+	 "get filesystem type (best-effort)\n"},
+#endif
+#endif /* ndef _WIN32 */
+#ifdef __APPLE__
+	{
+		"isgui", (PyCFunction)isgui, METH_NOARGS,
+		"Is a CoreGraphics session available?"
+	},
+#endif
+	{NULL, NULL}
+};
+
+static const int version = 1;
+
+#ifdef IS_PY3K
+static struct PyModuleDef osutil_module = {
+	PyModuleDef_HEAD_INIT,
+	"osutil",
+	osutil_doc,
+	-1,
+	methods
+};
+
+PyMODINIT_FUNC PyInit_osutil(void)
+{
+	PyObject *m;
+	if (PyType_Ready(&listdir_stat_type) < 0)
+		return NULL;
+
+	m = PyModule_Create(&osutil_module);
+	PyModule_AddIntConstant(m, "version", version);
+	return m;
+}
+#else
+PyMODINIT_FUNC initosutil(void)
+{
+	PyObject *m;
+	if (PyType_Ready(&listdir_stat_type) == -1)
+		return;
+
+	m = Py_InitModule3("osutil", methods, osutil_doc);
+	PyModule_AddIntConstant(m, "version", version);
+}
+#endif
--- a/mercurial/debugcommands.py	Fri Aug 12 11:30:17 2016 +0900
+++ b/mercurial/debugcommands.py	Fri Aug 12 11:35:17 2016 +0900
@@ -1029,6 +1029,8 @@
                 base85,
                 bdiff,
                 mpatch,
+            )
+            from .cext import (
                 osutil,
             )
             dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
--- a/mercurial/osutil.c	Fri Aug 12 11:30:17 2016 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1335 +0,0 @@
-/*
- osutil.c - native operating system services
-
- Copyright 2007 Matt Mackall and others
-
- This software may be used and distributed according to the terms of
- the GNU General Public License, incorporated herein by reference.
-*/
-
-#define _ATFILE_SOURCE
-#include <Python.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#ifdef _WIN32
-#include <windows.h>
-#include <io.h>
-#else
-#include <dirent.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#ifdef HAVE_LINUX_STATFS
-#include <linux/magic.h>
-#include <sys/vfs.h>
-#endif
-#ifdef HAVE_BSD_STATFS
-#include <sys/mount.h>
-#include <sys/param.h>
-#endif
-#endif
-
-#ifdef __APPLE__
-#include <sys/attr.h>
-#include <sys/vnode.h>
-#endif
-
-#include "util.h"
-
-/* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-#ifdef _WIN32
-/*
-stat struct compatible with hg expectations
-Mercurial only uses st_mode, st_size and st_mtime
-the rest is kept to minimize changes between implementations
-*/
-struct hg_stat {
-	int st_dev;
-	int st_mode;
-	int st_nlink;
-	__int64 st_size;
-	int st_mtime;
-	int st_ctime;
-};
-struct listdir_stat {
-	PyObject_HEAD
-	struct hg_stat st;
-};
-#else
-struct listdir_stat {
-	PyObject_HEAD
-	struct stat st;
-};
-#endif
-
-#ifdef IS_PY3K
-#define listdir_slot(name) \
-	static PyObject *listdir_stat_##name(PyObject *self, void *x) \
-	{ \
-		return PyLong_FromLong(((struct listdir_stat *)self)->st.name); \
-	}
-#else
-#define listdir_slot(name) \
-	static PyObject *listdir_stat_##name(PyObject *self, void *x) \
-	{ \
-		return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
-	}
-#endif
-
-listdir_slot(st_dev)
-listdir_slot(st_mode)
-listdir_slot(st_nlink)
-#ifdef _WIN32
-static PyObject *listdir_stat_st_size(PyObject *self, void *x)
-{
-	return PyLong_FromLongLong(
-		(PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
-}
-#else
-listdir_slot(st_size)
-#endif
-listdir_slot(st_mtime)
-listdir_slot(st_ctime)
-
-static struct PyGetSetDef listdir_stat_getsets[] = {
-	{"st_dev", listdir_stat_st_dev, 0, 0, 0},
-	{"st_mode", listdir_stat_st_mode, 0, 0, 0},
-	{"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
-	{"st_size", listdir_stat_st_size, 0, 0, 0},
-	{"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
-	{"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
-	{0, 0, 0, 0, 0}
-};
-
-static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
-{
-	return t->tp_alloc(t, 0);
-}
-
-static void listdir_stat_dealloc(PyObject *o)
-{
-	o->ob_type->tp_free(o);
-}
-
-static PyTypeObject listdir_stat_type = {
-	PyVarObject_HEAD_INIT(NULL, 0)
-	"osutil.stat",             /*tp_name*/
-	sizeof(struct listdir_stat), /*tp_basicsize*/
-	0,                         /*tp_itemsize*/
-	(destructor)listdir_stat_dealloc, /*tp_dealloc*/
-	0,                         /*tp_print*/
-	0,                         /*tp_getattr*/
-	0,                         /*tp_setattr*/
-	0,                         /*tp_compare*/
-	0,                         /*tp_repr*/
-	0,                         /*tp_as_number*/
-	0,                         /*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 | Py_TPFLAGS_BASETYPE, /*tp_flags*/
-	"stat objects",            /* 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 */
-	listdir_stat_getsets,      /* 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 */
-	listdir_stat_new,          /* tp_new */
-};
-
-#ifdef _WIN32
-
-static int to_python_time(const FILETIME *tm)
-{
-	/* number of seconds between epoch and January 1 1601 */
-	const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
-	/* conversion factor from 100ns to 1s */
-	const __int64 a1 = 10000000;
-	/* explicit (int) cast to suspend compiler warnings */
-	return (int)((((__int64)tm->dwHighDateTime << 32)
-			+ tm->dwLowDateTime) / a1 - a0);
-}
-
-static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
-{
-	PyObject *py_st;
-	struct hg_stat *stp;
-
-	int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-		? _S_IFDIR : _S_IFREG;
-
-	if (!wantstat)
-		return Py_BuildValue("si", fd->cFileName, kind);
-
-	py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
-	if (!py_st)
-		return NULL;
-
-	stp = &((struct listdir_stat *)py_st)->st;
-	/*
-	use kind as st_mode
-	rwx bits on Win32 are meaningless
-	and Hg does not use them anyway
-	*/
-	stp->st_mode  = kind;
-	stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
-	stp->st_ctime = to_python_time(&fd->ftCreationTime);
-	if (kind == _S_IFREG)
-		stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
-				+ fd->nFileSizeLow;
-	return Py_BuildValue("siN", fd->cFileName,
-		kind, py_st);
-}
-
-static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
-{
-	PyObject *rval = NULL; /* initialize - return value */
-	PyObject *list;
-	HANDLE fh;
-	WIN32_FIND_DATAA fd;
-	char *pattern;
-
-	/* build the path + \* pattern string */
-	pattern = PyMem_Malloc(plen + 3); /* path + \* + \0 */
-	if (!pattern) {
-		PyErr_NoMemory();
-		goto error_nomem;
-	}
-	memcpy(pattern, path, plen);
-
-	if (plen > 0) {
-		char c = path[plen-1];
-		if (c != ':' && c != '/' && c != '\\')
-			pattern[plen++] = '\\';
-	}
-	pattern[plen++] = '*';
-	pattern[plen] = '\0';
-
-	fh = FindFirstFileA(pattern, &fd);
-	if (fh == INVALID_HANDLE_VALUE) {
-		PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
-		goto error_file;
-	}
-
-	list = PyList_New(0);
-	if (!list)
-		goto error_list;
-
-	do {
-		PyObject *item;
-
-		if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
-			if (!strcmp(fd.cFileName, ".")
-			|| !strcmp(fd.cFileName, ".."))
-				continue;
-
-			if (skip && !strcmp(fd.cFileName, skip)) {
-				rval = PyList_New(0);
-				goto error;
-			}
-		}
-
-		item = make_item(&fd, wantstat);
-		if (!item)
-			goto error;
-
-		if (PyList_Append(list, item)) {
-			Py_XDECREF(item);
-			goto error;
-		}
-
-		Py_XDECREF(item);
-	} while (FindNextFileA(fh, &fd));
-
-	if (GetLastError() != ERROR_NO_MORE_FILES) {
-		PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
-		goto error;
-	}
-
-	rval = list;
-	Py_XINCREF(rval);
-error:
-	Py_XDECREF(list);
-error_list:
-	FindClose(fh);
-error_file:
-	PyMem_Free(pattern);
-error_nomem:
-	return rval;
-}
-
-#else
-
-int entkind(struct dirent *ent)
-{
-#ifdef DT_REG
-	switch (ent->d_type) {
-	case DT_REG: return S_IFREG;
-	case DT_DIR: return S_IFDIR;
-	case DT_LNK: return S_IFLNK;
-	case DT_BLK: return S_IFBLK;
-	case DT_CHR: return S_IFCHR;
-	case DT_FIFO: return S_IFIFO;
-	case DT_SOCK: return S_IFSOCK;
-	}
-#endif
-	return -1;
-}
-
-static PyObject *makestat(const struct stat *st)
-{
-	PyObject *stat;
-
-	stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
-	if (stat)
-		memcpy(&((struct listdir_stat *)stat)->st, st, sizeof(*st));
-	return stat;
-}
-
-static PyObject *_listdir_stat(char *path, int pathlen, int keepstat,
-			       char *skip)
-{
-	PyObject *list, *elem, *stat = NULL, *ret = NULL;
-	char fullpath[PATH_MAX + 10];
-	int kind, err;
-	struct stat st;
-	struct dirent *ent;
-	DIR *dir;
-#ifdef AT_SYMLINK_NOFOLLOW
-	int dfd = -1;
-#endif
-
-	if (pathlen >= PATH_MAX) {
-		errno = ENAMETOOLONG;
-		PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
-		goto error_value;
-	}
-	strncpy(fullpath, path, PATH_MAX);
-	fullpath[pathlen] = '/';
-
-#ifdef AT_SYMLINK_NOFOLLOW
-	dfd = open(path, O_RDONLY);
-	if (dfd == -1) {
-		PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
-		goto error_value;
-	}
-	dir = fdopendir(dfd);
-#else
-	dir = opendir(path);
-#endif
-	if (!dir) {
-		PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
-		goto error_dir;
-	}
-
-	list = PyList_New(0);
-	if (!list)
-		goto error_list;
-
-	while ((ent = readdir(dir))) {
-		if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
-			continue;
-
-		kind = entkind(ent);
-		if (kind == -1 || keepstat) {
-#ifdef AT_SYMLINK_NOFOLLOW
-			err = fstatat(dfd, ent->d_name, &st,
-				      AT_SYMLINK_NOFOLLOW);
-#else
-			strncpy(fullpath + pathlen + 1, ent->d_name,
-				PATH_MAX - pathlen);
-			fullpath[PATH_MAX] = '\0';
-			err = lstat(fullpath, &st);
-#endif
-			if (err == -1) {
-				/* race with file deletion? */
-				if (errno == ENOENT)
-					continue;
-				strncpy(fullpath + pathlen + 1, ent->d_name,
-					PATH_MAX - pathlen);
-				fullpath[PATH_MAX] = 0;
-				PyErr_SetFromErrnoWithFilename(PyExc_OSError,
-							       fullpath);
-				goto error;
-			}
-			kind = st.st_mode & S_IFMT;
-		}
-
-		/* quit early? */
-		if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
-			ret = PyList_New(0);
-			goto error;
-		}
-
-		if (keepstat) {
-			stat = makestat(&st);
-			if (!stat)
-				goto error;
-			elem = Py_BuildValue("siN", ent->d_name, kind, stat);
-		} else
-			elem = Py_BuildValue("si", ent->d_name, kind);
-		if (!elem)
-			goto error;
-		stat = NULL;
-
-		PyList_Append(list, elem);
-		Py_DECREF(elem);
-	}
-
-	ret = list;
-	Py_INCREF(ret);
-
-error:
-	Py_DECREF(list);
-	Py_XDECREF(stat);
-error_list:
-	closedir(dir);
-	/* closedir also closes its dirfd */
-	goto error_value;
-error_dir:
-#ifdef AT_SYMLINK_NOFOLLOW
-	close(dfd);
-#endif
-error_value:
-	return ret;
-}
-
-#ifdef __APPLE__
-
-typedef struct {
-	u_int32_t length;
-	attrreference_t name;
-	fsobj_type_t obj_type;
-	struct timespec mtime;
-#if __LITTLE_ENDIAN__
-	mode_t access_mask;
-	uint16_t padding;
-#else
-	uint16_t padding;
-	mode_t access_mask;
-#endif
-	off_t size;
-} __attribute__((packed)) attrbuf_entry;
-
-int attrkind(attrbuf_entry *entry)
-{
-	switch (entry->obj_type) {
-	case VREG: return S_IFREG;
-	case VDIR: return S_IFDIR;
-	case VLNK: return S_IFLNK;
-	case VBLK: return S_IFBLK;
-	case VCHR: return S_IFCHR;
-	case VFIFO: return S_IFIFO;
-	case VSOCK: return S_IFSOCK;
-	}
-	return -1;
-}
-
-/* get these many entries at a time */
-#define LISTDIR_BATCH_SIZE 50
-
-static PyObject *_listdir_batch(char *path, int pathlen, int keepstat,
-				char *skip, bool *fallback)
-{
-	PyObject *list, *elem, *stat = NULL, *ret = NULL;
-	int kind, err;
-	unsigned long index;
-	unsigned int count, old_state, new_state;
-	bool state_seen = false;
-	attrbuf_entry *entry;
-	/* from the getattrlist(2) man page: a path can be no longer than
-	   (NAME_MAX * 3 + 1) bytes. Also, "The getattrlist() function will
-	   silently truncate attribute data if attrBufSize is too small." So
-	   pass in a buffer big enough for the worst case. */
-	char attrbuf[LISTDIR_BATCH_SIZE * (sizeof(attrbuf_entry) + NAME_MAX * 3 + 1)];
-	unsigned int basep_unused;
-
-	struct stat st;
-	int dfd = -1;
-
-	/* these must match the attrbuf_entry struct, otherwise you'll end up
-	   with garbage */
-	struct attrlist requested_attr = {0};
-	requested_attr.bitmapcount = ATTR_BIT_MAP_COUNT;
-	requested_attr.commonattr = (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE |
-				     ATTR_CMN_MODTIME | ATTR_CMN_ACCESSMASK);
-	requested_attr.fileattr = ATTR_FILE_DATALENGTH;
-
-	*fallback = false;
-
-	if (pathlen >= PATH_MAX) {
-		errno = ENAMETOOLONG;
-		PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
-		goto error_value;
-	}
-
-	dfd = open(path, O_RDONLY);
-	if (dfd == -1) {
-		PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
-		goto error_value;
-	}
-
-	list = PyList_New(0);
-	if (!list)
-		goto error_dir;
-
-	do {
-		count = LISTDIR_BATCH_SIZE;
-		err = getdirentriesattr(dfd, &requested_attr, &attrbuf,
-					sizeof(attrbuf), &count, &basep_unused,
-					&new_state, 0);
-		if (err < 0) {
-			if (errno == ENOTSUP) {
-				/* We're on a filesystem that doesn't support
-				   getdirentriesattr. Fall back to the
-				   stat-based implementation. */
-				*fallback = true;
-			} else
-				PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
-			goto error;
-		}
-
-		if (!state_seen) {
-			old_state = new_state;
-			state_seen = true;
-		} else if (old_state != new_state) {
-			/* There's an edge case with getdirentriesattr. Consider
-			   the following initial list of files:
-
-			   a
-			   b
-			   <--
-			   c
-			   d
-
-			   If the iteration is paused at the arrow, and b is
-			   deleted before it is resumed, getdirentriesattr will
-			   not return d at all!  Ordinarily we're expected to
-			   restart the iteration from the beginning. To avoid
-			   getting stuck in a retry loop here, fall back to
-			   stat. */
-			*fallback = true;
-			goto error;
-		}
-
-		entry = (attrbuf_entry *)attrbuf;
-
-		for (index = 0; index < count; index++) {
-			char *filename = ((char *)&entry->name) +
-				entry->name.attr_dataoffset;
-
-			if (!strcmp(filename, ".") || !strcmp(filename, ".."))
-				continue;
-
-			kind = attrkind(entry);
-			if (kind == -1) {
-				PyErr_Format(PyExc_OSError,
-					     "unknown object type %u for file "
-					     "%s%s!",
-					     entry->obj_type, path, filename);
-				goto error;
-			}
-
-			/* quit early? */
-			if (skip && kind == S_IFDIR && !strcmp(filename, skip)) {
-				ret = PyList_New(0);
-				goto error;
-			}
-
-			if (keepstat) {
-				/* from the getattrlist(2) man page: "Only the
-				   permission bits ... are valid". */
-				st.st_mode = (entry->access_mask & ~S_IFMT) | kind;
-				st.st_mtime = entry->mtime.tv_sec;
-				st.st_size = entry->size;
-				stat = makestat(&st);
-				if (!stat)
-					goto error;
-				elem = Py_BuildValue("siN", filename, kind, stat);
-			} else
-				elem = Py_BuildValue("si", filename, kind);
-			if (!elem)
-				goto error;
-			stat = NULL;
-
-			PyList_Append(list, elem);
-			Py_DECREF(elem);
-
-			entry = (attrbuf_entry *)((char *)entry + entry->length);
-		}
-	} while (err == 0);
-
-	ret = list;
-	Py_INCREF(ret);
-
-error:
-	Py_DECREF(list);
-	Py_XDECREF(stat);
-error_dir:
-	close(dfd);
-error_value:
-	return ret;
-}
-
-#endif /* __APPLE__ */
-
-static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
-{
-#ifdef __APPLE__
-	PyObject *ret;
-	bool fallback = false;
-
-	ret = _listdir_batch(path, pathlen, keepstat, skip, &fallback);
-	if (ret != NULL || !fallback)
-		return ret;
-#endif
-	return _listdir_stat(path, pathlen, keepstat, skip);
-}
-
-static PyObject *statfiles(PyObject *self, PyObject *args)
-{
-	PyObject *names, *stats;
-	Py_ssize_t i, count;
-
-	if (!PyArg_ParseTuple(args, "O:statfiles", &names))
-		return NULL;
-
-	count = PySequence_Length(names);
-	if (count == -1) {
-		PyErr_SetString(PyExc_TypeError, "not a sequence");
-		return NULL;
-	}
-
-	stats = PyList_New(count);
-	if (stats == NULL)
-		return NULL;
-
-	for (i = 0; i < count; i++) {
-		PyObject *stat, *pypath;
-		struct stat st;
-		int ret, kind;
-		char *path;
-
-		/* With a large file count or on a slow filesystem,
-		   don't block signals for long (issue4878). */
-		if ((i % 1000) == 999 && PyErr_CheckSignals() == -1)
-			goto bail;
-
-		pypath = PySequence_GetItem(names, i);
-		if (!pypath)
-			goto bail;
-		path = PyBytes_AsString(pypath);
-		if (path == NULL) {
-			Py_DECREF(pypath);
-			PyErr_SetString(PyExc_TypeError, "not a string");
-			goto bail;
-		}
-		ret = lstat(path, &st);
-		Py_DECREF(pypath);
-		kind = st.st_mode & S_IFMT;
-		if (ret != -1 && (kind == S_IFREG || kind == S_IFLNK)) {
-			stat = makestat(&st);
-			if (stat == NULL)
-				goto bail;
-			PyList_SET_ITEM(stats, i, stat);
-		} else {
-			Py_INCREF(Py_None);
-			PyList_SET_ITEM(stats, i, Py_None);
-		}
-	}
-
-	return stats;
-
-bail:
-	Py_DECREF(stats);
-	return NULL;
-}
-
-/*
- * recvfds() simply does not release GIL during blocking io operation because
- * command server is known to be single-threaded.
- *
- * Old systems such as Solaris don't provide CMSG_LEN, msg_control, etc.
- * Currently, recvfds() is not supported on these platforms.
- */
-#ifdef CMSG_LEN
-
-static ssize_t recvfdstobuf(int sockfd, int **rfds, void *cbuf, size_t cbufsize)
-{
-	char dummy[1];
-	struct iovec iov = {dummy, sizeof(dummy)};
-	struct msghdr msgh = {0};
-	struct cmsghdr *cmsg;
-
-	msgh.msg_iov = &iov;
-	msgh.msg_iovlen = 1;
-	msgh.msg_control = cbuf;
-	msgh.msg_controllen = (socklen_t)cbufsize;
-	if (recvmsg(sockfd, &msgh, 0) < 0)
-		return -1;
-
-	for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg;
-	     cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
-		if (cmsg->cmsg_level != SOL_SOCKET ||
-		    cmsg->cmsg_type != SCM_RIGHTS)
-			continue;
-		*rfds = (int *)CMSG_DATA(cmsg);
-		return (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
-	}
-
-	*rfds = cbuf;
-	return 0;
-}
-
-static PyObject *recvfds(PyObject *self, PyObject *args)
-{
-	int sockfd;
-	int *rfds = NULL;
-	ssize_t rfdscount, i;
-	char cbuf[256];
-	PyObject *rfdslist = NULL;
-
-	if (!PyArg_ParseTuple(args, "i", &sockfd))
-		return NULL;
-
-	rfdscount = recvfdstobuf(sockfd, &rfds, cbuf, sizeof(cbuf));
-	if (rfdscount < 0)
-		return PyErr_SetFromErrno(PyExc_OSError);
-
-	rfdslist = PyList_New(rfdscount);
-	if (!rfdslist)
-		goto bail;
-	for (i = 0; i < rfdscount; i++) {
-		PyObject *obj = PyLong_FromLong(rfds[i]);
-		if (!obj)
-			goto bail;
-		PyList_SET_ITEM(rfdslist, i, obj);
-	}
-	return rfdslist;
-
-bail:
-	Py_XDECREF(rfdslist);
-	return NULL;
-}
-
-#endif /* CMSG_LEN */
-
-#if defined(HAVE_SETPROCTITLE)
-/* setproctitle is the first choice - available in FreeBSD */
-#define SETPROCNAME_USE_SETPROCTITLE
-#elif (defined(__linux__) || defined(__APPLE__)) && PY_MAJOR_VERSION == 2
-/* rewrite the argv buffer in place - works in Linux and OS X. Py_GetArgcArgv
- * in Python 3 returns the copied wchar_t **argv, thus unsupported. */
-#define SETPROCNAME_USE_ARGVREWRITE
-#else
-#define SETPROCNAME_USE_NONE
-#endif
-
-#ifndef SETPROCNAME_USE_NONE
-static PyObject *setprocname(PyObject *self, PyObject *args)
-{
-	const char *name = NULL;
-	if (!PyArg_ParseTuple(args, "s", &name))
-		return NULL;
-
-#if defined(SETPROCNAME_USE_SETPROCTITLE)
-	setproctitle("%s", name);
-#elif defined(SETPROCNAME_USE_ARGVREWRITE)
-	{
-		static char *argvstart = NULL;
-		static size_t argvsize = 0;
-		if (argvstart == NULL) {
-			int argc = 0, i;
-			char **argv = NULL;
-			char *argvend;
-			extern void Py_GetArgcArgv(int *argc, char ***argv);
-			Py_GetArgcArgv(&argc, &argv);
-
-			/* Check the memory we can use. Typically, argv[i] and
-			 * argv[i + 1] are continuous. */
-			argvend = argvstart = argv[0];
-			for (i = 0; i < argc; ++i) {
-				if (argv[i] > argvend || argv[i] < argvstart)
-					break; /* not continuous */
-				size_t len = strlen(argv[i]);
-				argvend = argv[i] + len + 1 /* '\0' */;
-			}
-			if (argvend > argvstart) /* sanity check */
-				argvsize = argvend - argvstart;
-		}
-
-		if (argvstart && argvsize > 1) {
-			int n = snprintf(argvstart, argvsize, "%s", name);
-			if (n >= 0 && (size_t)n < argvsize)
-				memset(argvstart + n, 0, argvsize - n);
-		}
-	}
-#endif
-
-	Py_RETURN_NONE;
-}
-#endif /* ndef SETPROCNAME_USE_NONE */
-
-#if defined(HAVE_BSD_STATFS)
-static const char *describefstype(const struct statfs *pbuf)
-{
-	/* BSD or OSX provides a f_fstypename field */
-	return pbuf->f_fstypename;
-}
-#elif defined(HAVE_LINUX_STATFS)
-static const char *describefstype(const struct statfs *pbuf)
-{
-	/* Begin of Linux filesystems */
-#ifdef ADFS_SUPER_MAGIC
-	if (pbuf->f_type == ADFS_SUPER_MAGIC)
-		return "adfs";
-#endif
-#ifdef AFFS_SUPER_MAGIC
-	if (pbuf->f_type == AFFS_SUPER_MAGIC)
-		return "affs";
-#endif
-#ifdef AUTOFS_SUPER_MAGIC
-	if (pbuf->f_type == AUTOFS_SUPER_MAGIC)
-		return "autofs";
-#endif
-#ifdef BDEVFS_MAGIC
-	if (pbuf->f_type == BDEVFS_MAGIC)
-		return "bdevfs";
-#endif
-#ifdef BEFS_SUPER_MAGIC
-	if (pbuf->f_type == BEFS_SUPER_MAGIC)
-		return "befs";
-#endif
-#ifdef BFS_MAGIC
-	if (pbuf->f_type == BFS_MAGIC)
-		return "bfs";
-#endif
-#ifdef BINFMTFS_MAGIC
-	if (pbuf->f_type == BINFMTFS_MAGIC)
-		return "binfmtfs";
-#endif
-#ifdef BTRFS_SUPER_MAGIC
-	if (pbuf->f_type == BTRFS_SUPER_MAGIC)
-		return "btrfs";
-#endif
-#ifdef CGROUP_SUPER_MAGIC
-	if (pbuf->f_type == CGROUP_SUPER_MAGIC)
-		return "cgroup";
-#endif
-#ifdef CIFS_MAGIC_NUMBER
-	if (pbuf->f_type == CIFS_MAGIC_NUMBER)
-		return "cifs";
-#endif
-#ifdef CODA_SUPER_MAGIC
-	if (pbuf->f_type == CODA_SUPER_MAGIC)
-		return "coda";
-#endif
-#ifdef COH_SUPER_MAGIC
-	if (pbuf->f_type == COH_SUPER_MAGIC)
-		return "coh";
-#endif
-#ifdef CRAMFS_MAGIC
-	if (pbuf->f_type == CRAMFS_MAGIC)
-		return "cramfs";
-#endif
-#ifdef DEBUGFS_MAGIC
-	if (pbuf->f_type == DEBUGFS_MAGIC)
-		return "debugfs";
-#endif
-#ifdef DEVFS_SUPER_MAGIC
-	if (pbuf->f_type == DEVFS_SUPER_MAGIC)
-		return "devfs";
-#endif
-#ifdef DEVPTS_SUPER_MAGIC
-	if (pbuf->f_type == DEVPTS_SUPER_MAGIC)
-		return "devpts";
-#endif
-#ifdef EFIVARFS_MAGIC
-	if (pbuf->f_type == EFIVARFS_MAGIC)
-		return "efivarfs";
-#endif
-#ifdef EFS_SUPER_MAGIC
-	if (pbuf->f_type == EFS_SUPER_MAGIC)
-		return "efs";
-#endif
-#ifdef EXT_SUPER_MAGIC
-	if (pbuf->f_type == EXT_SUPER_MAGIC)
-		return "ext";
-#endif
-#ifdef EXT2_OLD_SUPER_MAGIC
-	if (pbuf->f_type == EXT2_OLD_SUPER_MAGIC)
-		return "ext2";
-#endif
-#ifdef EXT2_SUPER_MAGIC
-	if (pbuf->f_type == EXT2_SUPER_MAGIC)
-		return "ext2";
-#endif
-#ifdef EXT3_SUPER_MAGIC
-	if (pbuf->f_type == EXT3_SUPER_MAGIC)
-		return "ext3";
-#endif
-#ifdef EXT4_SUPER_MAGIC
-	if (pbuf->f_type == EXT4_SUPER_MAGIC)
-		return "ext4";
-#endif
-#ifdef F2FS_SUPER_MAGIC
-	if (pbuf->f_type == F2FS_SUPER_MAGIC)
-		return "f2fs";
-#endif
-#ifdef FUSE_SUPER_MAGIC
-	if (pbuf->f_type == FUSE_SUPER_MAGIC)
-		return "fuse";
-#endif
-#ifdef FUTEXFS_SUPER_MAGIC
-	if (pbuf->f_type == FUTEXFS_SUPER_MAGIC)
-		return "futexfs";
-#endif
-#ifdef HFS_SUPER_MAGIC
-	if (pbuf->f_type == HFS_SUPER_MAGIC)
-		return "hfs";
-#endif
-#ifdef HOSTFS_SUPER_MAGIC
-	if (pbuf->f_type == HOSTFS_SUPER_MAGIC)
-		return "hostfs";
-#endif
-#ifdef HPFS_SUPER_MAGIC
-	if (pbuf->f_type == HPFS_SUPER_MAGIC)
-		return "hpfs";
-#endif
-#ifdef HUGETLBFS_MAGIC
-	if (pbuf->f_type == HUGETLBFS_MAGIC)
-		return "hugetlbfs";
-#endif
-#ifdef ISOFS_SUPER_MAGIC
-	if (pbuf->f_type == ISOFS_SUPER_MAGIC)
-		return "isofs";
-#endif
-#ifdef JFFS2_SUPER_MAGIC
-	if (pbuf->f_type == JFFS2_SUPER_MAGIC)
-		return "jffs2";
-#endif
-#ifdef JFS_SUPER_MAGIC
-	if (pbuf->f_type == JFS_SUPER_MAGIC)
-		return "jfs";
-#endif
-#ifdef MINIX_SUPER_MAGIC
-	if (pbuf->f_type == MINIX_SUPER_MAGIC)
-		return "minix";
-#endif
-#ifdef MINIX2_SUPER_MAGIC
-	if (pbuf->f_type == MINIX2_SUPER_MAGIC)
-		return "minix2";
-#endif
-#ifdef MINIX3_SUPER_MAGIC
-	if (pbuf->f_type == MINIX3_SUPER_MAGIC)
-		return "minix3";
-#endif
-#ifdef MQUEUE_MAGIC
-	if (pbuf->f_type == MQUEUE_MAGIC)
-		return "mqueue";
-#endif
-#ifdef MSDOS_SUPER_MAGIC
-	if (pbuf->f_type == MSDOS_SUPER_MAGIC)
-		return "msdos";
-#endif
-#ifdef NCP_SUPER_MAGIC
-	if (pbuf->f_type == NCP_SUPER_MAGIC)
-		return "ncp";
-#endif
-#ifdef NFS_SUPER_MAGIC
-	if (pbuf->f_type == NFS_SUPER_MAGIC)
-		return "nfs";
-#endif
-#ifdef NILFS_SUPER_MAGIC
-	if (pbuf->f_type == NILFS_SUPER_MAGIC)
-		return "nilfs";
-#endif
-#ifdef NTFS_SB_MAGIC
-	if (pbuf->f_type == NTFS_SB_MAGIC)
-		return "ntfs-sb";
-#endif
-#ifdef OCFS2_SUPER_MAGIC
-	if (pbuf->f_type == OCFS2_SUPER_MAGIC)
-		return "ocfs2";
-#endif
-#ifdef OPENPROM_SUPER_MAGIC
-	if (pbuf->f_type == OPENPROM_SUPER_MAGIC)
-		return "openprom";
-#endif
-#ifdef OVERLAYFS_SUPER_MAGIC
-	if (pbuf->f_type == OVERLAYFS_SUPER_MAGIC)
-		return "overlay";
-#endif
-#ifdef PIPEFS_MAGIC
-	if (pbuf->f_type == PIPEFS_MAGIC)
-		return "pipefs";
-#endif
-#ifdef PROC_SUPER_MAGIC
-	if (pbuf->f_type == PROC_SUPER_MAGIC)
-		return "proc";
-#endif
-#ifdef PSTOREFS_MAGIC
-	if (pbuf->f_type == PSTOREFS_MAGIC)
-		return "pstorefs";
-#endif
-#ifdef QNX4_SUPER_MAGIC
-	if (pbuf->f_type == QNX4_SUPER_MAGIC)
-		return "qnx4";
-#endif
-#ifdef QNX6_SUPER_MAGIC
-	if (pbuf->f_type == QNX6_SUPER_MAGIC)
-		return "qnx6";
-#endif
-#ifdef RAMFS_MAGIC
-	if (pbuf->f_type == RAMFS_MAGIC)
-		return "ramfs";
-#endif
-#ifdef REISERFS_SUPER_MAGIC
-	if (pbuf->f_type == REISERFS_SUPER_MAGIC)
-		return "reiserfs";
-#endif
-#ifdef ROMFS_MAGIC
-	if (pbuf->f_type == ROMFS_MAGIC)
-		return "romfs";
-#endif
-#ifdef SECURITYFS_MAGIC
-	if (pbuf->f_type == SECURITYFS_MAGIC)
-		return "securityfs";
-#endif
-#ifdef SELINUX_MAGIC
-	if (pbuf->f_type == SELINUX_MAGIC)
-		return "selinux";
-#endif
-#ifdef SMACK_MAGIC
-	if (pbuf->f_type == SMACK_MAGIC)
-		return "smack";
-#endif
-#ifdef SMB_SUPER_MAGIC
-	if (pbuf->f_type == SMB_SUPER_MAGIC)
-		return "smb";
-#endif
-#ifdef SOCKFS_MAGIC
-	if (pbuf->f_type == SOCKFS_MAGIC)
-		return "sockfs";
-#endif
-#ifdef SQUASHFS_MAGIC
-	if (pbuf->f_type == SQUASHFS_MAGIC)
-		return "squashfs";
-#endif
-#ifdef SYSFS_MAGIC
-	if (pbuf->f_type == SYSFS_MAGIC)
-		return "sysfs";
-#endif
-#ifdef SYSV2_SUPER_MAGIC
-	if (pbuf->f_type == SYSV2_SUPER_MAGIC)
-		return "sysv2";
-#endif
-#ifdef SYSV4_SUPER_MAGIC
-	if (pbuf->f_type == SYSV4_SUPER_MAGIC)
-		return "sysv4";
-#endif
-#ifdef TMPFS_MAGIC
-	if (pbuf->f_type == TMPFS_MAGIC)
-		return "tmpfs";
-#endif
-#ifdef UDF_SUPER_MAGIC
-	if (pbuf->f_type == UDF_SUPER_MAGIC)
-		return "udf";
-#endif
-#ifdef UFS_MAGIC
-	if (pbuf->f_type == UFS_MAGIC)
-		return "ufs";
-#endif
-#ifdef USBDEVICE_SUPER_MAGIC
-	if (pbuf->f_type == USBDEVICE_SUPER_MAGIC)
-		return "usbdevice";
-#endif
-#ifdef V9FS_MAGIC
-	if (pbuf->f_type == V9FS_MAGIC)
-		return "v9fs";
-#endif
-#ifdef VXFS_SUPER_MAGIC
-	if (pbuf->f_type == VXFS_SUPER_MAGIC)
-		return "vxfs";
-#endif
-#ifdef XENFS_SUPER_MAGIC
-	if (pbuf->f_type == XENFS_SUPER_MAGIC)
-		return "xenfs";
-#endif
-#ifdef XENIX_SUPER_MAGIC
-	if (pbuf->f_type == XENIX_SUPER_MAGIC)
-		return "xenix";
-#endif
-#ifdef XFS_SUPER_MAGIC
-	if (pbuf->f_type == XFS_SUPER_MAGIC)
-		return "xfs";
-#endif
-	/* End of Linux filesystems */
-	return NULL;
-}
-#endif /* def HAVE_LINUX_STATFS */
-
-#if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS)
-/* given a directory path, return filesystem type name (best-effort) */
-static PyObject *getfstype(PyObject *self, PyObject *args)
-{
-	const char *path = NULL;
-	struct statfs buf;
-	int r;
-	if (!PyArg_ParseTuple(args, "s", &path))
-		return NULL;
-
-	memset(&buf, 0, sizeof(buf));
-	r = statfs(path, &buf);
-	if (r != 0)
-		return PyErr_SetFromErrno(PyExc_OSError);
-	return Py_BuildValue("s", describefstype(&buf));
-}
-#endif /* defined(HAVE_LINUX_STATFS) || defined(HAVE_BSD_STATFS) */
-
-#endif /* ndef _WIN32 */
-
-static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
-{
-	PyObject *statobj = NULL; /* initialize - optional arg */
-	PyObject *skipobj = NULL; /* initialize - optional arg */
-	char *path, *skip = NULL;
-	int wantstat, plen;
-
-	static char *kwlist[] = {"path", "stat", "skip", NULL};
-
-	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
-			kwlist, &path, &plen, &statobj, &skipobj))
-		return NULL;
-
-	wantstat = statobj && PyObject_IsTrue(statobj);
-
-	if (skipobj && skipobj != Py_None) {
-		skip = PyBytes_AsString(skipobj);
-		if (!skip)
-			return NULL;
-	}
-
-	return _listdir(path, plen, wantstat, skip);
-}
-
-#ifdef _WIN32
-static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
-{
-	static char *kwlist[] = {"name", "mode", "buffering", NULL};
-	PyObject *file_obj = NULL;
-	char *name = NULL;
-	char *mode = "rb";
-	DWORD access = 0;
-	DWORD creation;
-	HANDLE handle;
-	int fd, flags = 0;
-	int bufsize = -1;
-	char m0, m1, m2;
-	char fpmode[4];
-	int fppos = 0;
-	int plus;
-	FILE *fp;
-
-	if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
-					 Py_FileSystemDefaultEncoding,
-					 &name, &mode, &bufsize))
-		return NULL;
-
-	m0 = mode[0];
-	m1 = m0 ? mode[1] : '\0';
-	m2 = m1 ? mode[2] : '\0';
-	plus = m1 == '+' || m2 == '+';
-
-	fpmode[fppos++] = m0;
-	if (m1 == 'b' || m2 == 'b') {
-		flags = _O_BINARY;
-		fpmode[fppos++] = 'b';
-	}
-	else
-		flags = _O_TEXT;
-	if (m0 == 'r' && !plus) {
-		flags |= _O_RDONLY;
-		access = GENERIC_READ;
-	} else {
-		/*
-		work around http://support.microsoft.com/kb/899149 and
-		set _O_RDWR for 'w' and 'a', even if mode has no '+'
-		*/
-		flags |= _O_RDWR;
-		access = GENERIC_READ | GENERIC_WRITE;
-		fpmode[fppos++] = '+';
-	}
-	fpmode[fppos++] = '\0';
-
-	switch (m0) {
-	case 'r':
-		creation = OPEN_EXISTING;
-		break;
-	case 'w':
-		creation = CREATE_ALWAYS;
-		break;
-	case 'a':
-		creation = OPEN_ALWAYS;
-		flags |= _O_APPEND;
-		break;
-	default:
-		PyErr_Format(PyExc_ValueError,
-			     "mode string must begin with one of 'r', 'w', "
-			     "or 'a', not '%c'", m0);
-		goto bail;
-	}
-
-	handle = CreateFile(name, access,
-			    FILE_SHARE_READ | FILE_SHARE_WRITE |
-			    FILE_SHARE_DELETE,
-			    NULL,
-			    creation,
-			    FILE_ATTRIBUTE_NORMAL,
-			    0);
-
-	if (handle == INVALID_HANDLE_VALUE) {
-		PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
-		goto bail;
-	}
-
-	fd = _open_osfhandle((intptr_t)handle, flags);
-
-	if (fd == -1) {
-		CloseHandle(handle);
-		PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
-		goto bail;
-	}
-#ifndef IS_PY3K
-	fp = _fdopen(fd, fpmode);
-	if (fp == NULL) {
-		_close(fd);
-		PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
-		goto bail;
-	}
-
-	file_obj = PyFile_FromFile(fp, name, mode, fclose);
-	if (file_obj == NULL) {
-		fclose(fp);
-		goto bail;
-	}
-
-	PyFile_SetBufSize(file_obj, bufsize);
-#else
-	file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
-	if (file_obj == NULL)
-		goto bail;
-#endif
-bail:
-	PyMem_Free(name);
-	return file_obj;
-}
-#endif
-
-#ifdef __APPLE__
-#include <ApplicationServices/ApplicationServices.h>
-
-static PyObject *isgui(PyObject *self)
-{
-	CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
-
-	if (dict != NULL) {
-		CFRelease(dict);
-		Py_RETURN_TRUE;
-	} else {
-		Py_RETURN_FALSE;
-	}
-}
-#endif
-
-static char osutil_doc[] = "Native operating system services.";
-
-static PyMethodDef methods[] = {
-	{"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
-	 "list a directory\n"},
-#ifdef _WIN32
-	{"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
-	 "Open a file with POSIX-like semantics.\n"
-"On error, this function may raise either a WindowsError or an IOError."},
-#else
-	{"statfiles", (PyCFunction)statfiles, METH_VARARGS | METH_KEYWORDS,
-	 "stat a series of files or symlinks\n"
-"Returns None for non-existent entries and entries of other types.\n"},
-#ifdef CMSG_LEN
-	{"recvfds", (PyCFunction)recvfds, METH_VARARGS,
-	 "receive list of file descriptors via socket\n"},
-#endif
-#ifndef SETPROCNAME_USE_NONE
-	{"setprocname", (PyCFunction)setprocname, METH_VARARGS,
-	 "set process title (best-effort)\n"},
-#endif
-#if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS)
-	{"getfstype", (PyCFunction)getfstype, METH_VARARGS,
-	 "get filesystem type (best-effort)\n"},
-#endif
-#endif /* ndef _WIN32 */
-#ifdef __APPLE__
-	{
-		"isgui", (PyCFunction)isgui, METH_NOARGS,
-		"Is a CoreGraphics session available?"
-	},
-#endif
-	{NULL, NULL}
-};
-
-static const int version = 1;
-
-#ifdef IS_PY3K
-static struct PyModuleDef osutil_module = {
-	PyModuleDef_HEAD_INIT,
-	"osutil",
-	osutil_doc,
-	-1,
-	methods
-};
-
-PyMODINIT_FUNC PyInit_osutil(void)
-{
-	PyObject *m;
-	if (PyType_Ready(&listdir_stat_type) < 0)
-		return NULL;
-
-	m = PyModule_Create(&osutil_module);
-	PyModule_AddIntConstant(m, "version", version);
-	return m;
-}
-#else
-PyMODINIT_FUNC initosutil(void)
-{
-	PyObject *m;
-	if (PyType_Ready(&listdir_stat_type) == -1)
-		return;
-
-	m = Py_InitModule3("osutil", methods, osutil_doc);
-	PyModule_AddIntConstant(m, "version", version);
-}
-#endif
--- a/mercurial/pure/osutil.py	Fri Aug 12 11:30:17 2016 +0900
+++ b/mercurial/pure/osutil.py	Fri Aug 12 11:35:17 2016 +0900
@@ -13,7 +13,7 @@
 import socket
 import stat as statmod
 
-from . import (
+from .. import (
     policy,
     pycompat,
 )
--- a/mercurial/util.py	Fri Aug 12 11:30:17 2016 +0900
+++ b/mercurial/util.py	Fri Aug 12 11:35:17 2016 +0900
@@ -46,11 +46,13 @@
     encoding,
     error,
     i18n,
-    osutil,
     parsers,
+    policy,
     pycompat,
 )
 
+osutil = policy.importmod(r'osutil')
+
 b85decode = base85.b85decode
 b85encode = base85.b85encode
 
--- a/mercurial/windows.py	Fri Aug 12 11:30:17 2016 +0900
+++ b/mercurial/windows.py	Fri Aug 12 11:35:17 2016 +0900
@@ -17,7 +17,7 @@
 from .i18n import _
 from . import (
     encoding,
-    osutil,
+    policy,
     pycompat,
     win32,
 )
@@ -28,6 +28,8 @@
 except ImportError:
     import winreg
 
+osutil = policy.importmod(r'osutil')
+
 executablepath = win32.executablepath
 getuser = win32.getuser
 hidewindow = win32.hidewindow
--- a/setup.py	Fri Aug 12 11:30:17 2016 +0900
+++ b/setup.py	Fri Aug 12 11:35:17 2016 +0900
@@ -641,7 +641,7 @@
                                     'mercurial/pathencode.c'],
               include_dirs=common_include_dirs,
               depends=common_depends),
-    Extension('mercurial.osutil', ['mercurial/osutil.c'],
+    Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
               include_dirs=common_include_dirs,
               extra_compile_args=osutil_cflags,
               extra_link_args=osutil_ldflags,