mirror of
git://source.winehq.org/git/wine.git
synced 2024-11-01 10:25:39 +00:00
6d89be5496
Signed-off-by: Austin English <austinenglish@gmail.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
1551 lines
52 KiB
C
1551 lines
52 KiB
C
/*
|
|
* Registry functions
|
|
*
|
|
* Copyright (C) 1999 Juergen Schmied
|
|
* Copyright (C) 2000 Alexandre Julliard
|
|
* Copyright 2005 Ivan Leo Puoti, Laurent Pinchart
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*
|
|
* NOTES:
|
|
* HKEY_LOCAL_MACHINE \\REGISTRY\\MACHINE
|
|
* HKEY_USERS \\REGISTRY\\USER
|
|
* HKEY_CURRENT_CONFIG \\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\HARDWARE PROFILES\\CURRENT
|
|
* HKEY_CLASSES \\REGISTRY\\MACHINE\\SOFTWARE\\CLASSES
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "wine/port.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "wine/library.h"
|
|
#include "ntdll_misc.h"
|
|
#include "wine/debug.h"
|
|
#include "wine/unicode.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(reg);
|
|
|
|
/* maximum length of a value name in bytes (without terminating null) */
|
|
#define MAX_VALUE_LENGTH (16383 * sizeof(WCHAR))
|
|
|
|
/******************************************************************************
|
|
* NtCreateKey [NTDLL.@]
|
|
* ZwCreateKey [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtCreateKey( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
|
|
ULONG TitleIndex, const UNICODE_STRING *class, ULONG options,
|
|
PULONG dispos )
|
|
{
|
|
NTSTATUS ret;
|
|
data_size_t len;
|
|
struct object_attributes *objattr;
|
|
|
|
if (!retkey || !attr) return STATUS_ACCESS_VIOLATION;
|
|
if (attr->Length > sizeof(OBJECT_ATTRIBUTES)) return STATUS_INVALID_PARAMETER;
|
|
|
|
TRACE( "(%p,%s,%s,%x,%x,%p)\n", attr->RootDirectory, debugstr_us(attr->ObjectName),
|
|
debugstr_us(class), options, access, retkey );
|
|
|
|
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
|
|
|
|
SERVER_START_REQ( create_key )
|
|
{
|
|
req->access = access;
|
|
req->options = options;
|
|
wine_server_add_data( req, objattr, len );
|
|
if (class) wine_server_add_data( req, class->Buffer, class->Length );
|
|
ret = wine_server_call( req );
|
|
*retkey = wine_server_ptr_handle( reply->hkey );
|
|
if (dispos && !ret) *dispos = reply->created ? REG_CREATED_NEW_KEY : REG_OPENED_EXISTING_KEY;
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
TRACE("<- %p\n", *retkey);
|
|
RtlFreeHeap( GetProcessHeap(), 0, objattr );
|
|
return ret;
|
|
}
|
|
|
|
NTSTATUS WINAPI NtCreateKeyTransacted( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
|
|
ULONG TitleIndex, const UNICODE_STRING *class, ULONG options,
|
|
HANDLE transacted, ULONG *dispos )
|
|
{
|
|
FIXME( "(%p,%s,%s,%x,%x,%p,%p)\n", attr->RootDirectory, debugstr_us(attr->ObjectName),
|
|
debugstr_us(class), options, access, transacted, retkey );
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS WINAPI NtRenameKey( HANDLE handle, UNICODE_STRING *name )
|
|
{
|
|
FIXME( "(%p %s)\n", handle, debugstr_us(name) );
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* RtlpNtCreateKey [NTDLL.@]
|
|
*
|
|
* See NtCreateKey.
|
|
*/
|
|
NTSTATUS WINAPI RtlpNtCreateKey( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
|
|
ULONG TitleIndex, const UNICODE_STRING *class, ULONG options,
|
|
PULONG dispos )
|
|
{
|
|
OBJECT_ATTRIBUTES oa;
|
|
|
|
if (attr)
|
|
{
|
|
oa = *attr;
|
|
oa.Attributes &= ~(OBJ_PERMANENT|OBJ_EXCLUSIVE);
|
|
attr = &oa;
|
|
}
|
|
|
|
return NtCreateKey(retkey, access, attr, 0, NULL, 0, dispos);
|
|
}
|
|
|
|
static NTSTATUS open_key( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, ULONG options )
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
if (!retkey || !attr || !attr->ObjectName) return STATUS_ACCESS_VIOLATION;
|
|
if ((ret = validate_open_object_attributes( attr ))) return ret;
|
|
|
|
TRACE( "(%p,%s,%x,%p)\n", attr->RootDirectory,
|
|
debugstr_us(attr->ObjectName), access, retkey );
|
|
if (options & ~REG_OPTION_OPEN_LINK)
|
|
FIXME("options %x not implemented\n", options);
|
|
|
|
SERVER_START_REQ( open_key )
|
|
{
|
|
req->parent = wine_server_obj_handle( attr->RootDirectory );
|
|
req->access = access;
|
|
req->attributes = attr->Attributes;
|
|
wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
|
|
ret = wine_server_call( req );
|
|
*retkey = wine_server_ptr_handle( reply->hkey );
|
|
}
|
|
SERVER_END_REQ;
|
|
TRACE("<- %p\n", *retkey);
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtOpenKeyEx [NTDLL.@]
|
|
* ZwOpenKeyEx [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtOpenKeyEx( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, ULONG options )
|
|
{
|
|
return open_key( retkey, access, attr, options );
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtOpenKey [NTDLL.@]
|
|
* ZwOpenKey [NTDLL.@]
|
|
*
|
|
* OUT HANDLE retkey (returns 0 when failure)
|
|
* IN ACCESS_MASK access
|
|
* IN POBJECT_ATTRIBUTES attr
|
|
*/
|
|
NTSTATUS WINAPI NtOpenKey( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
|
|
{
|
|
return open_key( retkey, access, attr, 0 );
|
|
}
|
|
|
|
NTSTATUS WINAPI NtOpenKeyTransactedEx( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
|
|
ULONG options, HANDLE transaction )
|
|
{
|
|
FIXME( "(%p %x %p %x %p)\n", retkey, access, attr, options, transaction );
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS WINAPI NtOpenKeyTransacted( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
|
|
HANDLE transaction )
|
|
{
|
|
return NtOpenKeyTransactedEx( retkey, access, attr, 0, transaction );
|
|
}
|
|
|
|
/******************************************************************************
|
|
* RtlpNtOpenKey [NTDLL.@]
|
|
*
|
|
* See NtOpenKey.
|
|
*/
|
|
NTSTATUS WINAPI RtlpNtOpenKey( PHANDLE retkey, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr )
|
|
{
|
|
if (attr)
|
|
attr->Attributes &= ~(OBJ_PERMANENT|OBJ_EXCLUSIVE);
|
|
return NtOpenKey(retkey, access, attr);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtDeleteKey [NTDLL.@]
|
|
* ZwDeleteKey [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtDeleteKey( HANDLE hkey )
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
TRACE( "(%p)\n", hkey );
|
|
|
|
SERVER_START_REQ( delete_key )
|
|
{
|
|
req->hkey = wine_server_obj_handle( hkey );
|
|
ret = wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* RtlpNtMakeTemporaryKey [NTDLL.@]
|
|
*
|
|
* See NtDeleteKey.
|
|
*/
|
|
NTSTATUS WINAPI RtlpNtMakeTemporaryKey( HANDLE hkey )
|
|
{
|
|
return NtDeleteKey(hkey);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtDeleteValueKey [NTDLL.@]
|
|
* ZwDeleteValueKey [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtDeleteValueKey( HANDLE hkey, const UNICODE_STRING *name )
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
TRACE( "(%p,%s)\n", hkey, debugstr_us(name) );
|
|
if (name->Length > MAX_VALUE_LENGTH) return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
SERVER_START_REQ( delete_key_value )
|
|
{
|
|
req->hkey = wine_server_obj_handle( hkey );
|
|
wine_server_add_data( req, name->Buffer, name->Length );
|
|
ret = wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* enumerate_key
|
|
*
|
|
* Implementation of NtQueryKey and NtEnumerateKey
|
|
*/
|
|
static NTSTATUS enumerate_key( HANDLE handle, int index, KEY_INFORMATION_CLASS info_class,
|
|
void *info, DWORD length, DWORD *result_len )
|
|
|
|
{
|
|
NTSTATUS ret;
|
|
void *data_ptr;
|
|
size_t fixed_size;
|
|
|
|
switch(info_class)
|
|
{
|
|
case KeyBasicInformation: data_ptr = ((KEY_BASIC_INFORMATION *)info)->Name; break;
|
|
case KeyFullInformation: data_ptr = ((KEY_FULL_INFORMATION *)info)->Class; break;
|
|
case KeyNodeInformation: data_ptr = ((KEY_NODE_INFORMATION *)info)->Name; break;
|
|
case KeyNameInformation: data_ptr = ((KEY_NAME_INFORMATION *)info)->Name; break;
|
|
case KeyCachedInformation: data_ptr = ((KEY_CACHED_INFORMATION *)info)+1; break;
|
|
default:
|
|
FIXME( "Information class %d not implemented\n", info_class );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
fixed_size = (char *)data_ptr - (char *)info;
|
|
|
|
SERVER_START_REQ( enum_key )
|
|
{
|
|
req->hkey = wine_server_obj_handle( handle );
|
|
req->index = index;
|
|
req->info_class = info_class;
|
|
if (length > fixed_size) wine_server_set_reply( req, data_ptr, length - fixed_size );
|
|
if (!(ret = wine_server_call( req )))
|
|
{
|
|
switch(info_class)
|
|
{
|
|
case KeyBasicInformation:
|
|
{
|
|
KEY_BASIC_INFORMATION keyinfo;
|
|
fixed_size = (char *)keyinfo.Name - (char *)&keyinfo;
|
|
keyinfo.LastWriteTime.QuadPart = reply->modif;
|
|
keyinfo.TitleIndex = 0;
|
|
keyinfo.NameLength = reply->namelen;
|
|
memcpy( info, &keyinfo, min( length, fixed_size ) );
|
|
}
|
|
break;
|
|
case KeyFullInformation:
|
|
{
|
|
KEY_FULL_INFORMATION keyinfo;
|
|
fixed_size = (char *)keyinfo.Class - (char *)&keyinfo;
|
|
keyinfo.LastWriteTime.QuadPart = reply->modif;
|
|
keyinfo.TitleIndex = 0;
|
|
keyinfo.ClassLength = wine_server_reply_size(reply);
|
|
keyinfo.ClassOffset = keyinfo.ClassLength ? fixed_size : -1;
|
|
keyinfo.SubKeys = reply->subkeys;
|
|
keyinfo.MaxNameLen = reply->max_subkey;
|
|
keyinfo.MaxClassLen = reply->max_class;
|
|
keyinfo.Values = reply->values;
|
|
keyinfo.MaxValueNameLen = reply->max_value;
|
|
keyinfo.MaxValueDataLen = reply->max_data;
|
|
memcpy( info, &keyinfo, min( length, fixed_size ) );
|
|
}
|
|
break;
|
|
case KeyNodeInformation:
|
|
{
|
|
KEY_NODE_INFORMATION keyinfo;
|
|
fixed_size = (char *)keyinfo.Name - (char *)&keyinfo;
|
|
keyinfo.LastWriteTime.QuadPart = reply->modif;
|
|
keyinfo.TitleIndex = 0;
|
|
if (reply->namelen < wine_server_reply_size(reply))
|
|
{
|
|
keyinfo.ClassLength = wine_server_reply_size(reply) - reply->namelen;
|
|
keyinfo.ClassOffset = fixed_size + reply->namelen;
|
|
}
|
|
else
|
|
{
|
|
keyinfo.ClassLength = 0;
|
|
keyinfo.ClassOffset = -1;
|
|
}
|
|
keyinfo.NameLength = reply->namelen;
|
|
memcpy( info, &keyinfo, min( length, fixed_size ) );
|
|
}
|
|
break;
|
|
case KeyNameInformation:
|
|
{
|
|
KEY_NAME_INFORMATION keyinfo;
|
|
fixed_size = (char *)keyinfo.Name - (char *)&keyinfo;
|
|
keyinfo.NameLength = reply->namelen;
|
|
memcpy( info, &keyinfo, min( length, fixed_size ) );
|
|
}
|
|
break;
|
|
case KeyCachedInformation:
|
|
{
|
|
KEY_CACHED_INFORMATION keyinfo;
|
|
fixed_size = sizeof(keyinfo);
|
|
keyinfo.LastWriteTime.QuadPart = reply->modif;
|
|
keyinfo.TitleIndex = 0;
|
|
keyinfo.SubKeys = reply->subkeys;
|
|
keyinfo.MaxNameLen = reply->max_subkey;
|
|
keyinfo.Values = reply->values;
|
|
keyinfo.MaxValueNameLen = reply->max_value;
|
|
keyinfo.MaxValueDataLen = reply->max_data;
|
|
keyinfo.NameLength = reply->namelen;
|
|
memcpy( info, &keyinfo, min( length, fixed_size ) );
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
*result_len = fixed_size + reply->total;
|
|
if (length < *result_len) ret = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
* NtEnumerateKey [NTDLL.@]
|
|
* ZwEnumerateKey [NTDLL.@]
|
|
*
|
|
* NOTES
|
|
* the name copied into the buffer is NOT 0-terminated
|
|
*/
|
|
NTSTATUS WINAPI NtEnumerateKey( HANDLE handle, ULONG index, KEY_INFORMATION_CLASS info_class,
|
|
void *info, DWORD length, DWORD *result_len )
|
|
{
|
|
/* -1 means query key, so avoid it here */
|
|
if (index == (ULONG)-1) return STATUS_NO_MORE_ENTRIES;
|
|
return enumerate_key( handle, index, info_class, info, length, result_len );
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* RtlpNtEnumerateSubKey [NTDLL.@]
|
|
*
|
|
*/
|
|
NTSTATUS WINAPI RtlpNtEnumerateSubKey( HANDLE handle, UNICODE_STRING *out, ULONG index )
|
|
{
|
|
KEY_BASIC_INFORMATION *info;
|
|
DWORD dwLen, dwResultLen;
|
|
NTSTATUS ret;
|
|
|
|
if (out->Length)
|
|
{
|
|
dwLen = out->Length + sizeof(KEY_BASIC_INFORMATION);
|
|
info = RtlAllocateHeap( GetProcessHeap(), 0, dwLen );
|
|
if (!info)
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
dwLen = 0;
|
|
info = NULL;
|
|
}
|
|
|
|
ret = NtEnumerateKey( handle, index, KeyBasicInformation, info, dwLen, &dwResultLen );
|
|
dwResultLen -= sizeof(KEY_BASIC_INFORMATION);
|
|
|
|
if (ret == STATUS_BUFFER_OVERFLOW)
|
|
out->Length = dwResultLen;
|
|
else if (!ret)
|
|
{
|
|
if (out->Length < info->NameLength)
|
|
{
|
|
out->Length = dwResultLen;
|
|
ret = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
else
|
|
{
|
|
out->Length = info->NameLength;
|
|
memcpy(out->Buffer, info->Name, info->NameLength);
|
|
}
|
|
}
|
|
|
|
RtlFreeHeap( GetProcessHeap(), 0, info );
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtQueryKey [NTDLL.@]
|
|
* ZwQueryKey [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtQueryKey( HANDLE handle, KEY_INFORMATION_CLASS info_class,
|
|
void *info, DWORD length, DWORD *result_len )
|
|
{
|
|
return enumerate_key( handle, -1, info_class, info, length, result_len );
|
|
}
|
|
|
|
|
|
/* fill the key value info structure for a specific info class */
|
|
static void copy_key_value_info( KEY_VALUE_INFORMATION_CLASS info_class, void *info,
|
|
DWORD length, int type, int name_len, int data_len )
|
|
{
|
|
switch(info_class)
|
|
{
|
|
case KeyValueBasicInformation:
|
|
{
|
|
KEY_VALUE_BASIC_INFORMATION keyinfo;
|
|
keyinfo.TitleIndex = 0;
|
|
keyinfo.Type = type;
|
|
keyinfo.NameLength = name_len;
|
|
length = min( length, (char *)keyinfo.Name - (char *)&keyinfo );
|
|
memcpy( info, &keyinfo, length );
|
|
break;
|
|
}
|
|
case KeyValueFullInformation:
|
|
{
|
|
KEY_VALUE_FULL_INFORMATION keyinfo;
|
|
keyinfo.TitleIndex = 0;
|
|
keyinfo.Type = type;
|
|
keyinfo.DataOffset = (char *)keyinfo.Name - (char *)&keyinfo + name_len;
|
|
keyinfo.DataLength = data_len;
|
|
keyinfo.NameLength = name_len;
|
|
length = min( length, (char *)keyinfo.Name - (char *)&keyinfo );
|
|
memcpy( info, &keyinfo, length );
|
|
break;
|
|
}
|
|
case KeyValuePartialInformation:
|
|
{
|
|
KEY_VALUE_PARTIAL_INFORMATION keyinfo;
|
|
keyinfo.TitleIndex = 0;
|
|
keyinfo.Type = type;
|
|
keyinfo.DataLength = data_len;
|
|
length = min( length, (char *)keyinfo.Data - (char *)&keyinfo );
|
|
memcpy( info, &keyinfo, length );
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* NtEnumerateValueKey [NTDLL.@]
|
|
* ZwEnumerateValueKey [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtEnumerateValueKey( HANDLE handle, ULONG index,
|
|
KEY_VALUE_INFORMATION_CLASS info_class,
|
|
void *info, DWORD length, DWORD *result_len )
|
|
{
|
|
NTSTATUS ret;
|
|
void *ptr;
|
|
size_t fixed_size;
|
|
|
|
TRACE( "(%p,%u,%d,%p,%d)\n", handle, index, info_class, info, length );
|
|
|
|
/* compute the length we want to retrieve */
|
|
switch(info_class)
|
|
{
|
|
case KeyValueBasicInformation: ptr = ((KEY_VALUE_BASIC_INFORMATION *)info)->Name; break;
|
|
case KeyValueFullInformation: ptr = ((KEY_VALUE_FULL_INFORMATION *)info)->Name; break;
|
|
case KeyValuePartialInformation: ptr = ((KEY_VALUE_PARTIAL_INFORMATION *)info)->Data; break;
|
|
default:
|
|
FIXME( "Information class %d not implemented\n", info_class );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
fixed_size = (char *)ptr - (char *)info;
|
|
|
|
SERVER_START_REQ( enum_key_value )
|
|
{
|
|
req->hkey = wine_server_obj_handle( handle );
|
|
req->index = index;
|
|
req->info_class = info_class;
|
|
if (length > fixed_size) wine_server_set_reply( req, ptr, length - fixed_size );
|
|
if (!(ret = wine_server_call( req )))
|
|
{
|
|
copy_key_value_info( info_class, info, length, reply->type, reply->namelen,
|
|
wine_server_reply_size(reply) - reply->namelen );
|
|
*result_len = fixed_size + reply->total;
|
|
if (length < *result_len) ret = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* NtQueryValueKey [NTDLL.@]
|
|
* ZwQueryValueKey [NTDLL.@]
|
|
*
|
|
* NOTES
|
|
* the name in the KeyValueInformation is never set
|
|
*/
|
|
NTSTATUS WINAPI NtQueryValueKey( HANDLE handle, const UNICODE_STRING *name,
|
|
KEY_VALUE_INFORMATION_CLASS info_class,
|
|
void *info, DWORD length, DWORD *result_len )
|
|
{
|
|
NTSTATUS ret;
|
|
UCHAR *data_ptr;
|
|
unsigned int fixed_size, min_size;
|
|
|
|
TRACE( "(%p,%s,%d,%p,%d)\n", handle, debugstr_us(name), info_class, info, length );
|
|
|
|
if (name->Length > MAX_VALUE_LENGTH) return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
/* compute the length we want to retrieve */
|
|
switch(info_class)
|
|
{
|
|
case KeyValueBasicInformation:
|
|
{
|
|
KEY_VALUE_BASIC_INFORMATION *basic_info = info;
|
|
min_size = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name);
|
|
fixed_size = min_size + name->Length;
|
|
if (min_size < length)
|
|
memcpy(basic_info->Name, name->Buffer, min(length - min_size, name->Length));
|
|
data_ptr = NULL;
|
|
break;
|
|
}
|
|
case KeyValueFullInformation:
|
|
{
|
|
KEY_VALUE_FULL_INFORMATION *full_info = info;
|
|
min_size = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name);
|
|
fixed_size = min_size + name->Length;
|
|
if (min_size < length)
|
|
memcpy(full_info->Name, name->Buffer, min(length - min_size, name->Length));
|
|
data_ptr = (UCHAR *)full_info->Name + name->Length;
|
|
break;
|
|
}
|
|
case KeyValuePartialInformation:
|
|
min_size = fixed_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
|
|
data_ptr = ((KEY_VALUE_PARTIAL_INFORMATION *)info)->Data;
|
|
break;
|
|
default:
|
|
FIXME( "Information class %d not implemented\n", info_class );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
SERVER_START_REQ( get_key_value )
|
|
{
|
|
req->hkey = wine_server_obj_handle( handle );
|
|
wine_server_add_data( req, name->Buffer, name->Length );
|
|
if (length > fixed_size && data_ptr) wine_server_set_reply( req, data_ptr, length - fixed_size );
|
|
if (!(ret = wine_server_call( req )))
|
|
{
|
|
copy_key_value_info( info_class, info, length, reply->type,
|
|
name->Length, reply->total );
|
|
*result_len = fixed_size + (info_class == KeyValueBasicInformation ? 0 : reply->total);
|
|
if (length < min_size) ret = STATUS_BUFFER_TOO_SMALL;
|
|
else if (length < *result_len) ret = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* RtlpNtQueryValueKey [NTDLL.@]
|
|
*
|
|
*/
|
|
NTSTATUS WINAPI RtlpNtQueryValueKey( HANDLE handle, ULONG *result_type, PBYTE dest,
|
|
DWORD *result_len, void *unknown )
|
|
{
|
|
KEY_VALUE_PARTIAL_INFORMATION *info;
|
|
UNICODE_STRING name;
|
|
NTSTATUS ret;
|
|
DWORD dwResultLen;
|
|
DWORD dwLen = sizeof (KEY_VALUE_PARTIAL_INFORMATION) + (result_len ? *result_len : 0);
|
|
|
|
info = RtlAllocateHeap( GetProcessHeap(), 0, dwLen );
|
|
if (!info)
|
|
return STATUS_NO_MEMORY;
|
|
|
|
name.Length = 0;
|
|
ret = NtQueryValueKey( handle, &name, KeyValuePartialInformation, info, dwLen, &dwResultLen );
|
|
|
|
if (!ret || ret == STATUS_BUFFER_OVERFLOW)
|
|
{
|
|
if (result_len)
|
|
*result_len = info->DataLength;
|
|
|
|
if (result_type)
|
|
*result_type = info->Type;
|
|
|
|
if (ret != STATUS_BUFFER_OVERFLOW)
|
|
memcpy( dest, info->Data, info->DataLength );
|
|
}
|
|
|
|
RtlFreeHeap( GetProcessHeap(), 0, info );
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtFlushKey [NTDLL.@]
|
|
* ZwFlushKey [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtFlushKey(HANDLE key)
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
TRACE("key=%p\n", key);
|
|
|
|
SERVER_START_REQ( flush_key )
|
|
{
|
|
req->hkey = wine_server_obj_handle( key );
|
|
ret = wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtLoadKey [NTDLL.@]
|
|
* ZwLoadKey [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtLoadKey( const OBJECT_ATTRIBUTES *attr, OBJECT_ATTRIBUTES *file )
|
|
{
|
|
NTSTATUS ret;
|
|
HANDLE hive;
|
|
IO_STATUS_BLOCK io;
|
|
data_size_t len;
|
|
struct object_attributes *objattr;
|
|
|
|
TRACE("(%p,%p)\n", attr, file);
|
|
|
|
ret = NtCreateFile(&hive, GENERIC_READ | SYNCHRONIZE, file, &io, NULL, FILE_ATTRIBUTE_NORMAL, 0,
|
|
FILE_OPEN, 0, NULL, 0);
|
|
if (ret) return ret;
|
|
|
|
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
|
|
|
|
SERVER_START_REQ( load_registry )
|
|
{
|
|
req->file = wine_server_obj_handle( hive );
|
|
wine_server_add_data( req, objattr, len );
|
|
ret = wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
NtClose(hive);
|
|
RtlFreeHeap( GetProcessHeap(), 0, objattr );
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtLoadKey2 [NTDLL.@]
|
|
* ZwLoadKey2 [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtLoadKey2(OBJECT_ATTRIBUTES *attr, OBJECT_ATTRIBUTES *file, ULONG flags)
|
|
{
|
|
FIXME("(%p,%p,0x%08x) semi-stub: ignoring flags\n", attr, file, flags);
|
|
return NtLoadKey(attr, file);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtNotifyChangeMultipleKeys [NTDLL.@]
|
|
* ZwNotifyChangeMultipleKeys [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtNotifyChangeMultipleKeys(
|
|
HANDLE KeyHandle,
|
|
ULONG Count,
|
|
OBJECT_ATTRIBUTES *SubordinateObjects,
|
|
HANDLE Event,
|
|
PIO_APC_ROUTINE ApcRoutine,
|
|
PVOID ApcContext,
|
|
PIO_STATUS_BLOCK IoStatusBlock,
|
|
ULONG CompletionFilter,
|
|
BOOLEAN WatchSubtree,
|
|
PVOID ChangeBuffer,
|
|
ULONG Length,
|
|
BOOLEAN Asynchronous)
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
TRACE("(%p,%u,%p,%p,%p,%p,%p,0x%08x, 0x%08x,%p,0x%08x,0x%08x)\n",
|
|
KeyHandle, Count, SubordinateObjects, Event, ApcRoutine, ApcContext, IoStatusBlock, CompletionFilter,
|
|
Asynchronous, ChangeBuffer, Length, WatchSubtree);
|
|
|
|
if (Count || SubordinateObjects || ApcRoutine || ApcContext || ChangeBuffer || Length)
|
|
FIXME("Unimplemented optional parameter\n");
|
|
|
|
if (!Asynchronous)
|
|
{
|
|
OBJECT_ATTRIBUTES attr;
|
|
InitializeObjectAttributes( &attr, NULL, 0, NULL, NULL );
|
|
ret = NtCreateEvent( &Event, EVENT_ALL_ACCESS, &attr, SynchronizationEvent, FALSE );
|
|
if (ret != STATUS_SUCCESS)
|
|
return ret;
|
|
}
|
|
|
|
SERVER_START_REQ( set_registry_notification )
|
|
{
|
|
req->hkey = wine_server_obj_handle( KeyHandle );
|
|
req->event = wine_server_obj_handle( Event );
|
|
req->subtree = WatchSubtree;
|
|
req->filter = CompletionFilter;
|
|
ret = wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
if (!Asynchronous)
|
|
{
|
|
if (ret == STATUS_PENDING)
|
|
ret = NtWaitForSingleObject( Event, FALSE, NULL );
|
|
NtClose( Event );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtNotifyChangeKey [NTDLL.@]
|
|
* ZwNotifyChangeKey [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtNotifyChangeKey(
|
|
IN HANDLE KeyHandle,
|
|
IN HANDLE Event,
|
|
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
|
IN PVOID ApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN ULONG CompletionFilter,
|
|
IN BOOLEAN WatchSubtree,
|
|
OUT PVOID ChangeBuffer,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Asynchronous)
|
|
{
|
|
return NtNotifyChangeMultipleKeys(KeyHandle, 0, NULL, Event, ApcRoutine, ApcContext,
|
|
IoStatusBlock, CompletionFilter, WatchSubtree,
|
|
ChangeBuffer, Length, Asynchronous);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtQueryMultipleValueKey [NTDLL]
|
|
* ZwQueryMultipleValueKey
|
|
*/
|
|
|
|
NTSTATUS WINAPI NtQueryMultipleValueKey(
|
|
HANDLE KeyHandle,
|
|
PKEY_MULTIPLE_VALUE_INFORMATION ListOfValuesToQuery,
|
|
ULONG NumberOfItems,
|
|
PVOID MultipleValueInformation,
|
|
ULONG Length,
|
|
PULONG ReturnLength)
|
|
{
|
|
FIXME("(%p,%p,0x%08x,%p,0x%08x,%p) stub!\n",
|
|
KeyHandle, ListOfValuesToQuery, NumberOfItems, MultipleValueInformation,
|
|
Length,ReturnLength);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtReplaceKey [NTDLL.@]
|
|
* ZwReplaceKey [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtReplaceKey(
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN HANDLE Key,
|
|
IN POBJECT_ATTRIBUTES ReplacedObjectAttributes)
|
|
{
|
|
FIXME("(%s,%p,%s),stub!\n", debugstr_ObjectAttributes(ObjectAttributes), Key,
|
|
debugstr_ObjectAttributes(ReplacedObjectAttributes) );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
/******************************************************************************
|
|
* NtRestoreKey [NTDLL.@]
|
|
* ZwRestoreKey [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtRestoreKey(
|
|
HANDLE KeyHandle,
|
|
HANDLE FileHandle,
|
|
ULONG RestoreFlags)
|
|
{
|
|
FIXME("(%p,%p,0x%08x) stub\n",
|
|
KeyHandle, FileHandle, RestoreFlags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
/******************************************************************************
|
|
* NtSaveKey [NTDLL.@]
|
|
* ZwSaveKey [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtSaveKey(IN HANDLE KeyHandle, IN HANDLE FileHandle)
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
TRACE("(%p,%p)\n", KeyHandle, FileHandle);
|
|
|
|
SERVER_START_REQ( save_registry )
|
|
{
|
|
req->hkey = wine_server_obj_handle( KeyHandle );
|
|
req->file = wine_server_obj_handle( FileHandle );
|
|
ret = wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
return ret;
|
|
}
|
|
/******************************************************************************
|
|
* NtSetInformationKey [NTDLL.@]
|
|
* ZwSetInformationKey [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtSetInformationKey(
|
|
IN HANDLE KeyHandle,
|
|
IN const int KeyInformationClass,
|
|
IN PVOID KeyInformation,
|
|
IN ULONG KeyInformationLength)
|
|
{
|
|
FIXME("(%p,0x%08x,%p,0x%08x) stub\n",
|
|
KeyHandle, KeyInformationClass, KeyInformation, KeyInformationLength);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* NtSetValueKey [NTDLL.@]
|
|
* ZwSetValueKey [NTDLL.@]
|
|
*
|
|
* NOTES
|
|
* win95 does not care about count for REG_SZ and finds out the len by itself (js)
|
|
* NT does definitely care (aj)
|
|
*/
|
|
NTSTATUS WINAPI NtSetValueKey( HANDLE hkey, const UNICODE_STRING *name, ULONG TitleIndex,
|
|
ULONG type, const void *data, ULONG count )
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
TRACE( "(%p,%s,%d,%p,%d)\n", hkey, debugstr_us(name), type, data, count );
|
|
|
|
if (name->Length > MAX_VALUE_LENGTH) return STATUS_INVALID_PARAMETER;
|
|
|
|
SERVER_START_REQ( set_key_value )
|
|
{
|
|
req->hkey = wine_server_obj_handle( hkey );
|
|
req->type = type;
|
|
req->namelen = name->Length;
|
|
wine_server_add_data( req, name->Buffer, name->Length );
|
|
wine_server_add_data( req, data, count );
|
|
ret = wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* RtlpNtSetValueKey [NTDLL.@]
|
|
*
|
|
*/
|
|
NTSTATUS WINAPI RtlpNtSetValueKey( HANDLE hkey, ULONG type, const void *data,
|
|
ULONG count )
|
|
{
|
|
UNICODE_STRING name;
|
|
|
|
name.Length = 0;
|
|
return NtSetValueKey( hkey, &name, 0, type, data, count );
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtUnloadKey [NTDLL.@]
|
|
* ZwUnloadKey [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtUnloadKey(IN POBJECT_ATTRIBUTES attr)
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
TRACE("(%p)\n", attr);
|
|
|
|
SERVER_START_REQ( unload_registry )
|
|
{
|
|
req->hkey = wine_server_obj_handle( attr->RootDirectory );
|
|
ret = wine_server_call(req);
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* RtlFormatCurrentUserKeyPath [NTDLL.@]
|
|
*
|
|
*/
|
|
NTSTATUS WINAPI RtlFormatCurrentUserKeyPath( IN OUT PUNICODE_STRING KeyPath)
|
|
{
|
|
static const WCHAR pathW[] = {'\\','R','e','g','i','s','t','r','y','\\','U','s','e','r','\\'};
|
|
char buffer[sizeof(TOKEN_USER) + sizeof(SID) + sizeof(DWORD)*SID_MAX_SUB_AUTHORITIES];
|
|
DWORD len = sizeof(buffer);
|
|
NTSTATUS status;
|
|
|
|
status = NtQueryInformationToken(GetCurrentThreadEffectiveToken(), TokenUser, buffer, len, &len);
|
|
if (status == STATUS_SUCCESS)
|
|
{
|
|
KeyPath->MaximumLength = 0;
|
|
status = RtlConvertSidToUnicodeString(KeyPath, ((TOKEN_USER *)buffer)->User.Sid, FALSE);
|
|
if (status == STATUS_BUFFER_OVERFLOW)
|
|
{
|
|
PWCHAR buf = RtlAllocateHeap(GetProcessHeap(), 0,
|
|
sizeof(pathW) + KeyPath->Length + sizeof(WCHAR));
|
|
if (buf)
|
|
{
|
|
memcpy(buf, pathW, sizeof(pathW));
|
|
KeyPath->MaximumLength = KeyPath->Length + sizeof(WCHAR);
|
|
KeyPath->Buffer = (PWCHAR)((LPBYTE)buf + sizeof(pathW));
|
|
status = RtlConvertSidToUnicodeString(KeyPath,
|
|
((TOKEN_USER *)buffer)->User.Sid, FALSE);
|
|
KeyPath->Buffer = buf;
|
|
KeyPath->Length += sizeof(pathW);
|
|
KeyPath->MaximumLength += sizeof(pathW);
|
|
}
|
|
else
|
|
status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* RtlOpenCurrentUser [NTDLL.@]
|
|
*
|
|
* NOTES
|
|
* If we return just HKEY_CURRENT_USER the advapi tries to find a remote
|
|
* registry (odd handle) and fails.
|
|
*/
|
|
NTSTATUS WINAPI RtlOpenCurrentUser(
|
|
IN ACCESS_MASK DesiredAccess, /* [in] */
|
|
OUT PHANDLE KeyHandle) /* [out] handle of HKEY_CURRENT_USER */
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING ObjectName;
|
|
NTSTATUS ret;
|
|
|
|
TRACE("(0x%08x, %p)\n",DesiredAccess, KeyHandle);
|
|
|
|
if ((ret = RtlFormatCurrentUserKeyPath(&ObjectName))) return ret;
|
|
InitializeObjectAttributes(&ObjectAttributes,&ObjectName,OBJ_CASE_INSENSITIVE,0, NULL);
|
|
ret = NtCreateKey(KeyHandle, DesiredAccess, &ObjectAttributes, 0, NULL, 0, NULL);
|
|
RtlFreeUnicodeString(&ObjectName);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static NTSTATUS RTL_ReportRegistryValue(PKEY_VALUE_FULL_INFORMATION pInfo,
|
|
PRTL_QUERY_REGISTRY_TABLE pQuery, PVOID pContext, PVOID pEnvironment)
|
|
{
|
|
PUNICODE_STRING str;
|
|
UNICODE_STRING src, dst;
|
|
LONG *bin;
|
|
ULONG offset;
|
|
PWSTR wstr;
|
|
DWORD res;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG len;
|
|
LPWSTR String;
|
|
ULONG count = 0;
|
|
|
|
if (pInfo == NULL)
|
|
{
|
|
if (pQuery->Flags & RTL_QUERY_REGISTRY_DIRECT)
|
|
return STATUS_INVALID_PARAMETER;
|
|
else
|
|
{
|
|
status = pQuery->QueryRoutine(pQuery->Name, pQuery->DefaultType, pQuery->DefaultData,
|
|
pQuery->DefaultLength, pContext, pQuery->EntryContext);
|
|
}
|
|
return status;
|
|
}
|
|
len = pInfo->DataLength;
|
|
|
|
if (pQuery->Flags & RTL_QUERY_REGISTRY_DIRECT)
|
|
{
|
|
str = pQuery->EntryContext;
|
|
|
|
switch(pInfo->Type)
|
|
{
|
|
case REG_EXPAND_SZ:
|
|
if (!(pQuery->Flags & RTL_QUERY_REGISTRY_NOEXPAND))
|
|
{
|
|
RtlInitUnicodeString(&src, (WCHAR*)(((CHAR*)pInfo) + pInfo->DataOffset));
|
|
res = 0;
|
|
dst.MaximumLength = 0;
|
|
RtlExpandEnvironmentStrings_U(pEnvironment, &src, &dst, &res);
|
|
dst.Length = 0;
|
|
dst.MaximumLength = res;
|
|
dst.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, res * sizeof(WCHAR));
|
|
RtlExpandEnvironmentStrings_U(pEnvironment, &src, &dst, &res);
|
|
status = pQuery->QueryRoutine(pQuery->Name, pInfo->Type, dst.Buffer,
|
|
dst.Length, pContext, pQuery->EntryContext);
|
|
RtlFreeHeap(GetProcessHeap(), 0, dst.Buffer);
|
|
}
|
|
|
|
case REG_SZ:
|
|
case REG_LINK:
|
|
if (str->Buffer == NULL)
|
|
RtlCreateUnicodeString(str, (WCHAR*)(((CHAR*)pInfo) + pInfo->DataOffset));
|
|
else
|
|
RtlAppendUnicodeToString(str, (WCHAR*)(((CHAR*)pInfo) + pInfo->DataOffset));
|
|
break;
|
|
|
|
case REG_MULTI_SZ:
|
|
if (!(pQuery->Flags & RTL_QUERY_REGISTRY_NOEXPAND))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (str->Buffer == NULL)
|
|
{
|
|
str->Buffer = RtlAllocateHeap(GetProcessHeap(), 0, len);
|
|
str->MaximumLength = len;
|
|
}
|
|
len = min(len, str->MaximumLength);
|
|
memcpy(str->Buffer, ((CHAR*)pInfo) + pInfo->DataOffset, len);
|
|
str->Length = len;
|
|
break;
|
|
|
|
default:
|
|
bin = pQuery->EntryContext;
|
|
if (pInfo->DataLength <= sizeof(ULONG))
|
|
memcpy(bin, ((CHAR*)pInfo) + pInfo->DataOffset,
|
|
pInfo->DataLength);
|
|
else
|
|
{
|
|
if (bin[0] <= sizeof(ULONG))
|
|
{
|
|
memcpy(&bin[1], ((CHAR*)pInfo) + pInfo->DataOffset,
|
|
min(-bin[0], pInfo->DataLength));
|
|
}
|
|
else
|
|
{
|
|
len = min(bin[0], pInfo->DataLength);
|
|
bin[1] = len;
|
|
bin[2] = pInfo->Type;
|
|
memcpy(&bin[3], ((CHAR*)pInfo) + pInfo->DataOffset, len);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if((pQuery->Flags & RTL_QUERY_REGISTRY_NOEXPAND) ||
|
|
(pInfo->Type != REG_EXPAND_SZ && pInfo->Type != REG_MULTI_SZ))
|
|
{
|
|
status = pQuery->QueryRoutine(pQuery->Name, pInfo->Type,
|
|
((CHAR*)pInfo) + pInfo->DataOffset, pInfo->DataLength,
|
|
pContext, pQuery->EntryContext);
|
|
}
|
|
else if (pInfo->Type == REG_EXPAND_SZ)
|
|
{
|
|
RtlInitUnicodeString(&src, (WCHAR*)(((CHAR*)pInfo) + pInfo->DataOffset));
|
|
res = 0;
|
|
dst.MaximumLength = 0;
|
|
RtlExpandEnvironmentStrings_U(pEnvironment, &src, &dst, &res);
|
|
dst.Length = 0;
|
|
dst.MaximumLength = res;
|
|
dst.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, res * sizeof(WCHAR));
|
|
RtlExpandEnvironmentStrings_U(pEnvironment, &src, &dst, &res);
|
|
status = pQuery->QueryRoutine(pQuery->Name, pInfo->Type, dst.Buffer,
|
|
dst.Length, pContext, pQuery->EntryContext);
|
|
RtlFreeHeap(GetProcessHeap(), 0, dst.Buffer);
|
|
}
|
|
else /* REG_MULTI_SZ */
|
|
{
|
|
if(pQuery->Flags & RTL_QUERY_REGISTRY_NOEXPAND)
|
|
{
|
|
for (offset = 0; offset <= pInfo->DataLength; offset += len + sizeof(WCHAR))
|
|
{
|
|
wstr = (WCHAR*)(((CHAR*)pInfo) + offset);
|
|
len = strlenW(wstr) * sizeof(WCHAR);
|
|
status = pQuery->QueryRoutine(pQuery->Name, pInfo->Type, wstr, len,
|
|
pContext, pQuery->EntryContext);
|
|
if(status != STATUS_SUCCESS && status != STATUS_BUFFER_TOO_SMALL)
|
|
return status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while(count<=pInfo->DataLength)
|
|
{
|
|
String = (WCHAR*)(((CHAR*)pInfo) + pInfo->DataOffset)+count;
|
|
count+=strlenW(String)+1;
|
|
RtlInitUnicodeString(&src, (WCHAR*)(((CHAR*)pInfo) + pInfo->DataOffset));
|
|
res = 0;
|
|
dst.MaximumLength = 0;
|
|
RtlExpandEnvironmentStrings_U(pEnvironment, &src, &dst, &res);
|
|
dst.Length = 0;
|
|
dst.MaximumLength = res;
|
|
dst.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, res * sizeof(WCHAR));
|
|
RtlExpandEnvironmentStrings_U(pEnvironment, &src, &dst, &res);
|
|
status = pQuery->QueryRoutine(pQuery->Name, pInfo->Type, dst.Buffer,
|
|
dst.Length, pContext, pQuery->EntryContext);
|
|
RtlFreeHeap(GetProcessHeap(), 0, dst.Buffer);
|
|
if(status != STATUS_SUCCESS && status != STATUS_BUFFER_TOO_SMALL)
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
static NTSTATUS RTL_GetKeyHandle(ULONG RelativeTo, PCWSTR Path, PHANDLE handle)
|
|
{
|
|
UNICODE_STRING KeyString;
|
|
OBJECT_ATTRIBUTES regkey;
|
|
PCWSTR base;
|
|
INT len;
|
|
NTSTATUS status;
|
|
|
|
static const WCHAR empty[] = {0};
|
|
static const WCHAR control[] = {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e',
|
|
'\\','S','y','s','t','e','m','\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
|
|
'C','o','n','t','r','o','l','\\',0};
|
|
|
|
static const WCHAR devicemap[] = {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\',
|
|
'H','a','r','d','w','a','r','e','\\','D','e','v','i','c','e','M','a','p','\\',0};
|
|
|
|
static const WCHAR services[] = {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\',
|
|
'S','y','s','t','e','m','\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
|
|
'S','e','r','v','i','c','e','s','\\',0};
|
|
|
|
static const WCHAR user[] = {'\\','R','e','g','i','s','t','r','y','\\','U','s','e','r','\\',
|
|
'C','u','r','r','e','n','t','U','s','e','r','\\',0};
|
|
|
|
static const WCHAR windows_nt[] = {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\',
|
|
'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
|
|
'W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',0};
|
|
|
|
switch (RelativeTo & 0xff)
|
|
{
|
|
case RTL_REGISTRY_ABSOLUTE:
|
|
base = empty;
|
|
break;
|
|
|
|
case RTL_REGISTRY_CONTROL:
|
|
base = control;
|
|
break;
|
|
|
|
case RTL_REGISTRY_DEVICEMAP:
|
|
base = devicemap;
|
|
break;
|
|
|
|
case RTL_REGISTRY_SERVICES:
|
|
base = services;
|
|
break;
|
|
|
|
case RTL_REGISTRY_USER:
|
|
base = user;
|
|
break;
|
|
|
|
case RTL_REGISTRY_WINDOWS_NT:
|
|
base = windows_nt;
|
|
break;
|
|
|
|
default:
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
len = (strlenW(base) + strlenW(Path) + 1) * sizeof(WCHAR);
|
|
KeyString.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, len);
|
|
if (KeyString.Buffer == NULL)
|
|
return STATUS_NO_MEMORY;
|
|
|
|
strcpyW(KeyString.Buffer, base);
|
|
strcatW(KeyString.Buffer, Path);
|
|
KeyString.Length = len - sizeof(WCHAR);
|
|
KeyString.MaximumLength = len;
|
|
InitializeObjectAttributes(®key, &KeyString, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
status = NtOpenKey(handle, KEY_ALL_ACCESS, ®key);
|
|
RtlFreeHeap(GetProcessHeap(), 0, KeyString.Buffer);
|
|
return status;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* RtlQueryRegistryValues [NTDLL.@]
|
|
*
|
|
* Query multiple registry values with a single call.
|
|
*
|
|
* PARAMS
|
|
* RelativeTo [I] Registry path that Path refers to
|
|
* Path [I] Path to key
|
|
* QueryTable [I] Table of key values to query
|
|
* Context [I] Parameter to pass to the application defined QueryRoutine function
|
|
* Environment [I] Optional parameter to use when performing expansion
|
|
*
|
|
* RETURNS
|
|
* STATUS_SUCCESS or an appropriate NTSTATUS error code.
|
|
*/
|
|
NTSTATUS WINAPI RtlQueryRegistryValues(IN ULONG RelativeTo, IN PCWSTR Path,
|
|
IN PRTL_QUERY_REGISTRY_TABLE QueryTable, IN PVOID Context,
|
|
IN PVOID Environment OPTIONAL)
|
|
{
|
|
UNICODE_STRING Value;
|
|
HANDLE handle, topkey;
|
|
PKEY_VALUE_FULL_INFORMATION pInfo = NULL;
|
|
ULONG len, buflen = 0;
|
|
NTSTATUS status=STATUS_SUCCESS, ret = STATUS_SUCCESS;
|
|
INT i;
|
|
|
|
TRACE("(%d, %s, %p, %p, %p)\n", RelativeTo, debugstr_w(Path), QueryTable, Context, Environment);
|
|
|
|
if(Path == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* get a valid handle */
|
|
if (RelativeTo & RTL_REGISTRY_HANDLE)
|
|
topkey = handle = (HANDLE)Path;
|
|
else
|
|
{
|
|
status = RTL_GetKeyHandle(RelativeTo, Path, &topkey);
|
|
handle = topkey;
|
|
}
|
|
if(status != STATUS_SUCCESS)
|
|
return status;
|
|
|
|
/* Process query table entries */
|
|
for (; QueryTable->QueryRoutine != NULL || QueryTable->Name != NULL; ++QueryTable)
|
|
{
|
|
if (QueryTable->Flags &
|
|
(RTL_QUERY_REGISTRY_SUBKEY | RTL_QUERY_REGISTRY_TOPKEY))
|
|
{
|
|
/* topkey must be kept open just in case we will reuse it later */
|
|
if (handle != topkey)
|
|
NtClose(handle);
|
|
|
|
if (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY)
|
|
{
|
|
handle = 0;
|
|
status = RTL_GetKeyHandle(PtrToUlong(QueryTable->Name), Path, &handle);
|
|
if(status != STATUS_SUCCESS)
|
|
{
|
|
ret = status;
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
handle = topkey;
|
|
}
|
|
|
|
if (QueryTable->Flags & RTL_QUERY_REGISTRY_NOVALUE)
|
|
{
|
|
QueryTable->QueryRoutine(QueryTable->Name, REG_NONE, NULL, 0,
|
|
Context, QueryTable->EntryContext);
|
|
continue;
|
|
}
|
|
|
|
if (!handle)
|
|
{
|
|
if (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED)
|
|
{
|
|
ret = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (QueryTable->Name == NULL)
|
|
{
|
|
if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT)
|
|
{
|
|
ret = STATUS_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
/* Report all subkeys */
|
|
for (i = 0;; ++i)
|
|
{
|
|
status = NtEnumerateValueKey(handle, i,
|
|
KeyValueFullInformation, pInfo, buflen, &len);
|
|
if (status == STATUS_NO_MORE_ENTRIES)
|
|
break;
|
|
if (status == STATUS_BUFFER_OVERFLOW ||
|
|
status == STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
buflen = len;
|
|
RtlFreeHeap(GetProcessHeap(), 0, pInfo);
|
|
pInfo = RtlAllocateHeap(GetProcessHeap(), 0, buflen);
|
|
NtEnumerateValueKey(handle, i, KeyValueFullInformation,
|
|
pInfo, buflen, &len);
|
|
}
|
|
|
|
status = RTL_ReportRegistryValue(pInfo, QueryTable, Context, Environment);
|
|
if(status != STATUS_SUCCESS && status != STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
ret = status;
|
|
goto out;
|
|
}
|
|
if (QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE)
|
|
{
|
|
RtlInitUnicodeString(&Value, pInfo->Name);
|
|
NtDeleteValueKey(handle, &Value);
|
|
}
|
|
}
|
|
|
|
if (i == 0 && (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED))
|
|
{
|
|
ret = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RtlInitUnicodeString(&Value, QueryTable->Name);
|
|
status = NtQueryValueKey(handle, &Value, KeyValueFullInformation,
|
|
pInfo, buflen, &len);
|
|
if (status == STATUS_BUFFER_OVERFLOW ||
|
|
status == STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
buflen = len;
|
|
RtlFreeHeap(GetProcessHeap(), 0, pInfo);
|
|
pInfo = RtlAllocateHeap(GetProcessHeap(), 0, buflen);
|
|
status = NtQueryValueKey(handle, &Value,
|
|
KeyValueFullInformation, pInfo, buflen, &len);
|
|
}
|
|
if (status != STATUS_SUCCESS)
|
|
{
|
|
if (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED)
|
|
{
|
|
ret = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
status = RTL_ReportRegistryValue(NULL, QueryTable, Context, Environment);
|
|
if(status != STATUS_SUCCESS && status != STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
ret = status;
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = RTL_ReportRegistryValue(pInfo, QueryTable, Context, Environment);
|
|
if(status != STATUS_SUCCESS && status != STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
ret = status;
|
|
goto out;
|
|
}
|
|
if (QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE)
|
|
NtDeleteValueKey(handle, &Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
RtlFreeHeap(GetProcessHeap(), 0, pInfo);
|
|
if (handle != topkey)
|
|
NtClose(handle);
|
|
NtClose(topkey);
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* RtlCheckRegistryKey [NTDLL.@]
|
|
*
|
|
* Query multiple registry values with a single call.
|
|
*
|
|
* PARAMS
|
|
* RelativeTo [I] Registry path that Path refers to
|
|
* Path [I] Path to key
|
|
*
|
|
* RETURNS
|
|
* STATUS_SUCCESS if the specified key exists, or an NTSTATUS error code.
|
|
*/
|
|
NTSTATUS WINAPI RtlCheckRegistryKey(IN ULONG RelativeTo, IN PWSTR Path)
|
|
{
|
|
HANDLE handle;
|
|
NTSTATUS status;
|
|
|
|
TRACE("(%d, %s)\n", RelativeTo, debugstr_w(Path));
|
|
|
|
if((!RelativeTo) && Path == NULL)
|
|
return STATUS_OBJECT_PATH_SYNTAX_BAD;
|
|
if(RelativeTo & RTL_REGISTRY_HANDLE)
|
|
return STATUS_SUCCESS;
|
|
|
|
status = RTL_GetKeyHandle(RelativeTo, Path, &handle);
|
|
if (handle) NtClose(handle);
|
|
if (status == STATUS_INVALID_HANDLE) status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
return status;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* RtlDeleteRegistryValue [NTDLL.@]
|
|
*
|
|
* Query multiple registry values with a single call.
|
|
*
|
|
* PARAMS
|
|
* RelativeTo [I] Registry path that Path refers to
|
|
* Path [I] Path to key
|
|
* ValueName [I] Name of the value to delete
|
|
*
|
|
* RETURNS
|
|
* STATUS_SUCCESS if the specified key is successfully deleted, or an NTSTATUS error code.
|
|
*/
|
|
NTSTATUS WINAPI RtlDeleteRegistryValue(IN ULONG RelativeTo, IN PCWSTR Path, IN PCWSTR ValueName)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE handle;
|
|
UNICODE_STRING Value;
|
|
|
|
TRACE("(%d, %s, %s)\n", RelativeTo, debugstr_w(Path), debugstr_w(ValueName));
|
|
|
|
RtlInitUnicodeString(&Value, ValueName);
|
|
if(RelativeTo == RTL_REGISTRY_HANDLE)
|
|
{
|
|
return NtDeleteValueKey((HANDLE)Path, &Value);
|
|
}
|
|
status = RTL_GetKeyHandle(RelativeTo, Path, &handle);
|
|
if (status) return status;
|
|
status = NtDeleteValueKey(handle, &Value);
|
|
NtClose(handle);
|
|
return status;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* RtlWriteRegistryValue [NTDLL.@]
|
|
*
|
|
* Sets the registry value with provided data.
|
|
*
|
|
* PARAMS
|
|
* RelativeTo [I] Registry path that path parameter refers to
|
|
* path [I] Path to the key (or handle - see RTL_GetKeyHandle)
|
|
* name [I] Name of the registry value to set
|
|
* type [I] Type of the registry key to set
|
|
* data [I] Pointer to the user data to be set
|
|
* length [I] Length of the user data pointed by data
|
|
*
|
|
* RETURNS
|
|
* STATUS_SUCCESS if the specified key is successfully set,
|
|
* or an NTSTATUS error code.
|
|
*/
|
|
NTSTATUS WINAPI RtlWriteRegistryValue( ULONG RelativeTo, PCWSTR path, PCWSTR name,
|
|
ULONG type, PVOID data, ULONG length )
|
|
{
|
|
HANDLE hkey;
|
|
NTSTATUS status;
|
|
UNICODE_STRING str;
|
|
|
|
TRACE( "(%d, %s, %s) -> %d: %p [%d]\n", RelativeTo, debugstr_w(path), debugstr_w(name),
|
|
type, data, length );
|
|
|
|
RtlInitUnicodeString( &str, name );
|
|
|
|
if (RelativeTo == RTL_REGISTRY_HANDLE)
|
|
return NtSetValueKey( (HANDLE)path, &str, 0, type, data, length );
|
|
|
|
status = RTL_GetKeyHandle( RelativeTo, path, &hkey );
|
|
if (status != STATUS_SUCCESS) return status;
|
|
|
|
status = NtSetValueKey( hkey, &str, 0, type, data, length );
|
|
NtClose( hkey );
|
|
|
|
return status;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* NtQueryLicenseValue [NTDLL.@]
|
|
*
|
|
* NOTES
|
|
* On Windows all license properties are stored in a single key, but
|
|
* unless there is some app which explicitly depends on that, there is
|
|
* no good reason to reproduce that.
|
|
*/
|
|
NTSTATUS WINAPI NtQueryLicenseValue( const UNICODE_STRING *name, ULONG *result_type,
|
|
PVOID data, ULONG length, ULONG *result_len )
|
|
{
|
|
static const WCHAR LicenseInformationW[] = {'M','a','c','h','i','n','e','\\',
|
|
'S','o','f','t','w','a','r','e','\\',
|
|
'W','i','n','e','\\','L','i','c','e','n','s','e',
|
|
'I','n','f','o','r','m','a','t','i','o','n',0};
|
|
KEY_VALUE_PARTIAL_INFORMATION *info;
|
|
NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
DWORD info_length, count;
|
|
OBJECT_ATTRIBUTES attr;
|
|
UNICODE_STRING keyW;
|
|
HANDLE hkey;
|
|
|
|
if (!name || !name->Buffer || !name->Length || !result_len)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
info_length = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + length;
|
|
info = RtlAllocateHeap( GetProcessHeap(), 0, info_length );
|
|
if (!info) return STATUS_NO_MEMORY;
|
|
|
|
attr.Length = sizeof(attr);
|
|
attr.RootDirectory = 0;
|
|
attr.ObjectName = &keyW;
|
|
attr.Attributes = 0;
|
|
attr.SecurityDescriptor = NULL;
|
|
attr.SecurityQualityOfService = NULL;
|
|
RtlInitUnicodeString( &keyW, LicenseInformationW );
|
|
|
|
/* @@ Wine registry key: HKLM\Software\Wine\LicenseInformation */
|
|
if (!NtOpenKey( &hkey, KEY_READ, &attr ))
|
|
{
|
|
status = NtQueryValueKey( hkey, name, KeyValuePartialInformation,
|
|
info, info_length, &count );
|
|
if (!status || status == STATUS_BUFFER_OVERFLOW)
|
|
{
|
|
if (result_type)
|
|
*result_type = info->Type;
|
|
|
|
*result_len = info->DataLength;
|
|
|
|
if (status == STATUS_BUFFER_OVERFLOW)
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
else
|
|
memcpy( data, info->Data, info->DataLength );
|
|
}
|
|
NtClose( hkey );
|
|
}
|
|
|
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
FIXME( "License key %s not found\n", debugstr_w(name->Buffer) );
|
|
|
|
RtlFreeHeap( GetProcessHeap(), 0, info );
|
|
return status;
|
|
}
|