Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3995 lines
122 KiB

//+----------------------------------------------------------------------------
//
// 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 "srvfsctl.h" //for calling into srv.sys
#include "dfsfsctl.h"
#include "DfsReparse.hxx"
//
// logging includes.
//
#include "DfsRootFolder.tmh"
#define FILETIMETO64(_f) (*(UINT64 *)(&(_f)))
LPWSTR DFSRENAMEPREFIX = L"DFS.";
ULONG DFSDIRWHACKQOFFSET = 4;
extern "C" {
DWORD
I_NetDfsIsThisADomainName(
IN LPWSTR wszName);
}
NTSTATUS
StripAndReturnLastPathComponent(
PUNICODE_STRING pPath,
PUNICODE_STRING pLeaf) ;
DFSSTATUS
SendShareToSrv(PUNICODE_STRING pShareName,
BOOLEAN fAttach) ;
//+-------------------------------------------------------------------------
//
// 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 = 0;
DFSSTATUS Status = ERROR_SUCCESS;
DfsRtlInitUnicodeStringEx( &_DfsNameContext, NULL );
DfsRtlInitUnicodeStringEx( &_LogicalShareName, NULL );
DfsRtlInitUnicodeStringEx( &_RootRegKeyName, NULL );
DfsRtlInitUnicodeStringEx( &_PhysicalShareName, NULL );
DfsRtlInitUnicodeStringEx( &_ShareFilePathName, NULL );
DfsRtlInitUnicodeStringEx( &_DirectoryCreateRootPathName, NULL );
DfsRtlInitUnicodeStringEx( &_DfsVisibleContext, NULL );
_DirectoryCreateError = STATUS_SUCCESS;
_ShareAcquireStatus = STATUS_SUCCESS;
_pMetadataNameTable = NULL;
_pLogicalPrefixTable = NULL;
_IgnoreNameContext = FALSE;
_CreateDirectories = DfsCheckCreateDirectories();
pStatistics = NULL;
_pChildLocks = NULL;
_pRootLock = NULL;
_ChildCount = 0;
_CurrentErrors = 0;
_RootFlags = 0;
_PrefetchNeeded = FALSE;
_LogicalShareAddedToTable = FALSE;
_RootFlavor = 0;
_fRootLockInit = FALSE;
_fpLockInit = FALSE;
_TooManyEventLogErrors = FEWERRORS_ON_ROOT;
ZeroMemory(&_fChildLocksInit, sizeof(_fChildLocksInit));
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 )
{
_fRootLockInit = (BOOLEAN) InitializeCriticalSectionAndSpinCount( _pRootLock, 0);
if(!_fRootLockInit)
{
Status = GetLastError();
}
}
if ( Status == ERROR_SUCCESS )
{
_pLock = new CRITICAL_SECTION;
if ( _pLock == NULL )
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
}
if ( Status == ERROR_SUCCESS )
{
_fpLockInit = (BOOLEAN) InitializeCriticalSectionAndSpinCount( _pLock, DFS_CRIT_SPIN_COUNT );
if(!_fpLockInit)
{
Status = GetLastError();
}
if(Status == ERROR_SUCCESS)
{
_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++ )
{
_fChildLocksInit[LockNum] = (BOOLEAN) InitializeCriticalSectionAndSpinCount( &_pChildLocks[LockNum],DFS_CRIT_SPIN_COUNT );
if(!_fChildLocksInit[LockNum])
{
Status = GetLastError();
break;
}
}
} 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;
//
// we start by assuming we have to use the PDC for AD Blobs.
//
_RootScalability = 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,
IN BOOLEAN CalledByApi )
{
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 DfStatus;
DfStatus = SetupLinkReparsePoint( pChildFolder->GetFolderLogicalNameString() );
if(DfStatus != ERROR_SUCCESS)
{
apszSubStrings[0] = pChildFolder->GetFolderLogicalNameString();
apszSubStrings[1] = GetDirectoryCreatePathName()->Buffer;
GenerateEventLog(DFS_ERROR_CREATE_REPARSEPOINT_FAILURE,
2,
apszSubStrings,
DfStatus);
}
if(CalledByApi)
{
Status = DfStatus;
}
DFS_TRACE_ERROR_LOW(DfStatus, REFERRAL_SERVER, "[%!FUNC!]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();
LoadServerSiteData(pChildFolder);
} 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 = FALSE;
UNREFERENCED_PARAMETER(ChildName);
UNREFERENCED_PARAMETER(pLinkName);
pChildFolder->RemoveReferralData( NULL, &Removed );
pStatistics->UpdateLinkModified();
if (Removed == TRUE)
{
pStatistics->UpdateForcedCacheFlush();
}
LoadServerSiteData(pChildFolder);
//
// 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, "[%!FUNC!] Tear down reparse for %p, status %x \n", pChildFolder, Status );
}
Status = pChildFolder->AcquireWriteLock();
if ( Status == ERROR_SUCCESS )
{
Status = RemoveLinkFolderFromMetadataTable( pChildFolder );
if ( Status == ERROR_SUCCESS )
{
DFSSTATUS LinkRemoveStatus;
pChildFolder->SetFlag( DFS_FOLDER_DELETE_IN_PROGRESS );
DecrementChildCount();
LinkRemoveStatus = RemoveLinkFolderFromLogicalTable( pChildFolder );
DFS_TRACE_ERROR_LOW(LinkRemoveStatus, REFERRAL_SERVER, "Remove Link From logical %p, status %x \n", pChildFolder, LinkRemoveStatus );
}
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: 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: 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.
//
//--------------------------------------------------------------------------
DFSSTATUS
DfsRootFolder::MorphLinkCollision(
PUNICODE_STRING ParentDirectory,
PUNICODE_STRING DirectoryToRename )
{
DFSSTATUS Status = ERROR_SUCCESS;
Status = RenamePath(ParentDirectory,
DirectoryToRename);
return Status;
}
DFSSTATUS
DfsRootFolder::TeardownLinkReparsePoint(
LPWSTR LinkNameString )
{
NTSTATUS NtStatus = STATUS_SUCCESS;
DFSSTATUS Status = ERROR_SUCCESS;
HANDLE DirectoryHandle = NULL;
UNICODE_STRING LinkName;
Status = DfsRtlInitUnicodeStringEx( &LinkName, LinkNameString);
if ((IsRootFolderShareAcquired() == TRUE) &&
(Status == ERROR_SUCCESS))
{
NtStatus = DfsOpenDirectory ( GetDirectoryCreatePathName(),
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
&DirectoryHandle,
NULL );
if (NtStatus == STATUS_SUCCESS)
{
//
// Delete empty parent directories as well.
//
NtStatus = DfsDeleteLinkReparsePointAndParents( &LinkName, DirectoryHandle );
DfsCloseDirectory( DirectoryHandle );
}
Status = RtlNtStatusToDosError(NtStatus);
}
return Status;
}
DFSSTATUS
DfsRootFolder::SetupLinkReparsePoint(
LPWSTR LinkNameString )
{
NTSTATUS NtStatus = STATUS_SUCCESS;
DFSSTATUS Status = ERROR_SUCCESS;
HANDLE DirectoryHandle = NULL;
UNICODE_STRING LinkName;
//
// We don't want to create any reparse points
// if we're not supposed to.
//
if (IsRootCreateDirectories() == FALSE)
return ERROR_SUCCESS;
Status = GetRootShareAcquireStatus();
if(Status == ERROR_SUCCESS)
{
Status = DfsRtlInitUnicodeStringEx( &LinkName, LinkNameString);
if (Status == ERROR_SUCCESS)
{
NtStatus = DfsOpenDirectory ( GetDirectoryCreatePathName(),
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
&DirectoryHandle,
NULL );
if (NtStatus == STATUS_SUCCESS)
{
Status = CreateLinkReparsePoint( &LinkName,
DirectoryHandle );
DfsCloseDirectory( DirectoryHandle );
}
else
{
Status = RtlNtStatusToDosError(NtStatus);
}
}
}
if (Status != ERROR_SUCCESS)
{
SetLastCreateDirectoryError(Status);
}
return Status;
}
DFSSTATUS
DfsRootFolder::CreateLinkReparsePoint(
PUNICODE_STRING pLinkName,
HANDLE RelativeHandle )
{
NTSTATUS NtStatus = STATUS_SUCCESS;
DFSSTATUS MorphStatus = ERROR_SUCCESS;
DFSSTATUS DosStatus = ERROR_SUCCESS;
ULONG ShareMode = 0;
ULONG RetryCount = 0;
HANDLE DirectoryHandle = INVALID_HANDLE_VALUE;
BOOLEAN IsNewlyCreated = FALSE;
UNICODE_STRING LastComp;
Retry:
DfsRtlInitUnicodeStringEx(&LastComp, NULL);
NtStatus = CreateLinkDirectories( pLinkName,
RelativeHandle,
&DirectoryHandle,
&IsNewlyCreated,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE );
if (NtStatus == STATUS_SUCCESS)
{
NtStatus = SetDfsReparsePoint( DirectoryHandle);
NtClose( DirectoryHandle);
if((RetryCount == 0) && (NtStatus == STATUS_DIRECTORY_NOT_EMPTY))
{
StripAndReturnLastPathComponent(pLinkName, &LastComp);
MorphStatus = MorphLinkCollision(pLinkName, &LastComp);
AddNextPathComponent(pLinkName);
if(MorphStatus == ERROR_SUCCESS)
{
RetryCount++;
goto Retry;
}
}
}
DosStatus = RtlNtStatusToDosError(NtStatus);
DFS_TRACE_ERROR_HIGH(DosStatus, REFERRAL_SERVER, "[%!FUNC!] DirectoryName of interest %wZ: Create Reparse point Status %x\n",
pLinkName,
DosStatus);
return DosStatus;
}
NTSTATUS
DfsRootFolder::CreateLinkDirectories(
PUNICODE_STRING pLinkName,
HANDLE RelativeHandle,
PHANDLE pDirectoryHandle,
PBOOLEAN pIsNewlyCreated,
ULONG ShareMode )
{
NTSTATUS NtStatus = STATUS_SUCCESS;
DFSSTATUS Status = ERROR_SUCCESS;
HANDLE CurrentDirectory = INVALID_HANDLE_VALUE;
HANDLE LocalRelativeHandle = INVALID_HANDLE_VALUE;
BOOLEAN DfsMountPoint = FALSE;
BOOLEAN NewlyCreated = FALSE;
UNICODE_STRING RemainingName;
UNICODE_STRING TempString;
UNICODE_STRING DirectoryToCreate;
UNICODE_STRING TrackDirectory;
TrackDirectory = *pLinkName;
TrackDirectory.Length = 0;
LocalRelativeHandle = RelativeHandle;
NtStatus = DfsGetFirstComponent(pLinkName,
&DirectoryToCreate,
&RemainingName);
while ( NtStatus == STATUS_SUCCESS)
{
NtStatus = DfsOpenDirectory( &DirectoryToCreate,
ShareMode,
LocalRelativeHandle,
&CurrentDirectory,
&NewlyCreated );
if (NtStatus == STATUS_SUCCESS)
{
AddNextPathComponent(&TrackDirectory);
if(LocalRelativeHandle != RelativeHandle)
{
DfsCloseDirectory( LocalRelativeHandle );
}
LocalRelativeHandle = CurrentDirectory;
CurrentDirectory = INVALID_HANDLE_VALUE;
if(RemainingName.Length == 0)
{
break;
}
NtStatus = DfsGetNextComponent(&RemainingName,
&DirectoryToCreate,
&TempString);
RemainingName = TempString;
}
else if(NtStatus == STATUS_NOT_A_DIRECTORY)
{
Status = MorphLinkCollision(&TrackDirectory,
&DirectoryToCreate);
if(Status == ERROR_SUCCESS)
{
NtStatus = STATUS_SUCCESS;
}
}
}
if (NtStatus == STATUS_SUCCESS)
{
IsDirectoryMountPoint(LocalRelativeHandle,
&DfsMountPoint);
if(DfsMountPoint)
{
NtStatus = STATUS_DIRECTORY_IS_A_REPARSE_POINT;
if(LocalRelativeHandle != INVALID_HANDLE_VALUE)
{
DfsCloseDirectory( LocalRelativeHandle );
LocalRelativeHandle = INVALID_HANDLE_VALUE;
}
}
else
{
if(pDirectoryHandle)
{
*pDirectoryHandle = LocalRelativeHandle;
}
else
{
DfsCloseDirectory( LocalRelativeHandle );
}
if(pIsNewlyCreated)
{
*pIsNewlyCreated = NewlyCreated;
}
}
}
else
{
if(LocalRelativeHandle != RelativeHandle)
{
if(LocalRelativeHandle != INVALID_HANDLE_VALUE)
{
DfsCloseDirectory( LocalRelativeHandle );
LocalRelativeHandle = INVALID_HANDLE_VALUE;
}
}
}
DFS_TRACE_ERROR_LOW(NtStatus, REFERRAL_SERVER, "CreateLinkDirectory: %wZ: Status %x\n",
pLinkName, NtStatus );
return NtStatus;
}
#define WHACKWHACKDOTWHACKDIROFFSET 14
//the code below attempt to see if the directory, pointed to by pDirectory
//name is a mount point. The name comes in looking like \\??\\c:\directoryname.
//To see if this name is a mount point, we have to open up the parent directory,
//and then try to create the subdirectory. In the name above, \\??\\c:\ is the
//parent directory and directoryname is the subdirectory.WHACKWHACKDOTWHACKDIROFFSET
//is the length of \\??\\c:\. This is the RootDir in the below code. The remaining
//name is the rest of the string
NTSTATUS
DfsRootFolder::IsRootShareMountPoint(PUNICODE_STRING pDirectoryName)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
HANDLE DirHandle = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING RemainingName;
UNICODE_STRING RootDir;
RootDir = RemainingName = * pDirectoryName;
//setup the root directory length (i.e \\??\\c:\)
RootDir.Length = (USHORT) WHACKWHACKDOTWHACKDIROFFSET;
//next, setup the renmaining piece of the directory
RemainingName.Length -= (USHORT) WHACKWHACKDOTWHACKDIROFFSET;
RemainingName.MaximumLength -= (USHORT)WHACKWHACKDOTWHACKDIROFFSET;
RemainingName.Buffer = &RemainingName.Buffer[WHACKWHACKDOTWHACKDIROFFSET/sizeof(WCHAR)];
//open the root directory
InitializeObjectAttributes ( &ObjectAttributes,
&RootDir,
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)
{
//now, if there is still a path left, try to create this directory.
if(RemainingName.Length != 0)
{
NtStatus = CreateLinkDirectories(&RemainingName,
DirHandle,
NULL,
NULL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE);
}
CloseHandle(DirHandle);
}
return NtStatus;
}
//+-------------------------------------------------------------------------
//
// 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;
PUNICODE_STRING pLogicalShare = NULL;
BOOLEAN SubStringMatch = FALSE;
BOOLEAN Inserted = FALSE;
BOOLEAN PhysicalShareInDFS = FALSE;
BOOLEAN DfsMountPoint = FALSE;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
ULONG pAttribInfoSize;
PFILE_FS_ATTRIBUTE_INFORMATION pAttribInfo = NULL;
UNICODE_STRING LogicalVol;
const TCHAR * apszSubStrings[4];
ZeroMemory (&LogicalVol, sizeof(LogicalVol));
pUseShare = GetRootPhysicalShareName();
pLogicalShare = GetLogicalShare();
DFS_TRACE_LOW(REFERRAL_SERVER, "[%!FUNC!] 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) ||
(IsRootCreateDirectories() == 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,
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)
{
NtStatus = STATUS_NOT_SUPPORTED;
apszSubStrings[0] = pUseShare->Buffer;
apszSubStrings[1] = pDirectoryName->Buffer;
GenerateEventLog(DFS_ERROR_UNSUPPORTED_FILESYSTEM,
2,
apszSubStrings,
0);
}
}
CloseHandle (DirHandle);
}
else
{
Status = RtlNtStatusToDosError(NtStatus);
DFS_TRACE_ERROR_HIGH(Status, REFERRAL_SERVER, "[%!FUNC!] - NTOPEN failed share for root %p, (%wZ) (%wZ) status %x NtStatus %x\n",
this, pUseShare, pDirectoryName, Status, NtStatus);
}
if (NtStatus == STATUS_SUCCESS)
{
NtStatus = IsRootShareMountPoint(pDirectoryName);
}
if( NtStatus != STATUS_SUCCESS)
{
Status = RtlNtStatusToDosError(NtStatus);
}
delete [] pAttribInfo;
pAttribInfo = NULL;
DFS_TRACE_ERROR_HIGH(Status, REFERRAL_SERVER, "AcquireRoot - IsRootShareMountPoint failed share for root %p, (%wZ) (%wZ) status %x NtStatus %x\n",
this, pUseShare, pDirectoryName, Status, NtStatus);
}
else {
Status = ERROR_NOT_ENOUGH_MEMORY;
}
}
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "[%!FUNC!] 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
{
Status = ERROR_BAD_PATHNAME;
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, "[%!FUNC!]Root %p, user mode attach status %x\n", this, Status);
if(Status == ERROR_SUCCESS)
{
PhysicalShareInDFS = TRUE;
if (RtlCompareUnicodeString(pUseShare, pLogicalShare, TRUE ))
{
Status = DfsUserModeAttachToFilesystem( &LogicalVol,
pLogicalShare);
if(Status == ERROR_SUCCESS)
{
_LogicalShareAddedToTable = TRUE;
}
}
}
}
if(Status == ERROR_SUCCESS)
{
Status = SendShareToSrv(pUseShare, TRUE);
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "[%!FUNC!]Root %p, user mode SendShareToSrv status %x\n", this, Status);
}
//
// if we are successful, we acquired the root share, now mark
// our state accordingly.
//
if (Status == ERROR_SUCCESS)
{
SetRootFolderShareAcquired();
SetRootShareAcquireStatus(Status);
}
else
{
//
// otherwise, clear up some of the work we just did.
//
ClearRootFolderShareAcquired();
SetRootShareAcquireStatus(Status);
if (Inserted == TRUE)
{
DfsRemoveKnownDirectoryPath( pDirectoryName,
pUseShare);
}
if(PhysicalShareInDFS == TRUE)
{ DFSSTATUS LocalStatus = ERROR_SUCCESS;
LocalStatus = DfsUserModeDetachFromFilesystem( pDirectoryName,
pUseShare);
}
if(_LogicalShareAddedToTable == TRUE)
{ DFSSTATUS LocalStatus = ERROR_SUCCESS;
LocalStatus = DfsUserModeDetachFromFilesystem( &LogicalVol,
pUseShare);
}
}
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "[%!FUNC!]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;
PUNICODE_STRING pLogicalShare = NULL;
PVOID pData = NULL;
BOOLEAN SubStringMatch = FALSE;
UNICODE_STRING LogicalVol;
DFS_TRACE_LOW(REFERRAL_SERVER, "[%!FUNC!]ReleaseRoot share for root %p\n", this);
if (IsRootFolderShareAcquired() == TRUE)
{
//
// get the logical share, and the physical directory backing the
// share.
//
ZeroMemory (&LogicalVol, sizeof(LogicalVol));
pUseShare = GetRootPhysicalShareName();
pLogicalShare = GetLogicalShare();
pDirectoryName = GetDirectoryCreatePathName();
if ( (pDirectoryName == NULL) ||
(pDirectoryName->Buffer == NULL) ||
(pDirectoryName->Length == 0) )
{
Status = ERROR_INVALID_PARAMETER;
}
if(Status == ERROR_SUCCESS)
{
Status = SendShareToSrv(pUseShare, FALSE);
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "[%!FUNC!]SendShareToSrv path %wZ, %wZ: Status %x\n",
pDirectoryName, pUseShare, Status );
}
//
// 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, "[%!FUNC!]user mode detach path %wZ, %wZ: Status %x\n",
pDirectoryName, pUseShare, Status );
}
if (Status == ERROR_SUCCESS)
{
if(_LogicalShareAddedToTable == TRUE)
{ DFSSTATUS LocalStatus = ERROR_SUCCESS;
LocalStatus = DfsUserModeDetachFromFilesystem( &LogicalVol,
pUseShare);
}
}
//
// 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, "[%!FUNC!]RemoveKnownDirectory path %wZ, %wZ: Status %x\n",
pDirectoryName, pUseShare, RemoveStatus );
}
if (Status == ERROR_SUCCESS)
{
ClearRootFolderShareAcquired();
}
}
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "[%!FUNC!]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 = ERROR_SUCCESS;
BOOLEAN IsDomainDfs = FALSE;
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)
{
GetVisibleNameContextLocked( NULL, &VisibleNameContext );
Status = GetMetadataStore()->GenerateMetadataLogicalName( &VisibleNameContext,
pLogicalName,
&UseName );
if (Status == ERROR_SUCCESS)
{
GetMetadataStore()->StoreInitializeNameInformation( &NameInfo,
&UseName,
&NewUid,
Comment );
if (DfsIsThisARealDfsName(ReplicaServer, ReplicaPath, &IsDomainDfs) == ERROR_SUCCESS)
{
NameInfo.Type |= (PKT_ENTRY_TYPE_OUTSIDE_MY_DOM);
}
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)
{
DFS_TRACE_ERROR_HIGH(Status, API, "DfsRootFolder::AddMetadataLink path %wZ, %ws, %ws: Status %x\n",
pLogicalName, ReplicaServer, ReplicaPath, Status );
ReSynchronize(TRUE);
}
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)
{
Status = UpdateLinkInformation( RootHandle,
LinkMetadataName.Buffer,
TRUE );
if(Status != ERROR_SUCCESS)
{
DFS_TRACE_ERROR_HIGH(Status, API, "DfsRootFolder::AddMetadataLink (2) path %wZ, %ws, %ws: Status %x\n",
pLogicalName, ReplicaServer, ReplicaPath, Status );
}
}
}
//
// 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 )
{
DFSSTATUS Status = ERROR_SUCCESS;
LPWSTR LinkMetadataName = NULL;
DfsFolder *pFolder = NULL;
DFS_METADATA_HANDLE RootHandle;
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;
DFS_TRACE_ERROR_HIGH(Status, API, "DfsRootFolder::RemoveMetadataLink (1) path %wZ: Status %x\n",
pLinkName, Status );
}
//
// 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)
{
DFS_TRACE_ERROR_HIGH(Status, API, "DfsRootFolder::RemoveMetadataLink path %wZ: Status %x\n",
pLinkName, Status );
ReSynchronize(TRUE);
}
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 = NULL;
BOOLEAN IsDomainDfs = FALSE;
LPWSTR LinkMetadataName = NULL;
UNICODE_STRING Remaining;
//
// 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 ( (Status == ERROR_SUCCESS) &&
(I_NetDfsIsThisADomainName(ReplicaServer) == ERROR_SUCCESS))
{
//
//dfsdev: include in this check nameinfo->type == OUTSIDE_MY_DOM.
//
pFolder->ReleaseReference();
Status = ERROR_NOT_SUPPORTED;
DFS_TRACE_ERROR_HIGH(Status, API, "DfsRootFolder::AddMetadataLinkReplica (1) path %wZ, %ws, %ws: Status %x\n",
pLinkName, ReplicaServer, ReplicaPath, Status );
}
//
// 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)
{
DFS_TRACE_ERROR_HIGH(Status, API, "DfsRootFolder::AddMetadataLinkReplica path %wZ, %ws, %ws: Status %x\n",
pLinkName, ReplicaServer, ReplicaPath, Status );
ReSynchronize(TRUE);
}
//
// 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 )
{
DFSSTATUS Status = ERROR_SUCCESS;
LPWSTR LinkMetadataName = NULL;
DfsFolder *pFolder = NULL;
DFS_METADATA_HANDLE RootHandle;
UNICODE_STRING Remaining;
//
// 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) && (Status != ERROR_LAST_ADMIN))
{
DFS_TRACE_ERROR_HIGH(Status, API, "DfsRootFolder::RemoveMetadataLinkReplica path %wZ, %ws, %ws: Status %x\n",
pLinkName, ReplicaServer, ReplicaPath, Status );
ReSynchronize(TRUE);
}
//
// 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 );
}
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 = ERROR_SUCCESS;
DFS_METADATA_HANDLE RootHandle = NULL;
UNICODE_STRING VisibleNameContext;
UNICODE_STRING DfsPath;
Status = AcquireRootLock();
if (Status != ERROR_SUCCESS)
{
return Status;
}
Status = DfsRtlInitUnicodeStringEx( &DfsPath,
DfsPathName );
if(Status == ERROR_SUCCESS)
{
//
// 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.
//
GetVisibleNameContextLocked( 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.
//
//
GetVisibleNameContextLocked( 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;
}
DFSSTATUS
DfsRootFolder::ExtendedRootAttributes(
PULONG pAttr,
PUNICODE_STRING pRemainingName,
BOOLEAN Set)
{
DFSSTATUS Status;
DFS_METADATA_HANDLE RootHandle;
LPWSTR MetadataName = NULL;
/*
we get RESETs too (Set == TRUE). INSITE_ONLY flag
is masked out in those. So there's no way to easily
do this check. BUG 603118
if (Set &&
!IsEmptyUnicodeString(pRemainingName) &&
(*pAttr != PKT_ENTRY_TYPE_INSITE_ONLY))
{
return ERROR_INVALID_PARAMETER;
}
*/
Status = AcquireRootLock();
if (Status != ERROR_SUCCESS)
{
return Status;
}
Status = GetMetadataHandle( &RootHandle );
if (Status == ERROR_SUCCESS)
{
if (!IsEmptyUnicodeString(pRemainingName))
{
PUNICODE_STRING pLinkName = pRemainingName;
UNICODE_STRING Remaining;
DfsFolder *pFolder = NULL;
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.
//
}
}
}
if (Status == ERROR_SUCCESS)
{
if (Set)
{
Status = GetMetadataStore()->SetExtendedAttributes( RootHandle,
MetadataName,
*pAttr);
}
else
{
Status = GetMetadataStore()->GetExtendedAttributes( RootHandle,
MetadataName,
pAttr);
}
}
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 = ERROR_SUCCESS;
DFS_METADATA_HANDLE RootHandle = NULL;
LPWSTR MetadataName = NULL;
DfsFolder *pFolder = NULL;
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;
}
//
// 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.
//
}
}
if (Status == ERROR_SUCCESS)
{
if ((Level == 101) && (MetadataName == NULL))
{
Status = ERROR_NOT_SUPPORTED;
}
else
{
//
// 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)
{
DFS_TRACE_ERROR_HIGH(Status, API, "DfsRootFolder::SetApiInformation path %wZ, %ws, %ws: Status %x\n",
pLinkName, Server, Share, Status );
ReSynchronize(TRUE);
}
else
{
Status = UpdateFolderInformation(RootHandle,
MetadataName,
pFolder);
}
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) &&
(MetadataName != NULL))
{
DFSSTATUS UpdateStatus;
UpdateStatus = UpdateLinkFolder( MetadataName,
pLinkName,
pFolder );
//
// dfsdev: log the update state.
//
}
}
if (pFolder != NULL) {
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, "[%!FUNC!]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 = NULL;
PDFS_REPLICA_INFORMATION pReplicaInfo = NULL;
PUNICODE_STRING pServerName = NULL;
DfsReplica *pReplica = NULL;
DFSSTATUS Status = ERROR_SUCCESS;
ULONG Replica = 0;
ULONG NumReplicas = 0;
BOOLEAN CacheHit = FALSE;
DFS_TRACE_LOW( REFERRAL_SERVER, "Load Replica Referral Data %ws, for %p\n", MetadataName, pReferralData);
pReferralData->pReplicas = NULL;
//
// Get the replica information.
//
if(!pReferralData->FolderOffLine)
{
Status = GetMetadataStore()->GetMetadataReplicaInformation(RootMetadataHandle,
MetadataName,
&pReplicaListInfo );
if(Status == ERROR_SUCCESS)
{
NumReplicas = pReplicaListInfo->ReplicaCount;
}
}
if ( Status == ERROR_SUCCESS )
{
//
// Set the appropriate count, and allocate the replicas
// required.
//
pReferralData->ReplicaCount = NumReplicas;
if (pReferralData->ReplicaCount > 0)
{
pReferralData->pReplicas = new DfsReplica [ NumReplicas ];
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 < NumReplicas) && (Status == ERROR_SUCCESS);
Replica++ )
{
UNICODE_STRING UseName;
RtlInitUnicodeString(&UseName, NULL);
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))
{
Status = GetVisibleNameContext( NULL,
&UseName);
if (Status == ERROR_SUCCESS)
{
*pServerName = UseName;
}
}
if ( Status == ERROR_SUCCESS )
{
Status = pReplica->SetTargetServer( pServerName, &CacheHit );
}
if ( Status == ERROR_SUCCESS )
{
pStatistics->UpdateServerSiteStat(CacheHit);
Status = pReplica->SetTargetFolder( &pReplicaInfo->ShareName );
}
if ( Status == ERROR_SUCCESS )
{
if ( pReplicaInfo->ReplicaState & REPLICA_STORAGE_STATE_OFFLINE )
{
pReplica->SetTargetOffline();
}
}
if (UseName.Length != 0)
{
DfsFreeUnicodeString(&UseName);
RtlInitUnicodeString(&UseName, NULL);
}
}
}
//
// Now release the replica information that was allocated
// by the store.
//
if(pReplicaListInfo)
{
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, "[%!FUNC!]Root %p being set to standby\n", this);
if (IsRootFolderStandby() == FALSE)
{
//
// dfsdev:: ignore error returns from these calls?
//
DFSSTATUS ReleaseStatus;
RemoveAllLinkFolders( TRUE ); // permanent removal
ReleaseStatus = ReleaseRootShareDirectory();
DFS_TRACE_ERROR_LOW( ReleaseStatus, REFERRAL_SERVER, "[%!FUNC!]Release root share status %x\n", ReleaseStatus);
SetRootFolderStandby();
}
else
{
DFS_TRACE_LOW( REFERRAL_SERVER, "[%!FUNC!]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, "[%!FUNC!]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, "[%!FUNC!]Set root resync: Synchronize status for %p is %x\n",
this, RootStatus);
}
else
{
//
// Synchronize without the FORCE flag here, because we don't want to
// get the entire blob unless the GUID has changed. This will still result in
// a minimum of one network call to get the GUID.
//
RootStatus = ReSynchronize();
DFS_TRACE_ERROR_LOW( RootStatus, REFERRAL_SERVER, "[%!FUNC!]Set root resync: Synchronize status for %p is %x\n",
this, RootStatus);
}
ReleaseRootLock();
return Status;
}
DFSSTATUS
DfsRootFolder::UpdateFolderInformation(
IN DFS_METADATA_HANDLE DfsHandle,
LPWSTR ChildName,
DfsFolder *pChildFolder)
{
DFSSTATUS Status = ERROR_SUCCESS;
ULONG Timeout = 0;
PDFS_NAME_INFORMATION pChild = NULL;
ULONG ChildType = 0;
ULONG ChildState = 0;
FILETIME LastModifiedTime;
Status = GetMetadataStore()->GetMetadataNameInformation( DfsHandle,
ChildName,
&pChild);
if(Status == ERROR_SUCCESS)
{
ChildType = pChild->Type;
ChildState = pChild->State;
Timeout = pChild->Timeout;
LastModifiedTime = pChild->LastModifiedTime;
if( pChildFolder != NULL)
{
if (ChildType & PKT_ENTRY_TYPE_OUTSIDE_MY_DOM)
{
pChildFolder->SetFlag( DFS_FOLDER_OUT_OF_DOMAIN );
}
else
{
pChildFolder->ResetFlag( DFS_FOLDER_OUT_OF_DOMAIN );
}
if((ChildState & DFS_VOLUME_STATE_OFFLINE) == DFS_VOLUME_STATE_OFFLINE)
{
pChildFolder->SetFlag( DFS_FOLDER_OFFLINE );
}
else
{
pChildFolder->ResetFlag( DFS_FOLDER_OFFLINE );
}
pChildFolder->SetTimeout(Timeout);
pChildFolder->SetUSN( FILETIMETO64(LastModifiedTime) );
}
else
{
SetTimeout(Timeout);
SetUSN( FILETIMETO64(LastModifiedTime) );
}
GetMetadataStore()->ReleaseMetadataNameInformation( DfsHandle, pChild );
}
//
// Now that we have the child name, get the name information
// for this child. This is the logical namespace information
// for this child.
//
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,
BOOLEAN CalledByApi )
{
DFSSTATUS Status = ERROR_SUCCESS;
ULONG Timeout = 0;
PDFS_NAME_INFORMATION pChild = NULL;
DfsFolder *pChildFolder = NULL;
FILETIME LastModifiedTime;
ULONG ChildType = 0;
ULONG ChildState = 0;
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 )
{
ChildType = pChild->Type;
ChildState = pChild->State;
Timeout = pChild->Timeout;
if ((ChildType & (PKT_ENTRY_TYPE_REFERRAL_SVC | PKT_ENTRY_TYPE_DFS)) ==
(PKT_ENTRY_TYPE_REFERRAL_SVC | PKT_ENTRY_TYPE_DFS))
{
SetTimeout(Timeout);
if (ChildType & PKT_ENTRY_TYPE_INSITE_ONLY)
{
SetFlag( DFS_FOLDER_INSITE_REFERRALS );
}
else
{
ResetFlag( DFS_FOLDER_INSITE_REFERRALS );
}
// If site-costing is turned on for this root, mark it so.
if (ChildType & PKT_ENTRY_TYPE_COST_BASED_SITE_SELECTION)
{
SetFlag( DFS_FOLDER_COST_BASED_SITE_SELECTION );
}
else
{
ResetFlag( DFS_FOLDER_COST_BASED_SITE_SELECTION );
}
if (ChildType & PKT_ENTRY_TYPE_ROOT_SCALABILITY)
{
SetRootScalabilityMode();
}
else
{
ResetRootScalabilityMode();
}
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) ) )
{
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());
}
}
else if ( Status == ERROR_NOT_FOUND )
{
//
// We have not seen this child before: create
// a new one.
//
Status = CreateLinkFolder( ChildName,
&LinkName,
&pChildFolder,
CalledByApi );
if(CalledByApi)
{
if(Status != ERROR_SUCCESS)
{
DFSSTATUS RemoveStatus = ERROR_SUCCESS;
DFS_TRACE_ERROR_HIGH(Status, API, "DfsRootFolder::UpdateLinkInformation (1) %ws: Status %x\n",
ChildName, Status );
RemoveStatus = RemoveMetadataLink(&LinkName);
if(RemoveStatus != ERROR_SUCCESS)
{
DFS_TRACE_ERROR_HIGH(Status, API, "DfsRootFolder::UpdateLinkInformation (2)%ws: Status %x\n",
ChildName, Status );
ReSynchronize(TRUE);
}
}
}
}
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 )
{
if (ChildType & PKT_ENTRY_TYPE_OUTSIDE_MY_DOM)
{
pChildFolder->SetFlag( DFS_FOLDER_OUT_OF_DOMAIN );
}
else
{
pChildFolder->ResetFlag( DFS_FOLDER_OUT_OF_DOMAIN );
}
if (ChildType & PKT_ENTRY_TYPE_INSITE_ONLY)
{
pChildFolder->SetFlag( DFS_FOLDER_INSITE_REFERRALS );
}
else
{
pChildFolder->ResetFlag( DFS_FOLDER_INSITE_REFERRALS );
}
if((ChildState & DFS_VOLUME_STATE_OFFLINE) == DFS_VOLUME_STATE_OFFLINE)
{
pChildFolder->SetFlag( DFS_FOLDER_OFFLINE );
}
else
{
pChildFolder->ResetFlag( DFS_FOLDER_OFFLINE );
}
pChildFolder->SetTimeout(Timeout);
pChildFolder->SetUSN( FILETIMETO64(LastModifiedTime) );
pChildFolder->ReleaseReference();
}
return Status;
}
void
DfsRootFolder::GenerateEventLog(DWORD EventMsg,
WORD Substrings,
const TCHAR * apszSubStrings[],
DWORD Errorcode)
{
LONG RetVal = FEWERRORS_ON_ROOT;
if(InterlockedIncrement(&_CurrentErrors) < DFS_MAX_ROOT_ERRORS)
{
DfsLogDfsEvent(EventMsg,
Substrings,
apszSubStrings,
Errorcode);
}
else
{
const TCHAR * apszErrSubStrings[1];
RetVal = InterlockedCompareExchange(&_TooManyEventLogErrors,
TOOMANY_ERRORS_ON_ROOT,
FEWERRORS_ON_ROOT);
if(RetVal == FEWERRORS_ON_ROOT)
{
apszErrSubStrings[0] = GetLogicalShare()->Buffer;
DfsLogDfsEvent(DFS_ERROR_TOO_MANY_ERRORS,
1,
apszErrSubStrings,
0);
}
}
}
NTSTATUS
DfsRootFolder::IsDirectoryMountPoint(
IN HANDLE DirHandle,
OUT PBOOLEAN pDfsMountPoint )
{
NTSTATUS NtStatus = STATUS_SUCCESS;
FILE_BASIC_INFORMATION BasicInfo;
IO_STATUS_BLOCK IoStatusBlock;
//
//we assume these are not reparse points.
//
*pDfsMountPoint = 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;
NtStatus = NtQueryInformationFile( DirHandle,
&IoStatusBlock,
(PVOID)&FileTagInformation,
sizeof(FileTagInformation),
FileAttributeTagInformation );
if (NtStatus == STATUS_SUCCESS)
{
//
// Checkif the tag indicates its a mount point,
// and setup the return accordingly.
//
if (FileTagInformation.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
{
*pDfsMountPoint = TRUE;
}
}
}
}
return NtStatus;
}
DFSSTATUS
DfsRootFolder::PrefetchReplicaData(DfsFolder *pChildFolder)
{
DFSSTATUS Status = ERROR_SUCCESS;
DFS_METADATA_HANDLE RootMetadataHandle;
Status = GetMetadataHandle( &RootMetadataHandle );
if(Status == ERROR_SUCCESS)
{
Status = LoadCachedServerSiteData(RootMetadataHandle,
pChildFolder->GetFolderMetadataNameString());
ReleaseMetadataHandle( RootMetadataHandle );
}
return Status;
}
DFSSTATUS
DfsRootFolder::LoadServerSiteData(DfsFolder *pChildFolder)
{
DFSSTATUS Status = ERROR_SUCCESS;
if(DfsServerGlobalData.IsStartupProcessingDone)
{
if(_PrefetchNeeded == FALSE)
{
_PrefetchNeeded = TRUE;
}
}
if(!_PrefetchNeeded)
{
return Status;
}
Status = PrefetchReplicaData(pChildFolder);
return Status;
}
DFSSTATUS
DfsRootFolder::LoadCachedServerSiteData(
DFS_METADATA_HANDLE RootMetadataHandle,
LPWSTR MetadataName)
{
PDFS_REPLICA_LIST_INFORMATION pReplicaListInfo = NULL;
PDFS_REPLICA_INFORMATION pReplicaInfo = NULL;
PUNICODE_STRING pServerName = NULL;
DfsReplica *pReplica = NULL;
DfsReplica *pReplicaList = NULL;
DfsServerSiteInfo *pInfo = NULL;
DFSSTATUS Status = ERROR_SUCCESS;
ULONG Replica = 0;
ULONG NumLocalReplicas = 0;
BOOLEAN CacheHit = FALSE;
DFS_TRACE_LOW( SITE, "LoadCachedServerSiteData %ws\n", MetadataName);
//
// Get the replica information.
//
Status = GetMetadataStore()->GetMetadataReplicaInformation(RootMetadataHandle,
MetadataName,
&pReplicaListInfo );
if ( Status == ERROR_SUCCESS )
{
//
// Set the appropriate count, and allocate the replicas
// required.
//
NumLocalReplicas = pReplicaListInfo->ReplicaCount;
if (NumLocalReplicas > 0)
{
pReplicaList = new DfsReplica [ pReplicaListInfo->ReplicaCount ];
if ( pReplicaList == 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++ )
{
UNICODE_STRING UseName;
RtlInitUnicodeString(&UseName, NULL);
pReplicaInfo = &pReplicaListInfo->pReplicas[Replica];
pReplica = &pReplicaList[ 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))
{
Status = GetVisibleNameContext( NULL,
&UseName);
if (Status == ERROR_SUCCESS)
{
*pServerName = UseName;
}
}
if ( Status == ERROR_SUCCESS )
{
Status = DfsGetServerInfo (pServerName, &pInfo, &CacheHit, TRUE);
}
if(Status == ERROR_SUCCESS)
{
DFS_TRACE_LOW( SITE, "LoadCachedServerSiteData: Server %wZ <->Site %ws\n",
pServerName, pInfo->GetSiteNameString());
pInfo->ReleaseReference();
}
if (UseName.Length != 0)
{
DfsFreeUnicodeString(&UseName);
RtlInitUnicodeString(&UseName, NULL);
}
}
}
//
// Now release the replica information that was allocated
// by the store.
//
if(pReplicaListInfo != NULL)
{
GetMetadataStore()->ReleaseMetadataReplicaInformation( RootMetadataHandle,
pReplicaListInfo );
}
if(pReplicaList)
{
delete []pReplicaList;
}
DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "Done with LoadCachedServerSiteData %ws, Status %x\n",
MetadataName, Status);
return Status;
}
//this callback should not hold any child folder locks
void PrefetchCallBack(LPVOID pEntry, LPVOID pContext)
{
DfsFolder *pFolder = (DfsFolder *) pEntry;
DfsRootFolder * pRootFolder = (DfsRootFolder *) pContext;
if (!DfsIsShuttingDown())
{
if(pFolder != NULL)
{
pRootFolder->PrefetchReplicaData(pFolder);
}
}
}
DFSSTATUS
DfsRootFolder::PreloadServerSiteData(void)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
NtStatus = DfsPrefixTableAcquireWriteLock( _pLogicalPrefixTable );
if (NtStatus == STATUS_SUCCESS)
{
DfsEnumeratePrefixTableLocked(_pLogicalPrefixTable, PrefetchCallBack, this);
DfsPrefixTableReleaseLock( _pLogicalPrefixTable );
}
_PrefetchNeeded = TRUE;
return NtStatus;
}
DFSSTATUS
DfsRootFolder::ValidateAPiShortName(PUNICODE_STRING pLinkName)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
DFSSTATUS Status = ERROR_SUCCESS;
HANDLE DirectoryHandle = NULL;
DWORD NumComponentsExpanded = 0;
UNICODE_STRING NewLinkName;
NewLinkName.Buffer = NULL;
NewLinkName.Length = NewLinkName.MaximumLength = 0;
if(IsAShortName(pLinkName))
{
NtStatus = DfsOpenDirectory ( GetDirectoryCreatePathName(),
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
&DirectoryHandle,
NULL );
if(NtStatus == STATUS_SUCCESS)
{
NtStatus = ExpandLinkDirectories(pLinkName,
&NewLinkName,
DirectoryHandle,
TRUE,
&NumComponentsExpanded);
if(NtStatus == STATUS_NAME_TOO_LONG)
{
Status = ERROR_BAD_PATHNAME;
}
else
{
Status = ERROR_SUCCESS;
}
CloseHandle (DirectoryHandle);
}
if(NewLinkName.Buffer)
{
delete [] NewLinkName.Buffer;
}
}
return Status;
}
DFSSTATUS
DfsRootFolder::ExpandShortName(PUNICODE_STRING pLinkName,
PUNICODE_STRING pNewPath,
PUNICODE_STRING pRemainingName)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
DFSSTATUS Status = ERROR_NOT_FOUND;
HANDLE DirectoryHandle = NULL;
DWORD NumComponentsExpanded = 0;
pNewPath->Length = 0;
pNewPath->MaximumLength = 0;
pNewPath->Buffer = NULL;
if(IsAShortName(pLinkName))
{
NtStatus = DfsOpenDirectory ( GetDirectoryCreatePathName(),
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
&DirectoryHandle,
NULL );
if(NtStatus == STATUS_SUCCESS)
{
NtStatus = ExpandLinkDirectories(pLinkName,
pNewPath,
DirectoryHandle,
FALSE,
&NumComponentsExpanded);
if((NtStatus == STATUS_DIRECTORY_IS_A_REPARSE_POINT) ||
(NtStatus == STATUS_NO_SUCH_FILE))
{
NtStatus = STATUS_SUCCESS;
}
CloseHandle (DirectoryHandle);
}
Status = RtlNtStatusToDosError(NtStatus);
if((Status == ERROR_SUCCESS) && NumComponentsExpanded && pRemainingName)
{
NtStatus = FindRemaingName(pLinkName,
pRemainingName,
NumComponentsExpanded);
Status = RtlNtStatusToDosError(NtStatus);
}
}
return Status;
}
NTSTATUS
DfsRootFolder::FindRemaingName(PUNICODE_STRING pLinkName,
PUNICODE_STRING pRemainingName,
DWORD NumComponentsExpanded)
{
DWORD LoopVar = 0;
NTSTATUS NtStatus = STATUS_SUCCESS;
UNICODE_STRING RemainingName;
UNICODE_STRING RemPath;
UNICODE_STRING TempString;
RemainingName = *pLinkName;
for (LoopVar = 0; LoopVar < NumComponentsExpanded; LoopVar++)
{
NtStatus = DfsGetNextComponent(&RemainingName,
&RemPath,
&TempString);
RemainingName = TempString;
}
*pRemainingName = RemainingName;
return NtStatus;
}
NTSTATUS
DfsRootFolder::ExpandLinkDirectories(
PUNICODE_STRING pLinkName,
PUNICODE_STRING pNewPath,
HANDLE RelativeHandle,
BOOLEAN FailOnExpand,
DWORD * NumComponentsExpanded)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
DFSSTATUS Status = ERROR_SUCCESS;
HANDLE CurrentDirectory = INVALID_HANDLE_VALUE;
HANDLE LocalRelativeHandle = INVALID_HANDLE_VALUE;
ULONG ShareMode = 0;
DWORD NumExpanded = 0;
BOOLEAN Expanded = FALSE;
BOOLEAN NewlyCreated = FALSE;
UNICODE_STRING RemainingName;
UNICODE_STRING TempString;
UNICODE_STRING DirectoryToCreate;
*NumComponentsExpanded = 0;
ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
LocalRelativeHandle = RelativeHandle;
RemainingName = *pLinkName;
while ( NtStatus == STATUS_SUCCESS)
{
NtStatus = DfsGetNextComponent(&RemainingName,
&DirectoryToCreate,
&TempString);
RemainingName = TempString;
Expanded = FALSE;
NtStatus = GetAShortName(LocalRelativeHandle,
&Expanded,
&DirectoryToCreate,
pNewPath);
if (NtStatus != STATUS_SUCCESS)
{
break;
}
NumExpanded++;
if( FailOnExpand && Expanded)
{
NtStatus = STATUS_NAME_TOO_LONG;
break;
}
NtStatus = DfsOpenDirectory( &DirectoryToCreate,
ShareMode,
LocalRelativeHandle,
&CurrentDirectory,
&NewlyCreated );
if (NtStatus == STATUS_SUCCESS)
{
if(LocalRelativeHandle != RelativeHandle)
{
DfsCloseDirectory( LocalRelativeHandle );
}
LocalRelativeHandle = CurrentDirectory;
CurrentDirectory = INVALID_HANDLE_VALUE;
if(RemainingName.Length == 0)
{
break;
}
}
}
*NumComponentsExpanded = NumExpanded;
if(LocalRelativeHandle != RelativeHandle)
{
if(LocalRelativeHandle != INVALID_HANDLE_VALUE)
{
DfsCloseDirectory( LocalRelativeHandle );
LocalRelativeHandle = INVALID_HANDLE_VALUE;
}
}
DFS_TRACE_ERROR_LOW(NtStatus, REFERRAL_SERVER, "ExpandLinkDirectory: %wZ: Status %x\n",
pLinkName, NtStatus );
return NtStatus;
}
BOOLEAN
DfsRootFolder::IsAShortName(PUNICODE_STRING pLinkName)
{
ULONG idx = 0;
ULONG nameLen = 0;
BOOLEAN RetVal = FALSE;
if(pLinkName->Length == 0)
{
return FALSE;
}
nameLen = pLinkName->Length / sizeof(WCHAR);
for (idx = 0; idx < nameLen; idx++)
{
if (pLinkName->Buffer[idx] == L'~')
{
RetVal = TRUE;
break;
}
}
return RetVal;
}
NTSTATUS
DfsRootFolder::GetAShortName(HANDLE ParentHandle,
BOOLEAN *Expanded,
PUNICODE_STRING pLinkName,
PUNICODE_STRING pNewPath)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PFILE_NAMES_INFORMATION pFileEntry = NULL;
ULONG NameInformationSize = 0;
IO_STATUS_BLOCK ioStatusBlock;
UNICODE_STRING LongName;
UNICODE_STRING PathSep;
*Expanded = FALSE;
PathSep.Length = sizeof(UNICODE_PATH_SEP);
PathSep.MaximumLength = sizeof(UNICODE_PATH_SEP);
PathSep.Buffer = L"\\";
NameInformationSize = sizeof(FILE_NAMES_INFORMATION) + DFS_SHORTNAME_EXPANSION_SIZE;
pFileEntry = (PFILE_NAMES_INFORMATION) new BYTE [NameInformationSize];
if (pFileEntry != NULL)
{
NtStatus = NtQueryDirectoryFile( ParentHandle,
NULL,
NULL,
NULL,
&ioStatusBlock,
pFileEntry,
NameInformationSize,
FileNamesInformation,
TRUE, // ReturnSingleEntry
pLinkName,
TRUE ); // RestartScan
if(NtStatus == STATUS_SUCCESS)
{
LongName.Buffer = pFileEntry->FileName;
LongName.Length = LongName.MaximumLength = (USHORT) pFileEntry->FileNameLength;
DFS_TRACE_ERROR_LOW(NtStatus, REFERRAL_SERVER, "GetShortName: %wZ expanded to %wZ: Status %x\n",
pLinkName, &LongName, NtStatus );
if (RtlCompareUnicodeString(&LongName, pLinkName, TRUE) != 0)
{
*Expanded = TRUE;
NtStatus = CopyShortName(&LongName,
pNewPath);
}
else
{
*Expanded = FALSE;
NtStatus = CopyShortName(pLinkName,
pNewPath);
}
//
// A previous CopyShortName may have failed because
// we were out of resources.
//
if (NtStatus == STATUS_SUCCESS)
{
NtStatus = CopyShortName(&PathSep,
pNewPath);
}
}
delete [] pFileEntry;
pFileEntry = NULL;
}
else
{
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
}
DFS_TRACE_ERROR_LOW(NtStatus, REFERRAL_SERVER, "GetShortName: Status %x\n",
NtStatus );
return NtStatus;
}
NTSTATUS
DfsRootFolder::CopyShortName(PUNICODE_STRING pNewName,
PUNICODE_STRING pNewPath)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
ULONG NewMaxLength = 0;
PWSTR pNewBuffer = NULL;
do {
NtStatus = RtlAppendUnicodeStringToString(pNewPath, pNewName);
//
// If the append succeeded then we are done.
//
if (NtStatus != STATUS_BUFFER_TOO_SMALL)
{
break;
}
NewMaxLength = pNewPath->MaximumLength +
pNewName->Length +
(DFS_SHORTNAME_EXPANSION_SIZE * sizeof(WCHAR));
// unicode strings can't go beyond a ushort
if (NewMaxLength > MAXUSHORT)
{
NtStatus = STATUS_INVALID_PARAMETER;
break;
}
pNewBuffer = (PWSTR) new BYTE [NewMaxLength];
if (pNewBuffer == NULL)
{
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
if (pNewPath->Buffer)
{
RtlMoveMemory( pNewBuffer, pNewPath->Buffer, pNewPath->Length );
delete [] pNewPath->Buffer;
pNewPath->Buffer = NULL;
}
pNewPath->Buffer = pNewBuffer;
pNewPath->MaximumLength = (USHORT) NewMaxLength;
NtStatus = RtlAppendUnicodeStringToString(pNewPath, pNewName);
} while (FALSE);
return NtStatus;
}
void
DfsRootFolder::FreeShortNameData(PUNICODE_STRING pLinkName)
{
if(pLinkName->Buffer != NULL)
{
delete [] pLinkName->Buffer;
pLinkName->Buffer = NULL;
}
}
DFSSTATUS
DfsRootFolder::CreateRenameName(PUNICODE_STRING pNewName)
{
DFSSTATUS Status = ERROR_SUCCESS;
LPWSTR String = NULL;
UUID NewUid;
Status = UuidCreate(&NewUid);
if (Status == ERROR_SUCCESS)
{
Status = UuidToString( &NewUid,
&String );
if (Status == ERROR_SUCCESS)
{
Status = DfsCreateUnicodePathString ( pNewName,
0, // no leading path sep
NULL,
String );
RpcStringFree(&String );
}
}
return Status;
}
DFSSTATUS
DfsRootFolder::RenamePath(PUNICODE_STRING ParentDirectory,
PUNICODE_STRING DirectoryToRename)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
DFSSTATUS Status = ERROR_SUCCESS;
ULONG NewdirectoryNameSize = 0;
ULONG OlddirectoryNameSize = 0;
BOOL fMoved = FALSE;
PUNICODE_STRING pRootDir = NULL;
PWSTR NewBuffer = NULL;
PWSTR NewBuffer2 = NULL;
PWSTR OldName = NULL;
PWSTR NewName = NULL;
UNICODE_STRING NewFileName;
UNICODE_STRING OldFileName;
UNICODE_STRING PartialName;
UNICODE_STRING PathSep;
UNICODE_STRING DfsPrefix;
UNICODE_STRING UniNull;
pRootDir = GetDirectoryCreatePathName();
DfsRtlInitUnicodeStringEx( &NewFileName, NULL);
DfsRtlInitUnicodeStringEx( &OldFileName, NULL);
DfsRtlInitUnicodeStringEx( &PartialName, NULL);
PathSep.Length = sizeof(UNICODE_PATH_SEP);
PathSep.MaximumLength = sizeof(UNICODE_PATH_SEP);
PathSep.Buffer = L"\\";
UniNull.Length = sizeof(WCHAR);
UniNull.MaximumLength = sizeof(WCHAR);
UniNull.Buffer = L"\0";
DfsRtlInitUnicodeStringEx( &DfsPrefix, DFSRENAMEPREFIX);
Status = CreateRenameName(&PartialName);
if(Status == ERROR_SUCCESS)
{
NewdirectoryNameSize = pRootDir->Length + ParentDirectory->Length +
DirectoryToRename->Length +
PartialName.Length + (3 *PathSep.Length) +
DfsPrefix.Length+UniNull.Length;
NewBuffer = (PWSTR) new BYTE[NewdirectoryNameSize];
if(NewBuffer)
{
NewFileName.Buffer = NewBuffer;
NewFileName.MaximumLength = (USHORT)NewdirectoryNameSize;
NtStatus = RtlAppendUnicodeStringToString(&NewFileName, pRootDir);
NtStatus = RtlAppendUnicodeStringToString(&NewFileName, &PathSep);
NtStatus = RtlAppendUnicodeStringToString(&NewFileName, ParentDirectory);
NtStatus = RtlAppendUnicodeStringToString(&NewFileName, &PathSep);
NtStatus = RtlAppendUnicodeStringToString(&NewFileName, &DfsPrefix);
NtStatus = RtlAppendUnicodeToString(&NewFileName, &PartialName.Buffer[1]);
NtStatus = RtlAppendUnicodeStringToString(&NewFileName, DirectoryToRename);
NtStatus = RtlAppendUnicodeStringToString(&NewFileName, &UniNull);
}
else
{
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
}
if(NtStatus == STATUS_SUCCESS)
{
OlddirectoryNameSize = pRootDir->Length + ParentDirectory->Length +
(2*PathSep.Length) + DirectoryToRename->Length +
UniNull.Length;
NewBuffer2 = (PWSTR) new BYTE[OlddirectoryNameSize];
if(NewBuffer2)
{
OldFileName.Buffer = NewBuffer2;
OldFileName.MaximumLength = (USHORT)OlddirectoryNameSize;
NtStatus = RtlAppendUnicodeStringToString(&OldFileName, pRootDir);
NtStatus = RtlAppendUnicodeStringToString(&OldFileName, &PathSep);
NtStatus = RtlAppendUnicodeStringToString(&OldFileName, ParentDirectory);
NtStatus = RtlAppendUnicodeStringToString(&OldFileName, &PathSep);
NtStatus = RtlAppendUnicodeStringToString(&OldFileName, DirectoryToRename);
NtStatus = RtlAppendUnicodeStringToString(&OldFileName, &UniNull);
}
else
{
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
Status = RtlNtStatusToDosError(NtStatus);
if(Status == ERROR_SUCCESS)
{
OldName = &OldFileName.Buffer[DFSDIRWHACKQOFFSET];
NewName = &NewFileName.Buffer[DFSDIRWHACKQOFFSET];
fMoved = MoveFileW(OldName, NewName);
if(fMoved == FALSE)
{
Status = GetLastError();
}
}
}
if(PartialName.Buffer)
{
DfsFreeUnicodeString( &PartialName );
}
if(OldFileName.Buffer)
{
delete [] OldFileName.Buffer;
}
if(NewFileName.Buffer)
{
delete [] NewFileName.Buffer;
}
DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "RenamePath, link %wZ, Status %x\n",
ParentDirectory, Status);
return Status;
}
NTSTATUS
StripAndReturnLastPathComponent(
PUNICODE_STRING pPath,
PUNICODE_STRING pLeaf)
{
USHORT i = 0, j = 0;
USHORT OriginalLength = 0;
NTSTATUS Status = STATUS_SUCCESS;
WCHAR *Src = NULL;
if (pPath->Length == 0)
{
return Status;
}
OriginalLength = pPath->Length;
for( i = (pPath->Length - 1)/ sizeof(WCHAR); i != 0; i--)
{
if (pPath->Buffer[i] != UNICODE_PATH_SEP)
{
break;
}
}
for (j = i; j != 0; j--)
{
if (pPath->Buffer[j] == UNICODE_PATH_SEP)
{
break;
}
}
//change the length of the orignal buffer to reflect
//that the last component was stripped off
pPath->Length = (j) * sizeof(WCHAR);
if(j > 0 )
{
Src = &pPath->Buffer[((pPath->Length + sizeof( WCHAR)) /sizeof( WCHAR ))];
pLeaf->Buffer = Src;
pLeaf->Length = OriginalLength - (pPath->Length + sizeof( WCHAR));
pLeaf->MaximumLength = pLeaf->Length;
}
else //didn't find any //s
{
Src = &pPath->Buffer[pPath->Length /sizeof( WCHAR )];
pLeaf->Buffer = Src;
pLeaf->Length = OriginalLength - (pPath->Length);
pLeaf->MaximumLength = pLeaf->Length;
}
return Status;
}
NTSTATUS
SendDataToSrv(PBYTE Buffer,
ULONG BUfferLength,
DWORD IoCtl)
{
NTSTATUS status = STATUS_SUCCESS;
HANDLE hMrxSmbHandle = NULL;
UNICODE_STRING unicodeServerName;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK ioStatusBlock;
//
// Open the server device.
//
DfsRtlInitUnicodeStringEx( &unicodeServerName, SERVER_DEVICE_NAME );
InitializeObjectAttributes(
&objectAttributes,
&unicodeServerName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
//
// Opening the server with desired access = SYNCHRONIZE and open
// options = FILE_SYNCHRONOUS_IO_NONALERT means that we don't have
// to worry about waiting for the NtFsControlFile to complete--this
// makes all IO system calls that use this handle synchronous.
//
status = NtOpenFile(
&hMrxSmbHandle,
FILE_READ_DATA | FILE_WRITE_DATA,
&objectAttributes,
&ioStatusBlock,
0,
0
);
if ( NT_SUCCESS(status) ) {
status = NtFsControlFile( hMrxSmbHandle,
NULL,
NULL,
NULL,
&ioStatusBlock,
IoCtl,
Buffer,
BUfferLength,
NULL,
0 );
NtClose( hMrxSmbHandle );
}
return status;
}
DFSSTATUS
SendShareToSrv(PUNICODE_STRING pShareName,
BOOLEAN fAttach)
{
NTSTATUS Status = STATUS_SUCCESS;
DFSSTATUS DosStatus = ERROR_SUCCESS;
PDFS_ATTACH_SHARE_BUFFER pBuffer = NULL;
DWORD SizeToAllocate = 0;
if((pShareName == NULL) || (pShareName->Buffer == NULL)
|| (pShareName->Length == 0))
{
return ERROR_INVALID_PARAMETER;
}
SizeToAllocate = pShareName->Length
+ sizeof(DFS_ATTACH_SHARE_BUFFER);
pBuffer = (PDFS_ATTACH_SHARE_BUFFER) HeapAlloc(GetProcessHeap(),
0,
SizeToAllocate);
if(pBuffer != NULL)
{
pBuffer->ShareNameLength = pShareName->Length ;
pBuffer->fAttach = fAttach;
RtlCopyMemory(
&pBuffer->ShareName[0],
pShareName->Buffer,
pBuffer->ShareNameLength);
Status = SendDataToSrv((PBYTE) pBuffer,
SizeToAllocate,
FSCTL_DFS_UPDATE_SHARE_TABLE);
DosStatus = RtlNtStatusToDosError(Status);
if(pBuffer)
{
HeapFree(GetProcessHeap(), 0, pBuffer);
}
if(DosStatus != ERROR_SUCCESS)
{
DFS_TRACE_ERROR_HIGH(Status, REFERRAL_SERVER, "[%!FUNC!] (1) %wZ status %x DosStatus %x\n", pShareName, Status, DosStatus);
}
}
else
{
DosStatus = ERROR_NOT_ENOUGH_MEMORY;
DFS_TRACE_ERROR_HIGH(Status, REFERRAL_SERVER, "[%!FUNC!] %wZ status %x\n", pShareName, DosStatus);
}
return DosStatus;
}