|
|
/*
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); }
|