Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

698 lines
17 KiB

/*
Copyright (c) 1992 Microsoft Corporation
Module Name:
forkio.c
Abstract:
This module contains the routines for performing fork reads and writes
directly by building IRPs and not using NtReadFile/NtWriteFile. This
should be used only by the FpRead and FpWrite Apis.
Author:
Jameel Hyder (microsoft!jameelh)
Revision History:
15 Jan 1993 Initial Version
Notes: Tab stop: 4
--*/
#define FILENUM FILE_FORKIO
#define FORKIO_LOCALS
#include <afp.h>
#include <forkio.h>
#include <gendisp.h>
#if DBG
PCHAR AfpIoForkFunc[] =
{
"",
"READ",
"WRITE",
"LOCK",
"UNLOCK"
};
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AfpIoForkRead)
#pragma alloc_text( PAGE, AfpIoForkWrite)
#pragma alloc_text( PAGE, AfpIoForkLockUnlock)
#endif
/*** afpIoGenericComplete
*
* This is the generic completion routine for a posted io request.
*/
NTSTATUS
afpIoGenericComplete(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp,
IN PCMPLCTXT pCmplCtxt
)
{
PSDA pSda; // Not valid for Unlock
struct _ResponsePacket // For lock/unlock request
{
union
{
BYTE __RangeStart[4];
BYTE __LastWritten[4];
};
};
ASSERT(VALID_CTX(pCmplCtxt));
if (pCmplCtxt->cc_Func != FUNC_UNLOCK)
{
pSda = (PSDA)(pCmplCtxt->cc_pSda);
ASSERT(VALID_SDA(pSda));
if (pCmplCtxt->cc_Func == FUNC_WRITE)
{
AfpFreeIOBuffer(pSda);
}
else if (!NT_SUCCESS(pIrp->IoStatus.Status) &&
(pCmplCtxt->cc_Func == FUNC_READ))
{
AfpIOFreeBackFillBuffer(pSda);
}
}
if (!NT_SUCCESS(pIrp->IoStatus.Status))
{
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_WARN,
("afpIoGenericComplete: %s ERROR %lx\n",
AfpIoForkFunc[pCmplCtxt->cc_Func], pIrp->IoStatus.Status));
if (pCmplCtxt->cc_Func != FUNC_UNLOCK)
{
if (pIrp->IoStatus.Status == STATUS_FILE_LOCK_CONFLICT)
pCmplCtxt->cc_SavedStatus = AFP_ERR_LOCK;
else if (pIrp->IoStatus.Status == STATUS_END_OF_FILE)
{
pCmplCtxt->cc_SavedStatus = AFP_ERR_NONE;
if (pIrp->IoStatus.Information == 0)
pCmplCtxt->cc_SavedStatus = AFP_ERR_EOF;
}
else if (pIrp->IoStatus.Status == STATUS_DISK_FULL)
pCmplCtxt->cc_SavedStatus = AFP_ERR_DISK_FULL;
else pCmplCtxt->cc_SavedStatus = AFP_ERR_MISC;
}
else
{
AFPLOG_ERROR(AFPSRVMSG_CANT_UNLOCK,
pIrp->IoStatus.Status,
NULL,
0,
NULL);
}
if (pCmplCtxt->cc_Func == FUNC_LOCK)
{
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_ERR,
("afpIoGenericComplete: ForkLock failed %lx, aborting for range %ld,%ld\n",
pIrp->IoStatus.Status,
pCmplCtxt->cc_pForkLock->flo_Offset,
pCmplCtxt->cc_pForkLock->flo_Offset+pCmplCtxt->cc_pForkLock->flo_Size-1));
AfpForkLockUnlink(pCmplCtxt->cc_pForkLock);
}
}
else switch (pCmplCtxt->cc_Func)
{
case FUNC_WRITE:
INTERLOCKED_ADD_STATISTICS(&AfpServerStatistics.stat_DataWritten,
pCmplCtxt->cc_Offst,
&AfpStatisticsLock);
pSda->sda_ReplySize = SIZE_RESPPKT;
if (AfpAllocReplyBuf(pSda) == AFP_ERR_NONE)
{
PUTDWORD2DWORD(pRspPkt->__LastWritten,
pCmplCtxt->cc_Offst + pCmplCtxt->cc_ReqCount);
}
else pCmplCtxt->cc_SavedStatus = AFP_ERR_MISC;
break;
case FUNC_READ:
{
LONG i, Size;
PBYTE pBuf;
BYTE NlChar = pCmplCtxt->cc_NlChar;
BYTE NlMask = pCmplCtxt->cc_NlMask;
INTERLOCKED_ADD_STATISTICS(&AfpServerStatistics.stat_DataRead,
(ULONG)pIrp->IoStatus.Information,
&AfpStatisticsLock);
Size = (LONG)pIrp->IoStatus.Information;
#if 0
// The following code does the right thing as per the spec but
// the finder seems to think otherwise.
if (Size < pCmplCtxt->cc_ReqCount)
pCmplCtxt->cc_SavedStatus = AFP_ERR_EOF;
#endif
if (Size == 0)
{
pCmplCtxt->cc_SavedStatus = AFP_ERR_EOF;
AfpIOFreeBackFillBuffer(pSda);
}
else if (pCmplCtxt->cc_NlMask != 0)
{
for (i = 0, pBuf = pSda->sda_ReplyBuf; i < Size; i++, pBuf++)
{
if ((*pBuf & NlMask) == NlChar)
{
Size = ++i;
pCmplCtxt->cc_SavedStatus = AFP_ERR_NONE;
break;
}
}
}
pSda->sda_ReplySize = (USHORT)Size;
}
ASSERT((pCmplCtxt->cc_SavedStatus != AFP_ERR_EOF) ||
(pSda->sda_ReplySize == 0));
break;
case FUNC_LOCK:
INTERLOCKED_ADD_ULONG(&AfpServerStatistics.stat_CurrentFileLocks,
1,
&AfpStatisticsLock);
pSda->sda_ReplySize = SIZE_RESPPKT;
if (AfpAllocReplyBuf(pSda) == AFP_ERR_NONE)
PUTDWORD2DWORD(pRspPkt->__RangeStart, pCmplCtxt->cc_pForkLock->flo_Offset);
else pCmplCtxt->cc_SavedStatus = AFP_ERR_MISC;
break;
case FUNC_UNLOCK:
INTERLOCKED_ADD_ULONG(
&AfpServerStatistics.stat_CurrentFileLocks,
(ULONG)-1,
&AfpStatisticsLock);
break;
default:
ASSERTMSG(0, "afpIoGenericComplete: Invalid function\n");
KeBugCheck(0);
break;
}
if (pIrp->MdlAddress != NULL)
AfpFreeMdl(pIrp->MdlAddress);
AfpFreeIrp(pIrp);
if (pCmplCtxt->cc_Func != FUNC_UNLOCK)
{
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
("afpIoGenericComplete: %s Returning %ld\n",
AfpIoForkFunc[pCmplCtxt->cc_Func], pCmplCtxt->cc_SavedStatus));
AfpCompleteApiProcessing(pSda, pCmplCtxt->cc_SavedStatus);
}
AfpFreeCmplCtxtBuf(pCmplCtxt);
// Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest
// will stop working on the IRP.
return STATUS_MORE_PROCESSING_REQUIRED;
}
/*** AfpIoForkRead
*
* Read a chunk of data from the open fork. The read buffer is always the
* the reply buffer in the sda (sda_ReplyBuf).
*/
AFPSTATUS
AfpIoForkRead(
IN PSDA pSda, // The session requesting read
IN POPENFORKENTRY pOpenForkEntry, // The open fork in question
IN PFORKOFFST pOffset, // Pointer to fork offset
IN LONG ReqCount, // Size of read request
IN BYTE NlMask,
IN BYTE NlChar
)
{
PIRP pIrp = NULL;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS Status;
PMDL pMdl = NULL;
PCMPLCTXT pCmplCtxt;
PAGED_CODE( );
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
ASSERT(VALID_OPENFORKENTRY(pOpenForkEntry));
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
("AfpIoForkRead: Session %ld, Offset %ld, Size %ld, Fork %ld\n",
pSda->sda_SessionId, pOffset->LowPart, ReqCount, pOpenForkEntry->ofe_ForkId));
do
{
// Allocate and initialize the completion context
pCmplCtxt = AfpAllocCmplCtxtBuf(pSda);
if (pCmplCtxt == NULL)
{
AfpFreeIOBuffer(pSda);
Status = AFP_ERR_MISC;
break;
}
afpInitializeCmplCtxt(pCmplCtxt,
FUNC_READ,
pSda->sda_ReadStatus,
pSda,
NULL,
ReqCount,
pOffset->LowPart);
pCmplCtxt->cc_NlChar = NlChar;
pCmplCtxt->cc_NlMask = NlMask;
// Allocate and initialize the IRP for this operation.
if ((pIrp = AfpAllocIrp(pOpenForkEntry->ofe_pDeviceObject->StackSize)) == NULL)
{
AfpFreeIOBuffer(pSda);
Status = AFP_ERR_MISC;
break;
}
if ((pOpenForkEntry->ofe_pDeviceObject->Flags & DO_BUFFERED_IO) == 0)
{
// Allocate an Mdl to describe the read buffer
if ((pMdl = AfpAllocMdl(pSda->sda_ReplyBuf, ReqCount, pIrp)) == NULL)
{
Status = AFP_ERR_MISC;
break;
}
}
// Set up the completion routine.
IoSetCompletionRoutine( pIrp,
(PIO_COMPLETION_ROUTINE)afpIoGenericComplete,
pCmplCtxt,
True,
True,
True);
pIrpSp = IoGetNextIrpStackLocation(pIrp);
pIrp->Tail.Overlay.OriginalFileObject = AfpGetRealFileObject(pOpenForkEntry->ofe_pFileObject);
pIrp->Tail.Overlay.Thread = AfpThread;
pIrp->RequestorMode = KernelMode;
// Get a pointer to the stack location for the first driver.
// This will be used to pass the original function codes and
// parameters.
pIrpSp->MajorFunction = IRP_MJ_READ;
pIrpSp->MinorFunction = IRP_MN_NORMAL;
pIrpSp->FileObject = AfpGetRealFileObject(pOpenForkEntry->ofe_pFileObject);
pIrpSp->DeviceObject = pOpenForkEntry->ofe_pDeviceObject;
// Copy the caller's parameters to the service-specific portion of the
// IRP.
pIrpSp->Parameters.Read.Length = ReqCount;
pIrpSp->Parameters.Read.Key = pSda->sda_SessionId;
pIrpSp->Parameters.Read.ByteOffset = *pOffset;
if ((pOpenForkEntry->ofe_pDeviceObject->Flags & DO_BUFFERED_IO) != 0)
{
pIrp->AssociatedIrp.SystemBuffer = pSda->sda_ReplyBuf;
pIrp->Flags = IRP_BUFFERED_IO | IRP_INPUT_OPERATION;
}
else if ((pOpenForkEntry->ofe_pDeviceObject->Flags & DO_DIRECT_IO) != 0)
{
pIrp->MdlAddress = pMdl;
}
else
{
pIrp->UserBuffer = pSda->sda_ReplyBuf;
pIrp->MdlAddress = pMdl;
}
// Now simply invoke the driver at its dispatch entry with the IRP.
IoCallDriver(pOpenForkEntry->ofe_pDeviceObject, pIrp);
Status = AFP_ERR_EXTENDED; // This makes the caller do nothing and
} while (False); // the completion routine handles everything
if (Status != AFP_ERR_EXTENDED)
{
if (pIrp != NULL)
AfpFreeIrp(pIrp);
if (pMdl != NULL)
AfpFreeMdl(pMdl);
if (pCmplCtxt)
{
AfpFreeCmplCtxtBuf(pCmplCtxt);
}
}
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
("AfpIoForkRead: Returning %ld\n", Status));
return Status;
}
/*** AfpIoForkWrite
*
* Write a chunk of data to the open fork. The write buffer is always the
* the write buffer in the sda (sda_IOBuf).
*/
AFPSTATUS
AfpIoForkWrite(
IN PSDA pSda, // The session requesting read
IN POPENFORKENTRY pOpenForkEntry, // The open fork in question
IN PFORKOFFST pOffset, // Pointer to fork offset
IN LONG ReqCount // Size of write request
)
{
PIRP pIrp = NULL;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS Status;
PMDL pMdl = NULL;
PCMPLCTXT pCmplCtxt;
PAGED_CODE( );
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
ASSERT(VALID_OPENFORKENTRY(pOpenForkEntry));
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
("AfpIoForkWrite: Session %ld, Offset %ld, Size %ld, Fork %ld\n",
pSda->sda_SessionId, pOffset->LowPart, ReqCount, pOpenForkEntry->ofe_ForkId));
do
{
// Allocate and initialize the completion context
pCmplCtxt = AfpAllocCmplCtxtBuf(pSda);
if (pCmplCtxt == NULL)
{
Status = AFP_ERR_MISC;
break;
}
afpInitializeCmplCtxt(pCmplCtxt,
FUNC_WRITE,
AFP_ERR_NONE,
pSda,
NULL,
ReqCount,
pOffset->LowPart);
// Allocate and initialize the IRP for this operation.
if ((pIrp = AfpAllocIrp(pOpenForkEntry->ofe_pDeviceObject->StackSize)) == NULL)
{
Status = AFP_ERR_MISC;
break;
}
if ((pOpenForkEntry->ofe_pDeviceObject->Flags & DO_BUFFERED_IO) == 0)
{
// Allocate an Mdl to describe the write buffer
if ((pMdl = AfpAllocMdl(pSda->sda_IOBuf, ReqCount, pIrp)) == NULL)
{
Status = AFP_ERR_MISC;
break;
}
}
// Set up the completion routine.
IoSetCompletionRoutine( pIrp,
(PIO_COMPLETION_ROUTINE)afpIoGenericComplete,
pCmplCtxt,
True,
True,
True);
pIrpSp = IoGetNextIrpStackLocation(pIrp);
pIrp->Tail.Overlay.OriginalFileObject = AfpGetRealFileObject(pOpenForkEntry->ofe_pFileObject);
pIrp->Tail.Overlay.Thread = AfpThread;
pIrp->RequestorMode = KernelMode;
// Get a pointer to the stack location for the first driver.
// This will be used to pass the original function codes and
// parameters.
pIrpSp->MajorFunction = IRP_MJ_WRITE;
pIrpSp->MinorFunction = IRP_MN_NORMAL;
pIrpSp->FileObject = AfpGetRealFileObject(pOpenForkEntry->ofe_pFileObject);
pIrpSp->DeviceObject = pOpenForkEntry->ofe_pDeviceObject;
// Copy the caller's parameters to the service-specific portion of the
// IRP.
pIrpSp->Parameters.Write.Length = ReqCount;
pIrpSp->Parameters.Write.Key = pSda->sda_SessionId;
pIrpSp->Parameters.Write.ByteOffset = *pOffset;
if ((pOpenForkEntry->ofe_pDeviceObject->Flags & DO_BUFFERED_IO) != 0)
{
pIrp->AssociatedIrp.SystemBuffer = pSda->sda_IOBuf;
pIrp->Flags = IRP_BUFFERED_IO;
}
else if ((pOpenForkEntry->ofe_pDeviceObject->Flags & DO_DIRECT_IO) != 0)
{
pIrp->MdlAddress = pMdl;
}
else
{
pIrp->UserBuffer = pSda->sda_IOBuf;
pIrp->MdlAddress = pMdl;
}
// Now simply invoke the driver at its dispatch entry with the IRP.
IoCallDriver(pOpenForkEntry->ofe_pDeviceObject, pIrp);
Status = AFP_ERR_EXTENDED; // This makes the caller do nothing and
} while (False); // the completion routine handles everything
if (Status != AFP_ERR_EXTENDED)
{
if (pIrp != NULL)
AfpFreeIrp(pIrp);
if (pMdl != NULL)
AfpFreeMdl(pMdl);
if (pCmplCtxt)
{
AfpFreeCmplCtxtBuf(pCmplCtxt);
}
}
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
("AfpIoForkWrite: Returning %ld\n", Status));
return Status;
}
/*** AfpIoForkLock
*
* Lock/Unlock a section of the open fork.
*/
AFPSTATUS
AfpIoForkLockUnlock(
IN PSDA pSda,
IN PFORKLOCK pForkLock,
IN PFORKOFFST pForkOffset,
IN PFORKSIZE pLockSize,
IN BYTE Func
)
{
PIRP pIrp = NULL;
PIO_STACK_LOCATION pIrpSp;
POPENFORKENTRY pOpenForkEntry = pForkLock->flo_pOpenForkEntry;
NTSTATUS Status;
PCMPLCTXT pCmplCtxt;
PAGED_CODE( );
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
ASSERT(VALID_OPENFORKENTRY(pOpenForkEntry));
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
("AfpIoForkLockUnlock: %sLOCK Session %ld, Offset %ld, Size %ld, Fork %ld\n",
(Func == FUNC_LOCK) ? "" : "UN", pSda->sda_SessionId,
pForkOffset->LowPart, pLockSize->LowPart, pOpenForkEntry->ofe_ForkId));
do
{
// Allocate and initialize the completion context
pCmplCtxt = AfpAllocCmplCtxtBuf(pSda);
if (pCmplCtxt == NULL)
{
Status = AFP_ERR_MISC;
break;
}
afpInitializeCmplCtxt(pCmplCtxt,
Func,
AFP_ERR_NONE,
pSda,
pForkLock,
pForkOffset->LowPart,
pLockSize->LowPart);
// Allocate and initialize the IRP for this operation.
if ((pIrp = AfpAllocIrp(pOpenForkEntry->ofe_pDeviceObject->StackSize)) == NULL)
{
Status = AFP_ERR_MISC;
break;
}
// Set up the completion routine.
IoSetCompletionRoutine( pIrp,
(PIO_COMPLETION_ROUTINE)afpIoGenericComplete,
pCmplCtxt,
True,
True,
True);
pIrpSp = IoGetNextIrpStackLocation(pIrp);
pIrp->Tail.Overlay.OriginalFileObject = AfpGetRealFileObject(pOpenForkEntry->ofe_pFileObject);
pIrp->Tail.Overlay.Thread = AfpThread;
pIrp->RequestorMode = KernelMode;
// Get a pointer to the stack location for the first driver.
// This will be used to pass the original function codes and parameters.
pIrpSp->MajorFunction = IRP_MJ_LOCK_CONTROL;
pIrpSp->MinorFunction = (Func == FUNC_LOCK) ? IRP_MN_LOCK : IRP_MN_UNLOCK_SINGLE;
pIrpSp->FileObject = AfpGetRealFileObject(pOpenForkEntry->ofe_pFileObject);
pIrpSp->DeviceObject = pOpenForkEntry->ofe_pDeviceObject;
// Copy the caller's parameters to the service-specific portion of the IRP.
pIrpSp->Parameters.LockControl.Length = pLockSize;
pIrpSp->Parameters.LockControl.Key = pSda->sda_SessionId;
pIrpSp->Parameters.LockControl.ByteOffset = *pForkOffset;
pIrp->MdlAddress = NULL;
pIrpSp->Flags = SL_FAIL_IMMEDIATELY | SL_EXCLUSIVE_LOCK;
// Now simply invoke the driver at its dispatch entry with the IRP.
IoCallDriver(pOpenForkEntry->ofe_pDeviceObject, pIrp);
// For lock operation this makes the caller do nothing
// and the completion routine handles everything
// For unlock operation we complete the request here.
Status = (Func == FUNC_LOCK) ? AFP_ERR_EXTENDED : AFP_ERR_NONE;
} while (False);
if ((Status != AFP_ERR_EXTENDED) && (Status != AFP_ERR_NONE))
{
if (pIrp != NULL)
AfpFreeIrp(pIrp);
if (pCmplCtxt)
{
AfpFreeCmplCtxtBuf(pCmplCtxt);
}
}
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
("AfpIoForkLock: Returning %ld\n", Status));
return Status;
}
PCMPLCTXT
AfpAllocCmplCtxtBuf(
IN PSDA pSda
)
{
KIRQL OldIrql;
PBYTE pRetBuffer;
ACQUIRE_SPIN_LOCK(&pSda->sda_Lock, &OldIrql);
ASSERT (sizeof(CMPLCTXT) <= pSda->sda_SizeNameXSpace);
if (((pSda->sda_Flags & SDA_NAMEXSPACE_IN_USE) == 0) &&
(sizeof(CMPLCTXT) <= pSda->sda_SizeNameXSpace))
{
pRetBuffer = pSda->sda_NameXSpace;
pSda->sda_Flags |= SDA_NAMEXSPACE_IN_USE;
}
else
{
pRetBuffer = AfpAllocNonPagedMemory(sizeof(CMPLCTXT));
}
RELEASE_SPIN_LOCK(&pSda->sda_Lock, OldIrql);
return ((PCMPLCTXT)(pRetBuffer));
}
VOID
AfpFreeCmplCtxtBuf(
IN PCMPLCTXT pCmplCtxt
)
{
KIRQL OldIrql;
PSDA pSda;
ASSERT(VALID_CTX(pCmplCtxt));
pSda = pCmplCtxt->cc_pSda;
ASSERT(VALID_SDA(pSda));
ACQUIRE_SPIN_LOCK(&pSda->sda_Lock, &OldIrql);
#if DBG
pCmplCtxt->Signature = 0x12341234;
pCmplCtxt->cc_Func = 0xff;
pCmplCtxt->cc_pSda = (PSDA)0x12341234;
pCmplCtxt->cc_pForkLock = (PFORKLOCK)0x12341234;
pCmplCtxt->cc_SavedStatus = 0x12341234;
pCmplCtxt->cc_ReqCount = 0x12341234;
pCmplCtxt->cc_Offst = 0x12341234;
#endif
if (((PBYTE)pCmplCtxt) == pSda->sda_NameXSpace)
{
ASSERT(pSda->sda_Flags & SDA_NAMEXSPACE_IN_USE);
pSda->sda_Flags &= ~SDA_NAMEXSPACE_IN_USE;
}
else
{
AfpFreeMemory((PBYTE)(pCmplCtxt));
}
RELEASE_SPIN_LOCK(&pSda->sda_Lock, OldIrql);
}