Windows: preserve prior vlserver list on dns failure
[openafs.git] / src / WINNT / afsd / cm_cell.c
index 2fd0237..7d37fed 100644 (file)
@@ -44,20 +44,8 @@ long cm_AddCellProc(void *rockp, struct sockaddr_in *addrp, char *hostnamep, uns
     cellp = cellrockp->cellp;
     probe = !(cellrockp->flags & CM_FLAG_NOPROBE);
 
-    /* if this server was previously created by fs setserverprefs */
-    if ( tsp = cm_FindServer(addrp, CM_SERVER_VLDB, FALSE))
-    {
-        if ( !tsp->cellp )
-            tsp->cellp = cellp;
-        else if (tsp->cellp != cellp) {
-            osi_Log3(afsd_logp, "found a vlserver %s associated with two cells named %s and %s",
-                     osi_LogSaveString(afsd_logp,hostnamep),
-                     osi_LogSaveString(afsd_logp,tsp->cellp->name),
-                     osi_LogSaveString(afsd_logp,cellp->name));
-        }
-    }
-    else
-        tsp = cm_NewServer(addrp, CM_SERVER_VLDB, cellp, NULL, probe ? 0 : CM_FLAG_NOPROBE);
+    tsp = cm_NewServer(addrp, CM_SERVER_VLDB, cellp, NULL,
+                      probe ? 0 : CM_FLAG_NOPROBE);
 
     if (adminRank)
         tsp->adminRank = adminRank;
@@ -354,6 +342,8 @@ cm_cell_t *cm_GetCell_Gen(char *namep, char *newnamep, afs_uint32 flags)
         }
 
         if (cp2) {
+           cm_server_t *tsp;
+
             if (!hasMutex) {
                 lock_ObtainMutex(&cp->mx);
                 hasMutex = 1;
@@ -364,7 +354,24 @@ cm_cell_t *cm_GetCell_Gen(char *namep, char *newnamep, afs_uint32 flags)
             cm_RemoveCellFromNameHashTable(cp);
             lock_ReleaseMutex(&cp->mx);
             hasMutex = 0;
-            cm_FreeCell(cp);
+
+           /* Replace cells references in servers */
+           lock_ObtainRead(&cm_serverLock);
+           for (tsp = cm_serversAllFirstp;
+                 tsp;
+                 tsp = (cm_server_t *)osi_QNext(&tsp->allq)) {
+               if (tsp->cellp == cp) {
+                   osi_Log4(afsd_logp, "cm_GetCell_gen race: replacing cell (%u) %s with cell (%u) %s",
+                             cp->cellID,
+                             osi_LogSaveString(afsd_logp,cp->name),
+                             cp2->cellID,
+                             osi_LogSaveString(afsd_logp,cp2->name));
+                   tsp->cellp = cp2;
+               }
+           }
+           lock_ReleaseRead(&cm_serverLock);
+
+           cm_FreeCell(cp);
             cp = cp2;
             goto done;
         }
@@ -469,6 +476,14 @@ cm_ValidateCell(void)
     afs_uint32 count1, count2;
 
     for (cellp = cm_data.allCellsp, count1 = 0; cellp; cellp=cellp->allNextp, count1++) {
+
+       if ( cellp < (cm_cell_t *)cm_data.cellBaseAddress ||
+            cellp >= (cm_cell_t *)cm_data.aclBaseAddress) {
+           afsi_log("cm_ValidateCell failure: out of range cm_cell_t pointers");
+           fprintf(stderr, "cm_ValidateCell failure: out of range cm_cell_t pointers\n");
+           return -10;
+       }
+
         if ( cellp->magic != CM_CELL_MAGIC ) {
             afsi_log("cm_ValidateCell failure: cellp->magic != CM_CELL_MAGIC");
             fprintf(stderr, "cm_ValidateCell failure: cellp->magic != CM_CELL_MAGIC\n");
@@ -483,6 +498,14 @@ cm_ValidateCell(void)
     }
 
     for (cellp = cm_data.freeCellsp, count2 = 0; cellp; cellp=cellp->freeNextp, count2++) {
+
+       if ( cellp < (cm_cell_t *)cm_data.cellBaseAddress ||
+            cellp >= (cm_cell_t *)cm_data.aclBaseAddress) {
+           afsi_log("cm_ValidateCell failure: out of range cm_cell_t pointers");
+           fprintf(stderr, "cm_ValidateCell failure: out of range cm_cell_t pointers\n");
+           return -11;
+       }
+
         if ( count2 != 0 && cellp == cm_data.freeCellsp ||
              count2 > cm_data.maxCells ) {
             afsi_log("cm_ValidateCell failure: cm_data.freeCellsp infinite loop");
@@ -724,16 +747,27 @@ cm_CreateCellWithInfo( char * cellname,
     rock.cellp = cm_GetCell(cellname, CM_FLAG_CREATE | CM_FLAG_NOPROBE);
     rock.flags = 0;
 
-    cm_FreeServerList(&rock.cellp->vlServersp, CM_FREESERVERLIST_DELETE);
-
     if (!(flags & CM_CELLFLAG_DNS)) {
+       int first = 1;
+
         for (i = 0; i < host_count; i++) {
             thp = gethostbyname(hostname[i]);
+           if (first) {
+               /*
+                * If there is at least one resolved vlserver or an authoritative,
+                * host not found response, destroy the prior list.
+                */
+               if (thp != NULL || WSAGetLastError() == WSAHOST_NOT_FOUND) {
+                   cm_FreeServerList(&rock.cellp->vlServersp, CM_FREESERVERLIST_DELETE);
+                   first = 0;
+               }
+           }
+
             if (thp) {
-                int foundAddr = 0;
+               if (thp->h_addrtype != AF_INET)
+                   continue;
+
                 for (j=0 ; thp->h_addr_list[j]; j++) {
-                    if (thp->h_addrtype != AF_INET)
-                        continue;
                     memcpy(&vlSockAddr.sin_addr.s_addr,
                            thp->h_addr_list[j],
                            sizeof(long));
@@ -747,6 +781,12 @@ cm_CreateCellWithInfo( char * cellname,
         _InterlockedAnd(&rock.cellp->flags, ~CM_CELLFLAG_DNS);
     } else if (cm_dnsEnabled) {
         int ttl;
+       cm_serverRef_t * vlServersp = NULL;
+
+       lock_ObtainWrite(&cm_serverLock);
+       vlServersp = rock.cellp->vlServersp;
+       rock.cellp->vlServersp = NULL;
+       lock_ReleaseWrite(&cm_serverLock);
 
         code = cm_SearchCellByDNS(rock.cellp->name, NULL, &ttl, cm_AddCellProc, &rock);
         lock_ObtainMutex(&rock.cellp->mx);
@@ -756,8 +796,29 @@ cm_CreateCellWithInfo( char * cellname,
 #ifdef DEBUG
             fprintf(stderr, "cell %s: ttl=%d\n", rock.cellp->name, ttl);
 #endif
-        }
+       } else {
+           lock_ObtainWrite(&cm_serverLock);
+           if (rock.cellp->vlServersp == NULL) {
+               rock.cellp->vlServersp = vlServersp;
+               vlServersp = NULL;
+           }
+           lock_ReleaseWrite(&cm_serverLock);
+       }
+
+       cm_FreeServerList(&vlServersp, CM_FREESERVERLIST_DELETE);
+       if (vlServersp != NULL) {
+           /*
+            * We moved the vlServer list out of the way and
+            * in the meantime it was replaced.  If the vlServerp
+            * list is non-Empty after cm_FreeServerList was called
+            * it means that there are deleted entries with active
+            * references.  Must put them back onto the list to
+            * avoid leaking the memory.
+            */
+           cm_AppendServerList(rock.cellp->vlServersp, &vlServersp);
+       }
     } else {
+       cm_FreeServerList(&rock.cellp->vlServersp, CM_FREESERVERLIST_DELETE);
         lock_ObtainMutex(&rock.cellp->mx);
         rock.cellp->flags &= ~CM_CELLFLAG_DNS;
     }