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