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