osutil.c
1422 lines
| 32.8 KiB
| text/x-c
|
CLexer
Yuya Nishihara
|
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 | ||||
Gregory Szorc
|
r42236 | #define PY_SSIZE_T_CLEAN | ||
Yuya Nishihara
|
r32367 | #include <Python.h> | ||
Gregory Szorc
|
r34439 | #include <errno.h> | ||
Yuya Nishihara
|
r32367 | #include <fcntl.h> | ||
#include <stdio.h> | ||||
#include <stdlib.h> | ||||
#include <string.h> | ||||
#ifdef _WIN32 | ||||
Gregory Szorc
|
r34439 | #include <io.h> | ||
Yuya Nishihara
|
r32367 | #include <windows.h> | ||
#else | ||||
#include <dirent.h> | ||||
Jun Wu
|
r35476 | #include <signal.h> | ||
Yuya Nishihara
|
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
|
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
|
r32367 | static PyTypeObject listdir_stat_type = { | ||
Augie Fackler
|
r34862 | PyVarObject_HEAD_INIT(NULL, 0) /* header */ | ||
Yuya Nishihara
|
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
|
r36798 | &listdir_stat_type_mapping_methods, /*tp_as_mapping*/ | ||
Yuya Nishihara
|
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
|
r36637 | return Py_BuildValue(PY23("si", "yi"), fd->cFileName, kind); | ||
Yuya Nishihara
|
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
|
r36637 | return Py_BuildValue(PY23("siN", "yiN"), fd->cFileName, | ||
Yuya Nishihara
|
r32367 | kind, py_st); | ||
} | ||||
Gregory Szorc
|
r42236 | static PyObject *_listdir(char *path, Py_ssize_t plen, int wantstat, char *skip) | ||
Yuya Nishihara
|
r32367 | { | ||
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
|
r36637 | elem = Py_BuildValue(PY23("siN", "yiN"), ent->d_name, | ||
kind, stat); | ||||
Yuya Nishihara
|
r32367 | } else | ||
Yuya Nishihara
|
r36637 | elem = Py_BuildValue(PY23("si", "yi"), ent->d_name, | ||
kind); | ||||
Yuya Nishihara
|
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
|
r36637 | elem = Py_BuildValue(PY23("siN", "yiN"), | ||
filename, kind, stat); | ||||
Yuya Nishihara
|
r32367 | } else | ||
Yuya Nishihara
|
r36637 | elem = Py_BuildValue(PY23("si", "yi"), | ||
filename, kind); | ||||
Yuya Nishihara
|
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 */ | ||||
Kyle Lippincott
|
r43231 | /* allow disabling setprocname via compiler flags */ | ||
#ifndef SETPROCNAME_USE_NONE | ||||
Yuya Nishihara
|
r32367 | #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 | ||||
Kyle Lippincott
|
r43231 | #endif /* ndef SETPROCNAME_USE_NONE */ | ||
Yuya Nishihara
|
r32367 | |||
#ifndef SETPROCNAME_USE_NONE | ||||
static PyObject *setprocname(PyObject *self, PyObject *args) | ||||
{ | ||||
const char *name = NULL; | ||||
Yuya Nishihara
|
r36638 | if (!PyArg_ParseTuple(args, PY23("s", "y"), &name)) | ||
Yuya Nishihara
|
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); | ||||
Kyle Lippincott
|
r43232 | /* Py_GetArgcArgv may not do much if a custom python | ||
* launcher is used that doesn't record the information | ||||
* it needs. Let's handle this gracefully instead of | ||||
* segfaulting. */ | ||||
if (argv != NULL) | ||||
argvend = argvstart = argv[0]; | ||||
else | ||||
argvend = argvstart = NULL; | ||||
Yuya Nishihara
|
r32367 | |||
/* Check the memory we can use. Typically, argv[i] and | ||||
* argv[i + 1] are continuous. */ | ||||
for (i = 0; i < argc; ++i) { | ||||
Yuya Nishihara
|
r45077 | size_t len; | ||
Yuya Nishihara
|
r32367 | if (argv[i] > argvend || argv[i] < argvstart) | ||
break; /* not continuous */ | ||||
Yuya Nishihara
|
r45077 | len = strlen(argv[i]); | ||
Yuya Nishihara
|
r32367 | 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
|
r36638 | if (!PyArg_ParseTuple(args, PY23("s", "y"), &path)) | ||
Yuya Nishihara
|
r32367 | return NULL; | ||
memset(&buf, 0, sizeof(buf)); | ||||
r = statfs(path, &buf); | ||||
if (r != 0) | ||||
return PyErr_SetFromErrno(PyExc_OSError); | ||||
Yuya Nishihara
|
r36637 | return Py_BuildValue(PY23("s", "y"), describefstype(&buf)); | ||
Yuya Nishihara
|
r32367 | } | ||
#endif /* defined(HAVE_LINUX_STATFS) || defined(HAVE_BSD_STATFS) */ | ||||
Matt Harbison
|
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
|
r36638 | if (!PyArg_ParseTuple(args, PY23("s", "y"), &path)) | ||
Matt Harbison
|
r35533 | return NULL; | ||
memset(&buf, 0, sizeof(buf)); | ||||
r = statfs(path, &buf); | ||||
if (r != 0) | ||||
return PyErr_SetFromErrno(PyExc_OSError); | ||||
Yuya Nishihara
|
r36637 | return Py_BuildValue(PY23("s", "y"), buf.f_mntonname); | ||
Matt Harbison
|
r35533 | } | ||
#endif /* defined(HAVE_BSD_STATFS) */ | ||||
Jun Wu
|
r35476 | static PyObject *unblocksignal(PyObject *self, PyObject *args) | ||
{ | ||||
int sig = 0; | ||||
Yuya Nishihara
|
r45077 | sigset_t set; | ||
Jun Wu
|
r35476 | int r; | ||
if (!PyArg_ParseTuple(args, "i", &sig)) | ||||
return NULL; | ||||
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
|
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; | ||||
Gregory Szorc
|
r42236 | Py_ssize_t plen; | ||
int wantstat; | ||||
Yuya Nishihara
|
r32367 | |||
static char *kwlist[] = {"path", "stat", "skip", NULL}; | ||||
Yuya Nishihara
|
r36638 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, PY23("s#|OO:listdir", | ||
"y#|OO:listdir"), | ||||
Yuya Nishihara
|
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; | ||||
Matt Harbison
|
r39646 | #ifndef IS_PY3K | ||
Yuya Nishihara
|
r32367 | FILE *fp; | ||
Matt Harbison
|
r39646 | #endif | ||
Yuya Nishihara
|
r32367 | |||
Yuya Nishihara
|
r36638 | if (!PyArg_ParseTupleAndKeywords(args, kwds, PY23("et|si:posixfile", | ||
"et|yi:posixfile"), | ||||
kwlist, | ||||
Yuya Nishihara
|
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
|
r35533 | #if defined(HAVE_BSD_STATFS) | ||
{"getfsmountpoint", (PyCFunction)getfsmountpoint, METH_VARARGS, | ||||
"get filesystem mount point (best-effort)\n"}, | ||||
#endif | ||||
Jun Wu
|
r35476 | {"unblocksignal", (PyCFunction)unblocksignal, METH_VARARGS, | ||
"change signal mask to unblock a given signal\n"}, | ||||
Yuya Nishihara
|
r32367 | #endif /* ndef _WIN32 */ | ||
#ifdef __APPLE__ | ||||
{ | ||||
"isgui", (PyCFunction)isgui, METH_NOARGS, | ||||
"Is a CoreGraphics session available?" | ||||
}, | ||||
#endif | ||||
{NULL, NULL} | ||||
}; | ||||
Augie Fackler
|
r36798 | static const int version = 4; | ||
Yuya Nishihara
|
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 | ||||