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.
1291 lines
35 KiB
1291 lines
35 KiB
/*
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fsp_fork.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the entry points for the AFP fork APIs queued to
|
|
the FSP. These are all callable from FSP Only.
|
|
|
|
Author:
|
|
|
|
Jameel Hyder (microsoft!jameelh)
|
|
|
|
|
|
Revision History:
|
|
25 Apr 1992 Initial Version
|
|
|
|
Notes: Tab stop: 4
|
|
--*/
|
|
|
|
#define FILENUM FILE_FSP_FORK
|
|
|
|
#include <afp.h>
|
|
#include <gendisp.h>
|
|
#include <fdparm.h>
|
|
#include <pathmap.h>
|
|
#include <forkio.h>
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
// #pragma alloc_text( PAGE, AfpFspDispOpenFork) // Do not page this out for perf.
|
|
// #pragma alloc_text( PAGE, AfpFspDispCloseFork) // Do not page this out for perf.
|
|
#pragma alloc_text( PAGE, AfpFspDispGetForkParms)
|
|
#pragma alloc_text( PAGE, AfpFspDispSetForkParms)
|
|
// #pragma alloc_text( PAGE, AfpFspDispRead) // Do not page this out for perf.
|
|
// #pragma alloc_text( PAGE, AfpFspDispWrite) // Do not page this out for perf.
|
|
#pragma alloc_text( PAGE, AfpFspDispByteRangeLock)
|
|
#pragma alloc_text( PAGE, AfpFspDispFlushFork)
|
|
#endif
|
|
|
|
/*** AfpFspDispOpenFork
|
|
*
|
|
* This is the worker routine for the AfpOpenFork API.
|
|
*
|
|
* The request packet is represented below.
|
|
*
|
|
* sda_AfpSubFunc BYTE Resource/Data Flag
|
|
* sda_ReqBlock PCONNDESC pConnDesc
|
|
* sda_ReqBlock DWORD ParentDirId
|
|
* sda_ReqBlock DWORD Bitmap
|
|
* sda_ReqBlock DWORD AccessMode
|
|
* sda_Name1 ANSI_STRING Path
|
|
*/
|
|
AFPSTATUS FASTCALL
|
|
AfpFspDispOpenFork(
|
|
IN PSDA pSda
|
|
)
|
|
{
|
|
DWORD Bitmap, BitmapI;
|
|
AFPSTATUS RetCode = AFP_ERR_NONE, Status = AFP_ERR_PARAM;
|
|
FILEDIRPARM FDParm;
|
|
PVOLDESC pVolDesc;
|
|
PATHMAPENTITY PME;
|
|
PCONNDESC pConnDesc;
|
|
POPENFORKENTRY pOpenForkEntry = NULL;
|
|
BOOLEAN Resource, CleanupLock = False;
|
|
BYTE OpenMode = 0;
|
|
UNICODE_STRING ParentPath;
|
|
struct _RequestPacket
|
|
{
|
|
PCONNDESC _pConnDesc;
|
|
DWORD _ParentDirId;
|
|
DWORD _Bitmap;
|
|
DWORD _AccessMode;
|
|
};
|
|
struct _ResponsePacket
|
|
{
|
|
BYTE __Bitmap[2];
|
|
BYTE __OForkRefNum[2];
|
|
};
|
|
#if DBG
|
|
static PBYTE OpenDeny[] = { "None", "Read", "Write", "ReadWrite" };
|
|
#endif
|
|
|
|
PAGED_CODE( );
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispOpenFork: Entered - Session %ld\n", pSda->sda_SessionId));
|
|
|
|
pConnDesc = pReqPkt->_pConnDesc;
|
|
|
|
ASSERT(VALID_CONNDESC(pConnDesc));
|
|
|
|
pVolDesc = pConnDesc->cds_pVolDesc;
|
|
|
|
ASSERT(VALID_VOLDESC(pVolDesc));
|
|
|
|
Bitmap = pReqPkt->_Bitmap;
|
|
|
|
Resource = ((pSda->sda_AfpSubFunc & FORK_RSRC) == FORK_RSRC) ? True : False;
|
|
|
|
if ((Resource && (Bitmap & FILE_BITMAP_DATALEN)) ||
|
|
(!Resource && (Bitmap & FILE_BITMAP_RESCLEN)))
|
|
{
|
|
return AFP_ERR_BITMAP;
|
|
}
|
|
|
|
do
|
|
{
|
|
AfpInitializeFDParms(&FDParm);
|
|
AfpInitializePME(&PME, 0, NULL);
|
|
|
|
// We will use the PME.pme_Handle for open fork handle
|
|
OpenMode = (BYTE)(pReqPkt->_AccessMode & FORK_OPEN_MASK);
|
|
|
|
// Validate volume type and open modes
|
|
if (!IS_CONN_NTFS(pConnDesc) && !IS_CONN_CD_HFS(pConnDesc))
|
|
{
|
|
// Resource fork only supported on NTFS and CD-HFS
|
|
if (Resource)
|
|
{
|
|
Status = AFP_ERR_OBJECT_NOT_FOUND;
|
|
break;
|
|
}
|
|
if (OpenMode & FORK_OPEN_WRITE)
|
|
{
|
|
Status = AFP_ERR_VOLUME_LOCKED;
|
|
break;
|
|
}
|
|
}
|
|
else if ((OpenMode & FORK_OPEN_WRITE) && IS_VOLUME_RO(pVolDesc))
|
|
{
|
|
Status = AFP_ERR_VOLUME_LOCKED;
|
|
break;
|
|
}
|
|
|
|
BitmapI = FILE_BITMAP_FILENUM |
|
|
FD_BITMAP_PARENT_DIRID |
|
|
FD_INTERNAL_BITMAP_RETURN_PMEPATHS;
|
|
|
|
// Encode the open access into the bitmap for pathmap
|
|
// to use when opening the fork.
|
|
if (Resource)
|
|
{
|
|
BitmapI |= FD_INTERNAL_BITMAP_OPENFORK_RESC;
|
|
}
|
|
if (OpenMode & FORK_OPEN_READ)
|
|
{
|
|
BitmapI |= FD_INTERNAL_BITMAP_OPENACCESS_READ;
|
|
}
|
|
if (OpenMode & FORK_OPEN_WRITE)
|
|
{
|
|
BitmapI |= FD_INTERNAL_BITMAP_OPENACCESS_WRITE;
|
|
}
|
|
|
|
// Encode the deny mode into the bitmap for pathmap
|
|
// to use when opening the fork.
|
|
BitmapI |= ((pReqPkt->_AccessMode >> FORK_DENY_SHIFT) &
|
|
FORK_DENY_MASK) <<
|
|
FD_INTERNAL_BITMAP_DENYMODE_SHIFT;
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispOpenFork: OpenMode %s, DenyMode %s\n",
|
|
OpenDeny[(pReqPkt->_AccessMode & FORK_OPEN_MASK)],
|
|
OpenDeny[(pReqPkt->_AccessMode >> FORK_DENY_SHIFT) & FORK_DENY_MASK]));
|
|
|
|
//
|
|
// Don't allow an FpExchangeFiles to occur while we are referencing
|
|
// the DFE FileId -- we want to make sure we put the right ID into
|
|
// the OpenForkDesc!!
|
|
//
|
|
AfpSwmrAcquireExclusive(&pVolDesc->vds_ExchangeFilesLock);
|
|
CleanupLock = True;
|
|
if ((Status = AfpMapAfpPathForLookup(pConnDesc,
|
|
pReqPkt->_ParentDirId,
|
|
&pSda->sda_Name1,
|
|
pSda->sda_PathType,
|
|
DFE_FILE,
|
|
Bitmap | BitmapI |
|
|
// Need these for drop folder
|
|
// checking
|
|
FILE_BITMAP_DATALEN | FILE_BITMAP_RESCLEN,
|
|
&PME,
|
|
&FDParm)) != AFP_ERR_NONE)
|
|
{
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispOpenFork: AfpMapAfpPathForLookup %lx\n", Status));
|
|
|
|
// If we got a DENY_CONFLICT error, then we still need the parameters
|
|
// Do an open for nothing with no deny modes to get the parameters.
|
|
PME.pme_Handle.fsh_FileHandle = NULL;
|
|
if (Status == AFP_ERR_DENY_CONFLICT)
|
|
{
|
|
AFPSTATUS xxStatus;
|
|
|
|
// Free up any path-buffer allocated
|
|
if (PME.pme_FullPath.Buffer != NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpFspDispOpenFork: (DenyConflict) Freeing path buffer %lx\n",
|
|
PME.pme_FullPath.Buffer));
|
|
AfpFreeMemory(PME.pme_FullPath.Buffer);
|
|
}
|
|
AfpInitializePME(&PME, 0, NULL);
|
|
|
|
BitmapI = FILE_BITMAP_FILENUM |
|
|
FD_BITMAP_PARENT_DIRID |
|
|
FD_INTERNAL_BITMAP_RETURN_PMEPATHS;
|
|
if (Resource)
|
|
{
|
|
BitmapI |= FD_INTERNAL_BITMAP_OPENFORK_RESC;
|
|
}
|
|
xxStatus = AfpMapAfpPathForLookup(pConnDesc,
|
|
pReqPkt->_ParentDirId,
|
|
&pSda->sda_Name1,
|
|
pSda->sda_PathType,
|
|
DFE_FILE,
|
|
Bitmap | BitmapI,
|
|
&PME,
|
|
&FDParm);
|
|
if (!NT_SUCCESS(xxStatus))
|
|
{
|
|
PME.pme_Handle.fsh_FileHandle = NULL;
|
|
Status = xxStatus;
|
|
break;
|
|
}
|
|
}
|
|
else break;
|
|
}
|
|
|
|
if (Status == AFP_ERR_NONE)
|
|
{
|
|
Status = AfpForkOpen(pSda,
|
|
pConnDesc,
|
|
&PME,
|
|
&FDParm,
|
|
pReqPkt->_AccessMode,
|
|
Resource,
|
|
&pOpenForkEntry,
|
|
&CleanupLock);
|
|
}
|
|
|
|
// At this point we have either successfully opened the fork,
|
|
// encountered a DENY_CONFLICT or some other error.
|
|
if ((Status != AFP_ERR_NONE) &&
|
|
(Status != AFP_ERR_DENY_CONFLICT))
|
|
break;
|
|
|
|
// Do drop folder sanity check if someone tries to open for Write only
|
|
if ((Status == AFP_ERR_NONE) &&
|
|
(OpenMode == FORK_OPEN_WRITE) &&
|
|
((FDParm._fdp_RescForkLen != 0) ||
|
|
(FDParm._fdp_DataForkLen != 0)))
|
|
{
|
|
ASSERT (VALID_OPENFORKENTRY(pOpenForkEntry));
|
|
|
|
// If either fork is not empty, and one of them is being
|
|
// opened for write, the user must also have READ access
|
|
// to the parent directory.
|
|
ParentPath = pOpenForkEntry->ofe_pOpenForkDesc->ofd_FilePath;
|
|
// adjust the length to not include the filename
|
|
ParentPath.Length -= pOpenForkEntry->ofe_pOpenForkDesc->ofd_FileName.Length;
|
|
if (ParentPath.Length > 0)
|
|
{
|
|
ParentPath.Length -= sizeof(WCHAR);
|
|
}
|
|
|
|
AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
|
|
Status = AfpCheckParentPermissions(pConnDesc,
|
|
FDParm._fdp_ParentId,
|
|
&ParentPath,
|
|
DIR_ACCESS_READ,
|
|
NULL,
|
|
NULL);
|
|
AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
|
|
//
|
|
// We are no longer referencing the FileId or path kept
|
|
// in the OpenForkDesc. Ok for FpExchangeFiles to resume.
|
|
//
|
|
AfpSwmrRelease(&pVolDesc->vds_ExchangeFilesLock);
|
|
CleanupLock = False;
|
|
|
|
if (Status != AFP_ERR_NONE)
|
|
{
|
|
AfpForkClose(pOpenForkEntry);
|
|
AfpForkDereference(pOpenForkEntry);
|
|
|
|
// set this to null so it wont be upgraded/deref'd
|
|
// in cleanup below
|
|
pOpenForkEntry = NULL;
|
|
|
|
// Set handle to null since it was closed in AfpForkClose
|
|
// and we wont want it to be closed in cleanup below
|
|
PME.pme_Handle.fsh_FileHandle = NULL;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AfpSwmrRelease(&pVolDesc->vds_ExchangeFilesLock);
|
|
CleanupLock = False;
|
|
}
|
|
|
|
if (RetCode == AFP_ERR_NONE)
|
|
{
|
|
pSda->sda_ReplySize = SIZE_RESPPKT +
|
|
EVENALIGN(AfpGetFileDirParmsReplyLength(&FDParm, Bitmap));
|
|
|
|
if ((RetCode = AfpAllocReplyBuf(pSda)) != AFP_ERR_NONE)
|
|
{
|
|
if (pOpenForkEntry != NULL)
|
|
AfpForkClose(pOpenForkEntry);
|
|
break;
|
|
}
|
|
AfpPackFileDirParms(&FDParm,
|
|
Bitmap,
|
|
pSda->sda_ReplyBuf + SIZE_RESPPKT);
|
|
PUTDWORD2SHORT(pRspPkt->__Bitmap, Bitmap);
|
|
PUTDWORD2SHORT(pRspPkt->__OForkRefNum, (pOpenForkEntry == NULL) ?
|
|
0 : pOpenForkEntry->ofe_OForkRefNum);
|
|
if (Status == AFP_ERR_NONE)
|
|
{
|
|
INTERLOCKED_INCREMENT_LONG(&pConnDesc->cds_cOpenForks);
|
|
}
|
|
}
|
|
else Status = RetCode;
|
|
} while (False);
|
|
|
|
|
|
if (CleanupLock)
|
|
{
|
|
AfpSwmrRelease(&pVolDesc->vds_ExchangeFilesLock);
|
|
}
|
|
|
|
// update the disk quota for this user
|
|
if (pVolDesc->vds_Flags & VOLUME_DISKQUOTA_ENABLED)
|
|
{
|
|
if (AfpConnectionReferenceByPointer(pConnDesc) != NULL)
|
|
{
|
|
afpUpdateDiskQuotaInfo(pConnDesc);
|
|
}
|
|
}
|
|
|
|
if (pOpenForkEntry != NULL)
|
|
{
|
|
if (Status == AFP_ERR_NONE)
|
|
AfpUpgradeHandle(&pOpenForkEntry->ofe_FileSysHandle);
|
|
AfpForkDereference(pOpenForkEntry);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (PME.pme_Handle.fsh_FileHandle != NULL)
|
|
AfpIoClose(&PME.pme_Handle);
|
|
}
|
|
|
|
if (PME.pme_FullPath.Buffer != NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpFspDispOpenFork: Freeing path buffer %lx\n",
|
|
PME.pme_FullPath.Buffer));
|
|
AfpFreeMemory(PME.pme_FullPath.Buffer);
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispOpenFork: Returning %ld\n", Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
/*** AfpFspDispCloseFork
|
|
*
|
|
* This is the worker routine for the AfpCloseFork API.
|
|
*
|
|
* The request packet is represented below.
|
|
*
|
|
* sda_ReqBlock POPENFORKENTRY pOpenForkEntry
|
|
*/
|
|
AFPSTATUS FASTCALL
|
|
AfpFspDispCloseFork(
|
|
IN PSDA pSda
|
|
)
|
|
{
|
|
struct _RequestPacket
|
|
{
|
|
POPENFORKENTRY _pOpenForkEntry;
|
|
};
|
|
|
|
PAGED_CODE( );
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispCloseFork: Entered - Session %ld, Fork %ld\n",
|
|
pSda->sda_SessionId, pReqPkt->_pOpenForkEntry->ofe_ForkId));
|
|
|
|
ASSERT(VALID_OPENFORKENTRY(pReqPkt->_pOpenForkEntry));
|
|
|
|
AfpForkClose(pReqPkt->_pOpenForkEntry);
|
|
|
|
return AFP_ERR_NONE;
|
|
}
|
|
|
|
|
|
|
|
/*** AfpFspDispGetForkParms
|
|
*
|
|
* This is the worker routine for the AfpGetForkParms API.
|
|
*
|
|
* The request packet is represented below.
|
|
*
|
|
* sda_ReqBlock POPENFORKENTRY pOpenForkEntry
|
|
* sda_ReqBlock DWORD Bitmap
|
|
*/
|
|
AFPSTATUS FASTCALL
|
|
AfpFspDispGetForkParms(
|
|
IN PSDA pSda
|
|
)
|
|
{
|
|
FILEDIRPARM FDParm;
|
|
DWORD Bitmap;
|
|
AFPSTATUS Status = AFP_ERR_PARAM;
|
|
struct _RequestPacket
|
|
{
|
|
POPENFORKENTRY _pOpenForkEntry;
|
|
DWORD _Bitmap;
|
|
};
|
|
struct _ResponsePacket
|
|
{
|
|
BYTE __Bitmap[2];
|
|
};
|
|
|
|
PAGED_CODE( );
|
|
|
|
ASSERT(VALID_OPENFORKENTRY(pReqPkt->_pOpenForkEntry));
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispGetForkParms: Entered Session %ld, Fork %ld\n",
|
|
pSda->sda_SessionId, pReqPkt->_pOpenForkEntry->ofe_ForkId));
|
|
|
|
Bitmap = pReqPkt->_Bitmap;
|
|
|
|
do
|
|
{
|
|
if ((RESCFORK(pReqPkt->_pOpenForkEntry) && (Bitmap & FILE_BITMAP_DATALEN)) ||
|
|
(DATAFORK(pReqPkt->_pOpenForkEntry) && (Bitmap & FILE_BITMAP_RESCLEN)))
|
|
{
|
|
Status = AFP_ERR_BITMAP;
|
|
break;
|
|
}
|
|
|
|
AfpInitializeFDParms(&FDParm);
|
|
|
|
// Optimize for the most common case.
|
|
if ((Bitmap & (FILE_BITMAP_DATALEN | FILE_BITMAP_RESCLEN)) != 0)
|
|
{
|
|
FORKOFFST ForkLength;
|
|
|
|
Status = AfpIoQuerySize(&pReqPkt->_pOpenForkEntry->ofe_FileSysHandle,
|
|
&ForkLength);
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
if (Bitmap & FILE_BITMAP_DATALEN)
|
|
FDParm._fdp_DataForkLen = ForkLength.LowPart;
|
|
else FDParm._fdp_RescForkLen = ForkLength.LowPart;
|
|
FDParm._fdp_Flags = 0; // Take out the directory flag
|
|
}
|
|
|
|
// If we need more stuff, go get it
|
|
if (Bitmap & ~(FILE_BITMAP_DATALEN | FILE_BITMAP_RESCLEN))
|
|
{
|
|
CONNDESC ConnDesc;
|
|
POPENFORKDESC pOpenForkDesc = pReqPkt->_pOpenForkEntry->ofe_pOpenForkDesc;
|
|
|
|
// Since the following call requires a pConnDesc and we do not
|
|
// really have one, manufacture it
|
|
ConnDesc.cds_pSda = pSda;
|
|
ConnDesc.cds_pVolDesc = pOpenForkDesc->ofd_pVolDesc;
|
|
|
|
// Don't let FpExchangeFiles come in while we are accessing
|
|
// the stored FileId and its corresponding DFE
|
|
AfpSwmrAcquireExclusive(&ConnDesc.cds_pVolDesc->vds_ExchangeFilesLock);
|
|
|
|
Status = AfpMapAfpIdForLookup(&ConnDesc,
|
|
pOpenForkDesc->ofd_FileNumber,
|
|
DFE_FILE,
|
|
Bitmap & ~(FILE_BITMAP_DATALEN | FILE_BITMAP_RESCLEN),
|
|
NULL,
|
|
&FDParm);
|
|
AfpSwmrRelease(&ConnDesc.cds_pVolDesc->vds_ExchangeFilesLock);
|
|
if (Status != AFP_ERR_NONE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Status == AFP_ERR_NONE)
|
|
{
|
|
pSda->sda_ReplySize = SIZE_RESPPKT +
|
|
EVENALIGN(AfpGetFileDirParmsReplyLength(&FDParm, Bitmap));
|
|
|
|
if ((Status = AfpAllocReplyBuf(pSda)) == AFP_ERR_NONE)
|
|
{
|
|
AfpPackFileDirParms(&FDParm, Bitmap, pSda->sda_ReplyBuf + SIZE_RESPPKT);
|
|
PUTDWORD2SHORT(&pRspPkt->__Bitmap, Bitmap);
|
|
}
|
|
}
|
|
} while (False);
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispGetForkParms: Returning %ld\n", Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
/*** AfpFspDispSetForkParms
|
|
*
|
|
* This is the worker routine for the AfpSetForkParms API.
|
|
* Only thing that can be set with this API is the fork length.
|
|
*
|
|
* The request packet is represented below.
|
|
*
|
|
* sda_ReqBlock POPENFORKENTRY pOpenForkEntry
|
|
* sda_ReqBlock DWORD Bitmap
|
|
* sda_ReqBlock LONG ForkLength
|
|
*
|
|
* LOCKS: vds_IdDbAccessLock (SWMR, Exclusive)
|
|
*/
|
|
AFPSTATUS FASTCALL
|
|
AfpFspDispSetForkParms(
|
|
IN PSDA pSda
|
|
)
|
|
{
|
|
AFPSTATUS Status;
|
|
DWORD Bitmap;
|
|
BOOLEAN SetSize = False;
|
|
PVOLDESC pVolDesc;
|
|
PCONNDESC pConnDesc;
|
|
struct _RequestPacket
|
|
{
|
|
POPENFORKENTRY _pOpenForkEntry;
|
|
DWORD _Bitmap;
|
|
LONG _ForkLength;
|
|
};
|
|
|
|
PAGED_CODE( );
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispSetForkParms: Entered Session %ld Fork %ld, Length %ld\n",
|
|
pSda->sda_SessionId, pReqPkt->_pOpenForkEntry->ofe_ForkId,
|
|
pReqPkt->_ForkLength));
|
|
|
|
ASSERT(VALID_OPENFORKENTRY(pReqPkt->_pOpenForkEntry));
|
|
|
|
Bitmap = pReqPkt->_Bitmap;
|
|
|
|
do
|
|
{
|
|
if ((RESCFORK(pReqPkt->_pOpenForkEntry) &&
|
|
(pReqPkt->_Bitmap & FILE_BITMAP_DATALEN)) ||
|
|
(DATAFORK(pReqPkt->_pOpenForkEntry) &&
|
|
(pReqPkt->_Bitmap & FILE_BITMAP_RESCLEN)))
|
|
{
|
|
Status = AFP_ERR_BITMAP;
|
|
break;
|
|
}
|
|
|
|
if (!(pReqPkt->_pOpenForkEntry->ofe_OpenMode & FORK_OPEN_WRITE))
|
|
{
|
|
Status = AFP_ERR_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
else if (pReqPkt->_ForkLength >= 0)
|
|
{
|
|
FORKSIZE OldSize;
|
|
|
|
// We don't try to catch our own changes for setting
|
|
// forksize because we don't know how many times the mac
|
|
// will set the size before closing the handle. Since
|
|
// a notification will only come in once the handle is
|
|
// closed, we may pile up a whole bunch of our changes
|
|
// in the list, but only one of them will get satisfied.
|
|
//
|
|
// We also do not want to attempt a change if the current length
|
|
// is same as what it is being set to (this happens a lot,
|
|
// unfortunately). Catch this red-handed.
|
|
|
|
Status = AfpIoQuerySize(&pReqPkt->_pOpenForkEntry->ofe_FileSysHandle,
|
|
&OldSize);
|
|
ASSERT (NT_SUCCESS(Status));
|
|
if (!(((LONG)(OldSize.LowPart) == pReqPkt->_ForkLength) &&
|
|
(OldSize.HighPart == 0)))
|
|
{
|
|
SetSize = True;
|
|
Status = AfpIoSetSize(&pReqPkt->_pOpenForkEntry->ofe_FileSysHandle,
|
|
pReqPkt->_ForkLength);
|
|
|
|
// update the disk quota for this user
|
|
pVolDesc = pReqPkt->_pOpenForkEntry->ofe_pConnDesc->cds_pVolDesc;
|
|
|
|
if (pVolDesc->vds_Flags & VOLUME_DISKQUOTA_ENABLED)
|
|
{
|
|
pConnDesc = pReqPkt->_pOpenForkEntry->ofe_pConnDesc;
|
|
if (AfpConnectionReferenceByPointer(pConnDesc) != NULL)
|
|
{
|
|
afpUpdateDiskQuotaInfo(pConnDesc);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the Dfe view of the fork length. Don't update the cached
|
|
// modified time even though it does change on NTFS immediately
|
|
// (LastWriteTime for setting length of data fork, ChangeTime for
|
|
// setting length of resource fork). We will let the
|
|
// change notify update the modified time when the handle is closed.
|
|
// Appleshare 3.0 and 4.0 do not reflect a changed modified time for
|
|
// changing fork length until the fork is closed (or flushed).
|
|
if (NT_SUCCESS(Status) && SetSize)
|
|
{
|
|
PVOLDESC pVolDesc;
|
|
PDFENTRY pDfEntry;
|
|
POPENFORKDESC pOpenForkDesc;
|
|
|
|
pOpenForkDesc = pReqPkt->_pOpenForkEntry->ofe_pOpenForkDesc;
|
|
pVolDesc = pOpenForkDesc->ofd_pVolDesc;
|
|
|
|
// Don't let FpExchangeFiles come in while we are accessing
|
|
// the stored FileId and its corresponding DFE
|
|
AfpSwmrAcquireExclusive(&pVolDesc->vds_ExchangeFilesLock);
|
|
|
|
AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
|
|
|
|
if ((pDfEntry = AfpFindDfEntryById(pVolDesc,
|
|
pOpenForkDesc->ofd_FileNumber,
|
|
DFE_FILE)) != NULL)
|
|
{
|
|
ASSERT (VALID_DFE(pDfEntry));
|
|
|
|
if (RESCFORK(pReqPkt->_pOpenForkEntry))
|
|
{
|
|
// If a FlushFork occurs on resource fork, it should
|
|
// update the modified time to the ChangeTime
|
|
pReqPkt->_pOpenForkEntry->ofe_Flags |= OPEN_FORK_WRITTEN;
|
|
|
|
pDfEntry->dfe_RescLen = pReqPkt->_ForkLength;
|
|
}
|
|
else
|
|
{
|
|
pDfEntry->dfe_DataLen = pReqPkt->_ForkLength;
|
|
}
|
|
}
|
|
|
|
AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
|
|
AfpSwmrRelease(&pVolDesc->vds_ExchangeFilesLock);
|
|
}
|
|
|
|
}
|
|
else Status = AFP_ERR_PARAM;
|
|
} while (False);
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispSetForkParms: Returning %ld\n", Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*** AfpFspDispRead
|
|
*
|
|
* This routine implements the AfpRead API.
|
|
*
|
|
* The request packet is represented below.
|
|
*
|
|
* sda_ReqBlock POPENFORKENTRY pOpenForkEntry
|
|
* sda_ReqBlock LONG Offset
|
|
* sda_ReqBlock LONG Size
|
|
* sda_ReqBlock DWORD NewLine Mask
|
|
* sda_ReqBlock DWORD NewLine Char
|
|
*/
|
|
AFPSTATUS FASTCALL
|
|
AfpFspDispRead(
|
|
IN PSDA pSda
|
|
)
|
|
{
|
|
AFPSTATUS Status=AFP_ERR_MISC;
|
|
FORKOFFST LOffset;
|
|
FORKSIZE LSize;
|
|
PFAST_IO_DISPATCH pFastIoDisp;
|
|
IO_STATUS_BLOCK IoStsBlk;
|
|
NTSTATUS NtStatus;
|
|
struct _RequestPacket
|
|
{
|
|
POPENFORKENTRY _pOpenForkEntry;
|
|
LONG _Offset;
|
|
LONG _Size;
|
|
DWORD _NlMask;
|
|
DWORD _NlChar;
|
|
};
|
|
|
|
PAGED_CODE( );
|
|
|
|
ASSERT(VALID_OPENFORKENTRY(pReqPkt->_pOpenForkEntry));
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispRead: Entered, Session %ld Fork %ld, Offset %ld, Size %ld\n",
|
|
pSda->sda_SessionId, pReqPkt->_pOpenForkEntry->ofe_ForkId,
|
|
pReqPkt->_Offset, pReqPkt->_Size));
|
|
|
|
if ((pReqPkt->_Size < 0) ||
|
|
(pReqPkt->_Offset < 0))
|
|
return AFP_ERR_PARAM;
|
|
|
|
if (!(pReqPkt->_pOpenForkEntry->ofe_OpenMode & FORK_OPEN_READ))
|
|
{
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_WARN,
|
|
("AfpFspDispRead: AfpRead on a Fork not opened for read\n"));
|
|
return AFP_ERR_ACCESS_DENIED;
|
|
}
|
|
|
|
if (pReqPkt->_Size >= 0)
|
|
{
|
|
if (pReqPkt->_Size > (LONG)pSda->sda_MaxWriteSize)
|
|
pReqPkt->_Size = (LONG)pSda->sda_MaxWriteSize;
|
|
|
|
Status = AFP_ERR_NONE;
|
|
|
|
if (pReqPkt->_Size > 0)
|
|
{
|
|
pSda->sda_ReadStatus = AFP_ERR_NONE;
|
|
LOffset.QuadPart = pReqPkt->_Offset;
|
|
LSize.QuadPart = pReqPkt->_Size;
|
|
|
|
if ((pReqPkt->_pOpenForkEntry->ofe_pOpenForkDesc->ofd_UseCount == 1) ||
|
|
(Status = AfpForkLockOperation( pSda,
|
|
pReqPkt->_pOpenForkEntry,
|
|
&LOffset,
|
|
&LSize,
|
|
IOCHECK,
|
|
False)) == AFP_ERR_NONE)
|
|
{
|
|
ASSERT (LSize.HighPart == 0);
|
|
ASSERT ((LONG)(LOffset.LowPart) == pReqPkt->_Offset);
|
|
if ((LONG)(LSize.LowPart) != pReqPkt->_Size)
|
|
pSda->sda_ReadStatus = AFP_ERR_LOCK;
|
|
|
|
Status = AFP_ERR_MISC;
|
|
|
|
pSda->sda_ReplySize = (USHORT)LSize.LowPart;
|
|
|
|
NtStatus = STATUS_UNSUCCESSFUL;
|
|
|
|
//
|
|
// if Read is large enough to justify going to the cache mgr, do it
|
|
//
|
|
if (pSda->sda_ReplySize >= CACHEMGR_READ_THRESHOLD)
|
|
{
|
|
NtStatus = AfpBorrowReadMdlFromCM(pSda);
|
|
}
|
|
|
|
//
|
|
// if we didn't go to the cache mgr, or if we did but cache mgr
|
|
// couldn't satisfy our request, continue with the read
|
|
//
|
|
if (NtStatus != STATUS_PENDING)
|
|
{
|
|
Status = AfpFspDispReadContinue(pSda);
|
|
}
|
|
|
|
//
|
|
// our attempt to get CacheMgr's mdl is pending. Return this
|
|
// error code so we don't complete the api as yet
|
|
//
|
|
else
|
|
{
|
|
Status = AFP_ERR_EXTENDED;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispRead: Returning %ld\n", Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
/*** AfpFspDispReadContinue
|
|
*
|
|
* This routine implements the AfpRead API if our attempt to get ReadMdl directly
|
|
* from the cache mgr fails.
|
|
*
|
|
*/
|
|
AFPSTATUS FASTCALL
|
|
AfpFspDispReadContinue(
|
|
IN PSDA pSda
|
|
)
|
|
{
|
|
AFPSTATUS Status=AFP_ERR_MISC;
|
|
FORKOFFST LOffset;
|
|
FORKSIZE LSize;
|
|
PFAST_IO_DISPATCH pFastIoDisp;
|
|
IO_STATUS_BLOCK IoStsBlk;
|
|
NTSTATUS NtStatus;
|
|
struct _RequestPacket
|
|
{
|
|
POPENFORKENTRY _pOpenForkEntry;
|
|
LONG _Offset;
|
|
LONG _Size;
|
|
DWORD _NlMask;
|
|
DWORD _NlChar;
|
|
};
|
|
|
|
PAGED_CODE( );
|
|
|
|
// allocate buffer for the read
|
|
AfpIOAllocBackFillBuffer(pSda);
|
|
|
|
if (pSda->sda_IOBuf != NULL)
|
|
{
|
|
#if DBG
|
|
AfpPutGuardSignature(pSda);
|
|
#endif
|
|
|
|
LOffset.QuadPart = pReqPkt->_Offset;
|
|
LSize.QuadPart = pReqPkt->_Size;
|
|
|
|
// Try the fast I/O path first. If that fails, call AfpIoForkRead
|
|
// to use the normal build-an-IRP path.
|
|
pFastIoDisp = pReqPkt->_pOpenForkEntry->ofe_pDeviceObject->DriverObject->FastIoDispatch;
|
|
if ((pFastIoDisp != NULL) &&
|
|
(pFastIoDisp->FastIoRead != NULL) &&
|
|
pFastIoDisp->FastIoRead(AfpGetRealFileObject(pReqPkt->_pOpenForkEntry->ofe_pFileObject),
|
|
&LOffset,
|
|
LSize.LowPart,
|
|
True,
|
|
pSda->sda_SessionId,
|
|
pSda->sda_ReplyBuf,
|
|
&IoStsBlk,
|
|
pReqPkt->_pOpenForkEntry->ofe_pDeviceObject))
|
|
{
|
|
LONG i, Size;
|
|
PBYTE pBuf;
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispRead: Fast Read Succeeded\n"));
|
|
|
|
#ifdef PROFILING
|
|
// The fast I/O path worked. Update statistics
|
|
INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoSucceeded));
|
|
#endif
|
|
INTERLOCKED_ADD_STATISTICS(&AfpServerStatistics.stat_DataRead,
|
|
(ULONG)IoStsBlk.Information,
|
|
&AfpStatisticsLock);
|
|
Status = pSda->sda_ReadStatus;
|
|
Size = (LONG)IoStsBlk.Information;
|
|
#if 0
|
|
// The following code does the right thing as per the spec but
|
|
// the finder seems to think otherwise.
|
|
if (Size < LSize.LowPart)
|
|
{
|
|
pSda->sda_ReadStatus = AFP_ERR_EOF;
|
|
}
|
|
#endif
|
|
if (Size == 0)
|
|
{
|
|
Status = AFP_ERR_EOF;
|
|
AfpIOFreeBackFillBuffer(pSda);
|
|
}
|
|
else if (pReqPkt->_NlMask != 0)
|
|
{
|
|
for (i = 0, pBuf = pSda->sda_ReplyBuf; i < Size; i++, pBuf++)
|
|
{
|
|
if ((*pBuf & (BYTE)(pReqPkt->_NlMask)) == (BYTE)(pReqPkt->_NlChar))
|
|
{
|
|
Size = ++i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pSda->sda_ReplySize = (USHORT)Size;
|
|
ASSERT((Status != AFP_ERR_EOF) || (pSda->sda_ReplySize == 0));
|
|
}
|
|
else
|
|
{
|
|
|
|
#ifdef PROFILING
|
|
INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoFailed));
|
|
#endif
|
|
Status = AfpIoForkRead(pSda,
|
|
pReqPkt->_pOpenForkEntry,
|
|
&LOffset,
|
|
LSize.LowPart,
|
|
(BYTE)pReqPkt->_NlMask,
|
|
(BYTE)pReqPkt->_NlChar);
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
/*** AfpFspDispWrite
|
|
*
|
|
* This routine implements the AfpWrite API.
|
|
*
|
|
* The request packet is represented below.
|
|
*
|
|
* sda_AfpSubFunc BYTE EndFlag
|
|
* sda_ReqBlock POPENFORKENTRY pOpenForkEntry
|
|
* sda_ReqBlock LONG Offset
|
|
* sda_ReqBlock LONG Size
|
|
*/
|
|
AFPSTATUS FASTCALL
|
|
AfpFspDispWrite(
|
|
IN PSDA pSda
|
|
)
|
|
{
|
|
FORKOFFST LOffset;
|
|
FORKSIZE LSize;
|
|
PFAST_IO_DISPATCH pFastIoDisp;
|
|
IO_STATUS_BLOCK IoStsBlk;
|
|
AFPSTATUS Status = AFP_ERR_NONE;
|
|
BOOLEAN EndFlag, FreeWriteBuf = True;
|
|
PVOLDESC pVolDesc;
|
|
PCONNDESC pConnDesc;
|
|
PREQUEST pRequest;
|
|
BOOLEAN fUpdateQuota=FALSE;
|
|
struct _RequestPacket
|
|
{
|
|
POPENFORKENTRY _pOpenForkEntry;
|
|
LONG _Offset;
|
|
LONG _Size;
|
|
};
|
|
struct _ResponsePacket
|
|
{
|
|
BYTE __RealOffset[4];
|
|
};
|
|
|
|
PAGED_CODE( );
|
|
|
|
EndFlag = (pSda->sda_AfpSubFunc & AFP_END_FLAG) ? True : False;
|
|
|
|
ASSERT(VALID_OPENFORKENTRY(pReqPkt->_pOpenForkEntry));
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispWrite: Entered, Session %ld, Fork %ld, Offset %ld, Size %ld %sRelative\n",
|
|
pSda->sda_SessionId, pReqPkt->_pOpenForkEntry->ofe_ForkId,
|
|
pReqPkt->_Offset, pReqPkt->_Size, EndFlag ? "End" : "Begin"));
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_WARN,
|
|
("AfpFspDispWrite: OForkRefNum = %d, pOpenForkEntry = %lx\n",
|
|
pReqPkt->_pOpenForkEntry->ofe_ForkId,pReqPkt->_pOpenForkEntry));
|
|
do
|
|
{
|
|
pRequest = pSda->sda_Request;
|
|
|
|
//
|
|
// if we got this Mdl from the Cache mgr, we must return it. Also, set
|
|
// the status code such that we con't complete the request as yet, but
|
|
// do so only afte cache mgr tells us that the write completed
|
|
//
|
|
if ((pRequest->rq_WriteMdl != NULL) &&
|
|
(pRequest->rq_CacheMgrContext != NULL))
|
|
{
|
|
PDELAYEDALLOC pDelAlloc;
|
|
|
|
pDelAlloc = pRequest->rq_CacheMgrContext;
|
|
|
|
pReqPkt->_pOpenForkEntry->ofe_Flags |= OPEN_FORK_WRITTEN;
|
|
|
|
ASSERT(pRequest->rq_WriteMdl == pDelAlloc->pMdl);
|
|
ASSERT(!(pDelAlloc->Flags & AFP_CACHEMDL_ALLOC_ERROR));
|
|
|
|
pRequest->rq_CacheMgrContext = NULL;
|
|
|
|
AfpReturnWriteMdlToCM(pDelAlloc);
|
|
|
|
FreeWriteBuf = False;
|
|
Status = AFP_ERR_EXTENDED;
|
|
break;
|
|
}
|
|
|
|
pConnDesc = pReqPkt->_pOpenForkEntry->ofe_pConnDesc;
|
|
|
|
ASSERT(VALID_CONNDESC(pConnDesc));
|
|
|
|
if ((pReqPkt->_Size < 0) ||
|
|
(!EndFlag && (pReqPkt->_Offset < 0)))
|
|
{
|
|
Status = AFP_ERR_PARAM;
|
|
break;
|
|
}
|
|
|
|
ASSERT((pReqPkt->_pOpenForkEntry->ofe_OpenMode & FORK_OPEN_WRITE) &&
|
|
((pReqPkt->_Size == 0) ||
|
|
((pReqPkt->_Size > 0) && (pSda->sda_IOBuf != NULL))));
|
|
|
|
if (pReqPkt->_Size > (LONG)pSda->sda_MaxWriteSize)
|
|
pReqPkt->_Size = (LONG)pSda->sda_MaxWriteSize;
|
|
|
|
// Check if we have a lock conflict and also convert the offset &
|
|
// size to absolute values if end relative
|
|
LOffset.QuadPart = pReqPkt->_Offset;
|
|
LSize.QuadPart = pReqPkt->_Size;
|
|
|
|
if (pReqPkt->_Size == 0)
|
|
{
|
|
if (!(EndFlag && (pReqPkt->_Offset < 0)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Skip lock-check if this is the only instance of the open fork and I/O is
|
|
// not end-relative.
|
|
if ((!EndFlag &&
|
|
(pReqPkt->_pOpenForkEntry->ofe_pOpenForkDesc->ofd_UseCount == 1)) ||
|
|
(Status = AfpForkLockOperation( pSda,
|
|
pReqPkt->_pOpenForkEntry,
|
|
&LOffset,
|
|
&LSize,
|
|
IOCHECK,
|
|
EndFlag)) == AFP_ERR_NONE)
|
|
{
|
|
ASSERT (LOffset.HighPart == 0);
|
|
if ((LONG)(LSize.LowPart) != pReqPkt->_Size)
|
|
{
|
|
Status = AFP_ERR_LOCK;
|
|
}
|
|
else if (LSize.LowPart > 0)
|
|
{
|
|
ASSERT(VALID_CONNDESC(pReqPkt->_pOpenForkEntry->ofe_pConnDesc));
|
|
|
|
// Assume write will succeed, set flag for FlushFork.
|
|
// This is a one way flag, i.e. only set, never cleared
|
|
pReqPkt->_pOpenForkEntry->ofe_Flags |= OPEN_FORK_WRITTEN;
|
|
|
|
// Try the fast I/O path first. If that fails, call AfpIoForkWrite
|
|
// to use the normal build-an-IRP path.
|
|
pFastIoDisp = pReqPkt->_pOpenForkEntry->ofe_pDeviceObject->DriverObject->FastIoDispatch;
|
|
if ((pFastIoDisp != NULL) &&
|
|
(pFastIoDisp->FastIoWrite != NULL) &&
|
|
pFastIoDisp->FastIoWrite(AfpGetRealFileObject(pReqPkt->_pOpenForkEntry->ofe_pFileObject),
|
|
&LOffset,
|
|
LSize.LowPart,
|
|
True,
|
|
pSda->sda_SessionId,
|
|
pSda->sda_IOBuf,
|
|
&IoStsBlk,
|
|
pReqPkt->_pOpenForkEntry->ofe_pDeviceObject))
|
|
{
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispWrite: Fast Write Succeeded\n"));
|
|
|
|
#ifdef PROFILING
|
|
// The fast I/O path worked. Update statistics
|
|
INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoSucceeded));
|
|
#endif
|
|
INTERLOCKED_ADD_STATISTICS(&AfpServerStatistics.stat_DataWritten,
|
|
(ULONG)IoStsBlk.Information,
|
|
&AfpStatisticsLock);
|
|
Status = AFP_ERR_NONE;
|
|
}
|
|
else
|
|
{
|
|
#ifdef PROFILING
|
|
INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoFailed));
|
|
#endif
|
|
if ((Status = AfpIoForkWrite(pSda,
|
|
pReqPkt->_pOpenForkEntry,
|
|
&LOffset,
|
|
LSize.LowPart)) == AFP_ERR_EXTENDED)
|
|
{
|
|
FreeWriteBuf = False;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
} while (False);
|
|
|
|
if (FreeWriteBuf)
|
|
{
|
|
AfpFreeIOBuffer(pSda);
|
|
}
|
|
|
|
if (Status == AFP_ERR_NONE)
|
|
{
|
|
pSda->sda_ReplySize = SIZE_RESPPKT;
|
|
if ((Status = AfpAllocReplyBuf(pSda)) == AFP_ERR_NONE)
|
|
{
|
|
PUTDWORD2DWORD(pRspPkt->__RealOffset, LOffset.LowPart+LSize.LowPart);
|
|
}
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispWrite: Returning %ld\n", Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
/*** AfpFspDispByteRangeLock
|
|
*
|
|
* This routine implements the AfpByteRangeLock API.
|
|
* We go ahead and call the file system to do the actual locking/unlocking.
|
|
*
|
|
* The request packet is represented below.
|
|
*
|
|
* sda_SubFunc BYTE Start/End Flag AND Lock/Unlock Flag
|
|
* sda_ReqBlock POPENFORKENTRY pOpenForkEntry
|
|
* sda_ReqBlock LONG Offset
|
|
* sda_ReqBlock LONG Length
|
|
*/
|
|
AFPSTATUS FASTCALL
|
|
AfpFspDispByteRangeLock(
|
|
IN PSDA pSda
|
|
)
|
|
{
|
|
BOOLEAN EndFlag;
|
|
LOCKOP Lock;
|
|
LONG Offset;
|
|
FORKOFFST LOffset;
|
|
FORKSIZE LSize;
|
|
AFPSTATUS Status = AFP_ERR_PARAM;
|
|
struct _RequestPacket
|
|
{
|
|
POPENFORKENTRY _pOpenForkEntry;
|
|
LONG _Offset;
|
|
LONG _Length;
|
|
};
|
|
struct _ResponsePacket
|
|
{
|
|
BYTE __RangeStart[4];
|
|
};
|
|
|
|
ASSERT (sizeof(struct _RequestPacket) <= (MAX_REQ_ENTRIES_PLUS_1)*sizeof(DWORD));
|
|
|
|
PAGED_CODE( );
|
|
|
|
Lock = (pSda->sda_AfpSubFunc & AFP_UNLOCK_FLAG) ? UNLOCK : LOCK;
|
|
EndFlag = (pSda->sda_AfpSubFunc & AFP_END_FLAG) ? True : False;
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispByteRangeLock: %sLock - Session %ld, Fork %ld Offset %ld Len %ld %sRelative\n",
|
|
(Lock == LOCK) ? "":"Un", pSda->sda_SessionId,
|
|
pReqPkt->_pOpenForkEntry->ofe_ForkId,
|
|
pReqPkt->_Offset, pReqPkt->_Length, EndFlag ? "End" : "Begin"));
|
|
|
|
ASSERT(VALID_OPENFORKENTRY(pReqPkt->_pOpenForkEntry));
|
|
|
|
if ((EndFlag && (Lock == UNLOCK)) ||
|
|
(pReqPkt->_Length == 0) ||
|
|
(!EndFlag && (pReqPkt->_Offset < 0))||
|
|
((pReqPkt->_Length < 0) && (pReqPkt->_Length != MAXULONG)))
|
|
NOTHING;
|
|
else
|
|
{
|
|
if (pReqPkt->_Length == MAXULONG)
|
|
pReqPkt->_Length = MAXLONG;
|
|
|
|
LOffset.QuadPart = Offset = pReqPkt->_Offset;
|
|
LSize.QuadPart = pReqPkt->_Length;
|
|
|
|
Status = AfpForkLockOperation(pSda,
|
|
pReqPkt->_pOpenForkEntry,
|
|
&LOffset,
|
|
&LSize,
|
|
Lock,
|
|
EndFlag);
|
|
|
|
if (Status == AFP_ERR_NONE)
|
|
{
|
|
ASSERT (LOffset.HighPart == 0);
|
|
ASSERT (EndFlag ||
|
|
((LONG)(LOffset.LowPart) == Offset));
|
|
pSda->sda_ReplySize = SIZE_RESPPKT;
|
|
if ((Status = AfpAllocReplyBuf(pSda)) == AFP_ERR_NONE)
|
|
PUTDWORD2DWORD(pRspPkt->__RangeStart, LOffset.LowPart);
|
|
}
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispByteRangeLock: Returning %ld\n", Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*** AfpFspDispFlushFork
|
|
*
|
|
* This routine implements the AfpFlushFork API. We don't actually do a
|
|
* real flush, we just query for the current forklength and modified time
|
|
* for this open fork handle and update our cached data. Note if 2
|
|
* different handles to the same file are flushed, we may end up with
|
|
* different information for each flush.
|
|
*
|
|
* The request packet is represented below.
|
|
*
|
|
* sda_ReqBlock POPENFORKENTRY pOpenForkEntry
|
|
*/
|
|
AFPSTATUS FASTCALL
|
|
AfpFspDispFlushFork(
|
|
IN PSDA pSda
|
|
)
|
|
{
|
|
FORKOFFST ForkLength;
|
|
DWORD Status;
|
|
PCONNDESC pConnDesc;
|
|
struct _RequestPacket
|
|
{
|
|
POPENFORKENTRY _pOpenForkEntry;
|
|
};
|
|
|
|
PAGED_CODE( );
|
|
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpFspDispFlushFork: Entered\n"));
|
|
|
|
ASSERT(VALID_OPENFORKENTRY(pReqPkt->_pOpenForkEntry));
|
|
|
|
do
|
|
{
|
|
Status = AfpIoQuerySize(&pReqPkt->_pOpenForkEntry->ofe_FileSysHandle,
|
|
&ForkLength);
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
PVOLDESC pVolDesc;
|
|
PDFENTRY pDfEntry;
|
|
POPENFORKDESC pOpenForkDesc;
|
|
|
|
pOpenForkDesc = pReqPkt->_pOpenForkEntry->ofe_pOpenForkDesc;
|
|
pVolDesc = pOpenForkDesc->ofd_pVolDesc;
|
|
|
|
ASSERT(IS_VOLUME_NTFS(pVolDesc));
|
|
|
|
// Don't let FpExchangeFiles come in while we are accessing
|
|
// the stored FileId and its corresponding DFE
|
|
AfpSwmrAcquireExclusive(&pVolDesc->vds_ExchangeFilesLock);
|
|
|
|
AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
|
|
|
|
if ((pDfEntry = AfpFindDfEntryById(pVolDesc,
|
|
pOpenForkDesc->ofd_FileNumber,
|
|
DFE_FILE)) != NULL)
|
|
{
|
|
ASSERT (VALID_DFE(pDfEntry));
|
|
|
|
AfpIoChangeNTModTime(&pReqPkt->_pOpenForkEntry->ofe_FileSysHandle,
|
|
&pDfEntry->dfe_LastModTime);
|
|
|
|
if (RESCFORK(pReqPkt->_pOpenForkEntry))
|
|
pDfEntry->dfe_RescLen = ForkLength.LowPart;
|
|
else pDfEntry->dfe_DataLen = ForkLength.LowPart;
|
|
}
|
|
|
|
AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
|
|
AfpSwmrRelease(&pVolDesc->vds_ExchangeFilesLock);
|
|
|
|
// update the disk quota for this user
|
|
if (pVolDesc->vds_Flags & VOLUME_DISKQUOTA_ENABLED)
|
|
{
|
|
pConnDesc = pReqPkt->_pOpenForkEntry->ofe_pConnDesc;
|
|
if (AfpConnectionReferenceByPointer(pConnDesc) != NULL)
|
|
{
|
|
afpUpdateDiskQuotaInfo(pConnDesc);
|
|
}
|
|
}
|
|
}
|
|
|
|
} while (False);
|
|
|
|
// Always return success
|
|
return AFP_ERR_NONE;
|
|
}
|
|
|
|
|
|
|