//+---------------------------------------------------------------------------- // // Copyright (C) 2000, Microsoft Corporation // // File: dfsCompCheck.c // // Contents: Checks if the existing DFS root is compatible with Windows XP server. // // History: Aug. 8 2001, Author: navjotv, some code picked up from other dfs source code files. // // //----------------------------------------------------------------------------- #include "dfsCompCheck.hxx" BOOLEAN IsNTFS( PFILE_FS_ATTRIBUTE_INFORMATION pInfo); LPWSTR DfsOldStandaloneChild = L"domainroot"; LPWSTR DfsOldRegistryLocation = L"SOFTWARE\\Microsoft\\DfsHost\\volumes"; LPWSTR DfsLogicalShareValueName = L"LogicalShare"; LPWSTR DfsRootShareValueName = L"RootShare"; //+------------------------------------------------------------------------- // // Function: CompatibilityCheck // // Arguments: CompatibilityCallback - function to call if a compatibility problem is found // Context // // Returns: TRUE on success // FALSE otherwise // // // Description: Check if existing DFS root will be supported after system upgarde. // //-------------------------------------------------------------------------- BOOLEAN APIENTRY CompatibilityCheck(PCOMPAIBILITYCALLBACK CompatibilityCallback, LPVOID Context) { DFSSTATUS Status = ERROR_SUCCESS; NTSTATUS NtStatus = STATUS_SUCCESS; HKEY OldStandaloneDfsKey; BOOLEAN MachineContacted = FALSE; ULONG pAttribInfoSize; PFILE_FS_ATTRIBUTE_INFORMATION pAttribInfo = NULL; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING DirectoryName; HANDLE DirHandle = NULL; UNICODE_STRING PhysicalShare; BOOLEAN ReturnVal = FALSE; COMPATIBILITY_ENTRY CompEntry; CompEntry.Description = L"The current DFS root hosted on this server will not be supported after this system upgrade"; CompEntry.HtmlName = L"compdata\\dfsComp.htm"; CompEntry.TextName = L"compdata\\dfsComp.txt"; CompEntry.RegKeyName = NULL; CompEntry.RegValName = NULL; CompEntry.RegValDataSize = NULL; CompEntry.RegValData = NULL; CompEntry.SaveValue = NULL; CompEntry.Flags = 0x00000000; CompEntry.InfName = NULL; CompEntry.InfSection = NULL; printf("\n dfsCompCheck:: Entering CompatibilityCheck"); Status = GetOldDfsRegistryKey( L"", FALSE, &MachineContacted, &OldStandaloneDfsKey ); // If we cn open this registry key, then we know that this machine //has old style DFS. if (Status == ERROR_SUCCESS) { Status = GetRootPhysicalShare(OldStandaloneDfsKey, &PhysicalShare); if (Status == ERROR_SUCCESS) { Status = DfsGetSharePath(L"", PhysicalShare.Buffer, &DirectoryName); if ( (DirectoryName.Buffer == NULL) || (DirectoryName.Length == 0) ) { Status = ERROR_INVALID_PARAMETER; } ReleaseRootPhysicalShare(&PhysicalShare); if (Status == ERROR_SUCCESS) { //why MAX_PATH?? pAttribInfoSize = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH; pAttribInfo = (PFILE_FS_ATTRIBUTE_INFORMATION) new BYTE [pAttribInfoSize]; if (pAttribInfo != NULL) { InitializeObjectAttributes ( &ObjectAttributes, &DirectoryName, OBJ_CASE_INSENSITIVE, //Attributes NULL, //Root handle NULL ); //Security descriptor. NtStatus = NtOpenFile( &DirHandle, (ACCESS_MASK)FILE_LIST_DIRECTORY | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT ); if (NtStatus == STATUS_SUCCESS) { // // Query for the basic information, which has the attributes. // NtStatus = NtQueryVolumeInformationFile( DirHandle, &IoStatusBlock, pAttribInfo, pAttribInfoSize, FileFsAttributeInformation ); if (NtStatus == STATUS_SUCCESS) { // // If the attributes indicate reparse point, we have a reparse // point directory on our hands. // if ( (pAttribInfo->FileSystemAttributes & FILE_SUPPORTS_REPARSE_POINTS) == 0 && !IsNTFS(pAttribInfo)) { NtStatus = STATUS_NOT_SUPPORTED; CompatibilityCallback(&CompEntry, Context); ReturnVal = FALSE; } else { ReturnVal = TRUE; } } CloseHandle (DirHandle); delete [] pAttribInfo; } } } } } else { //Does not have old DFS ReturnVal = TRUE; } return ReturnVal; } //+------------------------------------------------------------------------- // // Function: GetOldStandaloneRegistryKey // // Arguments: // // Returns: SUCCESS or error // // Description: Checks if Old Standalone DFS registry key exists. // // //-------------------------------------------------------------------------- static DFSSTATUS GetOldStandaloneRegistryKey( IN LPWSTR MachineName, BOOLEAN WritePermission, OUT BOOLEAN *pMachineContacted, OUT PHKEY pDfsRegKey ) { DFSSTATUS Status; HKEY DfsKey; Status = GetOldDfsRegistryKey (MachineName, WritePermission, pMachineContacted, &DfsKey ); if (Status == ERROR_SUCCESS) { Status = RegOpenKeyEx( DfsKey, DfsOldStandaloneChild, 0, KEY_READ | (WritePermission ? KEY_WRITE : 0), pDfsRegKey ); RegCloseKey( DfsKey); } return Status; } //+------------------------------------------------------------------------- // // Function: GetOldDfsRegistryKey // // Arguments: // // Returns: SUCCESS or error // // Description: Checks if Old DFS registry key exists. // // //-------------------------------------------------------------------------- static DFSSTATUS GetOldDfsRegistryKey( IN LPWSTR MachineName, BOOLEAN WritePermission, OUT BOOLEAN *pMachineContacted, OUT PHKEY pDfsRegKey ) { return GetDfsRegistryKey (MachineName, DfsOldRegistryLocation, WritePermission, pMachineContacted, pDfsRegKey ); } //+------------------------------------------------------------------------- // // Function: GetDfsRegistryKey // // Arguments: // // Returns: SUCCESS or error // // Description: // Function GetDfsRegistryKey: This function takes a Name as the input, // and looks up all DFS roots in that namespace. // //-------------------------------------------------------------------------- static 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; } //+------------------------------------------------------------------------- // // Function: GetRootPhysicalShare // // Arguments: // // Returns: SUCCESS or error // // Description: Gets the value of the DFS root share // // //-------------------------------------------------------------------------- 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, DfsRootShareValueName, 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; } //+------------------------------------------------------------------------- // // Function: ReleaseRootPhysicalShare // // Arguments: // // Returns: // // Description: Releases memory for pRootPhysicalShare->Buffer. // // //-------------------------------------------------------------------------- VOID ReleaseRootPhysicalShare( PUNICODE_STRING pRootPhysicalShare ) { delete [] pRootPhysicalShare->Buffer; } //+------------------------------------------------------------------------- // // Function: DfsGetSharePath // // Arguments: ServerName - the name of the server // ShareName - the name of the share // pPathName - the unicode string representing the NT name // of the local path representing the share // // Returns: SUCCESS or error // // Description: This routine takes a servername and a sharename, and // returns an NT pathname to the physical resource that is // backing the share name. // //-------------------------------------------------------------------------- DFSSTATUS DfsGetSharePath( IN LPWSTR ServerName, IN LPWSTR ShareName, OUT PUNICODE_STRING pPathName ) { LPWSTR UseServerName = NULL; ULONG InfoLevel = 2; PSHARE_INFO_2 pShareInfo; NET_API_STATUS NetStatus; DFSSTATUS Status; UNICODE_STRING NtSharePath; if (IsEmptyString(ServerName) == FALSE) { UseServerName = ServerName; } NetStatus = NetShareGetInfo( UseServerName, ShareName, InfoLevel, (LPBYTE *)&pShareInfo ); if (NetStatus != ERROR_SUCCESS) { Status = (DFSSTATUS)NetStatus; return Status; } if( RtlDosPathNameToNtPathName_U(pShareInfo->shi2_path, &NtSharePath, NULL, NULL ) == TRUE ) { Status = DfsCreateUnicodeString( pPathName, &NtSharePath ); RtlFreeUnicodeString( &NtSharePath ); } else { Status = ERROR_NOT_ENOUGH_MEMORY; } NetApiBufferFree( pShareInfo ); return Status; } //+------------------------------------------------------------------------- // // Function: DfsCreateUnicodeString // // Arguments: pDest - the destination unicode string // pSrc - the source unicode string // // Returns: SUCCESS or error // // Description: This routine creates a new unicode string that is a copy // of the original. The copied unicode string has a buffer // that is null terminated, so the buffer can be used as a // normal string if necessary. // //-------------------------------------------------------------------------- DFSSTATUS DfsCreateUnicodeString( PUNICODE_STRING pDest, PUNICODE_STRING pSrc ) { LPWSTR NewString; NewString = (LPWSTR) malloc(pSrc->Length + sizeof(WCHAR)); if ( NewString == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } RtlCopyMemory( NewString, pSrc->Buffer, pSrc->Length); NewString[ pSrc->Length / sizeof(WCHAR)] = UNICODE_NULL; RtlInitUnicodeString( pDest, NewString ); return ERROR_SUCCESS; } //+------------------------------------------------------------------------- // // Function: IsNTFS // // Arguments: pInfo // // Returns: True - if NTFS // False - if not NTFS // // Description: Checks if filesystem is NTFS // //-------------------------------------------------------------------------- BOOLEAN IsNTFS( PFILE_FS_ATTRIBUTE_INFORMATION pInfo) { if (pInfo->FileSystemNameLength == 8 && pInfo->FileSystemName[0] == 'N' && pInfo->FileSystemName[1] == 'T' && pInfo->FileSystemName[2] == 'F' && pInfo->FileSystemName[3] == 'S') return TRUE; else return FALSE; }