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.
1788 lines
51 KiB
1788 lines
51 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
DConnect.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the routines by which the a server\share comes
|
|
up in a disconnected state.
|
|
|
|
Author:
|
|
|
|
Joe Linn [JoeLinn] 5-may-1997
|
|
|
|
Revision History:
|
|
|
|
Shishir Pardikar(shishirp) Various bug fixes Aug 1997 onwards
|
|
|
|
Shishir Pardikar(shishirp) Change Notification In disconnected state 27-aug-1998
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#pragma code_seg("PAGE")
|
|
|
|
#ifdef MRXSMB_BUILD_FOR_CSC_DCON
|
|
extern DEBUG_TRACE_CONTROLPOINT RX_DEBUG_TRACE_MRXSMBCSC;
|
|
#define Dbg (DEBUG_TRACE_MRXSMBCSC)
|
|
|
|
WCHAR wchSingleBackSlash = '\\';
|
|
UNICODE_STRING vRootString = {2,2,&wchSingleBackSlash}; // root string for change notification
|
|
|
|
WCHAR vtzOfflineVolume[] = L"Offline";
|
|
|
|
typedef struct tagNOTIFYEE_FOBX
|
|
{
|
|
LIST_ENTRY NextNotifyeeFobx;
|
|
MRX_FOBX *pFobx;
|
|
}
|
|
NOTIFYEE_FOBX, *PNOTIFYEE_FOBX;
|
|
|
|
PNOTIFYEE_FOBX
|
|
PIsFobxInTheList(
|
|
PLIST_ENTRY pNotifyeeFobxList,
|
|
PMRX_FOBX pFobx
|
|
);
|
|
|
|
BOOL
|
|
FCleanupAllNotifyees(
|
|
PNOTIFY_SYNC pNotifySync,
|
|
PLIST_ENTRY pDirNotifyList,
|
|
PLIST_ENTRY pNotifyeeFobxList,
|
|
PFAST_MUTEX pNotifyeeFobxListMutex
|
|
);
|
|
|
|
PMRX_SMB_FCB
|
|
MRxSmbCscRecoverMrxFcbFromFdb (
|
|
IN PFDB Fdb
|
|
);
|
|
|
|
NTSTATUS
|
|
MRxSmbCscNegotiateDisconnected(
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes the place of negotiating when the special tranport marker
|
|
has been detected in the negotiate routine.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RxDbgTrace(0,Dbg,("MRxSmbCscNegotiateDisconnected %08lx %08lx\n",
|
|
pServerEntry, pServerEntry->pTransport));
|
|
if (MRxSmbIsCscEnabledForDisconnected) {
|
|
|
|
pServerEntry->ServerStatus = STATUS_SUCCESS;
|
|
|
|
SmbCeUpdateServerEntryState(
|
|
pServerEntry,
|
|
SMBCEDB_ACTIVE);
|
|
|
|
//no need for anyting else!
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
Status = STATUS_HOST_UNREACHABLE;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbCscDisconnectedConnect (
|
|
IN OUT PSMB_CONSTRUCT_NETROOT_EXCHANGE pNetRootExchange
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes the place of connecting when we're doing disconnected
|
|
mode. what we do is to simulate what would happen if the exchange had come thru
|
|
ParseSmbHeader.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_PENDING;
|
|
BOOLEAN PostFinalize;
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
// PSMBCEDB_SESSION_ENTRY pSessionEntry;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
|
|
|
SMBCEDB_OBJECT_STATE SessionState;
|
|
SMBCEDB_OBJECT_STATE NetRootState;
|
|
|
|
PMRX_V_NET_ROOT VNetRoot;
|
|
PMRX_NET_ROOT NetRoot;
|
|
|
|
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
|
|
|
CSC_SHARE_HANDLE hShare;
|
|
CSC_SHADOW_HANDLE hRootDir,hShadow;
|
|
|
|
|
|
PRX_CONTEXT RxContext = pNetRootExchange->pCreateNetRootContext->RxContext;
|
|
|
|
VNetRoot = pNetRootExchange->SmbCeContext.pVNetRoot;
|
|
NetRoot = VNetRoot->pNetRoot;
|
|
|
|
pVNetRootContext = SmbCeGetAssociatedVNetRootContext(VNetRoot);
|
|
|
|
pServerEntry = SmbCeGetExchangeServerEntry(&pNetRootExchange->Exchange);
|
|
// pSessionEntry = SmbCeGetExchangeSessionEntry(&pNetRootExchange->Exchange);
|
|
pNetRootEntry = SmbCeGetExchangeNetRootEntry(&pNetRootExchange->Exchange);
|
|
|
|
if ((NetRoot->Type == NET_ROOT_DISK) ||
|
|
(NetRoot->Type == NET_ROOT_WILD)) {
|
|
|
|
ASSERT(MRxSmbIsCscEnabledForDisconnected);
|
|
|
|
RxDbgTrace(0,Dbg,("MRxSmbCscDisconnectedConnect %08lx %08lx\n",
|
|
pServerEntry, pServerEntry->pTransport));
|
|
|
|
// init netrootentry. This will be inited by the ObtainShareHandles call
|
|
pNetRootEntry->NetRoot.CscEnabled = TRUE; // assume csc is enabled
|
|
pNetRootEntry->NetRoot.CscShadowable = FALSE; // ACHTUNG, don't set this to TRUE
|
|
// otherwise a share will get created
|
|
// in disconnectd state
|
|
|
|
|
|
pNetRootEntry->NetRoot.NetRootType = NET_ROOT_DISK;
|
|
|
|
hShare = pNetRootEntry->NetRoot.sCscRootInfo.hShare;
|
|
if (hShare==0) {
|
|
NTSTATUS LocalStatus;
|
|
EnterShadowCrit();
|
|
LocalStatus = MRxSmbCscObtainShareHandles(
|
|
NetRoot->pNetRootName,
|
|
TRUE,
|
|
FALSE,
|
|
SmbCeGetAssociatedNetRootEntry(NetRoot)
|
|
);
|
|
if (LocalStatus != STATUS_SUCCESS) {
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscDisconnectedConnect no server handle -> %08xl %08lx\n",
|
|
RxContext,LocalStatus ));
|
|
} else {
|
|
hShare = pNetRootEntry->NetRoot.sCscRootInfo.hShare;
|
|
}
|
|
LeaveShadowCrit();
|
|
}
|
|
} else {
|
|
hShare = 0;
|
|
}
|
|
|
|
//ok, we have to do everything that parsesmbheader would have done
|
|
|
|
if (hShare==0) {
|
|
//can't find it in the table......just fail........
|
|
pNetRootExchange->Status = STATUS_BAD_NETWORK_NAME;
|
|
// SessionState = SMBCEDB_INVALID;
|
|
NetRootState = SMBCEDB_MARKED_FOR_DELETION;
|
|
} else {
|
|
pNetRootExchange->Status = STATUS_SUCCESS;
|
|
pNetRootExchange->SmbStatus = STATUS_SUCCESS;
|
|
|
|
|
|
// SessionState = SMBCEDB_ACTIVE;
|
|
|
|
//NETROOT STUFF
|
|
//some of the netroot stuff is earlier....before the lookup
|
|
NetRootState = SMBCEDB_ACTIVE;
|
|
}
|
|
|
|
#if 0
|
|
SmbCeUpdateSessionEntryState(
|
|
pSessionEntry,
|
|
SessionState);
|
|
#endif
|
|
|
|
SmbCeUpdateVNetRootContextState(
|
|
pVNetRootContext,
|
|
NetRootState);
|
|
|
|
SmbConstructNetRootExchangeFinalize(
|
|
&pNetRootExchange->Exchange,
|
|
&PostFinalize);
|
|
|
|
ASSERT(!PostFinalize);
|
|
return Status;
|
|
}
|
|
|
|
typedef struct _MRXSMBCSC_QUERYDIR_INFO {
|
|
WCHAR Pattern[2];
|
|
FINDSHADOW sFS;
|
|
ULONG uShadowStatus;
|
|
_WIN32_FIND_DATA Find32;
|
|
ULONG NumCallsSoFar;
|
|
BOOLEAN IsNonEmpty;
|
|
} MRXSMBCSC_QUERYDIR_INFO, *PMRXSMBCSC_QUERYDIR_INFO;
|
|
|
|
NTSTATUS
|
|
MRxSmbCscLoadNextDirectoryEntry(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PMRXSMBCSC_QUERYDIR_INFO QuerydirInfo,
|
|
OUT LPHSHADOW hShadowp
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
int iRet;
|
|
HSHADOW hTmp=0; //???
|
|
|
|
if (QuerydirInfo->NumCallsSoFar <= 1)
|
|
{
|
|
iRet = GetAncestorsHSHADOW(QuerydirInfo->sFS.hDir, &hTmp, NULL);
|
|
|
|
if (iRet >= SRET_OK)
|
|
{
|
|
iRet = GetShadowInfo(hTmp,
|
|
QuerydirInfo->sFS.hDir,
|
|
&QuerydirInfo->Find32,
|
|
&QuerydirInfo->uShadowStatus,
|
|
NULL
|
|
);
|
|
if (iRet >= SRET_OK)
|
|
{
|
|
|
|
if (QuerydirInfo->NumCallsSoFar == 0 )
|
|
{
|
|
QuerydirInfo->Find32.cFileName[0] = (WCHAR)'.';
|
|
QuerydirInfo->Find32.cFileName[1] = 0;
|
|
QuerydirInfo->Find32.cAlternateFileName[0] = (WCHAR)'.';
|
|
QuerydirInfo->Find32.cAlternateFileName[1] = 0;
|
|
}
|
|
else
|
|
{
|
|
QuerydirInfo->Find32.cFileName[0] = (WCHAR)'.';
|
|
QuerydirInfo->Find32.cFileName[1] = (WCHAR)'.';
|
|
QuerydirInfo->Find32.cFileName[2] = 0;
|
|
QuerydirInfo->Find32.cAlternateFileName[0] = (WCHAR)'.';
|
|
QuerydirInfo->Find32.cAlternateFileName[1] = (WCHAR)'.';
|
|
QuerydirInfo->Find32.cAlternateFileName[2] = 0;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
else if (QuerydirInfo->NumCallsSoFar == 2)
|
|
{
|
|
|
|
iRet = FindOpenHSHADOW(&QuerydirInfo->sFS,
|
|
&hTmp,
|
|
&QuerydirInfo->Find32,
|
|
&QuerydirInfo->uShadowStatus,
|
|
NULL);
|
|
} else {
|
|
iRet = FindNextHSHADOW(&QuerydirInfo->sFS,
|
|
&hTmp,
|
|
&QuerydirInfo->Find32,
|
|
&QuerydirInfo->uShadowStatus,
|
|
NULL);
|
|
}
|
|
|
|
|
|
if (iRet < SRET_OK)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
else
|
|
{
|
|
if (QuerydirInfo->NumCallsSoFar >= 2)
|
|
{
|
|
if (hTmp)
|
|
{
|
|
*hShadowp = hTmp;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_NO_MORE_FILES;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*hShadowp = hTmp;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
QuerydirInfo->NumCallsSoFar++;
|
|
QuerydirInfo->IsNonEmpty = (Status==STATUS_SUCCESS);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MRxSmbDCscQueryDirectory (
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
PMRX_SMB_FOBX smbFobx = MRxSmbGetFileObjectExtension(capFobx);
|
|
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
FILE_INFORMATION_CLASS FileInformationClass;
|
|
PBYTE pBuffer;
|
|
PULONG pLengthRemaining;
|
|
PFILE_DIRECTORY_INFORMATION pPreviousBuffer = NULL;
|
|
|
|
PMRXSMBCSC_QUERYDIR_INFO QuerydirInfo;
|
|
BOOLEAN EnteredCriticalSection = FALSE;
|
|
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry
|
|
= SmbCeGetAssociatedNetRootEntry(capFcb->pNetRoot);
|
|
BOOLEAN Disconnected;
|
|
|
|
ULONG EntriesReturned = 0;
|
|
BOOLEAN IsResume = FALSE;
|
|
|
|
Disconnected = MRxSmbCSCIsDisconnectedOpen(capFcb, smbSrvOpen);
|
|
|
|
if (!Disconnected) {
|
|
return (STATUS_CONNECTION_DISCONNECTED);
|
|
}
|
|
|
|
// if there is reumeinfo but it is not the one that CSC allocated,
|
|
// we want to fail this find.
|
|
|
|
if (smbFobx->Enumeration.ResumeInfo &&
|
|
!FlagOn(smbFobx->Enumeration.Flags,SMBFOBX_ENUMFLAG_IS_CSC_SEARCH))
|
|
{
|
|
return (STATUS_NO_MORE_FILES);
|
|
}
|
|
|
|
FileInformationClass = RxContext->Info.FileInformationClass;
|
|
pBuffer = RxContext->Info.Buffer;
|
|
pLengthRemaining = &RxContext->Info.LengthRemaining;
|
|
|
|
RxDbgTrace(+1, Dbg,
|
|
("MRxSmbDCscQueryDirectory entry(%08lx)...%08lx %08lx %08lx %08lx\n",
|
|
RxContext,
|
|
FileInformationClass,pBuffer,*pLengthRemaining,
|
|
smbFobx->Enumeration.ResumeInfo ));
|
|
|
|
if (smbFobx->Enumeration.ResumeInfo == NULL) {
|
|
PUNICODE_STRING Template = &capFobx->UnicodeQueryTemplate;
|
|
|
|
if (smbFobx->Enumeration.WildCardsFound = FsRtlDoesNameContainWildCards(Template)){
|
|
//we need an upcased template for
|
|
RtlUpcaseUnicodeString( Template, Template, FALSE );
|
|
}
|
|
|
|
//allocate and initialize the structure
|
|
QuerydirInfo = (PMRXSMBCSC_QUERYDIR_INFO)RxAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(MRXSMBCSC_QUERYDIR_INFO),
|
|
MRXSMB_DIRCTL_POOLTAG);
|
|
if (QuerydirInfo==NULL) {
|
|
RxDbgTrace(0, Dbg, (" --> Couldn't get the QuerydirInfo!\n"));
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FINALLY;
|
|
}
|
|
|
|
smbFobx->Enumeration.Flags |= SMBFOBX_ENUMFLAG_IS_CSC_SEARCH;
|
|
|
|
smbFobx->Enumeration.ResumeInfo = (PMRX_SMB_DIRECTORY_RESUME_INFO)QuerydirInfo;
|
|
RtlZeroMemory(QuerydirInfo,sizeof(*QuerydirInfo));
|
|
QuerydirInfo->Pattern[0] = L'*'; //[1] is already null
|
|
|
|
QuerydirInfo->sFS.hDir = smbFcb->hShadow;
|
|
|
|
QuerydirInfo->sFS.uSrchFlags = FLAG_FINDSHADOW_META
|
|
|FLAG_FINDSHADOW_ALLOW_NORMAL
|
|
|FLAG_FINDSHADOW_NEWSTYLE;
|
|
|
|
QuerydirInfo->sFS.uAttrib = 0xffffffff;
|
|
QuerydirInfo->sFS.lpPattern = &QuerydirInfo->Pattern[0];
|
|
QuerydirInfo->sFS.lpfnMMProc = FsobjMMProc;
|
|
|
|
} else {
|
|
QuerydirInfo = (PMRXSMBCSC_QUERYDIR_INFO)(smbFobx->Enumeration.ResumeInfo);
|
|
ASSERT(FlagOn(smbFobx->Enumeration.Flags,SMBFOBX_ENUMFLAG_IS_CSC_SEARCH));
|
|
IsResume = TRUE;
|
|
}
|
|
|
|
EnterShadowCrit();
|
|
EnteredCriticalSection = TRUE;
|
|
|
|
for (;;) {
|
|
NTSTATUS LoadStatus;
|
|
BOOLEAN FilterFailure;
|
|
UNICODE_STRING FileName,AlternateFileName;
|
|
ULONG SpaceNeeded;
|
|
PBYTE pRememberBuffer;
|
|
_WIN32_FIND_DATA *Find32 = &QuerydirInfo->Find32;
|
|
BOOLEAN BufferOverflow;
|
|
HSHADOW hShadow = 0;
|
|
|
|
if (!QuerydirInfo->IsNonEmpty) {
|
|
LoadStatus = MRxSmbCscLoadNextDirectoryEntry(RxContext,QuerydirInfo, &hShadow);
|
|
if (LoadStatus!=STATUS_SUCCESS) {
|
|
smbFobx->Enumeration.Flags &= ~SMBFOBX_ENUMFLAG_IS_CSC_SEARCH;
|
|
Status = (EntriesReturned==0)?STATUS_NO_MORE_FILES:STATUS_SUCCESS;
|
|
if (EntriesReturned > 0)
|
|
Status = STATUS_SUCCESS;
|
|
else
|
|
Status = (IsResume == TRUE) ? STATUS_NO_MORE_FILES : STATUS_NO_SUCH_FILE;
|
|
goto FINALLY;
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbDCscQueryDirectory (%08lx)...qdiryaya <%ws>\n",
|
|
RxContext,
|
|
&QuerydirInfo->Find32.cFileName[0] ));
|
|
RtlInitUnicodeString(&FileName,&QuerydirInfo->Find32.cFileName[0]);
|
|
RtlInitUnicodeString(&AlternateFileName,&QuerydirInfo->Find32.cAlternateFileName[0]);
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbDCscQueryDirectory (%08lx)...qdiryaya2 <%wZ><%wZ>|<%wZ>\n",
|
|
RxContext,
|
|
&FileName,&AlternateFileName,
|
|
&capFobx->UnicodeQueryTemplate));
|
|
|
|
FilterFailure = FALSE;
|
|
|
|
if (smbFobx->Enumeration.WildCardsFound ) {
|
|
try
|
|
{
|
|
|
|
FilterFailure = !FsRtlIsNameInExpression(
|
|
&capFobx->UnicodeQueryTemplate,
|
|
&FileName,
|
|
TRUE,
|
|
NULL );
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
FilterFailure = TRUE;
|
|
}
|
|
} else {
|
|
FilterFailure = !RtlEqualUnicodeString(
|
|
&capFobx->UnicodeQueryTemplate,
|
|
&FileName,
|
|
TRUE ); //case-insensitive
|
|
}
|
|
|
|
//check shortname
|
|
if (FilterFailure) {
|
|
if (smbFobx->Enumeration.WildCardsFound ) {
|
|
try
|
|
{
|
|
FilterFailure = !FsRtlIsNameInExpression(
|
|
&capFobx->UnicodeQueryTemplate,
|
|
&AlternateFileName,
|
|
TRUE,
|
|
NULL );
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
FilterFailure = TRUE;
|
|
}
|
|
} else {
|
|
FilterFailure = !RtlEqualUnicodeString(
|
|
&capFobx->UnicodeQueryTemplate,
|
|
&AlternateFileName,
|
|
TRUE ); //case-insensitive
|
|
}
|
|
}
|
|
|
|
if (FilterFailure) {
|
|
QuerydirInfo->IsNonEmpty = FALSE;
|
|
continue;
|
|
}
|
|
|
|
//OK, we have an entry we'd like to return.....see if it will fit.
|
|
|
|
pRememberBuffer = pBuffer;
|
|
if (EntriesReturned != 0) {
|
|
pBuffer = (PBYTE)QuadAlignPtr(pBuffer); //assume that this will fit
|
|
}
|
|
SpaceNeeded = smbFobx->Enumeration.FileNameOffset+FileName.Length;
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbDCscQueryDirectory (%08lx)...qdiryaya3 <%wZ><%wZ>|<%wZ> needs %08lx %08lx %08lx %08lx\n",
|
|
RxContext,
|
|
&FileName,&AlternateFileName,
|
|
&capFobx->UnicodeQueryTemplate,
|
|
pBuffer,SpaceNeeded,pRememberBuffer,*pLengthRemaining));
|
|
|
|
if (pBuffer+SpaceNeeded > pRememberBuffer+*pLengthRemaining) {
|
|
|
|
//buffer overflow on this enrty....
|
|
//pBuffer = pRememberBuffer; //rollback
|
|
Status = (EntriesReturned==0)?STATUS_BUFFER_OVERFLOW:STATUS_SUCCESS;
|
|
goto FINALLY;
|
|
|
|
} else {
|
|
PFILE_DIRECTORY_INFORMATION pThisBuffer = (PFILE_DIRECTORY_INFORMATION)pBuffer;
|
|
|
|
if (pPreviousBuffer != NULL) {
|
|
pPreviousBuffer->NextEntryOffset = (ULONG)(((PBYTE)pThisBuffer)-((PBYTE)pPreviousBuffer));
|
|
}
|
|
pPreviousBuffer = pThisBuffer;
|
|
RtlZeroMemory(pBuffer,smbFobx->Enumeration.FileNameOffset);
|
|
RtlCopyMemory(pBuffer+smbFobx->Enumeration.FileNameOffset,
|
|
FileName.Buffer,
|
|
FileName.Length);
|
|
*((PULONG)(pBuffer+smbFobx->Enumeration.FileNameLengthOffset)) = FileName.Length;
|
|
//hallucinate the record based on specific return type
|
|
switch (FileInformationClass) {
|
|
case FileNamesInformation:
|
|
break;
|
|
|
|
case FileBothDirectoryInformation:{
|
|
PFILE_BOTH_DIR_INFORMATION pThisBufferAsBOTH
|
|
= (PFILE_BOTH_DIR_INFORMATION)pThisBuffer;
|
|
|
|
//Do not copy more than size of shortname
|
|
pThisBufferAsBOTH->ShortNameLength = min(sizeof(pThisBufferAsBOTH->ShortName),(CCHAR)(AlternateFileName.Length));
|
|
RtlCopyMemory( &pThisBufferAsBOTH->ShortName[0],
|
|
AlternateFileName.Buffer,
|
|
pThisBufferAsBOTH->ShortNameLength );
|
|
}
|
|
//no break intentional
|
|
|
|
case FileDirectoryInformation:
|
|
case FileFullDirectoryInformation:
|
|
//just fill what we have...
|
|
pThisBuffer->FileAttributes = Find32->dwFileAttributes;
|
|
COPY_STRUCTFILETIME_TO_LARGEINTEGER(
|
|
pThisBuffer->CreationTime,
|
|
Find32->ftCreationTime);
|
|
COPY_STRUCTFILETIME_TO_LARGEINTEGER(
|
|
pThisBuffer->LastAccessTime,
|
|
Find32->ftLastAccessTime);
|
|
COPY_STRUCTFILETIME_TO_LARGEINTEGER(
|
|
pThisBuffer->LastWriteTime,
|
|
Find32->ftLastWriteTime);
|
|
|
|
pThisBuffer->EndOfFile.HighPart = Find32->nFileSizeHigh;
|
|
pThisBuffer->EndOfFile.LowPart = Find32->nFileSizeLow;
|
|
pThisBuffer->AllocationSize = pThisBuffer->EndOfFile;
|
|
|
|
if (IsLeaf(hShadow)) {
|
|
PFDB pFDB = MRxSmbCscFindFdbFromHShadow(hShadow);
|
|
if (pFDB != NULL) {
|
|
PMRX_SMB_FCB smbFcb = MRxSmbCscRecoverMrxFcbFromFdb(pFDB);
|
|
PMRX_FCB mrxFcb = smbFcb->ContainingFcb;
|
|
|
|
pThisBuffer->EndOfFile = mrxFcb->Header.FileSize;
|
|
pThisBuffer->AllocationSize = pThisBuffer->EndOfFile;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbCoreFileSearch: Invalid FS information class\n"));
|
|
ASSERT(!"this can't happen");
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto FINALLY;
|
|
}
|
|
pBuffer += SpaceNeeded;
|
|
*pLengthRemaining -= (ULONG)(pBuffer-pRememberBuffer);
|
|
EntriesReturned++;
|
|
QuerydirInfo->IsNonEmpty = FALSE;
|
|
if (RxContext->QueryDirectory.ReturnSingleEntry) {
|
|
Status = STATUS_SUCCESS;
|
|
goto FINALLY;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
FINALLY:
|
|
|
|
if (EnteredCriticalSection) {
|
|
LeaveShadowCrit();
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbDCscQueryDirectory exit-> %08lx %08lx\n", RxContext, Status ));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbDCscGetFsSizeInfo (
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine routes a fs size query to the underlying filesystem. It does
|
|
this by opening a handle to the priorityqueue inode and uses this to route
|
|
call. CODE.IMPROVEMENT.ASHAMED if the system is converted to use relative
|
|
opens, then we should just use the relative open fileobject for this passthru.
|
|
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
FS_INFORMATION_CLASS FsInformationClass;
|
|
PBYTE pBuffer;
|
|
PULONG pLengthRemaining;
|
|
ULONG PassedInLength,ReturnedLength;
|
|
|
|
BOOLEAN CriticalSectionEntered = FALSE;
|
|
|
|
PNT5CSC_MINIFILEOBJECT MiniFileObject;
|
|
|
|
FsInformationClass = RxContext->Info.FileInformationClass;
|
|
pBuffer = RxContext->Info.Buffer;
|
|
pLengthRemaining = &RxContext->Info.LengthRemaining;
|
|
|
|
RxDbgTrace(+1, Dbg,
|
|
("MRxSmbDCscGetFsSizeInfo entry(%08lx)...%08lx %08lx %08lx\n",
|
|
RxContext,
|
|
FsInformationClass, pBuffer, *pLengthRemaining ));
|
|
|
|
EnterShadowCrit();
|
|
CriticalSectionEntered = TRUE;
|
|
|
|
|
|
OpenFileHSHADOW(ULID_PQ,
|
|
0,
|
|
0,
|
|
(CSCHFILE *)(&MiniFileObject)
|
|
);
|
|
|
|
if (MiniFileObject == NULL) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto FINALLY;
|
|
}
|
|
|
|
PassedInLength = *pLengthRemaining;
|
|
//DbgBreakPoint();
|
|
|
|
Status = Nt5CscXxxInformation(
|
|
(PCHAR)IRP_MJ_QUERY_VOLUME_INFORMATION,
|
|
MiniFileObject,
|
|
FsInformationClass,
|
|
PassedInLength,
|
|
pBuffer,
|
|
&ReturnedLength
|
|
);
|
|
|
|
if (!NT_ERROR(Status)) {
|
|
*pLengthRemaining -= ReturnedLength;
|
|
}
|
|
|
|
|
|
|
|
FINALLY:
|
|
if (MiniFileObject != NULL) {
|
|
CloseFileLocal((CSCHFILE)(MiniFileObject));
|
|
}
|
|
|
|
if (CriticalSectionEntered) {
|
|
LeaveShadowCrit();
|
|
}
|
|
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbDCscGetFsSizeInfo exit-> %08lx %08lx %08lx %08lx\n",
|
|
RxContext, Status, ReturnedLength, *pLengthRemaining ));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbDCscFlush (
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine just performs a flush in disconnected mode. since we don't send
|
|
a flush in disconnected mode and since we dont need to flush the shadow
|
|
(since we use all unbuffered writes) we can just return SUCCESS.
|
|
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
RxCaptureFcb;
|
|
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);
|
|
|
|
if (!SmbCeIsServerInDisconnectedMode(pServerEntry)) {
|
|
return(STATUS_MORE_PROCESSING_REQUIRED);
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbDCscQueryVolumeInformation (
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine just performs a queryvolume in disconnected mode. it draws on
|
|
the same philosphy as downlevel queryvolume.
|
|
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);
|
|
|
|
FS_INFORMATION_CLASS FsInformationClass;
|
|
PBYTE pBuffer;
|
|
PULONG pLengthRemaining;
|
|
ULONG LengthUsed;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry
|
|
= SmbCeGetAssociatedNetRootEntry(capFcb->pNetRoot);
|
|
BOOLEAN Disconnected;
|
|
PSMBCE_NET_ROOT psmbNetRoot = &pNetRootEntry->NetRoot;
|
|
|
|
Disconnected = SmbCeIsServerInDisconnectedMode(pServerEntry);
|
|
|
|
if (!Disconnected) {
|
|
return(STATUS_CONNECTION_DISCONNECTED);
|
|
}
|
|
|
|
FsInformationClass = RxContext->Info.FsInformationClass;
|
|
pBuffer = RxContext->Info.Buffer;
|
|
pLengthRemaining = &RxContext->Info.LengthRemaining;
|
|
|
|
RxDbgTrace(+1, Dbg,
|
|
("MRxSmbDCscQueryVolumeInformation entry(%08lx)...%08lx %08lx %08lx bytes @ %08lx %08lx %08lx\n",
|
|
RxContext,
|
|
FsInformationClass, pBuffer, *pLengthRemaining,
|
|
smbSrvOpen->hfShadow ));
|
|
|
|
switch (FsInformationClass) {
|
|
case FileFsAttributeInformation:
|
|
//here, cause it to return the data from our tableentry in downlvli.c
|
|
if (psmbNetRoot->FileSystemNameLength == 0) {
|
|
//set our name
|
|
psmbNetRoot->FileSystemNameLength = 14;
|
|
psmbNetRoot->FileSystemName[0] = '*';
|
|
psmbNetRoot->FileSystemName[1] = 'N';
|
|
psmbNetRoot->FileSystemName[2] = 'T';
|
|
psmbNetRoot->FileSystemName[3] = '5';
|
|
psmbNetRoot->FileSystemName[4] = 'C';
|
|
psmbNetRoot->FileSystemName[5] = 'S';
|
|
psmbNetRoot->FileSystemName[6] = 'C';
|
|
}
|
|
psmbNetRoot->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK;
|
|
psmbNetRoot->MaximumComponentNameLength = 255;
|
|
Status = MRxSmbGetFsAttributesFromNetRoot(RxContext);
|
|
goto FINALLY;
|
|
//no break needed because of gotofinally
|
|
|
|
case FileFsVolumeInformation: {
|
|
PFILE_FS_VOLUME_INFORMATION FsVolInfo = (PFILE_FS_VOLUME_INFORMATION)pBuffer;
|
|
|
|
ASSERT(*pLengthRemaining >= sizeof(FILE_FS_VOLUME_INFORMATION));
|
|
//here, we have no reliable information....return zeros
|
|
FsVolInfo->VolumeCreationTime.QuadPart = 0;
|
|
FsVolInfo->VolumeSerialNumber = 0;
|
|
FsVolInfo->VolumeLabelLength = 0;
|
|
FsVolInfo->SupportsObjects = FALSE;
|
|
|
|
// calculate the size of the VolumeLabel we have and put it in a temp var
|
|
LengthUsed = *pLengthRemaining - FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION,VolumeLabel[0]);
|
|
|
|
LengthUsed = min(LengthUsed, sizeof(vtzOfflineVolume)-2);
|
|
|
|
memcpy(FsVolInfo->VolumeLabel, vtzOfflineVolume, LengthUsed);
|
|
FsVolInfo->VolumeLabelLength = LengthUsed;
|
|
*pLengthRemaining -= (FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION,VolumeLabel[0])+LengthUsed);
|
|
}
|
|
goto FINALLY;
|
|
//no break needed because of gotofinally
|
|
|
|
case FileFsSizeInformation: case FileFsFullSizeInformation:
|
|
//here, we route to the underlying filesystem
|
|
Status = MRxSmbDCscGetFsSizeInfo(RxContext);
|
|
goto FINALLY;
|
|
//no break needed because of gotofinally
|
|
|
|
case FileFsDeviceInformation:
|
|
ASSERT(!"this should have been turned away");
|
|
//no break;
|
|
default:
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
goto FINALLY;
|
|
}
|
|
|
|
|
|
FINALLY:
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbDCscQueryVolumeInformation exit(%08lx %08lx)...%08lx %08lx %08lx bytes @ %08lx\n",
|
|
RxContext, Status,
|
|
FsInformationClass, pBuffer, *pLengthRemaining,
|
|
smbSrvOpen->hfShadow ));
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbDCscQueryFileInfo (
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine just performs a queryfileinfo in disconnected mode. because info buffering
|
|
is enabled, it should never call down here! so we can just return STATUS_DISCONNECTED
|
|
all the time!.
|
|
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
PSMBPSE_FILEINFO_BUNDLE pFileInfo = &smbSrvOpen->FileInfo;
|
|
|
|
FILE_INFORMATION_CLASS FileInformationClass;
|
|
PBYTE pBuffer;
|
|
PULONG pLengthRemaining;
|
|
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry
|
|
= SmbCeGetAssociatedNetRootEntry(capFcb->pNetRoot);
|
|
BOOLEAN Disconnected;
|
|
PSMBCE_NET_ROOT psmbNetRoot = &pNetRootEntry->NetRoot;
|
|
|
|
Disconnected = MRxSmbCSCIsDisconnectedOpen(capFcb, smbSrvOpen);
|
|
|
|
if (!Disconnected) {
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);
|
|
|
|
if (pServerEntry->Header.State == SMBCEDB_ACTIVE) {
|
|
return(STATUS_MORE_PROCESSING_REQUIRED);
|
|
} else {
|
|
return(STATUS_CONNECTION_DISCONNECTED);
|
|
}
|
|
}
|
|
|
|
|
|
FileInformationClass = RxContext->Info.FileInformationClass;
|
|
pBuffer = RxContext->Info.Buffer;
|
|
pLengthRemaining = &RxContext->Info.LengthRemaining;
|
|
|
|
RxDbgTrace(+1, Dbg,
|
|
("MRxSmbDCscQueryFileInfo entry(%08lx)...%08lx %08lx %08lx bytes @ %08lx %08lx %08lx\n",
|
|
RxContext,
|
|
FileInformationClass, pBuffer, *pLengthRemaining,
|
|
smbSrvOpen->hfShadow ));
|
|
|
|
switch (FileInformationClass) {
|
|
case FileBasicInformation:
|
|
{
|
|
PFILE_BASIC_INFORMATION Buffer = (PFILE_BASIC_INFORMATION)pBuffer;
|
|
|
|
switch (NodeType(capFcb)) {
|
|
case RDBSS_NTC_STORAGE_TYPE_DIRECTORY:
|
|
case RDBSS_NTC_STORAGE_TYPE_FILE:
|
|
|
|
//copy in all the stuff that we know....it may be enough.....
|
|
|
|
Buffer->ChangeTime = pFileInfo->Basic.ChangeTime;
|
|
Buffer->CreationTime = pFileInfo->Basic.CreationTime;
|
|
Buffer->LastWriteTime = pFileInfo->Basic.LastWriteTime;
|
|
Buffer->LastAccessTime = pFileInfo->Basic.LastAccessTime;
|
|
Buffer->FileAttributes = pFileInfo->Basic.FileAttributes;
|
|
|
|
if (FlagOn( capFcb->FcbState, FCB_STATE_TEMPORARY )) {
|
|
SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
|
|
}
|
|
|
|
RxContext->Info.LengthRemaining -= sizeof(FILE_BASIC_INFORMATION);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
break;
|
|
case FileStandardInformation:
|
|
{
|
|
PFILE_STANDARD_INFORMATION Buffer = (PFILE_STANDARD_INFORMATION)pBuffer;
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
|
|
switch (NodeType(capFcb)) {
|
|
case RDBSS_NTC_STORAGE_TYPE_DIRECTORY:
|
|
Buffer->Directory = TRUE;
|
|
RxContext->Info.LengthRemaining -= sizeof(FILE_STANDARD_INFORMATION);
|
|
break;
|
|
case RDBSS_NTC_STORAGE_TYPE_FILE:
|
|
memset(Buffer, 0, sizeof(FILE_STANDARD_INFORMATION));
|
|
Buffer->AllocationSize = smbFcb->NewShadowSize;
|
|
Buffer->EndOfFile = smbFcb->NewShadowSize;
|
|
RxContext->Info.LengthRemaining -= sizeof(FILE_STANDARD_INFORMATION);
|
|
break;
|
|
default:
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
break;
|
|
case FileEaInformation:
|
|
{
|
|
PFILE_EA_INFORMATION EaBuffer = (PFILE_EA_INFORMATION)pBuffer;
|
|
|
|
EaBuffer->EaSize = 0;
|
|
RxContext->Info.LengthRemaining -= sizeof(FILE_EA_INFORMATION);
|
|
}
|
|
break;
|
|
default:
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg,
|
|
("MRxSmbDCscQueryFileInfo exit(%08lx %08lx)...%08lx %08lx %08lx bytes @ %08lx\n",
|
|
RxContext, Status,
|
|
FileInformationClass, pBuffer, *pLengthRemaining,
|
|
smbSrvOpen->hfShadow ));
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbDCscSetFileInfo (
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine just performs a querydirectory in disconnected mode. it draws on
|
|
the same philosphy as downlevel querydirectory.
|
|
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
FILE_INFORMATION_CLASS FileInformationClass;
|
|
PBYTE pBuffer;
|
|
PULONG pLengthRemaining;
|
|
ULONG DummyReturnedLength;
|
|
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry
|
|
= SmbCeGetAssociatedNetRootEntry(capFcb->pNetRoot);
|
|
BOOLEAN Disconnected;
|
|
PSMBCE_NET_ROOT psmbNetRoot = &pNetRootEntry->NetRoot;
|
|
PNT5CSC_MINIFILEOBJECT MiniFileObject = (PNT5CSC_MINIFILEOBJECT)(smbSrvOpen->hfShadow);
|
|
|
|
Disconnected = MRxSmbCSCIsDisconnectedOpen(capFcb, smbSrvOpen);
|
|
|
|
if (!Disconnected) {
|
|
return(STATUS_CONNECTION_DISCONNECTED);
|
|
}
|
|
|
|
FileInformationClass = RxContext->Info.FileInformationClass;
|
|
pBuffer = RxContext->Info.Buffer;
|
|
pLengthRemaining = &RxContext->Info.LengthRemaining;
|
|
|
|
RxDbgTrace(+1, Dbg,
|
|
("MRxSmbDCscSetFileInfo entry(%08lx)...%08lx %08lx %08lx bytes @ %08lx %08lx %08lx\n",
|
|
RxContext,
|
|
FileInformationClass, pBuffer, *pLengthRemaining,
|
|
smbSrvOpen->hfShadow ));
|
|
|
|
switch (FileInformationClass) {
|
|
case FileEndOfFileInformation:
|
|
case FileAllocationInformation:
|
|
case FileBasicInformation:
|
|
case FileDispositionInformation:
|
|
MRxSmbCscSetFileInfoEpilogue(RxContext,&Status);
|
|
goto FINALLY;
|
|
|
|
case FileRenameInformation:
|
|
MRxSmbCscRenameEpilogue(RxContext,&Status);
|
|
goto FINALLY;
|
|
|
|
default:
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
goto FINALLY;
|
|
}
|
|
|
|
FINALLY:
|
|
|
|
RxDbgTrace(-1, Dbg,
|
|
("MRxSmbDCscSetFileInfo exit(%08lx %08lx)...%08lx %08lx %08lx bytes @ %08lx\n",
|
|
RxContext, Status,
|
|
FileInformationClass, pBuffer, *pLengthRemaining,
|
|
smbSrvOpen->hfShadow ));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MRxSmbDCscFsCtl(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine just performs a querydirectory in disconnected mode. it draws on
|
|
the same philosphy as downlevel querydirectory.
|
|
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbDCscIsValidDirectory(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN PUNICODE_STRING DirectoryName)
|
|
{
|
|
NTSTATUS Status;
|
|
MRX_SMB_FCB CscSmbFcb;
|
|
WIN32_FIND_DATA Find32;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
|
|
|
pNetRootEntry = SmbCeGetAssociatedNetRootEntry(RxContext->Create.pNetRoot);
|
|
memset(&(CscSmbFcb.MinimalCscSmbFcb), 0, sizeof(CscSmbFcb.MinimalCscSmbFcb));
|
|
|
|
if (!pNetRootEntry->NetRoot.sCscRootInfo.hRootDir)
|
|
{
|
|
return STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
EnterShadowCrit();
|
|
|
|
Status = MRxSmbCscCreateShadowFromPath(
|
|
DirectoryName,
|
|
&(pNetRootEntry->NetRoot.sCscRootInfo),
|
|
&Find32,
|
|
NULL,
|
|
(CREATESHADOW_CONTROL_NOCREATE |
|
|
CREATESHADOW_CONTROL_NOREVERSELOOKUP),
|
|
&(CscSmbFcb.MinimalCscSmbFcb),
|
|
RxContext,
|
|
TRUE,
|
|
NULL);
|
|
|
|
if ((Status != STATUS_SUCCESS) ||
|
|
!(Find32.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
Status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
LeaveShadowCrit();
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbCscNotifyChangeDirectory(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets a directory notification for a directory in disconnected state
|
|
The smbmini makes this call when it notices that the serverentry is in disconnected state
|
|
All change notifications are maintained in a list, so that when the server is being transitioned
|
|
from offline to online, we can complete all of them.
|
|
|
|
We use FOBX as the unique key for change notifications.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
If successfully registered a change notify, it returns STATUS_PENDING. It also
|
|
hijacks the IRP and reduces the refcount on the RxContext, so that the wrapper
|
|
will delete this rxcontext.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext;
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
BOOLEAN FcbAcquired = FALSE;
|
|
ULONG CompletionFilter;
|
|
BOOLEAN WatchTree;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
|
PUNICODE_STRING pDirName=NULL;
|
|
PNOTIFYEE_FOBX pNF = NULL;
|
|
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);
|
|
pNetRootEntry = SmbCeGetAssociatedNetRootEntry(capFcb->pNetRoot);
|
|
|
|
if (!RxIsFcbAcquiredExclusive(capFcb)) {
|
|
ASSERT(!RxIsFcbAcquiredShared(capFcb));
|
|
Status = RxAcquireExclusiveFcbResourceInMRx( capFcb );
|
|
|
|
FcbAcquired = (Status == STATUS_SUCCESS);
|
|
}
|
|
|
|
CompletionFilter = pLowIoContext->ParamsFor.NotifyChangeDirectory.CompletionFilter;
|
|
WatchTree = pLowIoContext->ParamsFor.NotifyChangeDirectory.WatchTree;
|
|
|
|
if (!(((PFCB)capFcb)->PrivateAlreadyPrefixedName).Length)
|
|
{
|
|
pDirName = &vRootString;
|
|
}
|
|
else
|
|
{
|
|
pDirName = &(((PFCB)capFcb)->PrivateAlreadyPrefixedName);
|
|
}
|
|
//
|
|
// Call the Fsrtl package to process the request.
|
|
//
|
|
|
|
pNF = AllocMem(sizeof(NOTIFYEE_FOBX));
|
|
|
|
if (!pNF)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
pNF->pFobx = capFobx;
|
|
|
|
SmbCeLog(("chngnotify fobx=%x\n", capFobx));
|
|
SmbLog(LOG,
|
|
MRxSmbCscNotifyChangeDirectory,
|
|
LOGPTR(capFobx));
|
|
|
|
// DbgPrint("chngnotify %wZ fobx=%x NR=%x DirList=%x\n", pDirName, capFobx, pNetRootEntry, &pNetRootEntry->NetRoot.DirNotifyList);
|
|
FsRtlNotifyFullChangeDirectory( pNetRootEntry->NetRoot.pNotifySync,
|
|
&pNetRootEntry->NetRoot.DirNotifyList,
|
|
capFobx,
|
|
(PSTRING)pDirName,
|
|
WatchTree,
|
|
TRUE,
|
|
CompletionFilter,
|
|
RxContext->CurrentIrp,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
// attach this
|
|
ExAcquireFastMutex(&pNetRootEntry->NetRoot.NotifyeeFobxListMutex);
|
|
|
|
if (!PIsFobxInTheList(&pNetRootEntry->NetRoot.NotifyeeFobxList, capFobx))
|
|
{
|
|
InsertTailList(&pNetRootEntry->NetRoot.NotifyeeFobxList, &pNF->NextNotifyeeFobx);
|
|
}
|
|
else
|
|
{
|
|
FreeMem((PVOID)pNF);
|
|
}
|
|
|
|
ExReleaseFastMutex(&pNetRootEntry->NetRoot.NotifyeeFobxListMutex);
|
|
|
|
// as we hijacked the Irp, let us make sure that rdbss gets rid of the rxcontext
|
|
|
|
RxCompleteRequest_Real( RxContext, NULL, STATUS_PENDING );
|
|
|
|
Status = STATUS_PENDING;
|
|
|
|
if (FcbAcquired) {
|
|
RxReleaseFcbResourceInMRx(capFcb );
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbCscCleanupFobx(
|
|
IN PRX_CONTEXT RxContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine cleans up a file system object.
|
|
For CSC, the only thing we do is to remove changenotification.
|
|
Arguments:
|
|
|
|
pRxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
BOOLEAN fInList = FALSE;
|
|
PNOTIFYEE_FOBX pNF = NULL;
|
|
|
|
pNetRootEntry = SmbCeGetAssociatedNetRootEntry(capFcb->pNetRoot);
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);
|
|
|
|
if (MRxSmbCSCIsDisconnectedOpen(capFcb, MRxSmbGetSrvOpenExtension(capFobx->pSrvOpen)))
|
|
{
|
|
ExAcquireFastMutex(&pNetRootEntry->NetRoot.NotifyeeFobxListMutex);
|
|
|
|
pNF = PIsFobxInTheList(&pNetRootEntry->NetRoot.NotifyeeFobxList, capFobx);
|
|
|
|
if (pNF)
|
|
{
|
|
RemoveEntryList(&pNF->NextNotifyeeFobx);
|
|
FreeMem(pNF);
|
|
pNF = NULL;
|
|
fInList = TRUE;
|
|
}
|
|
ExReleaseFastMutex(&pNetRootEntry->NetRoot.NotifyeeFobxListMutex);
|
|
|
|
if (fInList)
|
|
{
|
|
SmbCeLog(("chngnotify cleanup fobx=%x\n", capFobx));
|
|
SmbLog(LOG,
|
|
MRxSmbCscCleanupFobx,
|
|
LOGPTR(capFobx));
|
|
// DbgPrint("chngnotify Cleanup fobx=%x NR=%x DirList=%x\n", capFobx, pNetRootEntry, &pNetRootEntry->NetRoot.DirNotifyList);
|
|
FsRtlNotifyCleanup (
|
|
pNetRootEntry->NetRoot.pNotifySync,
|
|
&pNetRootEntry->NetRoot.DirNotifyList,
|
|
capFobx
|
|
);
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbCscInitializeNetRootEntry(
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes, the change notify structures in the netrootentry
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
|
|
try {
|
|
FsRtlNotifyInitializeSync( &pNetRootEntry->NetRoot.pNotifySync );
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
NtStatus = GetExceptionCode();
|
|
}
|
|
if (NtStatus == STATUS_SUCCESS) {
|
|
InitializeListHead( &pNetRootEntry->NetRoot.DirNotifyList );
|
|
InitializeListHead( &pNetRootEntry->NetRoot.NotifyeeFobxList);
|
|
ExInitializeFastMutex(&pNetRootEntry->NetRoot.NotifyeeFobxListMutex);
|
|
}
|
|
return NtStatus;
|
|
|
|
}
|
|
|
|
VOID
|
|
MRxSmbCscUninitializeNetRootEntry(
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine unitializes, the change notify structures
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
FsRtlNotifyUninitializeSync( &pNetRootEntry->NetRoot.pNotifySync );
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
MRxSmbCSCIsDisconnectedOpen(
|
|
PMRX_FCB pFcb,
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A slightly more involved check to see whether this is a disconnected open.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetAssociatedServerEntry(pFcb->pNetRoot->pSrvCall);
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(pFcb);
|
|
|
|
if(BooleanFlagOn(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_DISCONNECTED_OPEN))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// we need to also check that the serverentry is non-NULL. This can happen when
|
|
// you the syetem is about to shut down, or the FCB has been orphaned.
|
|
|
|
if (pServerEntry && SmbCeIsServerInDisconnectedMode(pServerEntry))
|
|
{
|
|
if (smbFcb->hShadow || smbFcb->hShadowRenamed)
|
|
{
|
|
// is the shadow visible in disconnected state?
|
|
return(IsShadowVisible(TRUE, smbFcb->dwFileAttributes, smbFcb->ShadowStatus) != 0);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif //ifdef MRXSMB_BUILD_FOR_CSC_DCON
|
|
|
|
|
|
|
|
|
|
PNOTIFYEE_FOBX
|
|
PIsFobxInTheList(
|
|
PLIST_ENTRY pNotifyeeFobxList,
|
|
PMRX_FOBX pFobx
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This a support routine form change notification. It checks whether an FOBX, which is
|
|
the redir's internal representation of a handle, is a change notification handle
|
|
or not. Note, the shadow crit sect must be held when calling this.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY pListEntry;
|
|
|
|
|
|
pListEntry = pNotifyeeFobxList->Flink;
|
|
|
|
if (pListEntry)
|
|
{
|
|
while (pListEntry != pNotifyeeFobxList)
|
|
{
|
|
PNOTIFYEE_FOBX pNF = (PNOTIFYEE_FOBX)CONTAINING_RECORD(pListEntry, NOTIFYEE_FOBX, NextNotifyeeFobx);
|
|
|
|
if (pNF->pFobx == pFobx)
|
|
{
|
|
return pNF;
|
|
}
|
|
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOL
|
|
FCleanupAllNotifyees(
|
|
PNOTIFY_SYNC pNotifySync,
|
|
PLIST_ENTRY pDirNotifyList,
|
|
PLIST_ENTRY pNotifyeeFobxList,
|
|
PFAST_MUTEX pNotifyeeFobxListMutex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes all outstanding changenotifications for a paricular list of
|
|
notifyees
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
|
|
PLIST_ENTRY pListEntry;
|
|
PNOTIFYEE_FOBX pNF;
|
|
BOOL fDoneSome = FALSE;
|
|
|
|
ExAcquireFastMutex(pNotifyeeFobxListMutex);
|
|
|
|
pListEntry = pNotifyeeFobxList->Flink;
|
|
|
|
if (pListEntry)
|
|
{
|
|
while (pListEntry != pNotifyeeFobxList)
|
|
{
|
|
pNF = (PNOTIFYEE_FOBX)CONTAINING_RECORD(pListEntry, NOTIFYEE_FOBX, NextNotifyeeFobx);
|
|
|
|
SmbCeLog(("chngnotify cleanup fobx=%x\n", pNF->pFobx));
|
|
SmbLog(LOG,
|
|
FCleanupAllNotifyees,
|
|
LOGPTR(pNF->pFobx));
|
|
// DbgPrint("chngnotify Cleanup fobx=%x DirList=%x\n", pNF->pFobx, pDirNotifyList);
|
|
FsRtlNotifyCleanup (
|
|
pNotifySync,
|
|
pDirNotifyList,
|
|
pNF->pFobx
|
|
);
|
|
|
|
RemoveEntryList(&pNF->NextNotifyeeFobx);
|
|
|
|
FreeMem(pNF);
|
|
fDoneSome = TRUE;
|
|
|
|
pListEntry = pNotifyeeFobxList->Flink;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ExReleaseFastMutex(pNotifyeeFobxListMutex);
|
|
|
|
return fDoneSome;
|
|
}
|
|
|
|
VOID
|
|
MRxSmbCSCResumeAllOutstandingOperations(
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes all outstanding change notifications on the server.
|
|
This is called when a server is being transitioned from offline to online.
|
|
The caller must make sure that smbceresource is held, so that there are
|
|
no synchronization problems while enumerating.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
|
|
|
pNetRootEntry = SmbCeGetFirstNetRootEntry(pServerEntry);
|
|
while (pNetRootEntry != NULL) {
|
|
if (pNetRootEntry->NetRoot.pNotifySync)
|
|
{
|
|
|
|
FCleanupAllNotifyees(pNetRootEntry->NetRoot.pNotifySync,
|
|
&pNetRootEntry->NetRoot.DirNotifyList,
|
|
&pNetRootEntry->NetRoot.NotifyeeFobxList,
|
|
&pNetRootEntry->NetRoot.NotifyeeFobxListMutex
|
|
);
|
|
}
|
|
pNetRootEntry = SmbCeGetNextNetRootEntry(pServerEntry,pNetRootEntry);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
MRxSmbCSCObtainRightsForUserOnFile(
|
|
IN PRX_CONTEXT pRxContext,
|
|
HSHADOW hDir,
|
|
HSHADOW hShadow,
|
|
OUT ACCESS_MASK *pMaximalAccessRights,
|
|
OUT ACCESS_MASK *pGuestMaximalAccessRights
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine gets the rights for a specific user. The routine is called during
|
|
|
|
a create operation in disconnected state.
|
|
|
|
|
|
Arguments:
|
|
|
|
pRxContext Context for the create operation. We use this to get the user SID
|
|
|
|
hDir Directory Inode
|
|
|
|
hShadow File Inode
|
|
|
|
pMaximalAccessRights Access rights on the file for the user returned to the caller
|
|
|
|
pGuestMaximalAccessRights Guest Access rights on the file returned to the caller
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN AccessGranted = FALSE, SidHasAccessMask;
|
|
SID_CONTEXT SidContext;
|
|
int i;
|
|
|
|
*pMaximalAccessRights = *pGuestMaximalAccessRights = 0;
|
|
|
|
Status = CscRetrieveSid(
|
|
pRxContext,
|
|
&SidContext);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
CACHED_SECURITY_INFORMATION CachedSecurityInformation;
|
|
|
|
ULONG BytesReturned,SidLength;
|
|
DWORD CscStatus;
|
|
CSC_SID_INDEX SidIndex;
|
|
|
|
if (SidContext.pSid != NULL) {
|
|
SidLength = RtlLengthSid(
|
|
SidContext.pSid);
|
|
|
|
SidIndex = CscMapSidToIndex(
|
|
SidContext.pSid,
|
|
SidLength);
|
|
} else {
|
|
SidIndex = CSC_INVALID_SID_INDEX;
|
|
}
|
|
|
|
if (SidIndex == CSC_INVALID_SID_INDEX) {
|
|
// The sid was not located in the existing Sid mappings
|
|
// Map this Sid to that of a Guest
|
|
SidIndex = CSC_GUEST_SID_INDEX;
|
|
}
|
|
|
|
BytesReturned = sizeof(CachedSecurityInformation);
|
|
|
|
CscStatus = GetShadowInfoEx(
|
|
hDir,
|
|
hShadow,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&CachedSecurityInformation,
|
|
&BytesReturned);
|
|
|
|
if (CscStatus == ERROR_SUCCESS) {
|
|
if (BytesReturned == sizeof(CACHED_SECURITY_INFORMATION)) {
|
|
// Walk through the cached access rights to determine the
|
|
// maximal permissible access rights.
|
|
for (i = 0; (i < CSC_MAXIMUM_NUMBER_OF_CACHED_SID_INDEXES); i++) {
|
|
|
|
if(CachedSecurityInformation.AccessRights[i].SidIndex == SidIndex)
|
|
{
|
|
if (CSC_GUEST_SID_INDEX != SidIndex)
|
|
{
|
|
*pMaximalAccessRights = CachedSecurityInformation.AccessRights[i].MaximalRights;
|
|
}
|
|
}
|
|
|
|
if (CachedSecurityInformation.AccessRights[i].SidIndex == CSC_GUEST_SID_INDEX)
|
|
{
|
|
*pGuestMaximalAccessRights = CachedSecurityInformation.AccessRights[i].MaximalRights;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
CscDiscardSid(&SidContext);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
MRxSmbCscFlushFdb(
|
|
IN PFDB Fdb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called from delete ioctl to flush an open file which is being delay closed.
|
|
Files which are closed by the user but for which the redir hasn't pushed out the
|
|
close, cannot have their cached replicas deleted because these are open too. This
|
|
causes CSCDeleteIoctl to fail, and the user has not idea why.
|
|
|
|
This routine must be called with ShadowCritSect held
|
|
|
|
Arguments:
|
|
|
|
Fdb CSC version of smbfcb.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PMRX_SMB_FCB pSmbFcb;
|
|
PNET_ROOT pNetRoot;
|
|
|
|
pSmbFcb = MRxSmbCscRecoverMrxFcbFromFdb(Fdb);
|
|
pNetRoot = (PNET_ROOT)(pSmbFcb->ContainingFcb->pNetRoot);
|
|
|
|
LeaveShadowCrit();
|
|
RxScavengeFobxsForNetRoot(pNetRoot,NULL);
|
|
EnterShadowCrit();
|
|
}
|