//+---------------------------------------------------------------------------- // // Copyright (C) 2000, Microsoft Corporation // // File: DfsStore.cxx // // Contents: the base DFS Store class, this contains the common // store functionality. // // Classes: DfsStore. // // History: Dec. 8 2000, Author: udayh // //----------------------------------------------------------------------------- #include "DfsStore.hxx" #include "DfsServerLibrary.hxx" #include // // logging stuff // #include "dfsstore.tmh" extern "C" { DWORD I_NetDfsIsThisADomainName( IN LPWSTR wszName); } // Initialize the common marshalling info for Registry and ADLegacy stores. // INIT_FILE_TIME_INFO(); INIT_DFS_REPLICA_INFO_MARSHAL_INFO(); //+------------------------------------------------------------------------- // // Function: PackGetInfo - unpacks information based on MARSHAL_INFO struct // // Arguments: pInfo - pointer to the info to fill. // ppBuffer - pointer to buffer that holds the binary stream. // pSizeRemaining - pointer to size of above buffer // pMarshalInfo - pointer to information that describes how to // interpret the binary stream. // // Returns: Status // ERROR_SUCCESS if we could unpack the name info // error status otherwise. // // // Description: This routine expects the binary stream to hold all the // information that is necessary to return the information // described by the MARSHAL_INFO structure. //-------------------------------------------------------------------------- DFSSTATUS DfsStore::PackGetInformation( ULONG_PTR Info, PVOID *ppBuffer, PULONG pSizeRemaining, PMARSHAL_INFO pMarshalInfo ) { PMARSHAL_TYPE_INFO typeInfo; DFSSTATUS Status = ERROR_INVALID_DATA; for ( typeInfo = &pMarshalInfo->_typeInfo[0]; typeInfo < &pMarshalInfo->_typeInfo[pMarshalInfo->_typecnt]; typeInfo++ ) { switch ( typeInfo->_type & MTYPE_BASE_TYPE ) { case MTYPE_COMPOUND: Status = PackGetInformation(Info + typeInfo->_off, ppBuffer, pSizeRemaining, typeInfo->_subinfo); break; case MTYPE_ULONG: Status = PackGetULong( (PULONG)(Info + typeInfo->_off), ppBuffer, pSizeRemaining ); break; case MTYPE_PWSTR: Status = PackGetString( (PUNICODE_STRING)(Info + typeInfo->_off), ppBuffer, pSizeRemaining ); break; case MTYPE_GUID: Status = PackGetGuid( (GUID *)(Info + typeInfo->_off), ppBuffer, pSizeRemaining ); break; default: break; } } return Status; } //+------------------------------------------------------------------------- // // Function: PackSetInformation - packs information based on MARSHAL_INFO struct // // Arguments: pInfo - pointer to the info buffer to pack // ppBuffer - pointer to buffer that holds the binary stream. // pSizeRemaining - pointer to size of above buffer // pMarshalInfo - pointer to information that describes how to // pack the info into the binary stream. // // Returns: Status // ERROR_SUCCESS if we could pack the info // error status otherwise. // // // Description: This routine expects the binary stream can hold all the // information that is necessary to pack the information // described by the MARSHAL_INFO structure. //-------------------------------------------------------------------------- DFSSTATUS DfsStore::PackSetInformation( ULONG_PTR Info, PVOID *ppBuffer, PULONG pSizeRemaining, PMARSHAL_INFO pMarshalInfo ) { PMARSHAL_TYPE_INFO typeInfo; DFSSTATUS Status = ERROR_INVALID_DATA; for ( typeInfo = &pMarshalInfo->_typeInfo[0]; typeInfo < &pMarshalInfo->_typeInfo[pMarshalInfo->_typecnt]; typeInfo++ ) { switch ( typeInfo->_type & MTYPE_BASE_TYPE ) { case MTYPE_COMPOUND: Status = PackSetInformation( Info + typeInfo->_off, ppBuffer, pSizeRemaining, typeInfo->_subinfo); break; case MTYPE_ULONG: Status = PackSetULong( *(PULONG)(Info + typeInfo->_off), ppBuffer, pSizeRemaining ); break; case MTYPE_PWSTR: Status = PackSetString( (PUNICODE_STRING)(Info + typeInfo->_off), ppBuffer, pSizeRemaining ); break; case MTYPE_GUID: Status = PackSetGuid( (GUID *)(Info + typeInfo->_off), ppBuffer, pSizeRemaining ); break; default: break; } } return Status; } //+------------------------------------------------------------------------- // // Function: PackSizeInformation - packs information based on MARSHAL_INFO struct // // Arguments: pInfo - pointer to the info buffer to pack // ppBuffer - pointer to buffer that holds the binary stream. // pSizeRemaining - pointer to size of above buffer // pMarshalInfo - pointer to information that describes how to // pack the info into the binary stream. // // Returns: Status // ERROR_SUCCESS if we could pack the info // error status otherwise. // // // Description: This routine expects the binary stream can hold all the // information that is necessary to pack the information // described by the MARSHAL_INFO structure. //-------------------------------------------------------------------------- ULONG DfsStore::PackSizeInformation( ULONG_PTR Info, PMARSHAL_INFO pMarshalInfo ) { PMARSHAL_TYPE_INFO typeInfo; ULONG Size = 0; for ( typeInfo = &pMarshalInfo->_typeInfo[0]; typeInfo < &pMarshalInfo->_typeInfo[pMarshalInfo->_typecnt]; typeInfo++ ) { switch ( typeInfo->_type & MTYPE_BASE_TYPE ) { case MTYPE_COMPOUND: Size += PackSizeInformation( Info + typeInfo->_off, typeInfo->_subinfo); break; case MTYPE_ULONG: Size += PackSizeULong(); break; case MTYPE_PWSTR: Size += PackSizeString( (PUNICODE_STRING)(Info + typeInfo->_off) ); break; case MTYPE_GUID: Size += PackSizeGuid(); break; default: break; } } return Size; } //+------------------------------------------------------------------------- // // Function: PackGetReplicaInformation - Unpacks the replica info // // Arguments: pDfsReplicaInfo - pointer to the info to fill. // ppBuffer - pointer to buffer that holds the binary stream. // pSizeRemaining - pointer to size of above buffer // // Returns: Status // ERROR_SUCCESS if we could unpack the name info // error status otherwise. // // // Description: This routine expects the binary stream to hold all the // information that is necessary to return a complete replica // info structure (as defined by MiDfsReplicaInfo). If the stream // does not have the sufficient info, ERROR_INVALID_DATA is // returned back. // This routine expects to find "replicaCount" number of individual // binary streams in passed in buffer. Each stream starts with // the size of the stream, followed by that size of data. // //-------------------------------------------------------------------------- DFSSTATUS DfsStore::PackGetReplicaInformation( PDFS_REPLICA_LIST_INFORMATION pReplicaListInfo, PVOID *ppBuffer, PULONG pSizeRemaining) { ULONG Count; ULONG ReplicaSizeRemaining; PVOID nextStream; DFSSTATUS Status = ERROR_SUCCESS; for ( Count = 0; Count < pReplicaListInfo->ReplicaCount; Count++ ) { PDFS_REPLICA_INFORMATION pReplicaInfo; pReplicaInfo = &pReplicaListInfo->pReplicas[Count]; // // We now have a binary stream in ppBuffer, the first word of which // indicates the size of this stream. // Status = PackGetULong( &pReplicaInfo->DataSize, ppBuffer, pSizeRemaining ); // // ppBuffer is now pointing past the size (to the binary stream) // because UnpackUlong added size of ulong to it. // Unravel that stream into the next array element. // Note that when this unpack returns, the ppBuffer is not necessarily // pointing to the next binary stream within this blob. // if(pReplicaInfo->DataSize > *pSizeRemaining) { Status = ERROR_INVALID_DATA; } if ( Status == ERROR_SUCCESS ) { nextStream = *ppBuffer; ReplicaSizeRemaining = pReplicaInfo->DataSize; Status = PackGetInformation( (ULONG_PTR)pReplicaInfo, ppBuffer, &ReplicaSizeRemaining, &MiDfsReplicaInfo ); if(Status == ERROR_SUCCESS) { // // We now point the buffer to the next sub-stream, which is the previous // stream + the size of the stream. We also set the remaining size // appropriately. // *ppBuffer = (PVOID)((ULONG_PTR)nextStream + pReplicaInfo->DataSize); *pSizeRemaining -= pReplicaInfo->DataSize; } } if ( Status != ERROR_SUCCESS ) { break; } } return Status; } //+------------------------------------------------------------------------- // // Function: PackSetReplicaInformation - packs the replica info // // Arguments: pDfsReplicaInfo - pointer to the info to pack // ppBuffer - pointer to buffer that holds the binary stream. // pSizeRemaining - pointer to size of above buffer // // Returns: Status // ERROR_SUCCESS if we could pack the replica info // error status otherwise. // // // Description: This routine expects the binary stream to be able to // hold the information that will be copied from the // info structure (as defined by MiDfsReplicaInfo). If the stream // does not have the sufficient info, ERROR_INVALID_DATA is // returned back. // This routine stores a "replicaCount" number of individual // binary streams as the first ulong, and then it packs each // stream. // //-------------------------------------------------------------------------- DFSSTATUS DfsStore::PackSetReplicaInformation( PDFS_REPLICA_LIST_INFORMATION pReplicaListInfo, PVOID *ppBuffer, PULONG pSizeRemaining) { ULONG Count; ULONG ReplicaSizeRemaining; PVOID nextStream; DFSSTATUS Status = ERROR_SUCCESS; for ( Count = 0; Count < pReplicaListInfo->ReplicaCount; Count++ ) { PDFS_REPLICA_INFORMATION pReplicaInfo; pReplicaInfo = &pReplicaListInfo->pReplicas[Count]; // // We now have a binary stream in ppBuffer, the first word of which // indicates the size of this stream. // Status = PackSetULong( pReplicaInfo->DataSize, ppBuffer, pSizeRemaining ); // // ppBuffer is now pointing past the size (to the binary stream) // because packUlong added size of ulong to it. // Unravel that stream into the next array element. // Note that when this returns, the ppBuffer is not necessarily // pointing to the next binary stream within this blob. // if ( Status == ERROR_SUCCESS ) { nextStream = *ppBuffer; ReplicaSizeRemaining = pReplicaInfo->DataSize; Status = PackSetInformation( (ULONG_PTR)pReplicaInfo, ppBuffer, &ReplicaSizeRemaining, &MiDfsReplicaInfo ); // // We now point the buffer to the next sub-stream, which is the previos // stream + the size of the stream. We also set the remaining size // appropriately. // *ppBuffer = (PVOID)((ULONG_PTR)nextStream + pReplicaInfo->DataSize); *pSizeRemaining -= pReplicaInfo->DataSize; } if ( Status != ERROR_SUCCESS ) { break; } } return Status; } //+------------------------------------------------------------------------- // // Function: PackSizeReplicaInformation - packs the replica info // // Arguments: pDfsReplicaInfo - pointer to the info to pack // ppBuffer - pointer to buffer that holds the binary stream. // pSizeRemaining - pointer to size of above buffer // // Returns: Status // ERROR_SUCCESS if we could pack the replica info // error status otherwise. // // // Description: This routine expects the binary stream to be able to // hold the information that will be copied from the // info structure (as defined by MiDfsReplicaInfo). If the stream // does not have the sufficient info, ERROR_INVALID_DATA is // returned back. // This routine stores a "replicaCount" number of individual // binary streams as the first ulong, and then it packs each // stream. // //-------------------------------------------------------------------------- ULONG DfsStore::PackSizeReplicaInformation( PDFS_REPLICA_LIST_INFORMATION pReplicaListInfo ) { ULONG Count; ULONG Size = 0; for ( Count = 0; Count < pReplicaListInfo->ReplicaCount; Count++ ) { PDFS_REPLICA_INFORMATION pReplicaInfo; pReplicaInfo = &pReplicaListInfo->pReplicas[Count]; Size += PackSizeULong(); pReplicaInfo->DataSize = PackSizeInformation( (ULONG_PTR)pReplicaInfo, &MiDfsReplicaInfo ); Size += pReplicaInfo->DataSize; } return Size; } //+------------------------------------------------------------------------- // // Function: LookupRoot - Find a root // // Arguments: pContextName - the Dfs Name Context // pLogicalShare - the Logical Share // ppRoot - the DfsRootFolder that was found // // Returns: Status // ERROR_SUCCESS if we found a matching root // error status otherwise. // // // Description: This routine takes a Dfs name context and logical share, // and returns a Root that matches the passed in name // context and logical share, if one exists. // Note that the same DFS NC and logical share may exist // in more than one store (though very unlikely). In this // case, the first registered store wins. // IF found, the reference root folder will be returned. // It is the callers responsibility to release this referencce // when the caller is done with this root. // //-------------------------------------------------------------------------- DFSSTATUS DfsStore::LookupRoot( PUNICODE_STRING pContextName, PUNICODE_STRING pLogicalShare, DfsRootFolder **ppRootFolder ) { DFSSTATUS Status; DfsRootFolder *pRoot; UNICODE_STRING DfsNetbiosNameContext; DFS_TRACE_LOW( REFERRAL_SERVER, "Lookup root %wZ %wZ\n", pContextName, pLogicalShare); // // First, convert the context name to a netbios context name. // DfsGetNetbiosName( pContextName, &DfsNetbiosNameContext, NULL ); // // Lock the store, so that we dont have new roots coming in while // we are taking a look. // Status = AcquireReadLock(); if ( Status != ERROR_SUCCESS ) { return Status; } // // The default return status is ERROR_NOT_FOUND; // Status = ERROR_NOT_FOUND; // // Run through our list of DFS roots, and see if any of them match // the passed in name context and logical share. // pRoot = _DfsRootList; if (pRoot != NULL) { do { DFS_TRACE_LOW( REFERRAL_SERVER, "Lookup root, checking root %wZ \n", pRoot->GetLogicalShare()); // // If the Root indicates that the name context needs to be // ignored, just check for logical share name match (aliasing // support). // Otherwise, compare the namecontext in the cases where // the passed in name context is not empty. // if ( (pRoot->IsIgnoreNameContext() == TRUE) || (DfsNetbiosNameContext.Length != 0 && (RtlCompareUnicodeString(&DfsNetbiosNameContext, pRoot->GetNetbiosNameContext(), TRUE) == 0 )) ) { if ( RtlCompareUnicodeString( pLogicalShare, pRoot->GetLogicalShare(), TRUE) == 0 ) { Status = ERROR_SUCCESS; break; } } pRoot = pRoot->pNextRoot; } while ( pRoot != _DfsRootList ); } // // IF we found a matching root, bump up its reference count, and // return the pointer to the root. // if ( Status == ERROR_SUCCESS ) { pRoot->AcquireReference(); *ppRootFolder = pRoot; } ReleaseLock(); DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "Done Lookup root for %wZ, root %p status %x\n", pLogicalShare, pRoot, Status); return Status; } DFSSTATUS DfsStore::GetRootPhysicalShare( HKEY RootKey, PUNICODE_STRING pRootPhysicalShare ) { DFSSTATUS Status = ERROR_SUCCESS; Status = DfsGetRegValueString( RootKey, DfsRootShareValueName, pRootPhysicalShare ); return Status; } VOID DfsStore::ReleaseRootPhysicalShare( PUNICODE_STRING pRootPhysicalShare ) { DfsReleaseRegValueString( pRootPhysicalShare ); } DFSSTATUS DfsStore::GetRootLogicalShare( HKEY RootKey, PUNICODE_STRING pRootLogicalShare ) { DFSSTATUS Status; Status = DfsGetRegValueString( RootKey, DfsLogicalShareValueName, pRootLogicalShare ); return Status; } VOID DfsStore::ReleaseRootLogicalShare( PUNICODE_STRING pRootLogicalShare ) { DfsReleaseRegValueString( pRootLogicalShare ); } //+------------------------------------------------------------------------- // // Function: StoreRecognizeNewDfs - the recognizer for new style dfs // // Arguments: Name - the namespace of interest. // DfsKey - the key for the DFS registry space. // // Returns: Status // ERROR_SUCCESS on success // ERROR status code otherwise // // // Description: This routine looks up all the standalone roots // hosted on this machine, and looks up the metadata for // the roots and either creates new roots or updates existing // ones to reflect the current children of the root. // //-------------------------------------------------------------------------- DFSSTATUS DfsStore::StoreRecognizeNewDfs( LPWSTR MachineName, HKEY DfsKey ) { DfsRootFolder *pRootFolder = NULL; HKEY DfsRootKey = NULL; DFSSTATUS Status = ERROR_SUCCESS; ULONG ChildNum = 0; DWORD CchMaxName = 0; DWORD CchChildName = 0; LPWSTR ChildName = NULL; DFSSTATUS RetStatus = ERROR_SUCCESS; DFS_TRACE_LOW(REFERRAL_SERVER, "Attempting to recognize new DFS\n"); // // First find the length of the longest subkey // and allocate a buffer big enough for it. // Status = RegQueryInfoKey( DfsKey, // Key NULL, // Class string NULL, // Size of class string NULL, // Reserved NULL, // # of subkeys &CchMaxName, // max size of subkey name in TCHARs NULL, // max size of class name NULL, // # of values NULL, // max size of value name NULL, // max size of value data, NULL, // security descriptor NULL ); // Last write time if (Status == ERROR_SUCCESS) { CchMaxName++; // Space for the NULL terminator. ChildName = (LPWSTR) new WCHAR [CchMaxName]; if (ChildName == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; } } if (Status == ERROR_SUCCESS) { // // For each child, get the child name. // do { CchChildName = CchMaxName; // // Now enumerate the children, starting from the first child. // Status = RegEnumKeyEx( DfsKey, ChildNum, ChildName, &CchChildName, NULL, NULL, NULL, NULL ); ChildNum++; if (Status == ERROR_SUCCESS) { DFS_TRACE_LOW(REFERRAL_SERVER, "Recognize New Dfs, found root (#%d) with metaname %ws\n", ChildNum, ChildName ); // // We have the name of a child, so open the key to // that root. // Status = RegOpenKeyEx( DfsKey, ChildName, 0, KEY_READ, &DfsRootKey ); if (Status == ERROR_SUCCESS) { DFSSTATUS RootStatus = ERROR_SUCCESS; // // Now get either an existing root by this name, // or create a new one. The error we get here is // not the original error generated by AD (or the Registry). // xxx We should try to propagate that error in future. // RootStatus = GetRootFolder( MachineName, ChildName, DfsRootKey, &pRootFolder ); if (RootStatus == ERROR_SUCCESS) { // // Call the synchronize method on the root to // update it with the latest children. // Again, ignore the error since we need to move // on to the next root. // dfsdev: need eventlog to signal this. // RootStatus = pRootFolder->Synchronize(); // // If the Synchronize above had succeeded, then it would've created // all the reparse points that the root needs. Now we need to add // this volume as one that has DFS reparse points so that we know // to perform garbage collection on this volume later on. // (VOID)pRootFolder->AddReparseVolumeToList(); // Release our reference on the root folder. pRootFolder->ReleaseReference(); } if (RootStatus != ERROR_SUCCESS) { if (!DfsStartupProcessingDone()) { const TCHAR * apszSubStrings[4]; apszSubStrings[0] = ChildName; DfsLogDfsEvent( DFS_ERROR_ON_ROOT, 1, apszSubStrings, RootStatus); } // Return the bad news. RetStatus = RootStatus; } DFS_TRACE_ERROR_LOW(RootStatus, REFERRAL_SERVER, "Recognize DFS: Root folder for %ws, Synchronize status %x\n", ChildName, RootStatus ); // // Close the root key, we are done with it. // RegCloseKey( DfsRootKey ); } } } while (Status == ERROR_SUCCESS); delete [] ChildName; } // // If we ran out of children, then return success code. // if (Status == ERROR_NO_MORE_ITEMS) { Status = ERROR_SUCCESS; } DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "Done with recognize new dfs, Status 0x%x, RetStatus 0x%x\n", Status, RetStatus); // If any of the roots failed to load, convey that to the caller. if (Status == ERROR_SUCCESS && RetStatus != ERROR_SUCCESS) { Status = RetStatus; } return Status; } //+------------------------------------------------------------------------- // // Function: StoreRecognizeNewDfs - the recognizer for new style dfs // // Arguments: Name - the namespace of interest. // LogicalShare // // Returns: Status // ERROR_SUCCESS on success // ERROR status code otherwise // // // Description: This routine looks up all the domain roots // hosted on this machine, and looks up the metadata for // the roots and either creates new roots or updates existing // ones to reflect the current children of the root. // //-------------------------------------------------------------------------- DFSSTATUS DfsStore::StoreRecognizeNewDfs ( LPWSTR DfsNameContext, PUNICODE_STRING pLogicalShare ) { DfsRootFolder *pRootFolder = NULL; DFSSTATUS Status = ERROR_SUCCESS; DFS_TRACE_LOW(REFERRAL_SERVER, "Attempting to recognize new remote DFS\n"); // // Now get either an existing root by this name, // or create a new one. // Status = GetRootFolder( DfsNameContext, pLogicalShare, &pRootFolder ); if (Status == ERROR_SUCCESS) { // // Call the synchronize method on the root to // update it with the latest children. // Status = pRootFolder->Synchronize(); DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "Recognize DFS: Root folder for %wZ, Synchronize status %x\n", pLogicalShare, Status ); // Release our reference on the root folder. pRootFolder->ReleaseReference(); } DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "Done with recognize new remote dfs, Status %x\n", Status); return Status; } //+------------------------------------------------------------------------- // // Function: DfsGetRootFolder - Get a root folder if the machine // hosts a registry based DFS. // // Arguments: Name - the namespace of interest. // Key - the root key // ppRootFolder - the created folder. // // Returns: Status // ERROR_SUCCESS on success // ERROR status code otherwise // // // Description: This routine reads in the information // about the root and creates and adds it to our // list of known roots, if this one does not already // exist in our list. // //-------------------------------------------------------------------------- DFSSTATUS DfsStore::GetRootFolder ( LPWSTR DfsNameContextString, LPWSTR RootRegKeyName, HKEY DfsRootKey, DfsRootFolder **ppRootFolder ) { DFSSTATUS Status = ERROR_SUCCESS; UNICODE_STRING LogicalShare; UNICODE_STRING PhysicalShare; // // Get the logical name information of this root. // Status = GetRootLogicalShare( DfsRootKey, &LogicalShare ); // // we successfully got the logical share, now get the physical share // if ( Status == ERROR_SUCCESS ) { Status = GetRootPhysicalShare( DfsRootKey, &PhysicalShare ); if (Status == ERROR_SUCCESS) { Status = GetRootFolder ( DfsNameContextString, RootRegKeyName, &LogicalShare, &PhysicalShare, ppRootFolder ); ReleaseRootPhysicalShare( &PhysicalShare ); } ReleaseRootLogicalShare( &LogicalShare ); } return Status; } DFSSTATUS DfsStore::GetRootFolder ( LPWSTR DfsNameContextString, LPWSTR RootRegKeyName, PUNICODE_STRING pLogicalShare, PUNICODE_STRING pPhysicalShare, DfsRootFolder **ppRootFolder ) { DFSSTATUS Status = ERROR_SUCCESS; UNICODE_STRING DfsNameContext; DFS_TRACE_LOW(REFERRAL_SERVER, "Get Root Folder for %wZ\n", pLogicalShare); // // we have both the logical DFS share name, as well as the local machine // physical share that is backing the DFS logical share. // now get a root folder for this dfs root. // Status = DfsRtlInitUnicodeStringEx( &DfsNameContext, DfsNameContextString ); if(Status != ERROR_SUCCESS) { return Status; } // // Check if we already know about this root. If we do, this // routine gives us a referenced root folder which we can return. // If not, we create a brand new root folder. // Status = LookupRoot( &DfsNameContext, pLogicalShare, ppRootFolder ); if (Status != STATUS_SUCCESS ) { // // We check if we can proceed here. // DFSSTATUS RootStatus; RootStatus = DfsCheckServerRootHandlingCapability(); if (RootStatus == ERROR_NOT_SUPPORTED) { DfsLogDfsEvent(DFS_ERROR_MUTLIPLE_ROOTS_NOT_SUPPORTED, 0, NULL, RootStatus); return RootStatus; } if (RootStatus != ERROR_SUCCESS) { return RootStatus; } } // // we did not find a root, so create a new one. // if ( Status != STATUS_SUCCESS ) { Status = CreateNewRootFolder( DfsNameContextString, RootRegKeyName, pLogicalShare, pPhysicalShare, ppRootFolder ); DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "Created New Dfs Root %p, Share %wZ, Status %x\n", *ppRootFolder, pPhysicalShare, Status); } else { // // There is an existing root with this name. // Validate that the root we looked up matches the // metadata we are currently processing. If not, we // have 2 roots with the same logical share name in the // registry which is bogus. // // Dfs dev: rip out the following code. // just check for equality of the 2 roots. // they will not be null strings // if (RootRegKeyName != NULL) { if (_wcsicmp(RootRegKeyName, (*ppRootFolder)->GetRootRegKeyNameString()) != 0) { (*ppRootFolder)->ReleaseReference(); *ppRootFolder = NULL; Status = ERROR_DUP_NAME; } } else { if (IsEmptyString((*ppRootFolder)->GetRootRegKeyNameString()) == FALSE) { (*ppRootFolder)->ReleaseReference(); *ppRootFolder = NULL; Status = ERROR_DUP_NAME; } } // // If the above comparison matched, we found a root for the // logical volume, make sure that the physical share names // that is exported on the local machine to back the logical share // matches. // if (Status == ERROR_SUCCESS) { if (RtlCompareUnicodeString( pPhysicalShare, (*ppRootFolder)->GetRootPhysicalShareName(), TRUE ) != 0) { // // dfsdev: use appropriate status code. // (*ppRootFolder)->ReleaseReference(); *ppRootFolder = NULL; Status = ERROR_INVALID_PARAMETER; } } } DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "GetRootFolder: Root %p, Status %x\n", *ppRootFolder, Status); return Status; } //+------------------------------------------------------------------------- // // Function: DfsGetRootFolder - Get a root folder given a remote // . // // // Arguments: Name - the namespace of interest. // LogicalShare // ppRootFolder - the created folder. // // Returns: Status // ERROR_SUCCESS on success // ERROR status code otherwise // // // Description: This routine creates and adds a root folder to our // list of known roots, if this one does not already // exist in our list. This doesn't care to read any of the information // about its physical share, etc. The caller (a 'referral server') // is remote. // //-------------------------------------------------------------------------- DFSSTATUS DfsStore::GetRootFolder ( LPWSTR DfsNameContextString, PUNICODE_STRING pLogicalShare, DfsRootFolder **ppRootFolder ) { DFSSTATUS Status = ERROR_SUCCESS; UNICODE_STRING DfsNameContext; UNICODE_STRING DfsShare; DFS_TRACE_LOW(REFERRAL_SERVER, "Get Root Folder (direct) for %wZ\n", pLogicalShare); Status = DfsRtlInitUnicodeStringEx( &DfsNameContext, DfsNameContextString ); if(Status != ERROR_SUCCESS) { return Status; } // // Check if we already know about this root. If we do, this // routine gives us a referenced root folder which we can return. // If not, we create a brand new root folder. // Status = DfsCreateUnicodeString( &DfsShare, pLogicalShare); if (Status != ERROR_SUCCESS) { return Status; } Status = LookupRoot( &DfsNameContext, pLogicalShare, ppRootFolder ); // // we did not find a root, so create a new one. // if ( Status != STATUS_SUCCESS ) { Status = CreateNewRootFolder( DfsNameContextString, DfsShare.Buffer, pLogicalShare, pLogicalShare, ppRootFolder ); DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "Created New Dfs Root %p, Share %wZ, Status %x\n", *ppRootFolder, pLogicalShare, Status); } else { // // There's precious little to check at this point to weed out duplicates. // We don't know what the physical share should be because we didn't // read that information from the (remote) registry. // NOTHING; DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "LookupRoot succeeded on folder %p, Share %wZ, Status %x\n", *ppRootFolder, pLogicalShare, Status); } DfsFreeUnicodeString( &DfsShare); return Status; } DFSSTATUS DfsStore::PackageEnumerationInfo( DWORD Level, DWORD EntryCount, LPBYTE pLinkBuffer, LPBYTE pBuffer, LPBYTE *ppCurrentBuffer, PLONG pSizeRemaining ) { PDFS_API_INFO pInfo = NULL; PDFS_API_INFO pCurrent = (PDFS_API_INFO)pLinkBuffer; LONG HeaderSize = 0; ULONG_PTR NextFreeMemory = NULL; PDFS_STORAGE_INFO pNewStorage = NULL; PDFS_STORAGE_INFO pOldStorage = NULL; LONG TotalStores = 0; LONG i = 0; DFSSTATUS Status = ERROR_SUCCESS; LONG NeedLen = 0; Status = DfsApiSizeLevelHeader( Level, &HeaderSize ); if (Status != ERROR_SUCCESS) { return Status; } NextFreeMemory = (ULONG_PTR)*ppCurrentBuffer; pInfo = (PDFS_API_INFO)((ULONG_PTR)pBuffer + HeaderSize * EntryCount); RtlCopyMemory( pInfo, pLinkBuffer, HeaderSize ); pNewStorage = NULL; switch (Level) { case 4: if (pNewStorage == NULL) { pNewStorage = pInfo->Info4.Storage = (PDFS_STORAGE_INFO)NextFreeMemory; pOldStorage = pCurrent->Info4.Storage; TotalStores = pInfo->Info4.NumberOfStorages; } case 3: if (pNewStorage == NULL) { pNewStorage = pInfo->Info3.Storage = (PDFS_STORAGE_INFO)NextFreeMemory; pOldStorage = pCurrent->Info3.Storage; TotalStores = pInfo->Info3.NumberOfStorages; } NeedLen = sizeof(DFS_STORAGE_INFO) * TotalStores; if (*pSizeRemaining >= NeedLen) { *pSizeRemaining -= NeedLen; NextFreeMemory += NeedLen; } else{ return ERROR_BUFFER_OVERFLOW; } for (i = 0; i < TotalStores; i++) { pNewStorage[i] = pOldStorage[i]; pNewStorage[i].ServerName = (LPWSTR)NextFreeMemory; NeedLen = (wcslen(pOldStorage[i].ServerName) + 1) * sizeof(WCHAR); if (*pSizeRemaining >= NeedLen) { *pSizeRemaining -= NeedLen; wcscpy(pNewStorage[i].ServerName, pOldStorage[i].ServerName); NextFreeMemory += NeedLen; } else { return ERROR_BUFFER_OVERFLOW; } pNewStorage[i].ShareName = (LPWSTR)NextFreeMemory; NeedLen = (wcslen(pOldStorage[i].ShareName) + 1) * sizeof(WCHAR); if (*pSizeRemaining >= NeedLen) { *pSizeRemaining -= NeedLen; wcscpy(pNewStorage[i].ShareName, pOldStorage[i].ShareName); NextFreeMemory += NeedLen; } else { return ERROR_BUFFER_OVERFLOW; } } case 2: pInfo->Info2.Comment = (LPWSTR)NextFreeMemory; NeedLen = (wcslen(pCurrent->Info2.Comment) + 1) * sizeof(WCHAR); if (*pSizeRemaining >= NeedLen) { *pSizeRemaining -= NeedLen; wcscpy(pInfo->Info2.Comment, pCurrent->Info2.Comment); NextFreeMemory += NeedLen; } else { return ERROR_BUFFER_OVERFLOW; } case 1: pInfo->Info1.EntryPath = (LPWSTR)NextFreeMemory; NeedLen = (wcslen(pCurrent->Info1.EntryPath) + 1) * sizeof(WCHAR); if (*pSizeRemaining >= NeedLen) { *pSizeRemaining -= NeedLen; wcscpy(pInfo->Info1.EntryPath, pCurrent->Info1.EntryPath); NextFreeMemory += NeedLen; } else { return ERROR_BUFFER_OVERFLOW; } *ppCurrentBuffer = (LPBYTE)NextFreeMemory; break; default: Status = ERROR_INVALID_PARAMETER; } return Status; } DFSSTATUS DfsStore::EnumerateRoots( BOOLEAN DomainRoots, PULONG_PTR pBuffer, PULONG pBufferSize, PULONG pEntriesRead, ULONG MaxEntriesToRead, PULONG pCurrentNumRoots, LPDWORD pResumeHandle, PULONG pSizeRequired ) { ULONG RootCount = 0; DFSSTATUS Status = ERROR_SUCCESS; DfsRootFolder *pRoot = NULL; PULONG pDfsInfo; ULONG TotalSize, EntriesRead; BOOLEAN OverFlow = FALSE; ULONG ResumeRoot; // // We start with total size and entries read with the passed in // values, since we expect them to be initialized correctly, and // possibly hold values from enumerations of other dfs flavors. // TotalSize = *pSizeRequired; EntriesRead = *pEntriesRead; // // point the dfsinfo300 structure to the start of buffer passed in // we will use this as an array of info200 buffers. // pDfsInfo = (PULONG)*pBuffer; // // We might not have to start at the beginning // ResumeRoot = 0; if (pResumeHandle && *pResumeHandle > 0) { ResumeRoot = *pResumeHandle; } // // now enumerate each child, and read its logical share name. // update the total size required: if we have sufficient space // in the passed in buffer, copy the information into the buffer. // RootCount = 0; while (Status == ERROR_SUCCESS) { // // Cap the total number of roots we can read. // if (EntriesRead >= MaxEntriesToRead) { break; } Status = FindNextRoot(RootCount, &pRoot); if (Status == ERROR_SUCCESS) { RootCount++; // // We need to skip entries unless we get to the root we // plan on resuming with. // if ((RootCount + *pCurrentNumRoots) > ResumeRoot) { PUNICODE_STRING pRootName; pRootName = pRoot->GetLogicalShare(); if (DomainRoots == TRUE) { Status = AddRootEnumerationInfo200( pRootName, (PDFS_INFO_200 *)(&pDfsInfo), pBufferSize, &EntriesRead, &TotalSize ); } else { UNICODE_STRING Context; Status = pRoot->GetVisibleContext(&Context); if (Status == ERROR_SUCCESS) { Status = AddRootEnumerationInfo( &Context, pRootName, pRoot->GetRootFlavor(), (PDFS_INFO_300 *)(&pDfsInfo), pBufferSize, &EntriesRead, &TotalSize ); pRoot->ReleaseVisibleContext(&Context); } } // // Continue on even if we get a buffer overflow. // We need to calculate the SizeRequired anyway. // if (Status == ERROR_BUFFER_OVERFLOW) { OverFlow = TRUE; Status = ERROR_SUCCESS; } DFS_TRACE_HIGH(API, "enumeratin child %wZ, Status %x\n", pRootName, Status); } pRoot->ReleaseReference(); } } // // if we broked out of the loop due to lack of children, // update the return pointers correctly. // if we had detected an overflow condition above, return overflow // otherwise return success. // *pSizeRequired = TotalSize; *pEntriesRead = EntriesRead; if (Status == ERROR_NOT_FOUND) { if (OverFlow) { Status = ERROR_BUFFER_OVERFLOW; } else { // // Don't return NO_MORE_ITEMS here because // the caller considers anything other than buffer overflow to be an error. // Before we return the final values, we'll adjust that // final status depending on the *total* number of // entries returned (which includes those returned from other stores). // Status = ERROR_SUCCESS; } } else if (OverFlow) { Status = ERROR_BUFFER_OVERFLOW; } // // We update resume handle only if we are returning success. // if (Status == ERROR_SUCCESS) { // // the buffer is now pointing to the next unused pDfsInfo array // entry: this lets the next enumerated flavor continue where // we left off. // *pBuffer = (ULONG_PTR)pDfsInfo; (*pCurrentNumRoots) += RootCount; } DFS_TRACE_NORM(REFERRAL_SERVER, "done with flavor read %x, Status %x\n", *pEntriesRead, Status); return Status; } DFSSTATUS DfsStore::AddRootEnumerationInfo200( PUNICODE_STRING pRootName, PDFS_INFO_200 *ppDfsInfo200, PULONG pBufferSize, PULONG pEntriesRead, PULONG pTotalSize ) { ULONG NeedSize; DFSSTATUS Status = ERROR_SUCCESS; DFS_TRACE_NORM(REFERRAL_SERVER, "add root enum: Read %d\n", *pEntriesRead); // // calculate amount of buffer space requirewd. // NeedSize = sizeof(DFS_INFO_200) + pRootName->MaximumLength; // // if it fits in the amount of space we have, we // can copy the info into the passed in buffer. // if (NeedSize <= *pBufferSize) { ULONG_PTR pStringBuffer; // // position the string buffer to the end of the buffer, // leaving enough space to copy the string. // This strategy allows us to treat the pDfsInfo200 // as an array, marching forward from the beginning // of the buffer, while the strings are allocated // starting from the end of the buffer, since we // dont know how many pDfsInfo200 buffers we will // be using. // pStringBuffer = (ULONG_PTR)(*ppDfsInfo200) + *pBufferSize - pRootName->MaximumLength; wcscpy( (LPWSTR)pStringBuffer, pRootName->Buffer); (*ppDfsInfo200)->FtDfsName = (LPWSTR)pStringBuffer; *pBufferSize -= NeedSize; (*pEntriesRead)++; (*ppDfsInfo200)++; } else { // // if the size does not fit, we have overflowed. // Status = ERROR_BUFFER_OVERFLOW; } // // set the total size under all circumstances. // *pTotalSize += NeedSize; DFS_TRACE_NORM(REFERRAL_SERVER, "add root enum: Read %d, Status %x\n", *pEntriesRead, Status); return Status; } DFSSTATUS DfsStore::AddRootEnumerationInfo( PUNICODE_STRING pVisibleName, PUNICODE_STRING pRootName, DWORD Flavor, PDFS_INFO_300 *ppDfsInfo300, PULONG pBufferSize, PULONG pEntriesRead, PULONG pTotalSize ) { ULONG NeedSize; DFSSTATUS Status = ERROR_SUCCESS; ULONG StringLen; DFS_TRACE_NORM(REFERRAL_SERVER, "add root enum: Read %d\n", *pEntriesRead); // // calculate amount of buffer space required. // StringLen = sizeof(WCHAR) + pVisibleName->Length + sizeof(WCHAR) + pRootName->Length + sizeof(WCHAR); NeedSize = sizeof(DFS_INFO_300) + StringLen; // // if it fits in the amount of space we have, we // can copy the info into the passed in buffer. // if (NeedSize <= *pBufferSize) { LPWSTR pStringBuffer; // // position the string buffer to the end of the buffer, // leaving enough space to copy the string. // This strategy allows us to treat the pDfsInfo300 // as an array, marching forward from the beginning // of the buffer, while the strings are allocated // starting from the end of the buffer, since we // dont know how many pDfsInfo300 buffers we will // be using. // pStringBuffer = (LPWSTR)((ULONG_PTR)(*ppDfsInfo300) + *pBufferSize - StringLen); RtlZeroMemory(pStringBuffer, StringLen); pStringBuffer[wcslen(pStringBuffer)] = UNICODE_PATH_SEP; RtlCopyMemory( &pStringBuffer[wcslen(pStringBuffer)], pVisibleName->Buffer, pVisibleName->Length); pStringBuffer[wcslen(pStringBuffer)] = UNICODE_PATH_SEP; RtlCopyMemory( &pStringBuffer[wcslen(pStringBuffer)], pRootName->Buffer, pRootName->Length); pStringBuffer[wcslen(pStringBuffer)] = UNICODE_NULL; (*ppDfsInfo300)->DfsName = (LPWSTR)pStringBuffer; (*ppDfsInfo300)->Flags = Flavor; *pBufferSize -= NeedSize; (*pEntriesRead)++; (*ppDfsInfo300)++; } else { // // if the size does not fit, we have overflowed. // Status = ERROR_BUFFER_OVERFLOW; } // // set the total size under all circumstances. // *pTotalSize += NeedSize; DFS_TRACE_NORM(REFERRAL_SERVER, "add root enum: Read %d, Status %x\n", *pEntriesRead, Status); return Status; } DFSSTATUS DfsStore::SetupADBlobRootKeyInformation( HKEY DfsKey, LPWSTR DfsLogicalShare, LPWSTR DfsPhysicalShare ) { DWORD Status = ERROR_SUCCESS; HKEY FtDfsShareKey = NULL; size_t PhysicalShareCchLength = 0; size_t LogicalShareCchLength = 0; Status = RegCreateKeyEx( DfsKey, DfsLogicalShare, 0, L"", REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &FtDfsShareKey, NULL ); // // Now set the values for this root key, so that we know // the DN for the root, and the physical share on the machine // for the root, etc. // if (Status == ERROR_SUCCESS) { Status = DfsStringCchLength( DfsPhysicalShare, MAXUSHORT, &PhysicalShareCchLength ); if (Status == ERROR_SUCCESS) { PhysicalShareCchLength++; // NULL Terminator Status = RegSetValueEx( FtDfsShareKey, DfsRootShareValueName, 0, REG_SZ, (PBYTE)DfsPhysicalShare, PhysicalShareCchLength * sizeof(WCHAR) ); } if (Status == ERROR_SUCCESS) { Status = DfsStringCchLength( DfsLogicalShare, MAXUSHORT, &LogicalShareCchLength ); if (Status == ERROR_SUCCESS) { LogicalShareCchLength++; // NULL Terminator Status = RegSetValueEx( FtDfsShareKey, DfsLogicalShareValueName, 0, REG_SZ, (PBYTE)DfsLogicalShare, LogicalShareCchLength * sizeof(WCHAR) ); } } RegCloseKey( FtDfsShareKey ); } return Status; } DFSSTATUS DfsStore::GetMetadataNameInformation( IN DFS_METADATA_HANDLE RootHandle, IN LPWSTR MetadataName, OUT PDFS_NAME_INFORMATION *ppInfo ) { PVOID pBlob = NULL; PVOID pUseBlob = NULL; ULONG BlobSize = 0; ULONG UseBlobSize = 0; PDFS_NAME_INFORMATION pNewInfo = NULL; DFSSTATUS Status = ERROR_SUCCESS; FILETIME BlobModifiedTime; Status = GetMetadataNameBlob( RootHandle, MetadataName, &pBlob, &BlobSize, &BlobModifiedTime ); if (Status == ERROR_SUCCESS) { pNewInfo = new DFS_NAME_INFORMATION; if (pNewInfo != NULL) { RtlZeroMemory (pNewInfo, sizeof(DFS_NAME_INFORMATION)); pUseBlob = pBlob; UseBlobSize = BlobSize; Status = PackGetNameInformation( pNewInfo, &pUseBlob, &UseBlobSize ); } else { Status = ERROR_NOT_ENOUGH_MEMORY; } if (Status != ERROR_SUCCESS) { ReleaseMetadataNameBlob( pBlob, BlobSize ); } } if (Status == ERROR_SUCCESS) { pNewInfo->pData = pBlob; pNewInfo->DataSize = BlobSize; *ppInfo = pNewInfo; } return Status; } VOID DfsStore::ReleaseMetadataNameInformation( IN DFS_METADATA_HANDLE DfsHandle, IN PDFS_NAME_INFORMATION pNameInfo ) { UNREFERENCED_PARAMETER(DfsHandle); ReleaseMetadataNameBlob( pNameInfo->pData, pNameInfo->DataSize ); delete [] pNameInfo; } DFSSTATUS DfsStore::SetMetadataNameInformation( IN DFS_METADATA_HANDLE RootHandle, IN LPWSTR MetadataName, IN PDFS_NAME_INFORMATION pNameInfo ) { PVOID pBlob = NULL; ULONG BlobSize = 0; DFSSTATUS Status = ERROR_SUCCESS; SYSTEMTIME CurrentTime; FILETIME ModifiedTime; GetSystemTime( &CurrentTime); if (SystemTimeToFileTime( &CurrentTime, &ModifiedTime )) { pNameInfo->LastModifiedTime = ModifiedTime; } Status = CreateNameInformationBlob( pNameInfo, &pBlob, &BlobSize ); if (Status == ERROR_SUCCESS) { Status = SetMetadataNameBlob( RootHandle, MetadataName, pBlob, BlobSize ); ReleaseMetadataNameBlob( pBlob, BlobSize ); } return Status; } DFSSTATUS DfsStore::GetMetadataReplicaInformation( IN DFS_METADATA_HANDLE RootHandle, IN LPWSTR MetadataName, OUT PDFS_REPLICA_LIST_INFORMATION *ppInfo ) { PVOID pBlob = NULL; PVOID pUseBlob = NULL; ULONG BlobSize =0; ULONG UseBlobSize = 0; PDFS_REPLICA_LIST_INFORMATION pNewInfo = NULL; DFSSTATUS Status = ERROR_SUCCESS; Status = GetMetadataReplicaBlob( RootHandle, MetadataName, &pBlob, &BlobSize, NULL ); if (Status == ERROR_SUCCESS) { pNewInfo = new DFS_REPLICA_LIST_INFORMATION; if (pNewInfo != NULL) { RtlZeroMemory (pNewInfo, sizeof(DFS_REPLICA_LIST_INFORMATION)); pUseBlob = pBlob; UseBlobSize = BlobSize; Status = PackGetULong( &pNewInfo->ReplicaCount, &pUseBlob, &UseBlobSize ); if (Status == ERROR_SUCCESS) { pNewInfo->pReplicas = new DFS_REPLICA_INFORMATION[ pNewInfo->ReplicaCount]; if ( pNewInfo->pReplicas != NULL ) { Status = PackGetReplicaInformation(pNewInfo, &pUseBlob, &UseBlobSize ); } else { Status = ERROR_NOT_ENOUGH_MEMORY; } } if (Status != ERROR_SUCCESS) { if(pNewInfo->pReplicas != NULL) { delete [] pNewInfo->pReplicas; } delete pNewInfo; } } else { Status = ERROR_NOT_ENOUGH_MEMORY; } if (Status != ERROR_SUCCESS) { ReleaseMetadataReplicaBlob( pBlob, BlobSize ); } } if (Status == ERROR_SUCCESS) { pNewInfo->pData = pBlob; pNewInfo->DataSize = BlobSize; *ppInfo = pNewInfo; } return Status; } VOID DfsStore::ReleaseMetadataReplicaInformation( IN DFS_METADATA_HANDLE DfsHandle, IN PDFS_REPLICA_LIST_INFORMATION pReplicaListInfo ) { UNREFERENCED_PARAMETER(DfsHandle); ReleaseMetadataReplicaBlob( pReplicaListInfo->pData, pReplicaListInfo->DataSize ); delete [] pReplicaListInfo->pReplicas; delete [] pReplicaListInfo; } DFSSTATUS DfsStore::SetMetadataReplicaInformation( IN DFS_METADATA_HANDLE RootHandle, IN LPWSTR MetadataName, IN PDFS_REPLICA_LIST_INFORMATION pReplicaListInfo ) { PVOID pBlob = NULL; ULONG BlobSize = 0; DFSSTATUS Status = ERROR_SUCCESS; Status = CreateReplicaListInformationBlob( pReplicaListInfo, &pBlob, &BlobSize ); if (Status == ERROR_SUCCESS) { Status = SetMetadataReplicaBlob( RootHandle, MetadataName, pBlob, BlobSize ); ReleaseMetadataReplicaBlob( pBlob, BlobSize ); } return Status; } DFSSTATUS DfsStore::CreateNameInformationBlob( IN PDFS_NAME_INFORMATION pDfsNameInfo, OUT PVOID *ppBlob, OUT PULONG pDataSize ) { PVOID pBlob = NULL; PVOID pUseBlob = NULL; ULONG BlobSize = 0; ULONG UseBlobSize = 0; DFSSTATUS Status = ERROR_SUCCESS; BlobSize = PackSizeNameInformation( pDfsNameInfo ); Status = AllocateMetadataNameBlob( &pBlob, BlobSize ); if ( Status == ERROR_SUCCESS ) { pUseBlob = pBlob; UseBlobSize = BlobSize; // Pack the name information into the binary stream allocated. // Status = PackSetNameInformation( pDfsNameInfo, &pUseBlob, &UseBlobSize ); if (Status != ERROR_SUCCESS) { ReleaseMetadataNameBlob( pBlob, BlobSize ); } } if (Status == ERROR_SUCCESS) { *ppBlob = pBlob; *pDataSize = BlobSize - UseBlobSize; } return Status; } DFSSTATUS DfsStore::CreateReplicaListInformationBlob( IN PDFS_REPLICA_LIST_INFORMATION pReplicaListInfo, OUT PVOID *ppBlob, OUT PULONG pDataSize ) { PVOID pBlob = NULL; PVOID pUseBlob = NULL; ULONG BlobSize = 0; ULONG UseBlobSize = 0; DFSSTATUS Status = ERROR_SUCCESS; BlobSize = PackSizeULong(); BlobSize += PackSizeReplicaInformation( pReplicaListInfo ); BlobSize += PackSizeULong(); Status = AllocateMetadataReplicaBlob( &pBlob, BlobSize ); if ( Status == ERROR_SUCCESS ) { pUseBlob = pBlob; UseBlobSize = BlobSize; // // The first item in the stream is the number of replicas // Status = PackSetULong(pReplicaListInfo->ReplicaCount, &pUseBlob, &UseBlobSize ); if (Status == ERROR_SUCCESS) { // // We than pack the array of replicas into the binary stream // Status = PackSetReplicaInformation( pReplicaListInfo, &pUseBlob, &UseBlobSize ); } if (Status == ERROR_SUCCESS) { // // We than pack the last word with a 0 so that all the // old crap still works. // Status = PackSetULong( 0, &pUseBlob, &UseBlobSize ); } if (Status != ERROR_SUCCESS) { ReleaseMetadataReplicaBlob( pBlob, BlobSize ); } } if (Status == ERROR_SUCCESS) { *ppBlob = pBlob; *pDataSize = BlobSize - UseBlobSize; } return Status; } DFSSTATUS DfsStore::AddChildReplica ( IN DFS_METADATA_HANDLE DfsHandle, LPWSTR LinkMetadataName, LPWSTR ServerName, LPWSTR SharePath ) { PDFS_REPLICA_LIST_INFORMATION pReplicaList = NULL; PDFS_REPLICA_INFORMATION pReplicaInfo = NULL; ULONG ReplicaIndex = 0; DFSSTATUS Status = ERROR_SUCCESS; WCHAR * NameToCheck = NULL; DFS_REPLICA_LIST_INFORMATION NewList; UNICODE_STRING DfsSharePath; UNICODE_STRING DfsServerName; DFS_TRACE_HIGH(API, "AddChildReplica for path %ws, Server=%ws Share=%ws \n", LinkMetadataName, ServerName, SharePath); if ( (ServerName == NULL) || (SharePath == NULL) ) { return ERROR_INVALID_PARAMETER; } Status = DfsRtlInitUnicodeStringEx( &DfsSharePath, SharePath ); if(Status != ERROR_SUCCESS) { return Status; } Status = DfsRtlInitUnicodeStringEx( &DfsServerName, ServerName ); if(Status != ERROR_SUCCESS) { return Status; } Status = GetMetadataReplicaInformation( DfsHandle, LinkMetadataName, &pReplicaList ); if (Status == ERROR_SUCCESS) { ReplicaIndex = FindReplicaInReplicaList( pReplicaList, ServerName, SharePath ); if (ReplicaIndex < pReplicaList->ReplicaCount) { Status = ERROR_FILE_EXISTS; } else if(pReplicaList->ReplicaCount == 1) { pReplicaInfo = &pReplicaList->pReplicas[0]; NameToCheck = new WCHAR[pReplicaInfo->ServerName.Length/sizeof(WCHAR) + 1]; if(NameToCheck != NULL) { RtlCopyMemory(NameToCheck, pReplicaInfo->ServerName.Buffer, pReplicaInfo->ServerName.Length); NameToCheck[pReplicaInfo->ServerName.Length / sizeof(WCHAR)] = UNICODE_NULL; //if this is a domain name, fail the request with not supported. //else continue on. Status = I_NetDfsIsThisADomainName(NameToCheck); if(Status == ERROR_SUCCESS) { Status = ERROR_NOT_SUPPORTED; } else { Status = ERROR_SUCCESS; } delete [] NameToCheck; } else { Status = ERROR_NOT_ENOUGH_MEMORY; } } if (Status == ERROR_SUCCESS) { RtlZeroMemory( &NewList, sizeof(DFS_REPLICA_LIST_INFORMATION)); NewList.ReplicaCount = pReplicaList->ReplicaCount + 1; NewList.pReplicas = new DFS_REPLICA_INFORMATION [ NewList.ReplicaCount ]; if (NewList.pReplicas == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; } else { if (pReplicaList->ReplicaCount) { RtlCopyMemory( &NewList.pReplicas[0], &pReplicaList->pReplicas[0], pReplicaList->ReplicaCount * sizeof(DFS_REPLICA_INFORMATION) ); } pReplicaInfo = &NewList.pReplicas[pReplicaList->ReplicaCount]; RtlZeroMemory( pReplicaInfo, sizeof(DFS_REPLICA_INFORMATION) ); DfsRtlInitUnicodeStringEx(&pReplicaInfo->ServerName, DfsServerName.Buffer); DfsRtlInitUnicodeStringEx(&pReplicaInfo->ShareName, DfsSharePath.Buffer); pReplicaInfo->ReplicaState = DFS_STORAGE_STATE_ONLINE; pReplicaInfo->ReplicaType = 2; // hack fro backwards compat. Status = SetMetadataReplicaInformation( DfsHandle, LinkMetadataName, &NewList ); delete [] NewList.pReplicas; } } ReleaseMetadataReplicaInformation( DfsHandle, pReplicaList ); } DFS_TRACE_HIGH(API, "AddChildReplica for path %ws, Server=%ws Share=%ws, Status=%d\n", LinkMetadataName, ServerName, SharePath, Status); return Status; } DFSSTATUS DfsStore::RemoveChildReplica ( IN DFS_METADATA_HANDLE DfsHandle, LPWSTR LinkMetadataName, LPWSTR ServerName, LPWSTR SharePath, PBOOLEAN pLastReplica, BOOLEAN Force) { PDFS_REPLICA_LIST_INFORMATION pReplicaList = NULL; ULONG DeleteIndex = 0; ULONG NextIndex = 0; DFSSTATUS Status = ERROR_SUCCESS; DFS_REPLICA_LIST_INFORMATION NewList; DFS_TRACE_HIGH(API, "RemoveChildReplica for path %ws, Server=%ws Share=%ws \n", LinkMetadataName, ServerName, SharePath); *pLastReplica = FALSE; RtlZeroMemory( &NewList, sizeof(DFS_REPLICA_LIST_INFORMATION)); if ( (ServerName == NULL) || (SharePath == NULL) ) { return ERROR_INVALID_PARAMETER; } Status = GetMetadataReplicaInformation( DfsHandle, LinkMetadataName, &pReplicaList ); if (Status == ERROR_SUCCESS) { DeleteIndex = FindReplicaInReplicaList( pReplicaList, ServerName, SharePath ); DFS_TRACE_HIGH(API, "DfsRemove for path %ws, Server=%ws Share=%ws, DeleteIndex=%d,pReplicaList->ReplicaCount=%d \n", LinkMetadataName, ServerName, SharePath, DeleteIndex, pReplicaList->ReplicaCount); if (DeleteIndex < pReplicaList->ReplicaCount) { NewList.ReplicaCount = pReplicaList->ReplicaCount - 1; if (NewList.ReplicaCount) { NewList.pReplicas = new DFS_REPLICA_INFORMATION [NewList.ReplicaCount]; if (NewList.pReplicas != NULL) { if (DeleteIndex) { RtlCopyMemory( &NewList.pReplicas[0], &pReplicaList->pReplicas[0], DeleteIndex * sizeof(DFS_REPLICA_INFORMATION) ); } NextIndex = DeleteIndex + 1; if ( NextIndex < pReplicaList->ReplicaCount) { RtlCopyMemory( &NewList.pReplicas[DeleteIndex], &pReplicaList->pReplicas[NextIndex], (pReplicaList->ReplicaCount - NextIndex) * sizeof(DFS_REPLICA_INFORMATION) ); } } else { Status = ERROR_NOT_ENOUGH_MEMORY; } } //if this is the last target, do not remove it. Return ERROR_LAST_ADMIN //to the upper code, so that he can remove the last target and link in one //atomic operation. if (Status == ERROR_SUCCESS) { if(NewList.ReplicaCount == 0) { *pLastReplica = TRUE; } if( (Force == FALSE) && (NewList.ReplicaCount == 0)) { Status = ERROR_LAST_ADMIN; } else { Status = SetMetadataReplicaInformation( DfsHandle, LinkMetadataName, &NewList ); } } if (NewList.pReplicas != NULL) { delete [] NewList.pReplicas; } } else { Status = ERROR_FILE_NOT_FOUND; } ReleaseMetadataReplicaInformation( DfsHandle, pReplicaList ); } DFS_TRACE_HIGH(API, "DfsRemove for path %ws, Server=%ws Share=%ws, LastReplica=%d, Status=%d\n", LinkMetadataName, ServerName, SharePath, *pLastReplica, Status); return Status; } DFSSTATUS DfsStore::GetStoreApiInformation( IN DFS_METADATA_HANDLE DfsHandle, PUNICODE_STRING pRootName, LPWSTR LinkMetadataName, DWORD Level, LPBYTE pBuffer, LONG BufferSize, PLONG pSizeRequired ) { DFSSTATUS Status = ERROR_SUCCESS; PDFS_NAME_INFORMATION pNameInformation = NULL; PDFS_REPLICA_LIST_INFORMATION pReplicaList = NULL; PDFS_REPLICA_INFORMATION pReplica = NULL; PDFS_API_INFO pInfo = (PDFS_API_INFO)pBuffer; PDFS_STORAGE_INFO pStorage = NULL; LONG HeaderSize = 0; LONG AdditionalSize = 0; ULONG_PTR NextFreeMemory = NULL; ULONG i = 0; UNICODE_STRING UsePrefixName; DFS_API_INFO LocalInfo; RtlZeroMemory(&LocalInfo, sizeof(DFS_API_INFO)); Status = GetMetadataNameInformation( DfsHandle, LinkMetadataName, &pNameInformation ); if (Status == ERROR_SUCCESS) { Status = GenerateApiLogicalPath( pRootName, &pNameInformation->Prefix, &UsePrefixName ); if (Status != ERROR_SUCCESS) { ReleaseMetadataNameInformation( DfsHandle, pNameInformation ); } } if (Status == ERROR_SUCCESS) { Status = GetMetadataReplicaInformation( DfsHandle, LinkMetadataName, &pReplicaList ); if (Status == ERROR_SUCCESS) { switch (Level) { case 100: if (HeaderSize == 0) { HeaderSize = sizeof(DFS_INFO_100); NextFreeMemory = (ULONG_PTR)(&pInfo->Info100 + 1); } AdditionalSize += pNameInformation->Comment.Length + sizeof(WCHAR); break; case 4: if (HeaderSize == 0) { HeaderSize = sizeof(DFS_INFO_4); LocalInfo.Info4.Timeout = pNameInformation->Timeout; LocalInfo.Info4.State = pNameInformation->State; LocalInfo.Info4.NumberOfStorages = pReplicaList->ReplicaCount; pStorage = LocalInfo.Info4.Storage = (PDFS_STORAGE_INFO)(&pInfo->Info4 + 1); NextFreeMemory = (ULONG_PTR)(LocalInfo.Info4.Storage + LocalInfo.Info4.NumberOfStorages); } case 3: if (HeaderSize == 0) { HeaderSize = sizeof(DFS_INFO_3); LocalInfo.Info3.State = pNameInformation->State; LocalInfo.Info3.NumberOfStorages = pReplicaList->ReplicaCount; pStorage = LocalInfo.Info3.Storage = (PDFS_STORAGE_INFO)(&pInfo->Info3 + 1); NextFreeMemory = (ULONG_PTR)(LocalInfo.Info3.Storage + LocalInfo.Info3.NumberOfStorages); } for (i = 0; i < pReplicaList->ReplicaCount; i++) { UNICODE_STRING ServerName = pReplicaList->pReplicas[i].ServerName; if (IsLocalName(&ServerName)) { ServerName = *pRootName; } AdditionalSize += ( sizeof(DFS_STORAGE_INFO) + ServerName.Length + sizeof(WCHAR) + pReplicaList->pReplicas[i].ShareName.Length + sizeof(WCHAR) ); } case 2: if (HeaderSize == 0) { HeaderSize = sizeof(DFS_INFO_2); LocalInfo.Info2.State = pNameInformation->State; LocalInfo.Info2.NumberOfStorages = pReplicaList->ReplicaCount; NextFreeMemory = (ULONG_PTR)(&pInfo->Info2 + 1); } AdditionalSize += pNameInformation->Comment.Length + sizeof(WCHAR); case 1: if (HeaderSize == 0) { HeaderSize = sizeof(DFS_INFO_1); NextFreeMemory = (ULONG_PTR)(&pInfo->Info1 + 1); } AdditionalSize += UsePrefixName.Length + sizeof(WCHAR); break; default: Status = ERROR_INVALID_PARAMETER; break; } *pSizeRequired = HeaderSize + AdditionalSize; if (*pSizeRequired > BufferSize) { Status = ERROR_BUFFER_OVERFLOW; } if (Status == ERROR_SUCCESS) { RtlZeroMemory( pBuffer, *pSizeRequired); RtlCopyMemory( &pInfo->Info4, &LocalInfo.Info4, HeaderSize ); switch (Level) { // // Extract just the comment for this root/link. // case 100: pInfo->Info100.Comment = (LPWSTR)NextFreeMemory; NextFreeMemory += pNameInformation->Comment.Length + sizeof(WCHAR); RtlCopyMemory( pInfo->Info100.Comment, pNameInformation->Comment.Buffer, pNameInformation->Comment.Length ); break; case 4: case 3: for (i = 0; i < pReplicaList->ReplicaCount; i++) { UNICODE_STRING ServerName = pReplicaList->pReplicas[i].ServerName; if (IsLocalName(&ServerName)) { ServerName = *pRootName; } pReplica = &pReplicaList->pReplicas[i]; pStorage[i].State = pReplica->ReplicaState; pStorage[i].ServerName = (LPWSTR)NextFreeMemory; NextFreeMemory += ServerName.Length + sizeof(WCHAR); pStorage[i].ShareName = (LPWSTR)NextFreeMemory; NextFreeMemory += pReplica->ShareName.Length + sizeof(WCHAR); RtlCopyMemory( pStorage[i].ServerName, ServerName.Buffer, ServerName.Length ); RtlCopyMemory( pStorage[i].ShareName, pReplica->ShareName.Buffer, pReplica->ShareName.Length ); } case 2: pInfo->Info2.Comment = (LPWSTR)NextFreeMemory; NextFreeMemory += pNameInformation->Comment.Length + sizeof(WCHAR); RtlCopyMemory( pInfo->Info2.Comment, pNameInformation->Comment.Buffer, pNameInformation->Comment.Length ); case 1: pInfo->Info1.EntryPath = (LPWSTR)NextFreeMemory; RtlCopyMemory( pInfo->Info1.EntryPath, UsePrefixName.Buffer, UsePrefixName.Length ); pInfo->Info1.EntryPath[UsePrefixName.Length/sizeof(WCHAR)] = UNICODE_NULL; break; } } ReleaseMetadataReplicaInformation( DfsHandle, pReplicaList ); } ReleaseApiLogicalPath( &UsePrefixName ); ReleaseMetadataNameInformation( DfsHandle, pNameInformation ); } return Status; } DFSSTATUS DfsStore::GetExtendedAttributes( IN DFS_METADATA_HANDLE DfsHandle, LPWSTR LinkMetadataName, PULONG pAttr) { DFSSTATUS Status = ERROR_SUCCESS; PDFS_NAME_INFORMATION pNameInformation = NULL; Status = GetMetadataNameInformation( DfsHandle, LinkMetadataName, &pNameInformation ); if (Status != ERROR_SUCCESS) { return Status; } *pAttr = (pNameInformation->Type & PKT_ENTRY_TYPE_EXTENDED_ATTRIBUTES); ReleaseMetadataNameInformation( DfsHandle, pNameInformation ); return Status; } DFSSTATUS DfsStore::SetExtendedAttributes( IN DFS_METADATA_HANDLE DfsHandle, LPWSTR LinkMetadataName, ULONG Attr) { DFSSTATUS Status = ERROR_SUCCESS; PDFS_NAME_INFORMATION pNameInformation = NULL; ULONG UseAttr; UseAttr = Attr & PKT_ENTRY_TYPE_EXTENDED_ATTRIBUTES; if (UseAttr != Attr) { return ERROR_INVALID_PARAMETER; } Status = GetMetadataNameInformation( DfsHandle, LinkMetadataName, &pNameInformation ); if (Status != ERROR_SUCCESS) { return Status; } pNameInformation->Type &= ~PKT_ENTRY_TYPE_EXTENDED_ATTRIBUTES; pNameInformation->Type |= UseAttr; Status = SetMetadataNameInformation( DfsHandle, LinkMetadataName, pNameInformation ); ReleaseMetadataNameInformation( DfsHandle, pNameInformation ); return Status; } DFSSTATUS DfsStore::SetStoreApiInformation( IN DFS_METADATA_HANDLE DfsHandle, LPWSTR LinkMetadataName, LPWSTR ServerName, LPWSTR SharePath, DWORD Level, LPBYTE pBuffer ) { DFSSTATUS Status = ERROR_SUCCESS; PDFS_NAME_INFORMATION pNameInformation = NULL; BOOLEAN TypeInfoSet = FALSE; // // The set is a little strange: the pBuffer is pointing to a pointer // that we are interested in. Grab it directly. // dfsdev: this is confusing, fix it or document in detail. // PDFS_API_INFO pApiInfo = (PDFS_API_INFO)(*(PULONG_PTR)pBuffer); if(pApiInfo == NULL) { return ERROR_INVALID_PARAMETER; } // // dfsdev: need to do some api work before enabling code // below. // #if 0 if ( (ServerName != NULL) || (SharePath != NULL) ) { if (Level != 101) { return ERROR_INVALID_PARAMETER; } } #endif Status = GetMetadataNameInformation( DfsHandle, LinkMetadataName, &pNameInformation ); if (Status != ERROR_SUCCESS) { return Status; } switch (Level) { case 100: Status = DfsRtlInitUnicodeStringEx( &pNameInformation->Comment, pApiInfo->Info100.Comment ); if(Status == ERROR_SUCCESS) { Status = SetMetadataNameInformation( DfsHandle, LinkMetadataName, pNameInformation ); } break; case 102: pNameInformation->Timeout = pApiInfo->Info102.Timeout; Status = SetMetadataNameInformation( DfsHandle, LinkMetadataName, pNameInformation ); break; case 101: if ((ServerName == NULL) && (SharePath == NULL)) { pNameInformation->State = pApiInfo->Info101.State; Status = SetMetadataNameInformation( DfsHandle, LinkMetadataName, pNameInformation ); } else { PDFS_REPLICA_LIST_INFORMATION pReplicaList; ULONG ReplicaIndex; Status = GetMetadataReplicaInformation (DfsHandle, LinkMetadataName, &pReplicaList ); if (Status == ERROR_SUCCESS) { ReplicaIndex = FindReplicaInReplicaList( pReplicaList, ServerName, SharePath ); if (ReplicaIndex >= pReplicaList->ReplicaCount) { Status = ERROR_NOT_FOUND; } else { DFS_REPLICA_LIST_INFORMATION NewList; RtlZeroMemory( &NewList, sizeof( DFS_REPLICA_LIST_INFORMATION) ); NewList.ReplicaCount = pReplicaList->ReplicaCount; NewList.pReplicas = new DFS_REPLICA_INFORMATION [ NewList.ReplicaCount ]; if (NewList.pReplicas != NULL) { RtlCopyMemory( &NewList.pReplicas[0], &pReplicaList->pReplicas[0], NewList.ReplicaCount * sizeof(DFS_REPLICA_INFORMATION) ); NewList.pReplicas[ReplicaIndex].ReplicaState = pApiInfo->Info101.State; NewList.pReplicas[ReplicaIndex].ReplicaType = 2; // hack fro backwards compat. Status = SetMetadataReplicaInformation (DfsHandle, LinkMetadataName, &NewList ); delete [] NewList.pReplicas; } else { Status = ERROR_NOT_ENOUGH_MEMORY; } } ReleaseMetadataReplicaInformation( DfsHandle, pReplicaList ); } } break; default: Status = ERROR_INVALID_PARAMETER; break; } ReleaseMetadataNameInformation( DfsHandle, pNameInformation ); return Status; } DFSSTATUS DfsStore::GetStoreApiInformationBuffer( IN DFS_METADATA_HANDLE DfsHandle, PUNICODE_STRING pRootName, LPWSTR LinkMetadataName, DWORD Level, LPBYTE *ppBuffer, PLONG pBufferSize ) { DFSSTATUS Status = ERROR_SUCCESS; LONG RequiredSize = 0; LONG BufferSize = 4096; LPBYTE pBuffer = new BYTE [ BufferSize ]; if (pBuffer == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } Status = GetStoreApiInformation( DfsHandle, pRootName, LinkMetadataName, Level, pBuffer, BufferSize, &RequiredSize ); if (Status == ERROR_BUFFER_OVERFLOW) { delete [] pBuffer; BufferSize = RequiredSize; pBuffer = new BYTE[ BufferSize ]; if (pBuffer == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; } else { Status = GetStoreApiInformation( DfsHandle, pRootName, LinkMetadataName, Level, pBuffer, BufferSize, &RequiredSize ); } } if (Status == ERROR_SUCCESS) { *ppBuffer = pBuffer; *pBufferSize = RequiredSize; } else { if(pBuffer != NULL) { delete [] pBuffer; } } return Status; } DFSSTATUS DfsStore::FindNextRoot( ULONG RootNum, DfsRootFolder **ppRootFolder ) { DFSSTATUS Status = ERROR_SUCCESS; DfsRootFolder *pRoot = NULL; ULONG Start = 0; DFS_TRACE_LOW( REFERRAL_SERVER, "Store %p, Find next root %d\n", this, RootNum ); // // Lock the store, so that we dont have new roots coming in while // we are taking a look. // Status = AcquireReadLock(); if ( Status != ERROR_SUCCESS ) { return Status; } // // The default return status is ERROR_NOT_FOUND; // Status = ERROR_NOT_FOUND; // // Run through our list of DFS roots, and see if any of them match // the passed in name context and logical share. // pRoot = _DfsRootList; Start = 0; if (pRoot != NULL) { do { if (Start++ == RootNum) { Status = ERROR_SUCCESS; break; } pRoot = pRoot->pNextRoot; } while ( pRoot != _DfsRootList ); } // // IF we found a matching root, bump up its reference count, and // return the pointer to the root. // if ( Status == ERROR_SUCCESS ) { pRoot->AcquireReference(); *ppRootFolder = pRoot; } ReleaseLock(); DFS_TRACE_ERROR_HIGH( Status, REFERRAL_SERVER, "Done find next %d, root %p status %x\n", RootNum, pRoot, Status); return Status; } // // the store syncrhonizer: syncrhonizes roots that we already know of. // Note that we could be racing with a delete: in the worst case we will // resync the same root more than once. // VOID DfsStore::StoreSynchronizer() { ULONG RootNum = 0; DFSSTATUS Status = ERROR_SUCCESS; DfsRootFolder *pRoot = NULL; while (Status != ERROR_NOT_FOUND) { Status = FindNextRoot(RootNum++, &pRoot); if (Status == ERROR_SUCCESS) { Status = pRoot->Synchronize(); DFS_TRACE_ERROR_HIGH( Status, REFERRAL_SERVER, "Root %p Synchronize Status %x\n", this, Status); pRoot->ReleaseReference(); } } return NOTHING; } DFSSTATUS DfsStore::LoadServerSiteDataPerRoot(void) { DFSSTATUS Status = ERROR_SUCCESS; DfsRootFolder *pRoot = NULL; Status = AcquireReadLock(); if ( Status != ERROR_SUCCESS ) { return Status; } pRoot = _DfsRootList; if(pRoot != NULL) { do { pRoot->PreloadServerSiteData(); pRoot = pRoot->pNextRoot; } while ( pRoot != _DfsRootList ); } ReleaseLock(); return Status; }