windows-get-cellinfo-afsdb-20070702
[openafs.git] / src / auth / cellconfig.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
13 RCSID
14     ("$Header$");
15
16 #include <afs/stds.h>
17 #include <afs/pthread_glock.h>
18 #ifdef UKERNEL
19 #include "afs/sysincludes.h"
20 #include "afsincludes.h"
21 #else /* UKERNEL */
22 #include <sys/types.h>
23 #ifdef AFS_NT40_ENV
24 #include <winsock2.h>
25 #include <sys/utime.h>
26 #include <io.h>
27 #include <WINNT/afssw.h>
28 #else
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <netdb.h>
32 #include <sys/file.h>
33 #include <sys/time.h>
34 #ifdef AFS_AFSDB_ENV
35 #include <arpa/nameser.h>
36 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
37 #include <arpa/nameser_compat.h>
38 #endif
39 #include <resolv.h>
40 #endif /* AFS_AFSDB_ENV */
41 #endif /* AFS_NT40_ENV */
42 #include <afs/afsint.h>
43 #include <errno.h>
44 #include <ctype.h>
45 #include <time.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <sys/stat.h>
49 #include <fcntl.h>
50 #ifdef HAVE_STRING_H
51 #include <string.h>
52 #else
53 #ifdef HAVE_STRINGS_H
54 #include <strings.h>
55 #endif
56 #endif
57 #ifdef HAVE_UNISTD_H
58 #include <unistd.h>
59 #endif
60 #endif /* UKERNEL */
61 #include <afs/afsutil.h>
62 #include "cellconfig.h"
63 #include "keys.h"
64 #ifdef AFS_NT40_ENV
65 #ifdef AFS_AFSDB_ENV
66 /* cm_dns.h depends on cellconfig.h */
67 #include <cm_dns.h>
68 #endif /* AFS_AFSDB_ENV */
69 #endif
70 static struct afsconf_servPair serviceTable[] = {
71     {"afs", 7000,},
72     {"afscb", 7001,},
73     {"afsprot", 7002,},
74     {"afsvldb", 7003,},
75     {"afskauth", 7004,},
76     {"afsvol", 7005,},
77     {"afserror", 7006,},
78     {"afsnanny", 7007,},
79     {"afsupdate", 7008,},
80     {"afsrmtsys", 7009,},
81     {"afsres", 7010,},          /* residency database for MR-AFS */
82     {"afsremio", 7011,},        /* remote I/O interface for MR-AFS */
83     {0, 0}                      /* insert new services before this spot */
84 };
85
86 /* Prototypes */
87 static afs_int32 afsconf_FindService(register const char *aname);
88 static int TrimLine(char *abuffer, int abufsize);
89 #ifdef AFS_NT40_ENV
90 static int IsClientConfigDirectory(const char *path);
91 static int GetCellNT(struct afsconf_dir *adir);
92 #endif
93 static int afsconf_Check(register struct afsconf_dir *adir);
94 static int afsconf_Touch(register struct afsconf_dir *adir);
95 static int GetCellUnix(struct afsconf_dir *adir);
96 static int afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
97                                 char clones[]);
98 static int ParseHostLine(char *aline, register struct sockaddr_in *addr,
99                          char *aname, char *aclone);
100 static int ParseCellLine(register char *aline, register char *aname,
101                          register char *alname);
102 static int afsconf_CloseInternal(register struct afsconf_dir *adir);
103 static int afsconf_Reopen(register struct afsconf_dir *adir);
104 static int SaveKeys(struct afsconf_dir *adir);
105
106 #ifndef T_AFSDB
107 #define T_AFSDB 18              /* per RFC1183 section 1 */
108 #endif
109
110 /*
111  * Basic Rule: we touch "<AFSCONF_DIR>/CellServDB" every time we change anything, so
112  * our code can tell if there is new info in the key files, the cell server db
113  * files or any of the other files (and reopen the thing) if the date on
114  * CellServDB changes.
115  */
116
117 #if defined(AFS_SUN5_ENV) && !defined(__sparcv9)
118 /* Solaris through 10 in 32 bit mode will return EMFILE if fopen can't
119    get an fd <= 255. We allow the fileserver to claim more fds than that.
120    This has always been a problem since pr_Initialize would have the same
121    issue, but hpr_Initialize makes it more likely that we would see this. 
122    Work around it. This is not generic. It's coded with the needs of
123    afsconf_* in mind only.
124
125    http://www.opensolaris.org/os/community/onnv/flag-days/pages/2006042001/
126 */
127
128 #define BUFFER 4096
129
130 struct afsconf_iobuffer {
131     int _file;
132     char *buffer;
133     char *ptr;
134     char *endptr;
135 };
136
137 typedef struct afsconf_iobuffer afsconf_FILE;
138
139 static afsconf_FILE *
140 afsconf_fopen(const char *fname, const char *fmode)
141 {
142     int fd;
143     afsconf_FILE *iop;
144     
145     if ((fd = open(fname, O_RDONLY)) == -1) {
146         return NULL;
147     }
148     
149     iop = malloc(sizeof(struct afsconf_iobuffer));
150     if (iop == NULL) {
151         (void) close(fd);
152         errno = ENOMEM;
153         return NULL;
154     }
155     iop->_file = fd;
156     iop->buffer = malloc(BUFFER);
157     if (iop->buffer == NULL) {
158         (void) close(fd);
159         free((void *) iop);
160         errno = ENOMEM;
161         return NULL;
162     }
163     iop->ptr = iop->buffer;
164     iop->endptr = iop->buffer;
165     return iop;
166 }
167
168 static int
169 afsconf_fclose(afsconf_FILE *iop)
170 {
171     if (iop == NULL) {
172         return 0;
173     }
174     close(iop->_file);
175     free((void *)iop->buffer);
176     free((void *)iop);
177     return 0;
178 }
179
180 static char *
181 afsconf_fgets(char *s, int n, afsconf_FILE *iop)
182 {
183     char *p;
184     
185     p = s;
186     for (;;) {
187         char c;
188         
189         if (iop->ptr == iop->endptr) {
190             ssize_t len;
191             
192             if ((len = read(iop->_file, (void *)iop->buffer, BUFFER)) == -1) {
193                 return NULL;
194             }
195             if (len == 0) {
196                 *p = 0;
197                 if (s == p) {
198                     return NULL;
199                 }
200                 return s;
201             }
202             iop->ptr = iop->buffer;
203             iop->endptr = iop->buffer + len;
204         }
205         c = *iop->ptr++;
206         *p++ = c;
207         if ((p - s) == (n - 1)) {
208             *p = 0;
209             return s;
210         }
211         if (c == '\n') {
212             *p = 0;
213             return s;
214         }
215     }
216 }
217 #define fopen afsconf_fopen
218 #define fclose afsconf_fclose
219 #define fgets afsconf_fgets
220 #else
221 #define afsconf_FILE FILE
222 #endif /* AFS_SUN5_ENV && ! __sparcv9 */
223
224 /* return port number in network byte order in the low 16 bits of a long; return -1 if not found */
225 static afs_int32
226 afsconf_FindService(register const char *aname)
227 {
228     /* lookup a service name */
229     struct servent *ts;
230     register struct afsconf_servPair *tsp;
231
232 #if     defined(AFS_OSF_ENV) 
233     ts = getservbyname(aname, "");
234 #else
235     ts = getservbyname(aname, NULL);
236 #endif
237     if (ts) {
238         /* we found it in /etc/services, so we use this value */
239         return ts->s_port;      /* already in network byte order */
240     }
241
242     /* not found in /etc/services, see if it is one of ours */
243     for (tsp = serviceTable;; tsp++) {
244         if (tsp->name == NULL)
245             return -1;
246         if (!strcmp(tsp->name, aname))
247             return htons(tsp->port);
248     }
249 }
250
251 static int
252 TrimLine(char *abuffer, int abufsize)
253 {
254     char tbuffer[256];
255     register char *tp;
256     register int tc;
257
258     tp = abuffer;
259     while ((tc = *tp)) {
260         if (!isspace(tc))
261             break;
262         tp++;
263     }
264     strlcpy(tbuffer, tp, sizeof tbuffer);
265     strlcpy(abuffer, tbuffer, abufsize);
266     return 0;
267 }
268
269 #ifdef AFS_NT40_ENV
270 /*
271  * IsClientConfigDirectory() -- determine if path matches well-known
272  *     client configuration directory.
273  */
274 static int
275 IsClientConfigDirectory(const char *path)
276 {
277     const char *cdir = AFSDIR_CLIENT_ETC_DIRPATH;
278     int i;
279
280     for (i = 0; cdir[i] != '\0' && path[i] != '\0'; i++) {
281         int cc = tolower(cdir[i]);
282         int pc = tolower(path[i]);
283
284         if (cc == '\\') {
285             cc = '/';
286         }
287         if (pc == '\\') {
288             pc = '/';
289         }
290         if (cc != pc) {
291             return 0;
292         }
293     }
294
295     /* hit end of one or both; allow mismatch in existence of trailing slash */
296     if (cdir[i] != '\0') {
297         if ((cdir[i] != '\\' && cdir[i] != '/') || (cdir[i + 1] != '\0')) {
298             return 0;
299         }
300     }
301     if (path[i] != '\0') {
302         if ((path[i] != '\\' && path[i] != '/') || (path[i + 1] != '\0')) {
303             return 0;
304         }
305     }
306     return 1;
307 }
308 #endif /* AFS_NT40_ENV */
309
310
311 static int
312 afsconf_Check(register struct afsconf_dir *adir)
313 {
314     char tbuffer[256], *p;
315     struct stat tstat;
316     register afs_int32 code;
317
318 #ifdef AFS_NT40_ENV
319     /* NT client CellServDB has different file name than NT server or Unix */
320     if (IsClientConfigDirectory(adir->name)) {
321         if (!afssw_GetClientCellServDBDir(&p)) {
322             strcompose(tbuffer, sizeof(tbuffer), p, "/",
323                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
324             free(p);
325         } else {
326             int len;
327             strncpy(tbuffer, adir->name, sizeof(tbuffer));
328             len = (int)strlen(tbuffer);
329             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
330                 strncat(tbuffer, "\\", sizeof(tbuffer));
331             }
332             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
333                     sizeof(tbuffer));
334             tbuffer[sizeof(tbuffer) - 1] = '\0';
335         }
336     } else {
337         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
338                    NULL);
339     }
340 #else
341     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
342 #endif /* AFS_NT40_ENV */
343
344     code = stat(tbuffer, &tstat);
345     if (code < 0) {
346         return code;
347     }
348     /* did file change? */
349     if (tstat.st_mtime == adir->timeRead) {
350         return 0;
351     }
352     /* otherwise file has changed, so reopen it */
353     return afsconf_Reopen(adir);
354 }
355
356 /* set modtime on file */
357 static int
358 afsconf_Touch(register struct afsconf_dir *adir)
359 {
360     char tbuffer[256], *p;
361 #ifndef AFS_NT40_ENV
362     struct timeval tvp[2];
363 #endif
364
365     adir->timeRead = 0;         /* just in case */
366
367 #ifdef AFS_NT40_ENV
368     /* NT client CellServDB has different file name than NT server or Unix */
369
370     if (IsClientConfigDirectory(adir->name)) {
371         if (!afssw_GetClientCellServDBDir(&p)) {
372             strcompose(tbuffer, sizeof(tbuffer), p, "/",
373                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
374             free(p);
375         } else {
376             int len = (int)strlen(tbuffer);
377             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
378                 strncat(tbuffer, "\\", sizeof(tbuffer));
379             }
380             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
381                     sizeof(tbuffer));
382             tbuffer[sizeof(tbuffer) - 1] = '\0';
383         }
384     } else {
385         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
386                    NULL);
387     }
388
389     return _utime(tbuffer, NULL);
390
391 #else
392     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
393     gettimeofday(&tvp[0], NULL);
394     tvp[1] = tvp[0];
395     return utimes(tbuffer, tvp);
396 #endif /* AFS_NT40_ENV */
397 }
398
399 struct afsconf_dir *
400 afsconf_Open(register const char *adir)
401 {
402     register struct afsconf_dir *tdir;
403     register afs_int32 code;
404
405     LOCK_GLOBAL_MUTEX;
406     /* zero structure and fill in name; rest is done by internal routine */
407     tdir = (struct afsconf_dir *)malloc(sizeof(struct afsconf_dir));
408     memset(tdir, 0, sizeof(struct afsconf_dir));
409     tdir->name = strdup(adir);
410
411     code = afsconf_OpenInternal(tdir, 0, 0);
412     if (code) {
413         char *afsconf_path, afs_confdir[128];
414
415         free(tdir->name);
416         /* Check global place only when local Open failed for whatever reason */
417         if (!(afsconf_path = getenv("AFSCONF"))) {
418             /* The "AFSCONF" environment (or contents of "/.AFSCONF") will be typically set to something like "/afs/<cell>/common/etc" where, by convention, the default files for "ThisCell" and "CellServDB" will reside; note that a major drawback is that a given afs client on that cell may NOT contain the same contents... */
419             char *home_dir;
420             afsconf_FILE *fp;
421             size_t len;
422
423             if (!(home_dir = getenv("HOME"))) {
424                 /* Our last chance is the "/.AFSCONF" file */
425                 fp = fopen("/.AFSCONF", "r");
426                 if (fp == 0) {
427                     free(tdir);
428                     UNLOCK_GLOBAL_MUTEX;
429                     return (struct afsconf_dir *)0;
430                 }
431                 fgets(afs_confdir, 128, fp);
432                 fclose(fp);
433             } else {
434                 char pathname[256];
435
436                 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
437                 fp = fopen(pathname, "r");
438                 if (fp == 0) {
439                     /* Our last chance is the "/.AFSCONF" file */
440                     fp = fopen("/.AFSCONF", "r");
441                     if (fp == 0) {
442                         free(tdir);
443                         UNLOCK_GLOBAL_MUTEX;
444                         return (struct afsconf_dir *)0;
445                     }
446                 }
447                 fgets(afs_confdir, 128, fp);
448                 fclose(fp);
449             }
450             len = strlen(afs_confdir);
451             if (len == 0) {
452                 free(tdir);
453                 UNLOCK_GLOBAL_MUTEX;
454                 return (struct afsconf_dir *)0;
455             }
456             if (afs_confdir[len - 1] == '\n') {
457                 afs_confdir[len - 1] = 0;
458             }
459             afsconf_path = afs_confdir;
460         }
461         tdir->name = strdup(afsconf_path);
462         code = afsconf_OpenInternal(tdir, 0, 0);
463         if (code) {
464             free(tdir->name);
465             free(tdir);
466             UNLOCK_GLOBAL_MUTEX;
467             return (struct afsconf_dir *)0;
468         }
469     }
470     UNLOCK_GLOBAL_MUTEX;
471     return tdir;
472 }
473
474 static int
475 GetCellUnix(struct afsconf_dir *adir)
476 {
477     char *rc;
478     char tbuffer[256];
479     char *start, *p;
480     afsconf_FILE *fp;
481     
482     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
483     fp = fopen(tbuffer, "r");
484     if (fp == 0) {
485         return -1;
486     }
487     rc = fgets(tbuffer, 256, fp);
488     fclose(fp);
489     if (rc == NULL)
490         return -1;
491
492     start = tbuffer;
493     while (*start != '\0' && isspace(*start))
494         start++;
495     p = start;
496     while (*p != '\0' && !isspace(*p))
497         p++;
498     *p = '\0';
499     if (*start == '\0')
500         return -1;
501
502     adir->cellName = strdup(start);
503     return 0;
504 }
505
506
507 #ifdef AFS_NT40_ENV
508 static int
509 GetCellNT(struct afsconf_dir *adir)
510 {
511     if (IsClientConfigDirectory(adir->name)) {
512         /* NT client config dir; ThisCell is in registry (no file). */
513         return afssw_GetClientCellName(&adir->cellName);
514     } else {
515         /* NT server config dir; works just like Unix */
516         return GetCellUnix(adir);
517     }
518 }
519 #endif /* AFS_NT40_ENV */
520
521
522 static int
523 afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
524                      char clones[])
525 {
526     afsconf_FILE *tf;
527     register char *tp, *bp;
528     register struct afsconf_entry *curEntry;
529     struct afsconf_aliasentry *curAlias;
530     register afs_int32 code;
531     afs_int32 i;
532     char tbuffer[256], tbuf1[256];
533     struct stat tstat;
534
535     /* figure out the local cell name */
536 #ifdef AFS_NT40_ENV
537     i = GetCellNT(adir);
538 #else
539     i = GetCellUnix(adir);
540 #endif
541
542 #ifndef AFS_FREELANCE_CLIENT    /* no local cell not fatal in freelance */
543     if (i) {
544         return i;
545     }
546 #endif
547
548     /* now parse the individual lines */
549     curEntry = 0;
550
551 #ifdef AFS_NT40_ENV
552     /* NT client/server have a CellServDB that is the same format as Unix.
553      * However, the NT client uses a different file name
554      */
555     if (IsClientConfigDirectory(adir->name)) {
556         /* NT client config dir */
557         char *p;
558         if (!afssw_GetClientCellServDBDir(&p)) {
559             strcompose(tbuffer, sizeof(tbuffer), p, "/",
560                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
561             free(p);
562         } else {
563             int len;
564             strncpy(tbuffer, adir->name, sizeof(tbuffer));
565             len = (int)strlen(tbuffer);
566             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
567                 strncat(tbuffer, "\\", sizeof(tbuffer));
568             }
569             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
570                     sizeof(tbuffer));
571             tbuffer[sizeof(tbuffer) - 1] = '\0';
572         }
573     } else {
574         /* NT server config dir */
575         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
576                    NULL);
577     }
578 #else
579     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
580 #endif /* AFS_NT40_ENV */
581
582     if (!stat(tbuffer, &tstat)) {
583         adir->timeRead = tstat.st_mtime;
584     } else {
585         adir->timeRead = 0;
586     }
587
588     strlcpy(tbuf1, tbuffer, sizeof tbuf1);
589     tf = fopen(tbuffer, "r");
590     if (!tf) {
591         return -1;
592     }
593     while (1) {
594         tp = fgets(tbuffer, sizeof(tbuffer), tf);
595         if (!tp)
596             break;
597         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
598         if (tbuffer[0] == 0 || tbuffer[0] == '\n')
599             continue;           /* empty line */
600         if (tbuffer[0] == '>') {
601             char linkedcell[MAXCELLCHARS];
602             /* start new cell item */
603             if (curEntry) {
604                 /* thread this guy on the list */
605                 curEntry->next = adir->entries;
606                 adir->entries = curEntry;
607                 curEntry = 0;
608             }
609             curEntry =
610                 (struct afsconf_entry *)malloc(sizeof(struct afsconf_entry));
611             memset(curEntry, 0, sizeof(struct afsconf_entry));
612             code =
613                 ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
614             if (code) {
615                 afsconf_CloseInternal(adir);
616                 fclose(tf);
617                 free(curEntry);
618                 return -1;
619             }
620             if (linkedcell[0] != '\0')
621                 curEntry->cellInfo.linkedCell = strdup(linkedcell);
622         } else {
623             /* new host in the current cell */
624             if (!curEntry) {
625                 afsconf_CloseInternal(adir);
626                 fclose(tf);
627                 return -1;
628             }
629             i = curEntry->cellInfo.numServers;
630             if (cell && !strcmp(cell, curEntry->cellInfo.name))
631                 code =
632                     ParseHostLine(tbuffer, &curEntry->cellInfo.hostAddr[i],
633                                   curEntry->cellInfo.hostName[i], &clones[i]);
634             else
635                 code =
636                     ParseHostLine(tbuffer, &curEntry->cellInfo.hostAddr[i],
637                                   curEntry->cellInfo.hostName[i], 0);
638             if (code) {
639                 if (code == AFSCONF_SYNTAX) {
640                     for (bp = tbuffer; *bp != '\n'; bp++) {     /* Take out the <cr> from the buffer */
641                         if (!*bp)
642                             break;
643                     }
644                     *bp = '\0';
645                     fprintf(stderr,
646                             "Can't properly parse host line \"%s\" in configuration file %s\n",
647                             tbuffer, tbuf1);
648                 }
649                 free(curEntry);
650                 fclose(tf);
651                 afsconf_CloseInternal(adir);
652                 return -1;
653             }
654             curEntry->cellInfo.numServers = ++i;
655         }
656     }
657     fclose(tf);                 /* close the file now */
658
659     /* end the last partially-completed cell */
660     if (curEntry) {
661         curEntry->next = adir->entries;
662         adir->entries = curEntry;
663     }
664
665     /* Read in the alias list */
666     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLALIAS_FILE, NULL);
667
668     tf = fopen(tbuffer, "r");
669     while (tf) {
670         char *aliasPtr;
671
672         tp = fgets(tbuffer, sizeof(tbuffer), tf);
673         if (!tp)
674             break;
675         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
676
677         if (tbuffer[0] == '\0' || tbuffer[0] == '\n' || tbuffer[0] == '#')
678             continue;           /* empty line */
679
680         tp = tbuffer;
681         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t')
682             tp++;
683         if (tp[0] == '\0')
684             continue;           /* invalid line */
685
686         while (tp[0] != '\0' && (tp[0] == ' ' || tp[0] == '\t'))
687             0[tp++] = '\0';
688         if (tp[0] == '\0')
689             continue;           /* invalid line */
690
691         aliasPtr = tp;
692         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t' && tp[0] != '\r'
693                && tp[0] != '\n')
694             tp++;
695         tp[0] = '\0';
696
697         curAlias = malloc(sizeof(*curAlias));
698         memset(curAlias, 0, sizeof(*curAlias));
699
700         strlcpy(curAlias->aliasInfo.aliasName, aliasPtr, sizeof curAlias->aliasInfo.aliasName);
701         strlcpy(curAlias->aliasInfo.realName, tbuffer, sizeof curAlias->aliasInfo.realName);
702
703         curAlias->next = adir->alias_entries;
704         adir->alias_entries = curAlias;
705     }
706
707     if (tf != NULL)
708         fclose(tf);
709     /* now read the fs keys, if possible */
710     adir->keystr = (struct afsconf_keys *)0;
711     afsconf_IntGetKeys(adir);
712
713     return 0;
714 }
715
716 /* parse a line of the form
717  *"128.2.1.3   #hostname" or
718  *"[128.2.1.3]  #hostname" for clones
719  * into the appropriate pieces.  
720  */
721 static int
722 ParseHostLine(char *aline, register struct sockaddr_in *addr, char *aname,
723               char *aclone)
724 {
725     int c1, c2, c3, c4;
726     register afs_int32 code;
727     register char *tp;
728
729     if (*aline == '[') {
730         if (aclone)
731             *aclone = 1;
732         /* FIXME: length of aname unknown here */
733         code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
734     } else {
735         if (aclone)
736             *aclone = 0;
737         /* FIXME: length of aname unknown here */
738         code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
739     }
740     if (code != 5)
741         return AFSCONF_SYNTAX;
742     addr->sin_family = AF_INET;
743     addr->sin_port = 0;
744 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
745     addr->sin_len = sizeof(struct sockaddr_in);
746 #endif
747     tp = (char *)&addr->sin_addr;
748     *tp++ = c1;
749     *tp++ = c2;
750     *tp++ = c3;
751     *tp++ = c4;
752     return 0;
753 }
754
755 /* parse a line of the form
756  * ">cellname [linkedcellname] [#comments]"
757  * into the appropriate pieces.
758  */
759 static int
760 ParseCellLine(register char *aline, register char *aname,
761               register char *alname)
762 {
763     register int code;
764     /* FIXME: length of aname, alname unknown here */
765     code = sscanf(aline, ">%s %s", aname, alname);
766     if (code == 1)
767         *alname = '\0';
768     if (code == 2) {
769         if (*alname == '#') {
770             *alname = '\0';
771         }
772     }
773     return (code > 0 ? 0 : AFSCONF_SYNTAX);
774 }
775
776 /* call aproc(entry, arock, adir) for all cells.  Proc must return 0, or we'll stop early and return the code it returns */
777 int
778 afsconf_CellApply(struct afsconf_dir *adir,
779                   int (*aproc) (struct afsconf_cell * cell, char *arock,
780                                 struct afsconf_dir * dir), char *arock)
781 {
782     register struct afsconf_entry *tde;
783     register afs_int32 code;
784     LOCK_GLOBAL_MUTEX;
785     for (tde = adir->entries; tde; tde = tde->next) {
786         code = (*aproc) (&tde->cellInfo, arock, adir);
787         if (code) {
788             UNLOCK_GLOBAL_MUTEX;
789             return code;
790         }
791     }
792     UNLOCK_GLOBAL_MUTEX;
793     return 0;
794 }
795
796 /* call aproc(entry, arock, adir) for all cell aliases.
797  * Proc must return 0, or we'll stop early and return the code it returns
798  */
799 int
800 afsconf_CellAliasApply(struct afsconf_dir *adir,
801                        int (*aproc) (struct afsconf_cellalias * alias,
802                                      char *arock, struct afsconf_dir * dir),
803                        char *arock)
804 {
805     register struct afsconf_aliasentry *tde;
806     register afs_int32 code;
807     LOCK_GLOBAL_MUTEX;
808     for (tde = adir->alias_entries; tde; tde = tde->next) {
809         code = (*aproc) (&tde->aliasInfo, arock, adir);
810         if (code) {
811             UNLOCK_GLOBAL_MUTEX;
812             return code;
813         }
814     }
815     UNLOCK_GLOBAL_MUTEX;
816     return 0;
817 }
818
819 afs_int32 afsconf_SawCell = 0;
820
821 int
822 afsconf_GetExtendedCellInfo(struct afsconf_dir *adir, char *acellName,
823                             char *aservice, struct afsconf_cell *acellInfo,
824                             char clones[])
825 {
826     afs_int32 code;
827     char *cell;
828
829     code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
830     if (code)
831         return code;
832
833     if (acellName)
834         cell = acellName;
835     else
836         cell = (char *)&acellInfo->name;
837
838     code = afsconf_OpenInternal(adir, cell, clones);
839     return code;
840 }
841
842 #ifdef AFS_AFSDB_ENV
843 #if !defined(AFS_NT40_ENV)
844 int
845 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
846                      struct afsconf_cell *acellInfo)
847 {
848     afs_int32 code;
849     int tservice, i, len;
850     unsigned char answer[1024];
851     unsigned char *p;
852     char *dotcellname;
853     int cellnamelength;
854     char realCellName[256];
855     char host[256];
856     int server_num = 0;
857     int minttl = 0;
858     int try_init = 0;
859
860     /* The resolver isn't always MT-safe.. Perhaps this ought to be
861      * replaced with a more fine-grained lock just for the resolver
862      * operations.
863      */
864
865  retryafsdb:
866     if ( ! strchr(acellName,'.') ) {
867        cellnamelength=strlen(acellName);
868        dotcellname=malloc(cellnamelength+2);
869        memcpy(dotcellname,acellName,cellnamelength);
870        dotcellname[cellnamelength]='.';
871        dotcellname[cellnamelength+1]=0;
872        LOCK_GLOBAL_MUTEX;
873        len = res_search(dotcellname, C_IN, T_AFSDB, answer, sizeof(answer));
874        if ( len < 0 ) {
875           len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
876        }
877        UNLOCK_GLOBAL_MUTEX;
878        free(dotcellname);
879     } else {
880        LOCK_GLOBAL_MUTEX;
881        len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
882        UNLOCK_GLOBAL_MUTEX;
883     }
884     if (len < 0) {
885         if (try_init < 1) {
886             try_init++;
887             res_init();
888             goto retryafsdb;
889         }
890         return AFSCONF_NOTFOUND;
891     }
892
893     p = answer + sizeof(HEADER);        /* Skip header */
894     code = dn_expand(answer, answer + len, p, host, sizeof(host));
895     if (code < 0)
896         return AFSCONF_NOTFOUND;
897
898     p += code + QFIXEDSZ;       /* Skip name */
899
900     while (p < answer + len) {
901         int type, ttl, size;
902
903         code = dn_expand(answer, answer + len, p, host, sizeof(host));
904         if (code < 0)
905             return AFSCONF_NOTFOUND;
906
907         p += code;              /* Skip the name */
908         type = (p[0] << 8) | p[1];
909         p += 4;                 /* Skip type and class */
910         ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
911         p += 4;                 /* Skip the TTL */
912         size = (p[0] << 8) | p[1];
913         p += 2;                 /* Skip the size */
914
915         if (type == T_AFSDB) {
916             struct hostent *he;
917             short afsdb_type;
918
919             afsdb_type = (p[0] << 8) | p[1];
920             if (afsdb_type == 1) {
921                 /*
922                  * We know this is an AFSDB record for our cell, of the
923                  * right AFSDB type.  Write down the true cell name that
924                  * the resolver gave us above.
925                  */
926                 strlcpy(realCellName, host, sizeof realCellName);
927             }
928
929             code = dn_expand(answer, answer + len, p + 2, host, sizeof(host));
930             if (code < 0)
931                 return AFSCONF_NOTFOUND;
932
933             if ((afsdb_type == 1) && (server_num < MAXHOSTSPERCELL) &&
934                 /* Do we want to get TTL data for the A record as well? */
935                 (he = gethostbyname(host))) {
936                 afs_int32 ipaddr;
937                 memcpy(&ipaddr, he->h_addr, he->h_length);
938                 acellInfo->hostAddr[server_num].sin_addr.s_addr = ipaddr;
939                 strncpy(acellInfo->hostName[server_num], host,
940                         sizeof(acellInfo->hostName[server_num]));
941                 server_num++;
942
943                 if (!minttl || ttl < minttl)
944                     minttl = ttl;
945             }
946         }
947
948         p += size;
949     }
950
951     if (server_num == 0)        /* No AFSDB records */
952         return AFSCONF_NOTFOUND;
953
954     /* Convert the real cell name to lowercase */
955     for (p = (unsigned char *)realCellName; *p; p++)
956         *p = tolower(*p);
957
958     strncpy(acellInfo->name, realCellName, sizeof(acellInfo->name));
959     acellInfo->numServers = server_num;
960
961     if (aservice) {
962         tservice = afsconf_FindService(aservice);
963         if (tservice < 0)
964             return AFSCONF_NOTFOUND;    /* service not found */
965         for (i = 0; i < acellInfo->numServers; i++) {
966             acellInfo->hostAddr[i].sin_port = tservice;
967         }
968     }
969
970     acellInfo->timeout = minttl ? (time(0) + minttl) : 0;
971
972     return 0;
973 }
974 #else /* windows */
975 int
976 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
977                      struct afsconf_cell *acellInfo)
978 {
979     register afs_int32 i;
980     int tservice;
981     struct afsconf_entry DNSce;
982     afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
983     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
984     int numServers;
985     int rc;
986     int ttl;
987
988     DNSce.cellInfo.numServers = 0;
989     DNSce.next = NULL;
990     rc = getAFSServer(acellName, cellHostAddrs, cellHostNames, &numServers,
991                       &ttl);
992     /* ignore the ttl here since this code is only called by transitory programs
993      * like klog, etc. */
994     if (rc < 0)
995         return -1;
996     if (numServers == 0)
997         return -1;
998
999     for (i = 0; i < numServers; i++) {
1000         memcpy(&acellInfo->hostAddr[i].sin_addr.s_addr, &cellHostAddrs[i],
1001                sizeof(long));
1002         memcpy(acellInfo->hostName[i], cellHostNames[i], MAXHOSTCHARS);
1003         acellInfo->hostAddr[i].sin_family = AF_INET;
1004
1005         /* sin_port supplied by connection code */
1006     }
1007
1008     acellInfo->numServers = numServers;
1009     strlcpy(acellInfo->name, acellName, sizeof acellInfo->name);
1010     if (aservice) {
1011         LOCK_GLOBAL_MUTEX;
1012         tservice = afsconf_FindService(aservice);
1013         UNLOCK_GLOBAL_MUTEX;
1014         if (tservice < 0) {
1015             return AFSCONF_NOTFOUND;    /* service not found */
1016         }
1017         for (i = 0; i < acellInfo->numServers; i++) {
1018             acellInfo->hostAddr[i].sin_port = tservice;
1019         }
1020     }
1021     acellInfo->linkedCell = NULL;       /* no linked cell */
1022     acellInfo->flags = 0;
1023     return 0;
1024 }
1025 #endif /* windows */
1026 #endif /* AFS_AFSDB_ENV */
1027
1028 int
1029 afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
1030                     struct afsconf_cell *acellInfo)
1031 {
1032     register struct afsconf_entry *tce;
1033     struct afsconf_aliasentry *tcae;
1034     struct afsconf_entry *bestce;
1035     register afs_int32 i;
1036     int tservice;
1037     char *tcell;
1038     int cnLen;
1039     int ambig;
1040     char tbuffer[64];
1041
1042     LOCK_GLOBAL_MUTEX;
1043     if (adir)
1044         afsconf_Check(adir);
1045     if (acellName) {
1046         tcell = acellName;
1047         cnLen = (int)(strlen(tcell) + 1);
1048         lcstring(tcell, tcell, cnLen);
1049         afsconf_SawCell = 1;    /* will ignore the AFSCELL switch on future */
1050         /* call to afsconf_GetLocalCell: like klog  */
1051     } else {
1052         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
1053         if (i) {
1054             UNLOCK_GLOBAL_MUTEX;
1055             return i;
1056         }
1057         tcell = tbuffer;
1058     }
1059     cnLen = strlen(tcell);
1060     bestce = (struct afsconf_entry *)0;
1061     ambig = 0;
1062     if (!adir) {
1063         UNLOCK_GLOBAL_MUTEX;
1064         return 0;
1065     }
1066
1067     /* Look through the list of aliases */
1068     for (tcae = adir->alias_entries; tcae; tcae = tcae->next) {
1069         if (strcasecmp(tcae->aliasInfo.aliasName, tcell) == 0) {
1070             tcell = tcae->aliasInfo.realName;
1071             break;
1072         }
1073     }
1074
1075     for (tce = adir->entries; tce; tce = tce->next) {
1076         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
1077             /* found our cell */
1078             bestce = tce;
1079             ambig = 0;
1080             break;
1081         }
1082         if (strlen(tce->cellInfo.name) < cnLen)
1083             continue;           /* clearly wrong */
1084         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
1085             if (bestce)
1086                 ambig = 1;      /* ambiguous unless we get exact match */
1087             bestce = tce;
1088         }
1089     }
1090     if (!ambig && bestce && bestce->cellInfo.numServers) {
1091         *acellInfo = bestce->cellInfo;  /* structure assignment */
1092         if (aservice) {
1093             tservice = afsconf_FindService(aservice);
1094             if (tservice < 0) {
1095                 UNLOCK_GLOBAL_MUTEX;
1096                 return AFSCONF_NOTFOUND;        /* service not found */
1097             }
1098             for (i = 0; i < acellInfo->numServers; i++) {
1099                 acellInfo->hostAddr[i].sin_port = tservice;
1100             }
1101         }
1102         acellInfo->timeout = 0;
1103         UNLOCK_GLOBAL_MUTEX;
1104         return 0;
1105     } else {
1106         UNLOCK_GLOBAL_MUTEX;
1107 #ifdef AFS_AFSDB_ENV
1108         return afsconf_GetAfsdbInfo(tcell, aservice, acellInfo);
1109 #else
1110         return AFSCONF_NOTFOUND;
1111 #endif /* AFS_AFSDB_ENV */
1112     }
1113 }
1114
1115 int
1116 afsconf_GetLocalCell(register struct afsconf_dir *adir, char *aname,
1117                      afs_int32 alen)
1118 {
1119     static int afsconf_showcell = 0;
1120     char *afscell_path;
1121     afs_int32 code = 0;
1122
1123     LOCK_GLOBAL_MUTEX;
1124     /*
1125      * If a cell switch was specified in a command, then it should override the 
1126      * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
1127      * is set and the cell name in the adir structure is used.
1128      * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
1129      */
1130     if (!afsconf_SawCell && (afscell_path = getenv("AFSCELL"))) {
1131         if (!afsconf_showcell) {
1132             fprintf(stderr, "Note: Operation is performed on cell %s\n",
1133                     afscell_path);
1134             afsconf_showcell = 1;
1135         }
1136         strncpy(aname, afscell_path, alen);
1137     } else {
1138         afsconf_Check(adir);
1139         if (adir->cellName) {
1140             strncpy(aname, adir->cellName, alen);
1141         } else
1142             code = AFSCONF_UNKNOWN;
1143     }
1144
1145     UNLOCK_GLOBAL_MUTEX;
1146     return (code);
1147 }
1148
1149 int
1150 afsconf_Close(struct afsconf_dir *adir)
1151 {
1152     LOCK_GLOBAL_MUTEX;
1153     afsconf_CloseInternal(adir);
1154     if (adir->name)
1155         free(adir->name);
1156     free(adir);
1157     UNLOCK_GLOBAL_MUTEX;
1158     return 0;
1159 }
1160
1161 static int
1162 afsconf_CloseInternal(register struct afsconf_dir *adir)
1163 {
1164     register struct afsconf_entry *td, *nd;
1165     struct afsconf_aliasentry *ta, *na;
1166     register char *tname;
1167
1168     tname = adir->name;         /* remember name, since that's all we preserve */
1169
1170     /* free everything we can find */
1171     if (adir->cellName)
1172         free(adir->cellName);
1173     for (td = adir->entries; td; td = nd) {
1174         nd = td->next;
1175         if (td->cellInfo.linkedCell)
1176             free(td->cellInfo.linkedCell);
1177         free(td);
1178     }
1179     for (ta = adir->alias_entries; ta; ta = na) {
1180         na = ta->next;
1181         free(ta);
1182     }
1183     if (adir->keystr)
1184         free(adir->keystr);
1185
1186     /* reinit */
1187     memset(adir, 0, sizeof(struct afsconf_dir));
1188     adir->name = tname;         /* restore it */
1189     return 0;
1190 }
1191
1192 static int
1193 afsconf_Reopen(register struct afsconf_dir *adir)
1194 {
1195     register afs_int32 code;
1196     code = afsconf_CloseInternal(adir);
1197     if (code)
1198         return code;
1199     code = afsconf_OpenInternal(adir, 0, 0);
1200     return code;
1201 }
1202
1203 /* called during opening of config file */
1204 int
1205 afsconf_IntGetKeys(struct afsconf_dir *adir)
1206 {
1207     char tbuffer[256];
1208     register int fd;
1209     struct afsconf_keys *tstr;
1210     register afs_int32 code;
1211
1212 #ifdef AFS_NT40_ENV
1213     /* NT client config dir has no KeyFile; don't risk attempting open
1214      * because there might be a random file of this name if dir is shared.
1215      */
1216     if (IsClientConfigDirectory(adir->name)) {
1217         adir->keystr = ((struct afsconf_keys *)
1218                         malloc(sizeof(struct afsconf_keys)));
1219         adir->keystr->nkeys = 0;
1220         return 0;
1221     }
1222 #endif /* AFS_NT40_ENV */
1223
1224     LOCK_GLOBAL_MUTEX;
1225     /* compute the key name and other setup */
1226     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1227     tstr = (struct afsconf_keys *)malloc(sizeof(struct afsconf_keys));
1228     adir->keystr = tstr;
1229
1230     /* read key file */
1231     fd = open(tbuffer, O_RDONLY);
1232     if (fd < 0) {
1233         tstr->nkeys = 0;
1234         UNLOCK_GLOBAL_MUTEX;
1235         return 0;
1236     }
1237     code = read(fd, tstr, sizeof(struct afsconf_keys));
1238     close(fd);
1239     if (code < sizeof(afs_int32)) {
1240         tstr->nkeys = 0;
1241         UNLOCK_GLOBAL_MUTEX;
1242         return 0;
1243     }
1244
1245     /* convert key structure to host order */
1246     tstr->nkeys = ntohl(tstr->nkeys);
1247
1248     if (code < sizeof(afs_int32) + (tstr->nkeys*sizeof(struct afsconf_key))) {
1249         tstr->nkeys = 0;
1250         UNLOCK_GLOBAL_MUTEX;
1251         return 0;
1252     }
1253
1254     for (fd = 0; fd < tstr->nkeys; fd++)
1255         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
1256
1257     UNLOCK_GLOBAL_MUTEX;
1258     return 0;
1259 }
1260
1261 /* get keys structure */
1262 int
1263 afsconf_GetKeys(struct afsconf_dir *adir, struct afsconf_keys *astr)
1264 {
1265     register afs_int32 code;
1266
1267     LOCK_GLOBAL_MUTEX;
1268     code = afsconf_Check(adir);
1269     if (code) {
1270         UNLOCK_GLOBAL_MUTEX;
1271         return AFSCONF_FAILURE;
1272     }
1273     memcpy(astr, adir->keystr, sizeof(struct afsconf_keys));
1274     UNLOCK_GLOBAL_MUTEX;
1275     return 0;
1276 }
1277
1278 /* get latest key */
1279 afs_int32
1280 afsconf_GetLatestKey(struct afsconf_dir * adir, afs_int32 * avno, char *akey)
1281 {
1282     register int i;
1283     int maxa;
1284     register struct afsconf_key *tk;
1285     register afs_int32 best;
1286     struct afsconf_key *bestk;
1287     register afs_int32 code;
1288
1289     LOCK_GLOBAL_MUTEX;
1290     code = afsconf_Check(adir);
1291     if (code) {
1292         UNLOCK_GLOBAL_MUTEX;
1293         return AFSCONF_FAILURE;
1294     }
1295     maxa = adir->keystr->nkeys;
1296
1297     best = -1;                  /* highest kvno we've seen yet */
1298     bestk = (struct afsconf_key *)0;    /* ptr to structure providing best */
1299     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1300         if (tk->kvno == 999)
1301             continue;           /* skip bcrypt keys */
1302         if (tk->kvno > best) {
1303             best = tk->kvno;
1304             bestk = tk;
1305         }
1306     }
1307     if (bestk) {                /* found any  */
1308         if (akey)
1309             memcpy(akey, bestk->key, 8);        /* copy out latest key */
1310         if (avno)
1311             *avno = bestk->kvno;        /* and kvno to caller */
1312         UNLOCK_GLOBAL_MUTEX;
1313         return 0;
1314     }
1315     UNLOCK_GLOBAL_MUTEX;
1316     return AFSCONF_NOTFOUND;    /* didn't find any keys */
1317 }
1318
1319 /* get a particular key */
1320 int
1321 afsconf_GetKey(struct afsconf_dir *adir, afs_int32 avno, char *akey)
1322 {
1323     register int i, maxa;
1324     register struct afsconf_key *tk;
1325     register afs_int32 code;
1326
1327     LOCK_GLOBAL_MUTEX;
1328     code = afsconf_Check(adir);
1329     if (code) {
1330         UNLOCK_GLOBAL_MUTEX;
1331         return AFSCONF_FAILURE;
1332     }
1333     maxa = adir->keystr->nkeys;
1334
1335     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1336         if (tk->kvno == avno) {
1337             memcpy(akey, tk->key, 8);
1338             UNLOCK_GLOBAL_MUTEX;
1339             return 0;
1340         }
1341     }
1342
1343     UNLOCK_GLOBAL_MUTEX;
1344     return AFSCONF_NOTFOUND;
1345 }
1346
1347 /* save the key structure in the appropriate file */
1348 static int
1349 SaveKeys(struct afsconf_dir *adir)
1350 {
1351     struct afsconf_keys tkeys;
1352     register int fd;
1353     register afs_int32 i;
1354     char tbuffer[256];
1355
1356     memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys));
1357
1358     /* convert it to net byte order */
1359     for (i = 0; i < tkeys.nkeys; i++)
1360         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
1361     tkeys.nkeys = htonl(tkeys.nkeys);
1362
1363     /* rewrite keys file */
1364     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1365     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
1366     if (fd < 0)
1367         return AFSCONF_FAILURE;
1368     i = write(fd, &tkeys, sizeof(tkeys));
1369     if (i != sizeof(tkeys)) {
1370         close(fd);
1371         return AFSCONF_FAILURE;
1372     }
1373     if (close(fd) < 0)
1374         return AFSCONF_FAILURE;
1375     return 0;
1376 }
1377
1378 int
1379 afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8],
1380                afs_int32 overwrite)
1381 {
1382     register struct afsconf_keys *tk;
1383     register struct afsconf_key *tkey;
1384     register afs_int32 i;
1385     int foundSlot;
1386
1387     LOCK_GLOBAL_MUTEX;
1388     tk = adir->keystr;
1389
1390     if (akvno != 999) {
1391         if (akvno < 0 || akvno > 255) {
1392             UNLOCK_GLOBAL_MUTEX;
1393             return ERANGE;
1394         }
1395     }
1396     foundSlot = 0;
1397     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1398         if (tkey->kvno == akvno) {
1399             if (!overwrite) {
1400                 UNLOCK_GLOBAL_MUTEX;
1401                 return AFSCONF_KEYINUSE;
1402             }
1403             foundSlot = 1;
1404             break;
1405         }
1406     }
1407     if (!foundSlot) {
1408         if (tk->nkeys >= AFSCONF_MAXKEYS) {
1409             UNLOCK_GLOBAL_MUTEX;
1410             return AFSCONF_FULL;
1411         }
1412         tkey = &tk->key[tk->nkeys++];
1413     }
1414     tkey->kvno = akvno;
1415     memcpy(tkey->key, akey, 8);
1416     i = SaveKeys(adir);
1417     afsconf_Touch(adir);
1418     UNLOCK_GLOBAL_MUTEX;
1419     return i;
1420 }
1421
1422 /* this proc works by sliding the other guys down, rather than using a funny
1423     kvno value, so that callers can count on getting a good key in key[0].
1424 */
1425 int
1426 afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno)
1427 {
1428     register struct afsconf_keys *tk;
1429     register struct afsconf_key *tkey;
1430     register int i;
1431     int foundFlag = 0;
1432
1433     LOCK_GLOBAL_MUTEX;
1434     tk = adir->keystr;
1435
1436     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1437         if (tkey->kvno == akvno) {
1438             foundFlag = 1;
1439             break;
1440         }
1441     }
1442     if (!foundFlag) {
1443         UNLOCK_GLOBAL_MUTEX;
1444         return AFSCONF_NOTFOUND;
1445     }
1446
1447     /* otherwise slide the others down.  i and tkey point at the guy to delete */
1448     for (; i < tk->nkeys - 1; i++, tkey++) {
1449         tkey->kvno = (tkey + 1)->kvno;
1450         memcpy(tkey->key, (tkey + 1)->key, 8);
1451     }
1452     tk->nkeys--;
1453     i = SaveKeys(adir);
1454     afsconf_Touch(adir);
1455     UNLOCK_GLOBAL_MUTEX;
1456     return i;
1457 }