rx: Remove RX_CALL_BUSY
[openafs.git] / src / WINNT / afsreg / afsreg.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16 #include <windows.h>
17
18 #include "afsreg.h"
19
20
21 /* Extended (alternative) versions of registry access functions.
22  * All functions return WIN32 style error codes.
23  */
24
25
26 static long CopyKey(const char *sourceKey, const char *targetKey);
27 static long CopyValues(HKEY srcKey, HKEY dupKey);
28 static long CopySubkeys(const char *srcName, HKEY srcKey,
29                         const char *dupName, HKEY dupKey);
30
31 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
32 int IsWow64(void)
33 {
34     static int init = TRUE;
35     static int bIsWow64 = FALSE;
36
37     if (init) {
38         HMODULE hModule;
39         LPFN_ISWOW64PROCESS fnIsWow64Process = NULL;
40
41         hModule = GetModuleHandle(TEXT("kernel32"));
42         if (hModule) {
43             fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(hModule, "IsWow64Process");
44
45             if (NULL != fnIsWow64Process)
46             {
47                 if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
48                 {
49                     // on error, assume FALSE.
50                     // in other words, do nothing.
51                 }
52             }
53             FreeLibrary(hModule);
54         }
55         init = FALSE;
56     }
57     return bIsWow64;
58 }
59
60
61 /* ----------------------- exported functions ----------------------- */
62
63
64 /* RegOpenKeyAlt() -- Open a key in the registry and return its handle.
65  *     If create is set, the subkey (and all its parent keys on the path)
66  *     is created if it does not exist.  In either case, the disposition of
67  *     the key is indicated as REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY.
68  *
69  *     Note: if key is null (AFSREG_NULL_KEY) then the first component of
70  *           subKeyName must be one of the predefined keys; resultKeyDispP
71  *           can always be NULL.
72  */
73
74 long
75 RegOpenKeyAlt(HKEY key,               /* [in] open key from which to start */
76               const char *subKeyName, /* [in] sub key path */
77               DWORD mode,             /* [in] desired access */
78               int create,             /* [in] if set, creates key(s) on path */
79               HKEY *resultKeyP,       /* [out] open key handle */
80               DWORD *resultKeyDispP)  /* [out] open key disposition */
81 {
82     long status;
83     DWORD keyDisp = REG_OPENED_EXISTING_KEY;
84
85     if (key == AFSREG_NULL_KEY) {
86         /* No starting key; first path component must be predefined key.
87          * NOTE: predefined keys are always open (i.e., don't need opening).
88          */
89         const char *tokenP = subKeyName + strspn(subKeyName, "\\");
90         size_t tokenSz = strcspn(tokenP, "\\");
91
92         if (!strncmp(tokenP, "HKEY_LOCAL_MACHINE", tokenSz))
93             key = HKEY_LOCAL_MACHINE;
94         else if (!strncmp(tokenP, "HKEY_CURRENT_USER", tokenSz))
95             key = HKEY_CURRENT_USER;
96         else if (!strncmp(tokenP, "HKEY_CURRENT_CONFIG", tokenSz))
97             key = HKEY_CURRENT_CONFIG;
98         else if (!strncmp(tokenP, "HKEY_USERS", tokenSz))
99             key = HKEY_USERS;
100         else if (!strncmp(tokenP, "HKEY_CLASSES_ROOT", tokenSz))
101             key = HKEY_CLASSES_ROOT;
102         else if (!strncmp(tokenP, "HKEY_PERFORMANCE_DATA", tokenSz))
103             key = HKEY_PERFORMANCE_DATA;
104         else if (!strncmp(tokenP, "HKEY_DYN_DATA", tokenSz))
105             key = HKEY_DYN_DATA;
106         else {
107             return ERROR_INVALID_PARAMETER;
108         }
109
110         subKeyName = tokenP + tokenSz + 1;
111     }
112
113     /* open (and possibly create) sub key */
114     if (create) {
115         status = RegCreateKeyEx(key, subKeyName,
116                                 (DWORD)0, "AFS", REG_OPTION_NON_VOLATILE,
117                                 (IsWow64()?KEY_WOW64_64KEY:0)|mode, NULL, resultKeyP, &keyDisp);
118     } else {
119         status = RegOpenKeyEx(key, subKeyName, (DWORD)0, (IsWow64()?KEY_WOW64_64KEY:0)|mode, resultKeyP);
120     }
121
122     if (resultKeyDispP) {
123         *resultKeyDispP = keyDisp;
124     }
125
126     return status;
127 }
128
129
130 /*
131  * RegQueryValueAlt() -- Read data associated with a key value.
132  *     If a buffer is supplied (i.e., *dataPP is not NULL) then
133  *     the data is placed in that buffer; in this case *dataSizeP
134  *     is the buffer size on input and the data size on output.
135  *     Otherwise, if *dataPP is NULL, a data buffer is allocated
136  *     and *dataPP is set to point to that buffer.
137  *
138  *     NOTE: dataTypeP can always be NULL, and dataSizeP can be NULL
139  *           only if *dataPP is NULL.
140  */
141
142 long
143 RegQueryValueAlt(HKEY key,               /* [in] open key handle */
144                  const char *valueName,  /* [in] value name */
145                  DWORD *dataTypeP,   /* [out] type of value's data */
146                  void **dataPP,      /* [in/out] buffer for value's data */
147                  DWORD *dataSizeP)   /* [in/out] size of data (buffer) */
148 {
149     long status;
150
151     if (*dataPP) {
152         /* Use user-supplied data buffer; no real work */
153         status = RegQueryValueEx(key, valueName, NULL,
154                                  dataTypeP, *dataPP, dataSizeP);
155     } else {
156         DWORD bufType, bufSize, dwordData;
157         char *buf;
158
159         /* get size of value's data; optimize read for common REG_DWORD */
160         bufSize = sizeof(DWORD);
161
162         status = RegQueryValueEx(key, valueName, NULL,
163                                  &bufType, (void*)&dwordData, &bufSize);
164
165         if (status == ERROR_SUCCESS || status == ERROR_MORE_DATA) {
166             /* allocate buffer for value's data */
167             buf = malloc(bufSize);
168
169             if (!buf) {
170                 status = ERROR_NOT_ENOUGH_MEMORY;
171             } else {
172                 if (status == ERROR_SUCCESS) {
173                     /* data fit in DWORD buffer; don't read registry again */
174                     memcpy(buf, &dwordData, bufSize);
175                 } else {
176                     /* data did not fit in DWORD buffer; read registry again */
177                     status = RegQueryValueEx(key, valueName, NULL,
178                                              &bufType, buf, &bufSize);
179                 }
180
181                 if (status == ERROR_SUCCESS) {
182                     /* return requested results */
183                     *dataPP = buf;
184                     if (dataTypeP) *dataTypeP = bufType;
185                     if (dataSizeP) *dataSizeP = bufSize;
186                 } else {
187                     /* registry read failed */
188                     free(buf);
189                 }
190             }
191         }
192     }
193
194     return status;
195 }
196
197
198
199 /*
200  * RegEnumKeyAlt() -- Enumerate subkey names of specified key.
201  *     Subkey names are returned in a single multistring (REG_MULTI_SZ).
202  *     If the key has no subkeys, then *subkeyNames is set to NULL.
203  *
204  *     Note:  A multistring is a char buffer containing individual strings
205  *     separated by a '\0' char with the last string being followed by
206  *     two '\0' chars.
207  */
208
209 long
210 RegEnumKeyAlt(HKEY key,            /* [in] open key handle */
211               char **subkeyNames)  /* [out] subkey name buf (multistring) */
212 {
213     long status;
214     DWORD skCount, skNameLenMax;
215     char *skNameBuf = NULL;
216
217     status = RegQueryInfoKey(key,
218                              NULL, NULL, NULL,
219                              &skCount, &skNameLenMax,
220                              NULL, NULL, NULL, NULL, NULL, NULL);
221
222     if (status == ERROR_SUCCESS && skCount > 0) {
223         skNameBuf = malloc((skCount * (skNameLenMax + 1)) + 1);
224
225         if (!skNameBuf) {
226             status = ERROR_NOT_ENOUGH_MEMORY;
227         } else {
228             DWORD i, skFillLen;
229             char *skBufP = skNameBuf;
230
231             for (i = 0; i < skCount && status == ERROR_SUCCESS; i++) {
232                 skFillLen = skNameLenMax + 1;
233                 status = RegEnumKeyEx(key, i, skBufP, &skFillLen,
234                                       NULL, NULL, NULL, NULL);
235                 if (status == ERROR_SUCCESS) {
236                     skBufP += skFillLen + 1;
237                 }
238             }
239             *skBufP = '\0';
240
241             if (status != ERROR_SUCCESS) {
242                 free(skNameBuf);
243                 skNameBuf = NULL;
244             }
245         }
246     }
247
248     *subkeyNames = skNameBuf;
249     return status;
250 }
251
252
253 /*
254  * RegDeleteKeyAlt() -- Delete named subkey and all its subkeys and values.
255  *
256  *    This is just a recursive version of RegDeleteKey(), which does not
257  *    recurse on NT (though it does on Win95/98).
258  */
259 long
260 RegDeleteKeyAlt(HKEY key,
261                 const char *subKeyName)
262 {
263     long status;
264
265     status = RegDeleteKey(key, subKeyName);
266
267     if (status != ERROR_SUCCESS) {
268         /* determine if delete failed due to subkeys */
269         HKEY subKey;
270
271         status = RegOpenKeyEx(key, subKeyName, 0, (IsWow64()?KEY_WOW64_64KEY:0)|KEY_ALL_ACCESS, &subKey);
272         if (status == ERROR_SUCCESS) {
273             char *keyEnum;
274
275             status = RegEnumKeyAlt(subKey, &keyEnum);
276             if (status == ERROR_SUCCESS && keyEnum != NULL) {
277                 /* subkeys found; try to delete each; ignore errors */
278                 char *keyEnumName;
279
280                 for (keyEnumName = keyEnum;
281                      *keyEnumName != '\0';
282                      keyEnumName += strlen(keyEnumName) + 1) {
283                     (void) RegDeleteKeyAlt(subKey, keyEnumName);
284                 }
285                 free(keyEnum);
286             }
287             (void) RegCloseKey(subKey);
288         }
289
290         /* try delete again */
291         status = RegDeleteKey(key, subKeyName);
292     }
293     return status;
294 }
295
296
297 /*
298  * RegDeleteEntryAlt() -- delete named key or value; if key, then all subkeys
299  *     and values are removed; entryName must be in canonical path format.
300  */
301 long
302 RegDeleteEntryAlt(const char *entryName, regentry_t entryType)
303 {
304     long status = ERROR_SUCCESS;
305     char *entryBuf = strdup(entryName);
306
307     if (entryBuf == NULL) {
308         status = ERROR_NOT_ENOUGH_MEMORY;
309     } else {
310         char *keyPath = entryBuf;
311         char *entryName = strrchr(entryBuf, '\\');
312
313         if (entryName == NULL) {
314             status = ERROR_INVALID_PARAMETER;
315         } else {
316             HKEY key;
317
318             *entryName = '\0';
319             entryName++;
320
321             status = RegOpenKeyAlt(AFSREG_NULL_KEY,
322                                    keyPath, KEY_ALL_ACCESS, 0, &key, NULL);
323             if (status == ERROR_SUCCESS) {
324                 if (entryType == REGENTRY_KEY) {
325                     status = RegDeleteKeyAlt(key, entryName);
326                 } else if (entryType == REGENTRY_VALUE) {
327                     status = RegDeleteValue(key, entryName);
328                 } else {
329                     status = ERROR_INVALID_PARAMETER;
330                 }
331                 (void) RegCloseKey(key);
332             }
333         }
334         free(entryBuf);
335     }
336     return status;
337 }
338
339
340 /*
341  * RegDupKeyAlt() -- duplicate sourceKey as targetKey; both sourceKey and
342  *     targetKey must be in canonical path format.
343  *
344  *     NOTE: if targetKey already exists it will be replaced.
345  */
346 long
347 RegDupKeyAlt(const char *sourceKey, const char *targetKey)
348 {
349     long status;
350
351     /* delete target key if extant */
352     status = RegDeleteEntryAlt(targetKey, REGENTRY_KEY);
353
354     if (status == ERROR_SUCCESS || status == ERROR_FILE_NOT_FOUND) {
355         status = CopyKey(sourceKey, targetKey);
356
357         if (status != ERROR_SUCCESS) {
358             /* clean-up partial duplication */
359             (void) RegDeleteEntryAlt(targetKey, REGENTRY_KEY);
360         }
361     }
362     return status;
363 }
364
365
366
367 /* ----------------------- local functions ----------------------- */
368
369
370 /*
371  * CopyKey() -- worker function implementing RegDupKeyAlt().
372  *
373  *     Note: - assumes target does not exist (i.e., deleted by RegDupKeyAlt())
374  *           - no cleanup on failure (i.e., assumes done by RegDupKeyAlt())
375  */
376 static long
377 CopyKey(const char *sourceKey, const char *targetKey)
378 {
379     long status;
380     HKEY srcKey, dupKey;
381
382     /* open source key */
383     status = RegOpenKeyAlt(AFSREG_NULL_KEY, sourceKey,
384                            KEY_READ, 0, &srcKey, NULL);
385     if (status == ERROR_SUCCESS) {
386         /* create target key */
387         status = RegOpenKeyAlt(AFSREG_NULL_KEY, targetKey,
388                                KEY_ALL_ACCESS, 1 /* create */, &dupKey, NULL);
389         if (status == ERROR_SUCCESS) {
390             /* copy values and their data from source to target */
391             status = CopyValues(srcKey, dupKey);
392
393             if (status == ERROR_SUCCESS) {
394                 /* copy subkeys from source to target */
395                 status = CopySubkeys(sourceKey, srcKey, targetKey, dupKey);
396             }
397             (void) RegCloseKey(dupKey);
398         }
399         (void) RegCloseKey(srcKey);
400     }
401     return status;
402 }
403
404
405 /*
406  * CopyValues() -- copy values and their data from srcKey to dupKey
407  */
408 static long
409 CopyValues(HKEY srcKey, HKEY dupKey)
410 {
411     long status;
412     DWORD valCount, valNameLenMax, valDataLenMax;
413
414     status = RegQueryInfoKey(srcKey,
415                              NULL, NULL, NULL, NULL, NULL, NULL,
416                              &valCount, &valNameLenMax, &valDataLenMax,
417                              NULL, NULL);
418
419     if (status == ERROR_SUCCESS && valCount > 0) {
420         char *valBuffer = malloc(valNameLenMax + 1 + valDataLenMax);
421
422         if (valBuffer == NULL) {
423             status = ERROR_NOT_ENOUGH_MEMORY;
424         } else {
425             DWORD i;
426             char *valName = valBuffer;
427             char *valData = valBuffer + (valNameLenMax + 1);
428
429             for (i = 0; i < valCount && status == ERROR_SUCCESS; i++) {
430                 DWORD valNameFillLen = valNameLenMax + 1;
431                 DWORD valDataFillLen = valDataLenMax;
432                 DWORD valDataType;
433
434                 status = RegEnumValue(srcKey, i,
435                                       valName, &valNameFillLen,
436                                       NULL,
437                                       &valDataType,
438                                       valData, &valDataFillLen);
439
440                 if (status == ERROR_SUCCESS) {
441                     status = RegSetValueEx(dupKey,
442                                            valName, 0,
443                                            valDataType,
444                                            valData, valDataFillLen);
445                 }
446             }
447             free(valBuffer);
448         }
449     }
450     return status;
451 }
452
453
454 /*
455  * CopySubkeys() -- copy subkeys from srcKey to dupKey
456  *
457  *     Note - srcName and dupName are the canonical path names of the
458  *            open keys srcKey and dupKey, respectively.
459  */
460 static long
461 CopySubkeys(const char *srcName, HKEY srcKey, const char *dupName, HKEY dupKey)
462 {
463     long status;
464     char *skEnum;
465
466     status = RegEnumKeyAlt(srcKey, &skEnum);
467
468     if (status == ERROR_SUCCESS && skEnum != NULL) {
469         char *skEnumName, *skNameBuf;
470         size_t skSrcNameMax, skDupNameMax, skNameMax;
471
472         skNameMax = 0;
473         skEnumName = skEnum;
474         while (*skEnumName != '\0') {
475             size_t skNameLen = strlen(skEnumName);
476             skNameMax = max(skNameMax, skNameLen);
477
478             skEnumName += skNameLen + 1;
479         }
480
481         skSrcNameMax = strlen(srcName) + 1 + skNameMax + 1;
482         skDupNameMax = strlen(dupName) + 1 + skNameMax + 1;
483
484         skNameBuf = malloc(skSrcNameMax + skDupNameMax);
485         if (skNameBuf == NULL) {
486             status = ERROR_NOT_ENOUGH_MEMORY;
487         } else {
488             char *skSrcName = skNameBuf;
489             char *skDupName = skNameBuf + skSrcNameMax;
490
491             for (skEnumName = skEnum;
492                  *skEnumName != '\0' && status == ERROR_SUCCESS;
493                  skEnumName += strlen(skEnumName) + 1) {
494                 sprintf(skSrcName, "%s\\%s", srcName, skEnumName);
495                 sprintf(skDupName, "%s\\%s", dupName, skEnumName);
496
497                 status = CopyKey(skSrcName, skDupName);
498             }
499             free(skNameBuf);
500         }
501         free(skEnum);
502     }
503     return status;
504 }