##// END OF EJS Templates
namespaces: let namespaces override singlenode() definition...
namespaces: let namespaces override singlenode() definition Some namespaces have multiple nodes per name (meaning that their namemap() returns multiple nodes). One such namespace is the "topics" namespace (from the evolve repo). We also have our own internal namespace at Google (for review units) that has multiple nodes per name. These namespaces may not want to use the default "pick highest revnum" resolution that we currently use when resolving a name to a single node. As an example, they may decide that `hg co <name>` should check out a commit that's last in some sense even if an earlier commit had just been amended and thus had a higher revnum [1]. This patch gives the namespace the option to continue to return multiple nodes and to override how the best node is picked. Allowing namespaces to override that may also be useful as an optimization (it may be cheaper for the namespace to find just that node). I have been arguing (in D3715) for using all the nodes returned from namemap() when resolving the symbol to a revset, so e.g. `hg log -r stable` would resolve to *all* nodes on stable, not just the one with the highest revnum (except that I don't actually think we should change it for the branch namespace because of BC). Most people seem opposed to that. If we decide not to do it, I think we can deprecate the namemap() function in favor of the new singlenode() (I find it weird to have namespaces, like the branch namespace, where namemap() isn't nodemap()'s inverse). I therefore think this patch makes sense regardless of what we decide on that issue. [1] Actually, even the branch namespace would have wanted to override singlenode() if it had supported multiple nodes. That's because closes branch heads are mostly ignored, so "hg co default" will not check out the highest-revnum node if that's a closed head. Differential Revision: https://phab.mercurial-scm.org/D3852

File last commit:

r36798:f3c31402 default
r38505:4c068365 @58 default
Show More
osutil.c
1407 lines | 32.3 KiB | text/x-c | CLexer
Yuya Nishihara
osutil: switch to policy importer...
r32367 /*
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>
Gregory Szorc
cext: reorder #include...
r34439 #include <errno.h>
Yuya Nishihara
osutil: switch to policy importer...
r32367 #include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
Gregory Szorc
cext: reorder #include...
r34439 #include <io.h>
Yuya Nishihara
osutil: switch to policy importer...
r32367 #include <windows.h>
#else
#include <dirent.h>
Jun Wu
osutil: add a function to unblock signals...
r35476 #include <signal.h>
Yuya Nishihara
osutil: switch to policy importer...
r32367 #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);
}
Augie Fackler
osutil: implement minimal __getitem__ compatibility on our custom listdir type...
r36798 static PyObject *listdir_stat_getitem(PyObject *self, PyObject *key)
{
long index = PyLong_AsLong(key);
if (index == -1 && PyErr_Occurred()) {
return NULL;
}
if (index != 8) {
PyErr_Format(PyExc_IndexError, "osutil.stat objects only "
"support stat.ST_MTIME in "
"__getitem__");
return NULL;
}
return listdir_stat_st_mtime(self, NULL);
}
static PyMappingMethods listdir_stat_type_mapping_methods = {
(lenfunc)NULL, /* mp_length */
(binaryfunc)listdir_stat_getitem, /* mp_subscript */
(objobjargproc)NULL, /* mp_ass_subscript */
};
Yuya Nishihara
osutil: switch to policy importer...
r32367 static PyTypeObject listdir_stat_type = {
Augie Fackler
cext: add /* header */ comment to all PyVarObject_HEAD_INIT() calls...
r34862 PyVarObject_HEAD_INIT(NULL, 0) /* header */
Yuya Nishihara
osutil: switch to policy importer...
r32367 "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*/
Augie Fackler
osutil: implement minimal __getitem__ compatibility on our custom listdir type...
r36798 &listdir_stat_type_mapping_methods, /*tp_as_mapping*/
Yuya Nishihara
osutil: switch to policy importer...
r32367 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)
Yuya Nishihara
py3: bulk-replace bytes format specifier passed to Py_BuildValue()...
r36637 return Py_BuildValue(PY23("si", "yi"), fd->cFileName, kind);
Yuya Nishihara
osutil: switch to policy importer...
r32367
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;
Yuya Nishihara
py3: bulk-replace bytes format specifier passed to Py_BuildValue()...
r36637 return Py_BuildValue(PY23("siN", "yiN"), fd->cFileName,
Yuya Nishihara
osutil: switch to policy importer...
r32367 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;
Yuya Nishihara
py3: bulk-replace bytes format specifier passed to Py_BuildValue()...
r36637 elem = Py_BuildValue(PY23("siN", "yiN"), ent->d_name,
kind, stat);
Yuya Nishihara
osutil: switch to policy importer...
r32367 } else
Yuya Nishihara
py3: bulk-replace bytes format specifier passed to Py_BuildValue()...
r36637 elem = Py_BuildValue(PY23("si", "yi"), ent->d_name,
kind);
Yuya Nishihara
osutil: switch to policy importer...
r32367 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;
Yuya Nishihara
py3: bulk-replace bytes format specifier passed to Py_BuildValue()...
r36637 elem = Py_BuildValue(PY23("siN", "yiN"),
filename, kind, stat);
Yuya Nishihara
osutil: switch to policy importer...
r32367 } else
Yuya Nishihara
py3: bulk-replace bytes format specifier passed to Py_BuildValue()...
r36637 elem = Py_BuildValue(PY23("si", "yi"),
filename, kind);
Yuya Nishihara
osutil: switch to policy importer...
r32367 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;
Yuya Nishihara
py3: bulk-replace 'const char*' format specifier passed to PyArg_ParseTuple*()...
r36638 if (!PyArg_ParseTuple(args, PY23("s", "y"), &name))
Yuya Nishihara
osutil: switch to policy importer...
r32367 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;
Yuya Nishihara
py3: bulk-replace 'const char*' format specifier passed to PyArg_ParseTuple*()...
r36638 if (!PyArg_ParseTuple(args, PY23("s", "y"), &path))
Yuya Nishihara
osutil: switch to policy importer...
r32367 return NULL;
memset(&buf, 0, sizeof(buf));
r = statfs(path, &buf);
if (r != 0)
return PyErr_SetFromErrno(PyExc_OSError);
Yuya Nishihara
py3: bulk-replace bytes format specifier passed to Py_BuildValue()...
r36637 return Py_BuildValue(PY23("s", "y"), describefstype(&buf));
Yuya Nishihara
osutil: switch to policy importer...
r32367 }
#endif /* defined(HAVE_LINUX_STATFS) || defined(HAVE_BSD_STATFS) */
Matt Harbison
osutil: implement getfsmountpoint() on BSD systems...
r35533 #if defined(HAVE_BSD_STATFS)
/* given a directory path, return filesystem mount point (best-effort) */
static PyObject *getfsmountpoint(PyObject *self, PyObject *args)
{
const char *path = NULL;
struct statfs buf;
int r;
Yuya Nishihara
py3: bulk-replace 'const char*' format specifier passed to PyArg_ParseTuple*()...
r36638 if (!PyArg_ParseTuple(args, PY23("s", "y"), &path))
Matt Harbison
osutil: implement getfsmountpoint() on BSD systems...
r35533 return NULL;
memset(&buf, 0, sizeof(buf));
r = statfs(path, &buf);
if (r != 0)
return PyErr_SetFromErrno(PyExc_OSError);
Yuya Nishihara
py3: bulk-replace bytes format specifier passed to Py_BuildValue()...
r36637 return Py_BuildValue(PY23("s", "y"), buf.f_mntonname);
Matt Harbison
osutil: implement getfsmountpoint() on BSD systems...
r35533 }
#endif /* defined(HAVE_BSD_STATFS) */
Jun Wu
osutil: add a function to unblock signals...
r35476 static PyObject *unblocksignal(PyObject *self, PyObject *args)
{
int sig = 0;
int r;
if (!PyArg_ParseTuple(args, "i", &sig))
return NULL;
sigset_t set;
r = sigemptyset(&set);
if (r != 0)
return PyErr_SetFromErrno(PyExc_OSError);
r = sigaddset(&set, sig);
if (r != 0)
return PyErr_SetFromErrno(PyExc_OSError);
r = sigprocmask(SIG_UNBLOCK, &set, NULL);
if (r != 0)
return PyErr_SetFromErrno(PyExc_OSError);
Py_RETURN_NONE;
}
Yuya Nishihara
osutil: switch to policy importer...
r32367 #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};
Yuya Nishihara
py3: bulk-replace 'const char*' format specifier passed to PyArg_ParseTuple*()...
r36638 if (!PyArg_ParseTupleAndKeywords(args, kwargs, PY23("s#|OO:listdir",
"y#|OO:listdir"),
Yuya Nishihara
osutil: switch to policy importer...
r32367 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;
Yuya Nishihara
py3: bulk-replace 'const char*' format specifier passed to PyArg_ParseTuple*()...
r36638 if (!PyArg_ParseTupleAndKeywords(args, kwds, PY23("et|si:posixfile",
"et|yi:posixfile"),
kwlist,
Yuya Nishihara
osutil: switch to policy importer...
r32367 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
Matt Harbison
osutil: implement getfsmountpoint() on BSD systems...
r35533 #if defined(HAVE_BSD_STATFS)
{"getfsmountpoint", (PyCFunction)getfsmountpoint, METH_VARARGS,
"get filesystem mount point (best-effort)\n"},
#endif
Jun Wu
osutil: add a function to unblock signals...
r35476 {"unblocksignal", (PyCFunction)unblocksignal, METH_VARARGS,
"change signal mask to unblock a given signal\n"},
Yuya Nishihara
osutil: switch to policy importer...
r32367 #endif /* ndef _WIN32 */
#ifdef __APPLE__
{
"isgui", (PyCFunction)isgui, METH_NOARGS,
"Is a CoreGraphics session available?"
},
#endif
{NULL, NULL}
};
Augie Fackler
osutil: implement minimal __getitem__ compatibility on our custom listdir type...
r36798 static const int version = 4;
Yuya Nishihara
osutil: switch to policy importer...
r32367
#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