bfef67261ac435d9bc9aa48adcbddf03613dff65
[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], *p;
309     struct stat tstat;
310     register afs_int32 code;
311
312 #ifdef AFS_NT40_ENV
313     /* NT client CellServDB has different file name than NT server or Unix */
314     if (IsClientConfigDirectory(adir->name)) {
315         if (!afssw_GetClientCellServDBDir(&p)) {
316             strcompose(tbuffer, sizeof(tbuffer), p, "/",
317                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
318             free(p);
319         } else {
320             int len;
321             strncpy(tbuffer, adir->name, sizeof(tbuffer));
322             len = (int)strlen(tbuffer);
323             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
324                 strncat(tbuffer, "\\", sizeof(tbuffer));
325             }
326             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
327                     sizeof(tbuffer));
328             tbuffer[sizeof(tbuffer) - 1] = '\0';
329         }
330     } else {
331         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
332                    NULL);
333     }
334 #else
335     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
336 #endif /* AFS_NT40_ENV */
337
338     code = stat(tbuffer, &tstat);
339     if (code < 0) {
340         return code;
341     }
342     /* did file change? */
343     if (tstat.st_mtime == adir->timeRead) {
344         return 0;
345     }
346     /* otherwise file has changed, so reopen it */
347     return afsconf_Reopen(adir);
348 }
349
350 /* set modtime on file */
351 static int
352 afsconf_Touch(register struct afsconf_dir *adir)
353 {
354     char tbuffer[256], *p;
355 #ifndef AFS_NT40_ENV
356     struct timeval tvp[2];
357 #endif
358
359     adir->timeRead = 0;         /* just in case */
360
361 #ifdef AFS_NT40_ENV
362     /* NT client CellServDB has different file name than NT server or Unix */
363
364     if (IsClientConfigDirectory(adir->name)) {
365         if (!afssw_GetClientCellServDBDir(&p)) {
366             strcompose(tbuffer, sizeof(tbuffer), p, "/",
367                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
368             free(p);
369         } else {
370             int len = (int)strlen(tbuffer);
371             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
372                 strncat(tbuffer, "\\", sizeof(tbuffer));
373             }
374             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
375                     sizeof(tbuffer));
376             tbuffer[sizeof(tbuffer) - 1] = '\0';
377         }
378     } else {
379         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
380                    NULL);
381     }
382
383     return _utime(tbuffer, NULL);
384
385 #else
386     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
387     gettimeofday(&tvp[0], NULL);
388     tvp[1] = tvp[0];
389     return utimes(tbuffer, tvp);
390 #endif /* AFS_NT40_ENV */
391 }
392
393 struct afsconf_dir *
394 afsconf_Open(register const char *adir)
395 {
396     register struct afsconf_dir *tdir;
397     register afs_int32 code;
398
399     LOCK_GLOBAL_MUTEX;
400     /* zero structure and fill in name; rest is done by internal routine */
401     tdir = (struct afsconf_dir *)malloc(sizeof(struct afsconf_dir));
402     memset(tdir, 0, sizeof(struct afsconf_dir));
403     tdir->name = strdup(adir);
404
405     code = afsconf_OpenInternal(tdir, 0, 0);
406     if (code) {
407         char *afsconf_path, afs_confdir[128];
408
409         free(tdir->name);
410         /* Check global place only when local Open failed for whatever reason */
411         if (!(afsconf_path = getenv("AFSCONF"))) {
412             /* 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... */
413             char *home_dir;
414             afsconf_FILE *fp;
415             size_t len;
416
417             if (!(home_dir = getenv("HOME"))) {
418                 /* Our last chance is the "/.AFSCONF" file */
419                 fp = fopen("/.AFSCONF", "r");
420                 if (fp == 0) {
421                     free(tdir);
422                     UNLOCK_GLOBAL_MUTEX;
423                     return (struct afsconf_dir *)0;
424                 }
425                 fgets(afs_confdir, 128, fp);
426                 fclose(fp);
427             } else {
428                 char pathname[256];
429
430                 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
431                 fp = fopen(pathname, "r");
432                 if (fp == 0) {
433                     /* Our last chance is the "/.AFSCONF" file */
434                     fp = fopen("/.AFSCONF", "r");
435                     if (fp == 0) {
436                         free(tdir);
437                         UNLOCK_GLOBAL_MUTEX;
438                         return (struct afsconf_dir *)0;
439                     }
440                 }
441                 fgets(afs_confdir, 128, fp);
442                 fclose(fp);
443             }
444             len = strlen(afs_confdir);
445             if (len == 0) {
446                 free(tdir);
447                 UNLOCK_GLOBAL_MUTEX;
448                 return (struct afsconf_dir *)0;
449             }
450             if (afs_confdir[len - 1] == '\n') {
451                 afs_confdir[len - 1] = 0;
452             }
453             afsconf_path = afs_confdir;
454         }
455         tdir->name = strdup(afsconf_path);
456         code = afsconf_OpenInternal(tdir, 0, 0);
457         if (code) {
458             free(tdir->name);
459             free(tdir);
460             UNLOCK_GLOBAL_MUTEX;
461             return (struct afsconf_dir *)0;
462         }
463     }
464     UNLOCK_GLOBAL_MUTEX;
465     return tdir;
466 }
467
468 static int
469 GetCellUnix(struct afsconf_dir *adir)
470 {
471     char *rc;
472     char tbuffer[256];
473     char *start, *p;
474     afsconf_FILE *fp;
475     
476     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
477     fp = fopen(tbuffer, "r");
478     if (fp == 0) {
479         return -1;
480     }
481     rc = fgets(tbuffer, 256, fp);
482     fclose(fp);
483     if (rc == NULL)
484         return -1;
485
486     start = tbuffer;
487     while (*start != '\0' && isspace(*start))
488         start++;
489     p = start;
490     while (*p != '\0' && !isspace(*p))
491         p++;
492     *p = '\0';
493     if (*start == '\0')
494         return -1;
495
496     adir->cellName = strdup(start);
497     return 0;
498 }
499
500
501 #ifdef AFS_NT40_ENV
502 static int
503 GetCellNT(struct afsconf_dir *adir)
504 {
505     if (IsClientConfigDirectory(adir->name)) {
506         /* NT client config dir; ThisCell is in registry (no file). */
507         return afssw_GetClientCellName(&adir->cellName);
508     } else {
509         /* NT server config dir; works just like Unix */
510         return GetCellUnix(adir);
511     }
512 }
513 #endif /* AFS_NT40_ENV */
514
515
516 static int
517 afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
518                      char clones[])
519 {
520     afsconf_FILE *tf;
521     register char *tp, *bp;
522     register struct afsconf_entry *curEntry;
523     struct afsconf_aliasentry *curAlias;
524     register afs_int32 code;
525     afs_int32 i;
526     char tbuffer[256], tbuf1[256];
527     struct stat tstat;
528
529     /* figure out the local cell name */
530 #ifdef AFS_NT40_ENV
531     i = GetCellNT(adir);
532 #else
533     i = GetCellUnix(adir);
534 #endif
535
536 #ifndef AFS_FREELANCE_CLIENT    /* no local cell not fatal in freelance */
537     if (i) {
538         return i;
539     }
540 #endif
541
542     /* now parse the individual lines */
543     curEntry = 0;
544
545 #ifdef AFS_NT40_ENV
546     /* NT client/server have a CellServDB that is the same format as Unix.
547      * However, the NT client uses a different file name
548      */
549     if (IsClientConfigDirectory(adir->name)) {
550         /* NT client config dir */
551         char *p;
552         if (!afssw_GetClientCellServDBDir(&p)) {
553             strcompose(tbuffer, sizeof(tbuffer), p, "/",
554                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
555             free(p);
556         } else {
557             int len;
558             strncpy(tbuffer, adir->name, sizeof(tbuffer));
559             len = (int)strlen(tbuffer);
560             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
561                 strncat(tbuffer, "\\", sizeof(tbuffer));
562             }
563             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
564                     sizeof(tbuffer));
565             tbuffer[sizeof(tbuffer) - 1] = '\0';
566         }
567     } else {
568         /* NT server config dir */
569         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
570                    NULL);
571     }
572 #else
573     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
574 #endif /* AFS_NT40_ENV */
575
576     if (!stat(tbuffer, &tstat)) {
577         adir->timeRead = tstat.st_mtime;
578     } else {
579         adir->timeRead = 0;
580     }
581
582     strlcpy(tbuf1, tbuffer, sizeof tbuf1);
583     tf = fopen(tbuffer, "r");
584     if (!tf) {
585         return -1;
586     }
587     while (1) {
588         tp = fgets(tbuffer, sizeof(tbuffer), tf);
589         if (!tp)
590             break;
591         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
592         if (tbuffer[0] == 0 || tbuffer[0] == '\n')
593             continue;           /* empty line */
594         if (tbuffer[0] == '>') {
595             char linkedcell[MAXCELLCHARS];
596             /* start new cell item */
597             if (curEntry) {
598                 /* thread this guy on the list */
599                 curEntry->next = adir->entries;
600                 adir->entries = curEntry;
601                 curEntry = 0;
602             }
603             curEntry =
604                 (struct afsconf_entry *)malloc(sizeof(struct afsconf_entry));
605             memset(curEntry, 0, sizeof(struct afsconf_entry));
606             code =
607                 ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
608             if (code) {
609                 afsconf_CloseInternal(adir);
610                 fclose(tf);
611                 free(curEntry);
612                 return -1;
613             }
614             if (linkedcell[0] != '\0')
615                 curEntry->cellInfo.linkedCell = strdup(linkedcell);
616         } else {
617             /* new host in the current cell */
618             if (!curEntry) {
619                 afsconf_CloseInternal(adir);
620                 fclose(tf);
621                 return -1;
622             }
623             i = curEntry->cellInfo.numServers;
624             if (cell && !strcmp(cell, curEntry->cellInfo.name))
625                 code =
626                     ParseHostLine(tbuffer, &curEntry->cellInfo.hostAddr[i],
627                                   curEntry->cellInfo.hostName[i], &clones[i]);
628             else
629                 code =
630                     ParseHostLine(tbuffer, &curEntry->cellInfo.hostAddr[i],
631                                   curEntry->cellInfo.hostName[i], 0);
632             if (code) {
633                 if (code == AFSCONF_SYNTAX) {
634                     for (bp = tbuffer; *bp != '\n'; bp++) {     /* Take out the <cr> from the buffer */
635                         if (!*bp)
636                             break;
637                     }
638                     *bp = '\0';
639                     fprintf(stderr,
640                             "Can't properly parse host line \"%s\" in configuration file %s\n",
641                             tbuffer, tbuf1);
642                 }
643                 free(curEntry);
644                 fclose(tf);
645                 afsconf_CloseInternal(adir);
646                 return -1;
647             }
648             curEntry->cellInfo.numServers = ++i;
649         }
650     }
651     fclose(tf);                 /* close the file now */
652
653     /* end the last partially-completed cell */
654     if (curEntry) {
655         curEntry->next = adir->entries;
656         adir->entries = curEntry;
657     }
658
659     /* Read in the alias list */
660     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLALIAS_FILE, NULL);
661
662     tf = fopen(tbuffer, "r");
663     while (tf) {
664         char *aliasPtr;
665
666         tp = fgets(tbuffer, sizeof(tbuffer), tf);
667         if (!tp)
668             break;
669         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
670
671         if (tbuffer[0] == '\0' || tbuffer[0] == '\n' || tbuffer[0] == '#')
672             continue;           /* empty line */
673
674         tp = tbuffer;
675         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t')
676             tp++;
677         if (tp[0] == '\0')
678             continue;           /* invalid line */
679
680         while (tp[0] != '\0' && (tp[0] == ' ' || tp[0] == '\t'))
681             0[tp++] = '\0';
682         if (tp[0] == '\0')
683             continue;           /* invalid line */
684
685         aliasPtr = tp;
686         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t' && tp[0] != '\r'
687                && tp[0] != '\n')
688             tp++;
689         tp[0] = '\0';
690
691         curAlias = malloc(sizeof(*curAlias));
692         memset(curAlias, 0, sizeof(*curAlias));
693
694         strlcpy(curAlias->aliasInfo.aliasName, aliasPtr, sizeof curAlias->aliasInfo.aliasName);
695         strlcpy(curAlias->aliasInfo.realName, tbuffer, sizeof curAlias->aliasInfo.realName);
696
697         curAlias->next = adir->alias_entries;
698         adir->alias_entries = curAlias;
699     }
700
701     if (tf != NULL)
702         fclose(tf);
703     /* now read the fs keys, if possible */
704     adir->keystr = (struct afsconf_keys *)0;
705     afsconf_IntGetKeys(adir);
706
707     return 0;
708 }
709
710 /* parse a line of the form
711  *"128.2.1.3   #hostname" or
712  *"[128.2.1.3]  #hostname" for clones
713  * into the appropriate pieces.  
714  */
715 static int
716 ParseHostLine(char *aline, register struct sockaddr_in *addr, char *aname,
717               char *aclone)
718 {
719     int c1, c2, c3, c4;
720     register afs_int32 code;
721     register char *tp;
722
723     if (*aline == '[') {
724         if (aclone)
725             *aclone = 1;
726         /* FIXME: length of aname unknown here */
727         code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
728     } else {
729         if (aclone)
730             *aclone = 0;
731         /* FIXME: length of aname unknown here */
732         code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
733     }
734     if (code != 5)
735         return AFSCONF_SYNTAX;
736     addr->sin_family = AF_INET;
737     addr->sin_port = 0;
738 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
739     addr->sin_len = sizeof(struct sockaddr_in);
740 #endif
741     tp = (char *)&addr->sin_addr;
742     *tp++ = c1;
743     *tp++ = c2;
744     *tp++ = c3;
745     *tp++ = c4;
746     return 0;
747 }
748
749 /* parse a line of the form
750  * ">cellname [linkedcellname] [#comments]"
751  * into the appropriate pieces.
752  */
753 static int
754 ParseCellLine(register char *aline, register char *aname,
755               register char *alname)
756 {
757     register int code;
758     /* FIXME: length of aname, alname unknown here */
759     code = sscanf(aline, ">%s %s", aname, alname);
760     if (code == 1)
761         *alname = '\0';
762     if (code == 2) {
763         if (*alname == '#') {
764             *alname = '\0';
765         }
766     }
767     return (code > 0 ? 0 : AFSCONF_SYNTAX);
768 }
769
770 /* call aproc(entry, arock, adir) for all cells.  Proc must return 0, or we'll stop early and return the code it returns */
771 int
772 afsconf_CellApply(struct afsconf_dir *adir,
773                   int (*aproc) (struct afsconf_cell * cell, void *arock,
774                                 struct afsconf_dir * dir), void *arock)
775 {
776     register struct afsconf_entry *tde;
777     register afs_int32 code;
778     LOCK_GLOBAL_MUTEX;
779     for (tde = adir->entries; tde; tde = tde->next) {
780         code = (*aproc) (&tde->cellInfo, arock, adir);
781         if (code) {
782             UNLOCK_GLOBAL_MUTEX;
783             return code;
784         }
785     }
786     UNLOCK_GLOBAL_MUTEX;
787     return 0;
788 }
789
790 /* call aproc(entry, arock, adir) for all cell aliases.
791  * Proc must return 0, or we'll stop early and return the code it returns
792  */
793 int
794 afsconf_CellAliasApply(struct afsconf_dir *adir,
795                        int (*aproc) (struct afsconf_cellalias * alias,
796                                      void *arock, struct afsconf_dir * dir),
797                        void *arock)
798 {
799     register struct afsconf_aliasentry *tde;
800     register afs_int32 code;
801     LOCK_GLOBAL_MUTEX;
802     for (tde = adir->alias_entries; tde; tde = tde->next) {
803         code = (*aproc) (&tde->aliasInfo, arock, adir);
804         if (code) {
805             UNLOCK_GLOBAL_MUTEX;
806             return code;
807         }
808     }
809     UNLOCK_GLOBAL_MUTEX;
810     return 0;
811 }
812
813 afs_int32 afsconf_SawCell = 0;
814
815 int
816 afsconf_GetExtendedCellInfo(struct afsconf_dir *adir, char *acellName,
817                             char *aservice, struct afsconf_cell *acellInfo,
818                             char clones[])
819 {
820     afs_int32 code;
821     char *cell;
822
823     code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
824     if (code)
825         return code;
826
827     if (acellName)
828         cell = acellName;
829     else
830         cell = (char *)&acellInfo->name;
831
832     code = afsconf_OpenInternal(adir, cell, clones);
833     return code;
834 }
835
836 #ifdef AFS_AFSDB_ENV
837 #if !defined(AFS_NT40_ENV)
838 int
839 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
840                      struct afsconf_cell *acellInfo)
841 {
842     afs_int32 code;
843     int tservice, i, len;
844     unsigned char answer[1024];
845     unsigned char *p;
846     char *dotcellname;
847     int cellnamelength;
848     char realCellName[256];
849     char host[256];
850     int server_num = 0;
851     int minttl = 0;
852     int try_init = 0;
853
854     /* The resolver isn't always MT-safe.. Perhaps this ought to be
855      * replaced with a more fine-grained lock just for the resolver
856      * operations.
857      */
858
859  retryafsdb:
860     if ( ! strchr(acellName,'.') ) {
861        cellnamelength=strlen(acellName);
862        dotcellname=malloc(cellnamelength+2);
863        memcpy(dotcellname,acellName,cellnamelength);
864        dotcellname[cellnamelength]='.';
865        dotcellname[cellnamelength+1]=0;
866        LOCK_GLOBAL_MUTEX;
867        len = res_search(dotcellname, C_IN, T_AFSDB, answer, sizeof(answer));
868        if ( len < 0 ) {
869           len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
870        }
871        UNLOCK_GLOBAL_MUTEX;
872        free(dotcellname);
873     } else {
874        LOCK_GLOBAL_MUTEX;
875        len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
876        UNLOCK_GLOBAL_MUTEX;
877     }
878     if (len < 0) {
879         if (try_init < 1) {
880             try_init++;
881             res_init();
882             goto retryafsdb;
883         }
884         return AFSCONF_NOTFOUND;
885     }
886
887     p = answer + sizeof(HEADER);        /* Skip header */
888     code = dn_expand(answer, answer + len, p, host, sizeof(host));
889     if (code < 0)
890         return AFSCONF_NOTFOUND;
891
892     p += code + QFIXEDSZ;       /* Skip name */
893
894     while (p < answer + len) {
895         int type, ttl, size;
896
897         code = dn_expand(answer, answer + len, p, host, sizeof(host));
898         if (code < 0)
899             return AFSCONF_NOTFOUND;
900
901         p += code;              /* Skip the name */
902         type = (p[0] << 8) | p[1];
903         p += 4;                 /* Skip type and class */
904         ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
905         p += 4;                 /* Skip the TTL */
906         size = (p[0] << 8) | p[1];
907         p += 2;                 /* Skip the size */
908
909         if (type == T_AFSDB) {
910             struct hostent *he;
911             short afsdb_type;
912
913             afsdb_type = (p[0] << 8) | p[1];
914             if (afsdb_type == 1) {
915                 /*
916                  * We know this is an AFSDB record for our cell, of the
917                  * right AFSDB type.  Write down the true cell name that
918                  * the resolver gave us above.
919                  */
920                 strlcpy(realCellName, host, sizeof realCellName);
921             }
922
923             code = dn_expand(answer, answer + len, p + 2, host, sizeof(host));
924             if (code < 0)
925                 return AFSCONF_NOTFOUND;
926
927             if ((afsdb_type == 1) && (server_num < MAXHOSTSPERCELL) &&
928                 /* Do we want to get TTL data for the A record as well? */
929                 (he = gethostbyname(host))) {
930                 afs_int32 ipaddr;
931                 memcpy(&ipaddr, he->h_addr, he->h_length);
932                 acellInfo->hostAddr[server_num].sin_addr.s_addr = ipaddr;
933                 strncpy(acellInfo->hostName[server_num], host,
934                         sizeof(acellInfo->hostName[server_num]));
935                 server_num++;
936
937                 if (!minttl || ttl < minttl)
938                     minttl = ttl;
939             }
940         }
941
942         p += size;
943     }
944
945     if (server_num == 0)        /* No AFSDB records */
946         return AFSCONF_NOTFOUND;
947
948     /* Convert the real cell name to lowercase */
949     for (p = (unsigned char *)realCellName; *p; p++)
950         *p = tolower(*p);
951
952     strncpy(acellInfo->name, realCellName, sizeof(acellInfo->name));
953     acellInfo->numServers = server_num;
954
955     if (aservice) {
956         tservice = afsconf_FindService(aservice);
957         if (tservice < 0)
958             return AFSCONF_NOTFOUND;    /* service not found */
959         for (i = 0; i < acellInfo->numServers; i++) {
960             acellInfo->hostAddr[i].sin_port = tservice;
961         }
962     }
963
964     acellInfo->timeout = minttl ? (time(0) + minttl) : 0;
965
966     return 0;
967 }
968 #else /* windows */
969 int
970 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
971                      struct afsconf_cell *acellInfo)
972 {
973     register afs_int32 i;
974     int tservice;
975     struct afsconf_entry DNSce;
976     afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
977     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
978     int numServers;
979     int rc;
980     int ttl;
981
982     DNSce.cellInfo.numServers = 0;
983     DNSce.next = NULL;
984     rc = getAFSServer(acellName, cellHostAddrs, cellHostNames, &numServers,
985                       &ttl);
986     /* ignore the ttl here since this code is only called by transitory programs
987      * like klog, etc. */
988     if (rc < 0)
989         return -1;
990     if (numServers == 0)
991         return -1;
992
993     for (i = 0; i < numServers; i++) {
994         memcpy(&acellInfo->hostAddr[i].sin_addr.s_addr, &cellHostAddrs[i],
995                sizeof(long));
996         memcpy(acellInfo->hostName[i], cellHostNames[i], MAXHOSTCHARS);
997         acellInfo->hostAddr[i].sin_family = AF_INET;
998
999         /* sin_port supplied by connection code */
1000     }
1001
1002     acellInfo->numServers = numServers;
1003     strlcpy(acellInfo->name, acellName, sizeof acellInfo->name);
1004     if (aservice) {
1005         LOCK_GLOBAL_MUTEX;
1006         tservice = afsconf_FindService(aservice);
1007         UNLOCK_GLOBAL_MUTEX;
1008         if (tservice < 0) {
1009             return AFSCONF_NOTFOUND;    /* service not found */
1010         }
1011         for (i = 0; i < acellInfo->numServers; i++) {
1012             acellInfo->hostAddr[i].sin_port = tservice;
1013         }
1014     }
1015     acellInfo->linkedCell = NULL;       /* no linked cell */
1016     acellInfo->flags = 0;
1017     return 0;
1018 }
1019 #endif /* windows */
1020 #endif /* AFS_AFSDB_ENV */
1021
1022 int
1023 afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
1024                     struct afsconf_cell *acellInfo)
1025 {
1026     register struct afsconf_entry *tce;
1027     struct afsconf_aliasentry *tcae;
1028     struct afsconf_entry *bestce;
1029     register afs_int32 i;
1030     int tservice;
1031     char *tcell;
1032     int cnLen;
1033     int ambig;
1034     char tbuffer[64];
1035
1036     LOCK_GLOBAL_MUTEX;
1037     if (adir)
1038         afsconf_Check(adir);
1039     if (acellName) {
1040         tcell = acellName;
1041         cnLen = (int)(strlen(tcell) + 1);
1042         lcstring(tcell, tcell, cnLen);
1043         afsconf_SawCell = 1;    /* will ignore the AFSCELL switch on future */
1044         /* call to afsconf_GetLocalCell: like klog  */
1045     } else {
1046         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
1047         if (i) {
1048             UNLOCK_GLOBAL_MUTEX;
1049             return i;
1050         }
1051         tcell = tbuffer;
1052     }
1053     cnLen = strlen(tcell);
1054     bestce = (struct afsconf_entry *)0;
1055     ambig = 0;
1056     if (!adir) {
1057         UNLOCK_GLOBAL_MUTEX;
1058         return 0;
1059     }
1060
1061     /* Look through the list of aliases */
1062     for (tcae = adir->alias_entries; tcae; tcae = tcae->next) {
1063         if (strcasecmp(tcae->aliasInfo.aliasName, tcell) == 0) {
1064             tcell = tcae->aliasInfo.realName;
1065             break;
1066         }
1067     }
1068
1069     for (tce = adir->entries; tce; tce = tce->next) {
1070         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
1071             /* found our cell */
1072             bestce = tce;
1073             ambig = 0;
1074             break;
1075         }
1076         if (strlen(tce->cellInfo.name) < cnLen)
1077             continue;           /* clearly wrong */
1078         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
1079             if (bestce)
1080                 ambig = 1;      /* ambiguous unless we get exact match */
1081             bestce = tce;
1082         }
1083     }
1084     if (!ambig && bestce && bestce->cellInfo.numServers) {
1085         *acellInfo = bestce->cellInfo;  /* structure assignment */
1086         if (aservice) {
1087             tservice = afsconf_FindService(aservice);
1088             if (tservice < 0) {
1089                 UNLOCK_GLOBAL_MUTEX;
1090                 return AFSCONF_NOTFOUND;        /* service not found */
1091             }
1092             for (i = 0; i < acellInfo->numServers; i++) {
1093                 acellInfo->hostAddr[i].sin_port = tservice;
1094             }
1095         }
1096         acellInfo->timeout = 0;
1097         UNLOCK_GLOBAL_MUTEX;
1098         return 0;
1099     } else {
1100         UNLOCK_GLOBAL_MUTEX;
1101 #ifdef AFS_AFSDB_ENV
1102         return afsconf_GetAfsdbInfo(tcell, aservice, acellInfo);
1103 #else
1104         return AFSCONF_NOTFOUND;
1105 #endif /* AFS_AFSDB_ENV */
1106     }
1107 }
1108
1109 int
1110 afsconf_GetLocalCell(register struct afsconf_dir *adir, char *aname,
1111                      afs_int32 alen)
1112 {
1113     static int afsconf_showcell = 0;
1114     char *afscell_path;
1115     afs_int32 code = 0;
1116
1117     LOCK_GLOBAL_MUTEX;
1118     /*
1119      * If a cell switch was specified in a command, then it should override the 
1120      * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
1121      * is set and the cell name in the adir structure is used.
1122      * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
1123      */
1124     if (!afsconf_SawCell && (afscell_path = getenv("AFSCELL"))) {
1125         if (!afsconf_showcell) {
1126             fprintf(stderr, "Note: Operation is performed on cell %s\n",
1127                     afscell_path);
1128             afsconf_showcell = 1;
1129         }
1130         strncpy(aname, afscell_path, alen);
1131     } else {
1132         afsconf_Check(adir);
1133         if (adir->cellName) {
1134             strncpy(aname, adir->cellName, alen);
1135         } else
1136             code = AFSCONF_UNKNOWN;
1137     }
1138
1139     UNLOCK_GLOBAL_MUTEX;
1140     return (code);
1141 }
1142
1143 int
1144 afsconf_Close(struct afsconf_dir *adir)
1145 {
1146     LOCK_GLOBAL_MUTEX;
1147     afsconf_CloseInternal(adir);
1148     if (adir->name)
1149         free(adir->name);
1150     free(adir);
1151     UNLOCK_GLOBAL_MUTEX;
1152     return 0;
1153 }
1154
1155 static int
1156 afsconf_CloseInternal(register struct afsconf_dir *adir)
1157 {
1158     register struct afsconf_entry *td, *nd;
1159     struct afsconf_aliasentry *ta, *na;
1160     register char *tname;
1161
1162     tname = adir->name;         /* remember name, since that's all we preserve */
1163
1164     /* free everything we can find */
1165     if (adir->cellName)
1166         free(adir->cellName);
1167     for (td = adir->entries; td; td = nd) {
1168         nd = td->next;
1169         if (td->cellInfo.linkedCell)
1170             free(td->cellInfo.linkedCell);
1171         free(td);
1172     }
1173     for (ta = adir->alias_entries; ta; ta = na) {
1174         na = ta->next;
1175         free(ta);
1176     }
1177     if (adir->keystr)
1178         free(adir->keystr);
1179
1180     /* reinit */
1181     memset(adir, 0, sizeof(struct afsconf_dir));
1182     adir->name = tname;         /* restore it */
1183     return 0;
1184 }
1185
1186 static int
1187 afsconf_Reopen(register struct afsconf_dir *adir)
1188 {
1189     register afs_int32 code;
1190     code = afsconf_CloseInternal(adir);
1191     if (code)
1192         return code;
1193     code = afsconf_OpenInternal(adir, 0, 0);
1194     return code;
1195 }
1196
1197 /* called during opening of config file */
1198 int
1199 afsconf_IntGetKeys(struct afsconf_dir *adir)
1200 {
1201     char tbuffer[256];
1202     register int fd;
1203     struct afsconf_keys *tstr;
1204     register afs_int32 code;
1205
1206 #ifdef AFS_NT40_ENV
1207     /* NT client config dir has no KeyFile; don't risk attempting open
1208      * because there might be a random file of this name if dir is shared.
1209      */
1210     if (IsClientConfigDirectory(adir->name)) {
1211         adir->keystr = ((struct afsconf_keys *)
1212                         malloc(sizeof(struct afsconf_keys)));
1213         adir->keystr->nkeys = 0;
1214         return 0;
1215     }
1216 #endif /* AFS_NT40_ENV */
1217
1218     LOCK_GLOBAL_MUTEX;
1219     /* compute the key name and other setup */
1220     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1221     tstr = (struct afsconf_keys *)malloc(sizeof(struct afsconf_keys));
1222     adir->keystr = tstr;
1223
1224     /* read key file */
1225     fd = open(tbuffer, O_RDONLY);
1226     if (fd < 0) {
1227         tstr->nkeys = 0;
1228         UNLOCK_GLOBAL_MUTEX;
1229         return 0;
1230     }
1231     code = read(fd, tstr, sizeof(struct afsconf_keys));
1232     close(fd);
1233     if (code < sizeof(afs_int32)) {
1234         tstr->nkeys = 0;
1235         UNLOCK_GLOBAL_MUTEX;
1236         return 0;
1237     }
1238
1239     /* convert key structure to host order */
1240     tstr->nkeys = ntohl(tstr->nkeys);
1241
1242     if (code < sizeof(afs_int32) + (tstr->nkeys*sizeof(struct afsconf_key))) {
1243         tstr->nkeys = 0;
1244         UNLOCK_GLOBAL_MUTEX;
1245         return 0;
1246     }
1247
1248     for (fd = 0; fd < tstr->nkeys; fd++)
1249         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
1250
1251     UNLOCK_GLOBAL_MUTEX;
1252     return 0;
1253 }
1254
1255 /* get keys structure */
1256 int
1257 afsconf_GetKeys(struct afsconf_dir *adir, struct afsconf_keys *astr)
1258 {
1259     register afs_int32 code;
1260
1261     LOCK_GLOBAL_MUTEX;
1262     code = afsconf_Check(adir);
1263     if (code) {
1264         UNLOCK_GLOBAL_MUTEX;
1265         return AFSCONF_FAILURE;
1266     }
1267     memcpy(astr, adir->keystr, sizeof(struct afsconf_keys));
1268     UNLOCK_GLOBAL_MUTEX;
1269     return 0;
1270 }
1271
1272 /* get latest key */
1273 afs_int32
1274 afsconf_GetLatestKey(struct afsconf_dir * adir, afs_int32 * avno, char *akey)
1275 {
1276     register int i;
1277     int maxa;
1278     register struct afsconf_key *tk;
1279     register afs_int32 best;
1280     struct afsconf_key *bestk;
1281     register afs_int32 code;
1282
1283     LOCK_GLOBAL_MUTEX;
1284     code = afsconf_Check(adir);
1285     if (code) {
1286         UNLOCK_GLOBAL_MUTEX;
1287         return AFSCONF_FAILURE;
1288     }
1289     maxa = adir->keystr->nkeys;
1290
1291     best = -1;                  /* highest kvno we've seen yet */
1292     bestk = (struct afsconf_key *)0;    /* ptr to structure providing best */
1293     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1294         if (tk->kvno == 999)
1295             continue;           /* skip bcrypt keys */
1296         if (tk->kvno > best) {
1297             best = tk->kvno;
1298             bestk = tk;
1299         }
1300     }
1301     if (bestk) {                /* found any  */
1302         if (akey)
1303             memcpy(akey, bestk->key, 8);        /* copy out latest key */
1304         if (avno)
1305             *avno = bestk->kvno;        /* and kvno to caller */
1306         UNLOCK_GLOBAL_MUTEX;
1307         return 0;
1308     }
1309     UNLOCK_GLOBAL_MUTEX;
1310     return AFSCONF_NOTFOUND;    /* didn't find any keys */
1311 }
1312
1313 /* get a particular key */
1314 int
1315 afsconf_GetKey(struct afsconf_dir *adir, afs_int32 avno, char *akey)
1316 {
1317     register int i, maxa;
1318     register struct afsconf_key *tk;
1319     register afs_int32 code;
1320
1321     LOCK_GLOBAL_MUTEX;
1322     code = afsconf_Check(adir);
1323     if (code) {
1324         UNLOCK_GLOBAL_MUTEX;
1325         return AFSCONF_FAILURE;
1326     }
1327     maxa = adir->keystr->nkeys;
1328
1329     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1330         if (tk->kvno == avno) {
1331             memcpy(akey, tk->key, 8);
1332             UNLOCK_GLOBAL_MUTEX;
1333             return 0;
1334         }
1335     }
1336
1337     UNLOCK_GLOBAL_MUTEX;
1338     return AFSCONF_NOTFOUND;
1339 }
1340
1341 /* save the key structure in the appropriate file */
1342 static int
1343 SaveKeys(struct afsconf_dir *adir)
1344 {
1345     struct afsconf_keys tkeys;
1346     register int fd;
1347     register afs_int32 i;
1348     char tbuffer[256];
1349
1350     memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys));
1351
1352     /* convert it to net byte order */
1353     for (i = 0; i < tkeys.nkeys; i++)
1354         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
1355     tkeys.nkeys = htonl(tkeys.nkeys);
1356
1357     /* rewrite keys file */
1358     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1359     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
1360     if (fd < 0)
1361         return AFSCONF_FAILURE;
1362     i = write(fd, &tkeys, sizeof(tkeys));
1363     if (i != sizeof(tkeys)) {
1364         close(fd);
1365         return AFSCONF_FAILURE;
1366     }
1367     if (close(fd) < 0)
1368         return AFSCONF_FAILURE;
1369     return 0;
1370 }
1371
1372 int
1373 afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8],
1374                afs_int32 overwrite)
1375 {
1376     register struct afsconf_keys *tk;
1377     register struct afsconf_key *tkey;
1378     register afs_int32 i;
1379     int foundSlot;
1380
1381     LOCK_GLOBAL_MUTEX;
1382     tk = adir->keystr;
1383
1384     if (akvno != 999) {
1385         if (akvno < 0 || akvno > 255) {
1386             UNLOCK_GLOBAL_MUTEX;
1387             return ERANGE;
1388         }
1389     }
1390     foundSlot = 0;
1391     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1392         if (tkey->kvno == akvno) {
1393             if (!overwrite) {
1394                 UNLOCK_GLOBAL_MUTEX;
1395                 return AFSCONF_KEYINUSE;
1396             }
1397             foundSlot = 1;
1398             break;
1399         }
1400     }
1401     if (!foundSlot) {
1402         if (tk->nkeys >= AFSCONF_MAXKEYS) {
1403             UNLOCK_GLOBAL_MUTEX;
1404             return AFSCONF_FULL;
1405         }
1406         tkey = &tk->key[tk->nkeys++];
1407     }
1408     tkey->kvno = akvno;
1409     memcpy(tkey->key, akey, 8);
1410     i = SaveKeys(adir);
1411     afsconf_Touch(adir);
1412     UNLOCK_GLOBAL_MUTEX;
1413     return i;
1414 }
1415
1416 /* this proc works by sliding the other guys down, rather than using a funny
1417     kvno value, so that callers can count on getting a good key in key[0].
1418 */
1419 int
1420 afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno)
1421 {
1422     register struct afsconf_keys *tk;
1423     register struct afsconf_key *tkey;
1424     register int i;
1425     int foundFlag = 0;
1426
1427     LOCK_GLOBAL_MUTEX;
1428     tk = adir->keystr;
1429
1430     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1431         if (tkey->kvno == akvno) {
1432             foundFlag = 1;
1433             break;
1434         }
1435     }
1436     if (!foundFlag) {
1437         UNLOCK_GLOBAL_MUTEX;
1438         return AFSCONF_NOTFOUND;
1439     }
1440
1441     /* otherwise slide the others down.  i and tkey point at the guy to delete */
1442     for (; i < tk->nkeys - 1; i++, tkey++) {
1443         tkey->kvno = (tkey + 1)->kvno;
1444         memcpy(tkey->key, (tkey + 1)->key, 8);
1445     }
1446     tk->nkeys--;
1447     i = SaveKeys(adir);
1448     afsconf_Touch(adir);
1449     UNLOCK_GLOBAL_MUTEX;
1450     return i;
1451 }