|
|
//+----------------------------------------------------------------------------
//
// 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; }
|