Don't load user32 too early on for 16-bit apps, so that app-specific
[wine] / memory / registry.c
1 /*
2  * Registry management
3  *
4  * Copyright (C) 1999 Alexandre Julliard
5  *
6  * Based on misc/registry.c code
7  * Copyright (C) 1996 Marcus Meissner
8  * Copyright (C) 1998 Matthew Becker
9  * Copyright (C) 1999 Sylvain St-Germain
10  *
11  * This file is concerned about handle management and interaction with the Wine server.
12  * Registry file I/O is in misc/registry.c.
13  */
14
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <unistd.h>
18
19 #include "winbase.h"
20 #include "winreg.h"
21 #include "winerror.h"
22 #include "wine/winbase16.h"
23 #include "wine/unicode.h"
24 #include "heap.h"
25 #include "server.h"
26 #include "debugtools.h"
27
28 DEFAULT_DEBUG_CHANNEL(reg);
29
30
31 /* check if value type needs string conversion (Ansi<->Unicode) */
32 static inline int is_string( DWORD type )
33 {
34     return (type == REG_SZ) || (type == REG_EXPAND_SZ) || (type == REG_MULTI_SZ);
35 }
36
37
38 /******************************************************************************
39  *           RegCreateKeyExA   [ADVAPI32.130]
40  */
41 DWORD WINAPI RegCreateKeyExA( HKEY hkey, LPCSTR name, DWORD reserved, LPSTR class,
42                               DWORD options, REGSAM access, SECURITY_ATTRIBUTES *sa, 
43                               LPHKEY retkey, LPDWORD dispos )
44 {
45     OBJECT_ATTRIBUTES attr;
46     UNICODE_STRING nameW, classW;
47     ANSI_STRING nameA, classA;
48     NTSTATUS status;
49
50     if (reserved) return ERROR_INVALID_PARAMETER;
51     if (!(access & KEY_ALL_ACCESS) || (access & ~KEY_ALL_ACCESS)) return ERROR_ACCESS_DENIED;
52
53     attr.Length = sizeof(attr);
54     attr.RootDirectory = hkey;
55     attr.ObjectName = &nameW;
56     attr.Attributes = 0;
57     attr.SecurityDescriptor = NULL;
58     attr.SecurityQualityOfService = NULL;
59     RtlInitAnsiString( &nameA, name );
60     RtlInitAnsiString( &classA, class );
61
62     /* FIXME: should use Unicode buffer in TEB */
63     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
64     {
65         if (!(status = RtlAnsiStringToUnicodeString( &classW, &classA, TRUE )))
66         {
67             status = NtCreateKey( retkey, access, &attr, 0, &classW, options, dispos );
68             RtlFreeUnicodeString( &classW );
69         }
70         RtlFreeUnicodeString( &nameW );
71     }
72     return RtlNtStatusToDosError( status );
73 }
74
75
76 /******************************************************************************
77  *           RegCreateKeyA   [ADVAPI32.129]
78  */
79 DWORD WINAPI RegCreateKeyA( HKEY hkey, LPCSTR name, LPHKEY retkey )
80 {
81     return RegCreateKeyExA( hkey, name, 0, NULL, REG_OPTION_NON_VOLATILE,
82                             KEY_ALL_ACCESS, NULL, retkey, NULL );
83 }
84
85
86
87 /******************************************************************************
88  *           RegOpenKeyExA   [ADVAPI32.149]
89  */
90 DWORD WINAPI RegOpenKeyExA( HKEY hkey, LPCSTR name, DWORD reserved, REGSAM access, LPHKEY retkey )
91 {
92     OBJECT_ATTRIBUTES attr;
93     UNICODE_STRING nameW;
94     STRING nameA;
95     NTSTATUS status;
96
97     attr.Length = sizeof(attr);
98     attr.RootDirectory = hkey;
99     attr.ObjectName = &nameW;
100     attr.Attributes = 0;
101     attr.SecurityDescriptor = NULL;
102     attr.SecurityQualityOfService = NULL;
103
104     RtlInitAnsiString( &nameA, name );
105     /* FIXME: should use Unicode buffer in TEB */
106     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
107     {
108         status = NtOpenKey( retkey, access, &attr );
109         RtlFreeUnicodeString( &nameW );
110     }
111     return RtlNtStatusToDosError( status );
112 }
113
114
115 /******************************************************************************
116  *           RegOpenKeyA   [ADVAPI32.148]
117  */
118 DWORD WINAPI RegOpenKeyA( HKEY hkey, LPCSTR name, LPHKEY retkey )
119 {
120     return RegOpenKeyExA( hkey, name, 0, KEY_ALL_ACCESS, retkey );
121 }
122
123
124 /******************************************************************************
125  *           RegEnumKeyExA   [ADVAPI32.138]
126  */
127 DWORD WINAPI RegEnumKeyExA( HKEY hkey, DWORD index, LPSTR name, LPDWORD name_len,
128                             LPDWORD reserved, LPSTR class, LPDWORD class_len, FILETIME *ft )
129 {
130     NTSTATUS status;
131     char buffer[256], *buf_ptr = buffer;
132     KEY_NODE_INFORMATION *info = (KEY_NODE_INFORMATION *)buffer;
133     DWORD total_size;
134
135     TRACE( "(0x%x,%ld,%p,%p(%ld),%p,%p,%p,%p)\n", hkey, index, name, name_len,
136            name_len ? *name_len : -1, reserved, class, class_len, ft );
137
138     if (reserved) return ERROR_INVALID_PARAMETER;
139
140     status = NtEnumerateKey( hkey, index, KeyNodeInformation,
141                              buffer, sizeof(buffer), &total_size );
142
143     while (status == STATUS_BUFFER_OVERFLOW)
144     {
145         /* retry with a dynamically allocated buffer */
146         if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
147         if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
148             return ERROR_NOT_ENOUGH_MEMORY;
149         info = (KEY_NODE_INFORMATION *)buf_ptr;
150         status = NtEnumerateKey( hkey, index, KeyNodeInformation,
151                                  buf_ptr, total_size, &total_size );
152     }
153
154     if (!status)
155     {
156         DWORD len = WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
157                                          NULL, 0, NULL, NULL );
158         DWORD cls_len = WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info->ClassOffset),
159                                              info->ClassLength / sizeof(WCHAR),
160                                              NULL, 0, NULL, NULL );
161
162         if (ft) *ft = info->LastWriteTime;
163
164         if (len >= *name_len || (class_len && (cls_len >= *class_len)))
165             status = STATUS_BUFFER_OVERFLOW;
166         else
167         {
168             *name_len = len;
169             WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
170                                  name, len, NULL, NULL );
171             name[len] = 0;
172             if (class_len)
173             {
174                 *class_len = cls_len;
175                 if (class)
176                 {
177                     WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info->ClassOffset),
178                                          info->ClassLength / sizeof(WCHAR),
179                                          class, cls_len, NULL, NULL );
180                     class[cls_len] = 0;
181                 }
182             }
183         }
184     }
185
186     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
187     return RtlNtStatusToDosError( status );
188 }
189
190
191 /******************************************************************************
192  *           RegEnumKeyA   [ADVAPI32.137]
193  */
194 DWORD WINAPI RegEnumKeyA( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
195 {
196     return RegEnumKeyExA( hkey, index, name, &name_len, NULL, NULL, NULL, NULL );
197 }
198
199
200 /******************************************************************************
201  *           RegQueryInfoKeyA   [ADVAPI32.152]
202  */
203 DWORD WINAPI RegQueryInfoKeyA( HKEY hkey, LPSTR class, LPDWORD class_len, LPDWORD reserved,
204                                LPDWORD subkeys, LPDWORD max_subkey, LPDWORD max_class,
205                                LPDWORD values, LPDWORD max_value, LPDWORD max_data,
206                                LPDWORD security, FILETIME *modif )
207 {
208     NTSTATUS status;
209     char buffer[256], *buf_ptr = buffer;
210     KEY_FULL_INFORMATION *info = (KEY_FULL_INFORMATION *)buffer;
211     DWORD total_size;
212
213     TRACE( "(0x%x,%p,%ld,%p,%p,%p,%p,%p,%p,%p,%p)\n", hkey, class, class_len ? *class_len : 0,
214            reserved, subkeys, max_subkey, values, max_value, max_data, security, modif );
215
216     if (class && !class_len && !(GetVersion() & 0x80000000 /*NT*/))
217         return ERROR_INVALID_PARAMETER;
218
219     status = NtQueryKey( hkey, KeyFullInformation, buffer, sizeof(buffer), &total_size );
220
221     if (class || class_len)
222     {
223         /* retry with a dynamically allocated buffer */
224         while (status == STATUS_BUFFER_OVERFLOW)
225         {
226             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
227             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
228                 return ERROR_NOT_ENOUGH_MEMORY;
229             info = (KEY_FULL_INFORMATION *)buf_ptr;
230             status = NtQueryKey( hkey, KeyFullInformation, buf_ptr, total_size, &total_size );
231         }
232
233         if (!status)
234         {
235             DWORD len = WideCharToMultiByte( CP_ACP, 0,
236                                              (WCHAR *)(buf_ptr + info->ClassOffset),
237                                              info->ClassLength/sizeof(WCHAR),
238                                              NULL, 0, NULL, NULL );
239             if (class_len)
240             {
241                 if (len + 1 > *class_len) status = STATUS_BUFFER_OVERFLOW;
242                 *class_len = len;
243             }
244             if (class && !status)
245             {
246                 WideCharToMultiByte( CP_ACP, 0,
247                                      (WCHAR *)(buf_ptr + info->ClassOffset),
248                                      info->ClassLength/sizeof(WCHAR),
249                                      class, len, NULL, NULL );
250                 class[len] = 0;
251             }
252         }
253     }
254
255     if (!status || status == STATUS_BUFFER_OVERFLOW)
256     {
257         if (subkeys) *subkeys = info->SubKeys;
258         if (max_subkey) *max_subkey = info->MaxNameLen;
259         if (max_class) *max_class = info->MaxClassLen;
260         if (values) *values = info->Values;
261         if (max_value) *max_value = info->MaxValueNameLen;
262         if (max_data) *max_data = info->MaxValueDataLen;
263         if (modif) *modif = info->LastWriteTime;
264     }
265
266     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
267     return RtlNtStatusToDosError( status );
268 }
269
270
271 /******************************************************************************
272  *           RegCloseKey   [ADVAPI32.126]
273  *
274  * Releases the handle of the specified key
275  *
276  * PARAMS
277  *    hkey [I] Handle of key to close
278  *
279  * RETURNS
280  *    Success: ERROR_SUCCESS
281  *    Failure: Error code
282  */
283 DWORD WINAPI RegCloseKey( HKEY hkey )
284 {
285     if (!hkey || hkey >= 0x80000000) return ERROR_SUCCESS;
286     return RtlNtStatusToDosError( NtClose( hkey ) );
287 }
288
289
290 /******************************************************************************
291  *           RegDeleteKeyA   [ADVAPI32.133]
292  */
293 DWORD WINAPI RegDeleteKeyA( HKEY hkey, LPCSTR name )
294 {
295     DWORD ret;
296     HKEY tmp;
297
298     if (!name || !*name) return NtDeleteKey( hkey );
299     if (!(ret = RegOpenKeyExA( hkey, name, 0, 0, &tmp )))
300     {
301         ret = RtlNtStatusToDosError( NtDeleteKey( tmp ) );
302         RegCloseKey( tmp );
303     }
304     return ret;
305 }
306
307
308
309 /******************************************************************************
310  *           RegSetValueExA   [ADVAPI32.169]
311  */
312 DWORD WINAPI RegSetValueExA( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
313                              CONST BYTE *data, DWORD count )
314 {
315     UNICODE_STRING nameW;
316     ANSI_STRING nameA;
317     WCHAR *dataW = NULL;
318     NTSTATUS status;
319
320     if (count && is_string(type))
321     {
322         /* if user forgot to count terminating null, add it (yes NT does this) */
323         if (data[count-1] && !data[count]) count++;
324     }
325
326     if (is_string( type )) /* need to convert to Unicode */
327     {
328         DWORD lenW = MultiByteToWideChar( CP_ACP, 0, data, count, NULL, 0 );
329         if (!(dataW = HeapAlloc( GetProcessHeap(), 0, lenW*sizeof(WCHAR) )))
330             return ERROR_OUTOFMEMORY;
331         MultiByteToWideChar( CP_ACP, 0, data, count, dataW, lenW );
332         count = lenW * sizeof(WCHAR);
333         data = (BYTE *)dataW;
334     }
335
336     RtlInitAnsiString( &nameA, name );
337     /* FIXME: should use Unicode buffer in TEB */
338     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
339     {
340         status = NtSetValueKey( hkey, &nameW, 0, type, data, count );
341         RtlFreeUnicodeString( &nameW );
342     }
343     if (dataW) HeapFree( GetProcessHeap(), 0, dataW );
344     return RtlNtStatusToDosError( status );
345 }
346
347
348 /******************************************************************************
349  *           RegSetValueA   [ADVAPI32.168]
350  */
351 DWORD WINAPI RegSetValueA( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
352 {
353     HKEY subkey = hkey;
354     DWORD ret;
355
356     TRACE("(0x%x,%s,%ld,%s,%ld)\n", hkey, debugstr_a(name), type, debugstr_a(data), count );
357
358     if (type != REG_SZ) return ERROR_INVALID_PARAMETER;
359
360     if (name && name[0])  /* need to create the subkey */
361     {
362         if ((ret = RegCreateKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
363     }
364     ret = RegSetValueExA( subkey, NULL, 0, REG_SZ, (LPBYTE)data, strlen(data)+1 );
365     if (subkey != hkey) RegCloseKey( subkey );
366     return ret;
367 }
368
369
370
371 /******************************************************************************
372  *           RegQueryValueExA   [ADVAPI32.157]
373  *
374  * NOTES:
375  * the documentation is wrong: if the buffer is too small it remains untouched 
376  */
377 DWORD WINAPI RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
378                                LPBYTE data, LPDWORD count )
379 {
380     NTSTATUS status;
381     ANSI_STRING nameA;
382     UNICODE_STRING nameW;
383     DWORD total_size;
384     char buffer[256], *buf_ptr = buffer;
385     KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
386     static const int info_size = sizeof(*info) - sizeof(info->Data);
387
388     TRACE("(0x%x,%s,%p,%p,%p,%p=%ld)\n",
389           hkey, debugstr_a(name), reserved, type, data, count, count ? *count : 0 );
390
391     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
392
393     RtlInitAnsiString( &nameA, name );
394     /* FIXME: should use Unicode buffer in TEB */
395     if ((status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
396         return RtlNtStatusToDosError(status);
397
398     status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
399                               buffer, sizeof(buffer), &total_size );
400     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
401
402     /* we need to fetch the contents for a string type even if not requested,
403      * because we need to compute the length of the ASCII string. */
404     if (data || is_string(info->Type))
405     {
406         /* retry with a dynamically allocated buffer */
407         while (status == STATUS_BUFFER_OVERFLOW)
408         {
409             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
410             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
411             {
412                 status = STATUS_NO_MEMORY;
413                 goto done;
414             }
415             info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr;
416             status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
417                                       buf_ptr, total_size, &total_size );
418         }
419
420         if (!status)
421         {
422             if (is_string(info->Type))
423             {
424                 DWORD len = WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info_size),
425                                                  (total_size - info_size) /sizeof(WCHAR),
426                                                  NULL, 0, NULL, NULL );
427                 if (data && len)
428                 {
429                     if (len > *count) status = STATUS_BUFFER_OVERFLOW;
430                     else
431                     {
432                         WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info_size),
433                                              (total_size - info_size) /sizeof(WCHAR),
434                                              data, len, NULL, NULL );
435                         /* if the type is REG_SZ and data is not 0-terminated
436                          * and there is enough space in the buffer NT appends a \0 */
437                         if (len < *count && data[len-1]) data[len] = 0;
438                     }
439                 }
440                 total_size = len + info_size;
441             }
442             else if (data)
443             {
444                 if (total_size - info_size > *count) status = STATUS_BUFFER_OVERFLOW;
445                 else memcpy( data, buf_ptr + info_size, total_size - info_size );
446             }
447         }
448         else if (status != STATUS_BUFFER_OVERFLOW) goto done;
449     }
450
451     if (type) *type = info->Type;
452     if (count) *count = total_size - info_size;
453
454  done:
455     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
456     RtlFreeUnicodeString( &nameW );
457     return RtlNtStatusToDosError(status);
458 }
459
460
461 /******************************************************************************
462  *           RegQueryValueA   [ADVAPI32.156]
463  */
464 DWORD WINAPI RegQueryValueA( HKEY hkey, LPCSTR name, LPSTR data, LPLONG count )
465 {
466     DWORD ret;
467     HKEY subkey = hkey;
468
469     TRACE("(%x,%s,%p,%ld)\n", hkey, debugstr_a(name), data, count ? *count : 0 );
470
471     if (name && name[0])
472     {
473         if ((ret = RegOpenKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
474     }
475     ret = RegQueryValueExA( subkey, NULL, NULL, NULL, (LPBYTE)data, count );
476     if (subkey != hkey) RegCloseKey( subkey );
477     if (ret == ERROR_FILE_NOT_FOUND)
478     {
479         /* return empty string if default value not found */
480         if (data) *data = 0;
481         if (count) *count = 1;
482         ret = ERROR_SUCCESS;
483     }
484     return ret;
485 }
486
487
488 /******************************************************************************
489  *           RegEnumValueA   [ADVAPI32.141]
490  */
491 DWORD WINAPI RegEnumValueA( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
492                             LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
493 {
494     NTSTATUS status;
495     DWORD total_size;
496     char buffer[256], *buf_ptr = buffer;
497     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
498     static const int info_size = sizeof(*info) - sizeof(info->Name);
499
500     TRACE("(%x,%ld,%p,%p,%p,%p,%p,%p)\n",
501           hkey, index, value, val_count, reserved, type, data, count );
502
503     /* NT only checks count, not val_count */
504     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
505
506     total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR);
507     if (data) total_size += *count;
508     total_size = min( sizeof(buffer), total_size );
509
510     status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
511                                   buffer, total_size, &total_size );
512     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
513
514     /* we need to fetch the contents for a string type even if not requested,
515      * because we need to compute the length of the ASCII string. */
516     if (value || data || is_string(info->Type))
517     {
518         /* retry with a dynamically allocated buffer */
519         while (status == STATUS_BUFFER_OVERFLOW)
520         {
521             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
522             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
523                 return ERROR_NOT_ENOUGH_MEMORY;
524             info = (KEY_VALUE_FULL_INFORMATION *)buf_ptr;
525             status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
526                                           buf_ptr, total_size, &total_size );
527         }
528
529         if (status) goto done;
530
531         if (value)
532         {
533             DWORD len = WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
534                                              NULL, 0, NULL, NULL );
535             if (len >= *val_count)
536             {
537                 status = STATUS_BUFFER_OVERFLOW;
538                 goto done;
539             }
540             WideCharToMultiByte( CP_ACP, 0, info->Name, info->NameLength/sizeof(WCHAR),
541                                  value, len, NULL, NULL );
542             value[len] = 0;
543             *val_count = len;
544         }
545
546         if (is_string(info->Type))
547         {
548             DWORD len = WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info->DataOffset),
549                                              (total_size - info->DataOffset) / sizeof(WCHAR),
550                                              NULL, 0, NULL, NULL );
551             if (data && len)
552             {
553                 if (len > *count)
554                 {
555                     status = STATUS_BUFFER_OVERFLOW;
556                     goto done;
557                 }
558                 WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info->DataOffset),
559                                      (total_size - info->DataOffset) / sizeof(WCHAR),
560                                      data, len, NULL, NULL );
561                 /* if the type is REG_SZ and data is not 0-terminated
562                  * and there is enough space in the buffer NT appends a \0 */
563                 if (len < *count && data[len-1]) data[len] = 0;
564             }
565             info->DataLength = len;
566         }
567         else if (data)
568         {
569             if (total_size - info->DataOffset > *count) status = STATUS_BUFFER_OVERFLOW;
570             else memcpy( data, buf_ptr + info->DataOffset, total_size - info->DataOffset );
571         }
572     }
573
574     if (type) *type = info->Type;
575     if (count) *count = info->DataLength;
576
577  done:
578     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
579     return RtlNtStatusToDosError(status);
580 }
581
582
583
584 /******************************************************************************
585  *           RegDeleteValueA   [ADVAPI32.135]
586  */
587 DWORD WINAPI RegDeleteValueA( HKEY hkey, LPCSTR name )
588 {
589     UNICODE_STRING nameW;
590     STRING nameA;
591     NTSTATUS status;
592
593     RtlInitAnsiString( &nameA, name );
594     /* FIXME: should use Unicode buffer in TEB */
595     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
596     {
597         status = NtDeleteValueKey( hkey, &nameW );
598         RtlFreeUnicodeString( &nameW );
599     }
600     return RtlNtStatusToDosError( status );
601 }
602
603
604 /******************************************************************************
605  *           RegLoadKeyA   [ADVAPI32.184]
606  */
607 LONG WINAPI RegLoadKeyA( HKEY hkey, LPCSTR subkey, LPCSTR filename )
608 {
609     HANDLE file;
610     DWORD ret, len, err = GetLastError();
611
612     TRACE( "(%x,%s,%s)\n", hkey, debugstr_a(subkey), debugstr_a(filename) );
613
614     if (!filename || !*filename) return ERROR_INVALID_PARAMETER;
615     if (!subkey || !*subkey) return ERROR_INVALID_PARAMETER;
616
617     len = MultiByteToWideChar( CP_ACP, 0, subkey, strlen(subkey), NULL, 0 ) * sizeof(WCHAR);
618     if (len > MAX_PATH*sizeof(WCHAR)) return ERROR_INVALID_PARAMETER;
619
620     if ((file = CreateFileA( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING,
621                              FILE_ATTRIBUTE_NORMAL, 0 )) == INVALID_HANDLE_VALUE)
622     {
623         ret = GetLastError();
624         goto done;
625     }
626
627     SERVER_START_VAR_REQ( load_registry, len )
628     {
629         req->hkey  = hkey;
630         req->file  = file;
631         MultiByteToWideChar( CP_ACP, 0, subkey, strlen(subkey),
632                              server_data_ptr(req), len/sizeof(WCHAR) );
633         ret = RtlNtStatusToDosError( SERVER_CALL() );
634     }
635     SERVER_END_VAR_REQ;
636     CloseHandle( file );
637
638  done:
639     SetLastError( err );  /* restore the last error code */
640     return ret;
641 }
642
643
644 /******************************************************************************
645  *           RegSaveKeyA   [ADVAPI32.165]
646  *
647  * PARAMS
648  *    hkey   [I] Handle of key where save begins
649  *    lpFile [I] Address of filename to save to
650  *    sa     [I] Address of security structure
651  */
652 LONG WINAPI RegSaveKeyA( HKEY hkey, LPCSTR file, LPSECURITY_ATTRIBUTES sa )
653 {
654     char buffer[1024];
655     int count = 0;
656     LPSTR name;
657     DWORD ret, err;
658     HANDLE handle;
659
660     TRACE( "(%x,%s,%p)\n", hkey, debugstr_a(file), sa );
661
662     if (!file || !*file) return ERROR_INVALID_PARAMETER;
663
664     err = GetLastError();
665     GetFullPathNameA( file, sizeof(buffer), buffer, &name );
666     for (;;)
667     {
668         sprintf( name, "reg%04x.tmp", count++ );
669         handle = CreateFileA( buffer, GENERIC_WRITE, 0, NULL,
670                             CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
671         if (handle != INVALID_HANDLE_VALUE) break;
672         if ((ret = GetLastError()) != ERROR_ALREADY_EXISTS) goto done;
673
674         /* Something gone haywire ? Please report if this happens abnormally */
675         if (count >= 100)
676             MESSAGE("Wow, we are already fiddling with a temp file %s with an ordinal as high as %d !\nYou might want to delete all corresponding temp files in that directory.\n", buffer, count);
677     }
678
679     SERVER_START_REQ( save_registry )
680     {
681         req->hkey = hkey;
682         req->file = handle;
683         ret = RtlNtStatusToDosError( SERVER_CALL() );
684     }
685     SERVER_END_REQ;
686
687     CloseHandle( handle );
688     if (!ret)
689     {
690         if (!MoveFileExA( buffer, file, MOVEFILE_REPLACE_EXISTING ))
691         {
692             ERR( "Failed to move %s to %s\n", buffer, file );
693             ret = GetLastError();
694         }
695     }
696     if (ret) DeleteFileA( buffer );
697
698 done:
699     SetLastError( err );  /* restore last error code */
700     return ret;
701 }