summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorLarry Hastings <larry@hastings.org>2012-05-06 00:39:09 (GMT)
committerLarry Hastings <larry@hastings.org>2012-05-06 00:39:09 (GMT)
commitcfe6f2af3cdf485cb376cd313866612796becaae (patch)
tree7ffd0c956cd0b1d7982aebe69dda9a73c2346574 /Modules
parentfaf91e75ab287ae6c377b05b7eb91d7f5274fbc5 (diff)
downloadcpython-cfe6f2af3cdf485cb376cd313866612796becaae.zip
cpython-cfe6f2af3cdf485cb376cd313866612796becaae.tar.gz
cpython-cfe6f2af3cdf485cb376cd313866612796becaae.tar.bz2
Update Misc/NEWS for issues #14127 and #14705. (And, technically, #10148.)
Diffstat (limited to 'Modules')
-rw-r--r--Modules/posixmodule.c364
1 files changed, 333 insertions, 31 deletions
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index dc3302c..7b45154 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -398,6 +398,268 @@ static int win32_can_symlink = 0;
#endif
#endif
+/*
+ * De-vararg'd PyArg_ParseTupleAndKeywords()
+ */
+typedef struct argument_path_s {
+ struct argument_path_s *next;
+ PyObject *object;
+ char *format;
+ wchar_t **wide;
+ char **narrow;
+ Py_ssize_t *length;
+} argument_path_t;
+
+typedef struct {
+ PyObject *args;
+ PyObject *kwargs;
+ char format[32];
+ char *format_trace;
+ char *keywords[8];
+ int keyword_count;
+ PyObject **varargs[16];
+ int vararg_count;
+ const char *function_name;
+ argument_path_t *path_head;
+} argument_parser_table_t;
+
+#define APT_DECLARE(apt, fname) \
+ argument_parser_table_t apt; \
+ memset(&apt, 0, sizeof(apt)); \
+ apt.function_name = fname; \
+ apt.args = args; \
+ apt.kwargs = kwargs; \
+ apt.format_trace = apt.format
+
+#define _APT_ARGUMENT_OBJECT(apt, name, variable, format) \
+ *apt.format_trace++ = (format); \
+ apt.keywords[apt.keyword_count++] = (name); \
+ apt.varargs[apt.vararg_count++] = (PyObject **)(variable); \
+
+#define APT_ARGUMENT_OBJECT(apt, name, variable) \
+ _APT_ARGUMENT_OBJECT(apt, name, variable, 'O')
+
+#define APT_ARGUMENT_OBJECT_WITH_CONVERTER(apt, name, variable, converter) \
+ apt.varargs[apt.vararg_count++] = (PyObject **)converter; \
+ APT_ARGUMENT_OBJECT(apt, name, variable); \
+ *apt.format_trace++ = '&'; \
+
+#define APT_ARGUMENT_UNICODE(apt, name, variable) \
+ _APT_ARGUMENT_OBJECT(apt, name, variable, 'U')
+
+#define APT_ARGUMENT_BYTES(apt, name, variable) \
+ _APT_ARGUMENT_OBJECT(apt, name, variable, 'y')
+
+#define APT_ARGUMENT_INT(apt, name, variable) \
+ _APT_ARGUMENT_OBJECT(apt, name, variable, 'i')
+
+static int
+bool_converter(PyObject *object, void *address) {
+ int *output = (int *)address;
+ int value = PyObject_IsTrue(object);
+ if (value == -1)
+ return 0;
+ *output = value;
+ return 1;
+}
+
+#define APT_ARGUMENT_BOOL(apt, name, variable) \
+ _APT_ARGUMENT_OBJECT(apt, name, variable, 'p')
+
+#if !MS_WINDOWS
+ #define _APT_DEFAULT_PATH_TYPE 'U'
+ #define _APT_PATH_BEFORE ;
+ #define _APT_PATH_AFTER ;
+#else
+ #define _APT_DEFAULT_PATH_TYPE 'O'
+ #define _APT_PATH_BEFORE apt.varargs[apt.vararg_count++] = (PyObject **)PyUnicode_FSConverter;
+ #define _APT_PATH_AFTER *apt.format_trace++ = '&';
+#endif
+
+#define APT_ARGUMENT_PATH(apt, name, w, n, l) \
+ { \
+ argument_path_t *_path_ = (argument_path_t *)alloca(sizeof(argument_path_t)); \
+ _APT_PATH_BEFORE; \
+ memset(_path_, 0, sizeof(*_path_)); \
+ _path_->wide = w; \
+ _path_->narrow = n; \
+ _path_->length = l; \
+ _path_->next = apt.path_head; \
+ apt.path_head = _path_; \
+ _APT_ARGUMENT_OBJECT(apt, name, &_path_->object, _APT_DEFAULT_PATH_TYPE); \
+ _APT_PATH_AFTER; \
+ } \
+
+#define APT_OPTIONAL(apt) \
+ *apt.format_trace++ = '|' \
+
+#define APT_KEYWORD_ONLY(apt) \
+ *apt.format_trace++ = '$' \
+
+
+static int apt_parse(argument_parser_table_t *apt) {
+ argument_path_t *trace;
+
+ *apt->format_trace++ = ':';
+ strcpy(apt->format_trace, apt->function_name);
+ apt->keywords[apt->keyword_count] = NULL;
+
+ for (;;) {
+ int result;
+
+ switch (apt->vararg_count) {
+ case 0:
+ PyErr_SetString(PyExc_RuntimeError, "suspiciously too few arguments for apt_parse");
+ return 0;
+
+ #define APT_PREAMBLE \
+ result = PyArg_ParseTupleAndKeywords(apt->args, apt->kwargs, apt->format, apt->keywords, \
+
+ #define APT_POSTSCRIPT \
+ ); \
+ break; \
+
+ case 1:
+ APT_PREAMBLE
+ apt->varargs[0]
+ APT_POSTSCRIPT
+ case 2:
+ APT_PREAMBLE
+ apt->varargs[0],
+ apt->varargs[1]
+ APT_POSTSCRIPT
+ case 3:
+ APT_PREAMBLE
+ apt->varargs[0],
+ apt->varargs[1],
+ apt->varargs[2]
+ APT_POSTSCRIPT
+ case 4:
+ APT_PREAMBLE
+ apt->varargs[0],
+ apt->varargs[1],
+ apt->varargs[2],
+ apt->varargs[3]
+ APT_POSTSCRIPT
+ case 5:
+ APT_PREAMBLE
+ apt->varargs[0],
+ apt->varargs[1],
+ apt->varargs[2],
+ apt->varargs[3],
+ apt->varargs[4]
+ APT_POSTSCRIPT
+ case 6:
+ APT_PREAMBLE
+ apt->varargs[0],
+ apt->varargs[1],
+ apt->varargs[2],
+ apt->varargs[3],
+ apt->varargs[4],
+ apt->varargs[5]
+ APT_POSTSCRIPT
+ case 7:
+ APT_PREAMBLE
+ apt->varargs[0],
+ apt->varargs[1],
+ apt->varargs[2],
+ apt->varargs[3],
+ apt->varargs[4],
+ apt->varargs[5],
+ apt->varargs[6]
+ APT_POSTSCRIPT
+ case 8:
+ APT_PREAMBLE
+ apt->varargs[0],
+ apt->varargs[1],
+ apt->varargs[2],
+ apt->varargs[3],
+ apt->varargs[4],
+ apt->varargs[5],
+ apt->varargs[6],
+ apt->varargs[7]
+ APT_POSTSCRIPT
+ default:
+ PyErr_SetString(PyExc_RuntimeError, "too many arguments for apt_parse");
+ return 0;
+ }
+
+ if (result) {
+ for (trace = apt->path_head; trace != NULL; trace = trace->next) {
+#if MS_WINDOWS
+ switch (*trace->format) {
+ case 'U':
+ *trace->wide = PyUnicode_AsUnicode(trace->object);
+ if (trace->length)
+ *trace->length = PyUnicode_GET_SIZE(trace->object);
+ break;
+ case 'y'
+ if (win32_warn_bytes_api())
+ return 0;
+ *trace->narrow = trace->object;
+ if (trace->length)
+ *trace->length = strlen((char *)trace->object);
+ break;
+ default:
+ PyErr_SetString(PyExc_RuntimeError, "unexpected format character in apt_parse");
+ return 0;
+ }
+#else
+ assert(*trace->format == 'O');
+ *trace->narrow = PyBytes_AsString(trace->object);
+ if (trace->length)
+ *trace->length = PyBytes_GET_SIZE(trace->object);
+#endif
+ }
+ return result;
+ }
+
+#if !MS_WINDOWS
+ break;
+#else
+ for (trace = apt->path_head; trace != NULL; trace = trace->next) {
+ /*
+ * try flipping paths between wide and narrow.
+ *
+ * each element always started with wide.
+ * so:
+ * * if we see a wide, flip it to narrow and stop.
+ * * if we see a narrow, flip it to wide and move on to the next field.
+ * * if we run out of fields, we have exhausted all possibilities. fail.
+ *
+ * (conceptually it helps to think of the fields as a binary number
+ * with as many digits as there are entries in the path list.
+ * wide is 0, narrow is 1. we keep incrementing the number
+ * until we wrap around to 0 again.)
+ */
+ if (*trace->format == 'U') {
+ *trace->format = 'y';
+ break;
+ }
+ if (*trace->format == 'y') {
+ *trace->format = 'U';
+ continue;
+ }
+ PyErr_SetString(PyExc_RuntimeError, "unexpected format character in apt_parse");
+ return 0;
+ }
+ if (!trace)
+ break;
+#endif
+ }
+
+ return 0;
+}
+
+static void apt_cleanup(argument_parser_table_t *apt) {
+#if !MS_WINDOWS
+ argument_path_t *trace;
+ for (trace = apt->path_head; trace != NULL; trace = trace->next) {
+ Py_XDECREF(trace->object);
+ }
+#endif
+}
+
/* A helper used by a number of POSIX-only functions */
#ifndef MS_WINDOWS
static int
@@ -1942,6 +2204,12 @@ posix_do_stat(PyObject *self, PyObject *args,
/* POSIX methods */
+#ifdef AT_FDCWD
+#define DEFAULT_DIR_FD AT_FDCWD
+#else
+#define DEFAULT_DIR_FD (-100)
+#endif
+
PyDoc_STRVAR(posix_access__doc__,
"access(path, mode) -> True if granted, False otherwise\n\n\
Use the real uid/gid to test for access to a path. Note that most\n\
@@ -1951,34 +2219,54 @@ specified access to the path. The mode argument can be F_OK to test\n\
existence, or the inclusive-OR of R_OK, W_OK, and X_OK.");
static PyObject *
-posix_access(PyObject *self, PyObject *args)
+posix_access(PyObject *self, PyObject *args, PyObject *kwargs)
{
- const char *path;
int mode;
+ int dir_fd = DEFAULT_DIR_FD;
+ wchar_t *wide_path;
+ char *path;
+ int follow_symlinks = 1;
+ int effective_ids = 0;
#ifdef MS_WINDOWS
DWORD attr;
- PyObject *po;
- if (PyArg_ParseTuple(args, "Ui:access", &po, &mode)) {
- wchar_t* wpath = PyUnicode_AsUnicode(po);
- if (wpath == NULL)
- return NULL;
- Py_BEGIN_ALLOW_THREADS
- attr = GetFileAttributesW(wpath);
- Py_END_ALLOW_THREADS
- goto finish;
- }
- /* Drop the argument parsing error as narrow strings
- are also valid. */
- PyErr_Clear();
- if (!PyArg_ParseTuple(args, "yi:access", &path, &mode))
- return NULL;
- if (win32_warn_bytes_api())
+#else
+ int res;
+#endif
+
+ APT_DECLARE(apt, "access");
+ APT_ARGUMENT_PATH(apt, "path", &wide_path, &path, NULL);
+ APT_ARGUMENT_INT(apt, "mode", &mode);
+ APT_OPTIONAL(apt);
+ APT_KEYWORD_ONLY(apt);
+ APT_ARGUMENT_INT(apt, "dir_fd", &dir_fd);
+ APT_ARGUMENT_BOOL(apt, "follow_symlinks", &follow_symlinks);
+ APT_ARGUMENT_BOOL(apt, "effective_ids", &effective_ids);
+
+#define ACCESS_FAIL_IF_KEYWORD_USED \
+ if (dir_fd != DEFAULT_DIR_FD) \
+ PyErr_SetString(PyExc_NotImplementedError, "access: dir_fd unavailable on this platform"); \
+ if (!follow_symlinks) \
+ PyErr_SetString(PyExc_NotImplementedError, "access: follow_symlinks unavailable on this platform"); \
+ if (effective_ids) \
+ PyErr_SetString(PyExc_NotImplementedError, "access: effective_ids unavailable on this platform")
+
+ if (!apt_parse(&apt))
return NULL;
+ Py_RETURN_NONE;
+
+#ifdef MS_WINDOWS
+ ACCESS_FAIL_IF_KEYWORD_USED;
+
Py_BEGIN_ALLOW_THREADS
- attr = GetFileAttributesA(path);
+ if (wide != NULL)
+ attr = GetFileAttributesW(wide_path);
+ else
+ attr = GetFileAttributesA(path);
Py_END_ALLOW_THREADS
-finish:
+
+ apt_cleanup(&apt);
+
if (attr == 0xFFFFFFFF)
/* File does not exist, or cannot read attributes */
return PyBool_FromLong(0);
@@ -1989,16 +2277,28 @@ finish:
|| !(attr & FILE_ATTRIBUTE_READONLY)
|| (attr & FILE_ATTRIBUTE_DIRECTORY));
#else
- PyObject *opath;
- int res;
- if (!PyArg_ParseTuple(args, "O&i:access",
- PyUnicode_FSConverter, &opath, &mode))
- return NULL;
- path = PyBytes_AsString(opath);
- Py_BEGIN_ALLOW_THREADS
- res = access(path, mode);
- Py_END_ALLOW_THREADS
- Py_DECREF(opath);
+
+ if ((dir_fd != DEFAULT_DIR_FD)
+ || effective_ids || !follow_symlinks) {
+#ifdef HAVE_FACCESSAT
+ int flags = 0;
+ if (!follow_symlinks)
+ flags |= AT_SYMLINK_NOFOLLOW;
+ if (effective_ids)
+ flags |= AT_EACCESS;
+ Py_BEGIN_ALLOW_THREADS
+ res = faccessat(dir_fd, path, mode, flags);
+ Py_END_ALLOW_THREADS
+#else
+ ACCESS_FAIL_IF_KEYWORD_USED;
+#endif
+ } else {
+ Py_BEGIN_ALLOW_THREADS
+ res = access(path, mode);
+ Py_END_ALLOW_THREADS
+ }
+
+ apt_cleanup(&apt);
return PyBool_FromLong(res == 0);
#endif
}
@@ -10596,7 +10896,9 @@ get_terminal_size(PyObject *self, PyObject *args)
static PyMethodDef posix_methods[] = {
- {"access", posix_access, METH_VARARGS, posix_access__doc__},
+ {"access", (PyCFunction)posix_access,
+ METH_VARARGS | METH_KEYWORDS,
+ posix_access__doc__},
#ifdef HAVE_TTYNAME
{"ttyname", posix_ttyname, METH_VARARGS, posix_ttyname__doc__},
#endif