//+---------------------------------------------------------------------------- // // Copyright (C) 2000, Microsoft Corporation // // File: DfsRegistryStore.cxx // // Contents: the Registry DFS Store class, this contains the registry // specific functionality. // // Classes: DfsRegistryStore. // // History: Dec. 8 2000, Author: udayh // //----------------------------------------------------------------------------- #include "DfsRegistryStore.hxx" #include "DfsRegistryRootFolder.hxx" #include "DfsFilterApi.hxx" #include "dfsmisc.h" #include "domainControllerSupport.hxx" #include "DfsRegStrings.hxx" #include "shlwapi.h" #include "align.h" // // logging specific includes // #include "DfsRegistryStore.tmh" extern DFSSTATUS MigrateDfs( LPWSTR MachineName); //+---------------------------------------------------------------------------- // // Class: DfsRegistryStore // // Synopsis: This class inherits the basic DfsStore, and extends it // to include the registry specific functionality. // //----------------------------------------------------------------------------- LPWSTR DfsRegistryNameString = L"ID"; LPWSTR DfsRegistryRecoveryString = L"Recovery"; LPWSTR DfsRegistryReplicaString = L"Svc"; LPWSTR DfsRegistryDfsDriverLocation = DFS_REG_DFS_DRIVER_LOCATION; LPWSTR DfsLocalVolumesValue = DFS_REG_LOCAL_VOLUMES_CHILD; extern LPWSTR DfsVolumesLocation; //+------------------------------------------------------------------------- // // Function: StoreRecognizer - the recognizer for the store. // // Arguments: Name - the namespace of interest. // // Returns: Status // ERROR_SUCCESS on success // ERROR status code otherwise // // // Description: This routine checks if the specified namespace holds // a registry based DFS. If it does, it reads in the // root in that namespace and creates and adds it to our // list of known roots, if it doesn't already exist in our list. // //-------------------------------------------------------------------------- DFSSTATUS DfsRegistryStore::StoreRecognizer( LPWSTR Name ) { DFSSTATUS Status = ERROR_SUCCESS; HKEY OldStandaloneDfsKey = NULL; HKEY StandaloneDfsKey = NULL; BOOLEAN MachineContacted = FALSE; // // Make sure the namespace is the name of a machine. Registry based // dfs exist only on machines. // if (IsEmptyString(Name) == FALSE) { Status = DfsIsThisAMachineName( Name ); if(Status != ERROR_SUCCESS) { return Status; } } DFS_TRACE_LOW(REFERRAL_SERVER, "DfsRegistryStore:StoreRecognizer, Name %ws Is Machine Status %x\n", Name, Status); // // we now find all the migrated multiple roots standalone DFS's as // well as the old single root standalone DFS, and add them to our // in memory metadata. // Status = GetNewStandaloneRegistryKey( Name, FALSE, &MachineContacted, &StandaloneDfsKey ); if (Status == ERROR_SUCCESS) { Status = StoreRecognizeNewDfs( Name, StandaloneDfsKey ); RegCloseKey( StandaloneDfsKey ); } // // Need to refine the return status further: success should mean // machine is not std dfs or we have read the std dfs data correctly. // DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "Dfs registry store recognizser, status %x\n", Status); return Status; } //+------------------------------------------------------------------------- // // Function: StoreRecognizer - the recognizer for the store. // // Arguments: DfsNameContext - the namespace of interest. // LogicalShare // // Returns: Status // ERROR_SUCCESS on success // ERROR status code otherwise // // // Description: This routine checks if the specified namespace holds // a domain based DFS. If it does, it reads in the // root in that namespace and creates and adds it to our // list of known roots, if it doesn't already exist in our list. // //-------------------------------------------------------------------------- DFSSTATUS DfsRegistryStore::StoreRecognizer ( LPWSTR DfsNameContext, PUNICODE_STRING pLogicalShare ) { DFSSTATUS Status; Status = DfsIsThisAMachineName( DfsNameContext ); if (Status == ERROR_SUCCESS) { Status = StoreRecognizeNewDfs( DfsNameContext, pLogicalShare ); } DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "DfsRegistryStore:StoreRecognizer (remote), Status %x\n", Status); return Status; } DFSSTATUS DfsRegistryStore::LookupDotNetRootByName( LPWSTR ShareName, DfsRootFolder **ppRootFolder ) { DFSSTATUS Status = ERROR_SUCCESS; HKEY DfsKey = NULL; HKEY DfsRootKey = NULL; Status = GetNewStandaloneRegistryKey( NULL, FALSE, NULL, &DfsKey ); if (Status == ERROR_SUCCESS) { Status = RegOpenKeyEx( DfsKey, ShareName, 0, KEY_READ, &DfsRootKey ); RegCloseKey(DfsKey ); if (Status == ERROR_SUCCESS) { Status = GetRootFolder( NULL, ShareName, DfsRootKey, ppRootFolder ); RegCloseKey(DfsRootKey); } } return Status; } DFSSTATUS DfsRegistryStore::LookupOldRootByName( LPWSTR ShareName, DfsRootFolder **ppRootFolder ) { DFSSTATUS Status = ERROR_SUCCESS; HKEY DfsKey = NULL; HKEY DfsRootKey = NULL; UNICODE_STRING LogicalShare; Status = GetOldStandaloneRegistryKey( NULL, FALSE, NULL, &DfsRootKey ); if (Status == ERROR_SUCCESS) { Status = GetRootPhysicalShare( DfsRootKey, &LogicalShare); if (Status == ERROR_SUCCESS) { if (_wcsicmp(ShareName, LogicalShare.Buffer) != 0) { Status = ERROR_NOT_FOUND; ReleaseRootLogicalShare( &LogicalShare ); } } if(Status == ERROR_SUCCESS) { Status = MigrateDfs( L"" ); if(Status == ERROR_SUCCESS) { Status = GetRootFolder( L"", ShareName, &LogicalShare, &LogicalShare, ppRootFolder ); } ReleaseRootLogicalShare( &LogicalShare ); } if(DfsRootKey != NULL) { RegCloseKey( DfsRootKey ); } } return Status; } DFSSTATUS DfsRegistryStore::LookupNewRootByName( LPWSTR ShareName, DfsRootFolder **ppRootFolder ) { DFSSTATUS Status = ERROR_NOT_FOUND; if(DfsIsMachineCluster()) { Status = LookupOldRootByName (ShareName, ppRootFolder); if(Status != ERROR_SUCCESS) { Status = LookupDotNetRootByName (ShareName, ppRootFolder); } } return Status; } #if 0 //+------------------------------------------------------------------------- // // Function: StoreRecognizeOldStandaloneDfs - recognizer for single root dfs // // Arguments: Name - the namespace of interest. // HKEY OldDfsKey. // // Returns: Status // ERROR_SUCCESS on success // ERROR status code otherwise // // // Description: This routine reads a single root from the old standalone // location, and loads it in if the root exists. // //-------------------------------------------------------------------------- DFSSTATUS DfsRegistryStore::StoreRecognizeOldStandaloneDfs( LPWSTR MachineName, HKEY OldDfsKey ) { DfsRootFolder *pRootFolder = NULL; DFSSTATUS Status = ERROR_SUCCESS; DFSSTATUS RootStatus = ERROR_SUCCESS; UNICODE_STRING LogicalRoot; UNICODE_STRING DfsNameContext; UNREFERENCED_PARAMETER(MachineName); RtlZeroMemory(&LogicalRoot, sizeof(LogicalRoot)); // // Check if we have a root folder read from the old dfs location // ("domainroot"). If we have not seen the root already, and one // exists, a new root folder gets created. // Status = DfsGetRegValueString (OldDfsKey, // DfsRootShareValueName, // &LogicalRoot); Status = GetRootPhysicalShare(OldDfsKey,&LogicalRoot); if(Status == ERROR_SUCCESS) { RtlInitUnicodeStringEx( &DfsNameContext, NULL); // // 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, &LogicalRoot, &pRootFolder ); // // we did not find a root, so create a new one. // if ( Status != ERROR_SUCCESS ) { Status = MigrateStdDfs( L"", OldDfsKey, (LPWSTR) LogicalRoot.Buffer, (LPWSTR) LogicalRoot.Buffer, FALSE); if(Status == ERROR_SUCCESS) { // // Now get either an existing root by this name, // or create a new one. // RootStatus = GetRootFolder( L"", LogicalRoot.Buffer, &LogicalRoot, &LogicalRoot, &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 // all the reparse points that the root needs. Now we n // this volume as one that has DFS reparse points so th // to perform garbage collection on this volume later o // (VOID)pRootFolder->AddReparseVolumeToList(); } } } ReleaseRootPhysicalShare( &LogicalRoot ); } if(pRootFolder != NULL) { pRootFolder->ReleaseReference(); } return Status; } #endif #if 0 DFSSTATUS DfsRegistryStore::StoreRecognizeOldStandaloneDfs( LPWSTR MachineName, HKEY OldDfsKey ) { DfsRootFolder *pRootFolder = NULL; DFSSTATUS Status = ERROR_SUCCESS; // // Check if we have a root folder read from the old dfs location // ("domainroot"). If we have not seen the root already, and one // exists, a new root folder gets created. // Status = GetRootFolder( MachineName, NULL, OldDfsKey, &pRootFolder ); // // We got a root folder. call synchronize on it to update its contents. // The synchronize method fills the root with the most upto date // children data. // if (Status == ERROR_SUCCESS) { DFSSTATUS RootStatus; // // now acquire the root share directory. If this // fails, we continue our operation: we can continue // with synchonize and not create directories. // dfsdev:we need to post a eventlog or something when // we run into this. // RootStatus = pRootFolder->AcquireRootShareDirectory(); // // 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(); DFSLOG("Root folder for %ws, Synchronize status %x\n", RootStatus ); // Release our reference on the root folder. pRootFolder->ReleaseReference(); } return Status; } #endif //+------------------------------------------------------------------------- // // Function: CreateNewRootFolder - creates a new root folder // // Arguments: Name - the namespace of interest. // pPrefix - the logical share for this DFs. // ppRoot - the newly created root. // // // Returns: Status // ERROR_SUCCESS on success // ERROR status code otherwise // // // Description: This routine creates a new registry root folder, and // adds it to the list of known roots. // //-------------------------------------------------------------------------- DFSSTATUS DfsRegistryStore::CreateNewRootFolder ( LPWSTR MachineName, LPWSTR RootRegKeyName, PUNICODE_STRING pLogicalShare, PUNICODE_STRING pPhysicalShare, DfsRootFolder **ppRoot ) { DFSSTATUS Status = ERROR_SUCCESS; DfsRootFolder *pNewRoot = NULL; // // Create a new instance of the RegistryRootFolder class. // This gives us a reference RootFolder. // pNewRoot = new DfsRegistryRootFolder( MachineName, RootRegKeyName, pLogicalShare, pPhysicalShare, this, &Status ); if ( pNewRoot == NULL ) { Status = ERROR_NOT_ENOUGH_MEMORY; } if ( ERROR_SUCCESS == Status ) { // // AddRootFolder to the list of known roots. AddRootFolder // is responsible to acquire any reference on the root folder // if it is storing a reference to this root. Status = AddRootFolder( pNewRoot, NewRootList ); if ( ERROR_SUCCESS == Status ) { // // We were successful, return the reference root. The reference // that we are returning is the create reference on the new root. // *ppRoot = pNewRoot; } else { pNewRoot->ReleaseReference(); pNewRoot = NULL; } } else { if(pNewRoot != NULL) { pNewRoot->ReleaseReference(); pNewRoot = NULL; } } DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "RegistryStore::CreateNewRootFolder. New root %p, for root %wZ (%wZ) on machine %ws. Status %x\n", *ppRoot, pLogicalShare, pPhysicalShare, MachineName, Status); return Status; } //+------------------------------------------------------------------------- // // Function: GetMetadata- Gets the DFS metadata information // // Arguments: DfsMetadataKey - The key under which the information exists // RelativeName - the name of the subkey, having info of interest // RegistryValueNameString - The value name that holds the info. // ppData - the pointer that holds data buffer being returned // pDataSize - pointer to size of data being returned. // // Returns: Status // STATUS_SUCCESS if we could read the information, // error status otherwise. // // // Description: This routine reads the value of interest under the specified // key/subkey. The information is read in a buffer allocated // by this routine, and the buffer is returned to the caller. // It is the caller's responsibility to free this buffer when // done. // //-------------------------------------------------------------------------- DFSSTATUS DfsRegistryStore::GetMetadata ( IN HKEY DfsMetadataKey, IN LPWSTR RelativeName, IN LPWSTR RegistryValueNameString, OUT PVOID *ppData, OUT ULONG *pDataSize, OUT PFILETIME pLastModifiedTime) { HKEY NewKey = NULL; HKEY UseKey = NULL; PVOID pDataBuffer = NULL; ULONG DataSize, DataType; DFSSTATUS Status = ERROR_SUCCESS; // // If a relative name was passed in, we need to open a subkey under the // passed in key. Otherwise, we already have a key open to the information // of interest. // if ( RelativeName != NULL ) { Status = RegOpenKeyEx( DfsMetadataKey, RelativeName, 0, KEY_READ, &NewKey ); if ( Status == ERROR_SUCCESS ) { UseKey = NewKey; } else { DFS_TRACE_HIGH( REFERRAL_SERVER, "registry store, GetMetadata-RegOpenKeyEx %ws status=%d\n", RelativeName, Status); } } else { UseKey = DfsMetadataKey; } // // Get the largest size of any value under the key of interest, so we know // how much we need to allocate in the worst case. // (If a subkey has 3 values, this returns the maximum memory size required // to read any one of the values.) // if ( Status == ERROR_SUCCESS ) { Status = RegQueryInfoKey( UseKey, // Key NULL, // Class string NULL, // Size of class string NULL, // Reserved NULL, // # of subkeys NULL, // max size of subkey name NULL, // max size of class name NULL, // # of values NULL, // max size of value name &DataSize, // max size of value data, NULL, // security descriptor pLastModifiedTime ); // Last write time } // // We have the required size now: allocate a buffer for that size and // read the value we are interested in. // if ( Status == ERROR_SUCCESS ) { pDataBuffer = new BYTE [DataSize]; if ( pDataBuffer == NULL ) { Status = ERROR_NOT_ENOUGH_MEMORY; } else { Status = RegQueryValueEx( UseKey, RegistryValueNameString, NULL, &DataType, (LPBYTE)pDataBuffer, &DataSize ); // // If the format of data is not a certain type (usually binary type for DFS) // we have bogus data. // if ( (Status == ERROR_SUCCESS) && (DataType != DFS_REGISTRY_DATA_TYPE) ) { Status = ERROR_INVALID_DATA; } } } // // If we are successful in reading the value, pass the allcoated buffer and // size back to caller. Otherwise, free up the allocate buffer and return // error status to the caller. // if ( Status == ERROR_SUCCESS ) { *ppData = pDataBuffer; *pDataSize = DataSize; } else { if ( pDataBuffer != NULL ) { delete [] pDataBuffer; } } // // If we did open a new key, it is time to close it now. // if ( NewKey != NULL ) RegCloseKey(NewKey); DFS_TRACE_LOW( REFERRAL_SERVER, "registry store, GetMetadata-leaving %ws status=%d\n", RelativeName, Status); return Status; } //+------------------------------------------------------------------------- // // Function: SetMetadata- Sets the DFS metadata information // // Arguments: DfsMetadataKey - The key under which the information exists // RelativeName - the name of the subkey, to store info // RegistryValueNameString - The value name that holds the info. // pData - the pointer that holds data buffer // DataSize - Size of data // // Returns: Status // STATUS_SUCCESS if we could write the information, // error status otherwise. // // // Description: This routine writes the value of interest under the specified // key/subkey. // //-------------------------------------------------------------------------- DFSSTATUS DfsRegistryStore::SetMetadata ( IN HKEY DfsMetadataKey, IN LPWSTR RelativeName, IN LPWSTR RegistryValueNameString, IN PVOID pData, IN ULONG DataSize ) { HKEY UseKey = NULL; DFSSTATUS Status = ERROR_SUCCESS; // // If a relative name was passed in, we need to open a subkey under the // passed in key. Otherwise, we already have a key open to the information // of interest. // Status = RegOpenKeyEx( DfsMetadataKey, RelativeName, 0, KEY_READ | KEY_WRITE, &UseKey ); // // Store the value against the passed in value string // if (Status == ERROR_SUCCESS) { Status = RegSetValueEx( UseKey, RegistryValueNameString, NULL, DFS_REGISTRY_DATA_TYPE, (LPBYTE)pData, DataSize ); RegCloseKey(UseKey); } DFS_TRACE_LOW( REFERRAL_SERVER, "registry store, SetMetadata-RegOpenKeyEx %ws status=%d\n", RelativeName, Status); return Status; } //+------------------------------------------------------------------------- // // Function: GetRootKey - get the root key // // Arguments: Name - the namespace of interest. // pMachineContacted - did we contact the machine? // pDfsRootKey - the returned key. // // Returns: Status // ERROR_SUCCESS on success // ERROR status code otherwise // // // Description: This routine connects to the appropriate machine, and // opens the DFS metadata information in the registry. // If it succeeds, the machine hosts a registry DFs, and // the opened key is returned. // //-------------------------------------------------------------------------- DFSSTATUS DfsRegistryStore::GetRootKey( LPWSTR MachineName, LPWSTR ChildName, BOOLEAN *pMachineContacted, OUT PHKEY pDfsRootKey ) { DFSSTATUS Status; HKEY DfsStdRegistryKey; LPWSTR UseChildName = NULL; if (IsEmptyString(ChildName) == FALSE) { UseChildName = ChildName; } // // If there is no childname specified, we are dealing with the // old style dfs: a single root exists on the machine under "domainroot". // so open that key, and return it. // If a childname is specified, we are dealing with a migrated // dfs. Open the specified child in the new location. // if (UseChildName != NULL) { Status = GetNewStandaloneRegistryKey( MachineName, FALSE, pMachineContacted, &DfsStdRegistryKey ); if (Status == ERROR_SUCCESS) { // // We then open the required child key, and close // the parents key. // Status = RegOpenKeyEx( DfsStdRegistryKey, ChildName, 0, KEY_READ, pDfsRootKey ); RegCloseKey( DfsStdRegistryKey ); } } else { // // no name was specified. This must be the old standalone // child. (single root) // Status = GetOldStandaloneRegistryKey( MachineName, FALSE, pMachineContacted, pDfsRootKey ); } DFSLOG("DfsRegistryStore::GetDfsRootKey %ws Machine, Contacted %d Status %x\n", MachineName, pMachineContacted ? *pMachineContacted : 2, Status ); return Status; } //+------------------------------------------------------------------------- // // Function: GetDataRecoveryInformation - Gets recovery info // // Arguments: DfsMetadataKey - an open key to the registry. // RelativeName - Name of a subkey under the above key. // pRecoveryState - The recovery state we read. // // Returns: Status // STATUS_SUCCESS if we could read the information // error status otherwise. // // // Description: This routine read the RECOVERY value for the Root or link of // a dfs tree and returns with the value. // A non-zero state means that some recovery is required. // Note that there is more information in the RECOVERY value than // the state. Currently, we ignore that information and just return // the state. // In the future, we dont plan to use this value. // //-------------------------------------------------------------------------- DFSSTATUS DfsRegistryStore::GetDataRecoveryState ( IN HKEY DfsMetadataKey, IN LPWSTR RelativeName, OUT PULONG pRecoveryState ) { DFSSTATUS Status = ERROR_SUCCESS; PVOID DataBuffer = NULL, CurrentBuffer; ULONG DataSize, CurrentSize; Status = GetMetadata (DfsMetadataKey, RelativeName, DfsRegistryRecoveryString, &DataBuffer, &DataSize, NULL ); // we dont care about last mod time. // // We are currently interested in the first word in the data stream. // This holds the recovery state. // if ( Status == STATUS_SUCCESS ) { CurrentBuffer = DataBuffer; CurrentSize = DataSize; Status = PackGetULong( pRecoveryState, &CurrentBuffer, &CurrentSize ); } ReleaseMetadataBlob( DataBuffer ); // // If we could not read the value, then we return success and // and indicate state of 0. This is so that we can support roots // or links that dont have this value. // if ( Status != ERROR_SUCCESS ) { DFSLOG("DfsRegistryStore::DfsGetDataRecoveryState Status %x\n", Status); *pRecoveryState = 0; Status = ERROR_SUCCESS; } return Status; } INIT_STANDALONE_DFS_ID_PROPERTY_INFO(); //+------------------------------------------------------------------------- // // Function: PackGetNameInformation - Unpacks the root/link name info // // Arguments: pDfsNameInfo - 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 name // info structure (as defined by MiDfsIdProperty). If the stream // does not have the sufficient // info, ERROR_INVALID_DATA is returned back. // //-------------------------------------------------------------------------- DFSSTATUS DfsRegistryStore::PackGetNameInformation( IN PDFS_NAME_INFORMATION pDfsNameInfo, IN OUT PVOID *ppBuffer, IN OUT PULONG pSizeRemaining) { DFSSTATUS Status; // // Get the name information from the binary stream. // Status = PackGetInformation( (ULONG_PTR)pDfsNameInfo, ppBuffer, pSizeRemaining, &MiStdDfsIdProperty ); // // Immediately following the name value is a timeout value. // This is not in the MiDfsIdProperty because it appers that some // of the older DFS may not have the timeout value. // if ( Status == ERROR_SUCCESS ) { pDfsNameInfo->LastModifiedTime = pDfsNameInfo->PrefixTimeStamp; Status = PackGetULong(&pDfsNameInfo->Timeout, ppBuffer, pSizeRemaining); // // Use a default value if we cannot get the timeout value in // the stream. // if ( Status != ERROR_SUCCESS ) { pDfsNameInfo->Timeout = 300; // hard code this value. Status = ERROR_SUCCESS; } } if (Status == ERROR_SUCCESS) { if ((pDfsNameInfo->Type & 0x80) == 0x80) { pDfsNameInfo->State |= DFS_VOLUME_FLAVOR_STANDALONE; } } return Status; } //+------------------------------------------------------------------------- // // Function: PackSetNameInformation - Packs the root/link name info // // Arguments: pDfsNameInfo - 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 name info // error status otherwise. // // // Description: This routine takes the passedin name information and // stores it in the binary stream passed in. // //-------------------------------------------------------------------------- DFSSTATUS DfsRegistryStore::PackSetNameInformation( IN PDFS_NAME_INFORMATION pDfsNameInfo, IN OUT PVOID *ppBuffer, IN OUT PULONG pSizeRemaining) { DFSSTATUS Status; pDfsNameInfo->State &= ~DFS_VOLUME_FLAVORS; pDfsNameInfo->PrefixTimeStamp = pDfsNameInfo->LastModifiedTime; pDfsNameInfo->StateTimeStamp = pDfsNameInfo->LastModifiedTime; pDfsNameInfo->CommentTimeStamp = pDfsNameInfo->LastModifiedTime; // // Store the DfsNameInfo in the stream first. // Status = PackSetInformation( (ULONG_PTR)pDfsNameInfo, ppBuffer, pSizeRemaining, &MiStdDfsIdProperty ); // // Follow that info with the timeout information. // if ( Status == ERROR_SUCCESS ) { Status = PackSetULong( pDfsNameInfo->Timeout, ppBuffer, pSizeRemaining); } return Status; } //+------------------------------------------------------------------------- // // Function: PackSizeNameInformation - Gets the size of the name info. // // Arguments: pDfsNameInfo - info to size. // // Returns: Status // ULONG - size needed // // Description: This routine gets us the size of the binary stream // required to pack the passed in name info. // //-------------------------------------------------------------------------- ULONG DfsRegistryStore::PackSizeNameInformation( IN PDFS_NAME_INFORMATION pDfsNameInfo ) { ULONG Size; Size = PackSizeInformation( (ULONG_PTR)pDfsNameInfo, &MiStdDfsIdProperty ); Size += PackSizeULong(); return Size; } //+------------------------------------------------------------------------- // // Function: AddChild - Add a child to the metadata. // // Arguments: // DfsMetadataHandle - the Metadata key for the root. // PUNICODE_STRING pLinkLogicalName - the logical name of the child // LPWSTR ReplicaServer - the first target server for this link. // LPWSTR ReplicaPath - the target path for this link // LPWSTR Comment - the comment to be associated with this link. // LPWSTR pMetadataName - the metadata name for the child, returned.. // // // Returns: Status: // // Description: This routine adds a child to the Root metadata. It packs // the link name into the name information. If the replica // information exists, it packs that into the replica info. // It then saves the name and replica streams under the // Childkey. // NOTE: this function does not require that the link // have atleast one replica. Any such requirements // should be enforced by the caller. // //-------------------------------------------------------------------------- DFSSTATUS DfsRegistryStore::AddChild( IN DFS_METADATA_HANDLE DfsHandle, IN PDFS_NAME_INFORMATION pNameInfo, IN PDFS_REPLICA_LIST_INFORMATION pReplicaListInfo, IN PUNICODE_STRING pMetadataName ) { DFSSTATUS Status; PVOID pNameBlob, pReplicaBlob; ULONG NameBlobSize, ReplicaBlobSize; Status = CreateNameInformationBlob( pNameInfo, &pNameBlob, &NameBlobSize ); if (Status == ERROR_SUCCESS) { Status = CreateReplicaListInformationBlob( pReplicaListInfo, &pReplicaBlob, &ReplicaBlobSize ); if (Status == ERROR_SUCCESS) { Status = CreateNewChild( DfsHandle, pNameBlob, NameBlobSize, pReplicaBlob, ReplicaBlobSize, pMetadataName ); ReleaseMetadataReplicaBlob( pReplicaBlob, ReplicaBlobSize ); } ReleaseMetadataNameBlob( pNameBlob, NameBlobSize ); } return Status; } DFSSTATUS DfsRegistryStore::CreateNewChild( IN DFS_METADATA_HANDLE DfsHandle, IN PVOID pNameBlob, IN ULONG NameBlobSize, IN PVOID pReplicaBlob, IN ULONG ReplicaBlobSize, IN PUNICODE_STRING pMetadataName ) { HKEY DfsMetadataKey; DFSSTATUS Status; HKEY ChildKey; DfsMetadataKey = (HKEY)ExtractFromMetadataHandle( DfsHandle ); // // Now create the child with this name // Status = RegCreateKeyEx( DfsMetadataKey, pMetadataName->Buffer, 0, L"", REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &ChildKey, NULL ); // // Now set the name and replica information on this newly created // key. // if (Status == ERROR_SUCCESS) { Status = SetMetadata( ChildKey, NULL, DfsRegistryNameString, pNameBlob, NameBlobSize ); if (Status == ERROR_SUCCESS) { Status = SetMetadata( ChildKey, NULL, DfsRegistryReplicaString, pReplicaBlob, ReplicaBlobSize ); } // // we are done with the child key: close it here. // RegCloseKey (ChildKey); // // if we were unsuccessful in adding the name and // replica details, get rid of the new child registry entry. // if (Status != ERROR_SUCCESS) { DFSSTATUS DeleteStatus; DeleteStatus = RegDeleteKey( DfsMetadataKey, pMetadataName->Buffer ); DFSLOG("Created Key, but failed to add values %x. Delete Status %x\n", Status, DeleteStatus ); } } return Status; } DFSSTATUS DfsRegistryStore::RemoveChild( IN DFS_METADATA_HANDLE DfsHandle, LPWSTR ChildName ) { DFSSTATUS Status; HKEY DfsMetadataKey; DfsMetadataKey = (HKEY)ExtractFromMetadataHandle( DfsHandle ); Status = RegDeleteKey( DfsMetadataKey, ChildName ); return Status; } DFSSTATUS DfsRegistryStore::CreateStandaloneRoot( LPWSTR MachineName, LPWSTR LogicalShare, LPWSTR Comment ) { DFSSTATUS Status = ERROR_SUCCESS; HKEY StdDfsKey = NULL; HKEY StdDfsShareKey = NULL; DfsRootFolder *pRootFolder = NULL; PVOID pBlob = NULL; ULONG BlobSize = 0; DFS_NAME_INFORMATION NameInfo; DFS_REPLICA_LIST_INFORMATION ReplicaListInfo; DFS_REPLICA_INFORMATION ReplicaInfo; UNICODE_STRING DfsMachine; UNICODE_STRING DfsShare; DFS_TRACE_LOW( REFERRAL_SERVER, "registry store, create root %ws\n", LogicalShare); Status = DfsRtlInitUnicodeStringEx( &DfsMachine, MachineName ); if(Status != ERROR_SUCCESS) { Status = ERROR_INVALID_PARAMETER; DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "CreateStandaloneRoot, DfsRtlInitUnicodeStringEx - create root %ws, status %x\n", LogicalShare, Status); return Status; } Status = DfsRtlInitUnicodeStringEx( &DfsShare, LogicalShare ); if(Status != ERROR_SUCCESS) { Status = ERROR_INVALID_PARAMETER; DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "CreateStandaloneRoot, DfsRtlInitUnicodeStringEx - create root %ws, status %x\n", LogicalShare, Status); return Status; } RtlZeroMemory(&NameInfo, sizeof(DFS_NAME_INFORMATION)); if (DfsIsSpecialDomainShare(&DfsShare)) { Status = ERROR_INVALID_PARAMETER; DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "registry store, Special Share - create root %ws, status %x\n", LogicalShare, Status); return Status; } Status = LookupRoot( &DfsMachine, &DfsShare, &pRootFolder ); DFS_TRACE_LOW( REFERRAL_SERVER, "registry store, create root, lookup root %p, status %x\n", pRootFolder, Status); if (Status == ERROR_SUCCESS) { pRootFolder->ReleaseReference(); Status = ERROR_FILE_EXISTS; return Status; } Status = GetNewStandaloneRegistryKey( MachineName, TRUE, // write permission required NULL, &StdDfsKey ); if (Status == ERROR_SUCCESS) { UNICODE_STRING LogicalName; RtlInitUnicodeString( &LogicalName, LogicalShare ); StoreInitializeNameInformation( &NameInfo, &LogicalName, NULL, Comment ); NameInfo.Type |= 0x80; StoreInitializeReplicaInformation( &ReplicaListInfo, &ReplicaInfo, (MachineName == NULL) ? L"." : MachineName, LogicalShare ); } if (Status == ERROR_SUCCESS) { #if defined (DFS_FUTURES) // // Check if we need to store the root as a guid instead of // the root name itself. // It is probably not necessary, but for some reason if we need // to, we can scavenge this code. // // // get an unique metadata name for the root. // Status = DfsGenerateUuidString(&UuidString); #endif Status = RegCreateKeyEx( StdDfsKey, LogicalShare, 0, L"", REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &StdDfsShareKey, NULL ); if (Status == ERROR_SUCCESS) { size_t LogicalShareCchLength = 0; Status = DfsStringCchLength( LogicalShare, MAXUSHORT, &LogicalShareCchLength ); if (Status == ERROR_SUCCESS) { LogicalShareCchLength++; // NULL Terminator Status = RegSetValueEx( StdDfsShareKey, DfsRootShareValueName, 0, REG_SZ, (PBYTE)LogicalShare, LogicalShareCchLength * sizeof(WCHAR) ); } if (Status == ERROR_SUCCESS) { Status = RegSetValueEx( StdDfsShareKey, DfsLogicalShareValueName, 0, REG_SZ, (PBYTE)LogicalShare, LogicalShareCchLength * sizeof(WCHAR) ); } if (Status == ERROR_SUCCESS) { Status = CreateNameInformationBlob( &NameInfo, &pBlob, &BlobSize); if (Status == ERROR_SUCCESS) { Status = SetMetadata( StdDfsShareKey, NULL, DfsRegistryNameString, pBlob, BlobSize ); ReleaseMetadataNameBlob(pBlob, BlobSize ); } } if (Status == ERROR_SUCCESS) { Status = CreateReplicaListInformationBlob( &ReplicaListInfo, &pBlob, &BlobSize); if (Status == ERROR_SUCCESS) { Status = SetMetadata( StdDfsShareKey, NULL, DfsRegistryReplicaString, pBlob, BlobSize ); ReleaseMetadataNameBlob( pBlob, BlobSize ); } } if (Status == ERROR_SUCCESS) { Status = GetRootFolder( MachineName, LogicalShare, &DfsShare, &DfsShare, &pRootFolder ); if (Status == ERROR_SUCCESS) { // // Now, acquire the root share directory. If we fail // this, we deny the creation of the root. // acquire root share directory converts the root share // to a physical path, checks if that supports // reparse points, and if so tells the dfs driver // to attach to the drive. // if any of these fail, we cannot proceed. // Status = pRootFolder->AcquireRootShareDirectory(); if (Status != ERROR_SUCCESS) { // // make a best effort to remove ourselves // dont care about status return, thoug // we may want to log it. //dfsdev: add logging. // RemoveRootFolder(pRootFolder, TRUE ); // permanent removal } // // now mark the root folder as synchronized: // this is true since this root is empty. // if (Status == ERROR_SUCCESS) { pRootFolder->SetRootFolderSynchronized(); } pRootFolder->ReleaseReference(); } DFSLOG("Add Standalone Root, adding root folder status %x\n", Status); } RegCloseKey( StdDfsShareKey ); if (Status != ERROR_SUCCESS) { RegDeleteKey( StdDfsKey, LogicalShare ); } } #if defined (DFS_FUTURES) DfsReleaseUuidString(&UuidString); #endif RegCloseKey( StdDfsKey ); } DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "registry store, create root %ws, status %x\n", LogicalShare, Status); return Status; } DFSSTATUS DfsRegistryStore::DeleteStandaloneRoot( LPWSTR MachineName, LPWSTR LogicalShare ) { DFSSTATUS Status = ERROR_SUCCESS; HKEY DfsKey = NULL; DfsRootFolder *pRootFolder = NULL; LPWSTR RootRegistryName = NULL; LPWSTR UseChildName = NULL; UNICODE_STRING DfsMachine; UNICODE_STRING DfsShare; DFS_TRACE_LOW( REFERRAL_SERVER, "Delete Standalone root %ws\n", LogicalShare); Status = DfsRtlInitUnicodeStringEx( &DfsMachine, MachineName ); if(Status != ERROR_SUCCESS) { Status = ERROR_INVALID_PARAMETER; DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "DeleteStandaloneRoot, DfsRtlInitUnicodeStringEx - create root %ws, status %x\n", LogicalShare, Status); return Status; } Status = DfsRtlInitUnicodeStringEx( &DfsShare, LogicalShare ); if(Status != ERROR_SUCCESS) { Status = ERROR_INVALID_PARAMETER; DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "DeleteStandaloneRoot, DfsRtlInitUnicodeStringEx - create root %ws, status %x\n", LogicalShare, Status); return Status; } Status = LookupRoot( &DfsMachine, &DfsShare, &pRootFolder ); DFS_TRACE_LOW( REFERRAL_SERVER, "Delete Standalone root, lookup root %p, status %x\n", pRootFolder, Status); if (Status == ERROR_SUCCESS) { Status = pRootFolder->AcquireRootLock(); if (Status == ERROR_SUCCESS) { pRootFolder->SetRootFolderDeleteInProgress(); pRootFolder->ReleaseRootLock(); } } if (Status == ERROR_SUCCESS) { RootRegistryName = pRootFolder->GetRootRegKeyNameString(); UseChildName = (IsEmptyString(RootRegistryName) == TRUE) ? NULL : RootRegistryName; if (UseChildName == NULL) { Status = GetOldDfsRegistryKey( MachineName, TRUE, NULL, &DfsKey ); if (Status == ERROR_SUCCESS) { Status = SHDeleteKey( DfsKey, DfsOldStandaloneChild); RegCloseKey( DfsKey ); } } else { Status = GetNewStandaloneRegistryKey( MachineName, TRUE, NULL, &DfsKey ); if (Status == ERROR_SUCCESS) { Status = SHDeleteKey( DfsKey, UseChildName ); RegCloseKey( DfsKey ); } } } // // DfsDev: needs fixing. // if (Status == ERROR_SUCCESS) { NTSTATUS DeleteStatus; // // Release the root folder that we had acquired earlier for // this root. // DeleteStatus = RemoveRootFolder( pRootFolder, TRUE ); // permanent removal DFS_TRACE_ERROR_LOW( DeleteStatus, REFERRAL_SERVER, "remove root folder %p (%ws) status %x\n", pRootFolder, LogicalShare, DeleteStatus); DeleteStatus = pRootFolder->ReleaseRootShareDirectory(); DFS_TRACE_ERROR_LOW( DeleteStatus, REFERRAL_SERVER, "release root share for %ws status %x\n", LogicalShare, DeleteStatus); pRootFolder->ReleaseReference(); } DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "Delete Standalone root for %ws, done, status %x\n", LogicalShare, Status); return Status; } DFSSTATUS DfsRegistryStore::EnumerateApiLinks( IN DFS_METADATA_HANDLE DfsHandle, PUNICODE_STRING pRootName, DWORD Level, LPBYTE pBuffer, LONG BufferSize, LPDWORD pEntriesToRead, LPDWORD pResumeHandle, PLONG pSizeRequired ) { HKEY DfsKey = NULL; ULONG ChildNum = 0; DFSSTATUS Status = ERROR_SUCCESS; BOOLEAN OverFlow = FALSE; LONG HeaderSize = 0; LONG EntriesRead = 0; LONG EntriesToRead = *pEntriesToRead; LONG SizeRequired = 0; LONG EntryCount = 0; LPBYTE pLinkBuffer = NULL; LONG LinkBufferSize = 0; LPBYTE CurrentBuffer = NULL; LPBYTE NewBuffer = NULL; LONG SizeRemaining = 0; DFSSTATUS PackStatus = ERROR_SUCCESS; ULONG_PTR SizeDiff; DWORD CchMaxName = 0; DWORD CchChildName = 0; LPWSTR ChildName = NULL; Status = DfsApiSizeLevelHeader( Level, &HeaderSize ); if (Status != ERROR_SUCCESS) { return Status; } DfsKey = (HKEY)ExtractFromMetadataHandle( DfsHandle ); OverFlow = FALSE; SizeRequired = ROUND_UP_COUNT(EntriesToRead * HeaderSize, ALIGN_QUAD); if (EntriesToRead * HeaderSize < BufferSize ) { CurrentBuffer = (LPBYTE)((ULONG_PTR)pBuffer + EntriesToRead * HeaderSize); SizeRemaining = BufferSize - EntriesToRead * HeaderSize; } else { CurrentBuffer = pBuffer; SizeRemaining = 0; OverFlow = TRUE; } if(pResumeHandle) { EntryCount = *pResumeHandle; } if (EntryCount == 0) { Status = GetStoreApiInformationBuffer ( DfsHandle, pRootName, NULL, Level, &pLinkBuffer, &LinkBufferSize ); if (Status == ERROR_SUCCESS) { SizeRequired += ROUND_UP_COUNT(LinkBufferSize, ALIGN_QUAD); if (OverFlow == FALSE) { PackStatus = PackageEnumerationInfo( Level, EntriesRead, pLinkBuffer, pBuffer, &CurrentBuffer, &SizeRemaining ); if (PackStatus == ERROR_BUFFER_OVERFLOW) { OverFlow = TRUE; } NewBuffer = (LPBYTE)ROUND_UP_POINTER( CurrentBuffer, ALIGN_LPVOID); SizeDiff = (NewBuffer - CurrentBuffer); if ((LONG)SizeDiff > SizeRemaining) { SizeRemaining = 0; } else { SizeRemaining -= (LONG)SizeDiff; } CurrentBuffer = NewBuffer; } ReleaseStoreApiInformationBuffer( pLinkBuffer ); EntryCount++; EntriesRead++; } } if (Status != ERROR_SUCCESS ) { return Status; } ChildNum = EntryCount - 1; // // 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) { do { // // For each child, get the child name. // if (EntriesToRead && EntriesRead >= EntriesToRead) { break; } 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) { Status = GetStoreApiInformationBuffer( DfsHandle, pRootName, ChildName, Level, &pLinkBuffer, &LinkBufferSize ); if (Status == ERROR_SUCCESS) { SizeRequired += ROUND_UP_COUNT(LinkBufferSize, ALIGN_QUAD); if (OverFlow == FALSE) { PackStatus = PackageEnumerationInfo( Level, EntriesRead, pLinkBuffer, pBuffer, &CurrentBuffer, &SizeRemaining ); if (PackStatus == ERROR_BUFFER_OVERFLOW) { OverFlow = TRUE; } NewBuffer = (LPBYTE)ROUND_UP_POINTER( CurrentBuffer, ALIGN_LPVOID); SizeDiff = (NewBuffer - CurrentBuffer); if ((LONG)SizeDiff > SizeRemaining) { SizeRemaining = 0; } else { SizeRemaining -= (LONG)SizeDiff; } CurrentBuffer = NewBuffer; } ReleaseStoreApiInformationBuffer( pLinkBuffer ); EntryCount++; EntriesRead++; } } } while (Status == ERROR_SUCCESS); delete [] ChildName; } *pSizeRequired = SizeRequired; if (Status == ERROR_NO_MORE_ITEMS) { if (EntriesRead) { if (OverFlow) { Status = ERROR_BUFFER_OVERFLOW; } else { Status = ERROR_SUCCESS; } } } else if (OverFlow) { Status = ERROR_BUFFER_OVERFLOW; } if (Status == ERROR_SUCCESS) { if(pResumeHandle) { *pResumeHandle = EntryCount; } *pEntriesToRead = EntriesRead; } return Status; } DFSSTATUS DfsRegistryStore::GetMetadataNameBlob( DFS_METADATA_HANDLE RootHandle, LPWSTR MetadataName, PVOID *ppData, PULONG pDataSize, PFILETIME pLastModifiedTime ) { HKEY DfsMetadataKey; DFSSTATUS Status; DfsMetadataKey = (HKEY)ExtractFromMetadataHandle( RootHandle ); Status = GetMetadata( DfsMetadataKey, MetadataName, DfsRegistryNameString, ppData, pDataSize, pLastModifiedTime ); return Status; } DFSSTATUS DfsRegistryStore::GetMetadataReplicaBlob( DFS_METADATA_HANDLE RootHandle, LPWSTR MetadataName, PVOID *ppData, PULONG pDataSize, PFILETIME pLastModifiedTime ) { HKEY DfsMetadataKey; DFSSTATUS Status; DfsMetadataKey = (HKEY)ExtractFromMetadataHandle( RootHandle ); Status = GetMetadata( DfsMetadataKey, MetadataName, DfsRegistryReplicaString, ppData, pDataSize, pLastModifiedTime ); return Status; } DFSSTATUS DfsRegistryStore::SetMetadataNameBlob( DFS_METADATA_HANDLE RootHandle, LPWSTR MetadataName, PVOID pData, ULONG DataSize ) { HKEY DfsMetadataKey; DFSSTATUS Status; DfsMetadataKey = (HKEY)ExtractFromMetadataHandle( RootHandle ); Status = SetMetadata( DfsMetadataKey, MetadataName, DfsRegistryNameString, pData, DataSize ); return Status; } DFSSTATUS DfsRegistryStore::SetMetadataReplicaBlob( DFS_METADATA_HANDLE RootHandle, LPWSTR MetadataName, PVOID pData, ULONG DataSize ) { HKEY DfsMetadataKey; DFSSTATUS Status; DfsMetadataKey = (HKEY)ExtractFromMetadataHandle( RootHandle ); Status = SetMetadata( DfsMetadataKey, MetadataName, DfsRegistryReplicaString, pData, DataSize ); return Status; } // // This removes the target share from the // Microsoft\Dfs\Roots\Domain registry key. // DFSSTATUS DfsRegistryStore::CleanRegEntry( LPWSTR MachineName, LPWSTR LogicalShare ) { HKEY DfsKey; DFSSTATUS Status; DFSSTATUS RetStatus = ERROR_NOT_FOUND; HKEY DfsDriverKey; // // If we can open the old registry key, // then we assume we are dealing with an // old style single root standalone system. // Status = GetDfsRegistryKey (MachineName, DfsRegistryHostLocation, //DfsHost TRUE, NULL, &DfsKey ); // // Delete the \DfsHost\Volumes key and all its values. // if (Status == ERROR_SUCCESS) { Status = SHDeleteKey( DfsKey, DfsVolumesLocation ); RegCloseKey( DfsKey ); if (Status == ERROR_SUCCESS) { // Something succeeded. We'll return that. RetStatus = Status; // Delete the DfsDriver\LocalVolumes key as well. Status = GetDfsRegistryKey (MachineName, DfsRegistryDfsDriverLocation, //System\CCS\Services\DfsDriver TRUE, NULL, &DfsDriverKey ); // // Delete the \LocalVolumes key and all its values. // if (Status == ERROR_SUCCESS) { Status = SHDeleteKey( DfsDriverKey, DfsLocalVolumesValue ); RegCloseKey( DfsDriverKey ); // We don't bother to recreate the DfsHost\Volumes key on error. For all external // purposes the root should have disappeared now. } } } // // BUG 732833: Clean .Net location whether the Win2K location exists or not. // We need both locations to co-exist on mix-mode cluster machines. // Status = GetNewStandaloneRegistryKey( MachineName, TRUE, NULL, &DfsKey ); if (Status == ERROR_SUCCESS) { Status = SHDeleteKey( DfsKey, LogicalShare ); if (Status == ERROR_SUCCESS) { RetStatus = Status; } RegCloseKey( DfsKey ); } DFS_TRACE_LOW( REFERRAL_SERVER, "registry store, CleanRegistry %ws status=%d\n", MachineName, Status); return RetStatus; UNREFERENCED_PARAMETER( LogicalShare ); }