mirror of
https://github.com/python/cpython
synced 2024-10-14 11:53:54 +00:00
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
This commit is contained in:
parent
5f22e72e6d
commit
60853211da
|
@ -5,6 +5,7 @@
|
||||||
import os, sys
|
import os, sys
|
||||||
import unittest
|
import unittest
|
||||||
from test import support
|
from test import support
|
||||||
|
threading = support.import_module("threading")
|
||||||
from platform import machine
|
from platform import machine
|
||||||
|
|
||||||
# Do this first so test will be skipped if module doesn't exist
|
# Do this first so test will be skipped if module doesn't exist
|
||||||
|
@ -227,6 +228,58 @@ def test_context_manager(self):
|
||||||
except WindowsError:
|
except WindowsError:
|
||||||
self.assertEqual(h.handle, 0)
|
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, "")
|
||||||
|
|
||||||
# Reflection requires XP x64/Vista at a minimum. XP doesn't have this stuff
|
# Reflection requires XP x64/Vista at a minimum. XP doesn't have this stuff
|
||||||
# or DeleteKeyEx so make sure their use raises NotImplementedError
|
# or DeleteKeyEx so make sure their use raises NotImplementedError
|
||||||
@unittest.skipUnless(WIN_VER < (5, 2), "Requires Windows XP")
|
@unittest.skipUnless(WIN_VER < (5, 2), "Requires Windows XP")
|
||||||
|
|
|
@ -1220,6 +1220,9 @@ Library
|
||||||
Extension Modules
|
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 #8692: Optimize math.factorial: replace the previous naive
|
- Issue #8692: Optimize math.factorial: replace the previous naive
|
||||||
algorithm with an improved 'binary-split' algorithm that uses fewer
|
algorithm with an improved 'binary-split' algorithm that uses fewer
|
||||||
multiplications and allows many of the multiplications to be
|
multiplications and allows many of the multiplications to be
|
||||||
|
|
119
PC/winreg.c
119
PC/winreg.c
|
@ -1096,7 +1096,14 @@ PyEnumKey(PyObject *self, PyObject *args)
|
||||||
int index;
|
int index;
|
||||||
long rc;
|
long rc;
|
||||||
PyObject *retStr;
|
PyObject *retStr;
|
||||||
wchar_t 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. */
|
||||||
|
wchar_t tmpbuf[257];
|
||||||
DWORD len = sizeof(tmpbuf); /* includes NULL terminator */
|
DWORD len = sizeof(tmpbuf); /* includes NULL terminator */
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "Oi:EnumKey", &obKey, &index))
|
if (!PyArg_ParseTuple(args, "Oi:EnumKey", &obKey, &index))
|
||||||
|
@ -1123,8 +1130,8 @@ PyEnumValue(PyObject *self, PyObject *args)
|
||||||
long rc;
|
long rc;
|
||||||
wchar_t *retValueBuf;
|
wchar_t *retValueBuf;
|
||||||
BYTE *retDataBuf;
|
BYTE *retDataBuf;
|
||||||
DWORD retValueSize;
|
DWORD retValueSize, bufValueSize;
|
||||||
DWORD retDataSize;
|
DWORD retDataSize, bufDataSize;
|
||||||
DWORD typ;
|
DWORD typ;
|
||||||
PyObject *obData;
|
PyObject *obData;
|
||||||
PyObject *retVal;
|
PyObject *retVal;
|
||||||
|
@ -1142,6 +1149,8 @@ PyEnumValue(PyObject *self, PyObject *args)
|
||||||
"RegQueryInfoKey");
|
"RegQueryInfoKey");
|
||||||
++retValueSize; /* include null terminators */
|
++retValueSize; /* include null terminators */
|
||||||
++retDataSize;
|
++retDataSize;
|
||||||
|
bufDataSize = retDataSize;
|
||||||
|
bufValueSize = retValueSize;
|
||||||
retValueBuf = (wchar_t *)PyMem_Malloc(sizeof(wchar_t) * retValueSize);
|
retValueBuf = (wchar_t *)PyMem_Malloc(sizeof(wchar_t) * retValueSize);
|
||||||
if (retValueBuf == NULL)
|
if (retValueBuf == NULL)
|
||||||
return PyErr_NoMemory();
|
return PyErr_NoMemory();
|
||||||
|
@ -1151,16 +1160,33 @@ PyEnumValue(PyObject *self, PyObject *args)
|
||||||
return PyErr_NoMemory();
|
return PyErr_NoMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
while (1) {
|
||||||
rc = RegEnumValueW(hKey,
|
wchar_t *tmp;
|
||||||
index,
|
Py_BEGIN_ALLOW_THREADS
|
||||||
retValueBuf,
|
rc = RegEnumValueW(hKey,
|
||||||
&retValueSize,
|
index,
|
||||||
NULL,
|
retValueBuf,
|
||||||
&typ,
|
&retValueSize,
|
||||||
retDataBuf,
|
NULL,
|
||||||
&retDataSize);
|
&typ,
|
||||||
Py_END_ALLOW_THREADS
|
(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) {
|
if (rc != ERROR_SUCCESS) {
|
||||||
retVal = PyErr_SetFromWindowsErrWithFunction(rc,
|
retVal = PyErr_SetFromWindowsErrWithFunction(rc,
|
||||||
|
@ -1317,23 +1343,44 @@ PyQueryValue(PyObject *self, PyObject *args)
|
||||||
long rc;
|
long rc;
|
||||||
PyObject *retStr;
|
PyObject *retStr;
|
||||||
wchar_t *retBuf;
|
wchar_t *retBuf;
|
||||||
long bufSize = 0;
|
DWORD bufSize = 0;
|
||||||
|
DWORD retSize = 0;
|
||||||
|
wchar_t *tmp;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "OZ:QueryValue", &obKey, &subKey))
|
if (!PyArg_ParseTuple(args, "OZ:QueryValue", &obKey, &subKey))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE))
|
if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE))
|
||||||
return NULL;
|
return NULL;
|
||||||
if ((rc = RegQueryValueW(hKey, subKey, NULL, &bufSize))
|
|
||||||
!= ERROR_SUCCESS)
|
rc = RegQueryValueW(hKey, subKey, NULL, &retSize);
|
||||||
|
if (rc == ERROR_MORE_DATA)
|
||||||
|
retSize = 256;
|
||||||
|
else if (rc != ERROR_SUCCESS)
|
||||||
return PyErr_SetFromWindowsErrWithFunction(rc,
|
return PyErr_SetFromWindowsErrWithFunction(rc,
|
||||||
"RegQueryValue");
|
"RegQueryValue");
|
||||||
retBuf = (wchar_t *)PyMem_Malloc(bufSize);
|
|
||||||
|
bufSize = retSize;
|
||||||
|
retBuf = (wchar_t *) PyMem_Malloc(bufSize);
|
||||||
if (retBuf == NULL)
|
if (retBuf == NULL)
|
||||||
return PyErr_NoMemory();
|
return PyErr_NoMemory();
|
||||||
|
|
||||||
if ((rc = RegQueryValueW(hKey, subKey, retBuf, &bufSize))
|
while (1) {
|
||||||
!= ERROR_SUCCESS) {
|
retSize = bufSize;
|
||||||
|
rc = RegQueryValueW(hKey, subKey, retBuf, &retSize);
|
||||||
|
if (rc != ERROR_MORE_DATA)
|
||||||
|
break;
|
||||||
|
|
||||||
|
bufSize *= 2;
|
||||||
|
tmp = (wchar_t *) PyMem_Realloc(retBuf, bufSize);
|
||||||
|
if (tmp == NULL) {
|
||||||
|
PyMem_Free(retBuf);
|
||||||
|
return PyErr_NoMemory();
|
||||||
|
}
|
||||||
|
retBuf = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc != ERROR_SUCCESS) {
|
||||||
PyMem_Free(retBuf);
|
PyMem_Free(retBuf);
|
||||||
return PyErr_SetFromWindowsErrWithFunction(rc,
|
return PyErr_SetFromWindowsErrWithFunction(rc,
|
||||||
"RegQueryValue");
|
"RegQueryValue");
|
||||||
|
@ -1352,8 +1399,8 @@ PyQueryValueEx(PyObject *self, PyObject *args)
|
||||||
wchar_t *valueName;
|
wchar_t *valueName;
|
||||||
|
|
||||||
long rc;
|
long rc;
|
||||||
BYTE *retBuf;
|
BYTE *retBuf, *tmp;
|
||||||
DWORD bufSize = 0;
|
DWORD bufSize = 0, retSize;
|
||||||
DWORD typ;
|
DWORD typ;
|
||||||
PyObject *obData;
|
PyObject *obData;
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
|
@ -1363,18 +1410,34 @@ PyQueryValueEx(PyObject *self, PyObject *args)
|
||||||
|
|
||||||
if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE))
|
if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE))
|
||||||
return NULL;
|
return NULL;
|
||||||
if ((rc = RegQueryValueExW(hKey, valueName,
|
|
||||||
NULL, NULL, NULL,
|
rc = RegQueryValueExW(hKey, valueName, NULL, NULL, NULL, &bufSize);
|
||||||
&bufSize))
|
if (rc == ERROR_MORE_DATA)
|
||||||
!= ERROR_SUCCESS)
|
bufSize = 256;
|
||||||
|
else if (rc != ERROR_SUCCESS)
|
||||||
return PyErr_SetFromWindowsErrWithFunction(rc,
|
return PyErr_SetFromWindowsErrWithFunction(rc,
|
||||||
"RegQueryValueEx");
|
"RegQueryValueEx");
|
||||||
retBuf = (BYTE *)PyMem_Malloc(bufSize);
|
retBuf = (BYTE *)PyMem_Malloc(bufSize);
|
||||||
if (retBuf == NULL)
|
if (retBuf == NULL)
|
||||||
return PyErr_NoMemory();
|
return PyErr_NoMemory();
|
||||||
if ((rc = RegQueryValueExW(hKey, valueName, NULL,
|
|
||||||
&typ, retBuf, &bufSize))
|
while (1) {
|
||||||
!= ERROR_SUCCESS) {
|
retSize = bufSize;
|
||||||
|
rc = RegQueryValueExW(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);
|
PyMem_Free(retBuf);
|
||||||
return PyErr_SetFromWindowsErrWithFunction(rc,
|
return PyErr_SetFromWindowsErrWithFunction(rc,
|
||||||
"RegQueryValueEx");
|
"RegQueryValueEx");
|
||||||
|
|
Loading…
Reference in a new issue