Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

3294 lines
93 KiB

//+----------------------------------------------------------------------------
//
// Copyright (C) 1992, Microsoft Corporation.
//
// File: LOCALVOL.C
//
// Contents: This module implements the routines associated with
// managing local volumes. These are generally external
// interfaces which are called via NtFsControlFile.
//
// Functions: DfsFsctrlGetLocalVolumeEntry -
// DfsFsctrlCreateLocalPartition -
// DfsFsctrlDeleteLocalPartition -
// DfsFsctrlCreateExitPoint -
// DfsFsctrlDeleteExitPoint -
// BuildLocalVolPath - build the path to a file on a local volume
// DfsCreateExitPath -
// DfsDeleteExitPath -
// DfsGetPrincipalName -
// DfsFileOnExitPath -
// DfsStorageIdLegal -
//
// History: 28 May 1992 Peterco Created.
// 22 Jul 1992 Alanw Extended DfsFsctrlInitLocalPartitions
// SudK Creating/DeletingLocal Knowledge
// SudK Adding Aging PKT entries.
// 12 Jul 1993 Alanw Removed fsctrls for manipulating PKT.
//
//-----------------------------------------------------------------------------
#include "dfsprocs.h"
#include <align.h>
#include <stdlib.h>
#include <dfserr.h>
#include <netevent.h>
#include "fsctrl.h"
#include "registry.h"
#include "regkeys.h"
#include "log.h"
#include "know.h"
#include "lvolinit.h"
#include "attach.h"
#include "dfswml.h"
//
// The local debug trace level
//
#define Dbg (DEBUG_TRACE_LOCALVOL)
BOOLEAN
DfsDeleteDirectoryCheck(
UCHAR *Buffer);
ULONG
DfsGetDirectoriesToDelete(
PUNICODE_STRING pExitPtName,
PUNICODE_STRING pShareName);
NTSTATUS
DfsCreateExitPath(
IN PDFS_SERVICE pService,
IN PUNICODE_STRING pRemPath,
IN ULONG Disposition);
NTSTATUS
BuildShortPrefix(
IN PUNICODE_STRING pRemPath,
IN PDFS_SERVICE pService,
IN PDFS_PKT_ENTRY_ID PeidParent,
IN OUT PDFS_PKT_ENTRY_ID Peid);
VOID
StripLastComponent(PUNICODE_STRING pustr);
VOID
AddLastComponent(PUNICODE_STRING pustr);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, DfsFsctrlGetLocalVolumeEntry )
#pragma alloc_text( PAGE, DfsFsctrlGetEntryType )
#pragma alloc_text( PAGE, DfsFsctrlGetChildVolumes )
#pragma alloc_text( PAGE, BuildLocalVolPath )
#pragma alloc_text( PAGE, DfsRegModifyLocalVolume )
#pragma alloc_text( PAGE, DfsFsctrlCreateLocalPartition )
#pragma alloc_text( PAGE, DfsInternalDeleteLocalVolume )
#pragma alloc_text( PAGE, DfsFsctrlDeleteLocalPartition )
#pragma alloc_text( PAGE, DfsCreateExitPath )
#pragma alloc_text( PAGE, DfsDeleteExitPath )
#pragma alloc_text( PAGE, DfsInternalModifyPrefix )
#pragma alloc_text( PAGE, DfsFsctrlModifyLocalVolPrefix )
#pragma alloc_text( PAGE, DfsInternalCreateExitPoint )
#pragma alloc_text( PAGE, DfsFsctrlCreateExitPoint )
#pragma alloc_text( PAGE, DfsInternalDeleteExitPoint )
#pragma alloc_text( PAGE, DfsFsctrlDeleteExitPoint )
#pragma alloc_text( PAGE, DfsGetPrincipalName )
#pragma alloc_text( PAGE, DfsFileOnExitPath )
#pragma alloc_text( PAGE, DfsStorageIdLegal )
#pragma alloc_text( PAGE, DfsExitPtLegal )
#pragma alloc_text( PAGE, BuildShortPrefix)
#pragma alloc_text( PAGE, StripLastComponent)
#pragma alloc_text( PAGE, AddLastComponent)
#pragma alloc_text( PAGE, DfsDeleteDirectoryCheck)
#pragma alloc_text( PAGE, DfsGetDirectoriesToDelete)
#endif // ALLOC_PRAGMA
//+----------------------------------------------------------------------------
//
// Function: DfspBringVolumeOnline
//
// Synopsis: Brings an offline volume online
//
// Arguments: [pkt] -- The pkt and pktEntry identify the local volume to
// [pktEntry] -- bring online
//
// Returns:
//
//-----------------------------------------------------------------------------
NTSTATUS
DfspBringVolumeOnline(
IN PDFS_PKT pkt,
IN PDFS_PKT_ENTRY pktEntry)
{
NTSTATUS status;
UNICODE_STRING shareName, localVolAddress;
PDEVICE_OBJECT targetVdo;
PDFS_VOLUME_OBJECT dfsVdo;
ULONG volNameLen;
BOOLEAN fCreated;
//
// Construct the full share name.
//
ASSERT(pktEntry->LocalService != NULL );
shareName = pktEntry->LocalService->Address;
status = DfsGetAttachName( &shareName, &localVolAddress );
if (NT_SUCCESS(status)) {
status = DfsAttachVolume(
&shareName,
&pktEntry->LocalService->pProvider);
if (NT_SUCCESS(status)) {
pktEntry->LocalService->Type &= ~DFS_SERVICE_TYPE_OFFLINE;
RtlMoveMemory(
(PVOID) pktEntry->LocalService->Address.Buffer,
(PVOID) localVolAddress.Buffer,
localVolAddress.Length);
pktEntry->LocalService->Address.Length = localVolAddress.Length;
}
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return( status );
}
//+----------------------------------------------------------------------------
//
// Function: DfspTakeVolumeOffline
//
// Synopsis: Takes an online volume offline
//
// Arguments: [pkt] -- The pkt and pktEntry identify the local volume to be
// [pktEntry] -- taken off line.
//
// Returns: [STATUS_SUCCESS] -- The volume was sucessfully taken offline.
//
// [STATUS_FILES_OPEN] -- The volume could not be taken offline
// because it has open files.
//
// Notes: Assumes that the pkt has been acquired exclusive.
//
//-----------------------------------------------------------------------------
NTSTATUS
DfspTakeVolumeOffline(
IN PDFS_PKT pkt,
IN PDFS_PKT_ENTRY pktEntry)
{
NTSTATUS status;
if (pktEntry->FileOpenCount > 0) {
DebugTrace(0, Dbg, "Volume has %d open files\n", ULongToPtr( pktEntry->FileOpenCount ));
status = STATUS_FILES_OPEN;
} else {
UNICODE_STRING shareName, remPath;
DebugTrace(0, Dbg, "Volume has no files open!\n", 0);
//
// Detach the device object from the volume
//
remPath.Length = 0;
remPath.MaximumLength = 0;
remPath.Buffer = NULL;
status = BuildLocalVolPath(&shareName, pktEntry->LocalService, &remPath);
if (NT_SUCCESS(status)) {
DebugTrace(0, Dbg, "Detaching device object %wZ\n", &shareName);
if (!(pktEntry->Type & PKT_ENTRY_TYPE_LEAFONLY) &&
!(pktEntry->LocalService->pProvider->fProvCapability & PROV_UNAVAILABLE)) {
DfsDetachVolume( &shareName );
}
ASSERT( pktEntry->LocalService != NULL );
pktEntry->LocalService->Type |= DFS_SERVICE_TYPE_OFFLINE;
//
// At this point, the LocalService->Address field has the name
// relative to the device name. If we later try to bring this
// volume online, we'll need the full name, including the device.
// So, we'll junk the LocalService->Address field and copy the
// full name there, just in case we need to bring it online again.
//
ExFreePool( pktEntry->LocalService->Address.Buffer );
pktEntry->LocalService->Address = shareName;
} else {
DebugTrace(0, Dbg, "Unable to allocate share name %08lx\n", ULongToPtr( status ));
}
}
return( status );
}
//+----------------------------------------------------------------------------
//
// Function: DfsFsctrlSetVolumeState
//
// Synopsis: Sets the volume on or off line.
//
// Arguments:
//
// Returns: [STATUS_SUCCESS] -- The volume state was successfully set
// to the requested mode.
//
// [STATUS_FILES_OPEN] -- The volume has open files.
//
// [DFS_STATUS_NOSUCH_LOCAL_VOLUME] -- The volume is not local
//
// [STATUS_INSUFFICIENT_RESOURCES] -- Unable to unmarshal
// arguments or allocate memory to complete operations
//
//-----------------------------------------------------------------------------
NTSTATUS
DfsFsctrlSetVolumeState(
IN PIRP Irp,
IN PVOID InputBuffer,
IN ULONG InputBufferLength)
{
NTSTATUS status;
PDFS_SET_LOCAL_VOLUME_STATE_ARG setArg;
PDFS_PKT pkt;
DFS_PKT_ENTRY_ID EntryId;
PDFS_PKT_ENTRY pktEntry;
STD_FSCTRL_PROLOGUE(DfsFsctrlSetVolumeState, TRUE, FALSE);
if (InputBufferLength < sizeof(*setArg)+sizeof(UNICODE_NULL)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
//
// Unmarshal the name of the volume and the requested state.
//
setArg = (PDFS_SET_LOCAL_VOLUME_STATE_ARG) InputBuffer;
OFFSET_TO_POINTER( setArg->Prefix, setArg );
if (!DfspStringInBuffer(setArg->Prefix, InputBuffer, InputBufferLength)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
EntryId.Uid = setArg->Uid;
RtlInitUnicodeString( &EntryId.Prefix, setArg->Prefix );
pkt = _GetPkt();
PktAcquireExclusive( pkt, TRUE );
DebugTrace(0, Dbg, "Setting state for [%wZ]\n", &EntryId.Prefix );
DebugTrace(0, Dbg, "Requested state is %d\n", ULongToPtr( setArg->State ));
pktEntry = PktLookupEntryById( pkt, &EntryId );
if (pktEntry == NULL ) {
DebugTrace(0, Dbg, "Unable to locate Pkt Entry!\n", 0);
status = DFS_STATUS_NOSUCH_LOCAL_VOLUME;
} else {
if (!(pktEntry->Type & PKT_ENTRY_TYPE_LOCAL)) {
DebugTrace(0, Dbg, "Entry %wZ is not a local volume!\n",
&EntryId.Prefix);
status = DFS_STATUS_BAD_EXIT_POINT;
} else {
status = STATUS_SUCCESS;
}
}
if (NT_SUCCESS(status)) {
ASSERT( pktEntry != NULL );
//
// See if the state bit is already as desired; if not, then do
// the needful.
//
if (((pktEntry->LocalService->Type & DFS_SERVICE_TYPE_OFFLINE)
^ setArg->State) != 0) {
//
// Volume is not in requested state - try to switch it.
//
if (setArg->State == DFS_SERVICE_TYPE_OFFLINE) {
status = DfspTakeVolumeOffline( pkt, pktEntry );
} else {
status = DfspBringVolumeOnline( pkt, pktEntry );
}
//
// If successful, update our persistent knowledge in the registry
//
if (NT_SUCCESS(status)) {
status = DfsChangeLvolInfoServiceType(
&pktEntry->Id.Uid,
pktEntry->LocalService->Type);
if (!NT_SUCCESS(status)) {
NTSTATUS statusRecover;
DebugTrace(
0,
Dbg,
"DfsFsctrlSetVolumeState: Unable to update "
"registry %08lx!\n",
ULongToPtr( status ));
if (setArg->State == DFS_SERVICE_TYPE_OFFLINE) {
statusRecover = DfspBringVolumeOnline(pkt, pktEntry);
} else {
statusRecover = DfspTakeVolumeOffline(pkt, pktEntry);
}
if (!NT_SUCCESS(statusRecover)) {
DebugTrace(
0,
Dbg,
"DfsFsctrlSetVolumeState: Unable to recover "
"%08lx!\n",
ULongToPtr( statusRecover ));
}
}
}
} else {
//
// Volume is already in the requested state - return!
//
NOTHING;
}
}
PktRelease( pkt );
exit_with_status:
DebugTrace(-1, Dbg, "DfsFsctrlSetVolumeState: Returning %08lx\n", ULongToPtr( status ));
DfsCompleteRequest( Irp, status );
return( status );
}
//+----------------------------------------------------------------------------
//
// Function: DfsFsctrlGetEntryType
//
// Synopsis: Looks-up and retrieves the type of the Pkt Entry for the input
// prefix. If there is no exact match for the input prefix,
// this function fails with STATUS_OBJECT_NAME_NOT_FOUND
//
// Arguments:
//
// Returns: [STATUS_SUCCESS] -- Entry is in output buffer.
//
// [STATUS_BUFFER_TOO_SMALL] -- The OutputBuffer was less than
// 4 bytes, so can't return the type.
//
// [STATUS_OBJECT_NAME_NOT_FOUND] -- The input prefix is not
// in the local pkt.
//
//-----------------------------------------------------------------------------
NTSTATUS
DfsFsctrlGetEntryType(
IN PIRP Irp,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength)
{
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING prefix, remPath;
MARSHAL_BUFFER marshalBuffer;
PDFS_PKT pkt;
PDFS_PKT_ENTRY pktEntry;
STD_FSCTRL_PROLOGUE(DfsFsctrlGetEntryType, TRUE, TRUE);
//
// must be multiple of two
//
if ((InputBufferLength % sizeof(WCHAR)) != 0) {
Status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
pkt = _GetPkt();
PktAcquireShared(pkt, (BOOLEAN)TRUE);
prefix.MaximumLength = prefix.Length = (USHORT) InputBufferLength;
prefix.Buffer = (PWCHAR) InputBuffer;
DebugTrace(0, Dbg, "Getting Entry for [%wZ]\n", &prefix);
pktEntry = PktLookupEntryByPrefix( pkt, &prefix, &remPath );
if (pktEntry == NULL) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
if (NT_SUCCESS(Status) && remPath.Length != 0) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
if (NT_SUCCESS(Status)) {
DebugTrace(0, Dbg, "Found Pkt Entry @%08lx\n", pktEntry );
if (sizeof(ULONG) > OutputBufferLength) {
Status = STATUS_BUFFER_TOO_SMALL;
}
}
if (NT_SUCCESS(Status)) {
_PutULong( ((PUCHAR) OutputBuffer), pktEntry->Type );
Irp->IoStatus.Information = sizeof(ULONG);
}
PktRelease( pkt );
exit_with_status:
DebugTrace(-1, Dbg, "DfsFsctrlGetPktEntry returning %08lx\n", ULongToPtr( Status ));
DfsCompleteRequest( Irp, Status );
return( Status );
}
//+-------------------------------------------------------------------------
//
// Function: BuildLocalVolPath, local
//
// Synopsis: This function creates the path to a file on a local volume.
//
// Arguments: [pFullName] -- On return, the full path name to the file
// [pService] -- The Local Service which has storageId in it.
// [RemPath] -- The remaining path relative to storageId.
//
// Returns: [STATUS_SUCCESS] --
//
// [STATUS_INSUFFICIENT_RESOURCES] -- Out of memory
//
// History: 05-Apr-93 Alanw Created.
//
//--------------------------------------------------------------------------
NTSTATUS
BuildLocalVolPath(
OUT PUNICODE_STRING pFullName,
IN PDFS_SERVICE pService,
IN PUNICODE_STRING pRemPath
) {
//
// We figure out the FullName from the StorageId in the Service
// structure passed in and the remaining path passed in.
// The devicename and address are preceeded by path separators,
// so we don't need to add those in.
//
if (pService->pProvider != NULL) {
pFullName->MaximumLength = pService->pProvider->DeviceName.Length;
} else {
pFullName->MaximumLength = 0;
}
pFullName->MaximumLength += pService->Address.Length +
sizeof(WCHAR) + //For PATHSEP
pRemPath->Length +
sizeof(WCHAR); //For UNICODE_NULL.
pFullName->Buffer = ExAllocatePoolWithTag(PagedPool, pFullName->MaximumLength, ' sfD');
if (pFullName->Buffer==NULL) {
pFullName->Length = pFullName->MaximumLength = 0;
return(STATUS_INSUFFICIENT_RESOURCES);
}
if (pService->pProvider != NULL) {
pFullName->Length = pService->pProvider->DeviceName.Length;
RtlMoveMemory( pFullName->Buffer,
pService->pProvider->DeviceName.Buffer,
pService->pProvider->DeviceName.Length
);
} else {
pFullName->Length = 0;
}
DfsConcatenateFilePath(pFullName,
(PWCHAR)pService->Address.Buffer,
pService->Address.Length
);
if (pRemPath->Length > 0)
DfsConcatenateFilePath(pFullName,
pRemPath->Buffer,
pRemPath->Length
);
pFullName->Buffer[pFullName->Length/sizeof(WCHAR)] = UNICODE_NULL;
return STATUS_SUCCESS;
}
//+----------------------------------------------------------------------
//
// Function: DfsStorageIdLocal
//
// Synopsis: This function determines if a given storage Id is local or not.
//
// Arguments: [StorageId] -- UnicodeString which represents the StorageId.
// [RemovableableMedia] -- On return, if the storage Id represents
// removable media.
//
// Returns: TRUE if it is Local else FALSE.
//
// Note: This function can also return FALSE if it cannot find the
// drive at all as well.
//
// History: 10-10-1994 SudK Created.
//
//-----------------------------------------------------------------------
BOOLEAN
DfsStorageIdLocal(
IN PUNICODE_STRING StorageId,
OUT BOOLEAN *RemovableMedia
)
{
UNICODE_STRING DriveStgId;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE handle;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
FILE_FS_DEVICE_INFORMATION DeviceInfo;
if (StorageId->Length == 0)
return(FALSE);
//
// Should we verify that the storage id is of \?? form.
//
DriveStgId.Buffer = L"\\??\\C:\\";
DriveStgId.Length = wcslen(L"\\??\\C:\\")*sizeof(WCHAR);
DriveStgId.MaximumLength = DriveStgId.Length + sizeof(WCHAR);
DriveStgId.Buffer[DriveStgId.Length/sizeof(WCHAR)-3] =
StorageId->Buffer[DriveStgId.Length/sizeof(WCHAR)-3];
InitializeObjectAttributes(
&objectAttributes,
&DriveStgId,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = ZwCreateFile(
&handle,
FILE_READ_ATTRIBUTES,
&objectAttributes,
&ioStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if (!NT_SUCCESS(status)) {
if (status == STATUS_NO_MEDIA_IN_DEVICE) {
*RemovableMedia = TRUE;
return( TRUE );
} else {
return( FALSE );
}
}
//
// So we now have a open handle to the file
//
status = ZwQueryVolumeInformationFile(
handle,
&ioStatus,
&DeviceInfo,
sizeof(FILE_FS_DEVICE_INFORMATION),
FileFsDeviceInformation
);
ZwClose(handle);
if (!NT_SUCCESS(status)) {
DebugTrace(0, Dbg, "ZwQueryStdInformation failed %08lx\n", ULongToPtr( status ));
return(FALSE);
}
if (DeviceInfo.Characteristics & FILE_REMOTE_DEVICE) {
DebugTrace(0, Dbg, "Remote StgId passed in %wZ\n", StorageId);
return(FALSE);
}
switch (DeviceInfo.DeviceType) {
case FILE_DEVICE_NETWORK:
case FILE_DEVICE_NETWORK_FILE_SYSTEM:
case FILE_DEVICE_VIRTUAL_DISK:
DebugTrace(0, Dbg, "Remote StgId passed in %wZ\n", StorageId);
return(FALSE);
break;
case FILE_DEVICE_CD_ROM:
case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
*RemovableMedia = TRUE;
return(TRUE);
break;
case FILE_DEVICE_DISK:
case FILE_DEVICE_DISK_FILE_SYSTEM:
if (DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA) {
*RemovableMedia = TRUE;
} else {
*RemovableMedia = FALSE;
}
return(TRUE);
break;
default:
DebugTrace(0, Dbg, "Unknown type StgId passed in %wZ\n", StorageId);
return(FALSE);
break;
}
}
//+----------------------------------------------------------------------------
//
// Function: DfsInternalCreateLocalPartition, public
//
// Synopsis: This routine does all the server-side work required to share
// some local storage under Dfs. This includes:
//
// Verifying the storage id is legal
// Updating the registry
// Creating the local partition info in the Pkt.
//
// Arguments: [StgId] -- The storage id to be shared under Dfs.
//
// [CreateStorage] -- TRUE if you want Dfs to create the storage
// if it does not exist.
//
// [pInfo] -- The DFS_LOCAL_VOLUME_CONFIG structure describing the
// local volume.
//
//
//
// Returns: [STATUS_SUCCESS] -- Local volume was successfully creaeted.
//
// [DFS_STATUS_BAD_STORAGEID] -- The storage id is not a local
// storage, or does not exist and CreateStorage flag
// was not TRUE in the, or is a badly formatted string
//
// [DFS_STATUS_STORAGEID_ALREADY_INUSE] -- Some parent or child
// of this storage id is already in the dfs namespace.
//
// [DFS_STATUS_LOCAL_ENTRY] -- There is already a local volume
// with the same prefix as the input.
//
// [STATUS_INSUFFICIENT_RESOURCES] -- Out of memory
//
//-----------------------------------------------------------------------------
NTSTATUS
DfsInternalCreateLocalPartition(
IN PUNICODE_STRING StgId,
IN BOOLEAN CreateStorage,
IN OUT PDFS_LOCAL_VOLUME_CONFIG pConfigInfo)
{
NTSTATUS status = STATUS_SUCCESS;
PDFS_PKT pkt;
BOOLEAN fPreviousErrorMode, removableMedia = FALSE;
NTSTATUS DeleteStatus;
//
// Set this in case we are going to create a volume on removable
// media which has no media in it
//
fPreviousErrorMode = IoSetThreadHardErrorMode( FALSE );
if (!DfsStorageIdLocal(StgId, &removableMedia)) {
status = DFS_STATUS_BAD_STORAGEID;
}
if (NT_SUCCESS(status) && !DfsStorageIdLegal(StgId)) {
DebugTrace(0, Dbg,
"DfsFsctlCreateLPartition illegal stgid %wZ\n", StgId);
status = DFS_STATUS_STORAGEID_ALREADY_INUSE;
}
if (removableMedia) {
pConfigInfo->EntryType |= PKT_ENTRY_TYPE_LEAFONLY;
}
if (NT_SUCCESS(status)) {
pkt = _GetPkt();
PktAcquireExclusive(pkt, TRUE);
if (!DfsStorageIdExists(*StgId, CreateStorage) ) {
status = DFS_STATUS_BAD_STORAGEID;
} else {
status = DfsStoreLvolInfo(pConfigInfo, StgId);
//
// Now we need to initialize the PKT with this new partition info.
//
if (NT_SUCCESS(status)) {
//
// We'll initialize the local partition with no exit points.
// Then, if the local partition is initialized successfully,
// we'll try to create all the exit points with individual
// calls to DfsInternalCreateExitPoint
//
DFS_LOCAL_VOLUME_CONFIG configInfo;
UNICODE_STRING unusedShortPrefix;
ULONG i;
configInfo = *pConfigInfo;
configInfo.RelationInfo.SubordinateIdCount = 0;
status = PktInitializeLocalPartition( pkt, StgId, &configInfo);
if (NT_SUCCESS(status)) {
for (i = 0;
(i < pConfigInfo->RelationInfo.SubordinateIdCount)
&& NT_SUCCESS(status);
i++ ) {
RtlInitUnicodeString(&unusedShortPrefix,NULL);
status = DfsInternalCreateExitPoint(
&pConfigInfo->RelationInfo.SubordinateIdList[i],
PKT_ENTRY_TYPE_LOCAL_XPOINT,
FILE_OPEN,
&unusedShortPrefix);
if (NT_SUCCESS(status) && unusedShortPrefix.Buffer != NULL) {
ExFreePool(unusedShortPrefix.Buffer);
}
}
//
// If creating one of the junction points failed, we need
// to cleanup the the ones we created
//
if (!NT_SUCCESS(status)) {
for (i--; i > 0; i--) {
(void) DfsInternalDeleteExitPoint(
&pConfigInfo->RelationInfo.SubordinateIdList[i],
PKT_ENTRY_TYPE_LOCAL_XPOINT);
}
//
// We also need to "uninitialize" the local partition
// we just initialized
//
(void) DfsInternalDeleteLocalVolume(
&pConfigInfo->RelationInfo.EntryId);
DeleteStatus = DfsDeleteLvolInfo(
&pConfigInfo->RelationInfo.EntryId.Uid);
if (!NT_SUCCESS(DeleteStatus)) {
DebugTrace(
0, Dbg,
"Error %08lx deleting registry info after "
"failed create partition!\n",
ULongToPtr( DeleteStatus ));
}
}
} else {
DeleteStatus = DfsDeleteLvolInfo(
&pConfigInfo->RelationInfo.EntryId.Uid);
if (!NT_SUCCESS(DeleteStatus)) {
DebugTrace(
0, Dbg,
"Error %08lx deleting registry info after "
"failed create partition!\n",
ULongToPtr( DeleteStatus ));
}
}
} else {
DebugTrace(0, Dbg, "Error %08lx storing info in registry\n", ULongToPtr( status ));
}
}
//
// Release the PKT.
//
PktRelease(pkt);
}
IoSetThreadHardErrorMode( fPreviousErrorMode );
return( status );
}
//+-------------------------------------------------------------------------
//
// Function: DfsFsctrlCreateLocalPartition, public
//
// Synopsis:
//
// Arguments:
//
// Returns:
//
// Notes: We only process this FSCTRL from the file system process,
// never from the driver.
//
//--------------------------------------------------------------------------
NTSTATUS
DfsFsctrlCreateLocalPartition(
IN PIRP Irp,
IN PVOID InputBuffer,
IN ULONG InputBufferLength
)
{
NTSTATUS status = STATUS_SUCCESS;
MARSHAL_BUFFER marshalBuffer;
PDFS_CREATE_LOCAL_PARTITION_ARG arg;
PDFS_LOCAL_VOLUME_CONFIG configInfo;
UNICODE_STRING sharePath;
ULONG i;
STD_FSCTRL_PROLOGUE(DfsFsctrlCreateLocalPartition, TRUE, FALSE);
if (InputBufferLength < sizeof(*arg)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
//
// unmarshal the arguments...
//
arg = (PDFS_CREATE_LOCAL_PARTITION_ARG) InputBuffer;
OFFSET_TO_POINTER(arg->ShareName, arg);
OFFSET_TO_POINTER(arg->SharePath, arg);
OFFSET_TO_POINTER(arg->EntryPrefix, arg);
OFFSET_TO_POINTER(arg->ShortName, arg);
OFFSET_TO_POINTER(arg->RelationInfo, arg);
if (
!DfspStringInBuffer(arg->ShareName, InputBuffer, InputBufferLength) ||
!DfspStringInBuffer(arg->SharePath, InputBuffer, InputBufferLength) ||
!DfspStringInBuffer(arg->EntryPrefix, InputBuffer, InputBufferLength) ||
!DfspStringInBuffer(arg->ShortName, InputBuffer, InputBufferLength) ||
!POINTER_IS_VALID(arg->RelationInfo, InputBuffer, InputBufferLength)
) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
if (!POINTER_IN_BUFFER(
&arg->RelationInfo->Buffer,
sizeof(arg->RelationInfo->Buffer),
InputBuffer,
InputBufferLength)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
OFFSET_TO_POINTER( arg->RelationInfo->Buffer, arg );
if (!POINTER_IS_VALID(arg->RelationInfo->Buffer, InputBuffer, InputBufferLength)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
for (i = 0; i < arg->RelationInfo->Count; i++) {
if (!POINTER_IN_BUFFER(
&arg->RelationInfo->Buffer[i].Prefix,
sizeof(arg->RelationInfo->Buffer[i].Prefix),
InputBuffer,
InputBufferLength)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
OFFSET_TO_POINTER( arg->RelationInfo->Buffer[i].Prefix, arg );
if (!DfspStringInBuffer(
arg->RelationInfo->Buffer[i].Prefix,
InputBuffer,
InputBufferLength)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
}
RtlInitUnicodeString( &sharePath, arg->SharePath );
configInfo = DfsNetInfoToConfigInfo(
PKT_ENTRY_TYPE_CAIRO,
DFS_SERVICE_TYPE_MASTER,
arg->SharePath,
arg->ShareName,
&arg->EntryUid,
arg->EntryPrefix,
arg->ShortName,
arg->RelationInfo );
if (configInfo == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
}
if (NT_SUCCESS(status)) {
status = DfsInternalCreateLocalPartition(
&sharePath,
FALSE,
configInfo);
//
// need to deallocate the config info...
//
LocalVolumeConfigInfoDestroy( configInfo, TRUE );
} else {
DebugTrace(0, Dbg, "DfsFsctrlCreateLPart Unmarshal Err %08lx\n", ULongToPtr( status ));
}
exit_with_status:
DfsCompleteRequest( Irp, status );
DebugTrace(-1, Dbg,
"DfsFsctrlCreateLocalPartition: Exit -> %08lx\n", ULongToPtr( status ));
return status;
}
//+-------------------------------------------------------------------------
//
// function: DfsInternalDeleteLocalVolume, public
//
// Synopsis: This function deletes a local volume knowledge given the
// EntryId describing the local volume.
//
// Arguments: [localEntryId] -- The entryId describing the local volume.
//
// Returns: [SUCCESS_SUCCESS] -- If all went well.
//
// [STATUS_INSUFFICIENT_RESOURCES] -- Out of memory.
//
// [DFS_STATUS_NOSUCH_LOCAL_VOLUME] -- Couldn't find pkt entry
// in Pkt.
//
// This routine can also return error codes returned by
// NT Registry calls.
//
// Notes: This function will attempt to acquire Exclusive Locks on PKT.
//
// History: 31 March 1993 Created SudK from
// DfsFsctrlDeleteLocalPartition.
//
//--------------------------------------------------------------------------
NTSTATUS
DfsInternalDeleteLocalVolume(
IN PDFS_PKT_ENTRY_ID localEntryId)
{
NTSTATUS status = STATUS_SUCCESS;
DebugTrace(+1, Dbg, "DfsInternalDeleteLocalVolume: Entered\n", 0);
ASSERT(ARGUMENT_PRESENT(localEntryId));
if (NT_SUCCESS(status)) {
PDFS_PKT pkt;
PDFS_PKT_ENTRY localEntry;
//
// Find the local entry...and make sure that its local...
//
pkt = _GetPkt();
PktAcquireExclusive(pkt, TRUE);
localEntry = PktLookupEntryById(pkt, localEntryId);
if ((localEntry != NULL) &&
(localEntry->Type & PKT_ENTRY_TYPE_LOCAL)) {
PDFS_SERVICE service;
status = DfsDeleteLvolInfo(&localEntryId->Uid);
//
// We don't want to invalidate this entry unless everything
// else (deletion of the registry info) went well.
//
if (NT_SUCCESS(status)) {
UNICODE_STRING ShareName = {0, 0, NULL};
UNICODE_STRING RemPath = {0, 0, NULL};
//
// First, we delete the local DFS_SERVICE associated with
// the Pkt Entry
//
if (! (localEntry->LocalService->Type &
DFS_SERVICE_TYPE_OFFLINE)) {
status = BuildLocalVolPath(&ShareName, localEntry->LocalService, &RemPath);
if (NT_SUCCESS(status)) {
DebugTrace(0, Dbg, "Trying to remove local service %wZ\n", &ShareName);
PktEntryRemoveLocalService(pkt, localEntry, &ShareName);
}
} else {
PktServiceDestroy(localEntry->LocalService, (BOOLEAN)TRUE);
localEntry->LocalService = NULL;
}
//
// Next, we delete the Pkt Entry if it is only a local
// volume. If it is also an exit point, then we want to
// keep the entry.
//
if (NT_SUCCESS(status)) {
localEntry->Type &= ~PKT_ENTRY_TYPE_LOCAL;
if (! (localEntry->Type & PKT_ENTRY_TYPE_LOCAL_XPOINT)) {
status = PktInvalidateEntry(pkt, localEntry);
ASSERT( status == STATUS_SUCCESS );
}
if (ShareName.Buffer) {
ExFreePool(ShareName.Buffer);
}
} else {
DebugTrace(0, Dbg, "Unable to allocate share name %08lx\n", ULongToPtr( status ));
}
} else {
DebugTrace(0, Dbg, "Error %08lx deleting registry info\n", ULongToPtr( status ));
}
} else {
//
// We did not find a local entry that matches the Prefix that
// has been requested to be deleted. We have to return an
// error code here.
//
status = DFS_STATUS_NOSUCH_LOCAL_VOLUME;
}
//
// We can release the Pkt now...
//
PktRelease(pkt);
}
DebugTrace(-1, Dbg, "DfsInternalDeleteLocalVolume: Exit -> %08lx\n", ULongToPtr( status ));
return status;
}
//+-------------------------------------------------------------------------
//
// Function: DfsFsctrlDeleteLocalPartition, public
//
// Synopsis: This function services an FSCTRL which the DC can make to
// delete knowledge of a local partition in the PKT.
//
// Arguments:
//
// Returns:
//
// History: March 31 1993 Modified to use DfsInternalDelete by SudK
//
//--------------------------------------------------------------------------
NTSTATUS
DfsFsctrlDeleteLocalPartition(
IN PIRP Irp,
IN PVOID InputBuffer,
IN ULONG InputBufferLength
)
{
NTSTATUS status = STATUS_SUCCESS;
DFS_PKT_ENTRY_ID localEntryId;
PDFS_DELETE_LOCAL_PARTITION_ARG arg;
STD_FSCTRL_PROLOGUE(DfsFsctrlDeleteLocalPartition, TRUE, FALSE);
if (InputBufferLength < sizeof(*arg)+sizeof(UNICODE_NULL)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
//
// Unmarshal the argument...
//
arg = (PDFS_DELETE_LOCAL_PARTITION_ARG) InputBuffer;
OFFSET_TO_POINTER( arg->Prefix, arg );
if (!DfspStringInBuffer(arg->Prefix, InputBuffer, InputBufferLength)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
localEntryId.Uid = arg->Uid;
RtlInitUnicodeString( &localEntryId.Prefix, arg->Prefix );
DebugTrace(0, Dbg, "Deleting [%wZ]", &localEntryId.Prefix );
status = DfsInternalDeleteLocalVolume(&localEntryId);
exit_with_status:
DfsCompleteRequest( Irp, status );
DebugTrace(-1, Dbg, "DfsFsctrlDeleteLocalPartition: Exit -> %08lx\n", ULongToPtr( status ));
return status;
}
//+-------------------------------------------------------------------------
//
// Function: DfsCreateExitPath, local
//
// Synopsis: This function creates the exit point on the disk.
//
// Arguments: [pService] -- The Local Service which has storageId in it.
// [pRemPath] -- The remaining path relative to storageId.
// [Disposition] -- If this is set to FILE_OPEN_IF, success
// is returned if the exit path already exists on disk.
// If it is set to FILE_CREATE, then an error is
// returned if the exit path exists.
// [pExitPointHandle] -- On successful return, handle to the
// newly create exit point is returned here.
//
// Returns: [STATUS_SUCCESS] -- If everything succeeds.
//
// [DFS_STATUS_BAD_EXIT_POINT] -- If any error was encountered
// during the creation of the exit path.
//
// [STATUS_INSUFFICIENT_RESOURCES] -- If unable to allocate
// memory for the Device Path form of the exit
// path.
//
// History: 02-Feb-93 SudK Created.
//
//--------------------------------------------------------------------------
NTSTATUS
DfsCreateExitPath(
PDFS_SERVICE pService,
PUNICODE_STRING pRemPath,
ULONG Disposition)
{
HANDLE exitPtHandle;
UNICODE_STRING exitPtName;
UNICODE_STRING exitPtPath;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
BOOLEAN fDoingParents = TRUE;
DebugTrace(+1, Dbg, "DfsCreateExitPath()\n", 0 );
//
// Get the full name of the exit point.
//
status = BuildLocalVolPath(&exitPtName, pService, pRemPath);
if (! NT_SUCCESS(status)) {
return status;
}
//
// Now we have the Full Path of the ExitPoint. Let's go create it.
//
InitializeObjectAttributes(
&objectAttributes,
&exitPtName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
//
// Bug fix: 431227. Attempt to create the dir if it doesn't already
// exist. Instead of always deleting the directory and then recreating
// it, since that causes FRS headaches.
status = ZwCreateFile(
&exitPtHandle,
DELETE | FILE_READ_ATTRIBUTES,
&objectAttributes,
&ioStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_IF,
FILE_DIRECTORY_FILE |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
DFS_TRACE_ERROR_HIGH(status,ALL_ERROR, DfsCreateExitPath_Error_ZwCreateFile,
LOGSTATUS(status)
LOGUSTR(*pRemPath));
if (!NT_SUCCESS(status)) {
(void) ZwDeleteFile(&objectAttributes);
} else {
ZwClose( exitPtHandle );
goto done;
}
exitPtPath = exitPtName;
exitPtPath.MaximumLength = exitPtPath.Length;
do {
DebugTrace( 0, Dbg, "exitPtPath=%wZ\n", &exitPtPath);
status = ZwCreateFile(
&exitPtHandle,
DELETE | FILE_READ_ATTRIBUTES,
&objectAttributes,
&ioStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
Disposition,
FILE_DIRECTORY_FILE |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
DFS_TRACE_ERROR_HIGH(status,ALL_ERROR, DfsCreateExitPath_Error_ZwCreateFile2,
LOGSTATUS(status)
LOGUSTR(*pRemPath));
if (!NT_SUCCESS(status)) {
StripLastComponent(&exitPtPath);
InitializeObjectAttributes(
&objectAttributes,
&exitPtPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
} else {
ZwClose( exitPtHandle );
}
} while (!NT_SUCCESS(status) && exitPtPath.Length > (7 * sizeof(WCHAR)));
if (NT_SUCCESS(status)) {
while (exitPtPath.Length < exitPtPath.MaximumLength) {
AddLastComponent(&exitPtPath);
InitializeObjectAttributes(
&objectAttributes,
&exitPtPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
DebugTrace( 0, Dbg, "exitPtPath=%wZ\n", &exitPtPath);
status = ZwCreateFile(
&exitPtHandle,
DELETE | FILE_READ_ATTRIBUTES,
&objectAttributes,
&ioStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
Disposition,
FILE_DIRECTORY_FILE |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
DFS_TRACE_ERROR_HIGH(status,ALL_ERROR, DfsCreateExitPath_Error_ZwCreateFile3,
LOGSTATUS(status)
LOGUSTR(*pRemPath));
if (!NT_SUCCESS(status)) {
break;
}
ZwClose( exitPtHandle );
}
}
done:
if (!NT_SUCCESS(status)) {
status = DFS_STATUS_BAD_EXIT_POINT;
}
if (exitPtName.Buffer != NULL) {
ExFreePool(exitPtName.Buffer);
}
DebugTrace(-1, Dbg, "DfsCreateExitPath: Exit -> %08lx\n", ULongToPtr( status ));
return(status);
}
//+-------------------------------------------------------------------------
//
// Function: DfsDeleteExitPath, local
//
// Synopsis: This function deletes the exit point on the disk.
//
// Arguments: [pService] -- The Local Service which has storageId in it.
// [pRemPath] -- The remaining path relative to storageId.
//
// Returns: [STATUS_INSUFFICIENT_RESOURCES] -- If unable to build a
// local path.
//
// [STATUS_DEVICE_OFF_LINE] -- If the local volume on which the
// exit path resides is offline.
//
// NTSTATUS from the ZwCreateFile call to delete the exit path.
//
// History: 02-Feb-93 SudK Created.
//
//--------------------------------------------------------------------------
NTSTATUS
DfsDeleteExitPath(PDFS_SERVICE pService, PUNICODE_STRING pRemPath)
{
UNICODE_STRING ExitPtName, ExitPtPath;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE exitPtHandle;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
FILE_DISPOSITION_INFORMATION FileDispInfo;
UNICODE_STRING ShareRemPath = {0, 0, NULL};
UNICODE_STRING ShareName = {0, 0, NULL};
ULONG Deletes;
ULONG i;
//
// See if the local volume from which the exit point has to be deleted
// is offline.
//
if (pService->Type & DFS_SERVICE_TYPE_OFFLINE) {
return( STATUS_DEVICE_OFF_LINE );
}
//
// Get the name of the Share (this is NOT the full name of exit point).
// The sharename is that specified in the pservice.
//
status = BuildLocalVolPath(&ShareName, pService, &ShareRemPath);
if (! NT_SUCCESS(status)) {
return status;
}
//
// Get the full name of the exit point.
//
status = BuildLocalVolPath(&ExitPtName, pService, pRemPath);
if (! NT_SUCCESS(status)) {
return status;
}
Deletes = DfsGetDirectoriesToDelete(&ExitPtName, &ShareName);
ExitPtPath = ExitPtName;
//
// Now we have the Full Path of the ExitPoint. Let's go delete it.
//
for (i = 0; (i < Deletes) && (NT_SUCCESS(status)); i++) {
InitializeObjectAttributes(
&objectAttributes,
&ExitPtPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = ZwOpenFile(
&exitPtHandle,
DELETE,
&objectAttributes,
&ioStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_DIRECTORY_FILE);
DFS_TRACE_ERROR_HIGH(status,ALL_ERROR, DfsDeleteExitPath_Error_ZwOpenFile,
LOGSTATUS(status)
LOGUSTR(*pRemPath));
if (NT_SUCCESS(status)) {
FILE_DISPOSITION_INFORMATION disposition;
disposition.DeleteFile = TRUE;
status = ZwSetInformationFile(
exitPtHandle,
&ioStatus,
&disposition,
sizeof(disposition),
FileDispositionInformation);
DFS_TRACE_ERROR_HIGH(status,ALL_ERROR, DfsDeleteExitPath_Error_ZwSetInformationFile,
LOGSTATUS(status)
LOGUSTR(*pRemPath));
ZwClose(exitPtHandle);
StripLastComponent(&ExitPtPath);
}
}
if (ExitPtName.Buffer != NULL) {
ExFreePool(ExitPtName.Buffer);
}
return(status);
}
//+------------------------------------------------------------------------
//
// Function: DfsInternalModifyPrefix, private
//
// Synopsis: This function fixes the entrypath of the volume whose EntryId
// is passed to it.
//
// Arguments: [peid] -- The PktEntryId of vol to be fixed. The GUID is
// used to look up the volume.
//
// Returns: [DFS_STATUS_NOSUCH_LOCAL_VOLUME] -- If no pkt entry with the
// GUID in peid is found.
//
// [DFS_STATUS_BAD_EXIT_POINT] -- If the new prefix could not
// be inserted in the Prefix Table (because
// of a conflicting entry already in the Pkt)
//
// [STATUS_INSUFFICIENT_RESOURCES] -- If room for the new
// prefix could not be allocated.
//
// [STATUS_SUCCESS] -- If everything succeeds
//
// History: 24 Oct 1993 SudK Created.
//
//-------------------------------------------------------------------------
NTSTATUS
DfsInternalModifyPrefix(
IN PDFS_PKT_ENTRY_ID peid
)
{
PDFS_PKT pkt;
PDFS_PKT_ENTRY localEntry;
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING oldPrefix;
DebugTrace(+1, Dbg, "DfsInternalModifyPrefix: Entered\n", 0);
memset(&oldPrefix, 0, sizeof(UNICODE_STRING));
pkt = _GetPkt();
PktAcquireExclusive(pkt, TRUE);
localEntry = PktLookupEntryByUid(pkt, &peid->Uid);
if ((localEntry != NULL) &&
(localEntry->Type & PKT_ENTRY_TYPE_LOCAL)) {
ASSERT(localEntry->LocalService != NULL);
//
// We now need to save the old prefix first.
//
oldPrefix = localEntry->Id.Prefix;
oldPrefix.Buffer = ExAllocatePoolWithTag(PagedPool, oldPrefix.MaximumLength, ' sfD');
if (oldPrefix.Buffer != NULL) {
RtlMoveMemory( oldPrefix.Buffer,
localEntry->Id.Prefix.Buffer,
oldPrefix.MaximumLength);
status = PktEntryModifyPrefix(pkt, &peid->Prefix, localEntry);
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
//
// The best match with the ExitPath was not a local volume.
// Hence we cannot create this exit point. There might be some
// knowledge discrepancy here.
//
DebugTrace(0, Dbg, "Did Not Find Entry to ModifyPrefix %ws\n",
peid->Prefix.Buffer);
status = DFS_STATUS_NOSUCH_LOCAL_VOLUME;
}
//
// OK we took care of the PKT. We still need to take care of the
// info in the registry.
//
if (NT_SUCCESS(status)) {
status = DfsChangeLvolInfoEntryPath(&localEntry->Id.Uid,
&peid->Prefix);
if (!NT_SUCCESS(status)) {
status = PktEntryModifyPrefix(pkt, &oldPrefix, localEntry);
}
}
//
// We can release the Pkt now...
//
if (oldPrefix.Buffer != NULL)
ExFreePool(oldPrefix.Buffer);
PktRelease(pkt);
DebugTrace(-1, Dbg, "DfsInternalModifyPrefix: Exit -> %08lx\n", ULongToPtr( status ));
return(status);
}
//+----------------------------------------------------------------------
//
// Function: DfsFsctrlModifyLocalVolPrefix
//
// Synopsis: This function is called on a client/server by the DC during
// knowledge synchronisation to fix a bad prefix match.
//
// Arguments: The PktEntryID.
//
// Returns: [STATUS_DATA_ERROR] -- If the input buffer is not formatted
// correctly.
//
// [DFS_STATUS_NOSUCH_LOCAL_VOLUME] -- If no pkt entry with the
// GUID in peid is found.
//
// [DFS_STATUS_BAD_EXIT_POINT] -- If the new prefix could not
// be inserted in the Prefix Table (because
// of a conflicting entry already in the Pkt)
//
// [STATUS_INSUFFICIENT_RESOURCES] -- If memory could not be
// allocated.
//
// [STATUS_SUCCESS] -- If everything succeeds
//
//-----------------------------------------------------------------------
NTSTATUS
DfsFsctrlModifyLocalVolPrefix(
IN PIRP Irp,
IN PVOID InputBuffer,
IN ULONG InputBufferLength
)
{
NTSTATUS status = STATUS_SUCCESS;
DFS_PKT_ENTRY_ID ExitPtId;
MARSHAL_BUFFER marshalBuffer;
STD_FSCTRL_PROLOGUE(DfsFsctrlModifyLocalVolPrefix, TRUE, FALSE);
//
// Unmarshal the argument...
//
MarshalBufferInitialize(&marshalBuffer, InputBufferLength, InputBuffer);
status = DfsRtlGet(&marshalBuffer, &MiPktEntryId, &ExitPtId);
if (NT_SUCCESS(status)) {
status = DfsInternalModifyPrefix(&ExitPtId);
//
// Need to deallocate the entry Id...
//
PktEntryIdDestroy(&ExitPtId, FALSE);
} else
DebugTrace(0, Dbg,
"DfsFsctrlModifyLocalVolPrefix: Unmarshalling Error!\n", 0);
DfsCompleteRequest( Irp, status );
DebugTrace(-1, Dbg, "DfsFsctrlModifyLocalVolPrefix: Exit -> %08lx\n", ULongToPtr( status ));
return status;
}
//+-------------------------------------------------------------------------
//
// Function: DfsInternalCreateExitPoint, private
//
// Synopsis: This is the same as the function DfsFsctrlCreateExitPoint
// except that it expects to be called after args are unmarshaled.
//
// Arguments: [peid] -- ExitPt to create.
// [Type] -- The type of exit point. For now only
// PKT_ENTRY_TYPE_MACHINE is relevant - if the type is
// PKT_ENTRY_TYPE_MACHINE, no attempt is made to create
// the on-disk exit point.
// [Disposition] -- What to do if the on-disk exit point already
// exists. If Disposition == FILE_OPEN, a success is
// returned if the exit point already exits on disk.
// [ShortPrefix] -- On successful return, the short prefix
// of the exit point that was created. Caller must free
// the buffer of this variable.
//
// Returns: [STATUS_INVALID_DEVICE_REQUEST] -- If the local volume is
// leafonly (removable media)
//
// [DFS_STATUS_NOSUCH_LOCAL_VOLUME] -- If there is no appropriate
// local volume to create the exit pt under.
//
// [DFS_STATUS_LOCAL_ENTRY] - creation of the subordinate entry
// would have required that a local entry or exit point
// be invalidated.
//
// [DFS_STATUS_INCONSISTENT] - an inconsistency in the PKT
// has been discovered.
//
// [STATUS_DEVICE_OFF_LINE] -- The local volume on which the
// exit point has to be created is currently offline.
//
// [STATUS_INVALID_PARAMETER] - the Id specified for the
// subordinate is invalid.
//
// [STATUS_INSUFFICIENT_RESOURCES] - not enough memory was
// available to complete the operation.
//
// History: 24 Oct 1993 SudK Created.
//
//--------------------------------------------------------------------------
NTSTATUS
DfsInternalCreateExitPoint(
PDFS_PKT_ENTRY_ID peid,
ULONG Type,
ULONG Disposition,
PUNICODE_STRING ShortPrefix
)
{
PDFS_PKT pkt;
PDFS_PKT_ENTRY localEntry;
UNICODE_STRING RemainingPath;
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN ExitPtCreated = FALSE;
BOOLEAN PktEntryCreated = FALSE;
PDFS_SERVICE service;
PDFS_PKT_ENTRY pSubEntry;
UNICODE_STRING ustrPrefix;
DebugTrace(+1, Dbg, "DfsInternalCreateExitPoint: Entered\n",0 );
//
// Find the local entry in which the exit point needs to be created
// and make sure that its local...
//
pkt = _GetPkt();
PktAcquireExclusive(pkt, TRUE);
RtlInitUnicodeString(&ustrPrefix, NULL);
localEntry = PktLookupEntryByPrefix(
pkt,
&peid->Prefix,
&RemainingPath);
if ((localEntry != NULL) &&
(localEntry->Type & PKT_ENTRY_TYPE_LEAFONLY)) {
status = STATUS_INVALID_DEVICE_REQUEST;
}
if (NT_SUCCESS(status) &&
(localEntry != NULL) &&
(localEntry->Type & PKT_ENTRY_TYPE_LOCAL)) {
ASSERT(localEntry->LocalService != NULL);
if (localEntry->LocalService->Type & DFS_SERVICE_TYPE_OFFLINE) {
//
// This volume is offline, can't create exit point.
//
status = STATUS_DEVICE_OFF_LINE;
} else if (DfsExitPtLegal(pkt, localEntry, &RemainingPath)) {
//
// Now we first want to create the exit point since we now
// know that there is an appropriate place to create it.
//
service = localEntry->LocalService;
status = STATUS_SUCCESS;
//
// Only if this is not an exit point leading to a machine volume
// will we create the physical exit path on disk.
//
if (!(Type & PKT_ENTRY_TYPE_MACHINE)) {
status = DfsCreateExitPath(
service,
&RemainingPath,
Disposition);
if (NT_SUCCESS(status)) {
ExitPtCreated = TRUE;
status = BuildShortPrefix(
&RemainingPath,
service,
&localEntry->Id,
peid);
}
} else {
status = DFS_STATUS_BAD_EXIT_POINT;
}
//
// Only if we succeeded in Creating ExitPoint will we get here.
//
if (NT_SUCCESS(status)) {
//
// We now merely need to update the PKT.
// This should create no problems in general.
//
status = PktCreateSubordinateEntry(
pkt,
localEntry,
PKT_ENTRY_TYPE_LOCAL_XPOINT |
PKT_ENTRY_TYPE_PERMANENT,
peid,
NULL,
PKT_ENTRY_SUPERSEDE,
&pSubEntry
);
}
if (NT_SUCCESS(status)) {
//
// Now that we have created the exit pt on disk, let us
// update our knowledge in the registry.
//
PktEntryCreated = TRUE;
ustrPrefix = pSubEntry->Id.ShortPrefix;
ustrPrefix.Buffer = ExAllocatePoolWithTag(
PagedPool,
pSubEntry->Id.ShortPrefix.Length,
' fsD');
if (ustrPrefix.Buffer != NULL) {
RtlCopyMemory(ustrPrefix.Buffer,
pSubEntry->Id.ShortPrefix.Buffer,
pSubEntry->Id.ShortPrefix.Length);
status = DfsCreateExitPointInfo(
&localEntry->Id.Uid,
&pSubEntry->Id);
} else {
ustrPrefix.Length = ustrPrefix.MaximumLength = 0;
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
//
// Now we check for an error. If we do have an error then we
// need to back out all the operations that we did so far.
//
if (!NT_SUCCESS(status)) {
if (PktEntryCreated == TRUE) {
//
// What do we do with an error in this case.
//
pSubEntry->Type &= ~(PKT_ENTRY_TYPE_LOCAL_XPOINT);
PktEntryDestroy(pSubEntry, pkt, TRUE);
}
if ((ExitPtCreated == TRUE)&&!( Type & PKT_ENTRY_TYPE_MACHINE)) {
//
// What do we do with an error in this case.
//
status = DfsDeleteExitPath(service, &RemainingPath);
}
}
} else {
DebugTrace(0, Dbg, "Illegal exit pt passed in %wZ\n",
&peid->Prefix);
status = DFS_STATUS_NOSUCH_LOCAL_VOLUME;
}
} else {
//
// The best match with the ExitPath was not a local volume.
// Hence we cannot create this exit point. There might be some
// knowledge discrepancy here.
//
status = DFS_STATUS_NOSUCH_LOCAL_VOLUME;
}
//
// We can release the Pkt now...
//
PktRelease(pkt);
//
// Either return the ShortPrefix with the allocated buffer or destroy it, depending on the
// status returned.
//
if (NT_SUCCESS(status)) {
*ShortPrefix = ustrPrefix;
} else if (ustrPrefix.Buffer != NULL) {
ExFreePool(ustrPrefix.Buffer);
}
DebugTrace(-1, Dbg, "DfsInternalCreateExitPoint: Exit -> %08lx\n", ULongToPtr( status ));
return(status);
}
//+-------------------------------------------------------------------------
//
// Function: DfsFsctrlCreateExitPoint, public
//
// Synopsis: This method creates an exit point on disk, creates a DFS.CFG
// file and updates the PKT with the new exit point information.
// This method of course checks to make sure that it makes sense
// to create such an exit point. i.e. there is a local volume
// under which this exit point can be created. Also if any of the
// operations above fail then it backs out the entire operation.
// If no system failures occur during this function, then it will
// behave in an atomic fashion.
//
// Arguments:
//
// Returns: [SUCCESS_SUCCESS] -- If all went well.
//
// [STATUS_DATA_ERROR] -- If unable to unmarshal the input
// buffer.
//
// [DFS_STATUS_BAD_EXIT_POINT] -- If cannot create exit point.
//
// History: 02-Feb-93 SudK Created.
//
//--------------------------------------------------------------------------
NTSTATUS
DfsFsctrlCreateExitPoint(
IN PIRP Irp,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer,
IN ULONG OutputBufferLength
)
{
NTSTATUS status = STATUS_SUCCESS;
DFS_PKT_ENTRY_ID ExitPtId;
PDFS_CREATE_EXIT_POINT_ARG arg;
ULONG Type;
UNICODE_STRING ShortPrefix;
WCHAR *wcp;
PCHAR BufferEnd;
STD_FSCTRL_PROLOGUE(DfsFsctrlCreateExitPoint, TRUE, FALSE);
if (InputBufferLength < sizeof(*arg)+sizeof(UNICODE_NULL) ||
OutputBufferLength < sizeof(UNICODE_NULL)) {
status = STATUS_INVALID_PARAMETER;
DfsCompleteRequest( Irp, status );
DebugTrace(-1, Dbg, "DfsFsctrlCreateExitPoint: Exit -> %08lx\n", ULongToPtr( status ));
return( status );
}
//
// Unmarshal the argument...
//
arg = (PDFS_CREATE_EXIT_POINT_ARG) InputBuffer;
OFFSET_TO_POINTER( arg->Prefix, arg );
if (!DfspStringInBuffer(arg->Prefix, InputBuffer, InputBufferLength)) {
status = STATUS_INVALID_PARAMETER;
DfsCompleteRequest( Irp, status );
DebugTrace(-1, Dbg, "DfsFsctrlCreateExitPoint: Exit -> %08lx\n", ULongToPtr( status ));
return( status );
}
RtlInitUnicodeString(&ShortPrefix,NULL);
DFS_DUPLICATE_STRING(ExitPtId.Prefix, arg->Prefix, status);
if (!NT_SUCCESS(status)) {
DfsCompleteRequest( Irp, status );
DebugTrace(-1, Dbg, "DfsFsctrlCreateExitPoint: Exit -> %08lx\n", ULongToPtr( status ));
return( status );
}
ExitPtId.ShortPrefix.Length = ExitPtId.ShortPrefix.MaximumLength = 0;
ExitPtId.ShortPrefix.Buffer = NULL;
ExitPtId.Uid = arg->Uid;
Type = arg->Type;
if (NT_SUCCESS(status)) {
DebugTrace(0, Dbg, "Creating Exit Point [%wZ]\n", &ExitPtId.Prefix );
DebugTrace(0, Dbg, "Type == %d\n", ULongToPtr( Type ) );
status = DfsInternalCreateExitPoint(&ExitPtId, Type, FILE_CREATE, &ShortPrefix);
PktEntryIdDestroy(&ExitPtId, FALSE);
if (NT_SUCCESS(status)) {
if (OutputBufferLength >= (ShortPrefix.Length + sizeof(WCHAR))) {
RtlCopyMemory(
OutputBuffer,
ShortPrefix.Buffer,
ShortPrefix.Length);
((PWCHAR) OutputBuffer)[ShortPrefix.Length/sizeof(WCHAR)] = UNICODE_NULL;
OutputBufferLength = ShortPrefix.Length + sizeof(WCHAR);
} else {
RtlZeroMemory(OutputBuffer, OutputBufferLength);
}
Irp->IoStatus.Information = OutputBufferLength;
}
}
if (ShortPrefix.Buffer != NULL) {
ExFreePool(ShortPrefix.Buffer);
}
DfsCompleteRequest( Irp, status );
DebugTrace(-1, Dbg, "DfsFsctrlCreateExitPoint: Exit -> %08lx\n", ULongToPtr( status ) );
return status;
}
//+-------------------------------------------------------------------------
//
// Function: DfsInternalDeleteExitPoint, public
//
// Synopsis: This method deletes an exit point on disk, updates the local
// volume info in the registry and updates the PKT wrt
// exit point information.
//
// Arguments: [ExitPtId] -- THis is the exitPath and GUID.
// [Type] -- The kind of ExitPt (Machine/Cairo/NonCairo)
//
// Returns: [SUCCESS_SUCCESS] -- If all went well.
//
// [DFS_STATUS_BAD_EXIT_POINT] -- If cannot find exit point.
//
// [DFS_STATUS_NOSUCH_LOCAL_VOLUME] -- Unable to find a local
// service for the volume on which the exit pt is.
//
// This routine can return errors from the NT Registry API.
//
// Note:
//
// History: March 31 1993 SudK Created from DfsFstrlDeleteExitPoint.
//
//--------------------------------------------------------------------------
NTSTATUS
DfsInternalDeleteExitPoint(
IN PDFS_PKT_ENTRY_ID ExitPtId,
IN ULONG Type)
{
NTSTATUS status = STATUS_SUCCESS;
PDFS_PKT pkt = _GetPkt();
PWCHAR pwch;
PDFS_PKT_ENTRY localEntry;
UNICODE_STRING RemainingPath;
PDFS_SERVICE service;
DFS_LOCAL_VOLUME_CONFIG ConfigInfo;
BOOLEAN PktUpdated = FALSE;
BOOLEAN ExitPtDeleted = FALSE;
UNICODE_STRING ParentPrefix;
DebugTrace(+1, Dbg, "DfsInternalDeleteExitPoint: Entered\n", 0);
ASSERT(ARGUMENT_PRESENT(ExitPtId));
memset(&ConfigInfo, 0, sizeof(DFS_LOCAL_VOLUME_CONFIG));
//
// Find the local entry in which the exit point needs to be created
// and make sure that it's local...
//
PktAcquireExclusive(pkt, TRUE);
localEntry = PktLookupEntryByPrefix(
pkt,
&ExitPtId->Prefix,
&RemainingPath);
if ((localEntry == NULL) ||
(RemainingPath.Length != 0) ||
!(localEntry->Type & PKT_ENTRY_TYPE_LOCAL_XPOINT)) {
//
// We don't have a perfect match or if we do, it is not
// a local exit point.
//
status = DFS_STATUS_BAD_EXIT_POINT;
}
//
// We go in here only if we found the exit pt in the PKT.
//
if ((RemainingPath.Length == 0) && (NT_SUCCESS(status))) {
//
// We first delete the exit Point info from the PKT. Else we
// will not be able to delete the exit point??
//
if (localEntry->LocalService != NULL) {
//
// In this case we should only turn off the exit point
// bit and leave the entry intact.
//
PktEntryUnlinkSubordinate((localEntry->Superior), localEntry);
localEntry->Type &= ~PKT_ENTRY_TYPE_LOCAL_XPOINT;
} else {
PktEntryDestroy(localEntry, pkt, TRUE);
}
PktUpdated = TRUE;
}
//
// We will attempt to delete the exit point on disk irrespective of
// whether we found it in the PKT.
//
// We will remove the last component of the exit path in order to find
// the PKT entry for the local volume on which the exit point exists.
//
ASSERT(ExitPtId->Prefix.Length >= 2*sizeof(WCHAR));
pwch = ExitPtId->Prefix.Buffer;
pwch = pwch + ExitPtId->Prefix.Length/sizeof(WCHAR) - 1;
while ((pwch >= ExitPtId->Prefix.Buffer) && (*pwch != L'\\')) {
pwch--;
}
ASSERT((pwch > ExitPtId->Prefix.Buffer) ||
((pwch == ExitPtId->Prefix.Buffer) && (*pwch == L'\\')));
if (pwch > ExitPtId->Prefix.Buffer) {
//
// Save original length before we update.
//
ParentPrefix = ExitPtId->Prefix;
ParentPrefix.Length = (USHORT)((pwch-ExitPtId->Prefix.Buffer))*sizeof(WCHAR);
} else if (pwch == ExitPtId->Prefix.Buffer) {
//
// This means that we have an exit point off of O:
//
ParentPrefix = ExitPtId->Prefix;
ParentPrefix.Length = sizeof(WCHAR);
} else {
//
// This should never happen because of above asserts
//
DebugTrace(0, 1,
"DFS: Got a Bad ExitPt for deletion: %wZ\n", &ExitPtId->Prefix);
return(DFS_STATUS_BAD_EXIT_POINT);
}
localEntry = PktLookupEntryByPrefix(
pkt,
&ParentPrefix,
&RemainingPath);
if (localEntry && localEntry->LocalService) {
service = localEntry->LocalService;
//
// Adjust the remaining path to take account of the path name
// component we removed above.
//
if (RemainingPath.Buffer == NULL) {
RemainingPath.Buffer = pwch + 1;
RemainingPath.Length =
ExitPtId->Prefix.Length - ParentPrefix.Length;
if (pwch != ExitPtId->Prefix.Buffer) {
RemainingPath.Length -= sizeof(WCHAR);
}
RemainingPath.MaximumLength = RemainingPath.Length + sizeof(WCHAR);
} else {
//
// Here we include the leading Path separator before last component
// in length field whereas in the Previous case we do not
//
RemainingPath.Length += ExitPtId->Prefix.Length-ParentPrefix.Length;
RemainingPath.MaximumLength = RemainingPath.Length + sizeof(WCHAR);
}
//
// We attempt to delete the exit path. But we ignore all
// errors here and continue on. We delete exit path only if we
// are not dealing with an exit point leading to a machine volume.
//
if (!(Type & PKT_ENTRY_TYPE_MACHINE)) {
status = DfsDeleteExitPath(service, &RemainingPath);
if (NT_SUCCESS(status)) {
ExitPtDeleted = TRUE;
}
}
//
// Now that we have deleted the exit pt on disk, let us
// update our knowledge in the registry.
//
if (NT_SUCCESS(status)) {
status = DfsDeleteExitPointInfo(
&localEntry->Id.Uid,
&ExitPtId->Uid);
}
} else {
DebugTrace(0, 1,
"DFS: Could not find a LocalEntry To Delete ExitPt %ws\n",
ExitPtId->Prefix.Buffer);
status = DFS_STATUS_NOSUCH_LOCAL_VOLUME;
}
//
// We go in here only if we managed to Update the PKT and at the
// same time also managed to delete the ExitPt from the Disk, but
// failed to update the DFS.CFG file.
//
if (!NT_SUCCESS(status)) {
//
// If we got here. We definitely did not update the registry config
// info with changes. We might have updated the PKT and
// also deleted the exit point.
//
if (ExitPtDeleted == TRUE) {
// RAID 455283: What do we do here? Should we attempt to
// recreate the exitPt?
}
if (PktUpdated == TRUE) {
// RAID 455283: What do we do here? Should we update
// the Pkt with the exit pt info again?
}
}
//
// We can release the Pkt now...
//
PktRelease(pkt);
DebugTrace(-1, Dbg, "DfsInternalDeleteExitPoint: Exit -> %08lx\n", ULongToPtr( status ));
return status;
}
//+-------------------------------------------------------------------------
//
// Function: DfsFsctrlDeleteExitPoint, public
//
// Synopsis: This method deletes an exit point on disk, updates a DFS.CFG
// file and updates the PKT without the exit point information.
// What should this method do if there are errors along the way
// during any of its 3 steps described above. RAID 455283
//
// Arguments:
//
// Returns: STATUS_SUCCESS -- If all went well.
//
// DFS_STATUS_BAD_EXIT_POINT -- If cannot find exit point.
//
// Note:
//
// History: 02-Feb-93 SudK Created.
//
//--------------------------------------------------------------------------
NTSTATUS
DfsFsctrlDeleteExitPoint(
IN PIRP Irp,
IN PVOID InputBuffer,
IN ULONG InputBufferLength
)
{
NTSTATUS status = STATUS_SUCCESS;
PDFS_DELETE_EXIT_POINT_ARG arg;
DFS_PKT_ENTRY_ID ExitPtId;
ULONG Type;
STD_FSCTRL_PROLOGUE(DfsFsctrlDeleteExitPoint, TRUE, FALSE);
if (InputBufferLength < sizeof(*arg)+sizeof(UNICODE_NULL)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
//
// Unmarshal the argument...
//
arg = (PDFS_DELETE_EXIT_POINT_ARG) InputBuffer;
OFFSET_TO_POINTER( arg->Prefix, arg );
if (!DfspStringInBuffer(arg->Prefix, InputBuffer, InputBufferLength)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
ExitPtId.Uid = arg->Uid;
RtlInitUnicodeString( &ExitPtId.Prefix, arg->Prefix );
Type = arg->Type;
DebugTrace(0, Dbg, "Deleting Exit Point [%wZ]\n", &ExitPtId.Prefix );
DebugTrace(0, Dbg, "Type == %d\n", ULongToPtr( Type ));
status = DfsInternalDeleteExitPoint(&ExitPtId, Type);
exit_with_status:
DfsCompleteRequest( Irp, status );
DebugTrace(-1, Dbg, "DfsFsctrlDeleteExitPoint: Exit -> %08lx\n", ULongToPtr( status ) );
return status;
}
//+---------------------------------------------------------------------
//
// Function: DfsGetPrincipalName, public
//
// Synopsis: Gets the principal name of this machine either from the
// DfsData structure or creates a new one by looking at Registry
// and domain service.
//
// Arguments: [pustrName] -- The Service Name is to be filled in here.
//
// Returns: STATUS_SUCCESS -- If all went well.
//
// History: 30 May 1993 SudK Created.
//
//----------------------------------------------------------------------
NTSTATUS
DfsGetPrincipalName(PUNICODE_STRING pustrName)
{
NTSTATUS status;
ASSERT(ARGUMENT_PRESENT(pustrName));
//
// We first need to acquire DfsData resource.
//
ExAcquireResourceSharedLite(&DfsData.Resource, TRUE);
ASSERT(DfsData.PrincipalName.Length != 0);
if (DfsData.PrincipalName.Length != 0) {
*pustrName = DfsData.PrincipalName;
status = STATUS_SUCCESS;
} else {
status = STATUS_OBJECT_NAME_NOT_FOUND;
}
ExReleaseResourceLite(&DfsData.Resource);
return(status);
}
//+----------------------------------------------------------------------
//
// Function: DfsFileOnExitPath,
//
// Synopsis: This function figures out if the given path lies along an
// exit path on the local machine.
//
// Arguments: [Pkt] -- Shared access is sufficient.
// [StorageId] -- The FilePath which we need to test for.
//
// Returns: TRUE if FilePath is a prefix of some exit path.
// Also if FilePath matches a StorageId then we return TRUE.
// ELSE we return FALSE.
//-----------------------------------------------------------------------
BOOLEAN
DfsFileOnExitPath(
PDFS_PKT Pkt,
PUNICODE_STRING StorageId
)
{
PDFS_LOCAL_VOL_ENTRY lv;
UNICODE_STRING RemStgId;
lv = PktEntryLookupLocalService(Pkt, StorageId, &RemStgId);
if ((lv != NULL) && (RemStgId.Length == 0)) {
//
// If we did find a perfect match, return TRUE
// from this perfect match.
//
return(TRUE);
} else if (lv != NULL) {
//
// We did not have a perfect match. We now need to look for exit pts
// crossing into this StorageId from the match that we found.
//
PDFS_PKT_ENTRY pktEntry, pktExitEntry;
USHORT prefixLength;
UNICODE_STRING RemExitPt;
pktEntry = lv->PktEntry;
pktExitEntry = PktEntryFirstSubordinate(pktEntry);
//
// As long as there are more exit points see if that exit point crosses
// into the new storage Id.
//
while (pktExitEntry != NULL) {
PUNICODE_STRING ExitPrefix;
ExitPrefix = &pktExitEntry->Id.Prefix;
prefixLength = pktEntry->Id.Prefix.Length;
if (ExitPrefix->Buffer[prefixLength/sizeof(WCHAR)] == UNICODE_PATH_SEP)
prefixLength += sizeof(WCHAR);
RemExitPt.Length = pktExitEntry->Id.Prefix.Length - prefixLength;
RemExitPt.MaximumLength = RemExitPt.Length + 1;
RemExitPt.Buffer = &ExitPrefix->Buffer[prefixLength/sizeof(WCHAR)];
//
// Only if the ExitPt has the potential of crossing over into the
// storageId do we do this.
//
if (DfsRtlPrefixPath(&RemStgId, &RemExitPt, TRUE)) {
return(TRUE);
}
pktExitEntry = PktEntryNextSubordinate(pktEntry, pktExitEntry);
} //while exit pt exists
} // lv != NULL
return(FALSE);
}
//+----------------------------------------------------------------------
//
// Function: DfsStorageIdLegal
//
// Synopsis: This function determines if a given storage Id can be used
// to support a new part of the namespace without violating any
// of the DFS rules as specified below.
//
// Arguments: [StorageId] -- UnicodeString which represents the StorageId.
//
// Returns: TRUE if it is Legal else FALSE.
//
// History: 8th Dec. 1993 SudK Created.
//
//-----------------------------------------------------------------------
BOOLEAN
DfsStorageIdLegal(
PUNICODE_STRING StorageId
)
{
PDFS_PKT Pkt;
PDFS_LOCAL_VOL_ENTRY lv;
PUNICODE_PREFIX_TABLE_ENTRY lvpfx;
BOOLEAN conflictFound = FALSE;
if (StorageId->Length == 0)
return(FALSE);
//
// We need Read access to the Pkt.
//
Pkt = _GetPkt();
PktAcquireShared(Pkt, TRUE);
lvpfx = DfsNextUnicodePrefix(&Pkt->LocalVolTable, TRUE);
while (lvpfx != NULL && !conflictFound) {
lv = CONTAINING_RECORD(lvpfx, DFS_LOCAL_VOL_ENTRY, PrefixTableEntry);
if (lv->LocalPath.Length > StorageId->Length) {
if (RtlPrefixUnicodeString(StorageId, &lv->LocalPath, TRUE)) {
//
// StorageId scopes over a directory which is in Dfs - this is
// not allowed.
//
conflictFound = TRUE;
}
}
if (lv->LocalPath.Length <= StorageId->Length) {
if (RtlPrefixUnicodeString(&lv->LocalPath, StorageId, TRUE)) {
//
// StorageId is a child of a Dfs volume - this is not allowed
// either.
//
conflictFound = TRUE;
}
}
lvpfx = DfsNextUnicodePrefix(&Pkt->LocalVolTable, FALSE);
}
//
// If we got here the StorageId was perfectly legal.
//
PktRelease(Pkt);
return( !conflictFound );
}
//+-------------------------------------------------------------------------
//
// Function: DfsExitPtLegal
//
// Synopsis: This function checks to see whether a given exit point is legal
//
// Arguments: [localEntry] -- The Local Entry where exit pt is to be created.
// [Remaining] -- Remaining Path on local Stg Id to be created.
//
// History: 8th Dec. 1993 SudK Created.
//
//--------------------------------------------------------------------------
BOOLEAN
DfsExitPtLegal(
IN PDFS_PKT Pkt,
IN PDFS_PKT_ENTRY localEntry,
IN PUNICODE_STRING Remaining
)
{
NTSTATUS status;
UNICODE_STRING LocalExitPt;
PDFS_LOCAL_VOL_ENTRY lv;
UNICODE_STRING RemExitPt;
PWCHAR pwsz;
BOOLEAN retCode;
USHORT index;
DebugTrace(+1, Dbg, "DfsExitPtLegal Entered : %wZ\n", Remaining);
ASSERT(localEntry != NULL);
//
// We first compose the local path to the exit pt.
//
status = BuildLocalVolPath(&LocalExitPt,
localEntry->LocalService,
Remaining);
//
// If for some reason (Mem Failure) we fail above, then we will just make
// the exit pt to be illegal. That should be an OK restrictions since the
// only reason the above would fail is due to memory failure.
//
if (!NT_SUCCESS(status)) {
DebugTrace(-1, Dbg, "DfsExitPtLegal Exited %08lx\n", ULongToPtr( status ));
return(FALSE);
}
lv = PktEntryLookupLocalService(Pkt, &LocalExitPt, &RemExitPt);
//
// There is atleast the stg id in the local entry so we better getback a
// Non Null value here.
//
ASSERT(lv != NULL);
if (lv != NULL) {
ASSERT(RemExitPt.Length <= Remaining->Length);
//
// If we the remaining path is less than original remaining exit path
// the we definitely hit a different and longer storage Id. So the
// exit pt is illegal since it would cross over into a new storageId.
//
if (RemExitPt.Length < Remaining->Length) {
DebugTrace(-1, Dbg, "Illegal exit pt creation attempted %wZ\n",
&LocalExitPt);
ExFreePool(LocalExitPt.Buffer);
return(FALSE);
}
}
else {
DebugTrace(-1, Dbg, "InternalData structures seem to be corrupt\n",0);
ExFreePool(LocalExitPt.Buffer);
return(FALSE); // We should never get here.
}
//
// Now we need to see if there is an encompassing and shorter StorageId.
// If so we wont allow this exit pt to be created.
//
LocalExitPt.Length = LocalExitPt.Length - Remaining->Length - sizeof(WCHAR);
index = LocalExitPt.Length/sizeof(WCHAR) - 1;
pwsz = &LocalExitPt.Buffer[index];
while ((*pwsz != UNICODE_PATH_SEP) && (index > 0)) {
pwsz--;
index--;
}
if (index == 0) {
ExFreePool(LocalExitPt.Buffer);
return(TRUE); // When would this happen at all.
}
else {
*pwsz = UNICODE_NULL;
LocalExitPt.Length = index*sizeof(WCHAR);
//
// Now we have the StorageId with the last component of StorageId in
// the current local service removed. Lets do a lookup for a shorter
// StgId now.
//
lv = PktEntryLookupLocalService(Pkt, &LocalExitPt, &RemExitPt);
if (lv == NULL) {
retCode = TRUE;
}
else {
DebugTrace(0, Dbg,
"Found shorter StgId %wZ which makes ExitPt illegal\n",
&lv->LocalPath);
retCode = FALSE;
}
}
ExFreePool(LocalExitPt.Buffer);
DebugTrace(-1, Dbg, "DfsExitPtLegal Exited\n", 0);
return(retCode);
}
//+----------------------------------------------------------------------------
//
// Function: BuildShortPrefix
//
// Synopsis: Given the name of an exit path relative to some local volume,
// and the Dfs prefix of the local volume, this routine computes
// the short prefix of the exit path.
//
// Arguments: [pRemPath] -- Exit path relative to local volume
// [pService] -- DFS_SERVICE describing local volume
// [PeidParent] -- Entry path of the local volume
// [Peid] -- EntryID of the volume for which this exit point
// was created.
//
// Returns: [STATUS_SUCCESS] -- Successfully return short prefix in
// Peid->ShortPrefix.
//
// [STATUS_INSUFFICIENT_RESOURCES] -- Out of memory
//
//-----------------------------------------------------------------------------
NTSTATUS
BuildShortPrefix(
PUNICODE_STRING pRemPath,
PDFS_SERVICE pService,
PDFS_PKT_ENTRY_ID PeidParent,
PDFS_PKT_ENTRY_ID Peid)
{
NTSTATUS status;
UNICODE_STRING exitPath, exitPathComponent, shortPrefix;
DebugTrace(+1, Dbg, "BuildShortPrefix - Entered\n", 0);
status = BuildLocalVolPath( &exitPath, pService, pRemPath);
if (!NT_SUCCESS(status)) {
ASSERT(status == STATUS_INSUFFICIENT_RESOURCES );
return( status );
}
DebugTrace(0, Dbg, "Exit Path = [%wZ]\n", &exitPath);
//
// We now have the exit path name, which looks like
// \Device\Harddisk0\Partition1\a\b\c\rem-path
// The short prefix we are after looks like
// \PeidParent.ShortPrefix\8.3-form-of-pRemPath
//
//
// First, allocate room for the short prefix
//
shortPrefix.MaximumLength = PeidParent->ShortPrefix.Length +
exitPath.Length +
sizeof(FILE_NAME_INFORMATION);
shortPrefix.MaximumLength = (USHORT) ROUND_UP_COUNT(
shortPrefix.MaximumLength,
4);
shortPrefix.Buffer = ExAllocatePoolWithTag(PagedPool, shortPrefix.MaximumLength, ' sfD');
if (shortPrefix.Buffer == NULL) {
ExFreePool( exitPath.Buffer );
return( STATUS_INSUFFICIENT_RESOURCES );
}
//
// Copy the parent's short prefix first
//
shortPrefix.Length = PeidParent->ShortPrefix.Length;
RtlCopyMemory(
shortPrefix.Buffer,
PeidParent->ShortPrefix.Buffer,
PeidParent->ShortPrefix.Length);
//
// Now, for each component of pRemPath, figure out its 8.3 form, and
// append it to the short prefix we are constructing.
//
exitPathComponent = exitPath;
exitPathComponent.Length -= pRemPath->Length;
do {
ULONG n, lastIndex;
HANDLE componentHandle;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK ioStatus;
PFILE_NAME_INFORMATION shortNameInfo;
if (exitPathComponent.Buffer[
exitPathComponent.Length/sizeof(WCHAR)] ==
UNICODE_PATH_SEP) {
exitPathComponent.Length += sizeof(WCHAR);
}
for (n = exitPathComponent.Length / sizeof(WCHAR),
lastIndex = exitPath.Length / sizeof(WCHAR);
n < lastIndex &&
exitPathComponent.Buffer[n] != UNICODE_PATH_SEP;
n++,
exitPathComponent.Length += sizeof(WCHAR)) {
NOTHING;
}
DebugTrace(0, Dbg, "ExitPathComponent: [%wZ]\n", &exitPathComponent);
InitializeObjectAttributes(
&objectAttributes,
&exitPathComponent,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = ZwOpenFile(
&componentHandle,
FILE_READ_ATTRIBUTES,
&objectAttributes,
&ioStatus,
FILE_SHARE_VALID_FLAGS,
FILE_DIRECTORY_FILE);
if (NT_SUCCESS(status)) {
shortNameInfo = (PFILE_NAME_INFORMATION)
&shortPrefix.Buffer[
shortPrefix.Length/sizeof(WCHAR)];
shortNameInfo = ROUND_UP_POINTER(shortNameInfo, 4);
status = ZwQueryInformationFile(
componentHandle,
&ioStatus,
shortNameInfo,
shortPrefix.MaximumLength - shortPrefix.Length,
FileAlternateNameInformation);
ZwClose( componentHandle );
}
if (NT_SUCCESS(status)) {
ULONG componentLength;
componentLength = shortNameInfo->FileNameLength;
shortPrefix.Buffer[ shortPrefix.Length/sizeof(WCHAR) ] =
UNICODE_PATH_SEP;
shortPrefix.Length += sizeof(WCHAR);
RtlMoveMemory(
&shortPrefix.Buffer[ shortPrefix.Length/sizeof(WCHAR) ],
shortNameInfo->FileName,
componentLength);
shortPrefix.Length += (USHORT) componentLength;
}
DebugTrace(0, Dbg, "ShortPrefix: [%wZ]\n", &shortPrefix);
} while ( NT_SUCCESS(status) &&
exitPathComponent.Length < exitPath.Length );
if (!NT_SUCCESS(status)) {
//
// We failed to compute the short prefix. So, we'll just use the
// prefix as our short prefix.
//
shortPrefix.Length = PeidParent->Prefix.Length;
RtlCopyMemory(
&shortPrefix.Buffer[ shortPrefix.Length/sizeof(WCHAR) ],
&Peid->Prefix.Buffer[ PeidParent->Prefix.Length/sizeof(WCHAR) ],
Peid->Prefix.Length - PeidParent->Prefix.Length);
}
Peid->ShortPrefix = shortPrefix;
ExFreePool( exitPath.Buffer );
DebugTrace(-1, Dbg, "BuildShortPrefix: Exiting [%wZ]\n", &shortPrefix);
return( STATUS_SUCCESS );
}
//+----------------------------------------------------------------------------
//
// Function: StripLastComponent, private
//
// Synopsis: Strip the trailing backslash and path from a name. Adjusts
// the Length field, leaves the MaximumLength field alone.
//
// Arguments: [pustr] -- pointer to unicode string
//
// Returns: nothing
//
//-----------------------------------------------------------------------------
VOID
StripLastComponent(PUNICODE_STRING pustr)
{
PWCHAR pwch;
USHORT i = 0;
pwch = pustr->Buffer;
pwch += (pustr->Length/sizeof(WCHAR)) - 1;
while ((*pwch != UNICODE_PATH_SEP) && (pwch != pustr->Buffer)) {
i += sizeof(WCHAR);
pwch--;
}
if ((*pwch == UNICODE_PATH_SEP) && (pwch != pustr->Buffer)) {
i += sizeof(WCHAR);
pwch--;
}
pustr->Length -= i;
}
//+----------------------------------------------------------------------------
//
// Function: AddLastComponent, private
//
// Synopsis: Restore the trailing backslash and path from a name. Adjusts
// the Length field, leaves the MaximumLength field alone. Assumes
// that MaximumLength is the 'real' length of the string.
//
// Arguments: [pustr] -- pointer to unicode string
//
// Returns: nothing
//
//-----------------------------------------------------------------------------
VOID
AddLastComponent(PUNICODE_STRING pustr)
{
PWCHAR pwch;
PWCHAR pend;
USHORT i = 0;
pwch = pustr->Buffer;
pwch += (pustr->Length/sizeof(WCHAR)) - 1;
pend = &pustr->Buffer[pustr->MaximumLength/sizeof(WCHAR) - 1];
if (pwch != pend) {
i += sizeof(WCHAR);
pwch++;
}
if ((*pwch == UNICODE_PATH_SEP) && (pwch != pend)) {
i += sizeof(WCHAR);
pwch++;
}
while ((*pwch != UNICODE_PATH_SEP) && (pwch != pend)) {
i += sizeof(WCHAR);
pwch++;
}
if ((*pwch == UNICODE_PATH_SEP) && (pwch != pend)) {
i -= sizeof(WCHAR);
pwch--;
}
pustr->Length += i;
}
#define DFS_DIRECTORY_INFO_BUFFER_SIZE 1024
#define DFS_MAX_DELETE_FILES_IN_DIRECTORY 3
//+----------------------------------------------------------------------------
//
// Function: DfsDeleteDirectoryCheck, private
//
// Synopsis: Given a buffer of FILE_NAMES_INFORMATION, this will indicate
// if the given directory can be deleted.
//
// Arguments: [buffer] -- buffer containing info
//
// Returns: TRUE if directory can be deleted.
//
//-----------------------------------------------------------------------------
BOOLEAN
DfsDeleteDirectoryCheck(
UCHAR *Buffer)
{
FILE_NAMES_INFORMATION *buf;
ULONG numFiles = 1;
BOOLEAN DeleteOk = TRUE;
buf = (FILE_NAMES_INFORMATION *)Buffer;
while (buf->NextEntryOffset) {
numFiles++;
if (numFiles > DFS_MAX_DELETE_FILES_IN_DIRECTORY) {
DeleteOk = FALSE;
break;
}
buf = (FILE_NAMES_INFORMATION *)((UCHAR *)(buf) + buf->NextEntryOffset);
}
return DeleteOk;
}
//+----------------------------------------------------------------------------
//
// Function: DfsGetDirectoriesToDelete, private
//
// Synopsis: Walk the pathnames all the way up to the sharename, and
// and return a count of number of levels that can be deleted.
// NOTE: This is not atomic! (If someone creates a directory or
// file AFTER this check, they are hosed since we will mark the
// directory for delete)
//
// Arguments: pExitPtName : name of exit point
// pShareName : name of the share.
//
// Returns: count of number of levels that can be deleted.
//
//-----------------------------------------------------------------------------
ULONG
DfsGetDirectoriesToDelete(
PUNICODE_STRING pExitPtName,
PUNICODE_STRING pShareName)
{
PUCHAR Buffer;
ULONG Deletes = 0;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE exitPtHandle;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status, CloseStatus;
UNICODE_STRING ExitPtName;
ExitPtName = *pExitPtName;
Buffer = ExAllocatePoolWithTag(PagedPool,
DFS_DIRECTORY_INFO_BUFFER_SIZE,
' sfD');
if (Buffer == NULL)
return ++Deletes;
do {
InitializeObjectAttributes(
&objectAttributes,
&ExitPtName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = ZwOpenFile(
&exitPtHandle,
FILE_LIST_DIRECTORY | SYNCHRONIZE,
&objectAttributes,
&ioStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
if (NT_SUCCESS(status)) {
status = ZwQueryDirectoryFile(
exitPtHandle,
NULL, NULL, NULL,
&ioStatus,
Buffer,
DFS_DIRECTORY_INFO_BUFFER_SIZE,
FileNamesInformation,
FALSE,
NULL,
TRUE);
CloseStatus = ZwClose( exitPtHandle );
}
if (!NT_SUCCESS(status) ||
(DfsDeleteDirectoryCheck(Buffer) == FALSE)) {
break;
}
Deletes++;
StripLastComponent(&ExitPtName);
} while (!RtlEqualUnicodeString(&ExitPtName, pShareName, TRUE));
if (Buffer)
ExFreePool(Buffer);
return Deletes;
}