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