Windows: preserve prior vlserver list on dns failure
[openafs.git] / src / WINNT / afsd / cm_cell.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 #include <roken.h>
13
14 #include <afs/stds.h>
15
16 #include <windows.h>
17 #include <nb30.h>
18 #include <winsock2.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <malloc.h>
22 #include <osi.h>
23 #include <string.h>
24 #define STRSAFE_NO_DEPRECATE
25 #include <strsafe.h>
26
27 #include "afsd.h"
28
29 osi_rwlock_t cm_cellLock;
30
31 /* function called as callback proc from cm_SearchCellFile.  Return 0 to
32  * continue processing.
33  *
34  * At the present time the return value is ignored by the caller.
35  */
36 long cm_AddCellProc(void *rockp, struct sockaddr_in *addrp, char *hostnamep, unsigned short adminRank)
37 {
38     cm_server_t *tsp;
39     cm_serverRef_t *tsrp;
40     cm_cell_t *cellp;
41     cm_cell_rock_t *cellrockp = (cm_cell_rock_t *)rockp;
42     afs_uint32 probe;
43
44     cellp = cellrockp->cellp;
45     probe = !(cellrockp->flags & CM_FLAG_NOPROBE);
46
47     tsp = cm_NewServer(addrp, CM_SERVER_VLDB, cellp, NULL,
48                        probe ? 0 : CM_FLAG_NOPROBE);
49
50     if (adminRank)
51         tsp->adminRank = adminRank;
52
53     /* Insert the vlserver into a sorted list, sorted by server rank */
54     tsrp = cm_NewServerRef(tsp, 0);
55     cm_InsertServerList(&cellp->vlServersp, tsrp);
56
57     return 0;
58 }
59
60 /* if it's from DNS, see if it has expired
61  * and check to make sure we have a valid set of volume servers
62  * this function must not be called with a lock on cm_cellLock
63  */
64 cm_cell_t *cm_UpdateCell(cm_cell_t * cp, afs_uint32 flags)
65 {
66     long code = 0;
67     cm_cell_rock_t rock;
68     afs_uint32 mxheld = 0;
69
70     if (cp == NULL)
71         return NULL;
72
73     lock_ObtainMutex(&cp->mx);
74     mxheld = 1;
75
76 #ifdef AFS_FREELANCE_CLIENT
77     if (cp->flags & CM_CELLFLAG_FREELANCE) {
78         lock_ReleaseMutex(&cp->mx);
79         return cp;
80     }
81 #endif
82
83     if (cm_IsServerListEmpty(cp->vlServersp) ||
84         (time(0) > cp->timeout) ||
85         (cm_dnsEnabled &&
86          (cp->flags & CM_CELLFLAG_DNS) &&
87          ((cp->flags & CM_CELLFLAG_VLSERVER_INVALID))))
88     {
89         lock_ReleaseMutex(&cp->mx);
90         mxheld = 0;
91
92         /* must empty cp->vlServersp */
93         if (cp->vlServersp)
94             cm_FreeServerList(&cp->vlServersp, CM_FREESERVERLIST_DELETE);
95
96         rock.cellp = cp;
97         rock.flags = flags;
98         code = cm_SearchCellRegistry(1, cp->name, NULL, cp->linkedName, cm_AddCellProc, &rock);
99         if (code && code != CM_ERROR_FORCE_DNS_LOOKUP)
100             code = cm_SearchCellFileEx(cp->name, NULL, cp->linkedName, cm_AddCellProc, &rock);
101         if (code == 0) {
102             lock_ObtainMutex(&cp->mx);
103             mxheld = 1;
104             cp->timeout = time(0) + 7200;
105         }
106         else {
107             if (cm_dnsEnabled) {
108                 int ttl;
109
110                 code = cm_SearchCellByDNS(cp->name, NULL, &ttl, cm_AddCellProc, &rock);
111                 if (code == 0) {   /* got cell from DNS */
112                     lock_ObtainMutex(&cp->mx);
113                     mxheld = 1;
114                     _InterlockedOr(&cp->flags, CM_CELLFLAG_DNS);
115                     _InterlockedAnd(&cp->flags, ~CM_CELLFLAG_VLSERVER_INVALID);
116                     cp->timeout = time(0) + ttl;
117 #ifdef DEBUG
118                     fprintf(stderr, "cell %s: ttl=%d\n", cp->name, ttl);
119 #endif
120                 } else {
121                     /* if we fail to find it this time, we'll just do nothing and leave the
122                      * current entry alone
123                      */
124                     lock_ObtainMutex(&cp->mx);
125                     mxheld = 1;
126                     _InterlockedOr(&cp->flags, CM_CELLFLAG_VLSERVER_INVALID);
127                 }
128             }
129         }
130     }
131
132     if (code == 0)
133         cm_RandomizeServer(&cp->vlServersp);
134
135     if (mxheld)
136         lock_ReleaseMutex(&cp->mx);
137
138     return code ? NULL : cp;
139 }
140
141 /* load up a cell structure from the cell database, AFS_CELLSERVDB */
142 cm_cell_t *cm_GetCell(char *namep, afs_uint32 flags)
143 {
144     return cm_GetCell_Gen(namep, NULL, flags);
145 }
146
147 void cm_FreeCell(cm_cell_t *cellp)
148 {
149     lock_AssertWrite(&cm_cellLock);
150
151     if (cellp->vlServersp)
152         cm_FreeServerList(&cellp->vlServersp, CM_FREESERVERLIST_DELETE);
153     cellp->name[0] = '\0';
154
155     cellp->freeNextp = cm_data.freeCellsp;
156     cm_data.freeCellsp = cellp;
157 }
158
159 cm_cell_t *cm_GetCell_Gen(char *namep, char *newnamep, afs_uint32 flags)
160 {
161     cm_cell_t *cp, *cp2;
162     long code;
163     char fullname[CELL_MAXNAMELEN]="";
164     char linkedName[CELL_MAXNAMELEN]="";
165     char name[CELL_MAXNAMELEN]="";
166     int  hasWriteLock = 0;
167     int  hasMutex = 0;
168     afs_uint32 hash;
169     cm_cell_rock_t rock;
170     size_t len;
171     afs_int32 cellID;
172
173     if (namep == NULL || !namep[0] || !strcmp(namep,CM_IOCTL_FILENAME_NOSLASH))
174         return NULL;
175
176     /*
177      * Strip off any trailing dots at the end of the cell name.
178      * Failure to do so results in an undesireable alias as the
179      * result of DNS AFSDB record lookups where a trailing dot
180      * has special meaning.
181      */
182     strncpy(name, namep, CELL_MAXNAMELEN);
183     for (len = strlen(namep); len > 0 && namep[len-1] == '.'; len--) {
184         name[len-1] = '\0';
185     }
186     if (len == 0)
187         return NULL;
188     namep = name;
189
190     hash = CM_CELL_NAME_HASH(namep);
191
192     lock_ObtainRead(&cm_cellLock);
193     for (cp = cm_data.cellNameHashTablep[hash]; cp; cp=cp->nameNextp) {
194         if (cm_stricmp_utf8(namep, cp->name) == 0) {
195             strncpy(fullname, cp->name, CELL_MAXNAMELEN);
196             fullname[CELL_MAXNAMELEN-1] = '\0';
197             break;
198         }
199     }
200
201     if (!cp) {
202         for (cp = cm_data.allCellsp; cp; cp=cp->allNextp) {
203             if (strnicmp(namep, cp->name, strlen(namep)) == 0) {
204                 strncpy(fullname, cp->name, CELL_MAXNAMELEN);
205                 fullname[CELL_MAXNAMELEN-1] = '\0';
206                 break;
207             }
208         }
209     }
210
211     if (cp) {
212         lock_ReleaseRead(&cm_cellLock);
213         cm_UpdateCell(cp, flags);
214     } else if (flags & CM_FLAG_CREATE) {
215         lock_ConvertRToW(&cm_cellLock);
216         hasWriteLock = 1;
217
218         /* when we dropped the lock the cell could have been added
219          * to the list so check again while holding the write lock
220          */
221         for (cp = cm_data.cellNameHashTablep[hash]; cp; cp=cp->nameNextp) {
222             if (cm_stricmp_utf8(namep, cp->name) == 0) {
223                 strncpy(fullname, cp->name, CELL_MAXNAMELEN);
224                 fullname[CELL_MAXNAMELEN-1] = '\0';
225                 break;
226             }
227         }
228
229         if (cp)
230             goto done;
231
232         for (cp = cm_data.allCellsp; cp; cp=cp->allNextp) {
233             if (strnicmp(namep, cp->name, strlen(namep)) == 0) {
234                 strncpy(fullname, cp->name, CELL_MAXNAMELEN);
235                 fullname[CELL_MAXNAMELEN-1] = '\0';
236                 break;
237             }
238         }
239
240         if (cp) {
241             lock_ReleaseWrite(&cm_cellLock);
242             lock_ObtainMutex(&cp->mx);
243             lock_ObtainWrite(&cm_cellLock);
244             cm_AddCellToNameHashTable(cp);
245             cm_AddCellToIDHashTable(cp);
246             lock_ReleaseMutex(&cp->mx);
247             goto done;
248         }
249
250         if ( cm_data.freeCellsp != NULL ) {
251             cp = cm_data.freeCellsp;
252             cm_data.freeCellsp = cp->freeNextp;
253
254             /*
255              * The magic, cellID, and mx fields are already set.
256              */
257         } else {
258             if ( cm_data.currentCells >= cm_data.maxCells )
259                 osi_panic("Exceeded Max Cells", __FILE__, __LINE__);
260
261             /*
262              * the cellID cannot be 0.
263              * If there is a name collision, one of the entries
264              * will end up on cm_data.freeCellsp for reuse.
265              */
266             cellID = InterlockedIncrement(&cm_data.currentCells);
267             cp = &cm_data.cellBaseAddress[cellID - 1];
268             memset(cp, 0, sizeof(cm_cell_t));
269             cp->magic = CM_CELL_MAGIC;
270             cp->cellID = cellID;
271
272             /* otherwise we found the cell, and so we're nearly done */
273             lock_InitializeMutex(&cp->mx, "cm_cell_t mutex", LOCK_HIERARCHY_CELL);
274         }
275
276         lock_ReleaseWrite(&cm_cellLock);
277         hasWriteLock = 0;
278
279         rock.cellp = cp;
280         rock.flags = flags;
281         code = cm_SearchCellRegistry(1, namep, fullname, linkedName, cm_AddCellProc, &rock);
282         if (code && code != CM_ERROR_FORCE_DNS_LOOKUP)
283             code = cm_SearchCellFileEx(namep, fullname, linkedName, cm_AddCellProc, &rock);
284         if (code) {
285             osi_Log4(afsd_logp,"in cm_GetCell_gen cm_SearchCellFileEx(%s) returns code= %d fullname= %s linkedName= %s",
286                       osi_LogSaveString(afsd_logp,namep), code, osi_LogSaveString(afsd_logp,fullname),
287                       osi_LogSaveString(afsd_logp,linkedName));
288
289             if (cm_dnsEnabled) {
290                 int ttl;
291
292                 code = cm_SearchCellByDNS(namep, fullname, &ttl, cm_AddCellProc, &rock);
293                 if ( code ) {
294                     osi_Log3(afsd_logp,"in cm_GetCell_gen cm_SearchCellByDNS(%s) returns code= %d fullname= %s",
295                              osi_LogSaveString(afsd_logp,namep), code, osi_LogSaveString(afsd_logp,fullname));
296                     lock_ObtainMutex(&cp->mx);
297                     lock_ObtainWrite(&cm_cellLock);
298                     hasWriteLock = 1;
299                     cm_RemoveCellFromIDHashTable(cp);
300                     cm_RemoveCellFromNameHashTable(cp);
301                     lock_ReleaseMutex(&cp->mx);
302                     cm_FreeCell(cp);
303                     cp = NULL;
304                     goto done;
305                 } else {   /* got cell from DNS */
306                     lock_ObtainMutex(&cp->mx);
307                     hasMutex = 1;
308                     _InterlockedOr(&cp->flags, CM_CELLFLAG_DNS);
309                     _InterlockedAnd(&cp->flags, ~CM_CELLFLAG_VLSERVER_INVALID);
310                     cp->timeout = time(0) + ttl;
311                 }
312             }
313             else
314             {
315                 lock_ObtainMutex(&cp->mx);
316                 lock_ObtainWrite(&cm_cellLock);
317                 hasWriteLock = 1;
318                 cm_RemoveCellFromIDHashTable(cp);
319                 cm_RemoveCellFromNameHashTable(cp);
320                 lock_ReleaseMutex(&cp->mx);
321                 cm_FreeCell(cp);
322                 cp = NULL;
323                 goto done;
324             }
325         } else {
326             lock_ObtainMutex(&cp->mx);
327             hasMutex = 1;
328             cp->timeout = time(0) + 7200;       /* two hour timeout */
329         }
330
331         /* we have now been given the fullname of the cell.  It may
332          * be that we already have a cell with that name.  If so,
333          * we should use it instead of completing the allocation
334          * of a new cm_cell_t
335          */
336         lock_ObtainRead(&cm_cellLock);
337         hash = CM_CELL_NAME_HASH(fullname);
338         for (cp2 = cm_data.cellNameHashTablep[hash]; cp2; cp2=cp2->nameNextp) {
339             if (cm_stricmp_utf8(fullname, cp2->name) == 0) {
340                 break;
341             }
342         }
343
344         if (cp2) {
345             cm_server_t *tsp;
346
347             if (!hasMutex) {
348                 lock_ObtainMutex(&cp->mx);
349                 hasMutex = 1;
350             }
351             lock_ConvertRToW(&cm_cellLock);
352             hasWriteLock = 1;
353             cm_RemoveCellFromIDHashTable(cp);
354             cm_RemoveCellFromNameHashTable(cp);
355             lock_ReleaseMutex(&cp->mx);
356             hasMutex = 0;
357
358             /* Replace cells references in servers */
359             lock_ObtainRead(&cm_serverLock);
360             for (tsp = cm_serversAllFirstp;
361                   tsp;
362                   tsp = (cm_server_t *)osi_QNext(&tsp->allq)) {
363                 if (tsp->cellp == cp) {
364                     osi_Log4(afsd_logp, "cm_GetCell_gen race: replacing cell (%u) %s with cell (%u) %s",
365                               cp->cellID,
366                               osi_LogSaveString(afsd_logp,cp->name),
367                               cp2->cellID,
368                               osi_LogSaveString(afsd_logp,cp2->name));
369                     tsp->cellp = cp2;
370                 }
371             }
372             lock_ReleaseRead(&cm_serverLock);
373
374             cm_FreeCell(cp);
375             cp = cp2;
376             goto done;
377         }
378         lock_ReleaseRead(&cm_cellLock);
379
380         /* randomise among those vlservers having the same rank*/
381         cm_RandomizeServer(&cp->vlServersp);
382
383         if (!hasMutex)
384             lock_ObtainMutex(&cp->mx);
385
386         /* copy in name */
387         strncpy(cp->name, fullname, CELL_MAXNAMELEN);
388         cp->name[CELL_MAXNAMELEN-1] = '\0';
389
390         strncpy(cp->linkedName, linkedName, CELL_MAXNAMELEN);
391         cp->linkedName[CELL_MAXNAMELEN-1] = '\0';
392
393         lock_ObtainWrite(&cm_cellLock);
394         hasWriteLock = 1;
395         cm_AddCellToNameHashTable(cp);
396         cm_AddCellToIDHashTable(cp);
397         lock_ReleaseMutex(&cp->mx);
398         hasMutex = 0;
399
400         /* append cell to global list */
401         if (cm_data.allCellsp == NULL) {
402             cm_data.allCellsp = cp;
403         } else {
404             for (cp2 = cm_data.allCellsp; cp2->allNextp; cp2=cp2->allNextp)
405                 ;
406             cp2->allNextp = cp;
407         }
408         cp->allNextp = NULL;
409
410     } else {
411         lock_ReleaseRead(&cm_cellLock);
412     }
413   done:
414     if (hasMutex && cp)
415         lock_ReleaseMutex(&cp->mx);
416     if (hasWriteLock)
417         lock_ReleaseWrite(&cm_cellLock);
418
419     /* fullname is not valid if cp == NULL */
420     if (newnamep) {
421         if (cp) {
422             strncpy(newnamep, fullname, CELL_MAXNAMELEN);
423             newnamep[CELL_MAXNAMELEN-1]='\0';
424         } else {
425             newnamep[0] = '\0';
426         }
427     }
428
429     if (cp && cp->linkedName[0]) {
430         cm_cell_t * linkedCellp = NULL;
431
432         if (!strcmp(cp->name, cp->linkedName)) {
433             cp->linkedName[0] = '\0';
434         } else if (!(flags & CM_FLAG_NOMOUNTCHASE)) {
435             linkedCellp = cm_GetCell(cp->linkedName, CM_FLAG_CREATE|CM_FLAG_NOPROBE|CM_FLAG_NOMOUNTCHASE);
436
437             lock_ObtainWrite(&cm_cellLock);
438             if (!linkedCellp ||
439                 (linkedCellp->linkedName[0] && strcmp(cp->name, linkedCellp->linkedName))) {
440                 cp->linkedName[0] = '\0';
441             } else {
442                 strncpy(linkedCellp->linkedName, cp->name, CELL_MAXNAMELEN);
443                 linkedCellp->linkedName[CELL_MAXNAMELEN-1]='\0';
444             }
445             lock_ReleaseWrite(&cm_cellLock);
446         }
447     }
448     return cp;
449 }
450
451 cm_cell_t *cm_FindCellByID(afs_int32 cellID, afs_uint32 flags)
452 {
453     cm_cell_t *cp;
454     afs_uint32 hash;
455
456     lock_ObtainRead(&cm_cellLock);
457
458     hash = CM_CELL_ID_HASH(cellID);
459
460     for (cp = cm_data.cellIDHashTablep[hash]; cp; cp=cp->idNextp) {
461         if (cellID == cp->cellID)
462             break;
463     }
464     lock_ReleaseRead(&cm_cellLock);
465
466     if (cp)
467         cm_UpdateCell(cp, flags);
468
469     return cp;
470 }
471
472 long
473 cm_ValidateCell(void)
474 {
475     cm_cell_t * cellp;
476     afs_uint32 count1, count2;
477
478     for (cellp = cm_data.allCellsp, count1 = 0; cellp; cellp=cellp->allNextp, count1++) {
479
480         if ( cellp < (cm_cell_t *)cm_data.cellBaseAddress ||
481              cellp >= (cm_cell_t *)cm_data.aclBaseAddress) {
482             afsi_log("cm_ValidateCell failure: out of range cm_cell_t pointers");
483             fprintf(stderr, "cm_ValidateCell failure: out of range cm_cell_t pointers\n");
484             return -10;
485         }
486
487         if ( cellp->magic != CM_CELL_MAGIC ) {
488             afsi_log("cm_ValidateCell failure: cellp->magic != CM_CELL_MAGIC");
489             fprintf(stderr, "cm_ValidateCell failure: cellp->magic != CM_CELL_MAGIC\n");
490             return -1;
491         }
492         if ( count1 != 0 && cellp == cm_data.allCellsp ||
493              count1 > cm_data.maxCells ) {
494             afsi_log("cm_ValidateCell failure: cm_data.allCellsp infinite loop");
495             fprintf(stderr, "cm_ValidateCell failure: cm_data.allCellsp infinite loop\n");
496             return -2;
497         }
498     }
499
500     for (cellp = cm_data.freeCellsp, count2 = 0; cellp; cellp=cellp->freeNextp, count2++) {
501
502         if ( cellp < (cm_cell_t *)cm_data.cellBaseAddress ||
503              cellp >= (cm_cell_t *)cm_data.aclBaseAddress) {
504             afsi_log("cm_ValidateCell failure: out of range cm_cell_t pointers");
505             fprintf(stderr, "cm_ValidateCell failure: out of range cm_cell_t pointers\n");
506             return -11;
507         }
508
509         if ( count2 != 0 && cellp == cm_data.freeCellsp ||
510              count2 > cm_data.maxCells ) {
511             afsi_log("cm_ValidateCell failure: cm_data.freeCellsp infinite loop");
512             fprintf(stderr, "cm_ValidateCell failure: cm_data.freeCellsp infinite loop\n");
513             return -3;
514         }
515     }
516
517     if ( (count1 + count2) != cm_data.currentCells ) {
518         afsi_log("cm_ValidateCell failure: count != cm_data.currentCells");
519         fprintf(stderr, "cm_ValidateCell failure: count != cm_data.currentCells\n");
520         return -4;
521     }
522
523     return 0;
524 }
525
526
527 long
528 cm_ShutdownCell(void)
529 {
530     cm_cell_t * cellp;
531
532     for (cellp = cm_data.allCellsp; cellp; cellp=cellp->allNextp)
533         lock_FinalizeMutex(&cellp->mx);
534
535     return 0;
536 }
537
538
539 void cm_InitCell(int newFile, long maxCells)
540 {
541     static osi_once_t once;
542
543     if (osi_Once(&once)) {
544         cm_cell_t * cellp;
545
546         lock_InitializeRWLock(&cm_cellLock, "cell global lock", LOCK_HIERARCHY_CELL_GLOBAL);
547
548         if ( newFile ) {
549             cm_data.allCellsp = NULL;
550             cm_data.currentCells = 0;
551             cm_data.maxCells = maxCells;
552             memset(cm_data.cellNameHashTablep, 0, sizeof(cm_cell_t *) * cm_data.cellHashTableSize);
553             memset(cm_data.cellIDHashTablep, 0, sizeof(cm_cell_t *) * cm_data.cellHashTableSize);
554
555 #ifdef AFS_FREELANCE_CLIENT
556             /* Generate a dummy entry for the Freelance cell whether or not
557              * freelance mode is being used in this session
558              */
559
560             cellp = &cm_data.cellBaseAddress[cm_data.currentCells++];
561             memset(cellp, 0, sizeof(cm_cell_t));
562             cellp->magic = CM_CELL_MAGIC;
563
564             lock_InitializeMutex(&cellp->mx, "cm_cell_t mutex", LOCK_HIERARCHY_CELL);
565
566             lock_ObtainMutex(&cellp->mx);
567             lock_ObtainWrite(&cm_cellLock);
568
569             /* copy in name */
570             strncpy(cellp->name, "Freelance.Local.Cell", CELL_MAXNAMELEN); /*safe*/
571             cellp->name[CELL_MAXNAMELEN-1] = '\0';
572
573             /* thread on global list */
574             cellp->allNextp = cm_data.allCellsp;
575             cm_data.allCellsp = cellp;
576
577             cellp->cellID = AFS_FAKE_ROOT_CELL_ID;
578             cellp->vlServersp = NULL;
579             _InterlockedOr(&cellp->flags, CM_CELLFLAG_FREELANCE);
580
581             cm_AddCellToNameHashTable(cellp);
582             cm_AddCellToIDHashTable(cellp);
583             lock_ReleaseWrite(&cm_cellLock);
584             lock_ReleaseMutex(&cellp->mx);
585 #endif
586         } else {
587             lock_ObtainRead(&cm_cellLock);
588             for (cellp = cm_data.allCellsp; cellp; cellp=cellp->allNextp) {
589                 lock_InitializeMutex(&cellp->mx, "cm_cell_t mutex", LOCK_HIERARCHY_CELL);
590                 cellp->vlServersp = NULL;
591                 _InterlockedOr(&cellp->flags, CM_CELLFLAG_VLSERVER_INVALID);
592             }
593             lock_ReleaseRead(&cm_cellLock);
594         }
595
596         osi_EndOnce(&once);
597     }
598 }
599
600 void cm_ChangeRankCellVLServer(cm_server_t *tsp)
601 {
602     cm_cell_t *cp;
603     int code;
604
605     cp = tsp->cellp;    /* cell that this vlserver belongs to */
606     if (cp) {
607         lock_ObtainMutex(&cp->mx);
608         code = cm_ChangeRankServer(&cp->vlServersp, tsp);
609
610         if ( !code )            /* if the server list was rearranged */
611             cm_RandomizeServer(&cp->vlServersp);
612
613         lock_ReleaseMutex(&cp->mx);
614     }
615 }
616
617 int cm_DumpCells(FILE *outputFile, char *cookie, int lock)
618 {
619     cm_cell_t *cellp;
620     int zilch;
621     char output[1024];
622
623     if (lock)
624         lock_ObtainRead(&cm_cellLock);
625
626     sprintf(output, "%s - dumping cells - cm_data.currentCells=%d, cm_data.maxCells=%d\r\n",
627             cookie, cm_data.currentCells, cm_data.maxCells);
628     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
629
630     for (cellp = cm_data.allCellsp; cellp; cellp=cellp->allNextp) {
631         sprintf(output, "%s cellp=0x%p,name=%s ID=%d flags=0x%x timeout=%I64u\r\n",
632                 cookie, cellp, cellp->name, cellp->cellID, cellp->flags, cellp->timeout);
633         WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
634     }
635
636     sprintf(output, "%s - Done dumping cells.\r\n", cookie);
637     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
638
639     if (lock)
640         lock_ReleaseRead(&cm_cellLock);
641
642     return(0);
643 }
644
645 /* call with volume write-locked and mutex held */
646 void cm_AddCellToNameHashTable(cm_cell_t *cellp)
647 {
648     int i;
649
650     lock_AssertWrite(&cm_cellLock);
651     lock_AssertMutex(&cellp->mx);
652
653     if (cellp->flags & CM_CELLFLAG_IN_NAMEHASH)
654         return;
655
656     i = CM_CELL_NAME_HASH(cellp->name);
657
658     cellp->nameNextp = cm_data.cellNameHashTablep[i];
659     cm_data.cellNameHashTablep[i] = cellp;
660     _InterlockedOr(&cellp->flags, CM_CELLFLAG_IN_NAMEHASH);
661 }
662
663 /* call with cell write-locked and mutex held */
664 void cm_RemoveCellFromNameHashTable(cm_cell_t *cellp)
665 {
666     cm_cell_t **lcellpp;
667     cm_cell_t *tcellp;
668     int i;
669
670     lock_AssertWrite(&cm_cellLock);
671     lock_AssertMutex(&cellp->mx);
672
673     if (cellp->flags & CM_CELLFLAG_IN_NAMEHASH) {
674         /* hash it out first */
675         i = CM_CELL_NAME_HASH(cellp->name);
676         for (lcellpp = &cm_data.cellNameHashTablep[i], tcellp = cm_data.cellNameHashTablep[i];
677              tcellp;
678              lcellpp = &tcellp->nameNextp, tcellp = tcellp->nameNextp) {
679             if (tcellp == cellp) {
680                 *lcellpp = cellp->nameNextp;
681                 _InterlockedAnd(&cellp->flags, ~CM_CELLFLAG_IN_NAMEHASH);
682                 cellp->nameNextp = NULL;
683                 break;
684             }
685         }
686     }
687 }
688
689 /* call with cell write-locked and mutex held */
690 void cm_AddCellToIDHashTable(cm_cell_t *cellp)
691 {
692     int i;
693
694     lock_AssertWrite(&cm_cellLock);
695     lock_AssertMutex(&cellp->mx);
696
697     if (cellp->flags & CM_CELLFLAG_IN_IDHASH)
698         return;
699
700     i = CM_CELL_ID_HASH(cellp->cellID);
701
702     cellp->idNextp = cm_data.cellIDHashTablep[i];
703     cm_data.cellIDHashTablep[i] = cellp;
704     _InterlockedOr(&cellp->flags, CM_CELLFLAG_IN_IDHASH);
705 }
706
707 /* call with cell write-locked and mutex held */
708 void cm_RemoveCellFromIDHashTable(cm_cell_t *cellp)
709 {
710     cm_cell_t **lcellpp;
711     cm_cell_t *tcellp;
712     int i;
713
714     lock_AssertWrite(&cm_cellLock);
715     lock_AssertMutex(&cellp->mx);
716
717     if (cellp->flags & CM_CELLFLAG_IN_IDHASH) {
718         /* hash it out first */
719         i = CM_CELL_ID_HASH(cellp->cellID);
720         for (lcellpp = &cm_data.cellIDHashTablep[i], tcellp = cm_data.cellIDHashTablep[i];
721              tcellp;
722              lcellpp = &tcellp->idNextp, tcellp = tcellp->idNextp) {
723             if (tcellp == cellp) {
724                 *lcellpp = cellp->idNextp;
725                 _InterlockedAnd(&cellp->flags, ~CM_CELLFLAG_IN_IDHASH);
726                 cellp->idNextp = NULL;
727                 break;
728             }
729         }
730     }
731 }
732
733 long
734 cm_CreateCellWithInfo( char * cellname,
735                        char * linked_cellname,
736                        unsigned short vlport,
737                        afs_uint32 host_count,
738                        char *hostname[],
739                        afs_uint32 flags)
740 {
741     afs_uint32 code = 0;
742     cm_cell_rock_t rock;
743     struct hostent *thp;
744     struct sockaddr_in vlSockAddr;
745     afs_uint32 i, j;
746
747     rock.cellp = cm_GetCell(cellname, CM_FLAG_CREATE | CM_FLAG_NOPROBE);
748     rock.flags = 0;
749
750     if (!(flags & CM_CELLFLAG_DNS)) {
751         int first = 1;
752
753         for (i = 0; i < host_count; i++) {
754             thp = gethostbyname(hostname[i]);
755             if (first) {
756                 /*
757                  * If there is at least one resolved vlserver or an authoritative,
758                  * host not found response, destroy the prior list.
759                  */
760                 if (thp != NULL || WSAGetLastError() == WSAHOST_NOT_FOUND) {
761                     cm_FreeServerList(&rock.cellp->vlServersp, CM_FREESERVERLIST_DELETE);
762                     first = 0;
763                 }
764             }
765
766             if (thp) {
767                 if (thp->h_addrtype != AF_INET)
768                     continue;
769
770                 for (j=0 ; thp->h_addr_list[j]; j++) {
771                     memcpy(&vlSockAddr.sin_addr.s_addr,
772                            thp->h_addr_list[j],
773                            sizeof(long));
774                     vlSockAddr.sin_port = htons(vlport ? vlport : 7003);
775                     vlSockAddr.sin_family = AF_INET;
776                     cm_AddCellProc(&rock, &vlSockAddr, hostname[i], CM_FLAG_NOPROBE);
777                 }
778             }
779         }
780         lock_ObtainMutex(&rock.cellp->mx);
781         _InterlockedAnd(&rock.cellp->flags, ~CM_CELLFLAG_DNS);
782     } else if (cm_dnsEnabled) {
783         int ttl;
784         cm_serverRef_t * vlServersp = NULL;
785
786         lock_ObtainWrite(&cm_serverLock);
787         vlServersp = rock.cellp->vlServersp;
788         rock.cellp->vlServersp = NULL;
789         lock_ReleaseWrite(&cm_serverLock);
790
791         code = cm_SearchCellByDNS(rock.cellp->name, NULL, &ttl, cm_AddCellProc, &rock);
792         lock_ObtainMutex(&rock.cellp->mx);
793         if (code == 0) {   /* got cell from DNS */
794             _InterlockedOr(&rock.cellp->flags, CM_CELLFLAG_DNS);
795             rock.cellp->timeout = time(0) + ttl;
796 #ifdef DEBUG
797             fprintf(stderr, "cell %s: ttl=%d\n", rock.cellp->name, ttl);
798 #endif
799         } else {
800             lock_ObtainWrite(&cm_serverLock);
801             if (rock.cellp->vlServersp == NULL) {
802                 rock.cellp->vlServersp = vlServersp;
803                 vlServersp = NULL;
804             }
805             lock_ReleaseWrite(&cm_serverLock);
806         }
807
808         cm_FreeServerList(&vlServersp, CM_FREESERVERLIST_DELETE);
809         if (vlServersp != NULL) {
810             /*
811              * We moved the vlServer list out of the way and
812              * in the meantime it was replaced.  If the vlServerp
813              * list is non-Empty after cm_FreeServerList was called
814              * it means that there are deleted entries with active
815              * references.  Must put them back onto the list to
816              * avoid leaking the memory.
817              */
818             cm_AppendServerList(rock.cellp->vlServersp, &vlServersp);
819         }
820     } else {
821         cm_FreeServerList(&rock.cellp->vlServersp, CM_FREESERVERLIST_DELETE);
822         lock_ObtainMutex(&rock.cellp->mx);
823         rock.cellp->flags &= ~CM_CELLFLAG_DNS;
824     }
825     _InterlockedOr(&rock.cellp->flags, CM_CELLFLAG_VLSERVER_INVALID);
826     StringCbCopy(rock.cellp->linkedName, CELL_MAXNAMELEN, linked_cellname);
827     lock_ReleaseMutex(&rock.cellp->mx);
828
829     if (rock.cellp->vlServersp)
830         cm_RandomizeServer(&rock.cellp->vlServersp);
831
832     return code;
833 }