aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Curtin <brian.curtin@gmail.com>2010-05-25 15:06:15 +0000
committerBrian Curtin <brian.curtin@gmail.com>2010-05-25 15:06:15 +0000
commitb64c89bd7abf05baad517b50cef7bc7de7b4eb74 (patch)
treeb6d6545bede935d810aa83e26f67845cfba48f15
parentAdd three items (diff)
downloadcpython-b64c89bd7abf05baad517b50cef7bc7de7b4eb74.tar.gz
cpython-b64c89bd7abf05baad517b50cef7bc7de7b4eb74.tar.bz2
cpython-b64c89bd7abf05baad517b50cef7bc7de7b4eb74.zip
Fix #2810 - handle the case where some registry calls return
ERROR_MORE_DATA, requiring another call to get the remaining data. Patch by Daniel Stutzbach
-rw-r--r--Lib/test/test_winreg.py53
-rw-r--r--Misc/NEWS3
-rw-r--r--PC/_winreg.c136
3 files changed, 159 insertions, 33 deletions
diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py
index 753be746040..da3a9187fd4 100644
--- a/Lib/test/test_winreg.py
+++ b/Lib/test/test_winreg.py
@@ -4,6 +4,7 @@
import os, sys
import unittest
from test import test_support
+threading = test_support.import_module("threading")
from platform import machine
# Do this first so test will be skipped if module doesn't exist
@@ -231,6 +232,58 @@ class LocalWinregTests(BaseWinregTests):
except WindowsError:
self.assertEqual(h.handle, 0)
+ def test_changing_value(self):
+ # Issue2810: A race condition in 2.6 and 3.1 may cause
+ # EnumValue or QueryValue to throw "WindowsError: More data is
+ # available"
+ done = False
+
+ class VeryActiveThread(threading.Thread):
+ def run(self):
+ with CreateKey(HKEY_CURRENT_USER, test_key_name) as key:
+ use_short = True
+ long_string = 'x'*2000
+ while not done:
+ s = 'x' if use_short else long_string
+ use_short = not use_short
+ SetValue(key, 'changing_value', REG_SZ, s)
+
+ thread = VeryActiveThread()
+ thread.start()
+ try:
+ with CreateKey(HKEY_CURRENT_USER,
+ test_key_name+'\\changing_value') as key:
+ for _ in range(1000):
+ num_subkeys, num_values, t = QueryInfoKey(key)
+ for i in range(num_values):
+ name = EnumValue(key, i)
+ QueryValue(key, name[0])
+ finally:
+ done = True
+ thread.join()
+ DeleteKey(HKEY_CURRENT_USER, test_key_name+'\\changing_value')
+ DeleteKey(HKEY_CURRENT_USER, test_key_name)
+
+ def test_long_key(self):
+ # Issue2810, in 2.6 and 3.1 when the key name was exactly 256
+ # characters, EnumKey threw "WindowsError: More data is
+ # available"
+ name = 'x'*256
+ try:
+ with CreateKey(HKEY_CURRENT_USER, test_key_name) as key:
+ SetValue(key, name, REG_SZ, 'x')
+ num_subkeys, num_values, t = QueryInfoKey(key)
+ EnumKey(key, 0)
+ finally:
+ DeleteKey(HKEY_CURRENT_USER, '\\'.join((test_key_name, name)))
+ DeleteKey(HKEY_CURRENT_USER, test_key_name)
+
+ def test_dynamic_key(self):
+ # Issue2810, when the value is dynamically generated, these
+ # throw "WindowsError: More data is available" in 2.6 and 3.1
+ EnumValue(HKEY_PERFORMANCE_DATA, 0)
+ QueryValueEx(HKEY_PERFORMANCE_DATA, None)
+
# Reflection requires XP x64/Vista at a minimum. XP doesn't have this stuff
# or DeleteKeyEx so make sure their use raises NotImplementedError
@unittest.skipUnless(WIN_VER < (5, 2), "Requires Windows XP")
diff --git a/Misc/NEWS b/Misc/NEWS
index 7b74743359e..9d693786404 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -63,6 +63,9 @@ Library
Extension Modules
-----------------
+- Issue #2810: Fix cases where the Windows registry API returns
+ ERROR_MORE_DATA, requiring a re-try in order to get the complete result.
+
- Issue #8674: Fixed a number of incorrect or undefined-behaviour-inducing
overflow checks in the ``audioop`` module.
diff --git a/PC/_winreg.c b/PC/_winreg.c
index 68726032ff9..d3c39f91eac 100644
--- a/PC/_winreg.c
+++ b/PC/_winreg.c
@@ -1153,7 +1153,14 @@ PyEnumKey(PyObject *self, PyObject *args)
int index;
long rc;
PyObject *retStr;
- char tmpbuf[256]; /* max key name length is 255 */
+
+ /* The Windows docs claim that the max key name length is 255
+ * characters, plus a terminating nul character. However,
+ * empirical testing demonstrates that it is possible to
+ * create a 256 character key that is missing the terminating
+ * nul. RegEnumKeyEx requires a 257 character buffer to
+ * retrieve such a key name. */
+ char tmpbuf[257];
DWORD len = sizeof(tmpbuf); /* includes NULL terminator */
if (!PyArg_ParseTuple(args, "Oi:EnumKey", &obKey, &index))
@@ -1180,8 +1187,8 @@ PyEnumValue(PyObject *self, PyObject *args)
long rc;
char *retValueBuf;
char *retDataBuf;
- DWORD retValueSize;
- DWORD retDataSize;
+ DWORD retValueSize, bufValueSize;
+ DWORD retDataSize, bufDataSize;
DWORD typ;
PyObject *obData;
PyObject *retVal;
@@ -1199,6 +1206,8 @@ PyEnumValue(PyObject *self, PyObject *args)
"RegQueryInfoKey");
++retValueSize; /* include null terminators */
++retDataSize;
+ bufDataSize = retDataSize;
+ bufValueSize = retValueSize;
retValueBuf = (char *)PyMem_Malloc(retValueSize);
if (retValueBuf == NULL)
return PyErr_NoMemory();
@@ -1208,16 +1217,33 @@ PyEnumValue(PyObject *self, PyObject *args)
return PyErr_NoMemory();
}
- Py_BEGIN_ALLOW_THREADS
- rc = RegEnumValue(hKey,
- index,
- retValueBuf,
- &retValueSize,
- NULL,
- &typ,
- (BYTE *)retDataBuf,
- &retDataSize);
- Py_END_ALLOW_THREADS
+ while (1) {
+ char *tmp;
+ Py_BEGIN_ALLOW_THREADS
+ rc = RegEnumValue(hKey,
+ index,
+ retValueBuf,
+ &retValueSize,
+ NULL,
+ &typ,
+ (BYTE *)retDataBuf,
+ &retDataSize);
+ Py_END_ALLOW_THREADS
+
+ if (rc != ERROR_MORE_DATA)
+ break;
+
+ bufDataSize *= 2;
+ tmp = (char *)PyMem_Realloc(retDataBuf, bufDataSize);
+ if (tmp == NULL) {
+ PyErr_NoMemory();
+ retVal = NULL;
+ goto fail;
+ }
+ retDataBuf = tmp;
+ retDataSize = bufDataSize;
+ retValueSize = bufValueSize;
+ }
if (rc != ERROR_SUCCESS) {
retVal = PyErr_SetFromWindowsErrWithFunction(rc,
@@ -1374,28 +1400,56 @@ PyQueryValue(PyObject *self, PyObject *args)
long rc;
PyObject *retStr;
char *retBuf;
- long bufSize = 0;
+ DWORD bufSize = 0;
+ DWORD retSize = 0;
+ char *tmp;
if (!PyArg_ParseTuple(args, "Oz:QueryValue", &obKey, &subKey))
return NULL;
if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE))
return NULL;
- if ((rc = RegQueryValue(hKey, subKey, NULL, &bufSize))
- != ERROR_SUCCESS)
+
+ rc = RegQueryValue(hKey, subKey, NULL, &retSize);
+ if (rc == ERROR_MORE_DATA)
+ retSize = 256;
+ else if (rc != ERROR_SUCCESS)
return PyErr_SetFromWindowsErrWithFunction(rc,
"RegQueryValue");
- retStr = PyString_FromStringAndSize(NULL, bufSize);
- if (retStr == NULL)
- return NULL;
- retBuf = PyString_AS_STRING(retStr);
- if ((rc = RegQueryValue(hKey, subKey, retBuf, &bufSize))
- != ERROR_SUCCESS) {
- Py_DECREF(retStr);
+
+ bufSize = retSize;
+ retBuf = (char *) PyMem_Malloc(bufSize);
+ if (retBuf == NULL)
+ return PyErr_NoMemory();
+
+ while (1) {
+ retSize = bufSize;
+ rc = RegQueryValue(hKey, subKey, retBuf, &retSize);
+ if (rc != ERROR_MORE_DATA)
+ break;
+
+ bufSize *= 2;
+ tmp = (char *) PyMem_Realloc(retBuf, bufSize);
+ if (tmp == NULL) {
+ PyMem_Free(retBuf);
+ return PyErr_NoMemory();
+ }
+ retBuf = tmp;
+ }
+
+ if (rc != ERROR_SUCCESS) {
+ PyMem_Free(retBuf);
return PyErr_SetFromWindowsErrWithFunction(rc,
"RegQueryValue");
}
- _PyString_Resize(&retStr, strlen(retBuf));
+
+ if (retBuf[retSize-1] == '\x00')
+ retSize--;
+ retStr = PyString_FromStringAndSize(retBuf, retSize);
+ if (retStr == NULL) {
+ PyMem_Free(retBuf);
+ return NULL;
+ }
return retStr;
}
@@ -1407,8 +1461,8 @@ PyQueryValueEx(PyObject *self, PyObject *args)
char *valueName;
long rc;
- char *retBuf;
- DWORD bufSize = 0;
+ char *retBuf, *tmp;
+ DWORD bufSize = 0, retSize;
DWORD typ;
PyObject *obData;
PyObject *result;
@@ -1418,18 +1472,34 @@ PyQueryValueEx(PyObject *self, PyObject *args)
if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE))
return NULL;
- if ((rc = RegQueryValueEx(hKey, valueName,
- NULL, NULL, NULL,
- &bufSize))
- != ERROR_SUCCESS)
+
+ rc = RegQueryValueEx(hKey, valueName, NULL, NULL, NULL, &bufSize);
+ if (rc == ERROR_MORE_DATA)
+ bufSize = 256;
+ else if (rc != ERROR_SUCCESS)
return PyErr_SetFromWindowsErrWithFunction(rc,
"RegQueryValueEx");
retBuf = (char *)PyMem_Malloc(bufSize);
if (retBuf == NULL)
return PyErr_NoMemory();
- if ((rc = RegQueryValueEx(hKey, valueName, NULL,
- &typ, (BYTE *)retBuf, &bufSize))
- != ERROR_SUCCESS) {
+
+ while (1) {
+ retSize = bufSize;
+ rc = RegQueryValueEx(hKey, valueName, NULL, &typ,
+ (BYTE *)retBuf, &retSize);
+ if (rc != ERROR_MORE_DATA)
+ break;
+
+ bufSize *= 2;
+ tmp = (char *) PyMem_Realloc(retBuf, bufSize);
+ if (tmp == NULL) {
+ PyMem_Free(retBuf);
+ return PyErr_NoMemory();
+ }
+ retBuf = tmp;
+ }
+
+ if (rc != ERROR_SUCCESS) {
PyMem_Free(retBuf);
return PyErr_SetFromWindowsErrWithFunction(rc,
"RegQueryValueEx");