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