//+------------------------------------------------------------------------- // // Copyright (C) 1997, Microsoft Corporation // // File: csites.cxx // // Contents: This module contains the functions which deal with the site table // // History: 02-Dec-1997 JHarper Created. // //-------------------------------------------------------------------------- #include "headers.hxx" #pragma hdrstop #include "dfsmsrv.h" #include "csites.hxx" #include "marshal.hxx" INIT_DFS_SITENAME_INFO_MARSHAL_INFO(); INIT_DFS_SITELIST_INFO_MARSHAL_INFO() INIT_DFSM_SITE_ENTRY_MARSHAL_INFO(); NTSTATUS DfsSendDelete( LPWSTR ServerName); //+---------------------------------------------------------------------------- // // Function: CSites::CSites // // Synopsis: Constructor for CSites - Initializes, but does not load, the site table // // Arguments: [pwszFileName] -- Name of the folder/LDAP_OBJECT to use // [pdwErr] -- On return, the result of initializing the instance // // Returns: Result returned in pdwErr argument: // // [ERROR_SUCCESS] -- Successfully initialized // // [ERROR_OUTOFMEMORY] -- Out of memory. // //----------------------------------------------------------------------------- CSites::CSites( LPWSTR pwszFileName, LPDWORD pdwErr) { DWORD dwErr = 0; IDfsVolInlineDebOut(( DEB_TRACE, "CSites::+CSites(0x%x)\n", this)); #if DBG if (DfsSvcVerbose) DbgPrint("+++CSites::CSites @0x%x\n", this); #endif _cRef = 0; _fDirty = FALSE; RtlZeroMemory(&_SiteTableGuid, sizeof(GUID)); InitializeListHead(&_SiteTableHead); _pwszFileName = new WCHAR [wcslen(pwszFileName) + 1]; if (_pwszFileName != NULL) { wcscpy(_pwszFileName, pwszFileName); } else { dwErr = ERROR_OUTOFMEMORY; } *pdwErr = dwErr; } //+---------------------------------------------------------------------------- // // Function: CSites::~CSites // // Synopsis: Destructor for CSites - Deallocates the CSites object // // Arguments: None // // Returns: Nothing // //----------------------------------------------------------------------------- CSites::~CSites() { PLIST_ENTRY pListHead; PDFSM_SITE_ENTRY pSiteInfo; IDfsVolInlineDebOut(( DEB_TRACE, "CSites::~CSites(0x%x)\n", this)); #if DBG if (DfsSvcVerbose) DbgPrint("---CSites::~CSites @0x%x\n", this); #endif ASSERT (_cRef == 0); if (_pwszFileName) { delete [] _pwszFileName; } // // Delete the linked list of SiteInfo's // pListHead = &_SiteTableHead; while (pListHead->Flink != pListHead) { pSiteInfo = CONTAINING_RECORD(pListHead->Flink, DFSM_SITE_ENTRY, Link); RemoveEntryList(pListHead->Flink); #if DBG if (DfsSvcVerbose) DbgPrint("CSites::~CSites: deleting SiteInfo@0x%x\n", pSiteInfo); #endif delete [] pSiteInfo; } } //+---------------------------------------------------------------------------- // // Function: CSites::AddRef // // Synopsis: Increases the ref count on the in-memory site table. If the // refcount is going from 0 to 1, an attempt is made to refresh // the in-memory site table from storage. // // Arguments: None // // Returns: Nothing // //----------------------------------------------------------------------------- VOID CSites::AddRef() { DWORD dwErr = 0; IDfsVolInlineDebOut((DEB_TRACE, "CSites::AddRef()\n")); _cRef++; if (_cRef == 1) { dwErr = _ReadSiteTable(); _fDirty = FALSE; } } //+---------------------------------------------------------------------------- // // Function: CSites::Release // // Synopsis: Decrease the ref count on the in-memory site table. If the // refcount is going from 1 to 0, then attempt to flush the // table to storage, if something has changed. // // Arguments: None // // Returns: Nothing // //----------------------------------------------------------------------------- VOID CSites::Release() { IDfsVolInlineDebOut((DEB_TRACE, "CSites::Release()\n")); ASSERT (_cRef > 0); _cRef--; if (_cRef == 0) { if (_fDirty == TRUE) { _WriteSiteTable(); _fDirty = FALSE; } } } //+---------------------------------------------------------------------------- // // Function: CSites::AddOrUpdateSiteInfo // // Synopsis: Adds or updates an entry in the site table. // // Arguments: [pServerName] -- Server name to update // [SiteCount] -- Number of entries in pSites // [pSites] -- pointer to array of site information. // // Returns: [ERROR_SUCCESS] -- Successfully updated the table // //----------------------------------------------------------------------------- DWORD CSites::AddOrUpdateSiteInfo( LPWSTR pServerName, ULONG SiteCount, PDFS_SITENAME_INFO pSites) { PDFSM_SITE_ENTRY pSiteInfo; PDFSM_SITE_ENTRY pExistingInfo; DWORD dwErr; ULONG i; IDfsVolInlineDebOut((DEB_TRACE, "CSites::AddOrUpdateSiteInfo(%ws)\n", pServerName)); dwErr = _AllocateSiteInfo( pServerName, SiteCount, pSites, &pSiteInfo); if (dwErr == ERROR_SUCCESS) { // // Only put the new entry in if it supercedes one there, // or is a new entry. // pExistingInfo = LookupSiteInfo(pServerName); if (pExistingInfo != NULL) { if (_CompareEntries(pSiteInfo,pExistingInfo) == TRUE ) { // // Same info - no update needed // delete [] pSiteInfo; } else { // // Remove the existing entry // RemoveEntryList(&pExistingInfo->Link); delete [] pExistingInfo; // // Put the new one in // DfsSendUpdate(pServerName,SiteCount,pSites); InsertHeadList(&_SiteTableHead, &pSiteInfo->Link); _fDirty = TRUE; } } else { // // Not in table - put it in // DfsSendUpdate(pServerName,SiteCount,pSites); InsertHeadList(&_SiteTableHead, &pSiteInfo->Link); _fDirty = TRUE; } } return ERROR_SUCCESS; } //+---------------------------------------------------------------------------- // // Function: CSites::LookupSiteInfo // // Synopsis: Finds a site table entry // // Arguments: [pServerName] -- Server name to look up // // Returns: [NULL] -- No entry found // [PDFSM_SITE_ENTRY] -- pointer to found entry // //----------------------------------------------------------------------------- PDFSM_SITE_ENTRY CSites::LookupSiteInfo( LPWSTR pServerName) { PLIST_ENTRY pListHead, pLink; PDFSM_SITE_ENTRY pSiteInfo; IDfsVolInlineDebOut((DEB_TRACE, "CSites::LookupSiteInfo(%ws)\n", pServerName)); pListHead = &_SiteTableHead; if (pListHead->Flink == pListHead) { // list empty return NULL; } for (pLink = pListHead->Flink; pLink != pListHead; pLink = pLink->Flink) { pSiteInfo = CONTAINING_RECORD(pLink, DFSM_SITE_ENTRY, Link); if (_wcsicmp(pSiteInfo->ServerName,pServerName) == 0) { // // If this was marked for delete, it isn't any more. // pSiteInfo->Flags &= ~DFSM_SITE_ENTRY_DELETE_PENDING; return pSiteInfo; } } return NULL; } //+---------------------------------------------------------------------------- // // Function: CSites::_AllocateSiteInfo, private // // Synopsis: Creates a DFSM_SITE_ENTRY struct, as one contiguous chunk of memory // // Arguments: [pServerName] -- Server name // [SiteCount] -- Number of entries in pSites // [pSites] -- pointer to array of site information. // [ppSiteInfo] -- pointer to pointer for the results // // Returns: [ERROR_SUCCESS] -- Successfully allocated and filled in the structure // [ERROR_OUTOFMEMORY] -- Couldn't allocate needed memory // //----------------------------------------------------------------------------- DWORD CSites::_AllocateSiteInfo( PWSTR pServerName, ULONG SiteCount, PDFS_SITENAME_INFO pSites, PDFSM_SITE_ENTRY *ppSiteInfo) { PDFSM_SITE_ENTRY pSiteInfo; WCHAR *wCp; ULONG i; ULONG Size = 0; IDfsVolInlineDebOut((DEB_TRACE, "CSites::_AllocateSiteInfo(%ws)\n", pServerName)); // // Calculate the size chunk of mem we'll need // Size = FIELD_OFFSET(DFSM_SITE_ENTRY,Info.Site[SiteCount]); // // Add space for server name // Size += (wcslen(pServerName) + 1) * sizeof(WCHAR); // // And space for all the sitenames // for (i = 0; i < SiteCount; i++) { if (pSites[i].SiteName != NULL) { Size += (wcslen(pSites[i].SiteName) + 1) * sizeof(WCHAR); } else { Size += sizeof(WCHAR); } } pSiteInfo = (PDFSM_SITE_ENTRY) new CHAR [Size]; if (pSiteInfo == NULL) { *ppSiteInfo = NULL; return ERROR_OUTOFMEMORY; } RtlZeroMemory(pSiteInfo, Size); // // Marshal the info into the buffer // pSiteInfo->Flags = 0; pSiteInfo->Info.cSites = SiteCount; wCp = (WCHAR *) &pSiteInfo->Info.Site[SiteCount]; pSiteInfo->ServerName = wCp; wcscpy(wCp, pServerName); wCp += wcslen(pServerName) + 1; for (i = 0; i < SiteCount; i++) { pSiteInfo->Info.Site[i].SiteFlags = pSites[i].SiteFlags; pSiteInfo->Info.Site[i].SiteName = wCp; if (pSites[i].SiteName != NULL) { wcscpy(wCp, pSites[i].SiteName); wCp += wcslen(pSites[i].SiteName) + 1; } else { *wCp++ = UNICODE_NULL; } } *ppSiteInfo = pSiteInfo; return ERROR_SUCCESS; } //+---------------------------------------------------------------------------- // // Function: CSites::_ReadSiteTable,private // // Synopsis: Loads the site table from storage // // First we check if the GUID has changed. If it hasn't, we abort the // load - the existing table is good. // // If the table is different (the GUID is different), we merge the new // data in with the old. This allows us to track which entries need to // be updated, which need to be deleted, and which haven't changed. The // process is done in three steps: // // Step 1: Go through the table, marking all the entries DELETE_PENDING // Step 2: Load the new entries. As they are loaded, the updated entries' // DELETE_PENDING bit(s) are turned off. // Step 3: Go though the table again, removing any entries that still // have the DELETE_PENDING bit on, and issuing an FSCTL to dfs.sys // to have it remove the entry from its table. // // Arguments: None // // Returns: [ERROR_SUCCESS] -- Successfully loaded the table // [ERROR_OUTOFMEMORY] -- Not enough memory // [other] -- returned from LdapGetData/RegGetData/DfsRtlXXX // //----------------------------------------------------------------------------- DWORD CSites::_ReadSiteTable() { DWORD dwErr; DWORD cbBuffer; PBYTE pBuffer = NULL; PBYTE bp; ULONG cObjects = 0; ULONG i; ULONG j; PDFSM_SITE_ENTRY pSiteInfo; MARSHAL_BUFFER marshalBuffer; GUID TempGuid; IDfsVolInlineDebOut((DEB_TRACE, "CSites::_ReadSiteTable()\n")); if (ulDfsManagerType == DFS_MANAGER_FTDFS) { dwErr = LdapGetData( _pwszFileName, &cbBuffer, (PCHAR *)&pBuffer); } else { dwErr = RegGetData( _pwszFileName, SITE_VALUE_NAME, &cbBuffer, &pBuffer); } if (dwErr == ERROR_SUCCESS && cbBuffer >= sizeof(ULONG) + sizeof(GUID)) { // // Unmarshal all the objects (DFS_SITENAME_INFO's) in the buffer // // // We marshal into a temporary buffer, big enough to hold whatever is // in the buffer. // pSiteInfo = (PDFSM_SITE_ENTRY) new BYTE [cbBuffer + sizeof(DFSM_SITE_ENTRY)]; if (pSiteInfo == NULL) { dwErr = ERROR_OUTOFMEMORY; } if (dwErr == ERROR_SUCCESS) { MarshalBufferInitialize( &marshalBuffer, cbBuffer, pBuffer); DfsRtlGetGuid(&marshalBuffer, &TempGuid); // // If the Guid hasn't changed, we abort the load. // if (RtlCompareMemory(&TempGuid, &_SiteTableGuid, sizeof(GUID)) == sizeof(GUID)) { delete [] pSiteInfo; delete [] pBuffer; goto NoLoadNecessary; } // // Ok, we're committed to loading this (supposedly different) version // of the site table. Mark all the existing entries DFSM_SITE_ENTRY_DELETE_PENDING. // MarkEntriesForMerge(); // // Grab the Guid // RtlCopyMemory(&_SiteTableGuid, &TempGuid, sizeof(GUID)); // // Get number of entries we'll be loading // DfsRtlGetUlong(&marshalBuffer, &cObjects); // // Now unmarshal each object/entry // for (j = 0; dwErr == ERROR_SUCCESS && j < cObjects; j++) { dwErr = DfsRtlGet(&marshalBuffer,&MiDfsmSiteEntry, pSiteInfo); if (dwErr == ERROR_SUCCESS) { // // And put it in the site table // AddOrUpdateSiteInfo( pSiteInfo->ServerName, pSiteInfo->Info.cSites, &pSiteInfo->Info.Site[0]); // // The unmarshalling routines allocate buffers; we need to // free them. // for (i = 0; i < pSiteInfo->Info.cSites; i++) { MarshalBufferFree(pSiteInfo->Info.Site[i].SiteName); } MarshalBufferFree(pSiteInfo->ServerName); } // // Now sync up the PKT in dfs.sys with this table // SyncPktSiteTable(); } delete [] pSiteInfo; } } if (pBuffer != NULL) { delete [] pBuffer; } NoLoadNecessary: return dwErr; } //+---------------------------------------------------------------------------- // // Function: CSites::_WriteSiteTable,private // // Synopsis: Writes the site table to storage // // Arguments: None // // Returns: [ERROR_SUCCESS] -- Successfully write the table // [ERROR_OUTOFMEMORY] -- Not enough memory // [other] -- returned from LdapGetData/RegGetData/DfsRtlXXX // //----------------------------------------------------------------------------- DWORD CSites::_WriteSiteTable() { DWORD dwErr; DWORD cbBuffer; PBYTE pBuffer; ULONG cObjects; ULONG i; PLIST_ENTRY pListHead, pLink; PDFSM_SITE_ENTRY pSiteInfo; MARSHAL_BUFFER marshalBuffer; IDfsVolInlineDebOut((DEB_TRACE, "CSites::_WriteSiteTable()\n")); // // Create a new Guid // UuidCreate(&_SiteTableGuid); // // The cObjects count // cbBuffer = sizeof(ULONG) + sizeof(GUID); // // Add up the number of entries we need to store, and the total size of all // of them. // cObjects = 0; pListHead = &_SiteTableHead; for (pLink = pListHead->Flink; pLink != pListHead; pLink = pLink->Flink) { pSiteInfo = CONTAINING_RECORD(pLink, DFSM_SITE_ENTRY, Link); DfsRtlSize(&MiDfsmSiteEntry, pSiteInfo, &cbBuffer); cObjects++; } // // Get a buffer big enough // pBuffer = new BYTE [cbBuffer]; if (pBuffer == NULL) { return ERROR_OUTOFMEMORY; } // // Put the guid, then the object count in the beginning of the buffer // MarshalBufferInitialize( &marshalBuffer, cbBuffer, pBuffer); DfsRtlPutGuid(&marshalBuffer, &_SiteTableGuid); DfsRtlPutUlong(&marshalBuffer, &cObjects); // // Walk the linked list of objects, marshalling them into the buffer. // for (pLink = pListHead->Flink; pLink != pListHead; pLink = pLink->Flink) { pSiteInfo = CONTAINING_RECORD(pLink, DFSM_SITE_ENTRY, Link); DfsRtlPut(&marshalBuffer,&MiDfsmSiteEntry, pSiteInfo); } // // Push out to storage // if (ulDfsManagerType == DFS_MANAGER_FTDFS) { dwErr = LdapPutData( _pwszFileName, cbBuffer, (PCHAR)pBuffer); } else { dwErr = RegPutData( _pwszFileName, SITE_VALUE_NAME, cbBuffer, pBuffer); } // // ...and free the marshal buffer we created. // delete [] pBuffer; return dwErr; } //+---------------------------------------------------------------------------- // // Function: CSites::_CompareEntries,private // // Synopsis: Compare two site table entries - case insensitive, and allows the site // lists to be in different order. // // Arguments: None // // Returns: [TRUE] -- The entries are essentially identical // [FALSE] -- The entries differ in some important way // //----------------------------------------------------------------------------- BOOLEAN CSites::_CompareEntries( PDFSM_SITE_ENTRY pDfsmInfo1, PDFSM_SITE_ENTRY pDfsmInfo2) { ULONG i; ULONG j; BOOLEAN fFound; // // cSites has to be the same // if (pDfsmInfo1->Info.cSites != pDfsmInfo2->Info.cSites) { goto ReturnFalse; } // // Server name has to be identical (why are we calling this // if they aren't?) // if (_wcsicmp(pDfsmInfo1->ServerName,pDfsmInfo2->ServerName) != 0) { goto ReturnFalse; } // // Check that every Site in pDfsmInfo1 is in pDfsmSiteInfo2 // for (i = 0; i < pDfsmInfo1->Info.cSites; i++) { fFound = FALSE; for (j = 0; fFound == FALSE && j < pDfsmInfo2->Info.cSites; j++) { if (_wcsicmp( pDfsmInfo1->Info.Site[i].SiteName, pDfsmInfo2->Info.Site[j].SiteName) == 0) { fFound = TRUE; } } if (fFound == FALSE) { goto ReturnFalse; } } // // ...and check that every site in pDfsmInfo2 is in pDfsmInfo1 // for (i = 0; i < pDfsmInfo2->Info.cSites; i++) { fFound = FALSE; for (j = 0; fFound == FALSE && j < pDfsmInfo1->Info.cSites; j++) { if (_wcsicmp( pDfsmInfo2->Info.Site[i].SiteName, pDfsmInfo1->Info.Site[j].SiteName) == 0) { fFound = TRUE; } } if (fFound == FALSE) { goto ReturnFalse; } } return TRUE; ReturnFalse: return FALSE; } //+---------------------------------------------------------------------------- // // Function: CSites::MarkEntriesForMerge // // Synopsis: Mark all entries in preparation for a merge. // (1) Mark all entries with delete_pending on // (2) Load a new table - duplicate entries remove the delete_pending bit // (3) Call SyncPktSiteTable(), which will bring the PKT in dfs.sys up to date, // by deleting delete_pending entries // // Arguments: None // // Returns: nothing // //----------------------------------------------------------------------------- VOID CSites::MarkEntriesForMerge() { PLIST_ENTRY pListHead, pLink; PDFSM_SITE_ENTRY pSiteInfo; ULONG i; pListHead = &_SiteTableHead; for (pLink = pListHead->Flink; pLink != pListHead; pLink = pLink->Flink) { pSiteInfo = CONTAINING_RECORD(pLink, DFSM_SITE_ENTRY, Link); pSiteInfo->Flags |= DFSM_SITE_ENTRY_DELETE_PENDING; } } //+---------------------------------------------------------------------------- // // Function: CSites::SyncPktSiteTable // // Synopsis: Step 3 of a table merge. Walk the table, removing any entries // with the DFSM_SITE_ENTRY_DELETE_PENDING (and telling dfs.sys // to do so) // // Arguments: None // // Returns: Nothing // //----------------------------------------------------------------------------- VOID CSites::SyncPktSiteTable() { PLIST_ENTRY pListHead; PLIST_ENTRY pLink; PLIST_ENTRY pNext; PDFSM_SITE_ENTRY pSiteInfo; IDfsVolInlineDebOut((DEB_TRACE, "CSites::SyncPktSiteTable()\n")); pListHead = &_SiteTableHead; for (pLink = pListHead->Flink; pLink != pListHead; pLink = pNext) { // // Save next in case we delete this one // pNext = pLink->Flink; pSiteInfo = CONTAINING_RECORD(pLink, DFSM_SITE_ENTRY, Link); if ((pSiteInfo->Flags & DFSM_SITE_ENTRY_DELETE_PENDING) != 0) { // // call dfs.sys with the update // DfsSendDelete(pSiteInfo->ServerName); RemoveEntryList(pLink); delete [] pSiteInfo; _fDirty = TRUE; } } IDfsVolInlineDebOut((DEB_TRACE, "CSites::SyncPktSiteTable exit\n")); } //+---------------------------------------------------------------------------- // // Function: CSites::_DumpSiteTable // // Synopsis: Spill dfs's guts about site table entries. // // Arguments: None // // Returns: Nothing // //----------------------------------------------------------------------------- VOID CSites::_DumpSiteTable() { PLIST_ENTRY pListHead, pLink; PDFSM_SITE_ENTRY pSiteInfo; ULONG i; pListHead = &_SiteTableHead; // // Print them (for debugging) // for (pLink = pListHead->Flink; pLink != pListHead; pLink = pLink->Flink) { pSiteInfo = CONTAINING_RECORD(pLink, DFSM_SITE_ENTRY, Link); DbgPrint("\tpSiteInfo(%ws)\n", pSiteInfo->ServerName); for (i = 0; i < pSiteInfo->Info.cSites; i++) { DbgPrint("\t\t%02d:%ws\n", i, pSiteInfo->Info.Site[i].SiteName); } } } //+---------------------------------------------------------------------------- // // Function: DfsSendDelete, private // // Synopsis: Send a DFS_DELETE_SITE_ARG down to dfs.sys // // Arguments: None // // Returns: Nothing // //----------------------------------------------------------------------------- NTSTATUS DfsSendDelete( LPWSTR ServerName) { ULONG size; DWORD dwErr; PDFS_DELETE_SITE_INFO_ARG arg; size = sizeof(DFS_DELETE_SITE_INFO_ARG) + wcslen(ServerName) * sizeof(WCHAR); arg = (PDFS_DELETE_SITE_INFO_ARG) new CHAR [size]; if (arg == NULL) { return ERROR_OUTOFMEMORY; } arg->ServerName.Buffer = (WCHAR *) &arg[1]; arg->ServerName.Length = wcslen(ServerName) * sizeof(WCHAR); arg->ServerName.MaximumLength = arg->ServerName.Length; RtlCopyMemory(arg->ServerName.Buffer, ServerName, arg->ServerName.Length); LPWSTR_TO_OFFSET(arg->ServerName.Buffer,arg); dwErr = DfsDeleteSiteEntry((PCHAR)arg, size); delete [] arg; return dwErr; } //+---------------------------------------------------------------------------- // // Function: DfsSendUpdate, private // // Synopsis: Send a DFS_CREATE_SITE_ARG down to dfs.sys // // Arguments: None // // Returns: Nothing // //----------------------------------------------------------------------------- NTSTATUS DfsSendUpdate( LPWSTR ServerName, ULONG SiteCount, PDFS_SITENAME_INFO pSites) { ULONG size; DWORD dwErr; ULONG i; PDFS_CREATE_SITE_INFO_ARG arg; WCHAR *wCp; size = FIELD_OFFSET(DFS_CREATE_SITE_INFO_ARG,SiteName[SiteCount]) + wcslen(ServerName) * sizeof(WCHAR); for (i = 0; i < SiteCount; i++) { size += wcslen(pSites[i].SiteName) * sizeof(WCHAR); } arg = (PDFS_CREATE_SITE_INFO_ARG) new CHAR [size]; if (arg == NULL) { return ERROR_OUTOFMEMORY; } wCp = (WCHAR *)(&arg->SiteName[SiteCount]); arg->ServerName.Buffer = wCp; wCp += wcslen(ServerName); arg->ServerName.Length = wcslen(ServerName) * sizeof(WCHAR); arg->ServerName.MaximumLength = arg->ServerName.Length; RtlCopyMemory(arg->ServerName.Buffer, ServerName, arg->ServerName.Length); LPWSTR_TO_OFFSET(arg->ServerName.Buffer,arg); arg->SiteCount = SiteCount; for (i = 0; i < SiteCount; i++) { arg->SiteName[i].Buffer = wCp; wCp += wcslen(pSites[i].SiteName); arg->SiteName[i].Length = wcslen(pSites[i].SiteName) * sizeof(WCHAR); arg->SiteName[i].MaximumLength = arg->SiteName[i].Length; RtlCopyMemory(arg->SiteName[i].Buffer, pSites[i].SiteName, arg->SiteName[i].Length); LPWSTR_TO_OFFSET(arg->SiteName[i].Buffer,arg); } dwErr = DfsCreateSiteEntry((PCHAR)arg, size); delete [] arg; return dwErr; }