//+---------------------------------------------------------------------------- // // Copyright (C) 1992, Microsoft Corporation. // // File: PKT.C // // Contents: This module implements the Partition Knowledge Table routines // for the Dfs driver. // // Functions: PktInitialize - // PktInitializeLocalPartition - // RemoveLastComponent - // PktCreateEntry - // PktCreateSubordinateEntry - // PktLookupEntryById - // PktEntryModifyPrefix - // PktLookupEntryByPrefix - // PktLookupEntryByUid - // PktLookupReferralEntry - // PktTrimSubordinates - // PktpRecoverLocalPartition - // PktpValidateLocalPartition - // PktCreateEntryFromReferral - // PktpAddEntry - // // History: 5 May 1992 PeterCo Created. // //----------------------------------------------------------------------------- #include "dfsprocs.h" #include #include #include "dnr.h" #include "log.h" #include "know.h" #define Dbg (DEBUG_TRACE_PKT) // // Local procedure prototypes // NTSTATUS PktpCheckReferralSyntax( IN PUNICODE_STRING ReferralPath, IN PRESP_GET_DFS_REFERRAL ReferralBuffer, IN DWORD ReferralSize); NTSTATUS PktpCheckReferralString( IN LPWSTR String, IN PCHAR ReferralBuffer, IN PCHAR ReferralBufferEnd); NTSTATUS PktpCheckReferralNetworkAddress( IN PWCHAR Address, IN ULONG MaxLength); NTSTATUS PktpCreateEntryIdFromReferral( IN PRESP_GET_DFS_REFERRAL Ref, IN PUNICODE_STRING ReferralPath, OUT ULONG *MatchingLength, OUT PDFS_PKT_ENTRY_ID Peid); NTSTATUS PktpAddEntry ( IN PDFS_PKT Pkt, IN PDFS_PKT_ENTRY_ID EntryId, IN PRESP_GET_DFS_REFERRAL ReferralBuffer, IN ULONG CreateDisposition, OUT PDFS_PKT_ENTRY *ppPktEntry); PDS_MACHINE PktpGetDSMachine( IN PUNICODE_STRING ServerName); VOID PktShuffleServiceList( PDFS_PKT_ENTRY_INFO pInfo); VOID PktShuffleGroup( PDFS_PKT_ENTRY_INFO pInfo, ULONG nStart, ULONG nEnd); #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, PktInitialize ) #pragma alloc_text( PAGE, RemoveLastComponent ) #pragma alloc_text( PAGE, PktCreateEntry ) #pragma alloc_text( PAGE, PktCreateDomainEntry ) #pragma alloc_text( PAGE, PktCreateMachineEntry ) #pragma alloc_text( PAGE, PktEntryModifyPrefix ) #pragma alloc_text( PAGE, PktLookupEntryByPrefix ) #pragma alloc_text( PAGE, PktLookupEntryByUid ) #pragma alloc_text( PAGE, PktLookupReferralEntry ) #pragma alloc_text( PAGE, PktCreateEntryFromReferral ) #pragma alloc_text( PAGE, PktpCheckReferralSyntax ) #pragma alloc_text( PAGE, PktpCheckReferralString ) #pragma alloc_text( PAGE, PktpCheckReferralNetworkAddress ) #pragma alloc_text( PAGE, PktpCreateEntryIdFromReferral ) #pragma alloc_text( PAGE, PktpAddEntry ) #pragma alloc_text( PAGE, PktpGetDSMachine ) #pragma alloc_text( PAGE, PktShuffleServiceList ) #pragma alloc_text( PAGE, PktShuffleGroup ) #endif // ALLOC_PRAGMA // // declare the global null guid // GUID _TheNullGuid; //+------------------------------------------------------------------------- // // Function: PktInitialize, public // // Synopsis: PktInitialize initializes the partition knowledge table. // // Arguments: [Pkt] - pointer to an uninitialized PKT // // Returns: NTSTATUS - STATUS_SUCCESS if no error. // // Notes: This routine is called only at driver init time. // //-------------------------------------------------------------------------- NTSTATUS PktInitialize( IN PDFS_PKT Pkt ) { DfsDbgTrace(+1, Dbg, "PktInitialize: Entered\n", 0); // // initialize the NULL GUID. // RtlZeroMemory(&_TheNullGuid, sizeof(GUID)); // // Always zero the pkt first // RtlZeroMemory(Pkt, sizeof(DFS_PKT)); // // do basic initialization // Pkt->NodeTypeCode = DSFS_NTC_PKT; Pkt->NodeByteSize = sizeof(DFS_PKT); ExInitializeResource(&Pkt->Resource); InitializeListHead(&Pkt->EntryList); DfsInitializeUnicodePrefix(&Pkt->PrefixTable); DfsInitializeUnicodePrefix(&Pkt->ShortPrefixTable); RtlInitializeUnicodePrefix(&Pkt->DSMachineTable); Pkt->EntryTimeToLive = MAX_REFERRAL_LIFE_TIME; DfsDbgTrace(-1, Dbg, "PktInitialize: Exit -> VOID\n", 0 ); return STATUS_SUCCESS; } //+------------------------------------------------------------------------- // // Function: RemoveLastComponent, public // // Synopsis: Removes the last component of the string passed. // // Arguments: [Prefix] -- The prefix whose last component is to be returned. // [newPrefix] -- The new Prefix with the last component removed. // // Returns: NTSTATUS - STATUS_SUCCESS if no error. // // Notes: On return, the newPrefix points to the same memory buffer // as Prefix. // //-------------------------------------------------------------------------- void RemoveLastComponent( PUNICODE_STRING Prefix, PUNICODE_STRING newPrefix ) { PWCHAR pwch; USHORT i=0; *newPrefix = *Prefix; pwch = newPrefix->Buffer; pwch += (Prefix->Length/sizeof(WCHAR)) - 1; while ((*pwch != UNICODE_PATH_SEP) && (pwch != newPrefix->Buffer)) { i += sizeof(WCHAR); pwch--; } newPrefix->Length = newPrefix->Length - i; } //+------------------------------------------------------------------------- // // Function: PktCreateEntry, public // // Synopsis: PktCreateEntry creates a new partition table entry or // updates an existing one. The PKT must be acquired // exclusively for this operation. // // Arguments: [Pkt] - pointer to an initialized (and exclusively acquired) PKT // [PktEntryType] - the type of entry to create/update. // [PktEntryId] - pointer to the Id of the entry to create // [PktEntryInfo] - pointer to the guts of the entry // [CreateDisposition] - specifies whether to overwrite if // an entry already exists, etc. // [ppPktEntry] - the new entry is placed here. // // Returns: [STATUS_SUCCESS] - if all is well. // // [DFS_STATUS_NO_SUCH_ENTRY] - the create disposition was // set to PKT_REPLACE_ENTRY and no entry of the specified // Id exists to replace. // // [DFS_STATUS_ENTRY_EXISTS] - a create disposition of // PKT_CREATE_ENTRY was specified and an entry of the // specified Id already exists. // // [DFS_STATUS_LOCAL_ENTRY] - creation of the entry would // required the invalidation of a local entry or exit point. // // [STATUS_INVALID_PARAMETER] - the Id specified for the // new entry is invalid. // // [STATUS_INSUFFICIENT_RESOURCES] - not enough memory was // available to complete the operation. // // Notes: The PktEntryId and PktEntryInfo structures are MOVED (not // COPIED) to the new entry. The memory used for UNICODE_STRINGS // and DFS_SERVICE arrays is used by the new entry. The // associated fields in the PktEntryId and PktEntryInfo // structures passed as arguments are Zero'd to indicate that // the memory has been "deallocated" from these strutures and // reallocated to the newly created PktEntry. Note that this // routine does not deallocate the PktEntryId structure or // the PktEntryInfo structure itself. On successful return from // this function, the PktEntryId structure will be modified // to have a NULL Prefix entry, and the PktEntryInfo structure // will be modified to have zero services and a null ServiceList // entry. // //-------------------------------------------------------------------------- NTSTATUS PktCreateEntry( IN PDFS_PKT Pkt, IN ULONG PktEntryType, IN PDFS_PKT_ENTRY_ID PktEntryId, IN PDFS_PKT_ENTRY_INFO PktEntryInfo OPTIONAL, IN ULONG CreateDisposition, OUT PDFS_PKT_ENTRY *ppPktEntry ) { NTSTATUS status = STATUS_SUCCESS; PDFS_PKT_ENTRY pfxMatchEntry = NULL; PDFS_PKT_ENTRY uidMatchEntry = NULL; PDFS_PKT_ENTRY entryToUpdate = NULL; PDFS_PKT_ENTRY entryToInvalidate = NULL; PDFS_PKT_ENTRY SupEntry = NULL; UNICODE_STRING remainingPath, newRemainingPath; ASSERT(ARGUMENT_PRESENT(Pkt) && ARGUMENT_PRESENT(PktEntryId) && ARGUMENT_PRESENT(ppPktEntry)); DfsDbgTrace(+1, Dbg, "PktCreateEntry: Entered\n", 0); // // We're pessimistic at first... // *ppPktEntry = NULL; // // See if there exists an entry with this prefix. The prefix // must match exactly (i.e. No remaining path). // pfxMatchEntry = PktLookupEntryByPrefix(Pkt, &PktEntryId->Prefix, &remainingPath); if (remainingPath.Length > 0) { SupEntry = pfxMatchEntry; pfxMatchEntry = NULL; } else { UNICODE_STRING newPrefix; RemoveLastComponent(&PktEntryId->Prefix, &newPrefix); SupEntry = PktLookupEntryByPrefix(Pkt, &newPrefix, &newRemainingPath); } // // Now search for an entry that has the same Uid. // uidMatchEntry = PktLookupEntryByUid(Pkt, &PktEntryId->Uid); // // Now we must determine if during this create, we are going to be // updating or invalidating any existing entries. If an existing // entry is found that has the same Uid as the one we are trying to // create, the entry becomes a target for "updating". If the Uid // passed in is NULL, then we check to see if an entry exists that // has a NULL Uid AND a Prefix that matches. If this is the case, // that entry becomes the target for "updating". // // To determine if there is an entry to invalidate, we look for an // entry with the same Prefix as the one we are trying to create, BUT, // which has a different Uid. If we detect such a situation, we // we make the entry with the same Prefix the target for invalidation // (we do not allow two entries with the same Prefix, and we assume // that the new entry takes precedence). // if (uidMatchEntry != NULL) { entryToUpdate = uidMatchEntry; if (pfxMatchEntry != uidMatchEntry) entryToInvalidate = pfxMatchEntry; } else if ((pfxMatchEntry != NULL) && NullGuid(&pfxMatchEntry->Id.Uid)) { // // This should go away once we don't have any NULL guids at all in // the driver. (BUGBUG) // entryToUpdate = pfxMatchEntry; } else { entryToInvalidate = pfxMatchEntry; } // // Now we check to make sure that our create disposition is // consistent with what we are about to do. // if ((CreateDisposition & PKT_ENTRY_CREATE) && entryToUpdate != NULL) { *ppPktEntry = entryToUpdate; status = DFS_STATUS_ENTRY_EXISTS; } else if ((CreateDisposition & PKT_ENTRY_REPLACE) && entryToUpdate==NULL) { status = DFS_STATUS_NO_SUCH_ENTRY; } // // if we have an error here we can get out now! // if (!NT_SUCCESS(status)) { DfsDbgTrace(-1, Dbg, "PktCreateEntry: Exit -> %08lx\n", status ); return status; } // // At this point we must insure that we are not going to // be invalidating any local partition entries. // if ((entryToInvalidate != NULL) && (!(entryToInvalidate->Type & PKT_ENTRY_TYPE_OUTSIDE_MY_DOM) ) && (entryToInvalidate->Type & (PKT_ENTRY_TYPE_LOCAL | PKT_ENTRY_TYPE_LOCAL_XPOINT | PKT_ENTRY_TYPE_PERMANENT))) { DfsDbgTrace(-1, Dbg, "PktCreateEntry: Exit -> %08lx\n", DFS_STATUS_LOCAL_ENTRY ); return DFS_STATUS_LOCAL_ENTRY; } // // We go up the links till we reach a REFERRAL entry type. Actually // we may never go up since we always link to a REFERRAL entry. Anyway // no harm done! // while ((SupEntry != NULL) && !(SupEntry->Type & PKT_ENTRY_TYPE_REFERRAL_SVC)) { SupEntry = SupEntry->ClosestDC; } // // If we had success then we need to see if we have to // invalidate an entry. // if (NT_SUCCESS(status) && entryToInvalidate != NULL) PktEntryDestroy(entryToInvalidate, Pkt, (BOOLEAN)TRUE); // // If we are not updating an entry we must construct a new one // from scratch. Otherwise we need to update. // if (entryToUpdate != NULL) { status = PktEntryReassemble(entryToUpdate, Pkt, PktEntryType, PktEntryId, PktEntryInfo); if (NT_SUCCESS(status)) { (*ppPktEntry) = entryToUpdate; PktEntryLinkChild(SupEntry, entryToUpdate); } } else { // // Now we are going to create a new entry. So we have to set // the ClosestDC Entry pointer while creating this entry. The // ClosestDC entry value is already in SupEntry. // PDFS_PKT_ENTRY newEntry; newEntry = (PDFS_PKT_ENTRY) ExAllocatePool(PagedPool, sizeof(DFS_PKT_ENTRY)); if (newEntry == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; } else { status = PktEntryAssemble(newEntry, Pkt, PktEntryType, PktEntryId, PktEntryInfo); if (!NT_SUCCESS(status)) { ExFreePool(newEntry); } else { (*ppPktEntry) = newEntry; PktEntryLinkChild(SupEntry, newEntry); } } } DfsDbgTrace(-1, Dbg, "PktCreateEntry: Exit -> %08lx\n", status ); return status; } //+---------------------------------------------------------------------------- // // Function: PktCreateDomainEntry // // Synopsis: Given a name that is thought to be a domain name, this routine // will create a Pkt Entry for the root of the domain's Dfs. // The domain must exist, must have a Dfs root, and must be // reachable for this routine to succeed. // // Arguments: [DomainName] -- Name of domain thought to support a Dfs // [ShareName] -- Name of share at the root of the domain Dfs // // Returns: [STATUS_SUCCESS] -- Successfully completed operation. // // [STATUS_INSUFFICIENT_RESOURCES] -- Unable to allocate memory. // // [STATUS_BAD_NETWORK_PATH] -- DomainName is not a trusted // domain. // //----------------------------------------------------------------------------- NTSTATUS PktCreateDomainEntry( IN PUNICODE_STRING DomainName, IN PUNICODE_STRING ShareName) { NTSTATUS status; LPWSTR domainName, shareName; BYTE stackBuffer[64]; PBYTE buffer = stackBuffer; ULONG size; size = sizeof(ULONG) + DomainName->Length + sizeof(UNICODE_NULL) + ShareName->Length + sizeof(UNICODE_NULL); if (size > sizeof(stackBuffer)) buffer = ExAllocatePool( PagedPool, size ); if (buffer != NULL) { *((PULONG) buffer) = DFS_MSGTYPE_GET_DOMAIN_REFERRAL; domainName = (LPWSTR) (buffer + sizeof(ULONG)); RtlCopyMemory( domainName, DomainName->Buffer, DomainName->Length ); domainName[ DomainName->Length / sizeof(WCHAR) ] = UNICODE_NULL; shareName = &domainName[ (DomainName->Length / sizeof(WCHAR)) + 1 ]; RtlCopyMemory( shareName, ShareName->Buffer, ShareName->Length ); shareName[ ShareName->Length / sizeof(WCHAR) ] = UNICODE_NULL; status = DfsDispatchUserModeThread( buffer, size ); if (!NT_SUCCESS(status)) status = STATUS_BAD_NETWORK_PATH; if (buffer != stackBuffer) ExFreePool( buffer ); } else { status = STATUS_INSUFFICIENT_RESOURCES; } return( status ); } //+---------------------------------------------------------------------------- // // Function: PktCreateMachineEntry // // Synopsis: Given a name that is thought to be a machine name, this // routine will create a Pkt Entry for the root of the machine's // Dfs. The machine must exist, must have a Dfs root, and must be // reachable for this routine to succeed. // // Arguments: [MachineName] -- Name of machine thought to support a Dfs // // Returns: [STATUS_SUCCESS] -- Successfully completed operation. // // [STATUS_BAD_NETWORK_PATH] -- MachineName is not a real // machine or its not reachable or does not support a // Dfs. // // [STATUS_INSUFFICIENT_RESOURCES] -- Out of memory condition. // //----------------------------------------------------------------------------- NTSTATUS PktCreateMachineEntry( IN PUNICODE_STRING MachineName, IN PUNICODE_STRING ShareName) { NTSTATUS status; HANDLE hServer = NULL; DFS_SERVICE service; PPROVIDER_DEF provider; PREQ_GET_DFS_REFERRAL ref = NULL; ULONG refSize = 0; ULONG type, matchLength; UNICODE_STRING refPath; IO_STATUS_BLOCK iosb; PDFS_PKT_ENTRY pktEntry; BOOLEAN attachedToSystemProcess = FALSE; DfsDbgTrace(+1, Dbg, "PktCreateMachineEntry: Entered %wZ\n", MachineName); // // First, get a provider and service describing the remote server. // provider = ReplLookupProvider( PROV_ID_DFS_RDR ); if (provider == NULL) { DfsDbgTrace(-1, Dbg, "Unable to open LM Rdr!\n", 0); return( STATUS_BAD_NETWORK_PATH ); } RtlZeroMemory( &service, sizeof(DFS_SERVICE) ); RtlZeroMemory( &refPath, sizeof(UNICODE_STRING) ); status = PktServiceConstruct( &service, DFS_SERVICE_TYPE_MASTER | DFS_SERVICE_TYPE_REFERRAL, PROV_DFS_RDR, STATUS_SUCCESS, PROV_ID_DFS_RDR, MachineName, NULL); DfsDbgTrace(0, Dbg, "PktServiceConstruct returned %08lx\n", status ); if (!NT_SUCCESS(status)) { ASSERT( status == STATUS_INSUFFICIENT_RESOURCES ); } // // Next, we build a connection to this machine and ask it for a referral // to its Dfs root. // if (NT_SUCCESS(status)) { BOOLEAN pktLocked; PktAcquireShared( TRUE, &pktLocked ); if (PsGetCurrentProcess() != DfsData.OurProcess) { KeAttachProcess( DfsData.OurProcess ); attachedToSystemProcess = TRUE; } status = DfsCreateConnection( &service, provider, &hServer); PktRelease(); DfsDbgTrace(0, Dbg, "DfsCreateConnection returned %08lx\n", status); } if (NT_SUCCESS(status)) { refPath.Length = 0; refPath.MaximumLength = sizeof(UNICODE_PATH_SEP_STR) + MachineName->Length + sizeof(UNICODE_PATH_SEP_STR) + ShareName->Length + sizeof(UNICODE_NULL); refPath.Buffer = ExAllocatePool( NonPagedPool, refPath.MaximumLength + MAX_REFERRAL_LENGTH ); if (refPath.Buffer != NULL) { ref = (PREQ_GET_DFS_REFERRAL) &refPath.Buffer[refPath.MaximumLength / sizeof(WCHAR)]; RtlAppendUnicodeToString( &refPath, UNICODE_PATH_SEP_STR); RtlAppendUnicodeStringToString( &refPath, MachineName); RtlAppendUnicodeToString( &refPath, UNICODE_PATH_SEP_STR); RtlAppendUnicodeStringToString( &refPath, ShareName ); refPath.Buffer[ refPath.Length / sizeof(WCHAR) ] = UNICODE_NULL; ref->MaxReferralLevel = 2; RtlMoveMemory( &ref->RequestFileName[0], refPath.Buffer, refPath.Length + sizeof(WCHAR)); DfsDbgTrace(0, Dbg, "Referral Path : %ws\n", ref->RequestFileName); refSize = sizeof(REQ_GET_DFS_REFERRAL) + refPath.Length + sizeof(WCHAR); DfsDbgTrace(0, Dbg, "Referral Size is %d bytes\n", refSize); } else { DfsDbgTrace(0, Dbg, "Unable to allocate %d bytes\n", (refPath.MaximumLength + MAX_REFERRAL_LENGTH)); status = STATUS_INSUFFICIENT_RESOURCES; } } if (NT_SUCCESS(status)) { DfsDbgTrace(0, Dbg, "Ref Buffer @%08lx\n", ref); status = ZwFsControlFile( hServer, // Target NULL, // Event NULL, // APC Routine NULL, // APC Context, &iosb, // Io Status block FSCTL_DFS_GET_REFERRALS, // FS Control code (PVOID) ref, // Input Buffer refSize, // Input Buffer Length (PVOID) ref, // Output Buffer MAX_REFERRAL_LENGTH); // Output Buffer Length DfsDbgTrace(0, Dbg, "Fscontrol returned %08lx\n", status); } if (NT_SUCCESS(status)) { status = PktCreateEntryFromReferral( &DfsData.Pkt, &refPath, iosb.Information, (PRESP_GET_DFS_REFERRAL) ref, PKT_ENTRY_SUPERSEDE, &matchLength, &type, &pktEntry); if (status == STATUS_INVALID_USER_BUFFER) status = STATUS_INVALID_NETWORK_RESPONSE; DfsDbgTrace(0, Dbg, "PktCreateEntryFromReferral returned %08lx\n", status); } // // Well, we are done. Cleanup all the things we allocated... // PktServiceDestroy( &service, FALSE ); if (hServer != NULL) { ZwClose( hServer ); } if (refPath.Buffer != NULL) { ExFreePool( refPath.Buffer ); } if (attachedToSystemProcess) { KeDetachProcess(); } if (status != STATUS_SUCCESS && status != STATUS_INSUFFICIENT_RESOURCES) { status = STATUS_BAD_NETWORK_PATH; } DfsDbgTrace(-1, Dbg, "PktCreateMachineEntry returning %08lx\n", status); return( status ); } //+------------------------------------------------------------------------- // // Function: PktLookupEntryByPrefix, public // // Synopsis: PktLookupEntryByPrefix finds an entry that has a // specified prefix. The PKT must be acquired for // this operation. // // Arguments: [Pkt] - pointer to a initialized (and acquired) PKT // [Prefix] - the partitions prefix to lookup. // [Remaining] - any remaining path. Points within // the Prefix to where any trailing (nonmatched) // characters are. // // Returns: The PKT_ENTRY that has the exact same prefix, or NULL, // if none exists. // // Notes: // //-------------------------------------------------------------------------- PDFS_PKT_ENTRY PktLookupEntryByPrefix( IN PDFS_PKT Pkt, IN PUNICODE_STRING Prefix, OUT PUNICODE_STRING Remaining ) { PUNICODE_PREFIX_TABLE_ENTRY pfxEntry; PDFS_PKT_ENTRY pktEntry; DfsDbgTrace(+1, Dbg, "PktLookupEntryByPrefix: Entered\n", 0); // // If there really is a prefix to lookup, use the prefix table // to initially find an entry // if ((Prefix->Length != 0) && (pfxEntry = DfsFindUnicodePrefix(&Pkt->PrefixTable,Prefix,Remaining))) { USHORT pfxLength; // // reset a pointer to the corresponding entry // pktEntry = CONTAINING_RECORD(pfxEntry, DFS_PKT_ENTRY, PrefixTableEntry ); pfxLength = pktEntry->Id.Prefix.Length; // // Now calculate the remaining path and return // the entry we found. Note that we bump the length // up by one char so that we skip any path separater. // if ((pfxLength < Prefix->Length) && (Prefix->Buffer[pfxLength/sizeof(WCHAR)] == UNICODE_PATH_SEP)) pfxLength += sizeof(WCHAR); if (pfxLength < Prefix->Length) { Remaining->Length = (USHORT)(Prefix->Length - pfxLength); Remaining->Buffer = &Prefix->Buffer[pfxLength/sizeof(WCHAR)]; Remaining->MaximumLength = (USHORT)(Prefix->MaximumLength - pfxLength); DfsDbgTrace( 0, Dbg, "PktLookupEntryByPrefix: Remaining = %wZ\n", Remaining); } else { Remaining->Length = Remaining->MaximumLength = 0; Remaining->Buffer = NULL; DfsDbgTrace( 0, Dbg, "PktLookupEntryByPrefix: No Remaining\n", 0); } DfsDbgTrace(-1, Dbg, "PktLookupEntryByPrefix: Exit -> %08lx\n", pktEntry); return pktEntry; } DfsDbgTrace(-1, Dbg, "PktLookupEntryByPrefix: Exit -> %08lx\n", NULL); return NULL; } //+------------------------------------------------------------------------- // // Function: PktLookupEntryByShortPrefix, public // // Synopsis: PktLookupEntryByShortPrefix finds an entry that has a // specified prefix. The PKT must be acquired for // this operation. // // Arguments: [Pkt] - pointer to a initialized (and acquired) PKT // [Prefix] - the partitions prefix to lookup. // [Remaining] - any remaining path. Points within // the Prefix to where any trailing (nonmatched) // characters are. // // Returns: The PKT_ENTRY that has the exact same prefix, or NULL, // if none exists. // // Notes: // //-------------------------------------------------------------------------- PDFS_PKT_ENTRY PktLookupEntryByShortPrefix( IN PDFS_PKT Pkt, IN PUNICODE_STRING Prefix, OUT PUNICODE_STRING Remaining ) { PUNICODE_PREFIX_TABLE_ENTRY pfxEntry; PDFS_PKT_ENTRY pktEntry; DfsDbgTrace(+1, Dbg, "PktLookupEntryByShortPrefix: Entered\n", 0); // // If there really is a prefix to lookup, use the prefix table // to initially find an entry // if ((Prefix->Length != 0) && (pfxEntry = DfsFindUnicodePrefix(&Pkt->ShortPrefixTable,Prefix,Remaining))) { USHORT pfxLength; // // reset a pointer to the corresponding entry // pktEntry = CONTAINING_RECORD(pfxEntry, DFS_PKT_ENTRY, PrefixTableEntry ); pfxLength = pktEntry->Id.ShortPrefix.Length; // // Now calculate the remaining path and return // the entry we found. Note that we bump the length // up by one char so that we skip any path separater. // if ((pfxLength < Prefix->Length) && (Prefix->Buffer[pfxLength/sizeof(WCHAR)] == UNICODE_PATH_SEP)) pfxLength += sizeof(WCHAR); if (pfxLength < Prefix->Length) { Remaining->Length = (USHORT)(Prefix->Length - pfxLength); Remaining->Buffer = &Prefix->Buffer[pfxLength/sizeof(WCHAR)]; Remaining->MaximumLength = (USHORT)(Prefix->MaximumLength - pfxLength); DfsDbgTrace( 0, Dbg, "PktLookupEntryByShortPrefix: Remaining = %wZ\n", Remaining); } else { Remaining->Length = Remaining->MaximumLength = 0; Remaining->Buffer = NULL; DfsDbgTrace( 0, Dbg, "PktLookupEntryByShortPrefix: No Remaining\n", 0); } DfsDbgTrace(-1, Dbg, "PktLookupEntryByShortPrefix: Exit -> %08lx\n", pktEntry); return pktEntry; } DfsDbgTrace(-1, Dbg, "PktLookupEntryByShortPrefix: Exit -> %08lx\n", NULL); return NULL; } //+------------------------------------------------------------------------- // // Function: PktLookupEntryByUid, public // // Synopsis: PktLookupEntryByUid finds an entry that has a // specified Uid. The PKT must be acquired for this operation. // // Arguments: [Pkt] - pointer to a initialized (and acquired) PKT // [Uid] - a pointer to the partitions Uid to lookup. // // Returns: A pointer to the PKT_ENTRY that has the exact same // Uid, or NULL, if none exists. // // Notes: The input Uid cannot be the Null GUID. // // On a DC where there may be *lots* of entries in the PKT, // we may want to consider using some other algorithm for // looking up by ID. // //-------------------------------------------------------------------------- PDFS_PKT_ENTRY PktLookupEntryByUid( IN PDFS_PKT Pkt, IN GUID *Uid ) { PDFS_PKT_ENTRY entry; DfsDbgTrace(+1, Dbg, "PktLookupEntryByUid: Entered\n", 0); // // We don't lookup NULL Uids // if (NullGuid(Uid)) { DfsDbgTrace(0, Dbg, "PktLookupEntryByUid: NULL Guid\n", NULL); entry = NULL; } else { entry = PktFirstEntry(Pkt); } while (entry != NULL) { if (GuidEqual(&entry->Id.Uid, Uid)) break; entry = PktNextEntry(Pkt, entry); } DfsDbgTrace(-1, Dbg, "PktLookupEntryByUid: Exit -> %08lx\n", entry); return entry; } //+------------------------------------------------------------------------- // // Function: PktLookupReferralEntry, public // // Synopsis: Given a PKT Entry pointer it returns the closest referral // entry in the PKT to this entry. // // Arguments: [Pkt] - A pointer to the PKT that is being manipulated. // [Entry] - The PKT entry passed in by caller. // // Returns: The pointer to the referral entry that was requested. // This could have a NULL value if we could not get anything // at all - The caller's responsibility to do whatever he wants // with it. // // Note: If the data structures in the PKT are not linked up right // this function might return a pointer to the DOMAIN_SERVICE // entry on the DC. If DNR uses this to do an FSCTL we will have // a deadlock. However, this should never happen. If it does we // have a BUG somewhere in our code. I cannot even have an // assert out here. // //-------------------------------------------------------------------------- PDFS_PKT_ENTRY PktLookupReferralEntry( PDFS_PKT Pkt, PDFS_PKT_ENTRY Entry ) { DfsDbgTrace(+1, Dbg, "PktLookupReferralEntry: Entered\n", 0); if (Entry == NULL) { return( NULL ); } // // Given a PKT entry we are going to traverse up the pointers till // we reach a DCs entry. This is what we are doing here. // while ((Entry->ClosestDC != NULL) && !(Entry->Type & PKT_ENTRY_TYPE_REFERRAL_SVC)) { Entry = Entry->ClosestDC; } // // Make sure that we did reach an entry for a DC or we return our domain's // DC entry in the worst case. // if (!(Entry->Type & PKT_ENTRY_TYPE_REFERRAL_SVC)) { Entry = NULL; } DfsDbgTrace(-1, Dbg, "PktLookupReferralEntry: Exit -> %08lx\n", Entry); return(Entry); } //+------------------------------------------------------------------------- // // Function: PktCreateEntryFromReferral, public // // Synopsis: PktCreateEntryFromReferral creates a new partition // table entry from a referral and places it in the table. // The PKT must be aquired exclusively for this operation. // // Arguments: [Pkt] -- pointer to a initialized (and exclusively // acquired) PKT // [ReferralPath] -- Path for which this referral was obtained. // [ReferralSize] -- size (in bytes) of the referral buffer. // [ReferralBuffer] -- pointer to a referral buffer // [CreateDisposition] -- specifies whether to overwrite if // an entry already exists, etc. // [MatchingLength] -- The length in bytes of referralPath that // matched. // [ReferralType] - On successful return, this is set to // DFS_STORAGE_REFERRAL or DFS_REFERRAL_REFERRAL // depending on the type of referral we just processed. // [ppPktEntry] - the new entry is placed here. // // Returns: NTSTATUS - STATUS_SUCCESS if no error. // // Notes: // //-------------------------------------------------------------------------- NTSTATUS PktCreateEntryFromReferral( IN PDFS_PKT Pkt, IN PUNICODE_STRING ReferralPath, IN ULONG ReferralSize, IN PRESP_GET_DFS_REFERRAL ReferralBuffer, IN ULONG CreateDisposition, OUT ULONG *MatchingLength, OUT ULONG *ReferralType, OUT PDFS_PKT_ENTRY *ppPktEntry ) { DFS_PKT_ENTRY_ID EntryId; UNICODE_STRING RemainingPath; ULONG RefListSize; NTSTATUS Status; BOOLEAN bPktAcquired = FALSE; UNREFERENCED_PARAMETER(Pkt); DfsDbgTrace(+1, Dbg, "PktCreateEntryFromReferral: Entered\n", 0); try { RtlZeroMemory(&EntryId, sizeof(EntryId)); // // Do some parameter validation // Status = PktpCheckReferralSyntax( ReferralPath, ReferralBuffer, ReferralSize); if (!NT_SUCCESS(Status)) { try_return(Status); } Status = PktpCreateEntryIdFromReferral( ReferralBuffer, ReferralPath, MatchingLength, &EntryId); if (!NT_SUCCESS(Status)) { try_return(Status); } // // Create/Update the prefix entry // PktAcquireExclusive(TRUE, &bPktAcquired); Status = PktpAddEntry(&DfsData.Pkt, &EntryId, ReferralBuffer, CreateDisposition, ppPktEntry); PktRelease(); bPktAcquired = FALSE; // // We have to tell the caller as to what kind of referral was just // received through ReferralType. // if (ReferralBuffer->StorageServers == 1) { *ReferralType = DFS_STORAGE_REFERRAL; } else { *ReferralType = DFS_REFERRAL_REFERRAL; } try_exit: NOTHING; } finally { DebugUnwind(PktCreateEntryFromReferral); if (bPktAcquired) PktRelease(); if (AbnormalTermination()) Status = STATUS_INVALID_USER_BUFFER; PktEntryIdDestroy( &EntryId, FALSE ); } DfsDbgTrace(-1, Dbg, "PktCreateEntryFromReferral: Exit -> %08lx\n", Status); return Status; } //+---------------------------------------------------------------------------- // // Function: PktpCheckReferralSyntax // // Synopsis: Does some validation of a Referral // // Arguments: [ReferralPath] -- The Path for which a referral was obtained // [ReferralBuffer] -- Pointer to RESP_GET_DFS_REFERRAL Buffer // [ReferralSize] -- Size of ReferralBuffer // // Returns: [STATUS_SUCCESS] -- Referral looks ok. // // [STATUS_INVALID_USER_BUFFER] -- Buffer looks hoky. // //----------------------------------------------------------------------------- NTSTATUS PktpCheckReferralSyntax( IN PUNICODE_STRING ReferralPath, IN PRESP_GET_DFS_REFERRAL ReferralBuffer, IN DWORD ReferralSize) { NTSTATUS status = STATUS_SUCCESS; ULONG i, sizeRemaining; PDFS_REFERRAL_V1 ref; PCHAR ReferralBufferEnd = (((PCHAR) ReferralBuffer) + ReferralSize); if (ReferralBuffer->PathConsumed > ReferralPath->Length) return( STATUS_INVALID_USER_BUFFER ); if (ReferralBuffer->NumberOfReferrals == 0) return( STATUS_INVALID_USER_BUFFER ); if (ReferralBuffer->NumberOfReferrals * sizeof(DFS_REFERRAL_V1) > ReferralSize) return( STATUS_INVALID_USER_BUFFER ); for (i = 0, ref = &ReferralBuffer->Referrals[0].v1, status = STATUS_SUCCESS, sizeRemaining = ReferralSize - FIELD_OFFSET(RESP_GET_DFS_REFERRAL, Referrals); i < ReferralBuffer->NumberOfReferrals; i++) { ULONG lenAddress; if ((ref->VersionNumber != 1 && ref->VersionNumber != 2) || ref->Size > sizeRemaining) { status = STATUS_INVALID_USER_BUFFER; break; } // // Check the network address syntax // if (ref->VersionNumber == 1) { status = PktpCheckReferralString( (LPWSTR) ref->ShareName, (PCHAR) ReferralBuffer, ReferralBufferEnd); if (NT_SUCCESS(status)) { lenAddress = ref->Size - FIELD_OFFSET(DFS_REFERRAL_V1, ShareName); lenAddress /= sizeof(WCHAR); status = PktpCheckReferralNetworkAddress( (LPWSTR) ref->ShareName, lenAddress); } } else { PDFS_REFERRAL_V2 refV2 = (PDFS_REFERRAL_V2) ref; PWCHAR dfsPath, dfsAlternatePath, networkAddress; dfsPath = (PWCHAR) (((PCHAR) refV2) + refV2->DfsPathOffset); dfsAlternatePath = (PWCHAR) (((PCHAR) refV2) + refV2->DfsAlternatePathOffset); networkAddress = (PWCHAR) (((PCHAR) refV2) + refV2->NetworkAddressOffset); status = PktpCheckReferralString( dfsPath, (PCHAR) ReferralBuffer, ReferralBufferEnd); if (NT_SUCCESS(status)) { status = PktpCheckReferralString( dfsAlternatePath, (PCHAR) ReferralBuffer, ReferralBufferEnd); } if (NT_SUCCESS(status)) { status = PktpCheckReferralString( networkAddress, (PCHAR) ReferralBuffer, ReferralBufferEnd); } if (NT_SUCCESS(status)) { lenAddress = ((ULONG) ReferralBufferEnd) - ((ULONG) networkAddress); lenAddress /= sizeof(WCHAR); status = PktpCheckReferralNetworkAddress( networkAddress, lenAddress); } } // // This ref is ok. Go on to the next one... // sizeRemaining -= ref->Size; ref = (PDFS_REFERRAL_V1) (((PUCHAR) ref) + ref->Size); } return( status ); } //+---------------------------------------------------------------------------- // // Function: PktpCheckReferralString // // Synopsis: Validates part of a Referral as being a valid "string" // // Arguments: [String] -- Pointer to buffer thought to contain string. // [ReferralBuffer] -- Start of Referral Buffer // [ReferralBufferEnd] -- End of Referral Buffer // // Returns: [STATUS_SUCCESS] -- Valid string at String. // // [STATUS_INVALID_USER_BUFFER] -- String doesn't check out. // //----------------------------------------------------------------------------- NTSTATUS PktpCheckReferralString( IN LPWSTR String, IN PCHAR ReferralBuffer, IN PCHAR ReferralBufferEnd) { NTSTATUS status = STATUS_SUCCESS; ULONG i, length; if ( (((ULONG) String) & 0x1) != 0 ) { // // Strings should always start at word aligned addresses! // return( STATUS_INVALID_USER_BUFFER ); } if ( (((ULONG) String) >= ((ULONG) ReferralBuffer)) && (((ULONG) String) < ((ULONG) ReferralBufferEnd)) ) { length = ( ((ULONG) ReferralBufferEnd) - ((ULONG) String) ) / sizeof(WCHAR); for (i = 0; (i < length) && (String[i] != UNICODE_NULL); i++) { NOTHING; } if (i >= length) status = STATUS_INVALID_USER_BUFFER; } else { status = STATUS_INVALID_USER_BUFFER; } return( status ); } //+---------------------------------------------------------------------------- // // Function: PktpCheckReferralNetworkAddress // // Synopsis: Checks to see if a NetworkAddress inside a referral // is of a valid form // // Arguments: [Address] -- Pointer to buffer containing network addresss // // [MaxLength] -- Maximum length, in wchars, that Address can be. // // Returns: [STATUS_SUCCESS] -- Network address checks out // // [STATUS_INVALID_USER_BUFFER] -- Network address looks bogus // //----------------------------------------------------------------------------- NTSTATUS PktpCheckReferralNetworkAddress( IN PWCHAR Address, IN ULONG MaxLength) { ULONG j; BOOLEAN foundShare; // // Address must be atleast \a\b followed by a NULL // if (MaxLength < 5) return(STATUS_INVALID_USER_BUFFER); // // Make sure the server name part is not NULL // if (Address[0] != UNICODE_PATH_SEP || Address[1] == UNICODE_PATH_SEP) return(STATUS_INVALID_USER_BUFFER); // // Find the backslash after the server name // for (j = 2, foundShare = FALSE; j < MaxLength && !foundShare; j++) { if (Address[j] == UNICODE_PATH_SEP) foundShare = TRUE; } if (foundShare) { // // We found the second backslash. Make sure the share name // part is not 0 length. // if (j == MaxLength) return(STATUS_INVALID_USER_BUFFER); else { ASSERT(Address[j-1] == UNICODE_PATH_SEP); if (Address[j] == UNICODE_PATH_SEP || Address[j] == UNICODE_NULL) return(STATUS_INVALID_USER_BUFFER); } } else return(STATUS_INVALID_USER_BUFFER); return( STATUS_SUCCESS ); } //+-------------------------------------------------------------------- // // Function: PktpAddEntry // // Synopsis: This function is called to create an entry which was obtained // in the form of a referral from a DC. This method should only // be called for adding entries which were obtained through // referrals. It sets an expire time on all these entries. // // Arguments: [Pkt] -- // [EntryId] -- // [ReferralBuffer] -- // [CreateDisposition] -- // [ppPktEntry] -- // // Returns: NTSTATUS // //--------------------------------------------------------------------- NTSTATUS PktpAddEntry ( IN PDFS_PKT Pkt, IN PDFS_PKT_ENTRY_ID EntryId, IN PRESP_GET_DFS_REFERRAL ReferralBuffer, IN ULONG CreateDisposition, OUT PDFS_PKT_ENTRY *ppPktEntry ) { NTSTATUS status; DFS_PKT_ENTRY_INFO pktEntryInfo; ULONG Type, n; PDFS_SERVICE service; PDFS_REFERRAL_V1 ref; LPWSTR shareName; PDS_MACHINE pMachine; ULONG TimeToLive = 0; DfsDbgTrace(+1, Dbg, "PktpAddEntry: Entered\n", 0); RtlZeroMemory(&pktEntryInfo, sizeof(DFS_PKT_ENTRY_INFO)); DfsDbgTrace( 0, Dbg, "PktpAddEntry: Id.Prefix = %wZ\n", &EntryId->Prefix); // // Now we go about the business of creating the entry Info structure. // pktEntryInfo.ServiceCount = ReferralBuffer->NumberOfReferrals; if (pktEntryInfo.ServiceCount > 0) { // // Allocate the service list. // n = pktEntryInfo.ServiceCount; pktEntryInfo.ServiceList = (PDFS_SERVICE) ExAllocatePool(PagedPool, sizeof(DFS_SERVICE) * n); if (pktEntryInfo.ServiceList == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlZeroMemory(pktEntryInfo.ServiceList, sizeof(DFS_SERVICE) * n); // // initialize temporary pointers // service = pktEntryInfo.ServiceList; ref = &ReferralBuffer->Referrals[0].v1; // // Cycle through the list of referrals initializing // service structures on the way. // while (n--) { if (ref->ServerType == 1) { service->Type = DFS_SERVICE_TYPE_MASTER; service->Capability = PROV_DFS_RDR; service->ProviderId = PROV_ID_DFS_RDR; } else { service->Type = DFS_SERVICE_TYPE_MASTER | DFS_SERVICE_TYPE_DOWN_LEVEL; service->Capability = PROV_STRIP_PREFIX; service->ProviderId = PROV_ID_LM_RDR; } if (ref->VersionNumber == 1) { shareName = (LPWSTR) (ref->ShareName); } else { PDFS_REFERRAL_V2 refV2 = (PDFS_REFERRAL_V2) ref; service->Cost = refV2->Proximity; TimeToLive = refV2->TimeToLive; shareName = (LPWSTR) (((PCHAR) refV2) + refV2->NetworkAddressOffset); } // // Now try and figure out the server name // { USHORT plen; WCHAR *pbuf; ASSERT( shareName[0] == UNICODE_PATH_SEP ); pbuf = wcschr( &shareName[1], UNICODE_PATH_SEP ); plen = (USHORT) (((ULONG)pbuf) - ((ULONG)&shareName[1])); service->Name.Length = plen; service->Name.MaximumLength = plen + sizeof(WCHAR); service->Name.Buffer = (PWCHAR) ExAllocatePool( PagedPool, plen + sizeof(WCHAR)); if (service->Name.Buffer == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlMoveMemory(service->Name.Buffer, &shareName[1], plen); service->Name.Buffer[ service->Name.Length / sizeof(WCHAR) ] = UNICODE_NULL; } // // Next, try and copy the address... // service->Address.Length = (USHORT) wcslen(shareName) * sizeof(WCHAR); service->Address.MaximumLength = service->Address.Length + sizeof(WCHAR); service->Address.Buffer = (PWCHAR) ExAllocatePool( PagedPool, service->Address.MaximumLength); if (service->Address.Buffer == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlMoveMemory(service->Address.Buffer, shareName, service->Address.MaximumLength); DfsDbgTrace( 0, Dbg, "PktpAddEntry: service->Address = %wZ\n", &service->Address); // // Get the Machine Address structure for this server... // pMachine = PktpGetDSMachine( &service->Name ); if (pMachine == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } service->pMachEntry = ExAllocatePool( PagedPool, sizeof(DFS_MACHINE_ENTRY)); if (service->pMachEntry == NULL) { DfsDbgTrace( 0, Dbg, "PktpAddEntry: Unable to allocate DFS_MACHINE_ENTRY\n", 0); status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlZeroMemory( (PVOID) service->pMachEntry, sizeof(DFS_MACHINE_ENTRY)); service->pMachEntry->pMachine = pMachine; service->pMachEntry->UseCount = 1; // // Now we need to advance to the next referral, and to // the next service structure. // ref = (PDFS_REFERRAL_V1) (((PUCHAR)ref) + ref->Size); service++; } // // Finally, we shuffle the services so that we achieve load balancing // while still maintaining site-cost based replica selection. // PktShuffleServiceList( &pktEntryInfo ); } // // Now we have to figure out the type for this entry. // if (ReferralBuffer->StorageServers == 0) { ASSERT(ReferralBuffer->ReferralServers == 1); Type = PKT_ENTRY_TYPE_OUTSIDE_MY_DOM; } else { Type = PKT_ENTRY_TYPE_DFS; } if (ReferralBuffer->ReferralServers == 1) Type |= PKT_ENTRY_TYPE_REFERRAL_SVC; // // At this point we have everything we need to create an entry, so // try to add the entry. // status = PktCreateEntry( Pkt, Type, EntryId, &pktEntryInfo, CreateDisposition, ppPktEntry); if (!NT_SUCCESS(status)) { // // Since we failed to add the entry, at least we need to release // all the memory before we return back. // goto Cleanup; } // // We set the ExpireTime in this entry to // Pkt->EntryTimeToLive. After these many number of seconds this // entry will get deleted from the PKT. Do this only for non-permanent // entries. // if (TimeToLive != 0) { (*ppPktEntry)->ExpireTime = TimeToLive; (*ppPktEntry)->TimeToLive = TimeToLive; } else { (*ppPktEntry)->ExpireTime = Pkt->EntryTimeToLive; (*ppPktEntry)->TimeToLive = Pkt->EntryTimeToLive; } DfsDbgTrace(-1, Dbg, "PktpAddEntry: Exit -> %08lx\n", status ); return status; Cleanup: if (pktEntryInfo.ServiceCount > 0) { n = pktEntryInfo.ServiceCount; if (pktEntryInfo.ServiceList != NULL) { service = pktEntryInfo.ServiceList; while (n--) { if (service->Name.Buffer != NULL) DfsFree(service->Name.Buffer); if (service->Address.Buffer != NULL) DfsFree(service->Address.Buffer); if (service->pMachEntry != NULL) { if (service->pMachEntry->pMachine != NULL) PktDSMachineDestroy(service->pMachEntry->pMachine, TRUE); ExFreePool( service->pMachEntry ); } service++; } ExFreePool(pktEntryInfo.ServiceList); } } DfsDbgTrace(-1, Dbg, "PktpAddEntry: Exit -> %08lx\n", status ); return status; } //+---------------------------------------------------------------------------- // // Function: PktpCreateEntryIdFromReferral // // Synopsis: Given a dfs referral, this routine constructs a PKT_ENTRY_ID // from the referral buffer which can then be used to create // the Pkt Entry. // // Arguments: [Ref] -- The referral buffer // [ReferralPath] -- The path for which the referral was obtained // [MatchingLength] -- The length in bytes of ReferralPath that // matched. // [Peid] -- On successful return, the entry id is returned // here. // // Returns: [STATUS_SUCCESS] -- Successfully create entry id. // // [STATUS_INSUFFICIENT_RESOURCES] -- Out of memory condition // //----------------------------------------------------------------------------- NTSTATUS PktpCreateEntryIdFromReferral( IN PRESP_GET_DFS_REFERRAL Ref, IN PUNICODE_STRING ReferralPath, OUT ULONG *MatchingLength, OUT PDFS_PKT_ENTRY_ID Peid) { NTSTATUS status = STATUS_SUCCESS; PDFS_REFERRAL_V2 pv; UNICODE_STRING prefix, shortPrefix; Peid->Prefix.Buffer = NULL; Peid->ShortPrefix.Buffer = NULL; pv = &Ref->Referrals[0].v2; if (pv->VersionNumber == 1) { // // A version 1 referral only has the number of characters that // matched, and it does not have short names. // prefix = *ReferralPath; prefix.Length = Ref->PathConsumed; if (prefix.Buffer[ prefix.Length/sizeof(WCHAR) - 1 ] == UNICODE_PATH_SEP) { prefix.Length -= sizeof(WCHAR); } prefix.MaximumLength = prefix.Length + sizeof(WCHAR); shortPrefix = prefix; *MatchingLength = prefix.Length; } else { LPWSTR volPrefix; LPWSTR volShortPrefix; volPrefix = (LPWSTR) (((PCHAR) pv) + pv->DfsPathOffset); volShortPrefix = (LPWSTR) (((PCHAR) pv) + pv->DfsAlternatePathOffset); RtlInitUnicodeString(&prefix, volPrefix); RtlInitUnicodeString(&shortPrefix, volShortPrefix); *MatchingLength = Ref->PathConsumed; } Peid->Prefix.Buffer = ExAllocatePool( PagedPool, prefix.MaximumLength); if (Peid->Prefix.Buffer == NULL) status = STATUS_INSUFFICIENT_RESOURCES; if (NT_SUCCESS(status)) { Peid->ShortPrefix.Buffer = ExAllocatePool( PagedPool, shortPrefix.MaximumLength); if (Peid->ShortPrefix.Buffer == NULL) status = STATUS_INSUFFICIENT_RESOURCES; } if (NT_SUCCESS(status)) { Peid->Prefix.Length = prefix.Length; Peid->Prefix.MaximumLength = prefix.MaximumLength; RtlCopyMemory( Peid->Prefix.Buffer, prefix.Buffer, prefix.Length); Peid->Prefix.Buffer[Peid->Prefix.Length/sizeof(WCHAR)] = UNICODE_NULL; Peid->ShortPrefix.Length = shortPrefix.Length; Peid->ShortPrefix.MaximumLength = shortPrefix.MaximumLength; RtlCopyMemory( Peid->ShortPrefix.Buffer, shortPrefix.Buffer, shortPrefix.Length); Peid->ShortPrefix.Buffer[Peid->ShortPrefix.Length/sizeof(WCHAR)] = UNICODE_NULL; } if (!NT_SUCCESS(status)) { if (Peid->Prefix.Buffer != NULL) { ExFreePool( Peid->Prefix.Buffer ); Peid->Prefix.Buffer = NULL; } if (Peid->ShortPrefix.Buffer != NULL) { ExFreePool( Peid->ShortPrefix.Buffer ); Peid->ShortPrefix.Buffer = NULL; } } return( status ); } //+---------------------------------------------------------------------------- // // Function: PktpGetDSMachine // // Synopsis: Builds a DS_MACHINE with a single NetBIOS address // // Arguments: [ServerName] -- Name of server. // // Returns: If successful, a pointer to a newly allocate DS_MACHINE, // otherwise, NULL // //----------------------------------------------------------------------------- PDS_MACHINE PktpGetDSMachine( IN PUNICODE_STRING ServerName) { PDS_MACHINE pMachine = NULL; PDS_TRANSPORT pdsTransport; PTDI_ADDRESS_NETBIOS ptdiNB; ANSI_STRING astrNetBios; // // Allocate the DS_MACHINE structure // pMachine = ExAllocatePool(PagedPool, sizeof(DS_MACHINE)); if (pMachine == NULL) { goto Cleanup; } RtlZeroMemory(pMachine, sizeof(DS_MACHINE)); // // Allocate the array of principal names // pMachine->cPrincipals = 1; pMachine->prgpwszPrincipals = (LPWSTR *) ExAllocatePool( PagedPool, sizeof(LPWSTR)); if (pMachine->prgpwszPrincipals == NULL) { goto Cleanup; } // // Allocate the principal name // pMachine->prgpwszPrincipals[0] = (PWCHAR) ExAllocatePool( PagedPool, ServerName->MaximumLength); if (pMachine->prgpwszPrincipals[0] == NULL) { goto Cleanup; } RtlMoveMemory( pMachine->prgpwszPrincipals[0], ServerName->Buffer, ServerName->MaximumLength); // // Allocate a single DS_TRANSPORT // pMachine->cTransports = 1; pMachine->rpTrans[0] = (PDS_TRANSPORT) ExAllocatePool( PagedPool, sizeof(DS_TRANSPORT) + sizeof(TDI_ADDRESS_NETBIOS)); if (pMachine->rpTrans[0] == NULL) { goto Cleanup; } // // Initialize the DS_TRANSPORT // pdsTransport = pMachine->rpTrans[0]; pdsTransport->usFileProtocol = FSP_SMB; pdsTransport->iPrincipal = 0; pdsTransport->grfModifiers = 0; // // Build the TA_ADDRESS_NETBIOS // pdsTransport->taddr.AddressLength = sizeof(TDI_ADDRESS_NETBIOS); pdsTransport->taddr.AddressType = TDI_ADDRESS_TYPE_NETBIOS; ptdiNB = (PTDI_ADDRESS_NETBIOS) &pdsTransport->taddr.Address[0]; ptdiNB->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; RtlFillMemory( &ptdiNB->NetbiosName[0], 16, ' ' ); astrNetBios.Length = 0; astrNetBios.MaximumLength = 16; astrNetBios.Buffer = ptdiNB->NetbiosName; RtlUnicodeStringToAnsiString(&astrNetBios, ServerName, FALSE); return( pMachine ); Cleanup: if (pMachine) { PktDSMachineDestroy( pMachine, TRUE ); pMachine = NULL; } return( pMachine ); } //+---------------------------------------------------------------------------- // // Function: PktShuffleServiceList // // Synopsis: Randomizes a service list for proper load balancing. This // routine assumes that the service list is ordered based on // site costs. For each equivalent cost group, this routine // shuffles the service list. // // Arguments: [pInfo] -- Pointer to PktEntryInfo whose service list needs to // be shuffled. // // Returns: Nothing, unless rand() fails! // //----------------------------------------------------------------------------- VOID PktShuffleServiceList( PDFS_PKT_ENTRY_INFO pInfo) { ULONG i, j; for (i = 0, j = 0; i < pInfo->ServiceCount; i++) { if (pInfo->ServiceList[i].Type & DFS_SERVICE_TYPE_COSTLIER) { PktShuffleGroup(pInfo, j, i); j = i; } } // // Shuffle the last group. // i; if (j != i) { ASSERT( j < i ); PktShuffleGroup( pInfo, j, i ); } } //+---------------------------------------------------------------------------- // // Function: PktShuffleGroup // // Synopsis: Shuffles a cost equivalent group of services around for load // balancing. Uses the classic card shuffling algorithm - for // each card in the deck, exchange it with a random card in the // deck. // // Arguments: // // Returns: // //----------------------------------------------------------------------------- VOID PktShuffleGroup( PDFS_PKT_ENTRY_INFO pInfo, ULONG nStart, ULONG nEnd) { ULONG i; LARGE_INTEGER seed; ASSERT( nStart < pInfo->ServiceCount ); ASSERT( nEnd <= pInfo->ServiceCount ); KeQuerySystemTime( &seed ); for (i = nStart; i < nEnd; i++) { DFS_SERVICE TempService; ULONG j; ASSERT (nEnd - nStart != 0); j = (RtlRandom( &seed.LowPart ) % (nEnd - nStart)) + nStart; ASSERT( j >= nStart && j <= nEnd ); TempService = pInfo->ServiceList[i]; pInfo->ServiceList[i] = pInfo->ServiceList[j]; pInfo->ServiceList[j] = TempService; } }