3223 lines
81 KiB
C
3223 lines
81 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
utils.c
|
||
|
||
Abstract:
|
||
|
||
This module implements common subroutines in the NT redirector
|
||
|
||
Author:
|
||
|
||
Larry Osterman (LarryO) 20-Jun-1990
|
||
|
||
Revision History:
|
||
|
||
20-Jun-1990 LarryO
|
||
|
||
Created
|
||
|
||
|
||
--*/
|
||
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, RdrLockUsersBuffer)
|
||
#pragma alloc_text(PAGE, RdrMapUsersBuffer)
|
||
#pragma alloc_text(PAGE, RdrCopyNetworkPath)
|
||
#pragma alloc_text(PAGE, RdrCanonicalizeFilename)
|
||
#pragma alloc_text(PAGE, RdrNumberOfComponents)
|
||
#pragma alloc_text(PAGE, RdrCanonicalizeAndCopyShare)
|
||
#pragma alloc_text(PAGE, RdrCopyUnicodeStringToUnicode)
|
||
#pragma alloc_text(PAGE, RdrCopyUnicodeStringToAscii)
|
||
#pragma alloc_text(PAGE, RdrExtractNextComponentName)
|
||
#pragma alloc_text(PAGE, RdrExtractPathAndFileName)
|
||
#pragma alloc_text(PAGE, RdrExtractServerShareAndPath)
|
||
#pragma alloc_text(PAGE, RdrConvertTimeToSmbTime)
|
||
#pragma alloc_text(PAGE, RdrTimeToSecondsSince1970)
|
||
#pragma alloc_text(PAGE, RdrCanFileBeBuffered)
|
||
#pragma alloc_text(PAGE, RdrMapDisposition)
|
||
#pragma alloc_text(PAGE, RdrMapDesiredAccess)
|
||
#pragma alloc_text(PAGE, RdrMapShareAccess)
|
||
#pragma alloc_text(PAGE, RdrMapFileAttributes)
|
||
#pragma alloc_text(PAGE, RdrPackNtString)
|
||
#pragma alloc_text(PAGE, RdrPackString)
|
||
#pragma alloc_text(PAGE, RdrExceptionFilter)
|
||
#pragma alloc_text(PAGE, RdrProcessException)
|
||
#pragma alloc_text(PAGE, RdrIsFileBatch)
|
||
|
||
#pragma alloc_text(PAGE3FILE, RdrSecondsSince1970ToTime)
|
||
#pragma alloc_text(PAGE3FILE, RdrMapSmbAttributes)
|
||
#pragma alloc_text(PAGE3FILE, RdrUnmapDisposition)
|
||
|
||
#endif
|
||
|
||
VOID
|
||
RdrSmbScrounge (
|
||
IN PSMB_HEADER Smb,
|
||
IN PSERVERLISTENTRY Sle OPTIONAL,
|
||
IN BOOLEAN DfsFile,
|
||
IN BOOLEAN KnowsEas,
|
||
IN BOOLEAN KnowsLongNames
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine "scrounges" common fields in an SMB header
|
||
.
|
||
Arguments:
|
||
|
||
PSMB_HEADER Smb - Supplies a pointer to the SMB header
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT Flags2 = 0;
|
||
|
||
*(PULONG)(&Smb->Protocol) = (ULONG)SMB_HEADER_PROTOCOL;
|
||
|
||
//
|
||
// First initialize all the fields in the SMB to 0
|
||
//
|
||
|
||
RtlZeroMemory(&Smb->ErrorClass, sizeof(SMB_HEADER) -
|
||
FIELD_OFFSET(SMB_HEADER, ErrorClass));
|
||
|
||
//
|
||
// By default, paths in SMB's are marked as case insensitive, and
|
||
// canonicalized.
|
||
//
|
||
|
||
Smb->Flags = SMB_FLAGS_CASE_INSENSITIVE | SMB_FLAGS_CANONICALIZED_PATHS;
|
||
|
||
if (Sle != NULL) {
|
||
if (Sle->Capabilities & DF_UNICODE) {
|
||
Flags2 |= SMB_FLAGS2_UNICODE;
|
||
}
|
||
|
||
}
|
||
if ((Sle == NULL) || (Sle->Capabilities & DF_LANMAN20)) {
|
||
|
||
//
|
||
// Only ask for longnames and EAs if the we don't know the server or
|
||
// if the server is a Lanman 2.0 or greater server.
|
||
//
|
||
|
||
if (KnowsEas) {
|
||
Flags2 |= SMB_FLAGS2_KNOWS_EAS;
|
||
}
|
||
|
||
if (KnowsLongNames) {
|
||
Flags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
|
||
}
|
||
|
||
}
|
||
|
||
if (Sle == NULL || (Sle->Capabilities & DF_DFSAWARE)) {
|
||
if (DfsFile) {
|
||
Flags2 |= SMB_FLAGS2_DFS;
|
||
}
|
||
}
|
||
|
||
SmbPutAlignedUshort(&Smb->Flags2, Flags2);
|
||
|
||
//
|
||
// We want to fill in the PID here once we figure out how to do that.
|
||
//
|
||
|
||
SmbPutUshort (&Smb->Pid, RDR_PROCESS_ID);
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
ULONG
|
||
RdrMdlLength (
|
||
register IN PMDL Mdl
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the number of bytes in an MDL
|
||
.
|
||
Arguments:
|
||
|
||
IN PMDL Mdl - Supplies the MDL to determine the length on.
|
||
|
||
Return Value:
|
||
|
||
ULONG - Number of bytes in the MDL
|
||
|
||
--*/
|
||
|
||
{
|
||
register ULONG Size = 0;
|
||
|
||
while (Mdl!=NULL) {
|
||
Size += MmGetMdlByteCount(Mdl);
|
||
Mdl = Mdl->Next;
|
||
}
|
||
return Size;
|
||
}
|
||
|
||
NTSTATUS
|
||
RdrLockUsersBuffer (
|
||
IN PIRP Irp,
|
||
IN LOCK_OPERATION Operation,
|
||
IN ULONG BufferLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will probe and lock the buffer described by the
|
||
provided Irp.
|
||
|
||
Arguments:
|
||
|
||
IN PIRP Irp - Supplies the IRP that is to be locked.
|
||
IN LOCK_OPERATION Operation - Supplies the operation type to probe.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ((Irp->MdlAddress == NULL)) {
|
||
|
||
Irp->MdlAddress = IoAllocateMdl(Irp->UserBuffer,
|
||
BufferLength,
|
||
FALSE,
|
||
TRUE,
|
||
NULL);
|
||
|
||
|
||
if (Irp->MdlAddress == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
try {
|
||
|
||
//
|
||
// Now probe and lock down the user's data buffer.
|
||
//
|
||
|
||
MmProbeAndLockPages(Irp->MdlAddress,
|
||
Irp->RequestorMode,
|
||
Operation);
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
|
||
//
|
||
// We blew up in the probe and lock, free up the MDL
|
||
// and set the IRP to have a null MDL pointer - we are failing the
|
||
// request
|
||
//
|
||
|
||
IoFreeMdl(Irp->MdlAddress);
|
||
Irp->MdlAddress = NULL;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return Status;
|
||
|
||
}
|
||
BOOLEAN
|
||
RdrMapUsersBuffer (
|
||
IN PIRP Irp,
|
||
OUT PVOID *UserBuffer,
|
||
IN ULONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will probe and lock the buffer described by the
|
||
provided Irp.
|
||
|
||
Arguments:
|
||
|
||
IN PIRP Irp - Supplies the IRP that is to be mapped.
|
||
OUT PVOID *Buffer - Returns a buffer that maps the user's buffer in the IRP
|
||
|
||
Return Value:
|
||
|
||
TRUE - The buffer was mapped into the current address space.
|
||
FALSE - The buffer was NOT mapped in, it was already mappable.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if (Irp->MdlAddress) {
|
||
*UserBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
|
||
|
||
} else if (Irp->AssociatedIrp.SystemBuffer != NULL) {
|
||
*UserBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
} else {
|
||
|
||
if (Irp->RequestorMode != KernelMode) {
|
||
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
if ((Length != 0) && (Irp->UserBuffer != 0)) {
|
||
|
||
if ((IrpSp->MajorFunction == IRP_MJ_READ) ||
|
||
(IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) ||
|
||
(IrpSp->MajorFunction == IRP_MJ_QUERY_VOLUME_INFORMATION) ||
|
||
(IrpSp->MajorFunction == IRP_MJ_QUERY_SECURITY) ||
|
||
(IrpSp->MajorFunction == IRP_MJ_DIRECTORY_CONTROL)) {
|
||
|
||
ProbeForWrite( Irp->UserBuffer,
|
||
Length,
|
||
sizeof(UCHAR) );
|
||
} else {
|
||
ProbeForRead( Irp->UserBuffer,
|
||
Length,
|
||
sizeof(UCHAR) );
|
||
}
|
||
}
|
||
}
|
||
|
||
*UserBuffer = Irp->UserBuffer;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
NTSTATUS
|
||
RdrCopyNetworkPath (
|
||
IN OUT PVOID *Destination,
|
||
IN PUNICODE_STRING PathName,
|
||
IN PSERVERLISTENTRY Server,
|
||
IN CHAR CoreProtocol,
|
||
IN USHORT SkipCount
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine places an SMB file name into the specified buffer.
|
||
It takes as input a UNICODE string and places the string in the buffer
|
||
in ANSI format. If you wish to store the filename as UNICODE, use
|
||
the RdrCopyUnicodeNetworkPath routine.
|
||
|
||
Arguments:
|
||
|
||
IN OUT PSZ *Destination - Supplies a pointer into the SMB buffer to send.
|
||
IN PUNICODE_STRING PathName - Supplies the name to put into the SMB.
|
||
IN PSERVERLISTENTRY Server - Supplies the server for the destination.
|
||
We use this to determine if the path should be unicode
|
||
or not in the SMB.
|
||
IN CHAR CoreProtocol - value for type parameter if this is for a
|
||
CORE ms-net protocol FALSE (0) otherwise.
|
||
IN USHORT SkipCount - Supplies the number of backslashes to skip.
|
||
Normally this is set to 3 so that
|
||
\SERVER\SHARE\PATH is skipped to \PATH unless
|
||
SHARE == PIPE in which the caller sets SkipPath to
|
||
2 to get \PIPE\PATH.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT i;
|
||
USHORT PathComp = 0;
|
||
USHORT PathLength;
|
||
UNICODE_STRING RemainingPart;
|
||
NTSTATUS Status;
|
||
PSZ dest = *Destination;
|
||
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_SMB, ("RdrCopyNetworkPath %wZ\n", PathName));
|
||
|
||
if (CoreProtocol) {
|
||
*dest++ = CoreProtocol;
|
||
}
|
||
|
||
//
|
||
// The name passed in is of the form \SERVER\SHARE\PATH.
|
||
//
|
||
// Skip over the first n components in the path. Use the literals
|
||
// SKIP_SERVER_SHARE(=3) and SKIP_SERVER(=2)
|
||
//
|
||
|
||
PathLength = PathName->Length/sizeof(WCHAR);
|
||
|
||
for (i=0; i<PathLength; i++) {
|
||
if ((PathName->Buffer[i] == OBJ_NAME_PATH_SEPARATOR) &&
|
||
(++PathComp==SkipCount)) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
RemainingPart.Buffer = &PathName->Buffer[i];
|
||
RemainingPart.MaximumLength =
|
||
RemainingPart.Length = PathName->Length - i*sizeof(WCHAR);
|
||
|
||
//
|
||
// If the last character in the name is a "\", strip the trailing "\"
|
||
//
|
||
|
||
if (PathName->Buffer[PathLength-1] == OBJ_NAME_PATH_SEPARATOR) {
|
||
RemainingPart.Length -= sizeof(WCHAR);
|
||
}
|
||
|
||
if (Server->Capabilities & DF_UNICODE) {
|
||
PWCH UDest = ALIGN_SMB_WSTR(dest);
|
||
|
||
//
|
||
// If there are not path components (if we are trying to access
|
||
// \Server\Share), or we are simply trying to access \Server\Share\,
|
||
// we want to put the directory name as the path
|
||
// into the SMB.
|
||
//
|
||
|
||
if (RemainingPart.Length == 0) {
|
||
*UDest++ = L'\\';
|
||
} else {
|
||
RtlCopyMemory(UDest,
|
||
RemainingPart.Buffer,
|
||
RemainingPart.Length);
|
||
|
||
UDest += RemainingPart.Length/sizeof(WCHAR);
|
||
}
|
||
|
||
*UDest++ = UNICODE_NULL;
|
||
|
||
*Destination = UDest;
|
||
} else {
|
||
//
|
||
// If there are not path components (if we are trying to access
|
||
// \Server\Share), we want to put the directory name as the path
|
||
// into the SMB.
|
||
//
|
||
|
||
if (RemainingPart.Length == 0) {
|
||
*dest++ = OBJ_NAME_PATH_SEPARATOR;
|
||
} else {
|
||
|
||
OEM_STRING AnsiPath = {0,RemainingPart.Length*sizeof(WCHAR),dest};
|
||
|
||
//
|
||
// Convert the UNICODE name into OEM directly into the SMB
|
||
//
|
||
|
||
Status = RtlUnicodeStringToOemString(&AnsiPath, &RemainingPart, FALSE);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
dest += AnsiPath.Length;
|
||
}
|
||
|
||
*dest++ = '\0';
|
||
|
||
*Destination = dest;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
#ifdef _M_IX86
|
||
_inline
|
||
#endif
|
||
BOOLEAN
|
||
RdrIsDiskOrPrintType(
|
||
IN PWSTR Source,
|
||
IN DWORD SourceLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns TRUE if the specified string is either a disk or print
|
||
share.
|
||
|
||
Arguments:
|
||
|
||
IN PWSTR Source - String to fill in with file name.
|
||
IN DWORD SourceLength - Number of bytes in Source.
|
||
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - True if this is <A-Z>: or LPT<1-9>:
|
||
|
||
--*/
|
||
{
|
||
if ((SourceLength > 2) &&
|
||
(Source[1] == L':') &&
|
||
(((Source[0] >= L'A') &&
|
||
(Source[0] <= L'Z')) ||
|
||
((Source[0] >= L'a') &&
|
||
(Source[0] <= L'z'))
|
||
)) {
|
||
return TRUE;
|
||
}
|
||
|
||
if ((SourceLength > 5) &&
|
||
(Source[4] == L':') &&
|
||
(Source[0] == L'L') &&
|
||
(Source[1] == L'P') &&
|
||
(Source[2] == L'T') &&
|
||
(Source[3] >= L'1') &&
|
||
(Source[3] <= L'9')
|
||
) {
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
NTSTATUS
|
||
RdrCanonicalizeFilename (
|
||
OUT PUNICODE_STRING NewFileName,
|
||
OUT PBOOLEAN WildCardsFound OPTIONAL,
|
||
OUT PUNICODE_STRING DeviceName OPTIONAL,
|
||
OUT PUNICODE_STRING BaseFileName OPTIONAL,
|
||
IN BOOLEAN WildCardsAllowed,
|
||
IN PUNICODE_STRING NtFileName,
|
||
IN PUNICODE_STRING RelatedName OPTIONAL,
|
||
IN PUNICODE_STRING RootDevice OPTIONAL,
|
||
IN CANONICALIZATION_TYPE Type
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes a file name and an optional related FCB, and
|
||
canonicalizes the file specified according to the naming rules of
|
||
the remote file system.
|
||
|
||
|
||
Arguments:
|
||
|
||
OUT PUNICODE_STRING NewFileName - String to fill in with file name.
|
||
|
||
OUT PBOOLEAN WildCardsFound OPTIONAL - Name contains one or more of ? or *.
|
||
|
||
OUT PUNICODE_STRING DeviceName OPTIONAL - Name of device (X:)
|
||
|
||
OUT PUNICODE_STRING BaseFileName OPTIONAL - Name of base file (if alternate
|
||
data streams specified)
|
||
|
||
IN PBOOLEAN WildCardsAllowed - If TRUE the last component can contain ?
|
||
or *.
|
||
|
||
IN PUNICODE_STRING NtFileName - Supplies the name that Nt OS/2 supplied the
|
||
redirector for the file to open.
|
||
|
||
IN PUNICODE_STRING RelatedName OPTIONAL - Optional name of related file object.
|
||
|
||
IN CANONICALIZATION_TYPE Type - Specifies the rules to apply when
|
||
canonicalizing
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Status of canonicalization operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
PWSTR NameBuffer = NULL;
|
||
USHORT NameBufferLength = 0;
|
||
LONG ComponentSize = 0;
|
||
LONG ComponentMaxSize;
|
||
PWSTR Dest;
|
||
PWSTR Source;
|
||
USHORT DestLength = 0;
|
||
USHORT i;
|
||
BOOLEAN WildName = FALSE;
|
||
UNICODE_STRING DestString;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
switch (Type) {
|
||
case CanonicalizeAsDownLevel:
|
||
ComponentMaxSize = MAXIMUM_COMPONENT_CORE;
|
||
break;
|
||
|
||
case CanonicalizeAsLanman20:
|
||
ComponentMaxSize = MAXIMUM_COMPONENT_LANMAN12;
|
||
break;
|
||
|
||
case CanonicalizeAsNtLanman:
|
||
ComponentMaxSize = MAXIMUM_FILENAME_LENGTH;
|
||
break;
|
||
|
||
default:
|
||
return(STATUS_INVALID_PARAMETER);
|
||
break;
|
||
}
|
||
|
||
NameBufferLength = NtFileName->Length + (ARGUMENT_PRESENT(RelatedName) ?
|
||
RelatedName->Length :
|
||
0)+2*sizeof(WCHAR);
|
||
|
||
NameBuffer = ALLOCATE_POOL(PagedPool, NameBufferLength, POOL_CANONNAME);
|
||
|
||
if (NameBuffer == NULL) {
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
NewFileName->Buffer = NULL;
|
||
|
||
Dest = NameBuffer;
|
||
|
||
try {
|
||
ULONG ComponentNumber = 0;
|
||
|
||
DestString.Buffer = NameBuffer;
|
||
DestString.Length = 0;
|
||
DestString.MaximumLength = NameBufferLength;
|
||
|
||
//
|
||
// Prepend the related FCB's name if one is supplied.
|
||
//
|
||
//
|
||
// Please note that the related name has already been canonicalized
|
||
// appropriately.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(RelatedName) && RelatedName->Length > 0) {
|
||
|
||
ComponentNumber = RdrNumberOfComponents(RelatedName);
|
||
|
||
RtlCopyUnicodeString(&DestString, RelatedName);
|
||
|
||
Dest += (RelatedName->Length/sizeof(WCHAR));
|
||
|
||
if ( *(Dest-1) != OBJ_NAME_PATH_SEPARATOR ) {
|
||
*Dest++ = OBJ_NAME_PATH_SEPARATOR; // Stick a "\" at the string's end.
|
||
ComponentNumber += 1;
|
||
}
|
||
}
|
||
|
||
if ((Source = NtFileName->Buffer) != NULL ) {
|
||
|
||
//
|
||
// If this is \x: (or \LPTx:), it's a drive. Skip over
|
||
// this portion of the file name
|
||
//
|
||
|
||
if (NtFileName->Length >= 2 * sizeof(WCHAR) &&
|
||
Source[0] == OBJ_NAME_PATH_SEPARATOR &&
|
||
RdrIsDiskOrPrintType(&Source[1], NtFileName->Length / sizeof(WCHAR) )) {
|
||
|
||
//
|
||
// If the caller is interested in finding out what device this
|
||
// file is associated with, fill it in.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(DeviceName)) {
|
||
if (ARGUMENT_PRESENT(RootDevice)) {
|
||
|
||
*DeviceName = *RootDevice;
|
||
|
||
} else {
|
||
UNICODE_STRING DeviceString;
|
||
|
||
//
|
||
// Concoct a unicode string that contains the name
|
||
// and duplicate it into the return buffer.
|
||
//
|
||
// For conventions sake, we simply store the "x:"
|
||
// or "LPTx:" in the buffer, we do NOT store the
|
||
// leading "\".
|
||
//
|
||
|
||
DeviceString.Buffer = Source + 1;
|
||
|
||
if (DeviceString.Buffer[1] == L':') {
|
||
DeviceString.Length = DeviceString.MaximumLength = 4;
|
||
} else {
|
||
DeviceString.Length = DeviceString.MaximumLength = 10;
|
||
}
|
||
|
||
*DeviceName = DeviceString;
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// This is a disk or print redirection. Skip over the
|
||
// \x: or \LPTx: and proceed.
|
||
//
|
||
|
||
if (Source[2] == ':') {
|
||
Source += 3; // Skip over the \x:
|
||
i = 3; // Initialize the source index to account for the \x:
|
||
} else {
|
||
Source += 6; // Skip over the \LPTx:
|
||
i = 6; // Initialize the source index to account for the \LPTx:
|
||
}
|
||
|
||
ASSERT (Source[-1] == L':');
|
||
|
||
} else {
|
||
|
||
//
|
||
// If the user cared if there is a device name attached, but
|
||
// there is no device name associated with the filename,
|
||
// return a null device name.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(DeviceName)) {
|
||
RtlInitUnicodeString(DeviceName, NULL);
|
||
}
|
||
|
||
//
|
||
// We're looking at a relative name, so we want to start the
|
||
// source index at 0, since we're looking at the start of
|
||
// the Source string.
|
||
//
|
||
|
||
i = 0;
|
||
}
|
||
|
||
for ( ;
|
||
|
||
i < (USHORT)(NtFileName->Length/sizeof(WCHAR)) ;
|
||
|
||
i++) {
|
||
WCHAR Ch = *Source++;
|
||
USHORT DotPosition = 0;
|
||
|
||
//
|
||
// If this is a path character, reset the component counter.
|
||
//
|
||
|
||
if (Ch == OBJ_NAME_PATH_SEPARATOR) {
|
||
|
||
ComponentSize = 0;
|
||
DestLength += 1;
|
||
DotPosition = 0;
|
||
|
||
ComponentNumber += 1;
|
||
|
||
if ( (Type == CanonicalizeAsLanman20) &&
|
||
(DestLength > LM20_PATHLEN) ) {
|
||
try_return (Status = STATUS_OBJECT_PATH_SYNTAX_BAD);
|
||
}
|
||
|
||
*Dest++ = Ch;
|
||
continue;
|
||
}
|
||
|
||
if ( FsRtlIsUnicodeCharacterWild(Ch) ) {
|
||
WildName = TRUE;
|
||
}
|
||
|
||
//
|
||
// Indicate we are putting one more byte into the component
|
||
// name.
|
||
//
|
||
|
||
ComponentSize += 1;
|
||
|
||
//
|
||
// If there are too many bytes in the component, return name
|
||
// invalid.
|
||
//
|
||
|
||
if (ComponentNumber == 1) {
|
||
|
||
//
|
||
// We can't support more than MAX_PATH bytes in a computer
|
||
// name
|
||
//
|
||
|
||
if (ComponentSize > MAX_PATH) {
|
||
try_return (Status = STATUS_BAD_NETWORK_PATH);
|
||
}
|
||
} else if (ComponentNumber == 2) {
|
||
//
|
||
// If we are looking at the share part, make sure that
|
||
// it is only NNLEN (or LM20_NNLEN) bytes long.
|
||
//
|
||
|
||
if ((Type == CanonicalizeAsNtLanman)) {
|
||
if (ComponentSize > NNLEN) {
|
||
|
||
try_return(Status = STATUS_BAD_NETWORK_NAME);
|
||
}
|
||
|
||
} else if (ComponentSize > LM20_NNLEN) {
|
||
|
||
try_return (Status = STATUS_BAD_NETWORK_NAME);
|
||
}
|
||
|
||
} else if (ComponentSize > ComponentMaxSize) {
|
||
|
||
//
|
||
// Otherwise we're looking at the file portion of
|
||
// the name, so if it's larger than the max component
|
||
// size, punt.
|
||
//
|
||
|
||
|
||
try_return (Status = STATUS_OBJECT_NAME_INVALID);
|
||
}
|
||
|
||
DestLength += 1;
|
||
|
||
if ( (Type == CanonicalizeAsDownLevel) &&
|
||
(DestLength > MAXIMUM_PATHLEN_CORE) ) {
|
||
try_return (Status = STATUS_OBJECT_PATH_SYNTAX_BAD);
|
||
}
|
||
|
||
if ( (Type == CanonicalizeAsLanman20) &&
|
||
(DestLength > MAXIMUM_PATHLEN_LANMAN12) ) {
|
||
try_return (Status = STATUS_OBJECT_PATH_SYNTAX_BAD);
|
||
}
|
||
|
||
*Dest++ = Ch;
|
||
|
||
}
|
||
}
|
||
|
||
DestLength += 1;
|
||
|
||
if (DestLength >= (USHORT)(NameBufferLength / sizeof(WCHAR))) {
|
||
try_return (Status = STATUS_OBJECT_PATH_SYNTAX_BAD);
|
||
}
|
||
|
||
*Dest++ = L'\0';
|
||
|
||
RtlInitUnicodeString(&DestString, NameBuffer);
|
||
|
||
if (Type==CanonicalizeAsDownLevel) {
|
||
Status = RtlUpcaseUnicodeString(&DestString, &DestString, FALSE);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
try_return (Status);
|
||
}
|
||
}
|
||
|
||
Status = RdrpDuplicateUnicodeStringWithString(NewFileName, &DestString,
|
||
PagedPool, FALSE);
|
||
if (!NT_SUCCESS(Status)) {
|
||
try_return (Status);
|
||
}
|
||
|
||
//
|
||
// Perform a preliminary check to make sure that the name conforms to
|
||
// the naming scheme of the destination server.
|
||
//
|
||
|
||
if ( (NewFileName->Length == 0) ||
|
||
(NewFileName->Buffer[0] != L'\\') ||
|
||
(!WildCardsAllowed && FsRtlDoesNameContainWildCards(NewFileName)) ) {
|
||
|
||
try_return(Status = STATUS_OBJECT_NAME_INVALID);
|
||
|
||
}
|
||
|
||
if (Type != CanonicalizeAsNtLanman) {
|
||
|
||
OEM_STRING OemString;
|
||
UNICODE_STRING ServerName;
|
||
UNICODE_STRING ShareName;
|
||
UNICODE_STRING PathName;
|
||
|
||
Status = RdrExtractServerShareAndPath(NewFileName, &ServerName, &ShareName,
|
||
&PathName);
|
||
if (!NT_SUCCESS(Status)) {
|
||
try_return(Status);
|
||
}
|
||
|
||
//
|
||
// If there was a path specified, check to make sure that it
|
||
// is legal.
|
||
//
|
||
|
||
if (PathName.Length != 0) {
|
||
|
||
Status = RtlUnicodeStringToOemString(&OemString, &PathName, TRUE);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
try_return(Status);
|
||
}
|
||
|
||
//
|
||
// If we are canonicalizing as FAT, use FAT rules, otherwise use
|
||
// HPFS rules.
|
||
//
|
||
|
||
if (Type == CanonicalizeAsDownLevel) {
|
||
|
||
if (!FsRtlIsFatDbcsLegal(OemString, WildCardsAllowed, TRUE, TRUE)) {
|
||
|
||
RtlFreeOemString(&OemString);
|
||
|
||
try_return(Status = STATUS_OBJECT_NAME_INVALID);
|
||
}
|
||
|
||
} else if (!FsRtlIsHpfsDbcsLegal(OemString, WildCardsAllowed, TRUE, TRUE)) {
|
||
|
||
RtlFreeOemString(&OemString);
|
||
|
||
try_return(Status = STATUS_OBJECT_NAME_INVALID);
|
||
|
||
}
|
||
|
||
RtlFreeOemString(&OemString);
|
||
|
||
}
|
||
} else {
|
||
LPWSTR LastCharacter;
|
||
|
||
//
|
||
// If the caller is interested in knowing the base file name,
|
||
// return it to them.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(BaseFileName)) {
|
||
|
||
*BaseFileName = *NewFileName;
|
||
|
||
//
|
||
// Now check for an alternate data streams.
|
||
//
|
||
|
||
LastCharacter = &NewFileName->Buffer[0];
|
||
|
||
//
|
||
// Scan forwards to the end of the string looking for either
|
||
// a ":" character or the end of the string.
|
||
//
|
||
|
||
while ( (LastCharacter < &NewFileName->Buffer[(NewFileName->Length / sizeof(WCHAR))]) &&
|
||
(*LastCharacter != L':')) {
|
||
LastCharacter += 1;
|
||
}
|
||
|
||
//
|
||
// We've found an alternate data stream. Deal with it.
|
||
//
|
||
|
||
if (LastCharacter < &NewFileName->Buffer[(NewFileName->Length / sizeof(WCHAR))]) {
|
||
|
||
UNICODE_STRING StreamName;
|
||
|
||
StreamName.Buffer = LastCharacter+1;
|
||
StreamName.Length = (((NewFileName->Length / sizeof(WCHAR)) - (LastCharacter - NewFileName->Buffer)) * sizeof(WCHAR)) - sizeof(WCHAR);
|
||
|
||
//
|
||
// If this is the data alternate data stream, then we don't
|
||
// want to return a base file name, we want to handle
|
||
// this as a normal open.
|
||
//
|
||
|
||
if (RtlEqualUnicodeString(&StreamName, &RdrDataText, TRUE)) {
|
||
|
||
BaseFileName->Length = 0;
|
||
BaseFileName->Buffer = NULL;
|
||
|
||
//
|
||
// Shorten the new filename length by the length of
|
||
// the alternate data stream (thus ignoring it).
|
||
//
|
||
|
||
NewFileName->Length = ((LastCharacter - NewFileName->Buffer) * sizeof(WCHAR));
|
||
} else {
|
||
BaseFileName->Length = ((LastCharacter - NewFileName->Buffer) * sizeof(WCHAR));
|
||
|
||
}
|
||
} else {
|
||
//
|
||
// There was no alternate data stream specified, thus ther
|
||
// is no base file name.
|
||
//
|
||
|
||
BaseFileName->Length = 0;
|
||
BaseFileName->Buffer = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(WildCardsFound)) {
|
||
*WildCardsFound = WildName;
|
||
}
|
||
|
||
try_exit:NOTHING;
|
||
} finally {
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
if (NewFileName->Buffer != NULL) {
|
||
FREE_POOL(NewFileName->Buffer);
|
||
NewFileName->Buffer = NULL;
|
||
NewFileName->Length = 0;
|
||
}
|
||
}
|
||
|
||
if (NameBuffer != NULL) {
|
||
FREE_POOL(NameBuffer);
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
ULONG
|
||
RdrNumberOfComponents(
|
||
IN PUNICODE_STRING String
|
||
)
|
||
{
|
||
ULONG i;
|
||
ULONG NumberOfComponents = 0;
|
||
|
||
PAGED_CODE();
|
||
|
||
for (i = 0; i < String->Length / sizeof(WCHAR); i++) {
|
||
if (String->Buffer[i] == OBJ_NAME_PATH_SEPARATOR) {
|
||
NumberOfComponents += 1;
|
||
}
|
||
}
|
||
|
||
return NumberOfComponents;
|
||
}
|
||
|
||
NTSTATUS
|
||
RdrCanonicalizeAndCopyShare (
|
||
OUT PVOID *SmbContents,
|
||
IN PUNICODE_STRING ServerName,
|
||
IN PUNICODE_STRING ShareName,
|
||
IN PSERVERLISTENTRY Server
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine canonicalizes and copies the specified string.
|
||
|
||
Arguments:
|
||
|
||
OUT PVOID *SmbContents - Supplies a pointer to the SMB to fill in.
|
||
IN PUNICODE_STRING ServerName - Supplies the connection name to copy.
|
||
IN PUNICODE_STRING ShareName
|
||
IN PSERVERLISTENTRY Server - Supplies the server (and thus the
|
||
canonicalization rules) for the destination.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT i;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (Server->Capabilities & DF_UNICODE) {
|
||
PWSTR NameString = ALIGN_SMB_WSTR(*SmbContents);
|
||
PWSTR PathPtr;
|
||
|
||
*((PWCH)NameString)++ = OBJ_NAME_PATH_SEPARATOR; // Initialize buffer to "\"
|
||
*((PWCH)NameString)++ = OBJ_NAME_PATH_SEPARATOR; // Initialize buffer to "\\"
|
||
|
||
PathPtr = ServerName->Buffer;
|
||
|
||
for (i=0; i < (USHORT)(ServerName->Length/sizeof(WCHAR)) ; i++) {
|
||
register WCHAR ch = *PathPtr++;
|
||
|
||
#ifdef MULTIPLE_VCS_PER_SERVER
|
||
if (ch == L'+') {
|
||
break;
|
||
}
|
||
#endif
|
||
|
||
*((PWCH)NameString)++ = RtlUpcaseUnicodeChar(ch);
|
||
}
|
||
|
||
*((PWCH)NameString)++ = OBJ_NAME_PATH_SEPARATOR; // Tack a "\" at the end of the buffer
|
||
|
||
PathPtr = ShareName->Buffer;
|
||
|
||
for (i=0; i < (USHORT)(ShareName->Length/sizeof(WCHAR)) ; i++) {
|
||
register WCHAR ch = *PathPtr++;
|
||
*((PWCH)NameString)++ = RtlUpcaseUnicodeChar(ch);
|
||
}
|
||
|
||
*((PWCH)NameString)++ = UNICODE_NULL;
|
||
|
||
*SmbContents = NameString;
|
||
|
||
} else {
|
||
PSZ NameString = *SmbContents;
|
||
OEM_STRING ServerNameA;
|
||
OEM_STRING ShareNameA;
|
||
|
||
Status = RtlUpcaseUnicodeStringToOemString(&ServerNameA, ServerName, TRUE);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
if( ServerNameA.Length > MAX_PATH ) {
|
||
|
||
RtlFreeOemString( &ServerNameA );
|
||
|
||
return STATUS_BAD_NETWORK_PATH;
|
||
}
|
||
|
||
Status = RtlUpcaseUnicodeStringToOemString(&ShareNameA, ShareName, TRUE);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
RtlFreeOemString(&ServerNameA);
|
||
|
||
return Status;
|
||
}
|
||
|
||
if (ShareNameA.Length > LM20_NNLEN) {
|
||
|
||
RtlFreeOemString(&ServerNameA);
|
||
|
||
RtlFreeOemString(&ShareNameA);
|
||
|
||
return STATUS_BAD_NETWORK_NAME;
|
||
}
|
||
|
||
*NameString++ = OBJ_NAME_PATH_SEPARATOR; // Initialize buffer to "\"
|
||
*NameString++ = OBJ_NAME_PATH_SEPARATOR; // Initialize buffer to "\\"
|
||
|
||
RtlCopyMemory(NameString, ServerNameA.Buffer, ServerNameA.Length);
|
||
|
||
NameString += ServerNameA.Length;
|
||
|
||
*NameString++ = OBJ_NAME_PATH_SEPARATOR; // Tack a "\" at the end of the buffer
|
||
|
||
RtlCopyMemory(NameString, ShareNameA.Buffer, ShareNameA.Length);
|
||
|
||
NameString += ShareNameA.Length;
|
||
|
||
*NameString++ = '\0';
|
||
|
||
*SmbContents = NameString;
|
||
|
||
RtlFreeOemString(&ServerNameA);
|
||
|
||
RtlFreeOemString(&ShareNameA);
|
||
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
RdrCopyUnicodeStringToUnicode (
|
||
OUT PVOID *Destination,
|
||
IN PUNICODE_STRING Source,
|
||
IN BOOLEAN AdjustPointer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
This routine copies the specified source string onto the destination
|
||
asciiz string.
|
||
|
||
Arguments:
|
||
|
||
OUT PUCHAR Destination, - Supplies the destination asciiz string.
|
||
IN PSTRING String - Supplies the source string.
|
||
IN BOOLEAN AdjustPointer - If TRUE, increment destination pointer
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
RtlCopyMemory((*Destination), (Source)->Buffer, (Source)->Length);
|
||
if (AdjustPointer) {
|
||
((PCHAR)(*Destination)) += ((Source)->Length);
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
RdrCopyUnicodeStringToAscii (
|
||
OUT PUCHAR *Destination,
|
||
IN PUNICODE_STRING Source,
|
||
IN BOOLEAN AdjustPointer,
|
||
IN USHORT MaxLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine copies the specified source string onto the destination
|
||
asciiz string.
|
||
|
||
Arguments:
|
||
|
||
OUT PUCHAR Destination, - Supplies the destination asciiz string.
|
||
IN PUNICODE_STRING String - Supplies the source string.
|
||
IN BOOLEAN AdjustPointer - If TRUE, increment destination pointer
|
||
|
||
Return Value:
|
||
|
||
Status of conversion.
|
||
--*/
|
||
{
|
||
OEM_STRING DestinationString;
|
||
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
DestinationString.Buffer = (*Destination);
|
||
|
||
DestinationString.MaximumLength = (USHORT)(MaxLength);
|
||
|
||
Status = RtlUnicodeStringToOemString(&DestinationString, (Source), FALSE);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
if (AdjustPointer) {
|
||
(*Destination) += DestinationString.Length;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
VOID
|
||
RdrExtractNextComponentName (
|
||
OUT PUNICODE_STRING ServerName,
|
||
IN PUNICODE_STRING ConnectionName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine extracts a the "next" component from a path string.
|
||
|
||
Arguments:
|
||
|
||
OUT PUNICODE_STRING ServerName - Returns a pointer to the server component of str
|
||
IN PUNICODE_STRING ConnectionName - Supplies a pointer to a connection/share
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
register USHORT i; // Index into ServerName string.
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Initialize string containing server name to point to
|
||
// servername portion of file to be created.
|
||
//
|
||
|
||
RtlInitUnicodeString(ServerName, NULL);
|
||
|
||
if (ConnectionName->Length == 0) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Initialize the extracted name to the name passed in.
|
||
//
|
||
|
||
*ServerName = *ConnectionName;
|
||
|
||
//
|
||
// If the input name starts with "\", skip over it.
|
||
//
|
||
|
||
if (ServerName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) {
|
||
ServerName->Buffer += 1;
|
||
ServerName->Length -= sizeof(WCHAR);
|
||
}
|
||
|
||
//
|
||
// Scan forward finding the terminal "\" in the server name.
|
||
//
|
||
|
||
for (i=0;i<(USHORT)(ServerName->Length/sizeof(WCHAR));i++) {
|
||
if (ServerName->Buffer[i] == OBJ_NAME_PATH_SEPARATOR) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Update the length and maximum length of the structure
|
||
// to match the new length.
|
||
//
|
||
|
||
ServerName->Length = ServerName->MaximumLength = (USHORT)(i*sizeof(WCHAR));
|
||
}
|
||
|
||
NTSTATUS
|
||
RdrExtractPathAndFileName (
|
||
IN PUNICODE_STRING EntryPath,
|
||
OUT PUNICODE_STRING PathString,
|
||
OUT PUNICODE_STRING FileName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine cracks the entry path into two pieces, the path and the file
|
||
name component at the start of the name. Please note that this routine
|
||
preserves the trailing "\" at the end of the Path string.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PUNICODE_STRING EntryPath - Supplies the path to disect.
|
||
OUT PUNICODE_STRING PathString - Returns the directory containing the file.
|
||
OUT PUNICODE_STRING FileName - Returns the file name specified.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - SUCCESS
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
UNICODE_STRING Component;
|
||
UNICODE_STRING FilePath = *EntryPath;
|
||
USHORT leadingSlashLength;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// If the input string is empty, return empty output strings.
|
||
//
|
||
|
||
if (FilePath.Length == 0) {
|
||
*PathString = *FileName = FilePath;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Scan through the current file name to find the entire path
|
||
// up to (but not including) the last component in the path.
|
||
//
|
||
|
||
do {
|
||
|
||
//
|
||
// Remember whether the input string starts with a "\".
|
||
//
|
||
|
||
leadingSlashLength = (FilePath.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) ? sizeof(WCHAR) : 0;
|
||
|
||
//
|
||
// Extract the next component from the name.
|
||
//
|
||
|
||
RdrExtractNextComponentName(&Component, &FilePath);
|
||
|
||
dprintf(DPRT_FILEINFO, ("Component: %wZ, Name:%wZ ", &Component, &FilePath));
|
||
|
||
//
|
||
// Bump the "remaining name" pointer by the length of this
|
||
// component
|
||
//
|
||
|
||
if (Component.Length != 0) {
|
||
|
||
FilePath.Length -= Component.Length + leadingSlashLength;
|
||
FilePath.MaximumLength -= Component.MaximumLength + leadingSlashLength;
|
||
FilePath.Buffer += (Component.Length + leadingSlashLength)/sizeof(WCHAR);
|
||
|
||
*FileName = Component;
|
||
}
|
||
|
||
dprintf(DPRT_FILEINFO, ("Last Component: %wZ\n", FileName));
|
||
|
||
} while ((Component.Length != 0) && (FilePath.Length != 0));
|
||
|
||
//
|
||
// Take the FCB's name, subtract the last component of the name
|
||
// and concatinate the current path with the new path.
|
||
//
|
||
|
||
*PathString = *EntryPath;
|
||
|
||
//
|
||
// Set the path's name based on the original name, subtracting
|
||
// the length of the name portion (including the "\")
|
||
//
|
||
PathString->Length -= FileName->Length + leadingSlashLength;
|
||
PathString->MaximumLength -= FileName->Length + leadingSlashLength;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
RdrExtractServerShareAndPath (
|
||
IN PUNICODE_STRING BaseName,
|
||
OUT PUNICODE_STRING ServerName,
|
||
OUT PUNICODE_STRING ShareName,
|
||
OUT PUNICODE_STRING PathName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine extracts the relevant portions from BaseName to extract
|
||
the components of the user's string.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PUNICODE_STRING BaseName - Supplies the base user's path
|
||
OUT PUNICODE_STRING ServerName - Supplies a string to hold the remote server name
|
||
OUT PUNICODE_STRING ShareName - Supplies a string to hold the remote share name
|
||
OUT PUNICODE_STRING PathName - Supplies a string to hold the remaining part of the
|
||
path
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Status of operation
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
UNICODE_STRING BaseCopy = *BaseName;
|
||
|
||
PAGED_CODE();
|
||
|
||
RdrExtractNextComponentName(ServerName, &BaseCopy);
|
||
|
||
//
|
||
// If the first component of the file name is a drive specifier (X:),
|
||
// skip over it.
|
||
//
|
||
|
||
|
||
if (RdrIsDiskOrPrintType(ServerName->Buffer, ServerName->Length)) {
|
||
|
||
BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
|
||
BaseCopy.Length -= ServerName->Length+sizeof(WCHAR);
|
||
BaseCopy.MaximumLength -= ServerName->MaximumLength+sizeof(WCHAR);
|
||
|
||
RdrExtractNextComponentName(ServerName, &BaseCopy);
|
||
|
||
}
|
||
|
||
|
||
if (ServerName->Length == 0) {
|
||
|
||
//
|
||
// If there's anything left to the name, blow this call off - it
|
||
// didn't work.
|
||
//
|
||
|
||
if (BaseCopy.Length != 0) {
|
||
return STATUS_OBJECT_NAME_INVALID;
|
||
}
|
||
|
||
ShareName->Length = 0;
|
||
ShareName->MaximumLength = 0;
|
||
|
||
PathName->Length = 0;
|
||
PathName->MaximumLength = 0;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// We bump the pointer by Length+1 to account for the "\" we skipped over.
|
||
//
|
||
|
||
BaseCopy.Buffer += (ServerName->Length/sizeof(WCHAR))+1;
|
||
BaseCopy.Length -= ServerName->Length+sizeof(WCHAR);
|
||
BaseCopy.MaximumLength -= ServerName->MaximumLength+sizeof(WCHAR);
|
||
|
||
RdrExtractNextComponentName(ShareName, &BaseCopy);
|
||
|
||
if (ShareName->Length == 0) {
|
||
PathName->Length = 0;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if (FsRtlDoesNameContainWildCards(ShareName)) {
|
||
|
||
return STATUS_OBJECT_NAME_INVALID;
|
||
}
|
||
|
||
//if (!FsRtlIsUnicodeNameValid(*ShareName, FALSE, NULL)) {
|
||
// return STATUS_OBJECT_NAME_INVALID;
|
||
//}
|
||
|
||
//
|
||
// We bump the pointer by Length+1 to account for the "\" we skipped over.
|
||
//
|
||
|
||
BaseCopy.Buffer += (ShareName->Length/sizeof(WCHAR))+1;
|
||
BaseCopy.Length -= ShareName->Length+sizeof(WCHAR);
|
||
BaseCopy.MaximumLength -= ShareName->MaximumLength+sizeof(WCHAR);
|
||
|
||
*PathName = BaseCopy;
|
||
|
||
if (PathName->Length == 0) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//if (!FsRtlIsUnicodePathValid(BaseCopy, TRUE/*\*/, FALSE/***/, NULL)) {
|
||
// return STATUS_OBJECT_NAME_INVALID;
|
||
//}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
LARGE_INTEGER
|
||
RdrConvertSmbTimeToTime (
|
||
IN SMB_TIME Time,
|
||
IN SMB_DATE Date,
|
||
IN PSERVERLISTENTRY Server OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine converts an SMB time to an NT time structure.
|
||
|
||
Arguments:
|
||
|
||
IN SMB_TIME Time - Supplies the time of day to convert
|
||
IN SMB_DATE Date - Supplies the day of the year to convert
|
||
IN PSERVERLISTENTRY Server - if supplied, supplies the server for tz bias.
|
||
|
||
Return Value:
|
||
|
||
LARGE_INTEGER - Time structure describing input time.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
TIME_FIELDS TimeFields;
|
||
LARGE_INTEGER OutputTime;
|
||
|
||
//
|
||
// This routine cannot be paged because it is called from both the
|
||
// RdrFileDiscardableSection and the RdrVCDiscardableSection.
|
||
//
|
||
|
||
if (SmbIsTimeZero(&Date) && SmbIsTimeZero(&Time)) {
|
||
OutputTime.LowPart = OutputTime.HighPart = 0;
|
||
} else {
|
||
TimeFields.Year = Date.Struct.Year + (USHORT )1980;
|
||
TimeFields.Month = Date.Struct.Month;
|
||
TimeFields.Day = Date.Struct.Day;
|
||
|
||
TimeFields.Hour = Time.Struct.Hours;
|
||
TimeFields.Minute = Time.Struct.Minutes;
|
||
TimeFields.Second = Time.Struct.TwoSeconds*(USHORT )2;
|
||
TimeFields.Milliseconds = 0;
|
||
|
||
//
|
||
// Make sure that the times specified in the SMB are reasonable
|
||
// before converting them.
|
||
//
|
||
|
||
if (TimeFields.Year < 1601) {
|
||
TimeFields.Year = 1601;
|
||
}
|
||
|
||
if (TimeFields.Month > 12) {
|
||
TimeFields.Month = 12;
|
||
}
|
||
|
||
if (TimeFields.Hour >= 24) {
|
||
TimeFields.Hour = 23;
|
||
}
|
||
if (TimeFields.Minute >= 60) {
|
||
TimeFields.Minute = 59;
|
||
}
|
||
if (TimeFields.Second >= 60) {
|
||
TimeFields.Second = 59;
|
||
|
||
}
|
||
|
||
if (!RtlTimeFieldsToTime(&TimeFields, &OutputTime)) {
|
||
OutputTime.HighPart = 0;
|
||
OutputTime.LowPart = 0;
|
||
|
||
return OutputTime;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(Server)) {
|
||
OutputTime.QuadPart = OutputTime.QuadPart + Server->TimeZoneBias.QuadPart;
|
||
}
|
||
|
||
ExLocalTimeToSystemTime(&OutputTime, &OutputTime);
|
||
|
||
}
|
||
|
||
return OutputTime;
|
||
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
RdrConvertTimeToSmbTime (
|
||
IN PLARGE_INTEGER InputTime,
|
||
IN PSERVERLISTENTRY Server OPTIONAL,
|
||
OUT PSMB_TIME Time,
|
||
OUT PSMB_DATE Date
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine converts an NT time structure to an SMB time.
|
||
|
||
Arguments:
|
||
|
||
IN LARGE_INTEGER InputTime - Supplies the time to convert.
|
||
OUT PSMB_TIME Time - Returns the converted time of day.
|
||
OUT PSMB_DATE Date - Returns the converted day of the year.
|
||
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if input time could be converted.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
TIME_FIELDS TimeFields;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (InputTime->LowPart == 0 && InputTime->HighPart == 0) {
|
||
Time->Ushort = Date->Ushort = 0;
|
||
} else {
|
||
LARGE_INTEGER LocalTime;
|
||
|
||
ExSystemTimeToLocalTime(InputTime, &LocalTime);
|
||
|
||
if (ARGUMENT_PRESENT(Server)) {
|
||
LocalTime.QuadPart -= Server->TimeZoneBias.QuadPart;
|
||
}
|
||
|
||
RtlTimeToTimeFields(&LocalTime, &TimeFields);
|
||
|
||
if (TimeFields.Year < 1980) {
|
||
return FALSE;
|
||
}
|
||
|
||
Date->Struct.Year = (USHORT )(TimeFields.Year - 1980);
|
||
Date->Struct.Month = TimeFields.Month;
|
||
Date->Struct.Day = TimeFields.Day;
|
||
|
||
Time->Struct.Hours = TimeFields.Hour;
|
||
Time->Struct.Minutes = TimeFields.Minute;
|
||
|
||
//
|
||
// When converting from a higher granularity time to a lesser
|
||
// granularity time (seconds to 2 seconds), always round up
|
||
// the time, don't round down.
|
||
//
|
||
|
||
Time->Struct.TwoSeconds = (TimeFields.Second + (USHORT)1) / (USHORT )2;
|
||
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
RdrTimeToSecondsSince1970 (
|
||
IN PLARGE_INTEGER CurrentTime,
|
||
IN PSERVERLISTENTRY Server OPTIONAL,
|
||
OUT PULONG SecondsSince1970
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the CurrentTime in UTC and returns the
|
||
equivalent current time in the servers timezone.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PLARGE_INTEGER CurrentTime - Supplies the current system time in UTC.
|
||
|
||
IN PSERVERLISTENTRY Server - Supplies the difference in timezones between
|
||
the server and the workstation. If not supplied
|
||
then the assumption is that they are in the
|
||
same timezone.
|
||
|
||
OUT PULONG SecondsSince1970 - Returns the # of seconds since 1970 in
|
||
the servers timezone or MAXULONG if conversion
|
||
fails.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the time could be converted.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
LARGE_INTEGER ServerTime;
|
||
LARGE_INTEGER TempTime;
|
||
BOOLEAN ReturnValue;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (ARGUMENT_PRESENT(Server)) {
|
||
|
||
TempTime.QuadPart = (*CurrentTime).QuadPart - Server->TimeZoneBias.QuadPart;
|
||
|
||
ExSystemTimeToLocalTime(&TempTime, &ServerTime);
|
||
|
||
} else {
|
||
|
||
ExSystemTimeToLocalTime(CurrentTime, &ServerTime);
|
||
|
||
}
|
||
|
||
ReturnValue = RtlTimeToSecondsSince1970(&ServerTime, SecondsSince1970);
|
||
|
||
if ( ReturnValue == FALSE ) {
|
||
//
|
||
// We can't represent the time legally, peg it at
|
||
// the max legal time.
|
||
//
|
||
|
||
*SecondsSince1970 = MAXULONG;
|
||
}
|
||
|
||
return ReturnValue;
|
||
}
|
||
|
||
VOID
|
||
RdrSecondsSince1970ToTime (
|
||
IN ULONG SecondsSince1970,
|
||
IN PSERVERLISTENTRY Server OPTIONAL,
|
||
OUT PLARGE_INTEGER CurrentTime
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the Local system time derived from a time
|
||
in seconds in the servers timezone.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN ULONG SecondsSince1970 - Supplies the # of seconds since 1970 in
|
||
servers timezone.
|
||
|
||
IN PSERVERLISTENTRY Server - Supplies the difference in timezones between
|
||
the server and the workstation. If not supplied
|
||
then the assumption is that they are in the
|
||
same timezone.
|
||
|
||
OUT PLARGE_INTEGER CurrentTime - Returns the current system time in UTC.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
LARGE_INTEGER LocalTime;
|
||
|
||
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
||
|
||
RtlSecondsSince1970ToTime (SecondsSince1970, &LocalTime);
|
||
|
||
ExLocalTimeToSystemTime(&LocalTime, CurrentTime);
|
||
|
||
if (ARGUMENT_PRESENT(Server)) {
|
||
(*CurrentTime).QuadPart = (*CurrentTime).QuadPart + Server->TimeZoneBias.QuadPart;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
BOOLEAN
|
||
RdrCanFileBeBuffered (
|
||
IN PICB Icb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns TRUE iff a specified instance of the file can be
|
||
buffered.
|
||
|
||
A file can be buffered if either no other processes can have the file
|
||
opened, or if the file is oplocked.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PICB Icb - Supplies an instance control block for the open file to check.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the file can be buffered, FALSE otherwise.
|
||
|
||
|
||
Note:
|
||
This should probably be moved inline....
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if (Icb->Type == DiskFile) {
|
||
|
||
//
|
||
// If this ICB is pseudo opened, but the FCB in question is
|
||
// oplocked by another instance, and there is still a valid handle
|
||
// to that oplocked file, then we can safely buffer this request,
|
||
// since we know noone else has the file opened, even though this
|
||
// particular instance isn't opened.
|
||
//
|
||
|
||
if ((Icb->Flags & ICB_PSEUDOOPENED) &&
|
||
(Icb->NonPagedFcb->Flags & FCB_OPLOCKED) &&
|
||
(Icb->NonPagedFcb->Flags & FCB_HASOPLOCKHANDLE)) {
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
//
|
||
// If we have exclusive access to the file, or if we have an
|
||
// oplock, and this is not a loopback connection, then we can
|
||
// buffer this file.
|
||
//
|
||
|
||
if (((Icb->u.f.Flags & ICBF_OPENEDEXCLUSIVE) ||
|
||
(Icb->u.f.Flags & ICBF_OPLOCKED)) &&
|
||
!Icb->Fcb->Connection->Server->IsLoopback) {
|
||
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// If the user will let us, we will buffer readonly files.
|
||
// Note that we will buffer readonly files even on loopback
|
||
// connections.
|
||
//
|
||
|
||
if ((Icb->Fcb->Attribute & FILE_ATTRIBUTE_READONLY) &&
|
||
RdrData.BufferReadOnlyFiles) {
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
ULONG
|
||
RdrMapSmbAttributes (
|
||
IN USHORT SmbAttribs
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine maps an SMB (DOS/OS2) file attribute into an NT
|
||
file attribute.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN USHORT SmbAttribs - Supplies the SMB attribute to map.
|
||
|
||
|
||
Return Value:
|
||
|
||
ULONG - NT Attribute mapping SMB attribute
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Attributes = 0;
|
||
|
||
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
||
|
||
if (SmbAttribs==0) {
|
||
Attributes = FILE_ATTRIBUTE_NORMAL;
|
||
} else {
|
||
|
||
ASSERT (SMB_FILE_ATTRIBUTE_READONLY == FILE_ATTRIBUTE_READONLY);
|
||
ASSERT (SMB_FILE_ATTRIBUTE_HIDDEN == FILE_ATTRIBUTE_HIDDEN);
|
||
ASSERT (SMB_FILE_ATTRIBUTE_SYSTEM == FILE_ATTRIBUTE_SYSTEM);
|
||
ASSERT (SMB_FILE_ATTRIBUTE_ARCHIVE == FILE_ATTRIBUTE_ARCHIVE);
|
||
ASSERT (SMB_FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY);
|
||
|
||
Attributes = SmbAttribs & FILE_ATTRIBUTE_VALID_FLAGS;
|
||
}
|
||
return Attributes;
|
||
}
|
||
|
||
USHORT
|
||
RdrMapDisposition (
|
||
IN ULONG Disposition
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes an NT disposition, and maps it into an OS/2
|
||
CreateAction to be put into an SMB.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN ULONG Disposition - Supplies the NT disposition to map.
|
||
|
||
|
||
Return Value:
|
||
|
||
USHORT - OS/2 Access mapping that maps NT access
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
switch (Disposition) {
|
||
case FILE_OVERWRITE_IF:
|
||
case FILE_SUPERSEDE:
|
||
return SMB_OFUN_OPEN_TRUNCATE | SMB_OFUN_CREATE_CREATE;
|
||
break;
|
||
|
||
case FILE_CREATE:
|
||
return SMB_OFUN_OPEN_FAIL | SMB_OFUN_CREATE_CREATE;
|
||
break;
|
||
|
||
case FILE_OVERWRITE:
|
||
return SMB_OFUN_OPEN_TRUNCATE | SMB_OFUN_CREATE_FAIL;
|
||
break;
|
||
|
||
case FILE_OPEN:
|
||
return SMB_OFUN_OPEN_OPEN | SMB_OFUN_CREATE_FAIL;
|
||
break;
|
||
|
||
case FILE_OPEN_IF:
|
||
return SMB_OFUN_OPEN_OPEN | SMB_OFUN_CREATE_CREATE;
|
||
break;
|
||
|
||
default:
|
||
InternalError(("Unknown disposition passed to RdrMapDisposition"));
|
||
RdrInternalError(EVENT_RDR_DISPOSITION);
|
||
return 0;
|
||
break;
|
||
}
|
||
}
|
||
ULONG
|
||
RdrUnmapDisposition (
|
||
IN USHORT SmbDisposition
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes an OS/2 disposition and maps it into an NT
|
||
disposition.
|
||
|
||
Arguments:
|
||
|
||
IN USHORT SmbDisposition - Supplies the OS/2 disposition to map.
|
||
|
||
Return Value:
|
||
|
||
ULONG - NT disposition mapping OS/2 disposition
|
||
|
||
--*/
|
||
|
||
{
|
||
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
||
|
||
//
|
||
// Mask off oplocked bit.
|
||
//
|
||
|
||
switch (SmbDisposition & 0x7fff) {
|
||
|
||
case SMB_OACT_OPENED:
|
||
return FILE_OPENED;
|
||
break;
|
||
|
||
case SMB_OACT_CREATED:
|
||
return FILE_CREATED;
|
||
break;
|
||
|
||
case SMB_OACT_TRUNCATED:
|
||
return FILE_OVERWRITTEN;
|
||
break;
|
||
}
|
||
|
||
ASSERT(FALSE);
|
||
return 0;
|
||
}
|
||
|
||
|
||
USHORT
|
||
RdrMapDesiredAccess (
|
||
IN ULONG DesiredAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes an NT DesiredAccess value and converts it
|
||
to an OS/2 access mode.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN ULONG DesiredAccess - Supplies the NT desired access to map.
|
||
|
||
Return Value:
|
||
|
||
USHORT - The mapped OS/2 access mode that compares to the NT code
|
||
specified. If there is no mapping for the NT code, we return
|
||
-1 as the access mode.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// If the user asked for both read and write access, return read/write.
|
||
//
|
||
|
||
if ((DesiredAccess & FILE_READ_DATA)&&(DesiredAccess & FILE_WRITE_DATA)) {
|
||
return SMB_DA_ACCESS_READ_WRITE;
|
||
}
|
||
|
||
//
|
||
// If the user requested WRITE_DATA, return write.
|
||
//
|
||
|
||
if (DesiredAccess & FILE_WRITE_DATA) {
|
||
return SMB_DA_ACCESS_WRITE;
|
||
}
|
||
|
||
//
|
||
// If the user requested READ_DATA, return read.
|
||
//
|
||
if (DesiredAccess & FILE_READ_DATA) {
|
||
return SMB_DA_ACCESS_READ;
|
||
}
|
||
|
||
//
|
||
// If the user requested ONLY execute access, then request execute
|
||
// access. Execute access is the "weakest" of the possible desired
|
||
// accesses, so it takes least precedence.
|
||
//
|
||
|
||
if (DesiredAccess & FILE_EXECUTE) {
|
||
return SMB_DA_ACCESS_EXECUTE;
|
||
}
|
||
|
||
//
|
||
// If we couldn't figure out what we were doing, use read mode
|
||
//
|
||
// Among the attributes that we do not map are:
|
||
//
|
||
// FILE_READ_ATTRIBUTES
|
||
// FILE_WRITE_ATTRIBUTES
|
||
// FILE_READ_EAS
|
||
// FILE_WRITE_EAS
|
||
//
|
||
|
||
// dprintf(DPRT_ERROR, ("Could not map DesiredAccess of %08lx\n", DesiredAccess));
|
||
|
||
return (USHORT)-1;
|
||
}
|
||
|
||
USHORT
|
||
RdrMapShareAccess (
|
||
IN USHORT ShareAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes an NT ShareAccess value and converts it to an
|
||
OS/2 sharing mode.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN USHORT ShareAccess - Supplies the OS/2 share access to map.
|
||
|
||
Return Value:
|
||
|
||
USHORT - The mapped OS/2 sharing mode that compares to the NT code
|
||
specified
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT ShareMode = SMB_DA_SHARE_EXCLUSIVE;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ((ShareAccess & (FILE_SHARE_READ | FILE_SHARE_WRITE)) ==
|
||
(FILE_SHARE_READ | FILE_SHARE_WRITE)) {
|
||
ShareMode = SMB_DA_SHARE_DENY_NONE;
|
||
} else if (ShareAccess & FILE_SHARE_READ) {
|
||
ShareMode = SMB_DA_SHARE_DENY_WRITE;
|
||
} else if (ShareAccess & FILE_SHARE_WRITE) {
|
||
ShareMode = SMB_DA_SHARE_DENY_READ;
|
||
}
|
||
|
||
// else if (ShareAccess & FILE_SHARE_DELETE) {
|
||
// InternalError(("Support for FILE_SHARE_DELETE NYI\n"));
|
||
// }
|
||
|
||
return ShareMode;
|
||
|
||
}
|
||
|
||
USHORT
|
||
RdrMapFileAttributes (
|
||
IN ULONG FileAttributes
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes an NT file attribute mapping and converts it into
|
||
an OS/2 file attribute definition.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN ULONG FileAttributes - Supplies the file attributes to map.
|
||
|
||
|
||
Return Value:
|
||
|
||
USHORT
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT ResultingAttributes = 0;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (FileAttributes==FILE_ATTRIBUTE_NORMAL) {
|
||
return ResultingAttributes;
|
||
}
|
||
|
||
if (FileAttributes & FILE_ATTRIBUTE_READONLY) {
|
||
ResultingAttributes |= SMB_FILE_ATTRIBUTE_READONLY;
|
||
}
|
||
|
||
if (FileAttributes & FILE_ATTRIBUTE_HIDDEN) {
|
||
ResultingAttributes |= SMB_FILE_ATTRIBUTE_HIDDEN;
|
||
}
|
||
|
||
if (FileAttributes & FILE_ATTRIBUTE_SYSTEM) {
|
||
ResultingAttributes |= SMB_FILE_ATTRIBUTE_SYSTEM;
|
||
}
|
||
if (FileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
|
||
ResultingAttributes |= SMB_FILE_ATTRIBUTE_ARCHIVE;
|
||
}
|
||
return ResultingAttributes;
|
||
}
|
||
|
||
/** RdrPackNtString
|
||
*
|
||
* RdrPackNtString is used to stuff variable-length data, which
|
||
* is pointed to by (surpise!) a pointer. The data is assumed
|
||
* to be a nul-terminated string (ASCIIZ). Repeated calls to
|
||
* this function are used to pack data from an entire structure.
|
||
*
|
||
* Upon first call, the laststring pointer should point to just
|
||
* past the end of the buffer. Data will be copied into the buffer from
|
||
* the end, working towards the beginning. If a data item cannot
|
||
* fit, the pointer will be set to NULL, else the pointer will be
|
||
* set to the new data location.
|
||
*
|
||
* Pointers which are passed in as NULL will be set to be pointer
|
||
* to and empty string, as the NULL-pointer is reserved for
|
||
* data which could not fit as opposed to data not available.
|
||
*
|
||
* Returns: 0 if could not fit data into buffer
|
||
* else size of data stuffed (guaranteed non-zero)
|
||
*
|
||
* See the test case for sample usage. (tst/packtest.c)
|
||
*/
|
||
|
||
ULONG
|
||
RdrPackNtString(
|
||
PUNICODE_STRING string,
|
||
ULONG BufferDisplacement,
|
||
PCHAR dataend,
|
||
PCHAR * laststring
|
||
)
|
||
{
|
||
LONG size;
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_FSCTL, ("RdrPackString:\n"));
|
||
dprintf(DPRT_FSCTL, (" string=%Fp, *string=%Fp, **string=\"%Fs\"\n",
|
||
string, *string, *string));
|
||
dprintf(DPRT_FSCTL, (" end=%Fp\n", dataend));
|
||
dprintf(DPRT_FSCTL, (" last=%Fp, *last=%Fp, **last=\"%Fs\"\n",
|
||
laststring, *laststring, *laststring));
|
||
|
||
ASSERT (dataend <= *laststring);
|
||
|
||
//
|
||
// is there room for the string?
|
||
//
|
||
|
||
size = string->Length;
|
||
|
||
if ((*laststring - dataend) < size) {
|
||
string->Length = 0;
|
||
return(0);
|
||
}
|
||
|
||
if( size == 0 ) {
|
||
|
||
//
|
||
// A NULL string! If there is room, put a NULL in the
|
||
// output buffer.
|
||
//
|
||
|
||
if( (*laststring - dataend) < sizeof( WCHAR ) ) {
|
||
//
|
||
// There is no room.
|
||
//
|
||
return( 0 );
|
||
|
||
} else {
|
||
//
|
||
// There is room.
|
||
//
|
||
*laststring -= sizeof( WCHAR );
|
||
**laststring = UNICODE_NULL;
|
||
size = sizeof( WCHAR );
|
||
}
|
||
|
||
} else {
|
||
*laststring -= size;
|
||
RtlCopyMemory(*laststring, string->Buffer, size);
|
||
}
|
||
|
||
(PCHAR )(string->Buffer) = *laststring;
|
||
(PCHAR )(string->Buffer) -= BufferDisplacement;
|
||
return(size);
|
||
}
|
||
|
||
ULONG
|
||
RdrPackString(
|
||
IN OUT PCHAR * string, // pointer by reference: string to be copied.
|
||
IN ULONG StringLength, // Length of this string.
|
||
IN ULONG OutputBufferDisplacement, // Amount to subtract from output buffer
|
||
IN PCHAR dataend, // pointer to end of fixed size data.
|
||
IN OUT PCHAR * laststring // pointer by reference: top of string data.
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
RdrPackString is used to stuff variable-length data, which
|
||
is pointed to by (surpise!) a pointer. The data is assumed
|
||
to be a nul-terminated string (ASCIIZ). Repeated calls to
|
||
this function are used to pack data from an entire structure.
|
||
|
||
Upon first call, the laststring pointer should point to just
|
||
past the end of the buffer. Data will be copied into the buffer from
|
||
the end, working towards the beginning. If a data item cannot
|
||
fit, the pointer will be set to NULL, else the pointer will be
|
||
set to the new data location.
|
||
|
||
Pointers which are passed in as NULL will be set to be pointer
|
||
to and empty string, as the NULL-pointer is reserved for
|
||
data which could not fit as opposed to data not available.
|
||
|
||
See the test case for sample usage. (tst/packtest.c)
|
||
|
||
|
||
Arguments:
|
||
|
||
string - pointer by reference: string to be copied.
|
||
|
||
dataend - pointer to end of fixed size data.
|
||
|
||
laststring - pointer by reference: top of string data.
|
||
|
||
Return Value:
|
||
|
||
0 - if it could not fit data into the buffer. Or...
|
||
|
||
sizeOfData - the size of data stuffed (guaranteed non-zero)
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD size;
|
||
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_FSCTL, ("NetpPackString:\n"));
|
||
dprintf(DPRT_FSCTL, (" string=%lx, *string=%lx, **string='%s'\n",
|
||
string, *string, *string));
|
||
dprintf(DPRT_FSCTL, (" end=%lx\n", dataend));
|
||
dprintf(DPRT_FSCTL, (" last=%lx, *last=%lx, **last='%s'\n",
|
||
laststring, *laststring, *laststring));
|
||
|
||
//
|
||
// convert NULL ptr to pointer to NULL string
|
||
//
|
||
|
||
if (*string == NULL) {
|
||
// BUG 20.1160 - replaced (dataend +1) with dataend
|
||
// to allow for a NULL ptr to be packed
|
||
// (as a NULL string) with one byte left in the
|
||
// buffer. - ERICPE
|
||
//
|
||
|
||
if ( *laststring > dataend ) {
|
||
*(--(*laststring)) = 0;
|
||
*string = *laststring;
|
||
*string -=OutputBufferDisplacement;
|
||
return 1;
|
||
} else {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// is there room for the string?
|
||
//
|
||
|
||
size = StringLength + sizeof(TCHAR);
|
||
|
||
if ( ((DWORD)(*laststring - dataend)) < size) {
|
||
*string = NULL;
|
||
return(0);
|
||
} else {
|
||
*laststring -= size;
|
||
RtlCopyMemory(*laststring, *string, size);
|
||
*string = *laststring;
|
||
(*string)[StringLength] = '\0';
|
||
#ifdef UNICODE
|
||
(*string)[StringLength+1] = '\0';
|
||
#endif
|
||
*string -=OutputBufferDisplacement;
|
||
return(size);
|
||
}
|
||
} // RdrPackString
|
||
|
||
|
||
LONG
|
||
RdrExceptionFilter (
|
||
IN PEXCEPTION_POINTERS ExceptionPointer,
|
||
OUT PNTSTATUS TrueStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to decide if we should or should not handle
|
||
an exception status that is being raised. It inserts the status
|
||
into the Irp and either indicates that we should handle
|
||
the exception or bug check the system.
|
||
|
||
Arguments:
|
||
|
||
ExceptionCode - Supplies the exception code to being checked.
|
||
|
||
Return Value:
|
||
|
||
LONG - returns EXCEPTION_EXECUTE_HANDLER or bugchecks
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (ExceptionCode == STATUS_IN_PAGE_ERROR &&
|
||
ExceptionPointer->ExceptionRecord->NumberParameters >= 3) {
|
||
|
||
ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionInformation[2];
|
||
|
||
|
||
}
|
||
|
||
*TrueStatus = ExceptionCode;
|
||
|
||
if (FsRtlIsNtstatusExpected( ExceptionCode )) {
|
||
|
||
return EXCEPTION_EXECUTE_HANDLER;
|
||
|
||
} else {
|
||
|
||
return EXCEPTION_CONTINUE_SEARCH;
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RdrProcessException (
|
||
IN PIRP Irp,
|
||
IN NTSTATUS ExceptionCode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine process an exception. It either completes the request
|
||
with the saved exception status or it sends the request off to the Fsp
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
ExceptionCode - Supplies the normalized exception status being handled
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Returns the results of either posting the Irp or the
|
||
saved completion status.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_ERROR, ("RdrProcessException: %X\n", ExceptionCode));
|
||
|
||
if (FsRtlIsNtstatusExpected( ExceptionCode )) {
|
||
|
||
RdrCompleteRequest( Irp, ExceptionCode );
|
||
|
||
} else {
|
||
|
||
KeBugCheck( RDR_FILE_SYSTEM );
|
||
}
|
||
|
||
return ExceptionCode;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
RdrIsFileBatch(
|
||
IN PUNICODE_STRING FileName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if the specified file is a batch file or not.
|
||
|
||
Arguments:
|
||
|
||
IN PUNICODE_STRING FileName - Specifies the file name to check.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE iff the file is a batch file.
|
||
|
||
|
||
Note:
|
||
This routine is called at DPC_LEVEL, and thus must not block.
|
||
|
||
--*/
|
||
{
|
||
ULONG i;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (FileName->Length <= 3*sizeof(WCHAR)) {
|
||
return FALSE;
|
||
}
|
||
|
||
if (FileName->Buffer[(FileName->Length / sizeof(WCHAR)) - 4] != L'.') {
|
||
return FALSE;
|
||
}
|
||
|
||
for (i = 0; i < RdrNumberOfBatchExtensions ; i++) {
|
||
if (!_wcsnicmp(RdrBatchExtensionArray[i],
|
||
&FileName->Buffer[(FileName->Length / sizeof(WCHAR)) - 4],
|
||
4)) {
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
#if DBG
|
||
ULONG
|
||
NumEntriesList (
|
||
IN PLIST_ENTRY List
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the number of entries on a list
|
||
.
|
||
Arguments:
|
||
|
||
IN PLIST_ENTRY List - Supplies the list to count.
|
||
|
||
|
||
Return Value:
|
||
|
||
ULONG - Number of entries on the list
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY Entry;
|
||
ULONG Count = 0;
|
||
|
||
for (Entry = List->Flink ; Entry != List ; Entry = Entry->Flink) {
|
||
ASSERT(Entry != Entry->Flink);
|
||
Count += 1;
|
||
}
|
||
|
||
return Count;
|
||
}
|
||
#endif // DBG
|
||
|
||
|
||
#if RDRDBG
|
||
|
||
/*
|
||
* Smb command table.
|
||
* points each smb cmd to it's description
|
||
* plus has an initial pasring setting for the smb buffer
|
||
*/
|
||
#define def 0,0,0
|
||
|
||
struct s_smbcmd rgCmdTable[] = {
|
||
0x00, "mkdir", /*SMBDdir*/NULL, 0, def,
|
||
0x01, "rmdir", /*SMBDdir*/NULL, 0, def,
|
||
0x02, "open", /*SMBDopen*/NULL, 0, def,
|
||
0x03, "create", /*SMBDcreate*/NULL, 0, def,
|
||
0x04, "close", /*SMBDclose*/NULL, 0, def,
|
||
0x05, "flush", /*SMBDhandle*/NULL, 0, def,
|
||
0x06, "unlink", /*SMBDunlink*/NULL, 0, def,
|
||
0x07, "mv", /*SMBDmv*/NULL, 0, def,
|
||
0x08, "getatr", /*SMBDgetattr*/NULL, 0, def,
|
||
0x09, "setatr", /*SMBDsetattr*/NULL, 0, def,
|
||
0x0A, "read", /*SMBDread*/NULL, 0, def,
|
||
0x0B, "write", /*SMBDwrite*/NULL, 0, def,
|
||
0x0C, "lock", /*SMBDlock*/NULL, 0, def,
|
||
0x0D, "unlock", /*SMBDlock*/NULL, 0, def,
|
||
0x0E, "ctemp", /*SMBDctemp*/NULL, 0, def,
|
||
0x0F, "mknew", /*SMBDmknew*/NULL, 0, def,
|
||
0x10, "chkpth", /*SMBDdir*/NULL, 0, def,
|
||
0x11, "exit", /*SMBDempty*/NULL, 0, def,
|
||
0x12, "lseek", /*SMBDlseek*/NULL, 0, def,
|
||
0x13, "lockread", /*SMBDlockread*/NULL, 0, def,
|
||
0x14, "writeunlock", /*SMBDwriteunlock*/NULL, 0, def,
|
||
0x1A, "readBraw", /*SMBDreadBraw*/NULL, 0, def,
|
||
0x1B, "readBmpx", /*SMBDreadBmpx*/NULL, S_BUFFRAW, def,
|
||
0x1C, "readBs", /*SMBDreadBmpx*/NULL, S_BUFFRAW, def,
|
||
0x1D, "writeBraw", /*SMBDwriteBraw*/NULL, S_BUFFRAW, def,
|
||
0x1E, "writeBmpx", /*SMBDwriteBmpx*/NULL, S_BUFFRAW, def,
|
||
0x1F, "writeBs", /*SMBDwriteBs*/NULL, S_BUFFRAW, def,
|
||
0x20, "writeC", /*SMBDwriteBs*/NULL, S_BUFFRAW, def,
|
||
0x21, "qrysrv", /*NULL*/NULL, 0, def,
|
||
0x22, "setattrE", /*SMBDsetattrE*/NULL, 0, def,
|
||
0x23, "getattrE", /*SMBDgetattrE*/NULL, 0, def,
|
||
0x24, "lockingX", /*SMBDlockingX*/NULL, S_ANDX | S_BUFFRAW, def,
|
||
0x25, "trans", /*SMBDtrans*/NULL, S_BUFFRAW, def,
|
||
// 0x26, "transs", /*SMBDtranss*/NULL, S_BUFFRAW, def,
|
||
0x27, "ioctl", /*SMBDioctl*/NULL, S_BUFFRAW, def,
|
||
0x28, "ioctls", /*SMBDioctls*/NULL, S_BUFFRAW, def,
|
||
0x29, "copy", /*SMBDcopy*/NULL, S_NULLSTR, def,
|
||
0x2A, "move", /*SMBDcopy*/NULL, S_NULLSTR, def,
|
||
0x2B, "echo", /*SMBDecho*/NULL, S_BUFFRAW, def,
|
||
0x2C, "writeclose", /*SMBDwriteclose*/NULL, S_BUFFRAW, def,
|
||
0x2D, "openX", /*SMBDopenX*/NULL, S_ANDX | S_NULLSTR, def,
|
||
0x2E, "readX", /*SMBDreadX*/NULL, S_ANDX | S_BUFFRAW, def,
|
||
0x2F, "writeX", /*SMBDwriteX*/NULL, S_ANDX | S_BUFFRAW, def,
|
||
0x30, "newsize", /*NULL*/NULL, 0, def,
|
||
0x31, "closeTD", /*NULL*/NULL, 0, def,
|
||
0x32, "trans2", /*SMBDtrans2*/NULL, S_BUFFRAW, def,
|
||
0x33, "T2-2ndary", /*T2_sec*/NULL, S_BUFFRAW, def,
|
||
0x34, "t2fclose", /*T2_fclose*/NULL, S_BUFFRAW, def,
|
||
|
||
0x73, "sesssetupX", /*SMBDsesssetupX*/NULL, S_ANDX | S_BUFFRAW, def,
|
||
0x74, "ulogoffX", /*SMBDulogoffX*/NULL, S_ANDX | S_BUFFRAW, def,
|
||
0x75, "tconX", /*SMBDtconX*/NULL, S_ANDX | S_BUFFRAW, def,
|
||
0x82, "find", /*SMBDfind*/NULL, 0, def,
|
||
0x83, "findunique", /*SMBDfind*/NULL, 0, def,
|
||
0x84, "fclose", /*SMBDfclose*/NULL, 0, def,
|
||
|
||
0x60, "logon", /*NULL*/NULL, 0, def,
|
||
0x61, "bind", /*NULL*/NULL, 0, def,
|
||
0x62, "unbind", /*NULL*/NULL, 0, def,
|
||
0x63, "getaccess", /*NULL*/NULL, 0, def,
|
||
0x64, "link", /*NULL*/NULL, 0, def,
|
||
0x65, "fork", /*NULL*/NULL, 0, def,
|
||
0x66, "ioctl", /*NULL*/NULL, 0, def,
|
||
0x67, "copy", /*NULL*/NULL, 0, def,
|
||
0x68, "getpath", /*NULL*/NULL, 0, def,
|
||
0x69, "readh", /*NULL*/NULL, 0, def,
|
||
0x6A, "move", /*NULL*/NULL, 0, def,
|
||
0x6B, "rdchk", /*NULL*/NULL, 0, def,
|
||
0x6C, "mknod", /*NULL*/NULL, 0, def,
|
||
0x6D, "rlink", /*NULL*/NULL, 0, def,
|
||
0x6E, "getlatr", /*NULL*/NULL, 0, def,
|
||
0x70, "tcon", /*SMBDtcon*/NULL, 0, def,
|
||
0x71, "tdis", /*SMBDempty*/NULL, 0, def,
|
||
0x72, "negprot", /*SMBDnegprot*/NULL, S_BUFFRAW_R, def,
|
||
0x80, "dskattr", /*SMBDdskattr*/NULL, 0, def,
|
||
0x81, "search", /*SMBDsearch*/NULL, 0, def,
|
||
|
||
0xA0, "NtTrans", /*SMBDsplopen*/NULL, 0, def,
|
||
0xA1, "NtTrans2", /*SMBDsplwr*/NULL, 0, def,
|
||
0xA2, "Nt Create&X", /*SMBDsplretq*/NULL, 0, def,
|
||
0xA4, "Cancel", /*SMBDsplretq*/NULL, 0, def,
|
||
0xA5, "Nt Rename", /*???*/NULL, S_BUFFRAW, def,
|
||
|
||
0xC0, "splopen", /*SMBDsplopen*/NULL, 0, def,
|
||
0xC1, "splwr", /*SMBDsplwr*/NULL, 0, def,
|
||
0xC2, "splclose", /*SMBDhandle*/NULL, 0, def,
|
||
0xC3, "splretq", /*SMBDsplretq*/NULL, 0, def,
|
||
0xD0, "sends", /*NULL*/NULL, 0, def,
|
||
0xD1, "sendb", /*NULL*/NULL, 0, def,
|
||
0xD2, "fwdname", /*NULL*/NULL, 0, def,
|
||
0xD3, "cancelf", /*NULL*/NULL, 0, def,
|
||
0xD4, "getmac", /*NULL*/NULL, 0, def,
|
||
0xD5, "sendstrt", /*NULL*/NULL, 0, def,
|
||
0xD6, "sendend", /*NULL*/NULL, 0, def,
|
||
0xD7, "sendtxt", /*NULL*/NULL, 0, def,
|
||
|
||
|
||
// Pseudo smb.cmds (trans2 sub-commands are mapped up here (if possible))
|
||
// 0xE0, "T2-2ndary", T2_sec, S_BUFFRAW, def,
|
||
|
||
0xE1, "T2Open", /*T2_Open*/NULL, S_BUFFRAW, def,
|
||
0xE2, "T2FindFirst", /*T2_FindFirst*/NULL, S_BUFFRAW, def,
|
||
0xE3, "T2FindNext", /*T2_FindNext*/NULL, S_BUFFRAW, def,
|
||
0xE4, "T2QFSInf", /*T2_QFSInf*/NULL, S_BUFFRAW, def,
|
||
0xE5, "T2SetFSInf", /*T2_SetFSInf*/NULL, S_BUFFRAW, def,
|
||
0xE6, "T2QPathInf", /*T2_QPathInf*/NULL, S_BUFFRAW, def,
|
||
0xE7, "T2SetPathInf", /*T2_SetPathInf*/NULL, S_BUFFRAW, def,
|
||
0xE8, "T2QFileInf", /*T2_QFileInf*/NULL, S_BUFFRAW, def,
|
||
0xE9, "T2SetFileInf", /*T2_SetFileInf*/NULL, S_BUFFRAW, def,
|
||
0xEA, "T2FSCTL", /*T2_FSCTL*/NULL, S_BUFFRAW, def,
|
||
0xEB, "T2IOCTL", /*T2_IOCTL*/NULL, S_BUFFRAW, def,
|
||
0xEC, "T2FNotifyFirst",/*NULL*/NULL, S_BUFFRAW, def,
|
||
0xED, "T2FNotifyNext", /*NULL*/NULL, S_BUFFRAW, def,
|
||
0xEE, "T2MkDir", /*T2_MkDir*/NULL, S_BUFFRAW, def,
|
||
|
||
|
||
0, NULL, NULL, 0, def,
|
||
} ;
|
||
|
||
VOID
|
||
HexDumpLine (
|
||
PCHAR pch,
|
||
ULONG len,
|
||
PCHAR s,
|
||
PCHAR t,
|
||
USHORT flag
|
||
);
|
||
|
||
VOID
|
||
SmbDump_raw (
|
||
PSMB_HEADER pSMB,
|
||
PMDL SmbMDL
|
||
);
|
||
|
||
VOID
|
||
PrSMB(
|
||
PSMB_HEADER pSMB,
|
||
PMDL SmbMDL
|
||
);
|
||
|
||
VOID
|
||
ndump_core(
|
||
PCHAR far_p,
|
||
ULONG len
|
||
);
|
||
|
||
VOID
|
||
prnt_cmd(UCHAR cmd);
|
||
|
||
struct s_smbcmd *LookUpSmb2 (USHORT cmd);
|
||
|
||
VOID
|
||
DumpSMB (
|
||
IN PMDL Smb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine dumps an SMB to the console.
|
||
.
|
||
Arguments:
|
||
|
||
IN PMDL Smb - Supplies the SMB to dump
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
|
||
print_smb_buf - print SMB based on SMBTraceValue value
|
||
if SMBTraceValue:
|
||
1 = print cmd value etc.
|
||
2 = print cmd value etc.+ call SPIDER if error in SMB
|
||
3 = print cmd value etc. dump smb (up to nmax_dump bytes)
|
||
4 = above + call SPIDER if error in SMB
|
||
5 = above + call SPIDER after dumping each SMB
|
||
|
||
called as below:
|
||
|
||
if (SMBTraceValue > 0)
|
||
print_smb_buf(pNCBQ);
|
||
if (SMBTraceValue > 0)
|
||
print_smb_buf(pNCBQ);
|
||
|
||
--*/
|
||
|
||
{
|
||
if (Smb==NULL) {
|
||
KdPrint ((" NCB !!! NULL !!!\n"));
|
||
return;
|
||
}
|
||
|
||
SmbDump_raw(MmGetSystemAddressForMdl(Smb), Smb);
|
||
|
||
}
|
||
|
||
|
||
|
||
/***
|
||
* SmbDump_raw - original smb dump routine.
|
||
*/
|
||
VOID
|
||
SmbDump_raw (
|
||
PSMB_HEADER pSMB,
|
||
PMDL SmbMDL)
|
||
{
|
||
PMDL pMDL;
|
||
USHORT i;
|
||
if (!RdrSMBTraceValue)
|
||
return;
|
||
|
||
// KdPrint (pMisc);
|
||
//
|
||
// if (DrvIndx)
|
||
// KdPrint(" dr %d,", DrvIndx);
|
||
// if (pNCB->ncb_lana_num)
|
||
// KdPrint(" ln %d,", pNCB->ncb_lana_num);
|
||
// if (pNCB->ncb_lsn)
|
||
// KdPrint(" lsn %d,", pNCB->ncb_lsn);
|
||
|
||
if (SmbGetUshort(&pSMB->Tid))
|
||
KdPrint((" tid %d,", SmbGetUshort(&pSMB->Tid)));
|
||
if (SmbGetUshort(&pSMB->Pid))
|
||
KdPrint((" pid %d,", SmbGetUshort(&pSMB->Pid)));
|
||
|
||
|
||
pMDL = SmbMDL;
|
||
i = 1;
|
||
do {
|
||
KdPrint(("\n buffer%d: MDL Address %lx Virtual Address %lx len %lx ",
|
||
i,
|
||
pMDL,
|
||
(PCHAR)pMDL->StartVa + pMDL->ByteOffset,
|
||
MmGetMdlByteCount(pMDL)));
|
||
|
||
pMDL = pMDL->Next;
|
||
i++;
|
||
} while (pMDL != NULL);
|
||
|
||
PrSMB (pSMB, SmbMDL);
|
||
}
|
||
|
||
|
||
VOID
|
||
PrSMB(
|
||
PSMB_HEADER pSMB,
|
||
PMDL SmbMdl
|
||
)
|
||
{
|
||
int cmd = pSMB->Command;
|
||
PSMB_PARAMS Params = (PSMB_PARAMS )(pSMB+1);
|
||
ULONG len;
|
||
PMDL Next;
|
||
|
||
prnt_cmd((unsigned char) cmd);
|
||
|
||
if (Params->WordCount) {
|
||
PGENERIC_ANDX pSMB2 = (PGENERIC_ANDX )(pSMB+1);
|
||
while ((cmd == SMB_COM_OPEN_ANDX) ||
|
||
(cmd == SMB_COM_READ_ANDX) ||
|
||
(cmd == SMB_COM_WRITE_ANDX) ||
|
||
(cmd == SMB_COM_LOCKING_ANDX) ||
|
||
(cmd == SMB_COM_SESSION_SETUP_ANDX) ||
|
||
(cmd == SMB_COM_LOGOFF_ANDX) ||
|
||
(cmd == SMB_COM_TREE_CONNECT_ANDX)) {
|
||
cmd = pSMB2->AndXCommand;
|
||
if (cmd != 0xFF) {
|
||
KdPrint((","));
|
||
prnt_cmd((unsigned char) cmd);
|
||
pSMB2 = (PGENERIC_ANDX)( (PCHAR) pSMB + SmbGetUshort(&pSMB2->AndXOffset));
|
||
|
||
if (pSMB2->WordCount == 0)
|
||
break; /* last command */
|
||
}
|
||
}
|
||
}
|
||
|
||
if (pSMB->ErrorClass != 0)
|
||
KdPrint((" %d %d ", pSMB->ErrorClass, SmbGetUshort(&pSMB->Error)));
|
||
|
||
if ((RdrSMBTraceValue >= 3) ||
|
||
(((RdrSMBTraceValue == 2) &&
|
||
(pSMB->ErrorClass)))) {
|
||
if ((pSMB->Command == SMB_COM_SEARCH) && (RdrSMBTraceValue == 2) &&
|
||
(SmbGetUshort(&pSMB->Error) == /*ERROR_NO_MORE_FILES*/12)) {
|
||
KdPrint(("\n")); /* always get this on search */
|
||
} else {
|
||
if ((Params->WordCount == 0) &&
|
||
(SmbGetUshort(&Params->ByteCount) == 0)) {
|
||
ndump_core((PCHAR) pSMB, sizeof (SMB_HEADER));
|
||
} else {
|
||
|
||
Next = SmbMdl; len = 0;
|
||
do {
|
||
|
||
ndump_core(MmGetSystemAddressForMdl(Next), MIN(MmGetMdlByteCount(Next), RdrMaxDump-len));
|
||
|
||
len += MmGetMdlByteCount(Next);
|
||
} while ( (Next = Next->Next) != NULL &&
|
||
len <= RdrMaxDump);
|
||
}
|
||
}
|
||
} else {
|
||
KdPrint(("\n"));
|
||
}
|
||
|
||
// fflush(stdout);
|
||
}
|
||
|
||
|
||
/*
|
||
* ndump_core: debug routine to print core
|
||
*/
|
||
void
|
||
ndump_core(
|
||
PCHAR far_p,
|
||
ULONG len
|
||
)
|
||
{
|
||
ULONG l;
|
||
char s[80], t[80];
|
||
|
||
if (len > RdrMaxDump)
|
||
len = RdrMaxDump;
|
||
|
||
while (len) {
|
||
l = len < 16 ? len : 16;
|
||
|
||
KdPrint (("\n%lx ", far_p));
|
||
HexDumpLine (far_p, l, s, t, 0);
|
||
KdPrint (("%s%.*s%s", s, 1 + ((16 - l) * 3), "", t));
|
||
|
||
len -= l;
|
||
far_p += l;
|
||
}
|
||
KdPrint (("\n"));
|
||
}
|
||
|
||
VOID
|
||
HexDumpLine (
|
||
PCHAR pch,
|
||
ULONG len,
|
||
PCHAR s,
|
||
PCHAR t,
|
||
USHORT flag
|
||
)
|
||
{
|
||
static UCHAR rghex[] = "0123456789ABCDEF";
|
||
|
||
UCHAR c;
|
||
UCHAR *hex, *asc;
|
||
|
||
|
||
hex = s;
|
||
asc = t;
|
||
|
||
*(asc++) = '*';
|
||
while (len--) {
|
||
c = *(pch++);
|
||
*(hex++) = rghex [c >> 4] ;
|
||
*(hex++) = rghex [c & 0x0F];
|
||
*(hex++) = ' ';
|
||
*(asc++) = (c < ' ' || c > '~') ? (CHAR )'.' : c;
|
||
}
|
||
*(asc++) = '*';
|
||
*asc = 0;
|
||
*hex = 0;
|
||
|
||
// if (flag) {
|
||
// QSmbText (s);
|
||
// GotoTab (TAB_ADUMP);
|
||
// QSmbText (t);
|
||
// }
|
||
flag;
|
||
}
|
||
|
||
VOID
|
||
prnt_cmd(UCHAR cmd)
|
||
{
|
||
struct s_smbcmd *pt;
|
||
|
||
// if (vT2Cmd) {
|
||
// KdPrint (("%s", vrgT2Name));
|
||
// return ;
|
||
// }
|
||
|
||
pt = LookUpSmb2 (cmd);
|
||
if (pt)
|
||
KdPrint (("%s", pt->text));
|
||
else KdPrint (("BAD COMMAND (0x%x)", cmd));
|
||
}
|
||
|
||
/***
|
||
* LookUpSmb2
|
||
*
|
||
* Looks up SMB info for a given smb cmd number
|
||
*/
|
||
struct s_smbcmd *LookUpSmb2 (USHORT cmd)
|
||
{
|
||
struct s_smbcmd *s;
|
||
|
||
if (cmd > 255)
|
||
return NULL;
|
||
|
||
for (s = rgCmdTable;s->text;s++) {
|
||
if (s->value==cmd) {
|
||
return s;
|
||
}
|
||
}
|
||
return NULL;
|
||
|
||
// return (rgLUTab [cmd]);
|
||
}
|
||
|
||
#endif
|
||
#if RDRPOOLDBG
|
||
typedef struct {
|
||
ULONG Count;
|
||
ULONG Size;
|
||
PCHAR FileName;
|
||
DWORD LineNumber;
|
||
} POOL_STATS, *PPOOL_STATS;
|
||
|
||
|
||
typedef struct _POOL_HEADER {
|
||
// LIST_ENTRY ListEntry;
|
||
ULONG NumberOfBytes;
|
||
PPOOL_STATS Stats;
|
||
} POOL_HEADER, *PPOOL_HEADER;
|
||
|
||
ULONG CurrentAllocationCount = 0;
|
||
ULONG CurrentAllocationSize = 0;
|
||
|
||
ULONG NextFreeEntry = 0;
|
||
|
||
POOL_STATS PoolStats[POOL_MAXTYPE+1] = {0};
|
||
|
||
PVOID
|
||
RdrAllocatePool (
|
||
IN POOL_TYPE PoolType,
|
||
IN ULONG NumberOfBytes,
|
||
IN PCHAR FileName,
|
||
IN DWORD LineNumber,
|
||
IN ULONG Tag
|
||
)
|
||
{
|
||
PPOOL_HEADER header;
|
||
KIRQL oldIrql;
|
||
#if 1
|
||
ULONG i;
|
||
#endif
|
||
|
||
header = ExAllocatePoolWithTag( PoolType, sizeof(POOL_HEADER) + NumberOfBytes, Tag );
|
||
if ( header == NULL ) {
|
||
return NULL;
|
||
}
|
||
header->NumberOfBytes = NumberOfBytes;
|
||
|
||
// DbgPrint( "RDR: allocated type %d, size %d at %x\n", AllocationType, NumberOfBytes, header );
|
||
|
||
ACQUIRE_SPIN_LOCK( &RdrStatisticsSpinLock, &oldIrql );
|
||
|
||
CurrentAllocationCount++;
|
||
CurrentAllocationSize += NumberOfBytes;
|
||
#if 1
|
||
//
|
||
// Lets see if we've already allocated one of these guys.
|
||
//
|
||
|
||
|
||
for (i = 0;i < POOL_MAXTYPE ; i+= 1 ) {
|
||
if ((PoolStats[i].LineNumber == LineNumber) &&
|
||
(PoolStats[i].FileName == FileName)) {
|
||
|
||
//
|
||
// Yup, remember this allocation and return.
|
||
//
|
||
|
||
header->Stats = &PoolStats[i];
|
||
PoolStats[i].Count++;
|
||
PoolStats[i].Size += NumberOfBytes;
|
||
|
||
RELEASE_SPIN_LOCK( &RdrStatisticsSpinLock, oldIrql );
|
||
|
||
return header + 1;
|
||
}
|
||
}
|
||
|
||
for (i = NextFreeEntry; i < POOL_MAXTYPE ; i+= 1 ) {
|
||
if ((PoolStats[i].LineNumber == 0) &&
|
||
(PoolStats[i].FileName == NULL)) {
|
||
|
||
PoolStats[i].Count++;
|
||
PoolStats[i].Size += NumberOfBytes;
|
||
PoolStats[i].FileName = FileName;
|
||
PoolStats[i].LineNumber = LineNumber;
|
||
header->Stats = &PoolStats[i];
|
||
|
||
NextFreeEntry = i+1;
|
||
|
||
RELEASE_SPIN_LOCK( &RdrStatisticsSpinLock, oldIrql );
|
||
|
||
return header + 1;
|
||
}
|
||
}
|
||
|
||
KdPrint(("RDR: POOL_MAXTYPE set too small - Overallocated\n"));
|
||
|
||
header->Stats = &PoolStats[i];
|
||
PoolStats[POOL_MAXTYPE].Count++;
|
||
PoolStats[POOL_MAXTYPE].Size += NumberOfBytes;
|
||
#endif
|
||
|
||
RELEASE_SPIN_LOCK( &RdrStatisticsSpinLock, oldIrql );
|
||
|
||
return header + 1;
|
||
}
|
||
|
||
PVOID
|
||
RdrAllocatePoolWithQuota (
|
||
IN POOL_TYPE PoolType,
|
||
IN ULONG NumberOfBytes,
|
||
IN PCHAR FileName,
|
||
IN DWORD LineNumber,
|
||
IN DWORD Tag
|
||
)
|
||
{
|
||
PPOOL_HEADER header;
|
||
KIRQL oldIrql;
|
||
#if 1
|
||
ULONG i;
|
||
#endif
|
||
|
||
header = ExAllocatePoolWithQuotaTag( PoolType, sizeof(POOL_HEADER) + NumberOfBytes, Tag );
|
||
if ( header == NULL ) {
|
||
return NULL;
|
||
}
|
||
header->NumberOfBytes = NumberOfBytes;
|
||
|
||
// DbgPrint( "Rdr: allocated type %d, size %d at %x\n", AllocationType, NumberOfBytes, header );
|
||
|
||
ACQUIRE_SPIN_LOCK( &RdrStatisticsSpinLock, &oldIrql );
|
||
|
||
CurrentAllocationCount++;
|
||
CurrentAllocationSize += NumberOfBytes;
|
||
#if 1
|
||
//
|
||
// Lets see if we've already allocated one of these guys.
|
||
//
|
||
|
||
|
||
for (i = 0;i < POOL_MAXTYPE ; i+= 1 ) {
|
||
if ((PoolStats[i].LineNumber == LineNumber) &&
|
||
(PoolStats[i].FileName == FileName)) {
|
||
|
||
//
|
||
// Yup, remember this allocation and return.
|
||
//
|
||
|
||
header->Stats = &PoolStats[i];
|
||
PoolStats[i].Count++;
|
||
PoolStats[i].Size += NumberOfBytes;
|
||
|
||
RELEASE_SPIN_LOCK( &RdrStatisticsSpinLock, oldIrql );
|
||
|
||
return header + 1;
|
||
}
|
||
}
|
||
|
||
for (i = NextFreeEntry; i < POOL_MAXTYPE ; i+= 1 ) {
|
||
if ((PoolStats[i].LineNumber == 0) &&
|
||
(PoolStats[i].FileName == NULL)) {
|
||
|
||
PoolStats[i].Count++;
|
||
PoolStats[i].Size += NumberOfBytes;
|
||
PoolStats[i].FileName = FileName;
|
||
PoolStats[i].LineNumber = LineNumber;
|
||
header->Stats = &PoolStats[i];
|
||
|
||
NextFreeEntry = i+1;
|
||
|
||
RELEASE_SPIN_LOCK( &RdrStatisticsSpinLock, oldIrql );
|
||
|
||
return header + 1;
|
||
}
|
||
}
|
||
|
||
header->Stats = &PoolStats[i];
|
||
PoolStats[POOL_MAXTYPE].Count++;
|
||
PoolStats[POOL_MAXTYPE].Size += NumberOfBytes;
|
||
|
||
#endif
|
||
|
||
RELEASE_SPIN_LOCK( &RdrStatisticsSpinLock, oldIrql );
|
||
|
||
return header + 1;
|
||
}
|
||
|
||
VOID
|
||
RdrFreePool (
|
||
IN PVOID P
|
||
)
|
||
{
|
||
PPOOL_HEADER header;
|
||
KIRQL oldIrql;
|
||
PPOOL_STATS stats;
|
||
ULONG size;
|
||
|
||
header = (PPOOL_HEADER)P - 1;
|
||
|
||
size = header->NumberOfBytes;
|
||
stats = header->Stats;
|
||
|
||
// DbgPrint( "Rdr: freed type %d, size %d at %x\n", allocationType, size, header );
|
||
|
||
ACQUIRE_SPIN_LOCK( &RdrStatisticsSpinLock, &oldIrql );
|
||
|
||
CurrentAllocationCount--;
|
||
CurrentAllocationSize -= size;
|
||
#if 1
|
||
stats->Count--;
|
||
stats->Size -= size;
|
||
#endif
|
||
|
||
RELEASE_SPIN_LOCK( &RdrStatisticsSpinLock, oldIrql );
|
||
|
||
ExFreePool( header );
|
||
|
||
return;
|
||
}
|
||
#endif // RDRPOOLDBG
|