/* * 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 */ extern "C" { #include #include } #include #include "internal.h" /* * DEFINITIONS ________________________________________________________________ * */ // Each CELL object maintains a list of servers; that list has // hashtables placed across the (shortened) server name for // faster lookup; it also maintains a hashtable across the // servers' primary IP address (the first in the list; most // servers will only have one anyway). The default table size // in a HASHLIST is 1000 elements--that's too large for a list // of servers-in-a-cell, as it's enough to handle up to 30,000 // servers before the table would need to resize iteself (see // the documentation in hashlist.cpp for info). Instead, we // choose a more reasonable default table size. // #define cKEYSERVERNAME_TABLESIZE 50 #define cKEYSERVERADDR_TABLESIZE 50 // Enable the definition below to do a better job of finding // user entries in PTS which have no KAS entries (for instance, // machine IP accounts). // #define FIND_PTS_DEBRIS /* * VARIABLES __________________________________________________________________ * */ LPHASHLIST CELL::x_lCells = NULL; /* * CONSTRUCTION _______________________________________________________________ * */ void CELL::InitClass (void) { if (x_lCells == NULL) { x_lCells = New (HASHLIST); x_lCells->SetCriticalSection (AfsClass_GetCriticalSection()); } } CELL::CELL (LPTSTR pszCellName, PVOID hCreds) { AfsClass_Enter(); InitClass(); m_hCell = 0; m_hKas = 0; lstrcpy (m_szName, pszCellName); m_nReqs = 0; m_hCreds = hCreds; m_apszServers = 0; m_fStatusOutOfDate = TRUE; m_fVLDBOutOfDate = TRUE; m_lpiThis = NULL; m_fServersOutOfDate = TRUE; m_nServersUnmonitored = 0; m_lServers = New (HASHLIST); m_lServers->SetCriticalSection (AfsClass_GetCriticalSection()); m_lkServerName = m_lServers->CreateKey ("Server Name", CELL::KeyServerName_Compare, CELL::KeyServerName_HashObject, CELL::KeyServerName_HashData, cKEYSERVERNAME_TABLESIZE); m_lkServerAddr = m_lServers->CreateKey ("Server Primary Address", CELL::KeyServerAddr_Compare, CELL::KeyServerAddr_HashObject, CELL::KeyServerAddr_HashData, cKEYSERVERADDR_TABLESIZE); m_fUsersOutOfDate = TRUE; m_lUsers = New (HASHLIST); m_lUsers->SetCriticalSection (AfsClass_GetCriticalSection()); m_lkUserName = m_lUsers->CreateKey ("User Name", CELL::KeyUserName_Compare, CELL::KeyUserName_HashObject, CELL::KeyUserName_HashData); m_lGroups = New (HASHLIST); m_lGroups->SetCriticalSection (AfsClass_GetCriticalSection()); m_lkGroupName = m_lGroups->CreateKey ("Group Name", CELL::KeyGroupName_Compare, CELL::KeyGroupName_HashObject, CELL::KeyGroupName_HashData); AfsClass_Leave(); } CELL::~CELL (void) { FreeUsers (FALSE); FreeServers (FALSE); if (m_lpiThis) m_lpiThis->m_cRef --; Delete (m_lServers); } void CELL::FreeServers (BOOL fNotify) { for (LPENUM pEnum = m_lServers->FindLast(); pEnum; pEnum = pEnum->FindPrevious()) { LPSERVER lpServer = (LPSERVER)(pEnum->GetObject()); if (fNotify) lpServer->SendDeleteNotifications(); m_lServers->Remove (lpServer); Delete (lpServer); } if (m_apszServers) { for (size_t ii = 0; m_apszServers[ii]; ++ii) FreeString (m_apszServers[ii]); Free (m_apszServers); m_apszServers = NULL; } } void CELL::FreeUsers (BOOL fNotify) { for (LPENUM pEnum = m_lGroups->FindLast(); pEnum; pEnum = pEnum->FindPrevious()) { LPPTSGROUP lpGroup = (LPPTSGROUP)(pEnum->GetObject()); if (fNotify) lpGroup->SendDeleteNotifications(); m_lGroups->Remove (lpGroup); Delete (lpGroup); } for (pEnum = m_lUsers->FindLast(); pEnum; pEnum = pEnum->FindPrevious()) { LPUSER lpUser = (LPUSER)(pEnum->GetObject()); if (fNotify) lpUser->SendDeleteNotifications(); m_lUsers->Remove (lpUser); Delete (lpUser); } } /* * CELL-LIST MANAGEMENT _______________________________________________________ * */ void CELL::Close (void) { AfsClass_Leave(); } BOOL CELL::GetDefaultCell (LPTSTR pszName, ULONG *pStatus) { WORKERPACKET wp; wp.wpClientLocalCellGet.pszCell = pszName; return Worker_DoTask (wtaskClientLocalCellGet, &wp, pStatus); } LPIDENT CELL::OpenCell (LPTSTR pszCell, PVOID hCreds, ULONG *pStatus) { LPIDENT lpiCell = NULL; AfsClass_Enter(); InitClass(); LPCELL lpCell; if ((lpCell = ReopenCell (pszCell, pStatus)) != NULL) { lpiCell = lpCell->GetIdentifier(); lpCell->m_nReqs++; lpCell->Close(); } else // cell hasn't been opened before? see if we can reach the cell. { lpCell = New2 (CELL,(pszCell, hCreds)); if ((lpCell->m_hCell = lpCell->GetCellObject (pStatus)) == NULL) Delete (lpCell); else { lpiCell = lpCell->GetIdentifier(); lpCell->m_nReqs = 1; x_lCells->Add (lpCell); NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpiCell); } } AfsClass_Leave(); return lpiCell; } void CELL::CloseCell (LPIDENT lpiCell) { LPCELL lpCell; if ((lpCell = lpiCell->OpenCell()) != NULL) { if (lpCell->m_nReqs > 1) { lpCell->m_nReqs--; lpCell->Close(); } else { NOTIFYCALLBACK::SendNotificationToAll (evtDestroy, lpiCell); lpCell->CloseKasObject(); lpCell->CloseCellObject(); lpCell->Close(); x_lCells->Remove (lpCell); Delete (lpCell); } } } LPCELL CELL::ReopenCell (LPTSTR pszCell, ULONG *pStatus) { LPCELL lpCell = NULL; AfsClass_Enter(); InitClass(); // Ordinarily we'd place a key on the cell name within the list of // cells--however, the most likely case only has one cell anyway. // So why expend the memory? // for (LPENUM pEnum = x_lCells->FindFirst(); pEnum; pEnum = pEnum->FindNext()) { LPCELL lpCellFound = (LPCELL)( pEnum->GetObject() ); if (!lstrcmpi (lpCellFound->m_szName, pszCell)) { lpCell = lpCellFound; Delete (pEnum); break; } } if (lpCell == NULL) { AfsClass_Leave(); if (pStatus) *pStatus = ERROR_FILE_NOT_FOUND; } // AfsClass_Leave() has been called only if no cell was opened in the search. return lpCell; } PVOID CELL::GetCurrentCredentials (void) { return m_hCreds; } void CELL::SetCurrentCredentials (PVOID hCreds) { CloseCellObject(); m_hCreds = hCreds; GetCellObject(); } /* * SERVER-LIST MANAGEMENT _____________________________________________________ * */ LPSERVER CELL::OpenServer (LPTSTR pszName, ULONG *pStatus) { if (!RefreshServers (TRUE, pStatus)) return NULL; LPSERVER lpServer; if ((lpServer = (LPSERVER)(m_lkServerName->GetFirstObject (pszName))) != NULL) AfsClass_Enter(); return lpServer; } LPSERVER CELL::OpenServer (LPSOCKADDR_IN pAddr, ULONG *pStatus) { if (!RefreshServers (TRUE, pStatus)) return NULL; // We'll try to use our lookup key first--since most machines only // have one IP address anyway, our hashtable should make this lookup // super fast. If it misses (i.e., if the server is multi-homed and // for some reason VLDB refers to it by the second address), we'll // have to do a brute-force search across each server in the cell. // Again, we could have a better-designed lookup table here--but // since multi-homing is the exception (by a vast majority), it's not // worth the extra effort and memory. This technique is fast enough. // LPSERVER lpServer; if ((lpServer = (LPSERVER)(m_lkServerAddr->GetFirstObject (pAddr))) != NULL) { AfsClass_Enter(); // Aren't HashLists great? We found the server. } else // Try brute-force search { HENUM hEnum; for (lpServer = ServerFindFirst (&hEnum, TRUE, pStatus); lpServer; lpServer = ServerFindNext (&hEnum)) { SERVERSTATUS ss; if (lpServer->GetStatus (&ss, TRUE, pStatus)) { for (size_t iAddr = 0; iAddr < ss.nAddresses; ++iAddr) { if (!memcmp (&ss.aAddresses[ iAddr ], pAddr, sizeof(SOCKADDR_IN))) { // don't close server! we're going to return this pointer. break; } } } lpServer->Close(); } ServerFindClose (&hEnum); } return lpServer; } LPSERVER CELL::ServerFindFirst (HENUM *phEnum, BOOL fNotify, ULONG *pStatus) { return ServerFindFirst (phEnum, NULL, fNotify, pStatus); } LPSERVER CELL::ServerFindFirst (HENUM *phEnum, LPIDENT lpiFind, BOOL fNotify, ULONG *pStatus) { LPSERVER lpServer = NULL; if (!RefreshServers (fNotify, pStatus)) return NULL; if (lpiFind != NULL) { lpServer = lpiFind->OpenServer(); *phEnum = NULL; } else if ((*phEnum = m_lServers->FindFirst()) != NULL) { lpServer = (LPSERVER)( (*phEnum)->GetObject() ); AfsClass_Enter(); } if (!lpServer && pStatus) *pStatus = ERROR_FILE_NOT_FOUND; return lpServer; } LPSERVER CELL::ServerFindNext (HENUM *phEnum) { LPSERVER lpServer = NULL; if (*phEnum) { if ((*phEnum = (*phEnum)->FindNext()) != NULL) { lpServer = (LPSERVER)( (*phEnum)->GetObject() ); AfsClass_Enter(); } } return lpServer; } void CELL::ServerFindClose (HENUM *phEnum) { if (*phEnum) { Delete (*phEnum); *phEnum = NULL; } } BOOL CELL::RefreshServers (BOOL fNotify, ULONG *pStatus) { if (!m_fServersOutOfDate) return TRUE; return RefreshServerList (fNotify, pStatus); } BOOL CELL::RefreshServerList (BOOL fNotify, ULONG *pStatus) { BOOL rc = TRUE; ULONG status = 0; BOOL fNotified = FALSE; if (fNotify && m_fServersOutOfDate) { NOTIFYCALLBACK::SendNotificationToAll (evtRefreshServersBegin, GetIdentifier()); fNotified = TRUE; } BOOL fReplaceList = m_fServersOutOfDate; m_fServersOutOfDate = FALSE; // Ordinarily we'd just clear the list of servers and // requery it from scratch; however, servers are an exception // to that technique: occasionally we may get a request to // just look for servers that have appeared or disappeared, // without refreshing data for other servers. Thus the revised // technique: // // 1- if fReplaceList, empty the list of servers. // otherwise, set each server's fDelete flag. // // 2- enumerate the servers in the cell: for each server, // if fReplaceList, add the server to the list // otherwise, if the server is in the list, clear its fDelete // // 3- if !fReplaceList, enumerate the list of servers: for each server, // if the server's fDelete flag is set, remove the server // for (LPENUM pEnum = m_lServers->FindFirst(); pEnum; pEnum = pEnum->FindNext()) { LPSERVER lpServer = (LPSERVER)(pEnum->GetObject()); if (fReplaceList) { lpServer->SendDeleteNotifications(); m_lServers->Remove (lpServer); Delete (lpServer); } else // the list of servers isn't invalidated, so just mark fDelete { lpServer->m_fDelete = TRUE; } } // Enumerate the servers in the cell. // PVOID hCell; if ((hCell = GetCellObject (&status)) == NULL) { rc = FALSE; FreeUsers (TRUE); FreeServers (TRUE); } else { WORKERPACKET wpBegin; wpBegin.wpClientAFSServerGetBegin.hCell = hCell; if (!Worker_DoTask (wtaskClientAFSServerGetBegin, &wpBegin, &status)) { rc = FALSE; } else { for (;;) { WORKERPACKET wpNext; wpNext.wpClientAFSServerGetNext.hEnum = wpBegin.wpClientAFSServerGetBegin.hEnum; if (!Worker_DoTask (wtaskClientAFSServerGetNext, &wpNext, &status)) { if (status == ADMITERATORDONE) status = 0; else rc = FALSE; break; } afs_serverEntry_p pEntry = &wpNext.wpClientAFSServerGetNext.Entry; TCHAR szServer[ cchNAME ]; CopyAnsiToString (szServer, pEntry->serverName); if (!szServer[0]) { int addrNetwork = htonl (pEntry->serverAddress[0]); lstrcpy (szServer, inet_ntoa (*(struct in_addr *)&addrNetwork)); } // The server identified by {pEntry} is in the cell. Now if we're // building a list of SERVER objects from scratch, we can just // add it--but if we're only touching up an existing list, // first make sure there isn't such an animal in there now. // BOOL fNotifyAboutThisServer = FALSE; LPSERVER lpServer = NULL; if (!fReplaceList) { if ((lpServer = (LPSERVER)(m_lkServerName->GetFirstObject (szServer))) != NULL) lpServer->m_fDelete = FALSE; } if (lpServer == NULL) { // Okay, it's a new server. Create a SERVER object for it and // add it to the list. // lpServer = New2 (SERVER,(this, szServer)); lpServer->m_wGhost |= GHOST_HAS_SERVER_ENTRY; m_lServers->Add (lpServer); fNotifyAboutThisServer = TRUE; } // Update the server's IP addresses // lpServer->m_ss.nAddresses = 0; for (size_t iAddr = 0; iAddr < AFS_MAX_SERVER_ADDRESS; ++iAddr) { if (pEntry->serverAddress[ iAddr ] == 0) continue; AfsClass_IntToAddress (&lpServer->m_ss.aAddresses[ lpServer->m_ss.nAddresses++ ], pEntry->serverAddress[ iAddr ]); } m_lServers->Update (lpServer); // That update affected a hashlistkey // Tell our clients that we've found a server // if (fNotify && fNotifyAboutThisServer) { if (!fNotified) { NOTIFYCALLBACK::SendNotificationToAll (evtRefreshServersBegin, GetIdentifier()); fNotified = TRUE; } NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpServer->GetIdentifier()); } } WORKERPACKET wpDone; wpDone.wpClientAFSServerGetDone.hEnum = wpBegin.wpClientAFSServerGetBegin.hEnum; Worker_DoTask (wtaskClientAFSServerGetDone, &wpDone); } } // Finally, look through our list of servers: if any have fDelete set, // then we didn't find them in the cell any longer. Remove those servers. // if (rc) { for (LPENUM pEnum = m_lServers->FindFirst(); pEnum; pEnum = pEnum->FindNext()) { LPSERVER lpServer = (LPSERVER)(pEnum->GetObject()); if (lpServer->m_fDelete) { if (fNotify && !fNotified) { NOTIFYCALLBACK::SendNotificationToAll (evtRefreshServersBegin, GetIdentifier()); fNotified = TRUE; } lpServer->SendDeleteNotifications(); m_lServers->Remove (lpServer); Delete (lpServer); } } } // Fix m_apszServers if we did anything to the list of servers // if (fNotified) { if (m_apszServers) { for (size_t ii = 0; m_apszServers[ii]; ++ii) FreeString (m_apszServers[ii]); Free (m_apszServers); m_apszServers = NULL; } size_t cServers = 0; for (LPENUM pEnum = m_lServers->FindFirst(); pEnum; pEnum = pEnum->FindNext()) ++cServers; if (cServers) { m_apszServers = (char**)Allocate (sizeof(char*) * (1+cServers)); memset (m_apszServers, 0x00, sizeof(char*) * (1+cServers)); size_t iServer = 0; for (pEnum = m_lServers->FindFirst(); pEnum; pEnum = pEnum->FindNext()) { LPSERVER lpServer = (LPSERVER)(pEnum->GetObject()); m_apszServers[ iServer ] = AllocateAnsi (cchNAME+1); CopyStringToAnsi (m_apszServers[ iServer ], lpServer->m_szName); ++iServer; } } } if (fNotified) NOTIFYCALLBACK::SendNotificationToAll (evtRefreshServersEnd, GetIdentifier(), ((rc) ? 0 : status)); if (pStatus && !rc) *pStatus = status; return rc; } /* * SERVER-LIST KEYS ___________________________________________________________ * */ BOOL CALLBACK CELL::KeyServerName_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData) { if (!lstrcmp (((LPSERVER)pObject)->m_szName, (LPTSTR)pData)) return TRUE; TCHAR szShortName[ cchNAME ]; SERVER::ShortenName (szShortName, ((LPSERVER)pObject)->m_szName); if (!lstrcmp (szShortName, (LPTSTR)pData)) return TRUE; return FALSE; } HASHVALUE CALLBACK CELL::KeyServerName_HashObject (LPHASHLISTKEY pKey, PVOID pObject) { return CELL::KeyServerName_HashData (pKey, ((LPSERVER)pObject)->m_szName); } HASHVALUE CALLBACK CELL::KeyServerName_HashData (LPHASHLISTKEY pKey, PVOID pData) { TCHAR szShortName[ cchNAME ]; SERVER::ShortenName (szShortName, (LPTSTR)pData); return HashString (szShortName); } BOOL CALLBACK CELL::KeyServerAddr_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData) { return !memcmp (&((LPSERVER)pObject)->m_ss.aAddresses[0], (LPSOCKADDR)pData, sizeof(SOCKADDR_IN)); } HASHVALUE CALLBACK CELL::KeyServerAddr_HashObject (LPHASHLISTKEY pKey, PVOID pObject) { return CELL::KeyServerAddr_HashData (pKey, &((LPSERVER)pObject)->m_ss.aAddresses[0]); } HASHVALUE CALLBACK CELL::KeyServerAddr_HashData (LPHASHLISTKEY pKey, PVOID pData) { return *(DWORD*)pData; } BOOL CALLBACK CELL::KeyUserName_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData) { return !lstrcmpi (((LPUSER)pObject)->m_szPrincipal, (LPTSTR)pData); } HASHVALUE CALLBACK CELL::KeyUserName_HashObject (LPHASHLISTKEY pKey, PVOID pObject) { return CELL::KeyUserName_HashData (pKey, ((LPUSER)pObject)->m_szPrincipal); } HASHVALUE CALLBACK CELL::KeyUserName_HashData (LPHASHLISTKEY pKey, PVOID pData) { return HashString ((LPTSTR)pData); } BOOL CALLBACK CELL::KeyGroupName_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData) { return !lstrcmpi (((LPPTSGROUP)pObject)->m_szName, (LPTSTR)pData); } HASHVALUE CALLBACK CELL::KeyGroupName_HashObject (LPHASHLISTKEY pKey, PVOID pObject) { return CELL::KeyGroupName_HashData (pKey, ((LPPTSGROUP)pObject)->m_szName); } HASHVALUE CALLBACK CELL::KeyGroupName_HashData (LPHASHLISTKEY pKey, PVOID pData) { return HashString ((LPTSTR)pData); } /* * CELL OBJECT ________________________________________________________________ * */ PVOID CELL::GetCellObject (ULONG *pStatus) { if (!m_hCell) { ULONG status; NOTIFYCALLBACK::SendNotificationToAll (evtOpenCellBegin, m_szName); WORKERPACKET wpOpen; wpOpen.wpClientCellOpen.pszCell = m_szName; wpOpen.wpClientCellOpen.hCreds = m_hCreds; if (Worker_DoTask (wtaskClientCellOpen, &wpOpen, &status)) m_hCell = wpOpen.wpClientCellOpen.hCell; if (pStatus) *pStatus = status; NOTIFYCALLBACK::SendNotificationToAll (evtOpenCellEnd, m_szName, status); } return m_hCell; } BOOL CELL::CloseCellObject (ULONG *pStatus) { BOOL rc = TRUE; if (m_hCell != NULL) { WORKERPACKET wp; wp.wpClientCellClose.hCell = m_hCell; rc = Worker_DoTask (wtaskClientCellClose, &wp, pStatus); m_hCell = NULL; } return rc; } PVOID CELL::GetKasObject (ULONG *pStatus) { // m_hKas is actually never set non-NULL; // leaving it NULL indicates we will work happily with *any* server. // return m_hKas; } BOOL CELL::CloseKasObject (ULONG *pStatus) { BOOL rc = TRUE; if (m_hKas != NULL) { WORKERPACKET wp; wp.wpKasServerClose.hServer = m_hKas; rc = Worker_DoTask (wtaskKasServerClose, &wp, pStatus); m_hKas = NULL; } return rc; } /* * CELL GENERAL _______________________________________________________________ * */ LPIDENT CELL::GetIdentifier (void) { if (m_lpiThis == NULL) { if ((m_lpiThis = IDENT::FindIdent (this)) == NULL) m_lpiThis = New2 (IDENT,(this)); m_lpiThis->m_cRef ++; } return m_lpiThis; } void CELL::GetName (LPTSTR pszName) { lstrcpy (pszName, m_szName); } PVOID CELL::GetUserParam (void) { return GetIdentifier()->GetUserParam(); } void CELL::SetUserParam (PVOID pUserNew) { GetIdentifier()->SetUserParam (pUserNew); } BOOL CELL::fAnyServersUnmonitored (void) { return (m_nServersUnmonitored > 0) ? TRUE : FALSE; } /* * CELL STATUS ________________________________________________________________ * */ void CELL::Invalidate (void) { if (!m_fServersOutOfDate || !m_fStatusOutOfDate || !m_fVLDBOutOfDate || !m_fUsersOutOfDate) { CloseKasObject(); CloseCellObject(); m_fServersOutOfDate = TRUE; m_fStatusOutOfDate = TRUE; m_fVLDBOutOfDate = TRUE; m_fUsersOutOfDate = TRUE; NOTIFYCALLBACK::SendNotificationToAll (evtInvalidate, GetIdentifier()); } } void CELL::InvalidateServers (void) { if (!m_fServersOutOfDate || !m_fVLDBOutOfDate) { CloseKasObject(); CloseCellObject(); m_fServersOutOfDate = TRUE; m_fVLDBOutOfDate = TRUE; NOTIFYCALLBACK::SendNotificationToAll (evtInvalidate, GetIdentifier()); } } void CELL::InvalidateUsers (void) { if (!m_fUsersOutOfDate) { m_fUsersOutOfDate = TRUE; NOTIFYCALLBACK::SendNotificationToAll (evtInvalidate, GetIdentifier()); } } BOOL CELL::RefreshStatus (BOOL fNotify, ULONG *pStatus) { BOOL rc = TRUE; ULONG status = 0; if (m_fStatusOutOfDate) { if (fNotify) NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusBegin, GetIdentifier()); // Hmmm...well, actually, there's nothing for us to do here. I'm // leaving this around, because the refreshed-cell-status notification // may be useful as an appropriate hooking point. rc = TRUE; status = 0; if (fNotify) NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusEnd, GetIdentifier(), ((rc) ? 0 : status)); } if (pStatus && !rc) *pStatus = status; return rc; } BOOL CELL::RefreshVLDB (BOOL fNotify, ULONG *pStatus) { BOOL rc = TRUE; if (m_fVLDBOutOfDate) { if ((rc = RefreshVLDB (GetIdentifier(), fNotify, pStatus)) == TRUE) { m_fVLDBOutOfDate = FALSE; } } return rc; } BOOL CELL::RefreshVLDB (LPIDENT lpiRef, BOOL fNotify, ULONG *pStatus, BOOL fAnythingRelatedToThisRWFileset) { BOOL rc = TRUE; DWORD status = 0; // What is the scope of this refresh operation? The entire cell, // or the filesets on a particular server or aggregate? // LPIDENT lpiRefCell = (lpiRef == NULL) ? GetIdentifier() : lpiRef->GetCell(); LPIDENT lpiRefServer = NULL; LPIDENT lpiRefAggregate = NULL; LPIDENT lpiRefFileset = NULL; VOLUMEID *pvidRefFileset = NULL; VOLUMEID vidRefFileset; if (fAnythingRelatedToThisRWFileset) { pvidRefFileset = &vidRefFileset; lpiRef->GetFilesetID (pvidRefFileset); } else { if (lpiRef && !lpiRef->fIsCell()) { lpiRefServer = lpiRef->GetServer(); } if (lpiRef && (lpiRef->fIsAggregate() || lpiRef->fIsFileset())) { lpiRefAggregate = lpiRef->GetAggregate(); } if (lpiRef && lpiRef->fIsFileset()) { lpiRefFileset = lpiRef; } } // If we've been told to update only one server, aggregate or // fileset, find out which IP addresses correspond with that // server. We'll need this for comparisons later. // SERVERSTATUS ssRefServer; if (rc && lpiRefServer) { LPSERVER lpServer; if ((lpServer = lpiRefServer->OpenServer (&status)) == NULL) rc = FALSE; else { rc = lpServer->GetStatus (&ssRefServer, fNotify, &status); lpServer->Close(); } } // Likewise, if we've been told to update only one aggregate, // find that aggregate's ID. We'll need it for comparisons later. // AGGREGATESTATUS asRefAggregate; int idPartition = NO_PARTITION; if (rc && lpiRefAggregate) { LPAGGREGATE lpAggregate; if ((lpAggregate = lpiRefAggregate->OpenAggregate (&status)) == NULL) rc = FALSE; else { idPartition = lpAggregate->GetID(); rc = lpAggregate->GetStatus (&asRefAggregate, fNotify, &status); lpAggregate->Close(); } } // Zip through the current list of objects that we're about to refresh. // On each such object, remove the GHOST_HAS_VLDB_ENTRY flag, // and delete objects entirely if that's the only ghost flag they have. // (e.g., If we went through this routine earlier and created a ghost // aggregate because VLDB referenced it and we couldn't find mention // of it on the server, delete that aggregate. We'll recreate it here // if necessary; otherwise, it needs to be gone.) // if (rc) { RefreshVLDB_RemoveReferences (lpiRefServer, lpiRefAggregate, lpiRefFileset, pvidRefFileset); } // We'll get a new list of filesets from VLDB, and to do that, we'll // need the cell's object. If we're enumerating a specific server, we'll // also need that server's object. Finally, if we're enumerating a // specific aggregate, we'll also need that aggregate's name. // PVOID hCell = NULL; PVOID hServer = NULL; if (rc) { if (!lpiRefServer) { if ((hCell = GetCellObject (&status)) == NULL) rc = FALSE; } else // get cell and server handles { LPSERVER lpServer; if ((lpServer = lpiRefServer->OpenServer()) == NULL) rc = FALSE; else { if ((hServer = lpServer->OpenVosObject (&hCell, &status)) == NULL) rc = FALSE; lpServer->Close(); } } } // Go get that list of filesets, and use it to update our knowledge // of the cell. Remember that, if {pvidRefFileset}, we only want // one VLDB entry. // if (rc) { if (pvidRefFileset) { WORKERPACKET wpGet; wpGet.wpVosVLDBGet.hCell = hCell; wpGet.wpVosVLDBGet.idVolume = *pvidRefFileset; if (!Worker_DoTask (wtaskVosVLDBGet, &wpGet, &status)) rc = FALSE; else RefreshVLDB_OneEntry (&wpGet.wpVosVLDBGet.Data, lpiRefServer, &ssRefServer, lpiRefAggregate, &asRefAggregate, lpiRefFileset, pvidRefFileset, fNotify); } else { WORKERPACKET wpBegin; wpBegin.wpVosVLDBGetBegin.hCell = hCell; wpBegin.wpVosVLDBGetBegin.hServer = hServer; wpBegin.wpVosVLDBGetBegin.idPartition = idPartition; if (!Worker_DoTask (wtaskVosVLDBGetBegin, &wpBegin, &status)) rc = FALSE; else { for (;;) { WORKERPACKET wpNext; wpNext.wpVosVLDBGetNext.hEnum = wpBegin.wpVosVLDBGetBegin.hEnum; if (!Worker_DoTask (wtaskVosVLDBGetNext, &wpNext, &status)) { if (status == ADMITERATORDONE) status = 0; else rc = FALSE; break; } RefreshVLDB_OneEntry (&wpNext.wpVosVLDBGetNext.Data, lpiRefServer, &ssRefServer, lpiRefAggregate, &asRefAggregate, lpiRefFileset, pvidRefFileset, fNotify); } WORKERPACKET wpDone; wpDone.wpVosVLDBGetDone.hEnum = wpBegin.wpVosVLDBGetBegin.hEnum; Worker_DoTask (wtaskVosVLDBGetDone, &wpDone); } } } // We've finished the update. If we were asked to send notifications // about our progress, do so. // if (fNotify) { LPIDENT lpiNotify = (lpiRef) ? lpiRef : GetIdentifier(); if (!lpiNotify->fIsFileset()) { NOTIFYCALLBACK::SendNotificationToAll (evtRefreshFilesetsBegin, lpiNotify); NOTIFYCALLBACK::SendNotificationToAll (evtRefreshFilesetsEnd, lpiNotify, status); } if (lpiNotify->fIsCell() || lpiNotify->fIsServer()) { NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAggregatesBegin, lpiNotify); NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAggregatesEnd, lpiNotify, status); } } if (hServer) { LPSERVER lpServer; if ((lpServer = lpiRefServer->OpenServer()) != NULL) { lpServer->CloseVosObject(); lpServer->Close(); } } if (pStatus && !rc) *pStatus = status; return rc; } void CELL::RefreshVLDB_RemoveReferences (LPIDENT lpiRefServer, LPIDENT lpiRefAggregate, LPIDENT lpiRefFileset, LPVOLUMEID pvidRefFileset) { // Zip through the current list of objects that we're about to refresh. // On each such object, remove the GHOST_HAS_VLDB_ENTRY flag, // and delete objects entirely if that's the only ghost flag they have. // (e.g., If we went through this routine earlier and created a ghost // aggregate because VLDB referenced it and we couldn't find mention // of it on the server, delete that aggregate. We'll recreate it here // if necessary; otherwise, it needs to be gone.) // // Note that by specifying {lpiRefServer} to start the enumeration, // we'll either enumerate only lpiRefServer, or all servers if it's NULL. // HENUM heServer; for (LPSERVER lpServer = ServerFindFirst (&heServer, lpiRefServer); lpServer; lpServer = ServerFindNext (&heServer)) { if (!pvidRefFileset) { // Since we're about to check VLDB for references to this server, // remove the GHOST_HAS_VLDB_ENTRY flag from its SERVER object. // If that's the only thing keeping the object around, remove the // object entirely. // if ((lpServer->m_wGhost &= (~GHOST_HAS_VLDB_ENTRY)) == 0) { lpServer->Close(); lpServer->SendDeleteNotifications(); m_lServers->Remove (lpServer); Delete (lpServer); continue; } } // Check each of the server's aggregates, and deal with them the same // way. // HENUM heAggregate; for (LPAGGREGATE lpAggregate = lpServer->AggregateFindFirst (&heAggregate, lpiRefAggregate); lpAggregate; lpAggregate = lpServer->AggregateFindNext (&heAggregate)) { if (!pvidRefFileset) { // Since we're about to check VLDB for references to this aggregate, // remove the GHOST_HAS_VLDB_ENTRY flag from its AGGREGATE object. // If that's the only thing keeping the object around, remove the // object entirely. // if ((lpAggregate->m_wGhost &= (~GHOST_HAS_VLDB_ENTRY)) == 0) { lpAggregate->Close(); lpAggregate->SendDeleteNotifications(); lpServer->m_lAggregates->Remove (lpAggregate); Delete (lpAggregate); continue; } } // Check each of the aggregate's filesets, and deal with them the same // way. // HENUM heFileset; for (LPFILESET lpFileset = lpAggregate->FilesetFindFirst (&heFileset, lpiRefFileset); lpFileset; lpFileset = lpAggregate->FilesetFindNext (&heFileset)) { if ((!pvidRefFileset) || (*pvidRefFileset == lpFileset->m_fs.idReadWrite)) { // Since we're about to check VLDB for references to this fileset, // remove the GHOST_HAS_VLDB_ENTRY flag from its FILESET object. // If that's the only thing keeping the object around, remove the // object entirely. // if ((lpFileset->m_wGhost &= (~GHOST_HAS_VLDB_ENTRY)) == 0) { lpFileset->Close(); lpFileset->SendDeleteNotifications(); lpAggregate->m_lFilesets->Remove (lpFileset); Delete (lpFileset); continue; } } lpFileset->Close(); } lpAggregate->Close(); } lpServer->Close(); } } void CELL::RefreshVLDB_OneEntry (PVOID pp, LPIDENT lpiRefServer, LPSERVERSTATUS pssRefServer, LPIDENT lpiRefAggregate, LPAGGREGATESTATUS pasRefAggregate, LPIDENT lpiRefFileset, LPVOLUMEID pvidRefFileset, BOOL fNotify) { vos_vldbEntry_p pEntry = (vos_vldbEntry_p)pp; // If we were asked to update all the replicas of a particular // fileset, then we set {pvidRefFileset} above to that fileset's // ID. Check this VLDB entry to see if it refers to that fileset; // if not, we're not interested in it. // if (pvidRefFileset) { if (memcmp (&pEntry->volumeId[ VOS_READ_WRITE_VOLUME ], pvidRefFileset, sizeof(VOLUMEID))) return; } for (int iRepSite = 0; iRepSite < pEntry->numServers; ++iRepSite) { SOCKADDR_IN RepSiteAddr; AfsClass_IntToAddress (&RepSiteAddr, pEntry->volumeSites[ iRepSite ].serverAddress); // Every fileset replication site which VLDB knows about // passes through this point, within {pEntry->volumeSites[ iRepSite ]}. // Are we going to be refreshing the server/aggregate on which // this repsite lives? If not, there's no need to process this // entry any further. // if (lpiRefServer) { BOOL fFilesetLivesOnThisServer = FALSE; for (size_t iAddress = 0; !fFilesetLivesOnThisServer && (iAddress < pssRefServer->nAddresses); ++iAddress) { if (!memcmp (&pssRefServer->aAddresses[ iAddress ], &RepSiteAddr, sizeof(SOCKADDR_IN))) { if (lpiRefAggregate) { if (pasRefAggregate->dwID != (DWORD)(pEntry->volumeSites[ iRepSite ].serverPartition)) continue; } fFilesetLivesOnThisServer = TRUE; } } if (!fFilesetLivesOnThisServer) continue; } // Do we know about the server mentioned by this replication // site? // LPSERVER lpServer; if (lpiRefServer != NULL) lpServer = lpiRefServer->OpenServer(); else lpServer = OpenServer (&RepSiteAddr); // If we found the server but aren't monitoring it, // forget about this fileset. // if (lpServer && !lpServer->fIsMonitored()) { lpServer->Close(); lpServer = NULL; continue; } // If we have no record of the server mentioned by this // replication site, we have to create a SERVER entry for // it before we can proceed. The server will appear as // a "ghost". // if (!lpServer) { if (lpiRefAggregate || pvidRefFileset) continue; LPTSTR pszServer = FormatString (TEXT("%1"), TEXT("%a"), &pEntry->volumeSites[ iRepSite ].serverAddress); lpServer = New2 (SERVER,(this, pszServer)); AfsClass_Enter(); FreeString (pszServer); lpServer->m_fStatusOutOfDate = FALSE; lpServer->m_ss.nAddresses = 1; memcpy (&lpServer->m_ss.aAddresses[0], &RepSiteAddr, sizeof(SOCKADDR_IN)); m_lServers->Add (lpServer); if (fNotify) NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpServer->GetIdentifier()); } lpServer->m_wGhost |= GHOST_HAS_VLDB_ENTRY; // Great--we now have a replication site for a particular // fileset known to VLDB, and a pointer to the server // on which it resides. Does that server contain the // aggregate which VLDB expects to find? // LPAGGREGATE lpAggregate; if (lpiRefAggregate != NULL) lpAggregate = lpiRefAggregate->OpenAggregate(); else lpAggregate = lpServer->OpenAggregate (pEntry->volumeSites[ iRepSite ].serverPartition); // If the server has no record of the aggregate mentioned // by this replication site, we have to create an // AGGREGATE entry for it before we can proceed. The // aggregate will appear as a "ghost". Note that we // can't update the list of aggregates on a server if // we've been asked to update a particular fileset, // because someone clearly has a pointer to the list. // if (!lpAggregate) { if (lpiRefFileset || pvidRefFileset) { lpServer->Close(); lpServer = NULL; continue; } // Even if the partition doesn't exist, we can still figger out // its name given its ID--'cause there's a 1:1 mapping between // allowed IDs and allowed partition names. I guess there's // something to be said for forcing partitions to be named "vicep*" // TCHAR szPartition[ cchNAME ]; WORKERPACKET wp; wp.wpVosPartitionIdToName.idPartition = pEntry->volumeSites[ iRepSite ].serverPartition; wp.wpVosPartitionIdToName.pszPartition = szPartition; if (!Worker_DoTask (wtaskVosPartitionIdToName, &wp)) wsprintf (szPartition, TEXT("#%lu"), pEntry->volumeSites[ iRepSite ].serverPartition); lpAggregate = New2 (AGGREGATE,(lpServer, szPartition, TEXT(""))); AfsClass_Enter(); lpAggregate->m_fStatusOutOfDate = FALSE; lpAggregate->m_as.dwID = pEntry->volumeSites[ iRepSite ].serverPartition; lpAggregate->m_as.ckStorageTotal = 0; lpAggregate->m_as.ckStorageFree = 0; lpServer->m_lAggregates->Add (lpAggregate); if (fNotify) NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpAggregate->GetIdentifier()); } lpAggregate->m_wGhost |= GHOST_HAS_VLDB_ENTRY; // Great--we now have a replication site for a particular // fileset known to VLDB, and a pointer to the server // and aggregate on which it resides. Does that aggregate // contain the fileset which VLDB expects to find? // // Remember that each iRepSite can represent up to three // filesets on that aggregate--a RW, a RO, and a BAK. // for (size_t iType = 0; iType < 3; ++iType) { // Does this repsite entry mention having this type // of fileset on this aggregate? // if ((vos_volumeType_t)iType == VOS_READ_WRITE_VOLUME) { if (!((DWORD)pEntry->volumeSites[ iRepSite ].serverFlags & (DWORD)VOS_VLDB_READ_WRITE)) continue; } else if ((vos_volumeType_t)iType == VOS_READ_ONLY_VOLUME) { if (!((DWORD)pEntry->volumeSites[ iRepSite ].serverFlags & (DWORD)VOS_VLDB_READ_ONLY)) continue; } else if ((vos_volumeType_t)iType == VOS_BACKUP_VOLUME) { if (!((DWORD)pEntry->status & (DWORD)VOS_VLDB_ENTRY_BACKEXISTS)) continue; // Only look for the backup where the R/W exists if (!((DWORD)pEntry->volumeSites[ iRepSite ].serverFlags & (DWORD)VOS_VLDB_READ_WRITE)) continue; } LPFILESET lpFileset = lpAggregate->OpenFileset ((LPVOLUMEID)&pEntry->volumeId[ iType ]); // If the aggregate has no record of the fileset mentioned // by this VLDB entry, we have to create a FILESET entry // for it. The fileset will appear as a "ghost". // if (!lpFileset) { TCHAR szFilesetName[ cchNAME ]; CopyAnsiToString (szFilesetName, pEntry->name); if ((vos_volumeType_t)iType == VOS_READ_ONLY_VOLUME) lstrcat (szFilesetName, TEXT(".readonly")); else if ((vos_volumeType_t)iType == VOS_BACKUP_VOLUME) lstrcat (szFilesetName, TEXT(".backup")); lpFileset = New2 (FILESET,(lpAggregate, &pEntry->volumeId[ iType ], szFilesetName)); AfsClass_Enter(); lpFileset->m_fs.id = pEntry->volumeId[ iType ]; lpFileset->m_fs.idReadWrite = pEntry->volumeId[ VOS_READ_WRITE_VOLUME ]; lpFileset->m_fs.idReplica = pEntry->volumeId[ VOS_READ_ONLY_VOLUME ]; AfsClass_UnixTimeToSystemTime (&lpFileset->m_fs.timeCreation, 0); AfsClass_UnixTimeToSystemTime (&lpFileset->m_fs.timeLastUpdate, 0); AfsClass_UnixTimeToSystemTime (&lpFileset->m_fs.timeLastAccess, 0); AfsClass_UnixTimeToSystemTime (&lpFileset->m_fs.timeLastBackup, 0); AfsClass_UnixTimeToSystemTime (&lpFileset->m_fs.timeCopyCreation, 0); lpFileset->m_fs.nFiles = 0; lpFileset->m_fs.ckQuota = 0; lpFileset->m_fs.ckUsed = 0; lpFileset->m_fs.Type = (iType == 0) ? ftREADWRITE : (iType == 1) ? ftREPLICA : ftCLONE; lpFileset->m_fs.State = fsNORMAL; lpAggregate->m_lFilesets->Add (lpFileset); if (fNotify) NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpFileset->GetIdentifier()); } if (fNotify) NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusBegin, lpFileset->GetIdentifier()); lpFileset->m_wGhost |= GHOST_HAS_VLDB_ENTRY; lpFileset->m_fStatusOutOfDate = FALSE; lpFileset->m_fs.idClone = pEntry->cloneId; lpFileset->m_fs.State &= ~fsMASK_VLDB; if (pEntry->status & VOS_VLDB_ENTRY_LOCKED) lpFileset->m_fs.State |= fsLOCKED; lpFileset->Close(); if (fNotify) NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusEnd, lpFileset->GetIdentifier(), 0); } if (lpServer && lpAggregate) { lpAggregate->InvalidateAllocation(); lpAggregate->Close(); lpAggregate = NULL; } if (lpServer) { lpServer->Close(); lpServer = NULL; } } } BOOL CELL::RefreshAll (ULONG *pStatus) { BOOL rc = TRUE; ULONG status = 0; BOOL fNotified = FALSE; if (m_fServersOutOfDate && (dwWant & AFSCLASS_WANT_VOLUMES)) { if (!fNotified) { fNotified = TRUE; if ((++cRefreshAllReq) == 1) { NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllBegin, GetIdentifier()); } } size_t nServersToRefresh = 0; HENUM hEnum; for (LPSERVER lpServer = ServerFindFirst (&hEnum); lpServer; lpServer = ServerFindNext (&hEnum)) { if (lpServer->fIsMonitored()) ++nServersToRefresh; lpServer->Close(); } if (nServersToRefresh) { size_t iServer = 0; for (LPSERVER lpServer = ServerFindFirst (&hEnum); lpServer; lpServer = ServerFindNext (&hEnum)) { if (lpServer->fIsMonitored()) { ULONG perComplete = (ULONG)iServer * 100L / nServersToRefresh; NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllUpdate, lpServer->GetIdentifier(), NULL, NULL, NULL, perComplete, 0); // intentionally ignore errors in refreshing individual // servers--we only want to return an error code if // we couldn't refresh the *cell*. // (void)lpServer->RefreshAll (NULL, ((double)iServer / (double)nServersToRefresh), (1.0 / (double)nServersToRefresh)); ++iServer; } lpServer->Close(); } rc = RefreshVLDB (NULL, TRUE, &status); } } if (rc && m_fUsersOutOfDate && (dwWant & AFSCLASS_WANT_USERS)) { if (!fNotified) { fNotified = TRUE; if ((++cRefreshAllReq) == 1) { NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllBegin, GetIdentifier(), NULL, NULL, NULL, 0, 0); } } rc = RefreshUsers (TRUE, &status); } if (fNotified) { if ((--cRefreshAllReq) == 0) { NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllEnd, GetIdentifier(), NULL, NULL, NULL, 100, status); } } if (!rc && pStatus) *pStatus = status; return rc; } /* * USER/GROUP-LIST MANAGEMENT _________________________________________________ * */ LPUSER CELL::OpenUser (LPTSTR pszName, LPTSTR pszInstance, ULONG *pStatus) { ULONG status = 0; // First off, do we have a USER object for this guy already? // LPUSER lpUser = NULL; for (LPENUM pEnum = m_lkUserName->FindFirst (pszName); pEnum; pEnum = pEnum->FindNext()) { LPUSER lpTest = (LPUSER)( pEnum->GetObject() ); if (!pszInstance || !lstrcmpi (lpTest->m_szInstance, pszInstance)) { lpUser = lpTest; AfsClass_Enter(); Delete (pEnum); break; } } // If not, see if we can create one... // if (!lpUser) { // See if there's a KAS or PTS entry for this user. // BOOL fHasKAS = FALSE; BOOL fHasPTS = FALSE; WORKERPACKET wp; wp.wpKasPrincipalGet.hCell = GetCellObject (&status); wp.wpKasPrincipalGet.hServer = GetKasObject (&status); wp.wpKasPrincipalGet.pszPrincipal = pszName; wp.wpKasPrincipalGet.pszInstance = pszInstance; if (Worker_DoTask (wtaskKasPrincipalGet, &wp, &status)) fHasKAS = TRUE; if (!fHasKAS) { TCHAR szFullName[ cchNAME ]; AfsClass_GenFullUserName (szFullName, pszName, pszInstance); WORKERPACKET wp; wp.wpPtsUserGet.hCell = GetCellObject(); wp.wpPtsUserGet.pszUser = szFullName; if (Worker_DoTask (wtaskPtsUserGet, &wp, &status)) fHasPTS = TRUE; } if (fHasKAS || fHasPTS) { lpUser = New2 (USER,(this, pszName, pszInstance)); m_lUsers->Add (lpUser); NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpUser->GetIdentifier()); AfsClass_Enter(); } } if (!lpUser && pStatus) *pStatus = status; return lpUser; } LPUSER CELL::UserFindFirst (HENUM *phEnum, BOOL fNotify, ULONG *pStatus) { return UserFindFirst (phEnum, NULL, fNotify, pStatus); } LPUSER CELL::UserFindFirst (HENUM *phEnum, LPIDENT lpiFind, BOOL fNotify, ULONG *pStatus) { LPUSER lpUser = NULL; if (!RefreshUsers (fNotify, pStatus)) return NULL; if (lpiFind != NULL) { lpUser = lpiFind->OpenUser(); *phEnum = NULL; } else if ((*phEnum = m_lUsers->FindFirst()) != NULL) { lpUser = (LPUSER)( (*phEnum)->GetObject() ); AfsClass_Enter(); } if (!lpUser && pStatus) *pStatus = ERROR_FILE_NOT_FOUND; return lpUser; } LPUSER CELL::UserFindNext (HENUM *phEnum) { LPUSER lpUser = NULL; if (*phEnum) { if ((*phEnum = (*phEnum)->FindNext()) != NULL) { lpUser = (LPUSER)( (*phEnum)->GetObject() ); AfsClass_Enter(); } } return lpUser; } void CELL::UserFindClose (HENUM *phEnum) { if (*phEnum) { Delete (*phEnum); *phEnum = NULL; } } LPPTSGROUP CELL::OpenGroup (LPTSTR pszName, ULONG *pStatus) { ULONG status; // First off, do we have a USER object for this guy already? // LPPTSGROUP lpGroup; if ((lpGroup = (LPPTSGROUP)(m_lkGroupName->GetFirstObject (pszName))) != NULL) AfsClass_Enter(); // If not, see if we can create one... // if (!lpGroup) { // See if there's a PTS entry for this group. // WORKERPACKET wp; wp.wpPtsGroupGet.hCell = GetCellObject(); wp.wpPtsGroupGet.pszGroup = pszName; if (Worker_DoTask (wtaskPtsGroupGet, &wp, &status)) { lpGroup = New2 (PTSGROUP,(this, pszName)); m_lGroups->Add (lpGroup); NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpGroup->GetIdentifier()); AfsClass_Enter(); } } if (!lpGroup && pStatus) *pStatus = status; return lpGroup; } LPPTSGROUP CELL::GroupFindFirst (HENUM *phEnum, BOOL fNotify, ULONG *pStatus) { return GroupFindFirst (phEnum, NULL, fNotify, pStatus); } LPPTSGROUP CELL::GroupFindFirst (HENUM *phEnum, LPIDENT lpiFind, BOOL fNotify, ULONG *pStatus) { LPPTSGROUP lpGroup = NULL; if (!RefreshUsers (fNotify, pStatus)) return NULL; if (lpiFind != NULL) { lpGroup = lpiFind->OpenGroup(); *phEnum = NULL; } else if ((*phEnum = m_lGroups->FindFirst()) != NULL) { lpGroup = (LPPTSGROUP)( (*phEnum)->GetObject() ); AfsClass_Enter(); } if (!lpGroup && pStatus) *pStatus = ERROR_FILE_NOT_FOUND; return lpGroup; } LPPTSGROUP CELL::GroupFindNext (HENUM *phEnum) { LPPTSGROUP lpGroup = NULL; if (*phEnum) { if ((*phEnum = (*phEnum)->FindNext()) != NULL) { lpGroup = (LPPTSGROUP)( (*phEnum)->GetObject() ); AfsClass_Enter(); } } return lpGroup; } void CELL::GroupFindClose (HENUM *phEnum) { if (*phEnum) { Delete (*phEnum); *phEnum = NULL; } } BOOL CELL::RefreshUsers (BOOL fNotify, ULONG *pStatus) { BOOL rc = TRUE; ULONG status = 0; if (m_fUsersOutOfDate) { m_fUsersOutOfDate = FALSE; if (fNotify) NOTIFYCALLBACK::SendNotificationToAll (evtRefreshUsersBegin, GetIdentifier()); // First, forget everything we think we know about all users and groups; // we wouldn't be here if someone didn't think it was all out of date. // FreeUsers (TRUE); // Then zip through KAS to build a list of all user entries; // we'll need hCell and hKAS for that. // WORKERPACKET wpBegin; wpBegin.wpKasPrincipalGetBegin.hCell = GetCellObject(); wpBegin.wpKasPrincipalGetBegin.hServer = GetKasObject (&status); if (!Worker_DoTask (wtaskKasPrincipalGetBegin, &wpBegin, &status)) rc = FALSE; else { for (;;) { TCHAR szPrincipal[ cchNAME ]; TCHAR szInstance[ cchNAME ]; WORKERPACKET wpNext; wpNext.wpKasPrincipalGetNext.hEnum = wpBegin.wpKasPrincipalGetBegin.hEnum; wpNext.wpKasPrincipalGetNext.pszPrincipal = szPrincipal; wpNext.wpKasPrincipalGetNext.pszInstance = szInstance; if (!Worker_DoTask (wtaskKasPrincipalGetNext, &wpNext, &status)) { if (status == ADMITERATORDONE) status = 0; else rc = FALSE; break; } // Okay, we got a user from kas. Create a USER object for it. // LPUSER lpUser = New2 (USER,(this, szPrincipal, szInstance)); m_lUsers->Add (lpUser); // That was easy, wasn't it? Now check this user's groups, // both the ones it owns and the ones to which it belongs, // so we can build a full list-o-groups. // LPTSTR mszGroups; if (lpUser->GetMemberOf (&mszGroups)) { BuildGroups (mszGroups); FreeString (mszGroups); } if (lpUser->GetOwnerOf (&mszGroups)) { BuildGroups (mszGroups); FreeString (mszGroups); } } WORKERPACKET wpDone; wpDone.wpKasPrincipalGetDone.hEnum = wpBegin.wpKasPrincipalGetBegin.hEnum; Worker_DoTask (wtaskKasPrincipalGetDone, &wpDone); } #ifdef FIND_PTS_DEBRIS // Icky horrible painful part: to catch entries which exist on PTS // but not in KAS, we need to zip back through our list of groups // and check thier memberships. // for (LPENUM pe = m_lGroups->FindFirst(); pe; pe = pe->FindNext()) { LPPTSGROUP lpGroup = (LPPTSGROUP)(pe->GetObject()); LPTSTR mszMembers; if (lpGroup->GetMembers (&mszMembers)) { for (LPTSTR pszMember = mszMembers; pszMember && *pszMember; pszMember += 1+lstrlen(pszMember)) { // Make sure we have a user or group account for this guy. // Remember that the member name may have both a name and // an instance. // if (m_lkGroupName->GetFirstObject (pszMember)) continue; TCHAR szNameMatch[ cchNAME ]; TCHAR szInstanceMatch[ cchNAME ]; USER::SplitUserName (pszMember, szNameMatch, szInstanceMatch); LPUSER lpFound = NULL; for (LPENUM pEnum = m_lkUserName->FindFirst (szNameMatch); pEnum; pEnum = pEnum->FindNext()) { LPUSER lpTest = (LPUSER)( pEnum->GetObject() ); if (!lstrcmpi (lpTest->m_szInstance, szInstanceMatch)) { lpFound = lpTest; Delete (pEnum); break; } } if (lpFound) continue; // Uh oh. Is this thing a user or a group? We're really only // interested in finding user-account debris here... // WORKERPACKET wpGet; wpGet.wpPtsUserGet.hCell = GetCellObject(); wpGet.wpPtsUserGet.pszUser = pszMember; if (Worker_DoTask (wtaskPtsUserGet, &wpGet)) { if (wpGet.wpPtsUserGet.Entry.nameUid > 0) { LPUSER lpUser = New2 (USER,(this, pszMember, TEXT(""))); m_lUsers->Add (lpUser); } } } } } #endif // We've finally generated a complete list of the users and groups in // this cell. If we've been asked to, send out notifications for all // the things we found. // if (fNotify) { for (LPENUM pEnum = m_lGroups->FindFirst(); pEnum; pEnum = pEnum->FindNext()) { LPPTSGROUP lpGroup = (LPPTSGROUP)(pEnum->GetObject()); NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpGroup->GetIdentifier()); } for (pEnum = m_lUsers->FindFirst(); pEnum; pEnum = pEnum->FindNext()) { LPUSER lpUser = (LPUSER)(pEnum->GetObject()); NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpUser->GetIdentifier()); } NOTIFYCALLBACK::SendNotificationToAll (evtRefreshUsersEnd, GetIdentifier(), ((rc) ? 0 : status)); } } if (!rc && pStatus) *pStatus = status; return rc; } void CELL::BuildGroups (LPTSTR mszGroups) { for (LPTSTR pszGroup = mszGroups; pszGroup && *pszGroup; pszGroup += 1+lstrlen(pszGroup)) { // Make sure we have this group in our list-of-groups // LPPTSGROUP lpGroup; if ((lpGroup = (LPPTSGROUP)m_lkGroupName->GetFirstObject (pszGroup)) == NULL) { lpGroup = New2 (PTSGROUP,(this, pszGroup)); m_lGroups->Add (lpGroup); } } } BOOL CELL::RefreshAccount (LPTSTR pszAccount, LPTSTR pszInstance, OP_CELL_REFRESH_ACCOUNT Op, LPIDENT *plpi) { BOOL rc = TRUE; // See if we can find this thing // LPIDENT lpi; if ((lpi = IDENT::FindUser (m_lpiThis, pszAccount, pszInstance)) != NULL) { if (lpi->m_cRef == 0) lpi = NULL; } if (!lpi) { if ((lpi = IDENT::FindGroup (m_lpiThis, pszAccount)) != NULL) if (lpi->m_cRef == 0) lpi = NULL; } // If we couldn't find it, and Op is _CREATED_*, then make a new account // if ((!lpi) && (Op == CELL_REFRESH_ACCOUNT_CREATED_USER)) { LPUSER lpUser = New2 (USER,(this, pszAccount, pszInstance)); m_lUsers->Add (lpUser); lpi = lpUser->GetIdentifier(); NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpi); } else if ((!lpi) && (Op == CELL_REFRESH_ACCOUNT_CREATED_GROUP)) { LPPTSGROUP lpGroup = New2 (PTSGROUP,(this, pszAccount)); m_lGroups->Add (lpGroup); lpi = lpGroup->GetIdentifier(); NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpi); } // If we did find it, and Op is _DELETED, then remove the account // if (lpi && (Op == CELL_REFRESH_ACCOUNT_DELETED)) { if (lpi && (lpi->GetType() == itUSER)) { LPUSER lpUser; if ((lpUser = lpi->OpenUser()) == NULL) rc = FALSE; else { lpUser->SendDeleteNotifications(); lpUser->Close(); m_lUsers->Remove (lpUser); Delete (lpUser); lpi = NULL; } } else if (lpi && (lpi->GetType() == itGROUP)) { LPPTSGROUP lpGroup; if ((lpGroup = lpi->OpenGroup()) == NULL) rc = FALSE; else { lpGroup->SendDeleteNotifications(); lpGroup->Close(); m_lGroups->Remove (lpGroup); Delete (lpGroup); lpi = NULL; } } else { rc = FALSE; } } // If we still have an ident, refresh the account's properties // if (lpi && (lpi->GetType() == itUSER)) { LPUSER lpUser; if ((lpUser = lpi->OpenUser()) == NULL) rc = FALSE; else { lpUser->Invalidate(); lpUser->RefreshStatus(); lpUser->Close(); } } else if (lpi && (lpi->GetType() == itGROUP)) { LPPTSGROUP lpGroup; if ((lpGroup = lpi->OpenGroup()) == NULL) rc = FALSE; else { lpGroup->Invalidate(); lpGroup->RefreshStatus(); lpGroup->Close(); } } if (plpi) *plpi = lpi; return rc; } BOOL CELL::RefreshAccounts (LPTSTR mszAccounts, OP_CELL_REFRESH_ACCOUNT Op) { BOOL rc = TRUE; for (LPTSTR psz = mszAccounts; psz && *psz; psz += 1+lstrlen(psz)) { if (!RefreshAccount (psz, NULL, Op)) rc = FALSE; } return rc; }