diff mercurial/cext/osutil.c @ 32367: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 mercurial/osutil.c@dc51700be00c
children b90e8da190da
line wrap: on
line diff
--- /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