/* * Copyright 2000, International Business Machines Corporation and others. * All Rights Reserved. * * This software has been released under the terms of the IBM Public * License. For details, see the LICENSE file in the top-level source * directory or online at http://www.openafs.org/dl/license10.html */ #include #include #ifndef DJGPP #include #include #include #else #include #endif /* !DJGPP */ #include #include #include #include #include "afsd.h" osi_rwlock_t cm_volumeLock; long cm_ValidateVolume(void) { cm_volume_t * volp; afs_uint32 count; for (volp = cm_data.allVolumesp, count = 0; volp; volp=volp->nextp, count++) { if ( volp->magic != CM_VOLUME_MAGIC ) { afsi_log("cm_ValidateVolume failure: volp->magic != CM_VOLUME_MAGIC"); fprintf(stderr, "cm_ValidateVolume failure: volp->magic != CM_VOLUME_MAGIC\n"); return -1; } if ( volp->cellp && volp->cellp->magic != CM_CELL_MAGIC ) { afsi_log("cm_ValidateVolume failure: volp->cellp->magic != CM_CELL_MAGIC"); fprintf(stderr, "cm_ValidateVolume failure: volp->cellp->magic != CM_CELL_MAGIC\n"); return -2; } if ( volp->nextp && volp->nextp->magic != CM_VOLUME_MAGIC ) { afsi_log("cm_ValidateVolume failure: volp->nextp->magic != CM_VOLUME_MAGIC"); fprintf(stderr, "cm_ValidateVolume failure: volp->nextp->magic != CM_VOLUME_MAGIC\n"); return -3; } if ( count != 0 && volp == cm_data.allVolumesp || count > cm_data.maxVolumes ) { afsi_log("cm_ValidateVolume failure: cm_data.allVolumep loop detected"); fprintf(stderr, "cm_ValidateVolume failure: cm_data.allVolumep loop detected\n"); return -4; } } if ( count != cm_data.currentVolumes ) { afsi_log("cm_ValidateVolume failure: count != cm_data.currentVolumes"); fprintf(stderr, "cm_ValidateVolume failure: count != cm_data.currentVolumes\n"); return -5; } return 0; } long cm_ShutdownVolume(void) { cm_volume_t * volp; for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) lock_FinalizeMutex(&volp->mx); return 0; } void cm_InitVolume(int newFile, long maxVols) { static osi_once_t once; if (osi_Once(&once)) { lock_InitializeRWLock(&cm_volumeLock, "cm global volume lock"); if ( newFile ) { cm_data.allVolumesp = NULL; cm_data.currentVolumes = 0; cm_data.maxVolumes = maxVols; } else { cm_volume_t * volp; for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) { lock_InitializeMutex(&volp->mx, "cm_volume_t mutex"); volp->flags |= CM_VOLUMEFLAG_RESET; volp->rwServersp = NULL; volp->roServersp = NULL; volp->bkServersp = NULL; } } osi_EndOnce(&once); } } /* * Update a volume. Caller holds volume's lock (volp->mx). * * * shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU} 01:38 (JHutz) * Yes, we support multihomed fileservers. * Since before we got the code from IBM. * But to find out about multiple addresses on a multihomed server, you need * to use VL_GetEntryByNameU and VL_GetAddrsU. If you use * VL_GetEntryByNameO or VL_GetEntryByNameN, the vlserver just gives you one * address per server. * shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU} 01:39 (JHutz) * see src/afs/afs_volume.c, paying particular attention to * afs_NewVolumeByName, afs_SetupVolume, and InstallUVolumeEntry * shadow / openafs / jaltman {ANDREW.CMU.EDU} 01:40 (Jeffrey Altman) * thanks. The windows client calls the 0 versions. * shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU} 01:51 (JHutz) * Oh. Ew. * By not using the N versions, you only get up to 8 sites instead of 13. * By not using the U versions, you don't get to know about multihomed serve * shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU} 01:52 (JHutz) * Of course, you probably want to support the older versions for backward * compatibility. If you do that, you need to call the newest interface * first, and fall back to successively older versions if you get * RXGEN_OPCODE. */ #define MULTIHOMED 1 long cm_UpdateVolume(struct cm_cell *cellp, cm_user_t *userp, cm_req_t *reqp, cm_volume_t *volp) { cm_conn_t *connp; int i, j, k; cm_serverRef_t *tsrp; cm_server_t *tsp; struct sockaddr_in tsockAddr; long tflags; u_long tempAddr; struct vldbentry vldbEntry; struct nvldbentry nvldbEntry; #ifdef MULTIHOMED struct uvldbentry uvldbEntry; #endif int type = -1; int ROcount = 0; long code; /* clear out old bindings */ if (volp->rwServersp) cm_FreeServerList(&volp->rwServersp); if (volp->roServersp) cm_FreeServerList(&volp->roServersp); if (volp->bkServersp) cm_FreeServerList(&volp->bkServersp); #ifdef AFS_FREELANCE_CLIENT if ( cellp->cellID == AFS_FAKE_ROOT_CELL_ID && atoi(volp->namep)==AFS_FAKE_ROOT_VOL_ID ) { memset(&vldbEntry, 0, sizeof(vldbEntry)); vldbEntry.flags |= VLF_RWEXISTS; vldbEntry.volumeId[0] = AFS_FAKE_ROOT_VOL_ID; code = 0; type = 0; } else #endif { /* now we have volume structure locked and held; make RPC to fill it */ do { code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp); if (code) continue; osi_Log1(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s", volp->namep); #ifdef MULTIHOMED code = VL_GetEntryByNameU(connp->callp, volp->namep, &uvldbEntry); type = 2; if ( code == RXGEN_OPCODE ) #endif { code = VL_GetEntryByNameN(connp->callp, volp->namep, &nvldbEntry); type = 1; } if ( code == RXGEN_OPCODE ) { code = VL_GetEntryByNameO(connp->callp, volp->namep, &vldbEntry); type = 0; } } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code)); code = cm_MapVLRPCError(code, reqp); } if (code == 0) { afs_int32 flags; afs_int32 nServers; afs_int32 rwID; afs_int32 roID; afs_int32 bkID; afs_int32 serverNumber[NMAXNSERVERS]; afs_int32 serverFlags[NMAXNSERVERS]; switch ( type ) { case 0: flags = vldbEntry.flags; nServers = vldbEntry.nServers; rwID = vldbEntry.volumeId[0]; roID = vldbEntry.volumeId[1]; bkID = vldbEntry.volumeId[2]; for ( i=0; ivlServersp, userp, reqp, &connp); if (code) continue; code = VL_GetAddrsU(connp->callp, &attrs, &uuid, &unique, &nentries, &addrs); if (code == 0 && nentries == 0) code = VL_NOENT; } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code)); code = cm_MapVLRPCError(code, reqp); if (code) return code; addrp = addrs.bulkaddrs_val; for (k = 0; k < nentries && j < NMAXNSERVERS; j++, k++) { serverFlags[j] = uvldbEntry.serverFlags[i]; serverNumber[j] = addrp[k]; } free(addrs.bulkaddrs_val); /* This is wrong */ } } nServers = j; /* update the server count */ break; #endif } /* decode the response */ lock_ObtainWrite(&cm_volumeLock); if (flags & VLF_RWEXISTS) volp->rwID = rwID; else volp->rwID = 0; if (flags & VLF_ROEXISTS) volp->roID = roID; else volp->roID = 0; if (flags & VLF_BACKEXISTS) volp->bkID = bkID; else volp->bkID = 0; lock_ReleaseWrite(&cm_volumeLock); for (i=0; icellp ) tsp->cellp = cellp; osi_assert(tsp != NULL); /* and add it to the list(s). */ /* * Each call to cm_NewServerRef() increments the * ref count of tsp. These reference will be dropped, * if and when the volume is reset; see reset code * earlier in this function. */ if ((tflags & VLSF_RWVOL) && (flags & VLF_RWEXISTS)) { tsrp = cm_NewServerRef(tsp); cm_InsertServerList(&volp->rwServersp, tsrp); lock_ObtainWrite(&cm_serverLock); tsrp->refCount--; /* drop allocation reference */ lock_ReleaseWrite(&cm_serverLock); } if ((tflags & VLSF_ROVOL) && (flags & VLF_ROEXISTS)) { tsrp = cm_NewServerRef(tsp); cm_InsertServerList(&volp->roServersp, tsrp); lock_ObtainWrite(&cm_serverLock); tsrp->refCount--; /* drop allocation reference */ lock_ReleaseWrite(&cm_serverLock); ROcount++; } /* We don't use VLSF_BACKVOL !?! */ if ((tflags & VLSF_RWVOL) && (flags & VLF_BACKEXISTS)) { tsrp = cm_NewServerRef(tsp); cm_InsertServerList(&volp->bkServersp, tsrp); lock_ObtainWrite(&cm_serverLock); tsrp->refCount--; /* drop allocation reference */ lock_ReleaseWrite(&cm_serverLock); } /* Drop the reference obtained by cm_FindServer() */ cm_PutServer(tsp); } /* * Randomize RO list * * If the first n servers have the same ipRank, then we * randomly pick one among them and move it to the beginning. * We don't bother to re-order the whole list because * the rest of the list is used only if the first server is * down. We only do this for the RO list; we assume the other * lists are length 1. */ if (ROcount > 1) { cm_RandomizeServer(&volp->roServersp); } } return code; } long cm_GetVolumeByID(cm_cell_t *cellp, long volumeID, cm_user_t *userp, cm_req_t *reqp, cm_volume_t **outVolpp) { cm_volume_t *volp; char volNameString[100]; long code; lock_ObtainWrite(&cm_volumeLock); for(volp = cm_data.allVolumesp; volp; volp=volp->nextp) { if (cellp == volp->cellp && ((unsigned) volumeID == volp->rwID || (unsigned) volumeID == volp->roID || (unsigned) volumeID == volp->bkID)) break; } /* hold the volume if we found it */ if (volp) volp->refCount++; lock_ReleaseWrite(&cm_volumeLock); /* return it held */ if (volp) { lock_ObtainMutex(&volp->mx); if (volp->flags & CM_VOLUMEFLAG_RESET) { code = cm_UpdateVolume(cellp, userp, reqp, volp); if (code == 0) { volp->flags &= ~CM_VOLUMEFLAG_RESET; } } else code = 0; lock_ReleaseMutex(&volp->mx); if (code == 0) *outVolpp = volp; return code; } /* otherwise, we didn't find it so consult the VLDB */ sprintf(volNameString, "%u", volumeID); code = cm_GetVolumeByName(cellp, volNameString, userp, reqp, 0, outVolpp); return code; } long cm_GetVolumeByName(struct cm_cell *cellp, char *volumeNamep, struct cm_user *userp, struct cm_req *reqp, long flags, cm_volume_t **outVolpp) { cm_volume_t *volp; long code; /* initialize this */ code = 0; lock_ObtainWrite(&cm_volumeLock); for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) { if (cellp == volp->cellp && strcmp(volumeNamep, volp->namep) == 0) { break; } } /* otherwise, get from VLDB */ if (!volp) { if ( cm_data.currentVolumes >= cm_data.maxVolumes ) { for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) { if ( volp->refCount == 0 ) { /* There is one we can re-use */ break; } } osi_panic("Exceeded Max Volumes", __FILE__, __LINE__); } if (volp) { volp->rwID = volp->roID = volp->bkID = 0; volp->dotdotFid.cell = 0; volp->dotdotFid.volume = 0; volp->dotdotFid.unique = 0; volp->dotdotFid.vnode = 0; } else { volp = &cm_data.volumeBaseAddress[cm_data.currentVolumes++]; memset(volp, 0, sizeof(cm_volume_t)); volp->magic = CM_VOLUME_MAGIC; volp->nextp = cm_data.allVolumesp; cm_data.allVolumesp = volp; lock_InitializeMutex(&volp->mx, "cm_volume_t mutex"); } volp->cellp = cellp; strncpy(volp->namep, volumeNamep, VL_MAXNAMELEN); volp->namep[VL_MAXNAMELEN-1] = '\0'; volp->refCount = 1; /* starts off held */ volp->flags = CM_VOLUMEFLAG_RESET; } else { volp->refCount++; } /* next should work since no one could have gotten ptr to this structure yet */ lock_ReleaseWrite(&cm_volumeLock); lock_ObtainMutex(&volp->mx); if (volp->flags & CM_VOLUMEFLAG_RESET) { code = cm_UpdateVolume(cellp, userp, reqp, volp); if (code == 0) volp->flags &= ~CM_VOLUMEFLAG_RESET; } if (code == 0) *outVolpp = volp; lock_ReleaseMutex(&volp->mx); return code; } void cm_ForceUpdateVolume(cm_fid_t *fidp, cm_user_t *userp, cm_req_t *reqp) { cm_cell_t *cellp; cm_volume_t *volp; if (!fidp) return; cellp = cm_FindCellByID(fidp->cell); if (!cellp) return; /* search for the volume */ lock_ObtainWrite(&cm_volumeLock); for(volp = cm_data.allVolumesp; volp; volp=volp->nextp) { if (cellp == volp->cellp && (fidp->volume == volp->rwID || fidp->volume == volp->roID || fidp->volume == volp->bkID)) break; } /* hold the volume if we found it */ if (volp) volp->refCount++; lock_ReleaseWrite(&cm_volumeLock); /* update it */ cm_data.mountRootGen = time(NULL); lock_ObtainMutex(&volp->mx); volp->flags |= CM_VOLUMEFLAG_RESET; #ifdef COMMENT /* Mark the volume to be updated but don't update it now. * This function is called only from within cm_Analyze * when cm_ConnByMServers has failed with all servers down * The problem is that cm_UpdateVolume is going to call * cm_ConnByMServers which may cause a recursive chain * of calls each returning a retry on failure. * Instead, set the flag so the next time the volume is * accessed by Name or ID the UpdateVolume call will * occur. */ code = cm_UpdateVolume(cellp, userp, reqp, volp); if (code == 0) volp->flags &= ~CM_VOLUMEFLAG_RESET; #endif lock_ReleaseMutex(&volp->mx); cm_PutVolume(volp); } /* find the appropriate servers from a volume */ cm_serverRef_t **cm_GetVolServers(cm_volume_t *volp, unsigned long volume) { cm_serverRef_t **serverspp; cm_serverRef_t *current;; lock_ObtainWrite(&cm_serverLock); if (volume == volp->rwID) serverspp = &volp->rwServersp; else if (volume == volp->roID) serverspp = &volp->roServersp; else if (volume == volp->bkID) serverspp = &volp->bkServersp; else osi_panic("bad volume ID in cm_GetVolServers", __FILE__, __LINE__); for (current = *serverspp; current; current = current->next) current->refCount++; lock_ReleaseWrite(&cm_serverLock); return serverspp; } void cm_PutVolume(cm_volume_t *volp) { lock_ObtainWrite(&cm_volumeLock); osi_assert(volp->refCount-- > 0); lock_ReleaseWrite(&cm_volumeLock); } /* return the read-only volume, if there is one, or the read-write volume if * not. */ long cm_GetROVolumeID(cm_volume_t *volp) { long id; lock_ObtainMutex(&volp->mx); if (volp->roID && volp->roServersp) id = volp->roID; else id = volp->rwID; lock_ReleaseMutex(&volp->mx); return id; } void cm_CheckVolumes(void) { cm_volume_t *volp; cm_data.mountRootGen = time(NULL); lock_ObtainWrite(&cm_volumeLock); for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) { volp->refCount++; lock_ReleaseWrite(&cm_volumeLock); lock_ObtainMutex(&volp->mx); volp->flags |= CM_VOLUMEFLAG_RESET; lock_ReleaseMutex(&volp->mx); lock_ObtainWrite(&cm_volumeLock); osi_assert(volp->refCount-- > 0); } lock_ReleaseWrite(&cm_volumeLock); /* We should also refresh cached mount points */ } /* ** Finds all volumes that reside on this server and reorders their ** RO list according to the changed rank of server. */ void cm_ChangeRankVolume(cm_server_t *tsp) { int code; cm_volume_t* volp; /* find volumes which might have RO copy on server*/ lock_ObtainWrite(&cm_volumeLock); for(volp = cm_data.allVolumesp; volp; volp=volp->nextp) { code = 1 ; /* assume that list is unchanged */ volp->refCount++; lock_ReleaseWrite(&cm_volumeLock); lock_ObtainMutex(&volp->mx); if ((tsp->cellp==volp->cellp) && (volp->roServersp)) code =cm_ChangeRankServer(&volp->roServersp, tsp); /* this volume list was changed */ if ( !code ) cm_RandomizeServer(&volp->roServersp); lock_ReleaseMutex(&volp->mx); lock_ObtainWrite(&cm_volumeLock); osi_assert(volp->refCount-- > 0); } lock_ReleaseWrite(&cm_volumeLock); }