Sweden-Number/dlls/ntdll/unix/registry.c

815 lines
28 KiB
C

/*
* Registry functions
*
* Copyright 1999 Juergen Schmied
* Copyright 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
*
*/
#if 0
#pragma makedep unix
#endif
#include <stdarg.h>
#include <string.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "winternl.h"
#include "unix_private.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(reg);
/* maximum length of a value name in bytes (without terminating null) */
#define MAX_VALUE_LENGTH (16383 * sizeof(WCHAR))
NTSTATUS open_hkcu_key( const char *path, HANDLE *key )
{
NTSTATUS status;
char buffer[256];
WCHAR bufferW[256];
DWORD_PTR sid_data[(sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE) / sizeof(DWORD_PTR)];
DWORD i, len = sizeof(sid_data);
SID *sid;
UNICODE_STRING name;
OBJECT_ATTRIBUTES attr;
status = NtQueryInformationToken( GetCurrentThreadEffectiveToken(), TokenUser, sid_data, len, &len );
if (status) return status;
sid = ((TOKEN_USER *)sid_data)->User.Sid;
len = sprintf( buffer, "\\Registry\\User\\S-%u-%u", sid->Revision,
MAKELONG( MAKEWORD( sid->IdentifierAuthority.Value[5], sid->IdentifierAuthority.Value[4] ),
MAKEWORD( sid->IdentifierAuthority.Value[3], sid->IdentifierAuthority.Value[2] )));
for (i = 0; i < sid->SubAuthorityCount; i++)
len += sprintf( buffer + len, "-%u", sid->SubAuthority[i] );
len += sprintf( buffer + len, "\\%s", path );
ascii_to_unicode( bufferW, buffer, len + 1 );
init_unicode_string( &name, bufferW );
InitializeObjectAttributes( &attr, &name, OBJ_CASE_INSENSITIVE, 0, NULL );
return NtCreateKey( key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL );
}
/******************************************************************************
* NtCreateKey (NTDLL.@)
*/
NTSTATUS WINAPI NtCreateKey( HANDLE *key, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
ULONG index, const UNICODE_STRING *class, ULONG options, ULONG *dispos )
{
NTSTATUS ret;
data_size_t len;
struct object_attributes *objattr;
if (!key || !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, key );
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 );
*key = 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", *key );
free( objattr );
return ret;
}
/******************************************************************************
* NtCreateKeyTransacted (NTDLL.@)
*/
NTSTATUS WINAPI NtCreateKeyTransacted( HANDLE *key, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
ULONG index, 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, key );
return STATUS_NOT_IMPLEMENTED;
}
/******************************************************************************
* NtOpenKeyEx (NTDLL.@)
*/
NTSTATUS WINAPI NtOpenKeyEx( HANDLE *key, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, ULONG options )
{
NTSTATUS ret;
if (!key || !attr || !attr->ObjectName) return STATUS_ACCESS_VIOLATION;
if (attr->Length != sizeof(*attr)) return STATUS_INVALID_PARAMETER;
if (attr->ObjectName->Length & 1) return STATUS_OBJECT_NAME_INVALID;
TRACE( "(%p,%s,%x,%p)\n", attr->RootDirectory, debugstr_us(attr->ObjectName), access, key );
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 );
*key = wine_server_ptr_handle( reply->hkey );
}
SERVER_END_REQ;
TRACE("<- %p\n", *key);
return ret;
}
/******************************************************************************
* NtOpenKey (NTDLL.@)
*/
NTSTATUS WINAPI NtOpenKey( HANDLE *key, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
{
return NtOpenKeyEx( key, access, attr, 0 );
}
/******************************************************************************
* NtOpenKeyTransactedEx (NTDLL.@)
*/
NTSTATUS WINAPI NtOpenKeyTransactedEx( HANDLE *key, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
ULONG options, HANDLE transaction )
{
FIXME( "(%p %x %p %x %p)\n", key, access, attr, options, transaction );
return STATUS_NOT_IMPLEMENTED;
}
/******************************************************************************
* NtOpenKeyTransacted (NTDLL.@)
*/
NTSTATUS WINAPI NtOpenKeyTransacted( HANDLE *key, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
HANDLE transaction )
{
return NtOpenKeyTransactedEx( key, access, attr, 0, transaction );
}
/******************************************************************************
* NtDeleteKey (NTDLL.@)
*/
NTSTATUS WINAPI NtDeleteKey( HANDLE key )
{
NTSTATUS ret;
TRACE( "(%p)\n", key );
SERVER_START_REQ( delete_key )
{
req->hkey = wine_server_obj_handle( key );
ret = wine_server_call( req );
}
SERVER_END_REQ;
return ret;
}
/******************************************************************************
* NtRenameKey (NTDLL.@)
*/
NTSTATUS WINAPI NtRenameKey( HANDLE handle, UNICODE_STRING *name )
{
FIXME( "(%p %s)\n", handle, debugstr_us(name) );
return STATUS_NOT_IMPLEMENTED;
}
/******************************************************************************
* 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.@)
*/
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 );
}
/******************************************************************************
* NtQueryKey (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 );
}
/******************************************************************************
* NtSetInformationKey (NTDLL.@)
*/
NTSTATUS WINAPI NtSetInformationKey( HANDLE key, int class, void *info, ULONG length )
{
FIXME( "(%p,0x%08x,%p,0x%08x) stub\n", key, class, info, length );
return STATUS_SUCCESS;
}
/* 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.@)
*/
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.@)
*/
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;
}
/******************************************************************************
* NtQueryMultipleValueKey (NTDLL.@)
*/
NTSTATUS WINAPI NtQueryMultipleValueKey( HANDLE key, KEY_MULTIPLE_VALUE_INFORMATION *info,
ULONG count, void *buffer, ULONG length, ULONG *retlen )
{
FIXME( "(%p,%p,0x%08x,%p,0x%08x,%p) stub!\n", key, info, count, buffer, length, retlen );
return STATUS_SUCCESS;
}
/******************************************************************************
* NtSetValueKey (NTDLL.@)
*/
NTSTATUS WINAPI NtSetValueKey( HANDLE key, const UNICODE_STRING *name, ULONG index,
ULONG type, const void *data, ULONG count )
{
NTSTATUS ret;
TRACE( "(%p,%s,%d,%p,%d)\n", key, 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( key );
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;
}
/******************************************************************************
* NtDeleteValueKey (NTDLL.@)
*/
NTSTATUS WINAPI NtDeleteValueKey( HANDLE key, const UNICODE_STRING *name )
{
NTSTATUS ret;
TRACE( "(%p,%s)\n", key, 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( key );
wine_server_add_data( req, name->Buffer, name->Length );
ret = wine_server_call( req );
}
SERVER_END_REQ;
return ret;
}
/******************************************************************************
* NtNotifyChangeMultipleKeys (NTDLL.@)
*/
NTSTATUS WINAPI NtNotifyChangeMultipleKeys( HANDLE key, ULONG count, OBJECT_ATTRIBUTES *attr,
HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
IO_STATUS_BLOCK *io, ULONG filter, BOOLEAN subtree,
void *buffer, ULONG length, BOOLEAN async )
{
NTSTATUS ret;
TRACE( "(%p,%u,%p,%p,%p,%p,%p,0x%08x, 0x%08x,%p,0x%08x,0x%08x)\n",
key, count, attr, event, apc, apc_context, io, filter, async, buffer, length, subtree );
if (count || attr || apc || apc_context || buffer || length)
FIXME( "Unimplemented optional parameter\n" );
if (!async)
{
OBJECT_ATTRIBUTES attr;
InitializeObjectAttributes( &attr, NULL, 0, NULL, NULL );
ret = NtCreateEvent( &event, EVENT_ALL_ACCESS, &attr, SynchronizationEvent, FALSE );
if (ret) return ret;
}
SERVER_START_REQ( set_registry_notification )
{
req->hkey = wine_server_obj_handle( key );
req->event = wine_server_obj_handle( event );
req->subtree = subtree;
req->filter = filter;
ret = wine_server_call( req );
}
SERVER_END_REQ;
if (!async)
{
if (ret == STATUS_PENDING) ret = NtWaitForSingleObject( event, FALSE, NULL );
NtClose( event );
}
return ret;
}
/******************************************************************************
* NtNotifyChangeKey (NTDLL.@)
*/
NTSTATUS WINAPI NtNotifyChangeKey( HANDLE key, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
IO_STATUS_BLOCK *io, ULONG filter, BOOLEAN subtree,
void *buffer, ULONG length, BOOLEAN async )
{
return NtNotifyChangeMultipleKeys( key, 0, NULL, event, apc, apc_context,
io, filter, subtree, buffer, length, async );
}
/******************************************************************************
* NtFlushKey (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.@)
*/
NTSTATUS WINAPI NtLoadKey( const OBJECT_ATTRIBUTES *attr, OBJECT_ATTRIBUTES *file )
{
NTSTATUS ret;
HANDLE key;
IO_STATUS_BLOCK io;
data_size_t len;
struct object_attributes *objattr;
TRACE("(%p,%p)\n", attr, file);
ret = NtCreateFile( &key, 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( key );
wine_server_add_data( req, objattr, len );
ret = wine_server_call( req );
}
SERVER_END_REQ;
NtClose( key );
free( objattr );
return ret;
}
/******************************************************************************
* NtLoadKey2 (NTDLL.@)
*/
NTSTATUS WINAPI NtLoadKey2( const 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 );
}
/******************************************************************************
* NtUnloadKey (NTDLL.@)
*/
NTSTATUS WINAPI NtUnloadKey( OBJECT_ATTRIBUTES *attr )
{
NTSTATUS ret;
TRACE( "(%p)\n", attr );
if (!attr || !attr->ObjectName) return STATUS_ACCESS_VIOLATION;
if (attr->Length != sizeof(*attr)) return STATUS_INVALID_PARAMETER;
if (attr->ObjectName->Length & 1) return STATUS_OBJECT_NAME_INVALID;
SERVER_START_REQ( unload_registry )
{
req->parent = wine_server_obj_handle( attr->RootDirectory );
req->attributes = attr->Attributes;
wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
ret = wine_server_call(req);
}
SERVER_END_REQ;
return ret;
}
/******************************************************************************
* NtSaveKey (NTDLL.@)
*/
NTSTATUS WINAPI NtSaveKey( HANDLE key, HANDLE file )
{
NTSTATUS ret;
TRACE( "(%p,%p)\n", key, file );
SERVER_START_REQ( save_registry )
{
req->hkey = wine_server_obj_handle( key );
req->file = wine_server_obj_handle( file );
ret = wine_server_call( req );
}
SERVER_END_REQ;
return ret;
}
/******************************************************************************
* NtRestoreKey (NTDLL.@)
*/
NTSTATUS WINAPI NtRestoreKey( HANDLE key, HANDLE file, ULONG flags )
{
FIXME( "(%p,%p,0x%08x) stub\n", key, file, flags );
return STATUS_SUCCESS;
}
/******************************************************************************
* NtReplaceKey (NTDLL.@)
*/
NTSTATUS WINAPI NtReplaceKey( OBJECT_ATTRIBUTES *attr, HANDLE key, OBJECT_ATTRIBUTES *replace )
{
FIXME( "(%s,%p,%s),stub!\n", debugstr_us(attr->ObjectName), key, debugstr_us(replace->ObjectName) );
return STATUS_SUCCESS;
}
/******************************************************************************
* 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 *type,
void *data, ULONG length, ULONG *retlen )
{
static const WCHAR nameW[] = {'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};
UNICODE_STRING keyW = { sizeof(nameW) - sizeof(WCHAR), sizeof(nameW), (WCHAR *)nameW };
KEY_VALUE_PARTIAL_INFORMATION *info;
NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND;
DWORD info_length, count;
OBJECT_ATTRIBUTES attr;
HANDLE key;
if (!name || !name->Buffer || !name->Length || !retlen) return STATUS_INVALID_PARAMETER;
info_length = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data ) + length;
if (!(info = malloc( info_length ))) return STATUS_NO_MEMORY;
InitializeObjectAttributes( &attr, &keyW, 0, 0, NULL );
/* @@ Wine registry key: HKLM\Software\Wine\LicenseInformation */
if (!NtOpenKey( &key, KEY_READ, &attr ))
{
status = NtQueryValueKey( key, name, KeyValuePartialInformation, info, info_length, &count );
if (!status || status == STATUS_BUFFER_OVERFLOW)
{
if (type) *type = info->Type;
*retlen = info->DataLength;
if (status == STATUS_BUFFER_OVERFLOW)
status = STATUS_BUFFER_TOO_SMALL;
else
memcpy( data, info->Data, info->DataLength );
}
NtClose( key );
}
if (status == STATUS_OBJECT_NAME_NOT_FOUND)
FIXME( "License key %s not found\n", debugstr_w(name->Buffer) );
free( info );
return status;
}