2025-04-27 07:49:33 -04:00

2767 lines
87 KiB
C++

//+----------------------------------------------------------------------------
//
// Copyright (C) 2000, Microsoft Corporation
//
// File: DfsRootFolder.cxx
//
// Contents: implements the base DFS Folder class
//
// Classes: DfsRootFolder.
//
// History: Dec. 8 2000, Author: udayh
//
//-----------------------------------------------------------------------------
#include "DfsRootFolder.hxx"
#include "dfsfilterapi.hxx"
#include "rpc.h"
#include "rpcdce.h"
#include "DfsStore.hxx"
#include "DelegationControl.hxx"
//
// logging includes.
//
#include "DfsRootFolder.tmh"
#define FILETIMETO64(_f) (*(UINT64 *)(&(_f)))
//+-------------------------------------------------------------------------
//
// Function: DfsRootFolder - Contstruct for the rootFolder class
//
// Arguments: NameContext - the Dfs Name context
// pLogicalShare - the Logical Share name
// ObType - the object type. Set to the derived class type.
// pStatus - status of this call.
//
// Returns: NONE
//
// Description: This routine initializes the class variables of the
// the root folder, and initialize the name context and
// logical share name to the passed in values.
// It also allocated and initializes the lock for the root
// folder, as well as all the locks that will be assigned
// to the child folders.
// We then create a metadata name table and a logical namespace
// prefix table.
//
//--------------------------------------------------------------------------
DfsRootFolder::DfsRootFolder(
IN LPWSTR NameContext,
IN LPWSTR RootRegKeyNameString,
IN PUNICODE_STRING pLogicalShare,
IN PUNICODE_STRING pPhysicalShare,
IN DfsObjectTypeEnumeration ObType,
OUT DFSSTATUS *pStatus ) : DfsFolder (NULL,
NULL,
ObType )
{
ULONG LockNum;
DFSSTATUS Status = ERROR_SUCCESS;
RtlInitUnicodeString( &_DfsNameContext, NULL );
RtlInitUnicodeString( &_LogicalShareName, NULL );
RtlInitUnicodeString( &_RootRegKeyName, NULL );
RtlInitUnicodeString( &_PhysicalShareName, NULL );
RtlInitUnicodeString( &_ShareFilePathName, NULL );
RtlInitUnicodeString( &_DirectoryCreateRootPathName, NULL );
RtlInitUnicodeString( &_DfsVisibleContext, NULL );
_DirectoryCreateError = STATUS_SUCCESS;
_pMetadataNameTable = NULL;
_pLogicalPrefixTable = NULL;
_IgnoreNameContext = FALSE;
_CreateDirectories = FALSE;
pStatistics = NULL;
_pChildLocks = NULL;
_ChildCount = 0;
_CurrentErrors = 0;
_RootFlags = 0;
Status = DfsCreateUnicodeStringFromString( &_DfsNameContext, NameContext );
if ( Status == ERROR_SUCCESS )
{
DfsGetNetbiosName( &_DfsNameContext, &_DfsNetbiosNameContext, NULL );
Status = DfsCreateUnicodeString( &_LogicalShareName, pLogicalShare );
}
if ( Status == ERROR_SUCCESS )
{
Status = DfsCreateUnicodeStringFromString( &_RootRegKeyName,
RootRegKeyNameString );
}
if ( Status == ERROR_SUCCESS )
{
Status = DfsCreateUnicodeString( &_PhysicalShareName,
pPhysicalShare );
}
if ( Status == ERROR_SUCCESS )
{
pStatistics = new DfsStatistics();
if (pStatistics == NULL)
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
}
if ( Status == ERROR_SUCCESS )
{
_pRootLock = new CRITICAL_SECTION;
if ( _pRootLock == NULL )
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
}
if ( Status == ERROR_SUCCESS )
{
InitializeCriticalSection( _pRootLock );
}
if ( Status == ERROR_SUCCESS )
{
_pLock = new CRITICAL_SECTION;
if ( _pLock == NULL )
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
}
if ( Status == ERROR_SUCCESS )
{
InitializeCriticalSection( _pLock );
_Flags = DFS_FOLDER_ROOT;
//
// Allocate the child locks, and initiailize them.
//
_pChildLocks = new CRITICAL_SECTION[ NUMBER_OF_SHARED_LINK_LOCKS ];
if ( _pChildLocks != NULL )
{
for ( LockNum = 0; LockNum < NUMBER_OF_SHARED_LINK_LOCKS; LockNum++ )
{
InitializeCriticalSection( &_pChildLocks[LockNum] );
}
} else
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
}
//
// Initialize the prefix and nametable for this root.
//
if ( Status == ERROR_SUCCESS )
{
Status = DfsInitializePrefixTable( &_pLogicalPrefixTable,
FALSE,
NULL );
}
if ( Status == ERROR_SUCCESS )
{
Status = DfsInitializeNameTable( 0, &_pMetadataNameTable );
}
//
// We have not assigned any of the child locks: set the lock index
// to 0. This index provides us a mechanism of allocating locks
// to the child folders in a round robin way.
//
_ChildLockIndex = 0;
_LocalCreate = FALSE;
pPrevRoot = pNextRoot = NULL;
*pStatus = Status;
}
//+-------------------------------------------------------------------------
//
// Function: CreateLinkFolder - Create a DfsFolder and initialize it.
//
// Arguments: ChildName - metadata name of the child
// pLinkName - the logical namespace name, relative to root
// ppChildFolder - the returned child folder
//
// Returns: Status: Success or error status
//
// Description: This routine Creates a link folder and adds it to the
// parent Root's table.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::CreateLinkFolder(
IN LPWSTR ChildName,
IN PUNICODE_STRING pLinkName,
OUT DfsFolder **ppChildFolder )
{
DfsFolder *pChildFolder = NULL;
DFSSTATUS Status = ERROR_SUCCESS;
const TCHAR * apszSubStrings[4];
DFS_TRACE_LOW( REFERRAL_SERVER, "Create Link Folder: MetaName %ws, Link %wZ\n",
ChildName, pLinkName );
//
// Create a new child folder. Allocate a lock for this child
// and pass the lock along to the Folder constructor.
//
pChildFolder = new DfsFolder (this,
GetChildLock() );
if ( pChildFolder == NULL )
{
Status = ERROR_NOT_ENOUGH_MEMORY;
} else
{
//
// We successfully created the folder. Now set the metadata
// and logical name of the child folder.
//
Status = pChildFolder->InitializeMetadataName( ChildName );
if ( Status == ERROR_SUCCESS )
{
Status = pChildFolder->InitializeLogicalName( pLinkName );
}
}
if ( Status == ERROR_SUCCESS )
{
//
// We now acquire the child folder's write lock, and insert
// the child into the parent's metadata and logical namespace
// tables.
// When adding/removing the child in one of these tables,
// it is necessary to acquire the child folder lock since
// we are setting state in the folder indicating whether the
// child is in any of these tables.
//
Status = pChildFolder->AcquireWriteLock();
if ( Status == ERROR_SUCCESS )
{
Status = InsertLinkFolderInMetadataTable( pChildFolder );
if ( Status == ERROR_SUCCESS )
{
IncrementChildCount();
Status = InsertLinkFolderInLogicalTable( pChildFolder );
}
pChildFolder->ReleaseLock();
}
}
if (Status == ERROR_SUCCESS)
{
DFSSTATUS CreateStatus;
CreateStatus = SetupLinkReparsePoint( pChildFolder->GetFolderLogicalNameString() );
if(CreateStatus != STATUS_SUCCESS)
{
CreateStatus = RtlNtStatusToDosError(CreateStatus);
apszSubStrings[0] = GetFolderLogicalNameString();
apszSubStrings[1] = GetDirectoryCreatePathName()->Buffer;
GenerateEventLog(DFS_ERROR_CREATE_REPARSEPOINT_FAILURE,
2,
apszSubStrings,
CreateStatus);
}
DFS_TRACE_ERROR_LOW(CreateStatus, REFERRAL_SERVER, "Setup link reparse point child %p, link %wZ, Status %x\n",
pChildFolder, pLinkName, Status);
}
//
// If we are successful, return the newly created child folder.
// We currently have a reference on the folder (the reference on
// the folder when the folder was created)
//
// If we encountered an error, and the childFolder has been created,
// get rid of out reference on this folder. This will usually
// destroy the childFolder.
//
//
if ( Status == ERROR_SUCCESS )
{
*ppChildFolder = pChildFolder;
pStatistics->UpdateLinkAdded();
} else
{
if ( pChildFolder != NULL )
{
pChildFolder->ReleaseReference();;
}
}
DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "Create Link Folder: MetaName %ws, Child %p Status %x\n",
ChildName, pChildFolder, Status );
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: UpdateLinkFolder - Update a DfsFolder.
//
// Arguments: ChildName - metadata name of the child
// pLinkName - the logical namespace name, relative to root
// pChildFolder - the child folder
//
// Returns: Status: Success or error status
//
// Description: This routine TBD
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::UpdateLinkFolder(
IN LPWSTR ChildName,
IN PUNICODE_STRING pLinkName,
IN DfsFolder *pChildFolder )
{
BOOLEAN Removed;
UNREFERENCED_PARAMETER(ChildName);
UNREFERENCED_PARAMETER(pLinkName);
pChildFolder->RemoveReferralData( NULL, &Removed );
pStatistics->UpdateLinkModified();
if (Removed == TRUE)
{
pStatistics->UpdateForcedCacheFlush();
}
//
// Create directories too. Delete old directories.
//
return ERROR_SUCCESS;
}
//+-------------------------------------------------------------------------
//
// Function: RemoveAllLinkFolders - Remove all folders of this root
//
// Arguments: None
//
// Returns: Status: Success or error status
//
// Description: This routine TBD
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::RemoveAllLinkFolders(
BOOLEAN IsPermanent)
{
DFSSTATUS Status = ERROR_SUCCESS;
DfsFolder *pChildFolder;
ULONG Count = 0;
while (Status == ERROR_SUCCESS)
{
Status = LookupFolder(&pChildFolder);
if (Status == ERROR_SUCCESS)
{
Status = RemoveLinkFolder(pChildFolder,
IsPermanent);
pChildFolder->ReleaseReference();
Count++;
}
}
DFS_TRACE_ERROR_HIGH( Status, REFERRAL_SERVER, "Remove all link folders Count %d Status %x\n",
Count,
Status);
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: RemoveLinkFolder - Update a DfsFolder.
//
// Arguments: ChildName - metadata name of the child
// pLinkName - the logical namespace name, relative to root
// pChildFolder - the child folder
//
// Returns: Status: Success or error status
//
// Description: This routine TBD
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::RemoveLinkFolder(
IN DfsFolder *pChildFolder,
BOOLEAN IsPermanent )
{
DFSSTATUS Status = ERROR_SUCCESS;
if (IsPermanent == TRUE)
{
//
// try to tear down the link reparse point: return status ignored.
//
Status = TeardownLinkReparsePoint( pChildFolder->GetFolderLogicalNameString() );
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "Tear down reparse for %p, status %x \n", pChildFolder, Status );
}
Status = pChildFolder->AcquireWriteLock();
if ( Status == ERROR_SUCCESS )
{
Status = RemoveLinkFolderFromMetadataTable( pChildFolder );
if ( Status == ERROR_SUCCESS )
{
DecrementChildCount();
Status = RemoveLinkFolderFromLogicalTable( pChildFolder );
}
pChildFolder->ReleaseLock();
}
if (Status == ERROR_SUCCESS)
{
pStatistics->UpdateLinkDeleted();
}
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "Remove Link Folder %p, status %x \n", pChildFolder, Status );
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: OpenLinkDirectory
//
// Arguments: DirectoryName - the full pathname to directory being created.
// ShareMode - the share mode (share all or share none)
// pDirHandle - pointer to return the opened handle
// pIsNewlyCreated - was the directory created or existing.
//
// Returns: SUCCESS or error
//
// Description: This routine takes an NT pathname to the directory
// representing the DFS link, and opens it, creating it
// if it was not already existing.
// It returns the open handle to the directory, and also
// returns an indication whether a new directory was
// created or an existing directory was opened.
//
//--------------------------------------------------------------------------
NTSTATUS
DfsRootFolder::OpenLinkDirectory(
LPWSTR DirectoryName,
ULONG ShareMode,
PHANDLE pDirHandle,
PBOOLEAN pIsNewlyCreated )
{
NTSTATUS NtStatus;
// NtCreateFile
OBJECT_ATTRIBUTES ObjectAttributes;
ACCESS_MASK DesiredAccess;
PLARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG CreateDisposition;
ULONG CreateOptions;
PVOID EaBuffer;
ULONG EaLength;
UNICODE_STRING ObjectName;
IO_STATUS_BLOCK IoStatusBlock;
RtlInitUnicodeString(&ObjectName, DirectoryName );
AllocationSize = NULL;
FileAttributes = FILE_ATTRIBUTE_NORMAL;
CreateDisposition = FILE_OPEN_IF;
CreateOptions = FILE_DIRECTORY_FILE |
FILE_OPEN_REPARSE_POINT;
EaBuffer = NULL;
EaLength = 0;
DesiredAccess = FILE_READ_DATA |
FILE_WRITE_DATA |
FILE_READ_ATTRIBUTES |
FILE_WRITE_ATTRIBUTES;
InitializeObjectAttributes (
&ObjectAttributes,
&ObjectName,
0,
NULL,
NULL);
NtStatus = NtCreateFile(pDirHandle,
DesiredAccess,
&ObjectAttributes,
&IoStatusBlock,
AllocationSize,
FileAttributes,
ShareMode,
CreateDisposition,
CreateOptions,
EaBuffer,
EaLength);
DFSLOG("Open on %wS: Status %x\n", DirectoryName, NtStatus);
if (NtStatus == STATUS_SUCCESS)
{
*pIsNewlyCreated = (IoStatusBlock.Information == FILE_CREATED)? TRUE : FALSE;
}
return NtStatus;
}
//+-------------------------------------------------------------------------
//
// Function: IsDirectoryReparsePoint
//
// Arguments: DirHandle - handle to open directory.
// pReparsePoint - returned boolean: true if this directory is
// a reparse point
// pDfsReparsePoint - returned boolean: true if this
// directory is a dfs reparse point
//
//
// Returns: SUCCESS or error
//
// Description: This routine takes a handle to an open directory and
// sets 2 booleans to indicate if this directory is a
// reparse point, and if so, if this directory is a dfs
// reparse point. The booleans are initialized if this
// function returns success.
//
//--------------------------------------------------------------------------
NTSTATUS
DfsRootFolder::IsDirectoryReparsePoint(
IN HANDLE DirHandle,
OUT PBOOLEAN pReparsePoint,
OUT PBOOLEAN pDfsReparsePoint )
{
NTSTATUS NtStatus;
FILE_BASIC_INFORMATION BasicInfo;
IO_STATUS_BLOCK IoStatusBlock;
//
//we assume these are not reparse points.
//
*pReparsePoint = FALSE;
*pDfsReparsePoint = FALSE;
//
// Query for the basic information, which has the attributes.
//
NtStatus = NtQueryInformationFile( DirHandle,
&IoStatusBlock,
(PVOID)&BasicInfo,
sizeof(BasicInfo),
FileBasicInformation );
if (NtStatus == STATUS_SUCCESS)
{
//
// If the attributes indicate reparse point, we have a reparse
// point directory on our hands.
//
if ( BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT )
{
FILE_ATTRIBUTE_TAG_INFORMATION FileTagInformation;
*pReparsePoint = TRUE;
NtStatus = NtQueryInformationFile( DirHandle,
&IoStatusBlock,
(PVOID)&FileTagInformation,
sizeof(FileTagInformation),
FileAttributeTagInformation );
if (NtStatus == STATUS_SUCCESS)
{
//
// Checkif the tag indicates its a DFS reparse point,
// and setup the return accordingly.
//
if (FileTagInformation.ReparseTag == IO_REPARSE_TAG_DFS)
{
*pDfsReparsePoint = TRUE;
}
}
}
}
return NtStatus;
}
//+-------------------------------------------------------------------------
//
// Function: SetDfsReparsePoint
//
// Arguments: DirHandle - handle on open directory
//
// Returns: SUCCESS or error
//
// Description: This routine takes a handle to an open directory and
// makes that directory a reparse point with the DFS tag
//
//--------------------------------------------------------------------------
NTSTATUS
DfsRootFolder::SetDfsReparsePoint(
IN HANDLE DirHandle )
{
NTSTATUS NtStatus;
REPARSE_DATA_BUFFER ReparseDataBuffer;
IO_STATUS_BLOCK IoStatusBlock;
//
// Attempt to set a reparse point on the directory
//
RtlZeroMemory( &ReparseDataBuffer, sizeof(ReparseDataBuffer) );
ReparseDataBuffer.ReparseTag = IO_REPARSE_TAG_DFS;
ReparseDataBuffer.ReparseDataLength = 0;
NtStatus = NtFsControlFile( DirHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_SET_REPARSE_POINT,
&ReparseDataBuffer,
REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseDataBuffer.ReparseDataLength,
NULL,
0 );
return NtStatus;
}
//+-------------------------------------------------------------------------
//
// Function: ClearDfsReparsePoint
//
// Arguments: DirHandle - handle on open directory
//
// Returns: SUCCESS or error
//
// Description: This routine takes a handle to an open directory and
// makes that directory a reparse point with the DFS tag
//
//--------------------------------------------------------------------------
NTSTATUS
DfsRootFolder::ClearDfsReparsePoint(
IN HANDLE DirHandle )
{
NTSTATUS NtStatus;
REPARSE_DATA_BUFFER ReparseDataBuffer;
IO_STATUS_BLOCK IoStatusBlock;
//
// Attempt to set a reparse point on the directory
//
RtlZeroMemory( &ReparseDataBuffer, sizeof(ReparseDataBuffer) );
ReparseDataBuffer.ReparseTag = IO_REPARSE_TAG_DFS;
ReparseDataBuffer.ReparseDataLength = 0;
NtStatus = NtFsControlFile( DirHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_DELETE_REPARSE_POINT,
&ReparseDataBuffer,
REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseDataBuffer.ReparseDataLength,
NULL,
0 );
return NtStatus;
}
//+-------------------------------------------------------------------------
//
// Function: MorphLinkCollision
//
// Arguments: DirectoryName - Name of directory to morph.
//
// Returns: SUCCESS or error
//
// Description: This routine takes a NT pathname to a directory. It
// renames that directory with a morphed name.
//
//--------------------------------------------------------------------------
NTSTATUS
DfsRootFolder::MorphLinkCollision(
LPWSTR DirectoryName )
{
UNREFERENCED_PARAMETER(DirectoryName);
return STATUS_NOT_SUPPORTED;
}
DFSSTATUS
DfsRootFolder::TeardownLinkReparsePoint(
LPWSTR LinkNameString )
{
NTSTATUS NtStatus = STATUS_SUCCESS;
HANDLE DirectoryHandle;
UNICODE_STRING LinkName;
BOOLEAN ImpersonationDisabled = FALSE;
DfsDisableRpcImpersonation(&ImpersonationDisabled);
if (IsRootFolderShareAcquired() == TRUE)
{
RtlInitUnicodeString( &LinkName, LinkNameString);
NtStatus = OpenDirectory ( GetDirectoryCreatePathName(),
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
&DirectoryHandle,
NULL );
if (NtStatus == STATUS_SUCCESS)
{
NtStatus = DeleteLinkReparsePoint( &LinkName,
DirectoryHandle );
CloseDirectory( DirectoryHandle );
}
}
if (ImpersonationDisabled)
{
DfsReEnableRpcImpersonation();
}
return NtStatus;
}
DFSSTATUS
DfsRootFolder::SetupLinkReparsePoint(
LPWSTR LinkNameString )
{
NTSTATUS NtStatus = STATUS_SUCCESS;
HANDLE DirectoryHandle;
UNICODE_STRING LinkName;
BOOLEAN ImpersonationDisabled = FALSE;
DfsDisableRpcImpersonation(&ImpersonationDisabled);
if (IsRootFolderShareAcquired() == TRUE)
{
RtlInitUnicodeString( &LinkName, LinkNameString);
NtStatus = OpenDirectory ( GetDirectoryCreatePathName(),
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
&DirectoryHandle,
NULL );
if (NtStatus == STATUS_SUCCESS)
{
NtStatus = CreateLinkReparsePoint( &LinkName,
DirectoryHandle );
CloseDirectory( DirectoryHandle );
}
if (NtStatus != STATUS_SUCCESS)
{
SetLastCreateDirectoryError(NtStatus);
}
}
if (ImpersonationDisabled)
{
DfsReEnableRpcImpersonation();
}
return NtStatus;
}
NTSTATUS
DfsRootFolder::OpenDirectory(
PUNICODE_STRING pDirectoryName,
ULONG ShareMode,
HANDLE RelativeHandle,
PHANDLE pOpenedHandle,
PBOOLEAN pIsNewlyCreated )
{
NTSTATUS NtStatus;
OBJECT_ATTRIBUTES ObjectAttributes;
ACCESS_MASK DesiredAccess;
PLARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG CreateDisposition;
ULONG CreateOptions;
IO_STATUS_BLOCK IoStatusBlock;
AllocationSize = NULL;
FileAttributes = FILE_ATTRIBUTE_NORMAL;
CreateDisposition = FILE_OPEN_IF;
CreateOptions = FILE_DIRECTORY_FILE |
FILE_OPEN_REPARSE_POINT |
FILE_SYNCHRONOUS_IO_NONALERT;
DesiredAccess = FILE_READ_DATA |
FILE_WRITE_DATA |
FILE_READ_ATTRIBUTES |
FILE_WRITE_ATTRIBUTES |
SYNCHRONIZE;
InitializeObjectAttributes (
&ObjectAttributes,
pDirectoryName, //Object Name
0, //Attributes
RelativeHandle, //Root handle
NULL); //Security descriptor.
NtStatus = NtCreateFile(pOpenedHandle,
DesiredAccess,
&ObjectAttributes,
&IoStatusBlock,
AllocationSize,
FileAttributes,
ShareMode,
CreateDisposition,
CreateOptions,
NULL, // EaBuffer
0 ); // EaLength
DFSLOG("Open on %wZ: Status %x\n", pDirectoryName, NtStatus);
if ( (NtStatus == STATUS_SUCCESS) && (pIsNewlyCreated != NULL) )
{
*pIsNewlyCreated = (IoStatusBlock.Information == FILE_CREATED)? TRUE : FALSE;
}
return NtStatus;
}
NTSTATUS
DfsRootFolder::CreateLinkReparsePoint(
PUNICODE_STRING pLinkName,
HANDLE RelativeHandle )
{
NTSTATUS NtStatus;
ULONG ShareMode = 0;
HANDLE DirectoryHandle;
BOOLEAN IsNewlyCreated;
DFSSTATUS DosStatus;
NtStatus = OpenDirectory( pLinkName,
ShareMode,
RelativeHandle,
&DirectoryHandle,
&IsNewlyCreated );
if (NtStatus != STATUS_SUCCESS)
{
NtStatus = CreateLinkDirectories( pLinkName,
RelativeHandle,
&DirectoryHandle,
&IsNewlyCreated );
}
if (NtStatus == STATUS_SUCCESS)
{
NtStatus = SetDfsReparsePoint( DirectoryHandle);
NtClose( DirectoryHandle);
}
DosStatus = RtlNtStatusToDosError(NtStatus);
DFS_TRACE_ERROR_HIGH(DosStatus, REFERRAL_SERVER, "DirectoryName of interest %wZ: Create Reparse point Status %x\n",
pLinkName,
DosStatus);
return DosStatus;
}
NTSTATUS
DfsRootFolder::DeleteLinkReparsePoint(
PUNICODE_STRING pDirectoryName,
HANDLE ParentHandle )
{
NTSTATUS NtStatus;
HANDLE LinkDirectoryHandle;
BOOLEAN IsReparsePoint, IsDfsReparsePoint;
NtStatus = OpenDirectory( pDirectoryName,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
ParentHandle,
&LinkDirectoryHandle,
NULL );
if (NtStatus == STATUS_SUCCESS)
{
NtStatus = IsDirectoryReparsePoint( LinkDirectoryHandle,
&IsReparsePoint,
&IsDfsReparsePoint );
if ((NtStatus == STATUS_SUCCESS) &&
(IsDfsReparsePoint == TRUE) )
{
NtStatus = ClearDfsReparsePoint( LinkDirectoryHandle );
}
NtClose( LinkDirectoryHandle );
}
if (NtStatus == STATUS_SUCCESS)
{
NtStatus = DeleteLinkDirectories( pDirectoryName,
ParentHandle );
}
return NtStatus;
}
NTSTATUS
DfsRootFolder::CreateLinkDirectories(
PUNICODE_STRING pLinkName,
HANDLE RelativeHandle,
PHANDLE pDirectoryHandle,
PBOOLEAN pIsNewlyCreated )
{
UNICODE_STRING DirectoryToCreate = *pLinkName;
NTSTATUS NtStatus = STATUS_SUCCESS;
ULONG ShareMode;
HANDLE CurrentDirectory;
ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
DirectoryToCreate.MaximumLength = DirectoryToCreate.Length;
StripLastPathComponent( &DirectoryToCreate );
while ( (NtStatus == STATUS_SUCCESS) && (DirectoryToCreate.Length != 0) )
{
NtStatus = OpenDirectory( &DirectoryToCreate,
ShareMode,
RelativeHandle,
&CurrentDirectory,
NULL );
if (NtStatus == ERROR_SUCCESS)
{
CloseDirectory( CurrentDirectory );
break;
}
NtStatus = StripLastPathComponent( &DirectoryToCreate );
}
ShareMode = 0;
while ( (NtStatus == STATUS_SUCCESS) && (DirectoryToCreate.Length != DirectoryToCreate.MaximumLength) )
{
AddNextPathComponent( &DirectoryToCreate );
NtStatus = OpenDirectory( &DirectoryToCreate,
ShareMode,
RelativeHandle,
&CurrentDirectory,
pIsNewlyCreated );
if ( (NtStatus == ERROR_SUCCESS) &&
(DirectoryToCreate.Length != DirectoryToCreate.MaximumLength) )
{
CloseDirectory( CurrentDirectory );
}
}
if (NtStatus == STATUS_SUCCESS)
{
*pDirectoryHandle = CurrentDirectory;
}
return NtStatus;
}
BOOLEAN
DfsRootFolder::IsEmptyDirectory(
HANDLE DirectoryHandle,
PVOID pDirectoryBuffer,
ULONG DirectoryBufferSize )
{
NTSTATUS NtStatus;
FILE_NAMES_INFORMATION *pFileInfo;
ULONG NumberOfFiles = 1;
BOOLEAN ReturnValue = FALSE;
IO_STATUS_BLOCK IoStatus;
NtStatus = NtQueryDirectoryFile ( DirectoryHandle,
NULL, // no event
NULL, // no apc routine
NULL, // no apc context
&IoStatus,
pDirectoryBuffer,
DirectoryBufferSize,
FileNamesInformation,
FALSE, // return single entry = false
NULL, // filename
FALSE ); // restart scan = false
if (NtStatus == ERROR_SUCCESS)
{
pFileInfo = (FILE_NAMES_INFORMATION *)pDirectoryBuffer;
while (pFileInfo->NextEntryOffset) {
NumberOfFiles++;
if (NumberOfFiles > 3)
{
break;
}
pFileInfo = (FILE_NAMES_INFORMATION *)((ULONG_PTR)(pFileInfo) +
pFileInfo->NextEntryOffset);
}
if (NumberOfFiles <= 2)
{
ReturnValue = TRUE;
}
}
return ReturnValue;
}
NTSTATUS
DfsRootFolder::DeleteLinkDirectories(
PUNICODE_STRING pLinkName,
HANDLE RelativeHandle )
{
UNICODE_STRING DirectoryToDelete = *pLinkName;
NTSTATUS NtStatus = STATUS_SUCCESS;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE CurrentDirectory = NULL;
ULONG ShareMode = 0;
const TCHAR * apszSubStrings[4];
ShareMode = FILE_SHARE_READ;
//
// dfsdev: fix this fixed size limit. it will hurt us in the future.
//
ULONG DirectoryBufferSize = 4096;
PBYTE pDirectoryBuffer = new BYTE [DirectoryBufferSize];
if (pDirectoryBuffer == NULL)
{
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
}
while ( (NtStatus == STATUS_SUCCESS) && (DirectoryToDelete.Length != 0) )
{
NtStatus = OpenDirectory( &DirectoryToDelete,
ShareMode,
RelativeHandle,
&CurrentDirectory,
NULL );
if (NtStatus == ERROR_SUCCESS)
{
if (IsEmptyDirectory(CurrentDirectory,
pDirectoryBuffer,
DirectoryBufferSize) == FALSE)
{
CloseDirectory( CurrentDirectory );
apszSubStrings[0] = GetFolderLogicalNameString();
apszSubStrings[1] = GetDirectoryCreatePathName()->Buffer;
GenerateEventLog(DFS_ERROR_DIRECTORY_NOT_EMPTY,
2,
apszSubStrings,
0);
break;
}
CloseDirectory( CurrentDirectory );
InitializeObjectAttributes (
&ObjectAttributes,
&DirectoryToDelete,
0,
RelativeHandle,
NULL);
NtStatus = NtDeleteFile( &ObjectAttributes );
StripLastPathComponent( &DirectoryToDelete );
}
}
if (pDirectoryBuffer != NULL)
{
delete [] pDirectoryBuffer;
}
return NtStatus;
}
VOID
DfsRootFolder::CloseDirectory(
HANDLE DirHandle )
{
NtClose( DirHandle );
}
//+-------------------------------------------------------------------------
//
// Function: AcquireRootShareDirectory
//
// Arguments: none
//
// Returns: Status: success if we passed all checks.
//
// Description: This routine checks to see if the share backing the
// the dfs root actually exists. If it does, it confirms
// that the filesystem hosting this directory supports
// reparse points. Finally, it tells the driver to attach
// to this directory.
// If all of this works, we have acquired the root share.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::AcquireRootShareDirectory(void)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
DFSSTATUS Status = ERROR_SUCCESS;
HANDLE DirHandle = NULL;
PUNICODE_STRING pDirectoryName = NULL;
PUNICODE_STRING pUseShare = NULL;
BOOLEAN SubStringMatch = FALSE;
BOOL Inserted = FALSE;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
ULONG pAttribInfoSize;
PFILE_FS_ATTRIBUTE_INFORMATION pAttribInfo = NULL;
const TCHAR * apszSubStrings[4];
pUseShare = GetRootPhysicalShareName();
DFS_TRACE_LOW(REFERRAL_SERVER, "AcquireRoot Share called for root %p, name %wZ\n", this, pUseShare);
//
// if either the root share is already acquired, or the library
// was told that we are not interested in creating directories, we
// are done.
//
if ( (IsRootFolderShareAcquired() == TRUE) ||
(DfsCheckCreateDirectories() == FALSE) )
{
DFS_TRACE_LOW(REFERRAL_SERVER, "Root %p, Share Already acquired\n", this);
return ERROR_SUCCESS;
}
//
// first we get the logical share
// Then we call into initialize directory information to setup the
// physical share path etc.
//
Status = InitializeDirectoryCreateInformation();
//
// If the directory create path is invalid, we are done.
//
if (Status == ERROR_SUCCESS)
{
pDirectoryName = GetDirectoryCreatePathName();
if ( (pDirectoryName == NULL) ||
(pDirectoryName->Buffer == NULL) ||
(pDirectoryName->Length == 0) )
{
Status = ERROR_INVALID_PARAMETER;
}
}
if (Status == ERROR_SUCCESS)
{
//
// Now allocate space to fill the attribute information we
// will query.
// dfsdev: document why we allocate an additional max_path.
//
pAttribInfoSize = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH;
pAttribInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)new BYTE [pAttribInfoSize];
if (pAttribInfo != NULL)
{
InitializeObjectAttributes ( &ObjectAttributes,
pDirectoryName,
0, //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)
{
NtStatus = STATUS_NOT_SUPPORTED;
apszSubStrings[0] = pUseShare->Buffer;
apszSubStrings[1] = pDirectoryName->Buffer;
GenerateEventLog(DFS_ERROR_UNSUPPORTED_FILESYSTEM,
2,
apszSubStrings,
0);
}
}
CloseHandle (DirHandle);
delete [] pAttribInfo;
}
if( NtStatus != STATUS_SUCCESS)
{
Status = RtlNtStatusToDosError(NtStatus);
}
}
else {
Status = ERROR_NOT_ENOUGH_MEMORY;
}
}
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "Root %p, Share check status %x\n", this, Status);
//
// Now check if we already know about parts of this path.
// if there is overlap with other paths that we already know about,
// we cannot handle this root, so reject it.
//
if (Status == ERROR_SUCCESS)
{
Status = DfsAddKnownDirectoryPath( pDirectoryName,
pUseShare );
if (Status == ERROR_SUCCESS)
{
Inserted = TRUE;
}
else
{
apszSubStrings[0] = pUseShare->Buffer;
apszSubStrings[1] = pDirectoryName->Buffer;
GenerateEventLog(DFS_ERROR_OVERLAPPING_DIRECTORIES,
2,
apszSubStrings,
0);
}
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "Root %p, Share add known directory status %x\n", this, Status);
}
//
// if we are here: we know this is a reparse point, and we have
// inserted in the user mode database.
// now call into the driver so it may attach to this filesystem.
//
if (Status == ERROR_SUCCESS)
{
Status = DfsUserModeAttachToFilesystem( pDirectoryName,
pUseShare);
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "Root %p, user mode attach status %x\n", this, Status);
}
//
// if we are successful, we acquired the root share, now mark
// our state accordingly.
//
if (Status == ERROR_SUCCESS)
{
SetRootFolderShareAcquired();
}
else
{
//
// otherwise, clear up some of the work we just did.
//
ClearRootFolderShareAcquired();
if (Inserted == TRUE)
{
DfsRemoveKnownDirectoryPath( pDirectoryName,
pUseShare);
}
}
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "AcquireRoot share for root %p, (%wZ) status %x\n",
this, pUseShare, Status);
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: ReleaseRootShareDirectory
//
// Arguments: none
//
// Returns: Status: success if we are successful
//
// Description: This routine checks to see if the share backing the
// the dfs root was acquired by us earlier. If so, we
// tell the driver to releast its reference on this
// share, and remove this information from our tables,
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::ReleaseRootShareDirectory(void)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
DFSSTATUS Status = ERROR_SUCCESS;
PUNICODE_STRING pDirectoryName = NULL;
PUNICODE_STRING pUseShare = NULL;
PVOID pData = NULL;
BOOLEAN SubStringMatch = FALSE;
DFS_TRACE_LOW(REFERRAL_SERVER, "ReleaseRoot share for root %p\n", this);
if (IsRootFolderShareAcquired() == TRUE)
{
//
// get the logical share, and the physical directory backing the
// share.
//
pUseShare = GetRootPhysicalShareName();
pDirectoryName = GetDirectoryCreatePathName();
if ( (pDirectoryName == NULL) ||
(pDirectoryName->Buffer == NULL) ||
(pDirectoryName->Length == 0) )
{
Status = ERROR_INVALID_PARAMETER;
}
//
// now, signal the driver to detach itself from this share.
//dfsdev: if this fails, we are in an inconsistent state, since
// we just removed it from our table above!
//
if (Status == ERROR_SUCCESS)
{
Status = DfsUserModeDetachFromFilesystem( pDirectoryName,
pUseShare);
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "user mode detach path %wZ, %wZ: Status %x\n",
pDirectoryName, pUseShare, Status );
}
//
// now, find the information in our database. if we did not find an
// exact match, something went wrong and signal that.
//
if (Status == ERROR_SUCCESS)
{
DFSSTATUS RemoveStatus;
RemoveStatus = DfsRemoveKnownDirectoryPath( pDirectoryName,
pUseShare );
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "RemoveKnownDirectory path %wZ, %wZ: Status %x\n",
pDirectoryName, pUseShare, RemoveStatus );
}
if (Status == ERROR_SUCCESS)
{
ClearRootFolderShareAcquired();
}
}
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "Release root share %p, Status %x\n",
this, Status );
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: AddMetadataLink
//
// Arguments:
// pLogicalName: the complete logical unc name of this link.
// ReplicaServer: the target server for this link.
// ReplicaPath: the target path on the server.
// Comment : comment to be associated with this link.
//
// Returns: SUCCESS or error
//
// Description: This routine Adds a link to the metadata.
// In future, ReplicaServer and ReplicaPath's
// can be null, since we will allow links with
// no targets.
// dfsdev: make sure we do the right thing for
// compat.
//
// Assumptions: the caller is responsible for mutual exclusion.
// The caller is also responsible for ensuring
// this link does not already exist in the
// the metadata.
// The caller is also responsible to make sure that this
// name does not overlap an existing link.
// (for example if link a/b exisits, link a or a/b/c are
// overlapping links and should be disallowed.)
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::AddMetadataLink(
PUNICODE_STRING pLogicalName,
LPWSTR ReplicaServer,
LPWSTR ReplicaPath,
LPWSTR Comment )
{
DFS_METADATA_HANDLE RootHandle = NULL;
DFSSTATUS Status;
UNICODE_STRING LinkMetadataName;
UNICODE_STRING VisibleNameContext, UseName;
DFS_NAME_INFORMATION NameInfo;
DFS_REPLICA_LIST_INFORMATION ReplicaListInfo;
DFS_REPLICA_INFORMATION ReplicaInfo;
UUID NewUid;
Status = AcquireRootLock();
if (Status != ERROR_SUCCESS)
{
return Status;
}
Status = UuidCreate(&NewUid);
if (Status == ERROR_SUCCESS)
{
Status = GetMetadataStore()->GenerateLinkMetadataName( &NewUid,
&LinkMetadataName);
}
if (Status == ERROR_SUCCESS)
{
//
// First get a handle to the
// metadata. The handle has different meaning to different
// underlying stores: for example the registry store may
// use the handle as a key, while the ad store may use the
// handle as a pointer in some cache.
//
Status = GetMetadataHandle( &RootHandle );
if (Status == ERROR_SUCCESS)
{
GetVisibleNameContext( NULL, &VisibleNameContext );
Status = GetMetadataStore()->GenerateMetadataLogicalName( &VisibleNameContext,
pLogicalName,
&UseName );
if (Status == ERROR_SUCCESS)
{
GetMetadataStore()->StoreInitializeNameInformation( &NameInfo,
&UseName,
&NewUid,
Comment );
GetMetadataStore()->StoreInitializeReplicaInformation( &ReplicaListInfo,
&ReplicaInfo,
ReplicaServer,
ReplicaPath );
Status = GetMetadataStore()->AddChild( RootHandle,
&NameInfo,
&ReplicaListInfo,
&LinkMetadataName );
//
// if we failed add child, we need to reset the
// internal state to wipe out any work we did when
// we were adding the child.
//
if (Status != ERROR_SUCCESS)
{
ReSynchronize();
}
GetMetadataStore()->ReleaseMetadataLogicalName(&UseName );
//
// if we successfully added the link, update the link information
// so that we can pass this out in referrals, and create the appropriate
// directories.
//
if (Status == ERROR_SUCCESS)
{
DFSSTATUS LinkUpdateStatus;
LinkUpdateStatus = UpdateLinkInformation( RootHandle,
LinkMetadataName.Buffer );
}
}
//
// Finally, release the root handle we acquired earlier.
//
ReleaseMetadataHandle( RootHandle );
}
GetMetadataStore()->ReleaseLinkMetadataName( &LinkMetadataName );
}
ReleaseRootLock();
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: RemoveMetadataLink
//
// Arguments:
// pLogicalName: the link name relative to root share
//
// Returns: SUCCESS or error
//
// Description: This routine removes a link from the the metadata.
//
// Assumptions: the caller is responsible for mutual exclusion.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::RemoveMetadataLink(
PUNICODE_STRING pLinkName )
{
DFS_METADATA_HANDLE RootHandle;
DFSSTATUS Status;
LPWSTR LinkMetadataName;
DfsFolder *pFolder;
UNICODE_STRING Remaining;
Status = AcquireRootLock();
if (Status != ERROR_SUCCESS)
{
return Status;
}
//
// First, look this link up in our local data structures.
//
Status = LookupFolderByLogicalName( pLinkName,
&Remaining,
&pFolder );
//
// if an EXACT match was not found, we are done.
//
if ( (Status == ERROR_SUCCESS) && (Remaining.Length != 0) )
{
pFolder->ReleaseReference();
Status = ERROR_NOT_FOUND;
}
//
// we found the child folder. Now work on removing the metadata
// and our local structures associated with this link.
//
if (Status == ERROR_SUCCESS)
{
//
// Get a handle to our metadata.
//
Status = GetMetadataHandle( &RootHandle );
//
//Now, look up the metadata name and remove it from the store.
//
if (Status == ERROR_SUCCESS)
{
LinkMetadataName = pFolder->GetFolderMetadataNameString();
Status = GetMetadataStore()->RemoveChild( RootHandle,
LinkMetadataName );
if (Status != ERROR_SUCCESS)
{
ReSynchronize();
}
ReleaseMetadataHandle( RootHandle );
//
// If we successfully removed the child from the metadata,
// remove the link folder associated with this child. This will
// get rid of our data structure and related directory for that child.
//
if (Status == ERROR_SUCCESS)
{
DFSSTATUS RemoveStatus;
RemoveStatus = RemoveLinkFolder( pFolder,
TRUE ); // permanent removal
}
}
pFolder->ReleaseReference();
}
ReleaseRootLock();
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: AddMetadataLinkReplica
//
// Arguments:
// pLinkName: the link name relative to root share
// ReplicaServer : the target server to add
// ReplicaPath : the target path on the server.
//
// Returns: SUCCESS or error
//
// Description: This routine adds a target to an existing link.
//
// Assumptions: the caller is responsible for mutual exclusion.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::AddMetadataLinkReplica(
PUNICODE_STRING pLinkName,
LPWSTR ReplicaServer,
LPWSTR ReplicaPath )
{
DFS_METADATA_HANDLE RootHandle;
DFSSTATUS Status = ERROR_SUCCESS;
DfsFolder *pFolder;
UNICODE_STRING Remaining;
LPWSTR LinkMetadataName;
//
// If either the target server or the path is null, reject the request.
//
if ((ReplicaServer == NULL) || (ReplicaPath == NULL))
{
return ERROR_INVALID_PARAMETER;
}
Status = AcquireRootLock();
if (Status != ERROR_SUCCESS)
{
return Status;
}
//
// Find the link folder associated with this logical name.
//
Status = LookupFolderByLogicalName( pLinkName,
&Remaining,
&pFolder );
//
// If we did not find an EXACT match on the logical name, we are done.
//
if ( (Status == ERROR_SUCCESS) && (Remaining.Length != 0) )
{
pFolder->ReleaseReference();
Status = ERROR_NOT_FOUND;
}
//
// if we are successful so far, call the store with a handle to
// the metadata to add this target.
//
if (Status == ERROR_SUCCESS)
{
//
// get the metadata name for this link from the root folder.
//
LinkMetadataName = pFolder->GetFolderMetadataNameString();
//
// Get a handle to the root metadata this root folder.
//
Status = GetMetadataHandle( &RootHandle );
if (Status == ERROR_SUCCESS)
{
Status = GetMetadataStore()->AddChildReplica( RootHandle,
LinkMetadataName,
ReplicaServer,
ReplicaPath );
if (Status != ERROR_SUCCESS)
{
ReSynchronize();
}
//
// Release the metadata handle we acquired earlier.
//
ReleaseMetadataHandle( RootHandle );
}
//
// If we successfully added the target in the metadata, update the
// link folder so that the next referral request will pick up the
// new target.
//
if (Status == ERROR_SUCCESS)
{
DFSSTATUS UpdateStatus;
UpdateStatus = UpdateLinkFolder( LinkMetadataName,
pLinkName,
pFolder );
//
// dfsdev: log the update state.
//
}
//
// we are done with this link folder: release thre reference we got
// when we looked it up.
//
pFolder->ReleaseReference();
}
ReleaseRootLock();
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: RemoveMetadataLinkReplica
//
// Arguments:
// pLinkName: the link name relative to root share
// ReplicaServer : the target server to remove
// ReplicaPath : the target path on the server.
// pLastReplica: pointer to boolean, returns true if the last
// target on this link is being deleted.
//
// Returns: SUCCESS or error
//
// Description: This routine removes the target of an existing link.
//
// Assumptions: the caller is responsible for mutual exclusion.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::RemoveMetadataLinkReplica(
PUNICODE_STRING pLinkName,
LPWSTR ReplicaServer,
LPWSTR ReplicaPath,
PBOOLEAN pLastReplica )
{
LPWSTR LinkMetadataName;
DFS_METADATA_HANDLE RootHandle;
UNICODE_STRING Remaining;
DFSSTATUS Status;
DfsFolder *pFolder;
//
// if either the target server or target path is empty, return error.
//
if ((ReplicaServer == NULL) || (ReplicaPath == NULL))
{
return ERROR_INVALID_PARAMETER;
}
Status = AcquireRootLock();
if (Status != ERROR_SUCCESS)
{
return Status;
}
//
//find the link folder associated with this logical name.
//
Status = LookupFolderByLogicalName( pLinkName,
&Remaining,
&pFolder );
//
// if we did not find an EXACT match on the logical name, we are done.
//
if ( (Status == ERROR_SUCCESS) && (Remaining.Length != 0) )
{
pFolder->ReleaseReference();
Status = ERROR_NOT_FOUND;
}
//
// Call the store to remove the target from this child.
//
if (Status == ERROR_SUCCESS)
{
//
// Get the link metadata name from the folder.
//
LinkMetadataName = pFolder->GetFolderMetadataNameString();
//
// Get the handle to the root metadata for this root folder.
//
Status = GetMetadataHandle( &RootHandle );
if (Status == ERROR_SUCCESS)
{
Status = GetMetadataStore()->RemoveChildReplica( RootHandle,
LinkMetadataName,
ReplicaServer,
ReplicaPath,
pLastReplica );
if (Status != ERROR_SUCCESS)
{
ReSynchronize();
}
//
// release the metadata handle we acquired a little bit earlier.
//
ReleaseMetadataHandle( RootHandle );
}
//
// if we are successful in removing the target, update the link
// folder so that future referrals will no longer see the target
// we just deleted.
//
if (Status == ERROR_SUCCESS)
{
DFSSTATUS UpdateStatus;
UpdateStatus = UpdateLinkFolder( LinkMetadataName,
pLinkName,
pFolder );
DFSLOG("Remove link replica, update link folder status %x\n", UpdateStatus);
}
pFolder->ReleaseReference();
}
ReleaseRootLock();
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: EnumerateApiLinks
//
// Arguments:
// LPWSTR DfsPathName : the dfs root to enumerate.
// DWORD Level : the enumeration level
// LPBYTE pBuffer : buffer to hold results.
// LONG BufferSize, : buffer size
// LPDWORD pEntriesRead : number of entries to read.
// LPDWORD pResumeHandle : the starting child to read.
// PLONG pNextSizeRequired : return value to hold size required in case of overflow.
//
// Returns: SUCCESS or error
//
// Description: This routine enumerates the dfs metadata information.
//
// Assumptions: the caller is responsible for mutual exclusion.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::EnumerateApiLinks(
LPWSTR DfsPathName,
DWORD Level,
LPBYTE pBuffer,
LONG BufferSize,
LPDWORD pEntriesRead,
LPDWORD pResumeHandle,
PLONG pNextSizeRequired )
{
DFSSTATUS Status;
DFS_METADATA_HANDLE RootHandle;
UNICODE_STRING VisibleNameContext;
UNICODE_STRING DfsPath;
Status = AcquireRootLock();
if (Status != ERROR_SUCCESS)
{
return Status;
}
RtlInitUnicodeString( &DfsPath,
DfsPathName );
//
// Get the name context for this call.
// do not use the user passed in name context within the path
// for this call: if the user comes in with an ip address, we want
// to return back the correct server/domain info to the caller
// so the dfsapi results will not show the ip address etc.
//
GetVisibleNameContext( NULL,
&VisibleNameContext );
//
// Get the handle to the metadata for this root folder, and call
// the store to enumerate the links.
//
Status = GetMetadataHandle( &RootHandle );
if (Status == ERROR_SUCCESS)
{
Status = GetMetadataStore()->EnumerateApiLinks( RootHandle,
&VisibleNameContext,
Level,
pBuffer,
BufferSize,
pEntriesRead,
pResumeHandle,
pNextSizeRequired);
//
// Release the metadata handle.
//
ReleaseMetadataHandle( RootHandle );
}
ReleaseRootLock();
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: GetApiInformation
//
// Arguments:
// PUNICODE DfsPathName : the dfs root name
// PUNICODE pLinkName : the link within the root.
// DWORD Level : the info level.
// LPBYTE pBuffer : buffer to hold results.
// LONG BufferSize, : buffer size
// PLONG pSizeRequired : return value to hold size required in case of overflow.
//
// Returns: SUCCESS or error
//
// Description: This routine gets the required information for a given root or link
//
// Assumptions: the caller is responsible for mutual exclusion.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::GetApiInformation(
PUNICODE_STRING pDfsName,
PUNICODE_STRING pLinkName,
DWORD Level,
LPBYTE pBuffer,
LONG BufferSize,
PLONG pSizeRequired )
{
UNREFERENCED_PARAMETER(pDfsName);
DFSSTATUS Status;
DFS_METADATA_HANDLE RootHandle;
LPWSTR MetadataName;
DfsFolder *pFolder;
UNICODE_STRING VisibleNameContext;
UNICODE_STRING Remaining;
Status = AcquireRootLock();
if (Status != ERROR_SUCCESS)
{
return Status;
}
//
//
// Do not base the context to use on the passed in dfsname:
// it is important to pass back our correct information
// in the api call.
//
//
GetVisibleNameContext( NULL,
&VisibleNameContext );
//
// If the link name is empty, we are dealing with the root.
// so set the metadata name to null.
//
if (pLinkName->Length == 0)
{
MetadataName = NULL;
Status = ERROR_SUCCESS;
}
//
// otherwise, lookup the link folder and get the metadataname for that link.
//
else {
Status = LookupFolderByLogicalName( pLinkName,
&Remaining,
&pFolder );
//
// if we did not find an EXACT match on the logical name, we are done.
//
if ( (Status == ERROR_SUCCESS) && (Remaining.Length != 0) )
{
pFolder->ReleaseReference();
Status = ERROR_NOT_FOUND;
}
//
// we had an exact match, so lookup the metadata name and release the
// the reference on the folder.
//
if (Status == ERROR_SUCCESS)
{
MetadataName = pFolder->GetFolderMetadataNameString();
pFolder->ReleaseReference();
}
}
//
// we got the metadata name: now call into the store to get the
// required information for the metadata name.
//
if (Status == ERROR_SUCCESS)
{
//
// Get the handle to the metadata for this root folder.
//
Status = GetMetadataHandle( &RootHandle );
if (Status == ERROR_SUCCESS)
{
Status = GetMetadataStore()->GetStoreApiInformation( RootHandle,
&VisibleNameContext,
MetadataName,
Level,
pBuffer,
BufferSize,
pSizeRequired);
ReleaseMetadataHandle( RootHandle );
}
}
ReleaseRootLock();
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: SetApiInformation
//
// Arguments:
// PUNICODE pLinkName : the name of link relative to root share
// LPWSTR Server, : the target server.
// LPWSTR Share, : the target path within the server.
// DWORD Level : the info level.
// LPBYTE pBuffer : buffer that has the information to be set.
//
// Returns: SUCCESS or error
//
// Description: This routine sets the required information for a given root or link
//
// Assumptions: the caller is responsible for mutual exclusion.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::SetApiInformation(
PUNICODE_STRING pLinkName,
LPWSTR Server,
LPWSTR Share,
DWORD Level,
LPBYTE pBuffer )
{
DFSSTATUS Status;
DFS_METADATA_HANDLE RootHandle;
LPWSTR MetadataName;
DfsFolder *pFolder;
UNICODE_STRING Remaining;
Status = AcquireRootLock();
if (Status != ERROR_SUCCESS)
{
return Status;
}
//
// if the link name is empty we are dealing with
// the root itself.
// dfsdev: we need to set the root metadata appropriately!
//
if (pLinkName->Length == 0)
{
MetadataName = NULL;
Status = ERROR_SUCCESS;
}
//
// else get to the link folder, and get
// the link metadata name.
//
else {
Status = LookupFolderByLogicalName( pLinkName,
&Remaining,
&pFolder );
//
// if we did not find an EXACT match on the logical name, we are done.
//
if ( (Status == ERROR_SUCCESS) && (Remaining.Length != 0) )
{
pFolder->ReleaseReference();
Status = ERROR_NOT_FOUND;
}
if (Status == ERROR_SUCCESS)
{
MetadataName = pFolder->GetFolderMetadataNameString();
//
// if we got the metadataname, call the store with the
// details so that it can associate the information
// with this root or link.
//
//
// Get the handle to the root of this metadata
//
Status = GetMetadataHandle( &RootHandle );
if (Status == ERROR_SUCCESS)
{
Status = GetMetadataStore()->SetStoreApiInformation( RootHandle,
MetadataName,
Server,
Share,
Level,
pBuffer );
if (Status != ERROR_SUCCESS)
{
ReSynchronize();
}
ReleaseMetadataHandle( RootHandle );
}
//
// If we successfully updated the metadata, update the
// link folder so that the next referral request will pick up the
// changes
//
if (Status == ERROR_SUCCESS)
{
DFSSTATUS UpdateStatus;
UpdateStatus = UpdateLinkFolder( MetadataName,
pLinkName,
pFolder );
//
// dfsdev: log the update state.
//
}
pFolder->ReleaseReference();
}
}
ReleaseRootLock();
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: LoadReferralData - Loads the referral data.
//
// Arguments: pReferralData - the referral data to load
//
// Returns: Status: Success or error code
//
// Description: This routine sets up the ReferralData instance to have
// all the information necessary to create a referral.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::LoadReferralData(
DfsFolderReferralData *pReferralData )
{
DFS_METADATA_HANDLE RootMetadataHandle;
DFSSTATUS Status;
DfsFolder *pFolder;
//
// Get the Root key for this root folder.
//
DFS_TRACE_LOW( REFERRAL_SERVER, "LoadReferralData called, %p\n", pReferralData);
Status = GetMetadataHandle( &RootMetadataHandle );
if ( Status == ERROR_SUCCESS )
{
//
// Now get the owning folder of the referralDAta. Note that
// this does not give us a new reference on the Folder.
// however, the folder is guaranteed to be around till
// we return from this call, since the pReferralData that
// was passed in to us cannot go away.
//
pFolder = pReferralData->GetOwningFolder();
DFS_TRACE_LOW( REFERRAL_SERVER, "Load referral data, Got Owning Folder %p\n", pFolder );
//
// Now load the replica referral data for the passed in folder.
//
Status = LoadReplicaReferralData( RootMetadataHandle,
pFolder->GetFolderMetadataNameString(),
pReferralData );
DFS_TRACE_LOW( REFERRAL_SERVER, "LoadReferralData for %p replica data loaded %x\n",
pReferralData, Status );
if ( Status == ERROR_SUCCESS )
{
//
// Load the policy referrral data for the passedin folder.
//
Status = LoadPolicyReferralData( RootMetadataHandle );
}
ReleaseMetadataHandle( RootMetadataHandle );
}
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "Done load referral data %p, Status %x\n",
pReferralData, Status);
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: UnloadReferralData - Unload the referral data.
//
// Arguments: pReferralData - the ReferralData instance to unload.
//
// Returns: Status: Success or Error status code.
//
// Description: This routine Unloads the referral data. It undoes what
// the corresponding load routine did.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::UnloadReferralData(
DfsFolderReferralData *pReferralData )
{
DFSSTATUS Status;
DFS_TRACE_LOW( REFERRAL_SERVER, "Unload referral data %p\n", pReferralData);
Status = UnloadReplicaReferralData( pReferralData );
if ( Status == ERROR_SUCCESS )
{
Status = UnloadPolicyReferralData( pReferralData );
}
DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "Unload referral data %p, Status %x\n", pReferralData, Status);
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: LoadReplicaReferralData - load the replica information.
//
// Arguments: RegKey - the registry key of the root folder,
// RegistryName - Name of the registry key relative to to Root Key
// pReferralData - the referral data to load.
//
// Returns: Status - Success or error status code.
//
// Description: This routine loads the replica referral data for the
// the passed in ReferralData instance.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::LoadReplicaReferralData(
DFS_METADATA_HANDLE RootMetadataHandle,
LPWSTR MetadataName,
DfsFolderReferralData *pReferralData )
{
PDFS_REPLICA_LIST_INFORMATION pReplicaListInfo;
PDFS_REPLICA_INFORMATION pReplicaInfo;
PUNICODE_STRING pServerName;
DfsReplica *pReplica;
DFSSTATUS Status;
ULONG Replica;
DFS_TRACE_LOW( REFERRAL_SERVER, "Load Replica Referral Data %ws, for %p\n", MetadataName, pReferralData);
pReferralData->pReplicas = NULL;
//
// Get the replica information.
//
Status = GetMetadataStore()->GetMetadataReplicaInformation(RootMetadataHandle,
MetadataName,
&pReplicaListInfo );
if ( Status == ERROR_SUCCESS )
{
//
// Set the appropriate count, and allocate the replicas
// required.
//
pReferralData->ReplicaCount = pReplicaListInfo->ReplicaCount;
if (pReferralData->ReplicaCount > 0)
{
pReferralData->pReplicas = new DfsReplica [ pReplicaListInfo->ReplicaCount ];
if ( pReferralData->pReplicas == NULL )
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
}
}
//
// Now, for each replica, set the replicas server name, the target
// folder and the replica state.
//
if ( Status == ERROR_SUCCESS )
{
for ( Replica = 0;
(Replica < pReplicaListInfo->ReplicaCount) && (Status == ERROR_SUCCESS);
Replica++ )
{
pReplicaInfo = &pReplicaListInfo->pReplicas[Replica];
pReplica = &pReferralData->pReplicas[ Replica ];
pServerName = &pReplicaInfo->ServerName;
//
// If the servername is a ., this is a special case where
// the servername is the root itself. In this case,
// set the server name to the name of this machine.
//
if (IsLocalName(pServerName))
{
GetVisibleNameContext( NULL,
pServerName );
}
Status = pReplica->SetTargetServer( pServerName );
if ( Status == ERROR_SUCCESS )
{
Status = pReplica->SetTargetFolder( &pReplicaInfo->ShareName );
}
if ( Status == ERROR_SUCCESS )
{
if ( pReplicaInfo->ReplicaState & REPLICA_STORAGE_STATE_OFFLINE )
{
pReplica->SetTargetOffline();
}
}
}
//
// Now release the replica information that was allocated
// by the store.
//
GetMetadataStore()->ReleaseMetadataReplicaInformation( RootMetadataHandle,
pReplicaListInfo );
}
DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "Done with Load Replica Referral Data %ws, for %p, Status %x\n",
MetadataName, pReferralData, Status);
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: UnloadReplicaReferralData - Unload the replicas
//
// Arguments: pReferralData - the DfsFolderReferralData to unload
//
// Returns: Status: Success always.
//
// Description: This routine gets rid of the allocate replicas in the
// folder's referral data.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::UnloadReplicaReferralData(
DfsFolderReferralData *pReferralData )
{
if (pReferralData->pReplicas != NULL) {
delete [] pReferralData->pReplicas;
pReferralData->pReplicas = NULL;
}
return ERROR_SUCCESS;
}
//+-------------------------------------------------------------------------
//
// Function: SetRootStandby - set the root in a standby mode.
//
// Arguments: none
//
// Returns: Status: Success always for now.
//
// Description: This routine checks if we are already in standby mode.
// If not, it releases the root share directory, removes
// all the link folders and set the root in a standby mode
// DFSDEV: need to take into consideration synchronization
// with other threads.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::SetRootStandby()
{
DFSSTATUS Status = ERROR_SUCCESS;
Status = AcquireRootLock();
if (Status != ERROR_SUCCESS)
{
return Status;
}
DFS_TRACE_LOW( REFERRAL_SERVER, "Root %p being set to standby\n", this);
if (IsRootFolderStandby() == FALSE)
{
//
// dfsdev:: ignore error returns from these calls?
//
DFSSTATUS ReleaseStatus;
ReleaseStatus = ReleaseRootShareDirectory();
DFS_TRACE_ERROR_LOW( ReleaseStatus, REFERRAL_SERVER, "Release root share status %x\n", ReleaseStatus);
RemoveAllLinkFolders( FALSE ); // Not a permanent removal
SetRootFolderStandby();
}
else
{
DFS_TRACE_LOW( REFERRAL_SERVER, "Root %p was already standby\n", this);
}
ReleaseRootLock();
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: SetRootResynchronize - set the root in a ready mode.
//
// Arguments: none
//
// Returns: Status: Success always for now.
//
// Description: This routine checks if we are already in ready mode.
// If not, it acquires the root share directory, calls
// synchronize to add all the links back
// DFSDEV: need to take into consideration synchronization
// with other threads.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::SetRootResynchronize()
{
DFSSTATUS Status = ERROR_SUCCESS;
DFSSTATUS RootStatus;
Status = AcquireRootLock();
if (Status != ERROR_SUCCESS)
{
return Status;
}
DFS_TRACE_LOW( REFERRAL_SERVER, "Root %p being resynced\n", this);
//
// if the root folder is already marked available, we are done
// otherwise, clear the standby mode, and try to bring this
// root into a useable state.
//
if (!IsRootFolderAvailable())
{
ClearRootFolderStandby();
//
//need to take appropriate locks.
//
RootStatus = Synchronize();
DFS_TRACE_ERROR_LOW( RootStatus, REFERRAL_SERVER, "Set root resync: Synchronize status for %p is %x\n",
this, RootStatus);
}
else
{
ReSynchronize();
}
ReleaseRootLock();
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: UpdateLinkInformation
//
// Arguments:
// DfsMetadataHandle - the parent handle
// LPWSTR ChildName - the child name
//
// Returns: Status: Success or Error status code
//
// Description: This routine reads the metadata for the child and, updates
// the child folder if necessary. This includes adding
// the folder if it does not exist, or if the folder exists
// but the metadata is newer, ensuring that all future
// request use the most upto date data.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::UpdateLinkInformation(
IN DFS_METADATA_HANDLE DfsHandle,
LPWSTR ChildName )
{
DFSSTATUS Status = ERROR_SUCCESS;
ULONG Timeout = 0;
PDFS_NAME_INFORMATION pChild = NULL;
DfsFolder *pChildFolder = NULL;
FILETIME LastModifiedTime;
UNICODE_STRING LinkName;
//
// Now that we have the child name, get the name information
// for this child. This is the logical namespace information
// for this child.
//
Status = GetMetadataStore()->GetMetadataNameInformation( DfsHandle,
ChildName,
&pChild);
if ( Status == ERROR_SUCCESS )
{
Timeout = pChild->Timeout;
//
// we want to ignore the root entry here. hardcode 81 till
// we get the defines in the right place
// dfsdev: fix this.
//if(pChild->Type == (PKT_ENTRY_TYPE_REFERRAL_SVC | PKT_ENTRY_TYPE_CAIRO))
if(pChild->Type == 0x81)
{
SetTimeout(Timeout);
GetMetadataStore()->ReleaseMetadataNameInformation( DfsHandle, pChild );
return Status;
}
}
if (Status == ERROR_SUCCESS)
{
LastModifiedTime = pChild->LastModifiedTime;
//
// Now translate the metadata logical name to a relative
// link name: each store has its own behavior, so
// the getlinkname function is implemented by each store.
//
Status = GetMetadataLogicalToLinkName(&pChild->Prefix,
&LinkName);
if ( Status == ERROR_SUCCESS )
{
Status = LookupFolderByMetadataName( ChildName,
&pChildFolder );
if ( Status == ERROR_SUCCESS )
{
//
// IF we already know this child, check if the child
// has been updated since we last visited it.
// If so, we need to update the child.
//
if ( pChildFolder->UpdateRequired( FILETIMETO64(LastModifiedTime) ) )
{
DFSLOG("Updating child %p\n", pChildFolder );
Status = UpdateLinkFolder( ChildName,
&LinkName,
pChildFolder );
}
//
// we now check if we need to create root directories for even
// those folders that we already know about. This may be true
// when we had one or more errors creating the
// directory when we initially created this folder or it may
// be true when we are going from standby to master.
//
if (IsRootDirectoriesCreated() == FALSE)
{
DFSSTATUS CreateStatus;
CreateStatus = SetupLinkReparsePoint(pChildFolder->GetFolderLogicalNameString());
DFSLOG("CreateStatus is %x\n", CreateStatus);
}
}
else if ( Status == ERROR_NOT_FOUND )
{
//
// We have not seen this child before: create
// a new one.
//
Status = CreateLinkFolder( ChildName,
&LinkName,
&pChildFolder );
DFSLOG("Adding new child %wS, %p status %x",
ChildName, pChildFolder, Status );
}
ReleaseMetadataLogicalToLinkName( &LinkName );
}
//
// Now release the name information of the child.
//
GetMetadataStore()->ReleaseMetadataNameInformation( DfsHandle, pChild );
}
//
// We were successful. We have a child folder that is
// returned to us with a valid reference. Set the Last
// modified time in the folder, and release our reference
// on the child folder.
//
if ( Status == ERROR_SUCCESS )
{
pChildFolder->SetTimeout(Timeout);
pChildFolder->SetUSN( FILETIMETO64(LastModifiedTime) );
pChildFolder->ReleaseReference();
}
return Status;
}
void
DfsRootFolder::GenerateEventLog(DWORD EventMsg,
WORD Substrings,
const TCHAR * apszSubStrings[],
DWORD Errorcode)
{
if(InterlockedIncrement(&_CurrentErrors) < DFS_MAX_ROOT_ERRORS)
{
DfsLogDfsEvent(EventMsg,
Substrings,
apszSubStrings,
Errorcode);
}
}