//-------------------------------------------------------------------------- // // Copyright (C) 1999, Microsoft Corporation // // File: misc.cxx // //-------------------------------------------------------------------------- #define UNICODE 1 extern "C" { #include #include #include #include #include #include #include #include #include #include #include #include #include #include } #include #include #include "struct.hxx" #include "flush.hxx" #include "misc.hxx" #include "messages.h" #include #include #include #include "dfspathname.hxx" DFSSTATUS GetWin2kStandaloneMetadata ( IN HKEY DfsMetadataKey, IN LPWSTR RelativeName, IN LPWSTR RegistryValueNameString, OUT PDFS_NAME_INFORMATION pNameInfo); DFSSTATUS SetWin2kStandaloneMetadata ( IN HKEY DfsMetadataKey, IN LPWSTR RelativeName, IN LPWSTR RegistryValueNameString, IN PVOID pData, IN ULONG DataSize ); DFSSTATUS GetDfsRegistryKey( IN LPWSTR MachineName, IN LPWSTR LocationString, BOOLEAN WritePermission, OUT BOOLEAN *pMachineContacted, OUT PHKEY pDfsRegKey ); DFSSTATUS GetRootPhysicalShare( HKEY RootKey, PUNICODE_STRING pRootPhysicalShare ); DFSSTATUS GetRootPhysicalShare( HKEY RootKey, PUNICODE_STRING pRootPhysicalShare ) { DFSSTATUS Status; ULONG DataSize, DataType; LPWSTR DfsRootShare = NULL; Status = RegQueryInfoKey( RootKey, // 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 NULL ); // Last write time if (Status == ERROR_SUCCESS) { DfsRootShare = (LPWSTR) new BYTE [DataSize]; if ( DfsRootShare == NULL ) { Status = ERROR_NOT_ENOUGH_MEMORY; } else { Status = RegQueryValueEx( RootKey, DFS_REG_ROOT_SHARE_VALUE, NULL, &DataType, (LPBYTE)DfsRootShare, &DataSize ); } } if (Status == ERROR_SUCCESS) { if (DataType == REG_SZ) { RtlInitUnicodeString( pRootPhysicalShare, DfsRootShare ); } else { Status = STATUS_INVALID_PARAMETER; } } if (Status != ERROR_SUCCESS) { if (DfsRootShare != NULL) { delete [] DfsRootShare; } } return Status; } DFSSTATUS GetDfsRegistryKey( IN LPWSTR MachineName, IN LPWSTR LocationString, BOOLEAN WritePermission, OUT BOOLEAN *pMachineContacted, OUT PHKEY pDfsRegKey ) { DFSSTATUS Status; HKEY RootKey; BOOLEAN Contacted = FALSE; LPWSTR UseMachineName = NULL; REGSAM DesiredAccess = KEY_READ; if (WritePermission == TRUE) { DesiredAccess |= KEY_WRITE; } if (IsEmptyString(MachineName) == FALSE) { UseMachineName = MachineName; } Status = RegConnectRegistry( UseMachineName, HKEY_LOCAL_MACHINE, &RootKey ); if ( Status == ERROR_SUCCESS ) { Contacted = TRUE; Status = RegOpenKeyEx( RootKey, LocationString, 0, DesiredAccess, pDfsRegKey ); // // There appears to be a bug in the registry code. When // we connect to the local machine, the key that is returned // in the RegConnectRegistry is HKEY_LOCAL_MACHINE. If we // then attempt to close it here, it affects other threads // that are using this code: they get STATUS_INVALID_HANDLE // in some cases. So, dont close the key if it is // HKEY_LOCAL_MACHINE. // if (RootKey != HKEY_LOCAL_MACHINE) { RegCloseKey( RootKey ); } } if (pMachineContacted != NULL) { *pMachineContacted = Contacted; } return Status; } DFSSTATUS 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 ); pBlob = (PVOID) new BYTE[BlobSize]; if (pBlob == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } pUseBlob = pBlob; UseBlobSize = BlobSize; // Pack the name information into the binary stream allocated. // Status = PackSetStandaloneNameInformation( pDfsNameInfo, &pUseBlob, &UseBlobSize ); if (Status != ERROR_SUCCESS) { delete [] pBlob; pBlob = NULL; } if (Status == ERROR_SUCCESS) { *ppBlob = pBlob; *pDataSize = BlobSize - UseBlobSize; } return Status; } DFSSTATUS DfsSetWin2kStdNameInfo( HKEY DomainRootKey, LPWSTR LinkName, PDFS_NAME_INFORMATION pNameInfo) { DFSSTATUS Status = ERROR_SUCCESS; PVOID pBlob = NULL; ULONG BlobSize = 0; Status = CreateNameInformationBlob( pNameInfo, &pBlob, &BlobSize ); if (Status != ERROR_SUCCESS) { DebugInformation((L"Error 0x%x creating registry blob with NameInfo\n", Status)); return Status; } if (Status == ERROR_SUCCESS) { Status = SetWin2kStandaloneMetadata( DomainRootKey, LinkName, // Can be NULL for roots L"ID", pBlob, BlobSize ); if (Status == ERROR_SUCCESS) { DebugInformation((L"Successfully wrote changes to the Windows2000 standalone root\n")); } else { DebugInformation((L"Error 0x%x writing changes to the Windows2000 standalone root\n", Status)); } } delete [] pBlob; return Status; } #if 0 DFSSTATUS DfsGetWin2kStdReplicaInfo( LPWSTR MachineName, PDFS_REPLICA_LIST_INFORMATION pReplicaInfo) { DFSSTATUS Status = ERROR_SUCCESS; HKEY VolumesKey = NULL; BOOLEAN MachineContacted = FALSE; PVOID pBlob = NULL; ULONG BlobSize = 0; Status = GetDfsRegistryKey (MachineName, DFS_REG_OLD_HOST_LOCATION, FALSE, &MachineContacted, &VolumesKey ); if (Status != ERROR_SUCCESS) { return Status; } Status = GetWin2kStandaloneMetadata( VolumesKey, DFS_REG_OLD_STANDALONE_CHILD, L"Svc", &pBlob, &BlobSize ); RegCloseKey( VolumesKey ); RtlZeroMemory (pReplicaInfo, sizeof(DFS_REPLICA_LIST_INFORMATION)); Status = PackGetULong( &pReplicaInfo->ReplicaCount, &pBlob, &BlobSize ); if (Status == ERROR_SUCCESS) { pReplicaInfo->pReplicas = new DFS_REPLICA_INFORMATION[ pReplicaInfo->ReplicaCount]; if ( pReplicaInfo->pReplicas != NULL ) { Status = PackGetReplicaInformation( pReplicaInfo, &pBlob, &BlobSize ); } else { Status = ERROR_NOT_ENOUGH_MEMORY; } } if (Status == ERROR_SUCCESS) { DumpReplicaInformation( pReplicaInfo ); delete [] pReplicaInfo->pReplicas; } return Status; } #endif DFSSTATUS DfsGetWin2kStdLinkNameInfo( HKEY DomainRootKey, PUNICODE_STRING pLinkName, PBOOLEAN pLinkFound, LPWSTR *pChildGuidName, PDFS_NAME_INFORMATION pNameInfo) { DFSSTATUS Status = ERROR_SUCCESS; ULONG ChildNum = 0; DWORD CchMaxName = 0; DWORD CchChildName = 0; LPWSTR ChildName = NULL; *pLinkFound = FALSE; // // First find the length of the longest subkey // and allocate a buffer big enough for it. // Status = RegQueryInfoKey( DomainRootKey, // 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) { // Space for the NULL terminator. CchMaxName++; ChildName = (LPWSTR) new WCHAR [CchMaxName]; if (ChildName == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; } } do { // // For each child, get the child name. // CchChildName = CchMaxName; Status = RegEnumKeyEx( DomainRootKey, ChildNum, ChildName, &CchChildName, NULL, NULL, NULL, NULL ); ChildNum++; // // Read in the child link and see if that's the link we need. // if ( Status == ERROR_SUCCESS ) { UNICODE_STRING PrefixName; Status = GetWin2kStandaloneMetadata( DomainRootKey, ChildName, L"ID", pNameInfo); if (Status == ERROR_SUCCESS) { DumpNameInformation( pNameInfo ); // Skip the leading '\'s if any. PrefixName = pNameInfo->Prefix; while (PrefixName.Buffer[0] == UNICODE_PATH_SEP) { PrefixName.Buffer++; PrefixName.Length -= sizeof(WCHAR); } if (RtlCompareUnicodeString( &PrefixName, pLinkName, TRUE ) == 0) { *pLinkFound = TRUE; *pChildGuidName = ChildName; Status = ERROR_SUCCESS; break; } else { // Go on to the next link DebugInformation((L"Found W2k Link %wZ, doesn't match %wZ\n", &PrefixName, pLinkName)); } } } } while ( Status == ERROR_SUCCESS ); if (*pLinkFound == FALSE) { delete [] ChildName; Status = ERROR_PATH_NOT_FOUND; } return Status; } DFSSTATUS DfsGetWin2kStdNameInfo( HKEY VolumesKey, HKEY DomainRootKey, PUNICODE_STRING pShareName, PDFS_NAME_INFORMATION pNameInfo) { DFSSTATUS Status = ERROR_SUCCESS; UNICODE_STRING W2kRootName; // Look at DfsHost\volumes-> RootShare key to get at the root name. Status = GetRootPhysicalShare( VolumesKey, &W2kRootName ); if (Status == ERROR_SUCCESS) { // We need to find a matching share. Win2k has only one root. if (RtlCompareUnicodeString( &W2kRootName, pShareName, TRUE ) == 0) { Status = GetWin2kStandaloneMetadata( DomainRootKey, NULL, L"ID", pNameInfo); } else { Status = ERROR_PATH_NOT_FOUND; DebugInformation((L"Found W2k root %wZ, doesn't match %wZ\n", &W2kRootName, pShareName)); } delete [] W2kRootName.Buffer; } return Status; } DFSSTATUS GetWin2kStandaloneMetadata ( IN HKEY DfsMetadataKey, IN LPWSTR RelativeName, IN LPWSTR RegistryValueNameString, OUT PDFS_NAME_INFORMATION pNameInfo) { 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 NULL ); // 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, // eg. "ID" 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 ) { Status = PackGetStandaloneNameInformation( pNameInfo, &pDataBuffer, &DataSize ); } if ( pDataBuffer != NULL ) { delete [] pDataBuffer; pDataBuffer = NULL; } // // If we did open a new key, it is time to close it now. // if ( NewKey != NULL ) RegCloseKey(NewKey); return Status; } DFSSTATUS SetWin2kStandaloneMetadata ( IN HKEY DomainRootKey, IN LPWSTR RelativeName, IN LPWSTR RegistryValueNameString, IN PVOID pData, IN ULONG DataSize ) { HKEY UseKey = DomainRootKey; 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) { Status = RegOpenKeyEx( DomainRootKey, 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 ); if (UseKey != DomainRootKey) RegCloseKey(UseKey); } return Status; } // // Look at the Win2k standalone location on the remote // machine to see if it has a matching root. If this is a // set operation, the Type info will be changed. Else this // returns existing Type attribute in NameInformation. // DFSSTATUS DfsExtendedWin2kRootAttributes( DfsPathName *Namespace, PULONG pAttr, BOOLEAN Set) { DFSSTATUS Status = ERROR_SUCCESS; DFS_NAME_INFORMATION NameInfo; BOOLEAN PathFound = FALSE; LPWSTR ChildGuidName = NULL; HKEY VolumesKey = NULL; HKEY DomainRootKey = NULL; // Open DfsHost\volumes Status = GetDfsRegistryKey (Namespace->GetServerString(), DFS_REG_OLD_HOST_LOCATION, TRUE, // RW NULL, &VolumesKey ); if (Status != ERROR_SUCCESS) { Status = ERROR_PATH_NOT_FOUND; return Status; } Status = RegOpenKeyEx( VolumesKey, DFS_REG_OLD_STANDALONE_CHILD, // 'domainroot', 0, KEY_READ|KEY_WRITE, &DomainRootKey ); if (Status != ERROR_SUCCESS) { RegCloseKey( VolumesKey ); Status = ERROR_PATH_NOT_FOUND; return Status; } do { DebugInformation((L"Attempting to get Windows2000 standalone root information for %wZ\n", Namespace->GetPathCountedString())); Status = DfsGetWin2kStdNameInfo( VolumesKey, DomainRootKey, Namespace->GetShareCountedString(), &NameInfo ); if (Status != ERROR_SUCCESS) { Status = ERROR_PATH_NOT_FOUND; break; } //DumpNameInformation( &NameInfo ); // We've matched the root name so far. See if we need to find the link component. if (!IsEmptyUnicodeString( Namespace->GetRemainingCountedString() )) { Status = DfsGetWin2kStdLinkNameInfo( DomainRootKey, Namespace->GetFolderCountedString(), &PathFound, &ChildGuidName, &NameInfo ); if (!PathFound) { Status = ERROR_PATH_NOT_FOUND; break; } } if (!Set) { *pAttr = NameInfo.Type & PKT_ENTRY_TYPE_EXTENDED_ATTRIBUTES; } else { NameInfo.Type &= ~PKT_ENTRY_TYPE_EXTENDED_ATTRIBUTES; NameInfo.Type |= *pAttr; //DebugInformation((L"New name information type flags will be 0x%x\n", NameInfo.Type)); Status = DfsSetWin2kStdNameInfo( DomainRootKey, ChildGuidName, // NULL for roots &NameInfo ); } } while (FALSE); RegCloseKey( DomainRootKey ); RegCloseKey( VolumesKey ); return Status; }