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.
10473 lines
332 KiB
10473 lines
332 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
csc.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the client side caching interface for the SMB mini rdr.
|
|
|
|
Author:
|
|
|
|
Joe Linn [joelinn] 21-jan-1997
|
|
|
|
Revision History:
|
|
|
|
Shishir Pardikar disconnected ops, parameter validation, bug fixes .....
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <smbdebug.h>
|
|
|
|
#define Dbg (DEBUG_TRACE_MRXSMBCSC)
|
|
RXDT_DefineCategory(MRXSMBCSC);
|
|
|
|
|
|
//local prototype
|
|
|
|
LONG
|
|
MRxSmbCSCExceptionFilter (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PEXCEPTION_POINTERS ExceptionPointer
|
|
);
|
|
|
|
BOOLEAN
|
|
CscpAccessCheck(
|
|
PCACHED_SECURITY_INFORMATION pCachedSecurityInformation,
|
|
ULONG CachedSecurityInformationLength,
|
|
CSC_SID_INDEX SidIndex,
|
|
ACCESS_MASK AccessMask,
|
|
BOOLEAN *pSidHasAccessmask
|
|
);
|
|
|
|
BOOLEAN
|
|
CscAccessCheck(
|
|
HSHADOW hParent,
|
|
HSHADOW hFile,
|
|
PRX_CONTEXT RxContext,
|
|
ACCESS_MASK AccessMask,
|
|
PCACHED_SECURITY_INFORMATION pCachedSecurityInformationForShadow,
|
|
PCACHED_SECURITY_INFORMATION pCachedSecurityInformationForShare
|
|
);
|
|
|
|
VOID
|
|
MRxSmbCscFillWithoutNamesFind32FromFcb (
|
|
IN PMINIMAL_CSC_SMBFCB MinimalCscSmbFcb,
|
|
OUT _WIN32_FIND_DATA *Find32
|
|
);
|
|
|
|
NTSTATUS
|
|
MRxSmbCscGetFileInfoForCshadow(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
|
|
);
|
|
|
|
NTSTATUS
|
|
MRxSmbGetFileInfoFromServer (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN PUNICODE_STRING FullFileName,
|
|
OUT _WIN32_FIND_DATA *Find32,
|
|
IN PMRX_SRV_OPEN pSrvOpen,
|
|
OUT BOOLEAN *lpfIsRoot
|
|
);
|
|
|
|
BOOLEAN
|
|
MRxSmbCscIsFatNameValid (
|
|
IN PUNICODE_STRING FileName,
|
|
IN BOOLEAN WildCardsPermissible
|
|
);
|
|
|
|
VOID
|
|
MRxSmbCscGenerate83NameAsNeeded(
|
|
IN CSC_SHADOW_HANDLE hDir,
|
|
PWCHAR FileName,
|
|
PWCHAR SFN
|
|
);
|
|
int
|
|
RefreshShadow( HSHADOW hDir,
|
|
IN HSHADOW hShadow,
|
|
IN LPFIND32 lpFind32,
|
|
OUT ULONG *lpuShadowStatus
|
|
);
|
|
|
|
NTSTATUS
|
|
SmbPseExchangeStart_CloseCopyChunk(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
|
|
);
|
|
|
|
NTSTATUS
|
|
MRxSmbCscCloseExistingThruOpen(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
);
|
|
|
|
ULONG
|
|
GetPathLevelFromUnicodeString (
|
|
PUNICODE_STRING Name
|
|
);
|
|
|
|
NTSTATUS
|
|
MRxSmbCscFixupFindFirst (
|
|
PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange
|
|
);
|
|
VOID
|
|
MRxSmbCscLocateAndFillFind32WithinSmbbuf(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
|
|
);
|
|
|
|
NTSTATUS
|
|
MRxSmbCscGetFileInfoFromServerWithinExchange (
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE,
|
|
PUNICODE_STRING FileName
|
|
);
|
|
|
|
NTSTATUS
|
|
IoctlGetDebugInfo(
|
|
PRX_CONTEXT RxContext,
|
|
PBYTE InputBuffer,
|
|
ULONG InputBufferLength,
|
|
PBYTE OutputBuffer,
|
|
ULONG OutputBufferLength);
|
|
|
|
NTSTATUS
|
|
MRxSmbCscLocalFileOpen(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
);
|
|
|
|
NTSTATUS
|
|
MRxSmbCscObtainShadowHandles (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PNTSTATUS Status,
|
|
IN OUT _WIN32_FIND_DATA *Find32,
|
|
OUT PBOOLEAN Created,
|
|
IN ULONG CreateShadowControls,
|
|
IN BOOLEAN Disconnected
|
|
);
|
|
|
|
// type of buffer used to capture structures passed in which have embedded pointers.
|
|
// Once we have captured the structure, the embedded pointers cannot be changed and
|
|
// our parameter validation holds good throughout the duration of the call
|
|
|
|
typedef union tagCAPTURE_BUFFERS
|
|
{
|
|
COPYPARAMSW sCP;
|
|
SHADOWINFO sSI;
|
|
SHAREINFO sSVI;
|
|
}
|
|
CAPTURE_BUFFERS, *LPCAPTURE_BUFFERS;
|
|
|
|
// table entry type off which the parameter validation is driven
|
|
typedef struct tagCSC_IOCTL_ENTRY
|
|
{
|
|
ULONG IoControlCode; // iocontrolcode for sanity check
|
|
DWORD dwFlags; // bits indicating what type of strucutre is passed in
|
|
DWORD dwLength; // size of the passed in strucutre
|
|
}
|
|
CSC_IOCTL_ENTRY;
|
|
|
|
// defines for the flags in dwFlags field in CSC_IOCTL_ENTRY structure
|
|
#define FLAG_CSC_IOCTL_PQPARAMS 0x00000001
|
|
#define FLAG_CSC_IOCTL_COPYPARAMS 0x00000002
|
|
#define FLAG_CSC_IOCTL_SHADOWINFO 0x00000004
|
|
#define FLAG_CSC_IOCTL_COPYCHUNKCONTEXT 0x00000008
|
|
#define FLAG_CSC_IOCTL_GLOBALSTATUS 0x00000010
|
|
|
|
#define FLAG_CSC_IOCTL_BUFFERTYPE_MASK 0xff
|
|
|
|
#define SMB_CSC_BITS_TO_DATABASE_CSC_BITS(CscFlags) (((CscFlags) << 4) & SHARE_CACHING_MASK)
|
|
#define DATABASE_CSC_BITS_TO_SMB_CSC_BITS(CscFlags) (((CscFlags) & SHARE_CACHING_MASK) >> 4)
|
|
|
|
|
|
// #define IOCTL_NAME_OF_SERVER_GOING_OFFLINE (_SHADOW_IOCTL_CODE(45))
|
|
|
|
#ifdef DEBUG
|
|
extern ULONG HookKdPrintVector = HOOK_KDP_BADERRORS;
|
|
extern ULONG HookKdPrintVectorDef = HOOK_KDP_GOOD_DEFAULT;
|
|
#endif
|
|
|
|
#ifdef RX_PRIVATE_BUILD
|
|
ULONG MRxSmbCscDbgPrintF = 0; // 1;
|
|
#endif //ifdef RX_PRIVATE_BUILD
|
|
|
|
//
|
|
// this variable is used to "help" the agent know when to recalculate
|
|
// the reference priorities
|
|
//
|
|
ULONG MRxSmbCscNumberOfShadowOpens = 0;
|
|
ULONG MRxSmbCscActivityThreshold = 16;
|
|
ULONG MRxSmbCscInitialRefPri = MAX_PRI;
|
|
// these two lists are used to list up all the netroots and fcbs
|
|
// that have shadows so that we can find them for the ioctls. today
|
|
// are just doubly-linked lists but we can anticipate that this may
|
|
// become a performance issue, particularly for fcbs. at that point, we
|
|
// can either change to bucket hashing or tries
|
|
|
|
LIST_ENTRY xCscFcbsList;
|
|
PIRP vIrpReint = NULL;
|
|
|
|
#define MRxSmbCscAddReverseFcbTranslation(smbFcb) {\
|
|
InsertTailList(&xCscFcbsList, \
|
|
&(smbFcb)->ShadowReverseTranslationLinks); \
|
|
}
|
|
#define MRxSmbCscRemoveReverseFcbTranslation(smbFcb) {\
|
|
RemoveEntryList(&(smbFcb)->ShadowReverseTranslationLinks); \
|
|
}
|
|
|
|
PMRX_SMB_FCB
|
|
MRxSmbCscRecoverMrxFcbFromFdb (
|
|
IN PFDB Fdb
|
|
);
|
|
|
|
BOOL
|
|
CscDfsShareIsInReint(
|
|
IN PRX_CONTEXT RxContext
|
|
);
|
|
//
|
|
// From zwapi.h.
|
|
//
|
|
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
ZwSetSecurityObject(
|
|
IN HANDLE Handle,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor
|
|
);
|
|
|
|
NTSTATUS
|
|
CaptureInputBufferIfNecessaryAndProbe(
|
|
DWORD IoControlCode,
|
|
PRX_CONTEXT pRxContext,
|
|
PBYTE InputBuffer,
|
|
LPCAPTURE_BUFFERS lpCapBuff,
|
|
PBYTE *ppAuxBuf,
|
|
PBYTE *ppOrgBuf,
|
|
PBYTE *ppReturnBuffer
|
|
);
|
|
|
|
NTSTATUS
|
|
ValidateCopyParams(
|
|
LPCOPYPARAMS lpCP
|
|
);
|
|
|
|
NTSTATUS
|
|
ValidateShadowInfo(
|
|
DWORD IoControlCode,
|
|
LPSHADOWINFO lpSI,
|
|
LPBYTE *ppAuxBuf,
|
|
LPBYTE *ppOrgBuf
|
|
);
|
|
|
|
NTSTATUS
|
|
ValidateCopyChunkContext(
|
|
PRX_CONTEXT RxContext,
|
|
DWORD IoControlCode
|
|
);
|
|
|
|
NTSTATUS
|
|
CscProbeForReadWrite(
|
|
PBYTE pBuffer,
|
|
DWORD dwSize
|
|
);
|
|
|
|
NTSTATUS
|
|
CscProbeAndCaptureForReadWrite(
|
|
PBYTE pBuffer,
|
|
DWORD dwSize,
|
|
PBYTE *ppAuxBuf
|
|
);
|
|
|
|
VOID
|
|
CopyBackIfNecessary(
|
|
DWORD IoControlCode,
|
|
PBYTE InputBuffer,
|
|
LPCAPTURE_BUFFERS lpCapBuff,
|
|
PBYTE pAuxBuf,
|
|
PBYTE pOrgBuf,
|
|
BOOL fSuccess
|
|
);
|
|
|
|
VOID
|
|
EnterShadowCritRx(
|
|
PRX_CONTEXT pRxContext
|
|
);
|
|
|
|
VOID
|
|
LeaveShadowCritRx(
|
|
PRX_CONTEXT pRxContext
|
|
);
|
|
|
|
#if defined(REMOTE_BOOT)
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
ZwOpenThreadToken(
|
|
IN HANDLE ThreadHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN BOOLEAN OpenAsSelf,
|
|
OUT PHANDLE TokenHandle
|
|
);
|
|
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
ZwOpenProcessToken(
|
|
IN HANDLE ProcessHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PHANDLE TokenHandle
|
|
);
|
|
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
ZwDuplicateToken(
|
|
IN HANDLE ExistingTokenHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN BOOLEAN EffectiveOnly,
|
|
IN TOKEN_TYPE TokenType,
|
|
OUT PHANDLE NewTokenHandle
|
|
);
|
|
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
ZwAdjustPrivilegesToken (
|
|
IN HANDLE TokenHandle,
|
|
IN BOOLEAN DisableAllPrivileges,
|
|
IN PTOKEN_PRIVILEGES NewState OPTIONAL,
|
|
IN ULONG BufferLength OPTIONAL,
|
|
IN PTOKEN_PRIVILEGES PreviousState OPTIONAL,
|
|
OUT PULONG ReturnLength
|
|
);
|
|
|
|
//
|
|
// From ntrtl.h.
|
|
//
|
|
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlGetSaclSecurityDescriptor (
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
PBOOLEAN SaclPresent,
|
|
PACL *Sacl,
|
|
PBOOLEAN SaclDefaulted
|
|
);
|
|
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlGetGroupSecurityDescriptor (
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
PSID *Group,
|
|
PBOOLEAN GroupDefaulted
|
|
);
|
|
|
|
#endif
|
|
|
|
//sigh BUBUG get this stuff into an include file.....
|
|
#define SHADOW_VERSION 0x8287
|
|
extern char vszShadowDir[MAX_SHADOW_DIR_NAME+1];
|
|
extern PVOID lpdbShadow;
|
|
//CODE.IMPROFVEMENT this should be in a .h file
|
|
extern PKEVENT MRxSmbAgentSynchronizationEvent;
|
|
extern PKEVENT MRxSmbAgentFillEvent;
|
|
extern PSMBCEDB_SERVER_ENTRY CscServerEntryBeingTransitioned;
|
|
extern ULONG CscSessionIdCausingTransition;
|
|
extern ULONG vulDatabaseStatus;
|
|
extern unsigned cntInodeTransactions;
|
|
|
|
extern VOID
|
|
MRxSmbDecrementSrvOpenCount(
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
|
LONG SrvOpenServerVersion,
|
|
PMRX_SRV_OPEN SrvOpen);
|
|
|
|
VOID ValidateSmbFcbList(VOID);
|
|
|
|
BOOL SetOfflineOpenStatusForShare(
|
|
CSC_SHARE_HANDLE hShare,
|
|
CSC_SHADOW_HANDLE hRootDir,
|
|
OUT PULONG pShareStatus
|
|
);
|
|
|
|
LONG CSCBeginReint(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT LPSHADOWINFO lpSI
|
|
);
|
|
|
|
ULONG CSCEndReint(
|
|
IN OUT LPSHADOWINFO lpSI
|
|
);
|
|
|
|
VOID CSCCancelReint(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP ThisIrp
|
|
);
|
|
|
|
VOID
|
|
CreateFakeFind32(
|
|
CSC_SHADOW_HANDLE hDir,
|
|
_WIN32_FIND_DATA *pFind32,
|
|
PRX_CONTEXT RxContext,
|
|
BOOLEAN LastComponentInName
|
|
);
|
|
|
|
NTSTATUS
|
|
OkToDeleteObject(
|
|
HSHADOW hDir,
|
|
HSHADOW hShadow,
|
|
_WIN32_FIND_DATA *Find32,
|
|
ULONG uShadowStatus,
|
|
BOOLEAN fDisconnected
|
|
);
|
|
|
|
#pragma alloc_text(PAGE, MRxSmbCSCExceptionFilter)
|
|
|
|
#if defined(REMOTE_BOOT)
|
|
#pragma alloc_text(PAGE, ZwImpersonateSelf)
|
|
#pragma alloc_text(PAGE, ZwAdjustPrivilege)
|
|
#pragma alloc_text(PAGE, RtlGetSecurityInformationFromSecurityDescriptor)
|
|
#endif
|
|
|
|
#pragma alloc_text(PAGE, MRxSmbInitializeCSC)
|
|
#pragma alloc_text(PAGE, MRxSmbUninitializeCSC)
|
|
#pragma alloc_text(PAGE, CscpAccessCheck)
|
|
#pragma alloc_text(PAGE, CscAccessCheck)
|
|
#pragma alloc_text(PAGE, MRxSmbCscAcquireSmbFcb)
|
|
#pragma alloc_text(PAGE, MRxSmbCscReleaseSmbFcb)
|
|
#pragma alloc_text(PAGE, MRxSmbCscSetFileInfoEpilogue)
|
|
#pragma alloc_text(PAGE, MRxSmbCscIoCtl)
|
|
#pragma alloc_text(PAGE, MRxSmbCscObtainShareHandles)
|
|
#pragma alloc_text(PAGE, MRxSmbCscFillWithoutNamesFind32FromFcb)
|
|
#pragma alloc_text(PAGE, MRxSmbCscGetFileInfoForCshadow)
|
|
#pragma alloc_text(PAGE, MRxSmbGetFileInfoFromServer)
|
|
#pragma alloc_text(PAGE, MRxSmbCscIsFatNameValid)
|
|
#pragma alloc_text(PAGE, MRxSmbCscGenerate83NameAsNeeded)
|
|
#pragma alloc_text(PAGE, MRxSmbCscCreateShadowFromPath)
|
|
#pragma alloc_text(PAGE, RefreshShadow)
|
|
#pragma alloc_text(PAGE, MRxSmbCscIsThisACopyChunkOpen)
|
|
#pragma alloc_text(PAGE, SmbPseExchangeStart_CloseCopyChunk)
|
|
#pragma alloc_text(PAGE, MRxSmbCscCloseExistingThruOpen)
|
|
#pragma alloc_text(PAGE, MRxSmbCscCreatePrologue)
|
|
#pragma alloc_text(PAGE, MRxSmbCscObtainShadowHandles)
|
|
#if defined(REMOTE_BOOT)
|
|
#pragma alloc_text(PAGE, MRxSmbCscSetSecurityOnShadow)
|
|
#endif
|
|
#pragma alloc_text(PAGE, MRxSmbCscCreateEpilogue)
|
|
#pragma alloc_text(PAGE, MRxSmbCscDeleteAfterCloseEpilogue)
|
|
#pragma alloc_text(PAGE, GetPathLevelFromUnicodeString)
|
|
#pragma alloc_text(PAGE, MRxSmbCscRenameEpilogue)
|
|
#pragma alloc_text(PAGE, MRxSmbCscCloseShadowHandle)
|
|
#pragma alloc_text(PAGE, MRxSmbCscFixupFindFirst)
|
|
#pragma alloc_text(PAGE, MRxSmbCscLocateAndFillFind32WithinSmbbuf)
|
|
#pragma alloc_text(PAGE, MRxSmbCscGetFileInfoFromServerWithinExchange)
|
|
#pragma alloc_text(PAGE, MRxSmbCscUpdateShadowFromClose)
|
|
#pragma alloc_text(PAGE, MRxSmbCscDeallocateForFcb)
|
|
#pragma alloc_text(PAGE, MRxSmbCscRecoverMrxFcbFromFdb)
|
|
#pragma alloc_text(PAGE, MRxSmbCscFindFdbFromHShadow)
|
|
#pragma alloc_text(PAGE, MRxSmbCscFindResourceFromHandlesWithModify)
|
|
#pragma alloc_text(PAGE, MRxSmbCscFindLocalFlagsFromFdb)
|
|
#pragma alloc_text(PAGE, MRxSmbCscSetSecurityPrologue)
|
|
#pragma alloc_text(PAGE, MRxSmbCscSetSecurityEpilogue)
|
|
#pragma alloc_text(PAGE, CaptureInputBufferIfNecessaryAndProbe)
|
|
#pragma alloc_text(PAGE, ValidateCopyParams)
|
|
#pragma alloc_text(PAGE, ValidateShadowInfo)
|
|
#pragma alloc_text(PAGE, ValidateCopyChunkContext)
|
|
#pragma alloc_text(PAGE, CscProbeForReadWrite)
|
|
#pragma alloc_text(PAGE, CopyBackIfNecessary)
|
|
#pragma alloc_text(PAGE, ValidateSmbFcbList)
|
|
#pragma alloc_text(PAGE, SetOfflineOpenStatusForShare)
|
|
#pragma alloc_text(PAGE, MRxSmbCscLocalFileOpen)
|
|
#pragma alloc_text(PAGE, CSCCheckLocalOpens)
|
|
#pragma alloc_text(PAGE, IsCSCBusy)
|
|
#pragma alloc_text(PAGE, ClearCSCStateOnRedirStructures)
|
|
#pragma alloc_text(PAGE, CscDfsShareIsInReint)
|
|
#pragma alloc_text(PAGE, CloseOpenFiles)
|
|
#pragma alloc_text(PAGE, CreateFakeFind32)
|
|
#pragma alloc_text(PAGE, OkToDeleteObject)
|
|
#pragma alloc_text(PAGE, IoctlGetDebugInfo)
|
|
|
|
//remember whether to delete the link
|
|
BOOLEAN MRxSmbCscLinkCreated = FALSE;
|
|
|
|
PCONTEXT CSCExpCXR;
|
|
PEXCEPTION_RECORD CSCExpEXR;
|
|
PVOID CSCExpAddr;
|
|
NTSTATUS CSCExpCode;
|
|
|
|
LONG
|
|
MRxSmbCSCExceptionFilter (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PEXCEPTION_POINTERS ExceptionPointer
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to decide if we should or should not handle
|
|
an exception status that is being raised. It first determines the true exception
|
|
code by examining the exception record. If there is an Irp Context, then it inserts the status
|
|
into the RxContext. Finally, it determines whether to handle the exception or bugcheck
|
|
according to whether the except is one of the expected ones. in actuality, all exceptions are expected
|
|
except for some lowlevel machine errors (see fsrtl\filter.c)
|
|
|
|
Arguments:
|
|
|
|
RxContext - the irp context of current operation for storing away the code.
|
|
|
|
ExceptionPointer - Supplies the exception context.
|
|
|
|
Return Value:
|
|
|
|
ULONG - returns EXCEPTION_EXECUTE_HANDLER or bugchecks
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ExceptionCode;
|
|
|
|
//save these values in statics so i can see 'em on the debugger............
|
|
ExceptionCode = CSCExpCode = ExceptionPointer->ExceptionRecord->ExceptionCode;
|
|
CSCExpAddr = ExceptionPointer->ExceptionRecord->ExceptionAddress;
|
|
CSCExpEXR = ExceptionPointer->ExceptionRecord;
|
|
CSCExpCXR = ExceptionPointer->ContextRecord;
|
|
|
|
RxDbgTrace(0, Dbg, ("!!! ExceptioCode=%lx Addr=%lx EXR=%lx CXR=%lx\n", CSCExpCode, CSCExpAddr, CSCExpEXR, CSCExpCXR));
|
|
RxLog(("!!! %lx %lx %lx %lx\n", CSCExpCode, CSCExpAddr, CSCExpEXR, CSCExpCXR));
|
|
|
|
// ASSERT(FALSE);
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
#if defined(REMOTE_BOOT)
|
|
//
|
|
// Stolen from RTL, changed to use Zw APis.
|
|
//
|
|
|
|
NTSTATUS
|
|
ZwImpersonateSelf(
|
|
IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine may be used to obtain an Impersonation token representing
|
|
your own process's context. This may be useful for enabling a privilege
|
|
for a single thread rather than for the entire process; or changing
|
|
the default DACL for a single thread.
|
|
|
|
The token is assigned to the callers thread.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
ImpersonationLevel - The level to make the impersonation token.
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - The thread is now impersonating the calling process.
|
|
|
|
Other - Status values returned by:
|
|
|
|
ZwOpenProcessToken()
|
|
ZwDuplicateToken()
|
|
ZwSetInformationThread()
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS
|
|
Status,
|
|
IgnoreStatus;
|
|
|
|
HANDLE
|
|
Token1,
|
|
Token2;
|
|
|
|
OBJECT_ATTRIBUTES
|
|
ObjectAttributes;
|
|
|
|
SECURITY_QUALITY_OF_SERVICE
|
|
Qos;
|
|
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
|
|
|
|
Qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|
Qos.ImpersonationLevel = ImpersonationLevel;
|
|
Qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
Qos.EffectiveOnly = FALSE;
|
|
ObjectAttributes.SecurityQualityOfService = &Qos;
|
|
|
|
Status = ZwOpenProcessToken( NtCurrentProcess(), TOKEN_DUPLICATE, &Token1 );
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = ZwDuplicateToken(
|
|
Token1,
|
|
TOKEN_IMPERSONATE,
|
|
&ObjectAttributes,
|
|
FALSE, //EffectiveOnly
|
|
TokenImpersonation,
|
|
&Token2
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = ZwSetInformationThread(
|
|
NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
&Token2,
|
|
sizeof(HANDLE)
|
|
);
|
|
|
|
IgnoreStatus = ZwClose( Token2 );
|
|
}
|
|
|
|
|
|
IgnoreStatus = ZwClose( Token1 );
|
|
}
|
|
|
|
|
|
return(Status);
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ZwAdjustPrivilege(
|
|
ULONG Privilege,
|
|
BOOLEAN Enable,
|
|
BOOLEAN Client,
|
|
PBOOLEAN WasEnabled
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure enables or disables a privilege process-wide.
|
|
|
|
Arguments:
|
|
|
|
Privilege - The lower 32-bits of the privilege ID to be enabled or
|
|
disabled. The upper 32-bits is assumed to be zero.
|
|
|
|
Enable - A boolean indicating whether the privilege is to be enabled
|
|
or disabled. TRUE indicates the privilege is to be enabled.
|
|
FALSE indicates the privilege is to be disabled.
|
|
|
|
Client - A boolean indicating whether the privilege should be adjusted
|
|
in a client token or the process's own token. TRUE indicates
|
|
the client's token should be used (and an error returned if there
|
|
is no client token). FALSE indicates the process's token should
|
|
be used.
|
|
|
|
WasEnabled - points to a boolean to receive an indication of whether
|
|
the privilege was previously enabled or disabled. TRUE indicates
|
|
the privilege was previously enabled. FALSE indicates the privilege
|
|
was previoulsy disabled. This value is useful for returning the
|
|
privilege to its original state after using it.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - The privilege has been sucessfully enabled or disabled.
|
|
|
|
STATUS_PRIVILEGE_NOT_HELD - The privilege is not held by the specified context.
|
|
|
|
Other status values as may be returned by:
|
|
|
|
ZwOpenProcessToken()
|
|
ZwAdjustPrivilegesToken()
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS
|
|
Status,
|
|
TmpStatus;
|
|
|
|
HANDLE
|
|
Token;
|
|
|
|
LUID
|
|
LuidPrivilege;
|
|
|
|
PTOKEN_PRIVILEGES
|
|
NewPrivileges,
|
|
OldPrivileges;
|
|
|
|
ULONG
|
|
Length;
|
|
|
|
UCHAR
|
|
Buffer1[sizeof(TOKEN_PRIVILEGES)+
|
|
((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))],
|
|
Buffer2[sizeof(TOKEN_PRIVILEGES)+
|
|
((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))];
|
|
|
|
|
|
NewPrivileges = (PTOKEN_PRIVILEGES)Buffer1;
|
|
OldPrivileges = (PTOKEN_PRIVILEGES)Buffer2;
|
|
|
|
//
|
|
// Open the appropriate token...
|
|
//
|
|
|
|
if (Client == TRUE) {
|
|
Status = ZwOpenThreadToken(
|
|
NtCurrentThread(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
FALSE,
|
|
&Token
|
|
);
|
|
} else {
|
|
|
|
Status = ZwOpenProcessToken(
|
|
NtCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
&Token
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Initialize the privilege adjustment structure
|
|
//
|
|
|
|
LuidPrivilege = RtlConvertUlongToLuid(Privilege);
|
|
|
|
|
|
NewPrivileges->PrivilegeCount = 1;
|
|
NewPrivileges->Privileges[0].Luid = LuidPrivilege;
|
|
NewPrivileges->Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;
|
|
|
|
|
|
|
|
//
|
|
// Adjust the privilege
|
|
//
|
|
|
|
Status = ZwAdjustPrivilegesToken(
|
|
Token, // TokenHandle
|
|
FALSE, // DisableAllPrivileges
|
|
NewPrivileges, // NewPrivileges
|
|
sizeof(Buffer1), // BufferLength
|
|
OldPrivileges, // PreviousState (OPTIONAL)
|
|
&Length // ReturnLength
|
|
);
|
|
|
|
|
|
TmpStatus = ZwClose(Token);
|
|
ASSERT(NT_SUCCESS(TmpStatus));
|
|
|
|
|
|
//
|
|
// Map the success code NOT_ALL_ASSIGNED to an appropriate error
|
|
// since we're only trying to adjust the one privilege.
|
|
//
|
|
|
|
if (Status == STATUS_NOT_ALL_ASSIGNED) {
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// If there are no privileges in the previous state, there were
|
|
// no changes made. The previous state of the privilege
|
|
// is whatever we tried to change it to.
|
|
//
|
|
|
|
if (OldPrivileges->PrivilegeCount == 0) {
|
|
|
|
(*WasEnabled) = Enable;
|
|
|
|
} else {
|
|
|
|
(*WasEnabled) =
|
|
(OldPrivileges->Privileges[0].Attributes & SE_PRIVILEGE_ENABLED)
|
|
? TRUE : FALSE;
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// May move this into RTL someday, and let it access internals directly.
|
|
//
|
|
|
|
NTSTATUS
|
|
RtlGetSecurityInformationFromSecurityDescriptor(
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
OUT PSECURITY_INFORMATION SecurityInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure sets the security information bits for fields
|
|
that are valid in the security descriptor.
|
|
|
|
Arguments:
|
|
|
|
SecurityDescriptor - The passed-in security descriptor.
|
|
|
|
SecurityInformation - Returns the bitmask.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - The bitmask was returned successfully.
|
|
|
|
Other status values if the security descriptor is invalid.
|
|
|
|
--*/
|
|
|
|
{
|
|
SECURITY_INFORMATION BuiltSecurityInformation = 0;
|
|
PACL TempAcl;
|
|
PSID TempSid;
|
|
BOOLEAN Present;
|
|
BOOLEAN Defaulted;
|
|
NTSTATUS Status;
|
|
|
|
Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor,
|
|
&Present,
|
|
&TempAcl,
|
|
&Defaulted);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
if (Present) {
|
|
BuiltSecurityInformation |= DACL_SECURITY_INFORMATION;
|
|
}
|
|
|
|
Status = RtlGetSaclSecurityDescriptor(SecurityDescriptor,
|
|
&Present,
|
|
&TempAcl,
|
|
&Defaulted);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
if (Present) {
|
|
BuiltSecurityInformation |= SACL_SECURITY_INFORMATION;
|
|
}
|
|
|
|
Status = RtlGetOwnerSecurityDescriptor(SecurityDescriptor,
|
|
&TempSid,
|
|
&Defaulted);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
if (TempSid != NULL) {
|
|
BuiltSecurityInformation |= OWNER_SECURITY_INFORMATION;
|
|
}
|
|
|
|
Status = RtlGetGroupSecurityDescriptor(SecurityDescriptor,
|
|
&TempSid,
|
|
&Defaulted);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
if (TempSid != NULL) {
|
|
BuiltSecurityInformation |= GROUP_SECURITY_INFORMATION;
|
|
}
|
|
|
|
*SecurityInformation = BuiltSecurityInformation;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
MRxSmbInitializeCSC (
|
|
PUNICODE_STRING SmbMiniRedirectorName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the CSC database
|
|
|
|
Arguments:
|
|
|
|
SmbMiniRedirectorName - the mini redirector name
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successfull otherwise appropriate error
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
UNICODE_STRING CscLinkName;
|
|
ULONG ii;
|
|
|
|
C_ASSERT(sizeof(GENERICHEADER)==64);
|
|
C_ASSERT(sizeof(INODEHEADER)==sizeof(GENERICHEADER));
|
|
C_ASSERT(sizeof(SHAREHEADER)==sizeof(GENERICHEADER));
|
|
C_ASSERT(sizeof(FILEHEADER)==sizeof(GENERICHEADER));
|
|
C_ASSERT(sizeof(QHEADER)==sizeof(GENERICHEADER));
|
|
|
|
if(!MRxSmbIsCscEnabled) {
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
try {
|
|
|
|
InitializeListHead(&xCscFcbsList);
|
|
|
|
ExInitializeFastMutex(&CscServerEntryTransitioningMutex);
|
|
KeInitializeEvent(
|
|
&CscServerEntryTransitioningEvent,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
//initialize the "semaphore" for the shadow critical section......
|
|
InitializeShadowCritStructures();
|
|
|
|
//create a symbolic link for the agent
|
|
RtlInitUnicodeString(&CscLinkName,MRXSMB_CSC_SYMLINK_NAME);
|
|
IoDeleteSymbolicLink(&CscLinkName);
|
|
Status = IoCreateSymbolicLink(&CscLinkName,SmbMiniRedirectorName);
|
|
if (!NT_SUCCESS( Status )) {
|
|
try_return( Status );
|
|
}
|
|
|
|
MRxSmbCscLinkCreated = TRUE;
|
|
|
|
try_exit: NOTHING;
|
|
} finally {
|
|
if (Status != STATUS_SUCCESS) {
|
|
MRxSmbUninitializeCSC();
|
|
}
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
VOID
|
|
MRxSmbUninitializeCSC(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine uninitializes the CSC database
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ii;
|
|
|
|
if(!MRxSmbIsCscEnabled) {
|
|
return;
|
|
}
|
|
if (MRxSmbCscLinkCreated) {
|
|
UNICODE_STRING CscLinkName;
|
|
RtlInitUnicodeString(&CscLinkName,MRXSMB_CSC_SYMLINK_NAME);
|
|
Status = IoDeleteSymbolicLink(&CscLinkName);
|
|
ASSERT(Status==STATUS_SUCCESS);
|
|
}
|
|
|
|
ii = CloseShadowDB();
|
|
CleanupShadowCritStructures();
|
|
|
|
//get rid of references on events
|
|
if (MRxSmbAgentSynchronizationEvent!=NULL) {
|
|
ObDereferenceObject(MRxSmbAgentSynchronizationEvent);
|
|
MRxSmbAgentSynchronizationEvent = NULL;
|
|
}
|
|
if (MRxSmbAgentFillEvent!=NULL) {
|
|
ObDereferenceObject(MRxSmbAgentFillEvent);
|
|
MRxSmbAgentFillEvent = NULL;
|
|
}
|
|
}
|
|
|
|
// The CSC database access rights are stored in terms of SID. The SID is the
|
|
// user security id that persists across reboots. The retrieval of the SID
|
|
// is a complicated process. This mechanism is captured by the two routines
|
|
// CscRetrieveSid and CscDiscardSid. This mechanism is required to avoid
|
|
// redundant copying of the SID data from the buffer allocated by the security
|
|
// sub system to the redirector buffers. Consequently we need to create a new
|
|
// data type which contains the SID alongwith the context ( security allocated
|
|
// buffer ). This buffer is allocated on retrieval and freed on discard.
|
|
|
|
NTSTATUS
|
|
CscRetrieveSid(
|
|
PRX_CONTEXT pRxContext,
|
|
PSID_CONTEXT pSidContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the SID associated with a given context
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RX_CONTEXT instance
|
|
|
|
pSidContext - the SID context
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successfull otherwise appropriate error
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PIO_SECURITY_CONTEXT pSecurityContext;
|
|
|
|
PACCESS_TOKEN pToken;
|
|
|
|
pSecurityContext = pRxContext->Create.NtCreateParameters.SecurityContext;
|
|
|
|
if (pSecurityContext != NULL) {
|
|
pToken = pSecurityContext->AccessState->SubjectSecurityContext.ClientToken;
|
|
|
|
if (pToken == NULL) {
|
|
pToken = pSecurityContext->AccessState->SubjectSecurityContext.PrimaryToken;
|
|
}
|
|
} else {
|
|
pSidContext->Context = NULL;
|
|
pSidContext->pSid = NULL;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (pToken != NULL) {
|
|
Status = SeQueryInformationToken(
|
|
pToken,
|
|
TokenUser,
|
|
&pSidContext->Context);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
PTOKEN_USER pCurrentTokenUser;
|
|
|
|
pCurrentTokenUser = (PTOKEN_USER)pSidContext->Context;
|
|
|
|
pSidContext->pSid = pCurrentTokenUser->User.Sid;
|
|
}
|
|
}
|
|
else {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
CscDiscardSid(
|
|
PSID_CONTEXT pSidContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine discards the sid context
|
|
|
|
Arguments:
|
|
|
|
pSidContext - the SID context
|
|
|
|
--*/
|
|
{
|
|
PTOKEN_USER pTokenUser;
|
|
|
|
pTokenUser = (PTOKEN_USER)pSidContext->Context;
|
|
|
|
if (pTokenUser != NULL) {
|
|
ASSERT(pTokenUser->User.Sid == pSidContext->pSid);
|
|
|
|
ExFreePool(pTokenUser);
|
|
}
|
|
}
|
|
|
|
BOOLEAN UseEagerEvaluation = TRUE;
|
|
|
|
BOOLEAN
|
|
CscpAccessCheck(
|
|
PCACHED_SECURITY_INFORMATION pCachedSecurityInformation,
|
|
ULONG CachedSecurityInformationLength,
|
|
CSC_SID_INDEX SidIndex,
|
|
ACCESS_MASK AccessMask,
|
|
BOOLEAN *pSidHasAccessMask
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine evaluates the access rights for a given SID index with the
|
|
cached security information
|
|
|
|
Arguments:
|
|
|
|
pCachedSecurityInformation - the cached security information
|
|
|
|
CachedSecurityInformationLength - the cached security information length
|
|
|
|
SidIndex - the SID index
|
|
|
|
AccessMask - desired access
|
|
|
|
--*/
|
|
{
|
|
CSC_SID_INDEX i;
|
|
BOOLEAN AccessGranted = FALSE;
|
|
|
|
*pSidHasAccessMask = FALSE;
|
|
|
|
if (CachedSecurityInformationLength == 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) &&
|
|
(pCachedSecurityInformation->AccessRights[i].SidIndex != SidIndex));
|
|
i++) {
|
|
}
|
|
|
|
if (i < CSC_MAXIMUM_NUMBER_OF_CACHED_SID_INDEXES) {
|
|
// Ensure that the desired access is a subset of the
|
|
// maximal access rights allowed for this SID
|
|
|
|
*pSidHasAccessMask = TRUE;
|
|
|
|
AccessGranted = ((AccessMask &
|
|
pCachedSecurityInformation->AccessRights[i].MaximalRights)
|
|
== AccessMask);
|
|
} else {
|
|
// if the index cannot be found, ensure that the SID_INDEXES
|
|
// are valid. If none of them are valid then we treat the
|
|
// cached security information as being invalid and let the
|
|
// access through
|
|
|
|
for(i = 0;
|
|
((i < CSC_MAXIMUM_NUMBER_OF_CACHED_SID_INDEXES) &&
|
|
(pCachedSecurityInformation->AccessRights[i].SidIndex ==
|
|
CSC_INVALID_SID_INDEX));
|
|
i++);
|
|
|
|
if (i == CSC_MAXIMUM_NUMBER_OF_CACHED_SID_INDEXES) {
|
|
AccessGranted = TRUE;
|
|
}
|
|
}
|
|
} else if (CachedSecurityInformationLength == 0) {
|
|
AccessGranted = TRUE;
|
|
} else {
|
|
AccessGranted = FALSE;
|
|
}
|
|
|
|
return AccessGranted;
|
|
}
|
|
|
|
BOOLEAN
|
|
CscAccessCheck(
|
|
HSHADOW hParent,
|
|
HSHADOW hFile,
|
|
PRX_CONTEXT RxContext,
|
|
ACCESS_MASK AccessMask,
|
|
PCACHED_SECURITY_INFORMATION pCachedSecurityInformationForShadow,
|
|
PCACHED_SECURITY_INFORMATION pCachedSecurityInformationForShare
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the access check for a given LUID and an ACCESS_MASK
|
|
against the saved rights
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
TRUE -- if access is granted
|
|
|
|
FALSE -- if access is denied
|
|
|
|
Notes:
|
|
|
|
This routine is the primary routine for evaluating access rights. In order
|
|
to acheive total encapsulation the signature of this routine needs to be
|
|
specified such that the eager evaluation approach as well as the lazy
|
|
evaluation approach can be supported.
|
|
|
|
This is a kernel mode only routine.
|
|
|
|
The ACCESS_MASK as specified in NT consists of two parts.. the lower 16 bits
|
|
are specific rights ( specified by file system etc. ) while the upper 16 bits
|
|
are generic rights common to all components.
|
|
|
|
The cached access rights stored in the CSC data structure store the specific
|
|
rights. Consequently the ACCESS_MASK specified needs to be stripped of the
|
|
generic rights bit before comparing them.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN AccessGranted = FALSE, SidHasAccessMask;
|
|
SID_CONTEXT SidContext;
|
|
|
|
Status = CscRetrieveSid(
|
|
RxContext,
|
|
&SidContext);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
if (UseEagerEvaluation) {
|
|
HSHARE hShare = 0;
|
|
|
|
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;
|
|
}
|
|
|
|
// Check the share level ACL if there is any.
|
|
if (GetAncestorsHSHADOW(
|
|
hFile,
|
|
NULL,
|
|
&hShare)) {
|
|
|
|
BytesReturned = sizeof(CachedSecurityInformation);
|
|
|
|
CscStatus = GetShareInfoEx(
|
|
hShare,
|
|
NULL,
|
|
NULL,
|
|
&CachedSecurityInformation,
|
|
&BytesReturned);
|
|
|
|
// return the info if the caller want's it
|
|
if (pCachedSecurityInformationForShare)
|
|
{
|
|
*pCachedSecurityInformationForShare = CachedSecurityInformation;
|
|
}
|
|
|
|
if (CscStatus == ERROR_SUCCESS) {
|
|
AccessGranted = CscpAccessCheck(
|
|
&CachedSecurityInformation,
|
|
BytesReturned,
|
|
SidIndex,
|
|
AccessMask & FILE_SHARE_VALID_FLAGS,
|
|
&SidHasAccessMask
|
|
);
|
|
|
|
// if access was not granted for a non-guest
|
|
// because there was no accessmask for him, then check whether
|
|
// he should be allowed access as guest
|
|
|
|
if (!AccessGranted && (SidIndex != CSC_GUEST_SID_INDEX) && !SidHasAccessMask)
|
|
{
|
|
AccessGranted = CscpAccessCheck(
|
|
&CachedSecurityInformation,
|
|
BytesReturned,
|
|
CSC_GUEST_SID_INDEX,
|
|
AccessMask & FILE_SHARE_VALID_FLAGS,
|
|
&SidHasAccessMask
|
|
);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (AccessGranted) {
|
|
|
|
BytesReturned = sizeof(CachedSecurityInformation);
|
|
|
|
CscStatus = GetShadowInfoEx(
|
|
hParent,
|
|
hFile,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&CachedSecurityInformation,
|
|
&BytesReturned);
|
|
if (CscStatus == ERROR_SUCCESS) {
|
|
|
|
// return the info if the caller want's it
|
|
if (pCachedSecurityInformationForShadow)
|
|
{
|
|
*pCachedSecurityInformationForShadow = CachedSecurityInformation;
|
|
}
|
|
|
|
AccessGranted = CscpAccessCheck(
|
|
&CachedSecurityInformation,
|
|
BytesReturned,
|
|
SidIndex,
|
|
AccessMask & 0x1ff,
|
|
&SidHasAccessMask
|
|
);
|
|
|
|
// if access was not granted for a non-guest
|
|
// because there was no accessmask for him, then check whether
|
|
// he should be allowed access as guest
|
|
if (!AccessGranted && (SidIndex != CSC_GUEST_SID_INDEX) && !SidHasAccessMask)
|
|
{
|
|
AccessGranted = CscpAccessCheck(
|
|
&CachedSecurityInformation,
|
|
BytesReturned,
|
|
CSC_GUEST_SID_INDEX,
|
|
AccessMask & 0x1ff,
|
|
&SidHasAccessMask
|
|
);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CscDiscardSid(&SidContext);
|
|
}
|
|
|
|
if (RxContext->CurrentIrp && (RxContext->CurrentIrp->Tail.Overlay.OriginalFileObject->FileName.Length > 0)) {
|
|
RxDbgTrace(0,Dbg,
|
|
("CscAccessCheck for %wZ DesiredAccess %lx AccessGranted %lx\n",
|
|
&RxContext->CurrentIrp->Tail.Overlay.OriginalFileObject->FileName,
|
|
AccessMask,
|
|
AccessGranted));
|
|
} else {
|
|
RxDbgTrace(0,Dbg,
|
|
("CscAccessCheck for DesiredAccess %lx AccessGranted %lx\n",
|
|
AccessMask,
|
|
AccessGranted));
|
|
}
|
|
|
|
return AccessGranted;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbCscAcquireSmbFcb (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN ULONG TypeOfAcquirePlusFlags,
|
|
OUT SMBFCB_HOLDING_STATE *SmbFcbHoldingState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the readwrite synchronization that is required for
|
|
keeping the cache consistent. Basically, the rule is many-readers-one-writer.
|
|
This code relies on being able to use the minirdr context for links.
|
|
|
|
A key concept here is that if we are entered and the minirdr context
|
|
is nonull, then we are being reentered(!) after being queued and our
|
|
acquire has succeeded.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
TypeOfAcquirePlusFlags -- flags for resource acquisition
|
|
|
|
SmbFcbHoldingState -- resource holding state on exit
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS - the lock was acquired
|
|
STATUS_CANCELLED - the operation was cancelled
|
|
while you were waiting
|
|
STATUS_PENDING - the lock was not acquire; the operation
|
|
will be issued when you do get it
|
|
STATUS_LOCK_NOT_GRANTED - couldn't get it and fail
|
|
immediately was spec'd
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_PENDING;
|
|
RxCaptureFcb;
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
BOOLEAN MutexAcquired = FALSE;
|
|
DEBUG_ONLY_DECL(BOOLEAN HadToWait = FALSE;)
|
|
USHORT TypeOfAcquire = (USHORT)TypeOfAcquirePlusFlags;
|
|
BOOLEAN FailImmediately = BooleanFlagOn(TypeOfAcquirePlusFlags,
|
|
FailImmediately_SmbFcbAcquire);
|
|
BOOLEAN DroppingFcbLock = BooleanFlagOn(TypeOfAcquirePlusFlags,
|
|
DroppingFcbLock_SmbFcbAcquire);
|
|
|
|
PMRXSMBCSC_SYNC_RX_CONTEXT pRxSyncContext
|
|
= MRxSmbGetMinirdrContextForCscSync(RxContext);
|
|
|
|
RxDbgTrace(0,Dbg,("MRxSmbCscAcquireSmbFcb"
|
|
" %08lx %08lx %08lx %08lx <%wZ>\n",
|
|
RxContext, TypeOfAcquire,
|
|
smbFcb, smbFcb->CscOutstandingReaders,
|
|
GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext)));
|
|
|
|
ASSERT ((TypeOfAcquire==Shared_SmbFcbAcquire)
|
|
||(TypeOfAcquire==Exclusive_SmbFcbAcquire));
|
|
ASSERT (sizeof(MRXSMBCSC_SYNC_RX_CONTEXT) <= MRX_CONTEXT_SIZE);
|
|
|
|
ExAcquireFastMutex(&MRxSmbSerializationMutex);
|
|
MutexAcquired = TRUE;
|
|
|
|
ASSERT(pRxSyncContext->Dummy == 0);
|
|
|
|
if (pRxSyncContext->TypeOfAcquire == 0) {
|
|
pRxSyncContext->TypeOfAcquire = TypeOfAcquire;
|
|
pRxSyncContext->FcbLockWasDropped = FALSE;
|
|
|
|
if (smbFcb->CscReadWriteWaitersList.Flink==NULL) {
|
|
InitializeListHead(&smbFcb->CscReadWriteWaitersList);
|
|
}
|
|
|
|
do {
|
|
if (pRxSyncContext->FcbLockWasDropped){
|
|
NTSTATUS AStatus;
|
|
RxDbgTrace(
|
|
0,Dbg,
|
|
("MRxSmbCscAcquireSmbFcb %08lx acquireing fcblock\n",
|
|
RxContext));
|
|
|
|
Status = RxAcquireExclusiveFcbResourceInMRx(capFcb);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
pRxSyncContext->FcbLockWasDropped = FALSE;
|
|
|
|
Status = STATUS_PENDING;
|
|
|
|
// Acquire the mutex again
|
|
ExAcquireFastMutex(&MRxSmbSerializationMutex);
|
|
MutexAcquired = TRUE;
|
|
}
|
|
|
|
//if no one is waiting, maybe we can get right in.....
|
|
if (IsListEmpty(&smbFcb->CscReadWriteWaitersList)) {
|
|
if (TypeOfAcquire==Shared_SmbFcbAcquire) {
|
|
if (smbFcb->CscOutstandingReaders >= 0) {
|
|
smbFcb->CscOutstandingReaders++;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
if (smbFcb->CscOutstandingReaders == 0) {
|
|
smbFcb->CscOutstandingReaders--; //sets to -1
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((Status == STATUS_PENDING) && FailImmediately) {
|
|
Status = STATUS_LOCK_NOT_GRANTED;
|
|
}
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
InsertTailList(&smbFcb->CscReadWriteWaitersList,
|
|
&pRxSyncContext->CscSyncLinks);
|
|
if (DroppingFcbLock) {
|
|
RxDbgTrace(
|
|
0,Dbg,
|
|
("MRxSmbCscAcquireSmbFcb %08lx dropping fcblock\n",
|
|
RxContext));
|
|
RxReleaseFcbResourceInMRx(capFcb);
|
|
pRxSyncContext->FcbLockWasDropped = TRUE;
|
|
}
|
|
if (FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
|
|
ASSERT(Status == STATUS_PENDING);
|
|
goto FINALLY;
|
|
}
|
|
|
|
KeInitializeEvent( &RxContext->SyncEvent,
|
|
NotificationEvent,
|
|
FALSE );
|
|
ExReleaseFastMutex( &MRxSmbSerializationMutex );
|
|
MutexAcquired = FALSE;
|
|
RxWaitSync( RxContext );
|
|
|
|
if (BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_CANCELLED)) {
|
|
Status = STATUS_CANCELLED;
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
} while ( (pRxSyncContext->FcbLockWasDropped) && (Status == STATUS_SUCCESS) );
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
DbgDoit(
|
|
HadToWait = TRUE;
|
|
)
|
|
}
|
|
|
|
FINALLY:
|
|
ASSERT(pRxSyncContext->Dummy == 0);
|
|
if (MutexAcquired) {
|
|
ExReleaseFastMutex(&MRxSmbSerializationMutex);
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
*SmbFcbHoldingState = TypeOfAcquire;
|
|
RxDbgTrace(0,Dbg,("MRxSmbCscAcquireSmbFcb"
|
|
" %08lx acquired %s %s c=%08lx,%08lx\n",
|
|
RxContext,
|
|
(TypeOfAcquire==Shared_SmbFcbAcquire)
|
|
?"Shared":"Exclusive",
|
|
(HadToWait)?"HadToWait":"W/O waiting",
|
|
smbFcb->CscOutstandingReaders));
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
VOID
|
|
MRxSmbCscReleaseSmbFcb (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN SMBFCB_HOLDING_STATE *SmbFcbHoldingState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the readwrite synchronization that is required for
|
|
keeping the cache consistent. Basically, the rule is many-readers-one-writer.
|
|
This code relies on being able to use the minirdr context for links.
|
|
|
|
A key concept here is that if we are entered and the minirdr context
|
|
is nonull, then we are being reentered(!) after being queued and our
|
|
acquire has succeeded.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_PENDING;
|
|
RxCaptureFcb;
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
BOOLEAN Reader = (RxContext->MajorFunction == IRP_MJ_READ);
|
|
|
|
PMRXSMBCSC_SYNC_RX_CONTEXT pRxSyncContext
|
|
= MRxSmbGetMinirdrContextForCscSync(RxContext);
|
|
|
|
RxDbgTrace(0,Dbg,("MRxSmbCscReleaseSmbFcb entry"
|
|
" %08lx %08lx %08lx <%wZ>\n",
|
|
RxContext, smbFcb,
|
|
smbFcb->CscOutstandingReaders,
|
|
GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext)));
|
|
|
|
ASSERT(pRxSyncContext->Dummy == 0);
|
|
ASSERT(*SmbFcbHoldingState!=SmbFcb_NotHeld);
|
|
|
|
ExAcquireFastMutex(&MRxSmbSerializationMutex);
|
|
|
|
//first, undo my doings.....
|
|
if (*SmbFcbHoldingState == SmbFcb_HeldShared) {
|
|
ASSERT(smbFcb->CscOutstandingReaders>0);
|
|
smbFcb->CscOutstandingReaders--;
|
|
} else {
|
|
ASSERT(smbFcb->CscOutstandingReaders==-1);
|
|
smbFcb->CscOutstandingReaders++; //sets it to zero
|
|
}
|
|
|
|
//now start up some guys who may be waiting
|
|
if (!IsListEmpty(&smbFcb->CscReadWriteWaitersList)) {
|
|
|
|
PLIST_ENTRY ListEntry = smbFcb->CscReadWriteWaitersList.Flink;
|
|
|
|
for (;ListEntry != &smbFcb->CscReadWriteWaitersList;) {
|
|
PLIST_ENTRY ThisListEntry = ListEntry;
|
|
PMRXSMBCSC_SYNC_RX_CONTEXT innerRxSyncContext
|
|
= CONTAINING_RECORD(ListEntry,
|
|
MRXSMBCSC_SYNC_RX_CONTEXT,
|
|
CscSyncLinks);
|
|
PRX_CONTEXT innerRxContext
|
|
= CONTAINING_RECORD(innerRxSyncContext,
|
|
RX_CONTEXT,
|
|
MRxContext[0]);
|
|
ULONG innerTypeOfAcquire = (innerRxSyncContext->TypeOfAcquire);
|
|
|
|
//move down the list before removing this entry!!!
|
|
ListEntry = ListEntry->Flink;
|
|
|
|
// in the followng, Routine is used to restart an async guy. only
|
|
// create, read, and write currently come thru here and of these
|
|
// only read and write are async. so it is okay to ignore create
|
|
// w.r.t. seeting the Routine
|
|
ASSERT(innerRxSyncContext->Dummy == 0);
|
|
|
|
if (!innerRxSyncContext->FcbLockWasDropped) {
|
|
if (innerTypeOfAcquire==Shared_SmbFcbAcquire) {
|
|
if (smbFcb->CscOutstandingReaders < 0) break;
|
|
smbFcb->CscOutstandingReaders++;
|
|
} else {
|
|
if (smbFcb->CscOutstandingReaders != 0) break;
|
|
smbFcb->CscOutstandingReaders--; //sets to -1
|
|
}
|
|
}
|
|
ASSERT(&innerRxSyncContext->CscSyncLinks == ThisListEntry);
|
|
RemoveEntryList(ThisListEntry);
|
|
RxDbgTrace(
|
|
0,Dbg,
|
|
("MRxSmbCscReleaseSmbFcb acquired after for %s c=%08lx, %08lx\n",
|
|
(innerTypeOfAcquire==Shared_SmbFcbAcquire)
|
|
?"Shared":"Exclusive",
|
|
smbFcb->CscOutstandingReaders,
|
|
innerRxContext));
|
|
if (FlagOn(innerRxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
|
|
NTSTATUS PostStatus;
|
|
DbgDoit(InitializeListHead(&innerRxSyncContext->CscSyncLinks);)
|
|
|
|
PostStatus = RxPostToWorkerThread(
|
|
MRxSmbDeviceObject,
|
|
CriticalWorkQueue,
|
|
&innerRxContext->WorkQueueItem,
|
|
MRxSmbResumeAsyncReadWriteRequests,
|
|
innerRxContext);
|
|
ASSERT(PostStatus == STATUS_SUCCESS);
|
|
} else {
|
|
RxSignalSynchronousWaiter(innerRxContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(smbFcb->CscOutstandingReaders>=-1);
|
|
|
|
ExReleaseFastMutex(&MRxSmbSerializationMutex);
|
|
*SmbFcbHoldingState = SmbFcb_NotHeld;
|
|
|
|
RxDbgTrace(0,Dbg,("MRxSmbCscReleaseSmbFcb exit"
|
|
" %08lx %08lx\n", RxContext, smbFcb->CscOutstandingReaders));
|
|
}
|
|
|
|
VOID
|
|
MRxSmbCscSetFileInfoEpilogue (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PNTSTATUS Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the tail of a write operation for CSC. In
|
|
particular, if the written data overlaps or extends the cached prefix
|
|
then we write the data into the cache.
|
|
|
|
The status of the write operation is passed in case we someday find
|
|
things are so messed up that we want to return a failure even after
|
|
a successful read. not today however...
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS LocalStatus = STATUS_SUCCESS;
|
|
ULONG iRet,ShadowFileLength;
|
|
|
|
RxCaptureFcb;RxCaptureFobx;
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
BOOLEAN EnteredCriticalSection = FALSE;
|
|
|
|
FILE_INFORMATION_CLASS FileInformationClass;
|
|
PVOID pBuffer;
|
|
ULONG BufferLength;
|
|
|
|
_WIN32_FIND_DATA Find32;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry
|
|
= SmbCeGetAssociatedNetRootEntry(capFcb->pNetRoot);
|
|
|
|
BOOLEAN fDisconnected;
|
|
|
|
ULONG uShadowStatus;
|
|
DWORD dwNotifyFilter=0;
|
|
|
|
|
|
if(!MRxSmbIsCscEnabled ||
|
|
(fShadow == 0)||
|
|
(!smbFcb->hShadow)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
fDisconnected = MRxSmbCSCIsDisconnectedOpen(capFcb, smbSrvOpen);
|
|
|
|
RxDbgTrace(+1, Dbg,
|
|
("MRxSmbCscSetFileInfoEpilogue...%08lx on handle %08lx\n",
|
|
RxContext,
|
|
smbSrvOpen->hfShadow ));
|
|
|
|
if (*Status != STATUS_SUCCESS) {
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbCscSetFileInfoEpilogue exit w/o extending -> %08lx\n", Status ));
|
|
goto FINALLY;
|
|
}
|
|
|
|
FileInformationClass = RxContext->Info.FileInformationClass;
|
|
pBuffer = RxContext->Info.Buffer;
|
|
BufferLength = RxContext->Info.Length;
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscSetFileInfoEpilogue: Class %08lx size %08lx\n",
|
|
FileInformationClass,BufferLength));
|
|
|
|
switch (FileInformationClass) {
|
|
case FileBasicInformation:
|
|
break;
|
|
|
|
case FileAllocationInformation:
|
|
break;
|
|
|
|
case FileEndOfFileInformation:
|
|
break;
|
|
|
|
case FileDispositionInformation:
|
|
break;
|
|
|
|
case FileRenameInformation:
|
|
default:
|
|
|
|
goto FINALLY;
|
|
}
|
|
|
|
EnterShadowCritRx(RxContext);
|
|
EnteredCriticalSection = TRUE;
|
|
|
|
if(GetShadowInfo(smbFcb->hParentDir,
|
|
smbFcb->hShadow,
|
|
&Find32,
|
|
&uShadowStatus, NULL) < SRET_OK) {
|
|
goto FINALLY;
|
|
}
|
|
|
|
// Bypass the shadow if it is not visibile for this connection
|
|
if (!IsShadowVisible(fDisconnected,
|
|
Find32.dwFileAttributes,
|
|
uShadowStatus)) {
|
|
goto FINALLY;
|
|
}
|
|
|
|
if (FileInformationClass==FileBasicInformation) {
|
|
//copy the stuff from the userbuffer as appropriate...these values
|
|
//must be appropriate since we were successful
|
|
PFILE_BASIC_INFORMATION BasicInfo = (PFILE_BASIC_INFORMATION)pBuffer;
|
|
if (BasicInfo->FileAttributes != 0) {
|
|
Find32.dwFileAttributes = ((BasicInfo->FileAttributes & ~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_DIRECTORY))
|
|
| (Find32.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
|
|
;
|
|
dwNotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
|
|
if (fDisconnected)
|
|
{
|
|
uShadowStatus |= SHADOW_ATTRIB_CHANGE;
|
|
smbSrvOpen->Flags |= SMB_SRVOPEN_FLAG_SHADOW_ATTRIB_MODIFIED;
|
|
}
|
|
}
|
|
if ((BasicInfo->CreationTime.QuadPart != 0)&&
|
|
(BasicInfo->CreationTime.QuadPart != 0xffffffffffffffff))
|
|
{
|
|
COPY_LARGEINTEGER_TO_STRUCTFILETIME(Find32.ftCreationTime,
|
|
BasicInfo->CreationTime);
|
|
}
|
|
if ((BasicInfo->LastAccessTime.QuadPart != 0) &&
|
|
(BasicInfo->LastAccessTime.QuadPart != 0xffffffffffffffff))
|
|
{
|
|
COPY_LARGEINTEGER_TO_STRUCTFILETIME(Find32.ftLastAccessTime,
|
|
BasicInfo->LastAccessTime);
|
|
}
|
|
|
|
//
|
|
// If the user is specifying -1 for a field, that means
|
|
// we should leave that field unchanged, even if we might
|
|
// have otherwise set it ourselves. We'll set the Ccb flag
|
|
// saying that the user set the field so that we
|
|
// don't do our default updating.
|
|
//
|
|
// We set the field to 0 then so we know not to actually
|
|
// set the field to the user-specified (and in this case,
|
|
// illegal) value.
|
|
//
|
|
|
|
if (BasicInfo->LastWriteTime.QuadPart == 0xffffffffffffffff)
|
|
{
|
|
BasicInfo->LastWriteTime.QuadPart = 0;
|
|
|
|
if (fDisconnected)
|
|
{
|
|
smbSrvOpen->Flags |= SMB_SRVOPEN_FLAG_SHADOW_LWT_MODIFIED;
|
|
}
|
|
}
|
|
|
|
if (BasicInfo->LastWriteTime.QuadPart != 0)
|
|
{
|
|
ASSERT(BasicInfo->LastWriteTime.QuadPart != 0xffffffffffffffff);
|
|
|
|
COPY_LARGEINTEGER_TO_STRUCTFILETIME(Find32.ftLastWriteTime,
|
|
BasicInfo->LastWriteTime);
|
|
if (fDisconnected)
|
|
{
|
|
uShadowStatus |= SHADOW_TIME_CHANGE;
|
|
smbSrvOpen->Flags |= SMB_SRVOPEN_FLAG_SHADOW_LWT_MODIFIED;
|
|
}
|
|
dwNotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
|
|
}
|
|
}
|
|
else if (FileInformationClass==FileDispositionInformation)
|
|
{
|
|
if (fDisconnected)
|
|
{
|
|
// if this is a file and we are trying to delete it
|
|
// without permissions, then bail
|
|
|
|
if (!(Find32.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)&&
|
|
!(FILE_WRITE_DATA & smbSrvOpen->MaximalAccessRights)&&
|
|
!(FILE_WRITE_DATA & smbSrvOpen->GuestMaximalAccessRights))
|
|
{
|
|
*Status = STATUS_ACCESS_DENIED;
|
|
RxLog(("No rights to del %x in dcon Status=%x\n", smbFcb->hShadow, LocalStatus));
|
|
HookKdPrint(BADERRORS, ("No rights to del %x in dcon Status=%x\n", smbFcb->hShadow, LocalStatus));
|
|
}
|
|
else
|
|
{
|
|
LocalStatus = OkToDeleteObject(smbFcb->hParentDir, smbFcb->hShadow, &Find32, uShadowStatus, fDisconnected);
|
|
|
|
if (LocalStatus != STATUS_SUCCESS)
|
|
{
|
|
RxLog(("Can't del %x in dcon Status=%x\n", smbFcb->hShadow, LocalStatus));
|
|
*Status = LocalStatus;
|
|
}
|
|
}
|
|
}
|
|
|
|
goto FINALLY;
|
|
|
|
}
|
|
else {
|
|
//basically, all i can do here is to ensure that the shadow is no bigger than the size
|
|
//given, whether allocationsize or filesize. when we read back the actual size at close
|
|
//some readjusting may be required so we turn sparse on.
|
|
PFILE_END_OF_FILE_INFORMATION UserEndOfFileInformation
|
|
= (PFILE_END_OF_FILE_INFORMATION)pBuffer;
|
|
int iRet;
|
|
ULONG ShadowFileLength;
|
|
|
|
ASSERT( FIELD_OFFSET(FILE_END_OF_FILE_INFORMATION,EndOfFile)
|
|
== FIELD_OFFSET(FILE_ALLOCATION_INFORMATION,AllocationSize) );
|
|
|
|
//don't need the shadowreadwritemutex here because SetFileInfo has both resources...
|
|
//thus, no other operations can come down
|
|
|
|
if (!(CSCHFILE)(smbSrvOpen->hfShadow))
|
|
{
|
|
if (fDisconnected)
|
|
{
|
|
*Status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
}
|
|
|
|
goto FINALLY;
|
|
}
|
|
iRet = GetFileSizeLocal((CSCHFILE)(smbSrvOpen->hfShadow), &ShadowFileLength);
|
|
if (iRet<0) {
|
|
if (fDisconnected)
|
|
{
|
|
*Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
goto FINALLY;
|
|
}
|
|
if (ShadowFileLength != UserEndOfFileInformation->EndOfFile.QuadPart) {
|
|
NTSTATUS SetStatus;
|
|
PNT5CSC_MINIFILEOBJECT MiniFileObject
|
|
= (PNT5CSC_MINIFILEOBJECT)(smbSrvOpen->hfShadow);
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
ULONG DummyReturnedLength;
|
|
|
|
// If we are connected, don't extend sparse files!!!!
|
|
if (fDisconnected ||
|
|
(!(uShadowStatus & SHADOW_SPARSE) || (ShadowFileLength > UserEndOfFileInformation->EndOfFile.QuadPart)))
|
|
{
|
|
// DbgPrint("SetEof on %x Old=%x New=%x \n", smbFcb->hShadow, ShadowFileLength, UserEndOfFileInformation->EndOfFile.QuadPart);
|
|
|
|
SetStatus = Nt5CscXxxInformation(
|
|
(PCHAR)IRP_MJ_SET_INFORMATION,
|
|
MiniFileObject,
|
|
FileEndOfFileInformation,
|
|
sizeof(FILE_END_OF_FILE_INFORMATION),
|
|
pBuffer,
|
|
&DummyReturnedLength
|
|
);
|
|
}
|
|
|
|
#if defined(BITCOPY)
|
|
// Do I need to check if EOFinfo (a 64-bit value) is using
|
|
// the upper 32 bits? CscBmp library only supports 32-bit
|
|
// file sizes.
|
|
if (smbFcb->lpDirtyBitmap && fDisconnected &&
|
|
UserEndOfFileInformation->EndOfFile.HighPart == 0) {
|
|
// Is it ShadowFileLength?
|
|
CscBmpResize(
|
|
smbFcb->lpDirtyBitmap,
|
|
(DWORD)UserEndOfFileInformation->EndOfFile.QuadPart);
|
|
} else if (UserEndOfFileInformation->EndOfFile.HighPart != 0) {
|
|
// File is too big to be represented by a CscBmp, delete.
|
|
CscBmpMarkInvalid(smbFcb->lpDirtyBitmap);
|
|
}
|
|
#endif // defined(BITCOPY)
|
|
|
|
if (fDisconnected)
|
|
{
|
|
uShadowStatus |= SHADOW_DIRTY;
|
|
dwNotifyFilter |= FILE_NOTIFY_CHANGE_SIZE;
|
|
}
|
|
mSetBits(smbSrvOpen->Flags, SMB_SRVOPEN_FLAG_SHADOW_DATA_MODIFIED);
|
|
Find32.nFileSizeLow = (DWORD)UserEndOfFileInformation->EndOfFile.QuadPart;
|
|
}
|
|
|
|
}
|
|
|
|
if (fDisconnected)
|
|
{
|
|
MarkShareDirty(&smbFcb->sCscRootInfo.ShareStatus, smbFcb->sCscRootInfo.hShare);
|
|
}
|
|
|
|
if(SetShadowInfo(smbFcb->hParentDir,
|
|
smbFcb->hShadow,
|
|
&Find32,
|
|
uShadowStatus,
|
|
SHADOW_FLAGS_ASSIGN
|
|
| ((fDisconnected)?SHADOW_FLAGS_DONT_UPDATE_ORGTIME
|
|
:0)
|
|
) < SRET_OK) {
|
|
goto FINALLY;
|
|
}
|
|
|
|
|
|
FINALLY:
|
|
if (EnteredCriticalSection) {
|
|
LeaveShadowCritRx(RxContext);
|
|
}
|
|
|
|
// in disconnected state, report the changes
|
|
if (fDisconnected && dwNotifyFilter)
|
|
{
|
|
FsRtlNotifyFullReportChange(
|
|
pNetRootEntry->NetRoot.pNotifySync,
|
|
&pNetRootEntry->NetRoot.DirNotifyList,
|
|
(PSTRING)GET_ALREADY_PREFIXED_NAME(SrvOpen,capFcb),
|
|
(USHORT)(GET_ALREADY_PREFIXED_NAME(SrvOpen, capFcb)->Length -
|
|
smbFcb->MinimalCscSmbFcb.LastComponentLength),
|
|
NULL,
|
|
NULL,
|
|
dwNotifyFilter,
|
|
FILE_ACTION_MODIFIED,
|
|
NULL);
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbCscSetFileInfoEpilogue -> %08lx\n", Status ));
|
|
return;
|
|
}
|
|
|
|
//this could easily bein a .h file
|
|
|
|
int IoctlRegisterAgent(
|
|
ULONG_PTR uHwnd
|
|
);
|
|
|
|
int IoctlUnRegisterAgent(
|
|
ULONG_PTR uHwnd
|
|
);
|
|
|
|
int IoctlGetUNCPath(
|
|
LPCOPYPARAMS lpCopyParams
|
|
);
|
|
|
|
int IoctlBeginPQEnum(
|
|
LPPQPARAMS lpPQPar
|
|
);
|
|
|
|
int IoctlEndPQEnum(
|
|
LPPQPARAMS lpPQPar
|
|
);
|
|
|
|
int IoctlNextPriShadow(
|
|
LPPQPARAMS lpPQPar
|
|
);
|
|
|
|
int IoctlPrevPriShadow(
|
|
LPPQPARAMS lpPQPar
|
|
);
|
|
|
|
int IoctlGetShadowInfo(
|
|
LPSHADOWINFO lpShadowInfo
|
|
);
|
|
|
|
int IoctlSetShadowInfo(
|
|
LPSHADOWINFO lpShadowInfo
|
|
);
|
|
|
|
int IoctlChkUpdtStatus(
|
|
LPSHADOWINFO lpShadowInfo
|
|
);
|
|
|
|
int IoctlDoShadowMaintenance(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
BOOLEAN
|
|
CscCheckForNullW(
|
|
PWCHAR pBuf,
|
|
ULONG Count);
|
|
|
|
NTSTATUS
|
|
MRxSmbCscIoctlOpenForCopyChunk (
|
|
PRX_CONTEXT RxContext
|
|
);
|
|
NTSTATUS
|
|
MRxSmbCscIoctlCloseForCopyChunk (
|
|
PRX_CONTEXT RxContext
|
|
);
|
|
NTSTATUS
|
|
MRxSmbCscIoctlCopyChunk (
|
|
PRX_CONTEXT RxContext
|
|
);
|
|
|
|
int IoctlBeginReint(
|
|
LPSHADOWINFO lpShadowInfo
|
|
);
|
|
|
|
int IoctlEndReint(
|
|
LPSHADOWINFO lpShadowInfo
|
|
);
|
|
|
|
int IoctlCreateShadow(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlDeleteShadow(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlGetShareStatus(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlSetShareStatus(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlAddUse(
|
|
LPCOPYPARAMS lpCP
|
|
);
|
|
|
|
int IoctlDelUse(
|
|
LPCOPYPARAMS lpCP
|
|
);
|
|
|
|
int IoctlGetUse(
|
|
LPCOPYPARAMS lpCP
|
|
);
|
|
|
|
int IoctlSwitches(LPSHADOWINFO lpSI);
|
|
|
|
int IoctlGetShadow(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlAddHint( // Add a new hint or change an existing hint
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlDeleteHint( // Delete an existing hint
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlGetHint(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlGetGlobalStatus(
|
|
ULONG SessionId,
|
|
LPGLOBALSTATUS lpGS
|
|
);
|
|
|
|
int IoctlFindOpenHSHADOW
|
|
(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlFindNextHSHADOW
|
|
(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlFindCloseHSHADOW
|
|
(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlFindOpenHint
|
|
(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlFindNextHint
|
|
(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlFindCloseHint
|
|
(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlSetPriorityHSHADOW(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlGetPriorityHSHADOW(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
int IoctlGetAliasHSHADOW(
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
#define CSC_CASE(__case) \
|
|
case __case: \
|
|
RxDbgTrace(0,Dbg,("MRxSmbCscIoctl %08lx %s %08lx %08lx\n",RxContext,#__case,InputBuffer,OutputBuffer));
|
|
|
|
ULONG GetNextPriShadowCount = 0;
|
|
|
|
NTSTATUS
|
|
MRxSmbCscIoCtl(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the special IOCTL operation for the CSC agent.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
ShadowIRet is overloaded:
|
|
-1 == error, copy the error back
|
|
0 == error, return Wrong password (STATUS_WRONG_PASSWORD)
|
|
1 == success, output params, copy them back
|
|
2 == return status unmodified, no output params
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
ULONG IoControlCode = LowIoContext->ParamsFor.IoCtl.IoControlCode;
|
|
PBYTE InputBuffer = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
|
|
PBYTE pNewInputBuffer=NULL;
|
|
ULONG InputBufferLength = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
|
|
PBYTE OutputBuffer = LowIoContext->ParamsFor.IoCtl.pOutputBuffer;
|
|
ULONG OutputBufferLength = LowIoContext->ParamsFor.IoCtl.OutputBufferLength;
|
|
LONG ShadowIRet = 0;
|
|
CAPTURE_BUFFERS sCapBuff;
|
|
PBYTE pAuxBuf = NULL;
|
|
PBYTE pOrgBuf = NULL;
|
|
BOOLEAN SuppressFinalTrace = FALSE, fEnteredShadowCrit=FALSE;
|
|
KPROCESSOR_MODE RequestorMode;
|
|
ULONG SessionId = 0;
|
|
|
|
#if defined (_WIN64)
|
|
if (IoIs32bitProcess(RxContext->CurrentIrp)) {
|
|
RxDbgTrace(0, Dbg, ("32 bit IOCTL in 64 bit returning STATUS_NOT_IMPLEMENTED\n"));
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
#endif // _WIN64
|
|
|
|
|
|
if (RxContext != NULL && RxContext->CurrentIrp != NULL)
|
|
IoGetRequestorSessionId(RxContext->CurrentIrp, &SessionId);
|
|
|
|
try
|
|
{
|
|
RequestorMode = RxContext->CurrentIrp->RequestorMode;
|
|
|
|
if (
|
|
RequestorMode != KernelMode
|
|
&&
|
|
IoControlCode != IOCTL_GET_DEBUG_INFO
|
|
) {
|
|
if (CaptureInputBufferIfNecessaryAndProbe(
|
|
IoControlCode,
|
|
RxContext,
|
|
InputBuffer,
|
|
&sCapBuff,
|
|
&pAuxBuf,
|
|
&pOrgBuf,
|
|
&pNewInputBuffer)!=STATUS_SUCCESS) {
|
|
RxDbgTrace(0, Dbg, ("Invalid parameters for Ioctl=%x\n", IoControlCode));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pNewInputBuffer = InputBuffer;
|
|
}
|
|
// DbgPrint("MRxSmbCscIoCtl IoControlCode=%d\n", (IoControlCode >> 2) & 0xfff);
|
|
switch (IoControlCode) {
|
|
CSC_CASE(IOCTL_SHADOW_GETVERSION)
|
|
Status = (NTSTATUS)(SHADOW_VERSION); // no-op
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHADOW_REGISTER_AGENT)
|
|
ShadowIRet = IoctlRegisterAgent((ULONG_PTR)pNewInputBuffer);
|
|
if (ShadowIRet>=0) {
|
|
MRxSmbCscReleaseRxContextFromAgentWait();
|
|
}
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHADOW_UNREGISTER_AGENT)
|
|
ShadowIRet = IoctlUnRegisterAgent((ULONG_PTR)pNewInputBuffer);
|
|
if (ShadowIRet>=0) {
|
|
MRxSmbCscReleaseRxContextFromAgentWait();
|
|
}
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHADOW_GET_UNC_PATH)
|
|
ShadowIRet = IoctlGetUNCPath((LPCOPYPARAMS)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHADOW_BEGIN_PQ_ENUM)
|
|
ShadowIRet = IoctlBeginPQEnum((LPPQPARAMS)pNewInputBuffer);
|
|
GetNextPriShadowCount = 0;
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHADOW_END_PQ_ENUM)
|
|
ShadowIRet = IoctlEndPQEnum((LPPQPARAMS)pNewInputBuffer);
|
|
break;
|
|
|
|
//CSC_CASE(IOCTL_SHADOW_NEXT_PRI_SHADOW)
|
|
case IOCTL_SHADOW_NEXT_PRI_SHADOW: \
|
|
if ((GetNextPriShadowCount<6) || ((GetNextPriShadowCount%40)==0)) {
|
|
RxDbgTrace(0,Dbg,("MRxSmbCscIoctl %08lx %s(%d) %08lx %08lx\n",
|
|
RxContext,
|
|
"IOCTL_SHADOW_NEXT_PRI_SHADOW",GetNextPriShadowCount,
|
|
pNewInputBuffer,OutputBuffer));
|
|
}
|
|
ShadowIRet = IoctlNextPriShadow((LPPQPARAMS)pNewInputBuffer);
|
|
GetNextPriShadowCount++;
|
|
SuppressFinalTrace = TRUE;
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHADOW_PREV_PRI_SHADOW)
|
|
ShadowIRet = IoctlPrevPriShadow((LPPQPARAMS)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHADOW_GET_SHADOW_INFO)
|
|
ShadowIRet = IoctlGetShadowInfo((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHADOW_SET_SHADOW_INFO)
|
|
ShadowIRet = IoctlSetShadowInfo((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHADOW_CHK_UPDT_STATUS)
|
|
ShadowIRet = IoctlChkUpdtStatus((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_DO_SHADOW_MAINTENANCE)
|
|
{
|
|
LPSHADOWINFO pShadowInfo = (LPSHADOWINFO)pNewInputBuffer;
|
|
|
|
#if defined(REMOTE_BOOT)
|
|
// If this IOCTL is for turning caching back on we need to update
|
|
// the mini redirector accordingly.
|
|
if ((pShadowInfo->uOp == SHADOW_CHANGE_HANDLE_CACHING_STATE) &&
|
|
(pShadowInfo->uStatus != FALSE)) {
|
|
RxDbgTrace(0, Dbg, ("RB Client : Turning caching back on\n"));
|
|
MRxSmbOplocksDisabledOnRemoteBootClients = FALSE;
|
|
}
|
|
#endif // defined(REMOTE_BOOT)
|
|
|
|
ShadowIRet = IoctlDoShadowMaintenance(pShadowInfo);
|
|
}
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_GET_DEBUG_INFO)
|
|
ShadowIRet = 2;
|
|
Status = IoctlGetDebugInfo(
|
|
RxContext,
|
|
InputBuffer,
|
|
InputBufferLength,
|
|
OutputBuffer,
|
|
OutputBufferLength);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHADOW_COPYCHUNK)
|
|
ShadowIRet = 2; //not -1, 0 or 1, No out parameters, Status is returned unmodified
|
|
Status = MRxSmbCscIoctlCopyChunk(RxContext);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_CLOSEFORCOPYCHUNK)
|
|
ShadowIRet = 2; //not -1, 0 or 1, No out parameters, Status is returned unmodified
|
|
Status = MRxSmbCscIoctlCloseForCopyChunk(RxContext);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_OPENFORCOPYCHUNK)
|
|
ShadowIRet = 2; //not -1, 0 or 1, No out parameters, Status is returned unmodified
|
|
Status = MRxSmbCscIoctlOpenForCopyChunk(RxContext);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_IS_SERVER_OFFLINE)
|
|
{
|
|
LPSHADOWINFO pShadowInfo = (LPSHADOWINFO)pNewInputBuffer;
|
|
|
|
if (pShadowInfo->lpBuffer == NULL
|
|
||
|
|
CscCheckForNullW(pShadowInfo->lpBuffer, pShadowInfo->cbBufferSize/sizeof(WCHAR)) == TRUE
|
|
) {
|
|
ShadowIRet = 1;
|
|
pShadowInfo->uStatus = CscIsServerOffline((PWCHAR)pShadowInfo->lpBuffer);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_TAKE_SERVER_OFFLINE)
|
|
{
|
|
LPSHADOWINFO pShadowInfo = (LPSHADOWINFO)pNewInputBuffer;
|
|
|
|
if (pShadowInfo->lpBuffer != NULL
|
|
&&
|
|
CscCheckForNullW(pShadowInfo->lpBuffer, pShadowInfo->cbBufferSize/sizeof(WCHAR)) == TRUE
|
|
) {
|
|
ShadowIRet = 1;
|
|
pShadowInfo->uStatus = CscTakeServerOffline( (PWCHAR)pShadowInfo->lpBuffer);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_TRANSITION_SERVER_TO_OFFLINE)
|
|
{
|
|
LPSHADOWINFO pShadowInfo = (LPSHADOWINFO)pNewInputBuffer;
|
|
|
|
ShadowIRet = 2; //not -1, 0 or 1, No out parameters, Status is returned unmodified
|
|
Status = CscTransitionServerToOffline(
|
|
SessionId,
|
|
pShadowInfo->hShare,
|
|
pShadowInfo->uStatus);
|
|
// DbgPrint("###IOCTL_TRANSITION_SERVER_TO_OFFLINE: pulsing fill event\n");
|
|
MRxSmbCscSignalFillAgent(NULL, 0);
|
|
}
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_TRANSITION_SERVER_TO_ONLINE)
|
|
{
|
|
LPSHADOWINFO pShadowInfo = (LPSHADOWINFO)pNewInputBuffer;
|
|
|
|
ShadowIRet = 2; //not -1, 0 or 1, No out parameters, Status is returned unmodified
|
|
Status = CscTransitionServerToOnline(
|
|
pShadowInfo->hShare);
|
|
// DbgPrint("###IOCTL_TRANSITION_SERVER_TO_ONLINE: pulsing fill event\n");
|
|
MRxSmbCscSignalFillAgent(NULL, 0);
|
|
}
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_NAME_OF_SERVER_GOING_OFFLINE)
|
|
{
|
|
LPSHADOWINFO lpSI = (LPSHADOWINFO)pNewInputBuffer;
|
|
|
|
ShadowIRet = 1;
|
|
|
|
CscGetServerNameWaitingToGoOffline(
|
|
lpSI->lpBuffer,
|
|
&(lpSI->cbBufferSize),
|
|
&Status);
|
|
if (Status == STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
((LPSHADOWINFO)InputBuffer)->cbBufferSize = lpSI->cbBufferSize;
|
|
|
|
HookKdPrint(ALWAYS, ("Buffer too small, Need %d \n", ((LPSHADOWINFO)InputBuffer)->cbBufferSize));
|
|
}
|
|
}
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHAREID_TO_SHARENAME)
|
|
{
|
|
LPSHADOWINFO lpSI = (LPSHADOWINFO)pNewInputBuffer;
|
|
|
|
ShadowIRet = 1;
|
|
|
|
CscShareIdToShareName(
|
|
lpSI->hShare,
|
|
lpSI->lpBuffer,
|
|
&(lpSI->cbBufferSize),
|
|
&Status);
|
|
if (Status == STATUS_BUFFER_TOO_SMALL) {
|
|
((LPSHADOWINFO)InputBuffer)->cbBufferSize = lpSI->cbBufferSize;
|
|
|
|
HookKdPrint(
|
|
ALWAYS,
|
|
("Buffer small, Need %d \n", ((LPSHADOWINFO)InputBuffer)->cbBufferSize));
|
|
} else if (Status != STATUS_SUCCESS) {
|
|
lpSI->dwError = ERROR_FILE_NOT_FOUND;
|
|
ShadowIRet = -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
CSC_CASE(IOCTL_SHADOW_BEGIN_REINT)
|
|
ShadowIRet = CSCBeginReint(RxContext, (LPSHADOWINFO)pNewInputBuffer);
|
|
if (ShadowIRet >= 1)
|
|
{
|
|
ShadowIRet = 2;
|
|
Status = STATUS_PENDING;
|
|
}
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHADOW_END_REINT)
|
|
ShadowIRet = CSCEndReint((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHADOW_CREATE)
|
|
{
|
|
LPSHADOWINFO pShadowInfo = (LPSHADOWINFO)pNewInputBuffer;
|
|
|
|
ShadowIRet = -1;
|
|
if (pShadowInfo->lpFind32
|
|
&&
|
|
CscCheckForNullW(pShadowInfo->lpFind32->cFileName, MAX_PATH) == TRUE
|
|
) {
|
|
ShadowIRet = IoctlCreateShadow(pShadowInfo);
|
|
}
|
|
}
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SHADOW_DELETE)
|
|
ShadowIRet = IoctlDeleteShadow((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_GET_SHARE_STATUS)
|
|
ShadowIRet = IoctlGetShareStatus((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SET_SHARE_STATUS)
|
|
ShadowIRet = IoctlSetShareStatus((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_ADDUSE)
|
|
//ShadowIRet = IoctlAddUse((LPCOPYPARAMS)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_DELUSE)
|
|
//ShadowIRet = IoctlDelUse((LPCOPYPARAMS)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_GETUSE)
|
|
//ShadowIRet = IoctlGetUse((LPCOPYPARAMS)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SWITCHES)
|
|
ShadowIRet = IoctlSwitches((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_GETSHADOW)
|
|
{
|
|
LPSHADOWINFO pShadowInfo = (LPSHADOWINFO)pNewInputBuffer;
|
|
|
|
ShadowIRet = -1;
|
|
if (pShadowInfo->lpFind32
|
|
&&
|
|
CscCheckForNullW(pShadowInfo->lpFind32->cFileName, MAX_PATH) == TRUE
|
|
) {
|
|
ShadowIRet = IoctlGetShadow(pShadowInfo);
|
|
}
|
|
}
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_GETGLOBALSTATUS)
|
|
ShadowIRet = IoctlGetGlobalStatus(SessionId, (LPGLOBALSTATUS)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_FINDOPEN_SHADOW)
|
|
{
|
|
LPSHADOWINFO pShadowInfo = (LPSHADOWINFO)pNewInputBuffer;
|
|
|
|
ShadowIRet = -1;
|
|
if (pShadowInfo->lpFind32
|
|
&&
|
|
CscCheckForNullW(pShadowInfo->lpFind32->cFileName, MAX_PATH) == TRUE
|
|
) {
|
|
ShadowIRet = IoctlFindOpenHSHADOW(pShadowInfo);
|
|
}
|
|
}
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_FINDNEXT_SHADOW)
|
|
ShadowIRet = IoctlFindNextHSHADOW((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_FINDCLOSE_SHADOW)
|
|
ShadowIRet = IoctlFindCloseHSHADOW((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_GETPRIORITY_SHADOW)
|
|
ShadowIRet = IoctlGetPriorityHSHADOW((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_SETPRIORITY_SHADOW)
|
|
ShadowIRet = IoctlSetPriorityHSHADOW((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_ADD_HINT)
|
|
ShadowIRet = IoctlAddHint((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_DELETE_HINT)
|
|
ShadowIRet = IoctlDeleteHint((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_FINDOPEN_HINT)
|
|
{
|
|
LPSHADOWINFO pShadowInfo = (LPSHADOWINFO)pNewInputBuffer;
|
|
|
|
ShadowIRet = -1;
|
|
if (pShadowInfo->lpFind32
|
|
&&
|
|
CscCheckForNullW(pShadowInfo->lpFind32->cFileName, MAX_PATH) == TRUE
|
|
) {
|
|
ShadowIRet = IoctlFindOpenHint(pShadowInfo);
|
|
}
|
|
}
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_FINDNEXT_HINT)
|
|
ShadowIRet = IoctlFindNextHint((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
CSC_CASE(IOCTL_FINDCLOSE_HINT)
|
|
ShadowIRet = IoctlFindCloseHint((LPSHADOWINFO)pNewInputBuffer);
|
|
break;
|
|
|
|
default:
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbCscIoCtl not csc ioctl-> %08lx\n", Status ));
|
|
return Status;
|
|
}
|
|
if (ShadowIRet == 0) {
|
|
Status = STATUS_WRONG_PASSWORD;
|
|
} else if (ShadowIRet == -1) {
|
|
if (RequestorMode != KernelMode)
|
|
{
|
|
CopyBackIfNecessary(
|
|
IoControlCode,
|
|
InputBuffer,
|
|
&sCapBuff,
|
|
pAuxBuf,
|
|
pOrgBuf,
|
|
FALSE);
|
|
}
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
} else if (ShadowIRet == 1) {
|
|
|
|
if (RequestorMode != KernelMode)
|
|
{
|
|
CopyBackIfNecessary(
|
|
IoControlCode,
|
|
InputBuffer,
|
|
&sCapBuff,
|
|
pAuxBuf,
|
|
pOrgBuf,
|
|
TRUE);
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (SuppressFinalTrace) {
|
|
RxDbgTraceUnIndent(-1, Dbg);
|
|
} else {
|
|
RxDbgTrace(-1, Dbg,
|
|
("MRxSmbCscIoCtl -> %08lx %08lx\n", Status, ShadowIRet ));
|
|
}
|
|
}
|
|
except(MRxSmbCSCExceptionFilter( RxContext, GetExceptionInformation() ))
|
|
{
|
|
RxDbgTrace(0, Dbg, ("MrxSmbCSCIoctl: took an exception \r\n"));
|
|
LeaveShadowCritIfThisThreadOwnsIt();
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (pAuxBuf != NULL) {
|
|
// DbgPrint("Freeing pAuxBuf\n");
|
|
RxFreePool(pAuxBuf);
|
|
}
|
|
|
|
// DbgPrint("MRxSmbCscIoCtl exit 0x%x\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbCscObtainShareHandles (
|
|
IN OUT PUNICODE_STRING ShareName,
|
|
IN BOOLEAN DisconnectedMode,
|
|
IN BOOLEAN CopyChunkOpen,
|
|
IN OUT PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the obtains the handles (Share and root directory)
|
|
for a particular \server\share, updating the values in the netrootentry
|
|
if found.
|
|
|
|
Arguments:
|
|
|
|
pNetRootEntry - the SMB MRX net root data structure
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
STATUS_NOT_INPLEMENTED - couldn't find or create
|
|
STATUS_SUCCESS - found or created
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
|
|
|
|
BOOLEAN CreateIfNotFound = FALSE;
|
|
|
|
SHADOWINFO ShadowInfo;
|
|
|
|
DbgDoit(ASSERT(vfInShadowCrit));
|
|
|
|
if (fShadow == 0) {
|
|
return(Status);
|
|
}
|
|
|
|
if (pNetRootEntry->NetRoot.sCscRootInfo.hShare != 0) {
|
|
Status = STATUS_SUCCESS;
|
|
goto FINALLY;
|
|
}
|
|
|
|
// At this stage one of the following two assumptions should be TRUE.
|
|
// Connected Mode Operation ...
|
|
// In this instance the call can succeed only if the Net Root is
|
|
// marked as being shadowable by the CSC client and iot is of type
|
|
// Disk.
|
|
// Disconnected Mode Operation ...
|
|
// In this case we have not yet ascertained the type and attributes
|
|
// Therefore we let the call go through. If we can open the handle
|
|
// to the Share then we mark the net root to be of the appropriate
|
|
// type.
|
|
|
|
if ( !DisconnectedMode &&
|
|
!CopyChunkOpen &&
|
|
(/*!pNetRootEntry->NetRoot.CscEnabled ||*/
|
|
(pNetRootEntry->NetRoot.NetRootType != NET_ROOT_DISK))) {
|
|
goto FINALLY;
|
|
}
|
|
|
|
// allocate a buffer that's the right size: one extra char is
|
|
// for a trailing null and the other for a preceding L'\\'
|
|
|
|
if (ShadowingON()) {
|
|
if (!DisconnectedMode &&
|
|
pNetRootEntry->NetRoot.CscShadowable) {
|
|
CreateIfNotFound = TRUE;
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscObtainShareHandles...servershare=%wZ %08lx\n",
|
|
ShareName,CreateIfNotFound));
|
|
|
|
if (FindCreateShareForNt(
|
|
ShareName,
|
|
CreateIfNotFound,
|
|
&ShadowInfo,
|
|
NULL //this means don't tell me if you create
|
|
) == SRET_OK ) {
|
|
|
|
ASSERT(ShadowInfo.hShare != 0);
|
|
|
|
pNetRootEntry->NetRoot.sCscRootInfo.hShare = ShadowInfo.hShare;
|
|
pNetRootEntry->NetRoot.sCscRootInfo.hRootDir = ShadowInfo.hShadow;
|
|
pNetRootEntry->NetRoot.sCscRootInfo.ShareStatus = (USHORT)(ShadowInfo.uStatus);
|
|
pNetRootEntry->NetRoot.sCscRootInfo.Flags = 0;
|
|
|
|
RxLog(("OSHH...hDir=%x\n",pNetRootEntry->NetRoot.sCscRootInfo.hRootDir));
|
|
|
|
// if we are connected, by this time we have the smb caching flags
|
|
// we check to see whether these match those on the database
|
|
// If they don't, we stamp the new ones
|
|
if (!DisconnectedMode)
|
|
{
|
|
if ((ShadowInfo.uStatus & SHARE_CACHING_MASK)!=
|
|
(ULONG)SMB_CSC_BITS_TO_DATABASE_CSC_BITS(pNetRootEntry->NetRoot.CscFlags))
|
|
{
|
|
// RxDbgTrace(0, Dbg, ("Mismatched smb caching flags, stamping %x on hShare=%x\n",
|
|
// SMB_CSC_BITS_TO_DATABASE_CSC_BITS(pNetRootEntry->NetRoot.CscFlags),
|
|
// pNetRootEntry->NetRoot.sCscRootInfo.hShare));
|
|
|
|
pNetRootEntry->NetRoot.sCscRootInfo.ShareStatus &= ~SHARE_CACHING_MASK;
|
|
pNetRootEntry->NetRoot.sCscRootInfo.ShareStatus |= SMB_CSC_BITS_TO_DATABASE_CSC_BITS(pNetRootEntry->NetRoot.CscFlags);
|
|
|
|
SetShareStatus( pNetRootEntry->NetRoot.sCscRootInfo.hShare,
|
|
pNetRootEntry->NetRoot.sCscRootInfo.ShareStatus,
|
|
SHADOW_FLAGS_ASSIGN);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// in disconnected mode we use the last set of flags
|
|
pNetRootEntry->NetRoot.CscFlags = DATABASE_CSC_BITS_TO_SMB_CSC_BITS(pNetRootEntry->NetRoot.sCscRootInfo.ShareStatus);
|
|
|
|
RxDbgTrace(0, Dbg, ("Setting CscFlags=%x on the netrootentry %x in disconnected state\n",pNetRootEntry->NetRoot.CscFlags, pNetRootEntry));
|
|
|
|
switch (pNetRootEntry->NetRoot.CscFlags) {
|
|
case SMB_CSC_CACHE_AUTO_REINT:
|
|
case SMB_CSC_CACHE_VDO:
|
|
pNetRootEntry->NetRoot.CscEnabled = TRUE;
|
|
pNetRootEntry->NetRoot.CscShadowable = TRUE;
|
|
break;
|
|
|
|
case SMB_CSC_CACHE_MANUAL_REINT:
|
|
pNetRootEntry->NetRoot.CscEnabled = TRUE;
|
|
pNetRootEntry->NetRoot.CscShadowable = FALSE;
|
|
break;
|
|
|
|
case SMB_CSC_NO_CACHING:
|
|
pNetRootEntry->NetRoot.CscEnabled = FALSE;
|
|
pNetRootEntry->NetRoot.CscShadowable = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
if (DisconnectedMode) {
|
|
Status = STATUS_BAD_NETWORK_PATH;
|
|
} else if (!CreateIfNotFound) {
|
|
pNetRootEntry->NetRoot.sCscRootInfo.hShare = 0;
|
|
pNetRootEntry->NetRoot.sCscRootInfo.hRootDir = 0;
|
|
pNetRootEntry->NetRoot.sCscRootInfo.ShareStatus = 0;
|
|
pNetRootEntry->NetRoot.sCscRootInfo.Flags = 0;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
FINALLY:
|
|
|
|
RxDbgTrace(
|
|
-1,
|
|
Dbg,
|
|
("MRxSmbCscObtainShareHandles -> %08lx (h=%08lx)\n",
|
|
Status, pNetRootEntry->NetRoot.sCscRootInfo.hShare ));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MRxSmbCscPartOfCreateVNetRoot (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN OUT PMRX_NET_ROOT NetRoot )
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PMRX_SRV_CALL SrvCall;
|
|
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
|
HSHARE hShare;
|
|
|
|
if(!MRxSmbIsCscEnabled ||
|
|
(fShadow == 0)
|
|
) {
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
ASSERT(RxContext->MajorFunction == IRP_MJ_CREATE);
|
|
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(NetRoot->pSrvCall);
|
|
pNetRootEntry = SmbCeGetAssociatedNetRootEntry(NetRoot);
|
|
|
|
if (!CscIsDfsOpen(RxContext)) {
|
|
BOOLEAN Disconnected = SmbCeIsServerInDisconnectedMode(pServerEntry);
|
|
|
|
EnterShadowCritRx(RxContext);
|
|
|
|
// force a database entry refresh
|
|
hShare = pNetRootEntry->NetRoot.sCscRootInfo.hShare;
|
|
pNetRootEntry->NetRoot.sCscRootInfo.hShare = 0;
|
|
#if 0
|
|
if ((NetRoot->pNetRootName->Length >= (sizeof(L"\\win95b\\fat")-2)) &&
|
|
!memcmp(NetRoot->pNetRootName->Buffer, L"\\win95b\\fat", sizeof(L"\\win95b\\fat")-2))
|
|
{
|
|
pNetRootEntry->NetRoot.CscShadowable =
|
|
pNetRootEntry->NetRoot.CscEnabled = TRUE;
|
|
}
|
|
#endif
|
|
Status = MRxSmbCscObtainShareHandles(
|
|
NetRoot->pNetRootName,
|
|
Disconnected,
|
|
FALSE,
|
|
pNetRootEntry
|
|
);
|
|
|
|
// update the share rights if necessary
|
|
|
|
if (!Disconnected) {
|
|
|
|
if(pNetRootEntry->NetRoot.UpdateCscShareRights) {
|
|
if (pNetRootEntry->NetRoot.sCscRootInfo.hShare != 0) {
|
|
CSC_SID_ACCESS_RIGHTS AccessRights[2];
|
|
|
|
DWORD CscStatus;
|
|
|
|
SID_CONTEXT SidContext;
|
|
|
|
// not a DFS root
|
|
pNetRootEntry->NetRoot.sCscRootInfo.Flags = 0;
|
|
|
|
if (CscRetrieveSid(RxContext,&SidContext) == STATUS_SUCCESS) {
|
|
AccessRights[0].pSid = SidContext.pSid;
|
|
AccessRights[0].SidLength = RtlLengthSid(SidContext.pSid);
|
|
AccessRights[0].MaximalAccessRights = pNetRootEntry->MaximalAccessRights;
|
|
|
|
AccessRights[1].pSid = CSC_GUEST_SID;
|
|
AccessRights[1].SidLength = CSC_GUEST_SID_LENGTH;
|
|
AccessRights[1].MaximalAccessRights = pNetRootEntry->GuestMaximalAccessRights;
|
|
|
|
CscStatus = CscAddMaximalAccessRightsForShare(
|
|
pNetRootEntry->NetRoot.sCscRootInfo.hShare,
|
|
2,
|
|
AccessRights);
|
|
if (CscStatus != ERROR_SUCCESS) {
|
|
RxDbgTrace(
|
|
0,
|
|
Dbg,
|
|
("MRxSmbCscCreateEpilogue Error Updating Access rights %lx\n",
|
|
Status));
|
|
}
|
|
else
|
|
{
|
|
pNetRootEntry->NetRoot.UpdateCscShareRights = FALSE;
|
|
}
|
|
|
|
CscDiscardSid(&SidContext);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LeaveShadowCritRx(RxContext);
|
|
|
|
} else {
|
|
pNetRootEntry->NetRoot.sCscRootInfo.hShare = 0;
|
|
pNetRootEntry->NetRoot.sCscRootInfo.hRootDir = 0;
|
|
pNetRootEntry->NetRoot.sCscRootInfo.Flags = 0;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
#ifndef MRXSMB_BUILD_FOR_CSC_DCON
|
|
VOID
|
|
MRxSmbCscFillWithoutNamesFind32FromFcb (
|
|
IN PMINIMAL_CSC_SMBFCB MinimalCscSmbFcb,
|
|
OUT _WIN32_FIND_DATA *Find32
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies the nonname stuff from the fcb to the find32.
|
|
|
|
Arguments:
|
|
|
|
Fcb
|
|
Find32
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PFCB wrapperFcb = (PFCB)(MinimalCscSmbFcb->ContainingFcb);
|
|
if (wrapperFcb==NULL) {
|
|
return;
|
|
}
|
|
Find32->dwFileAttributes = wrapperFcb->Attributes; //&~FILE_ATTRIBUTE_NORMAL??
|
|
COPY_LARGEINTEGER_TO_STRUCTFILETIME(Find32->ftLastWriteTime,
|
|
wrapperFcb->LastWriteTime);
|
|
//COPY_LARGEINTEGER_TO_STRUCTFILETIME(Find32->ftChangeTime,
|
|
// wrapperFcb->LastChangeTime);
|
|
COPY_LARGEINTEGER_TO_STRUCTFILETIME(Find32->ftCreationTime,
|
|
wrapperFcb->CreationTime);
|
|
COPY_LARGEINTEGER_TO_STRUCTFILETIME(Find32->ftLastAccessTime,
|
|
wrapperFcb->LastAccessTime);
|
|
Find32->nFileSizeHigh = wrapperFcb->Header.FileSize.HighPart;
|
|
Find32->nFileSizeLow = wrapperFcb->Header.FileSize.LowPart;
|
|
}
|
|
#endif //#ifndef MRXSMB_BUILD_FOR_CSC_DCON
|
|
|
|
NTSTATUS
|
|
MRxSmbCscGetFileInfoForCshadow(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the start routine that basically continues the implementation
|
|
of MRxSmbGetFileInfoFromServer within the exchange initiation.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState;
|
|
|
|
MRxSmbSetInitialSMB(StufferState STUFFERTRACE(Dbg,'FC'));
|
|
Status = MRxSmbCscGetFileInfoFromServerWithinExchange (
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS,
|
|
NULL); //NULL means the name is already in the exchange
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbGetFileInfoFromServer (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN PUNICODE_STRING FullFileName,
|
|
OUT _WIN32_FIND_DATA *Find32,
|
|
IN PMRX_SRV_OPEN pSrvOpen,
|
|
OUT BOOLEAN *lpfIsRoot
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine goes to the server to get a both_directory_info for
|
|
the file mentioned. Here, we have no exchange so we have to get
|
|
one. the underlying machinery for this leaves the pointer in the
|
|
exchange structure. We can then copy it out into the Find32 passed
|
|
in here.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = RX_MAP_STATUS(SUCCESS);
|
|
RxCaptureFcb; RxCaptureFobx;
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
PMRX_SRV_OPEN SrvOpen = NULL;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = NULL;
|
|
PMRX_V_NET_ROOT VNetRootToUse = NULL;
|
|
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext = NULL;
|
|
|
|
PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange;
|
|
BOOLEAN FinalizationComplete;
|
|
UNICODE_STRING uniRealName;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbGetFileInfoFromServer\n", 0 ));
|
|
|
|
if (pSrvOpen)
|
|
{
|
|
SrvOpen = pSrvOpen;
|
|
}
|
|
else
|
|
{
|
|
SrvOpen = capFobx->pSrvOpen;
|
|
}
|
|
|
|
if (lpfIsRoot)
|
|
{
|
|
*lpfIsRoot = FALSE;
|
|
}
|
|
|
|
smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
VNetRootToUse = SrvOpen->pVNetRoot;
|
|
pVNetRootContext = SmbCeGetAssociatedVNetRootContext(VNetRootToUse);
|
|
|
|
ASSERT( NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN );
|
|
|
|
Status = SmbPseCreateOrdinaryExchange(
|
|
RxContext,
|
|
SrvOpen->pVNetRoot,
|
|
SMBPSE_OE_FROM_GETFILEINFOFORCSHADOW,
|
|
MRxSmbCscGetFileInfoForCshadow,
|
|
&OrdinaryExchange);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RxDbgTrace(-1, Dbg, ("Couldn't get the smb buf!\n"));
|
|
return Status;
|
|
}
|
|
|
|
if (smbFcb->uniDfsPrefix.Buffer)
|
|
{
|
|
UNICODE_STRING DfsName;
|
|
|
|
if((Status = CscDfsDoDfsNameMapping(&smbFcb->uniDfsPrefix,
|
|
&smbFcb->uniActualPrefix,
|
|
FullFileName,
|
|
FALSE, // fDFSNameToResolvedName
|
|
&uniRealName
|
|
)) != STATUS_SUCCESS)
|
|
{
|
|
RxDbgTrace(-1, Dbg, ("Couldn't map DFS name to real name!\n"));
|
|
return Status;
|
|
}
|
|
|
|
// DbgPrint("MrxSmbCscgetFileInfoFromServer: %wZ, real name %wZ\n",FullFileName, &uniRealName);
|
|
// if this is a root, then fixup the filename
|
|
if ((uniRealName.Length == 0) ||
|
|
((uniRealName.Length == 2)&&(*uniRealName.Buffer == L'\\')))
|
|
{
|
|
if (lpfIsRoot)
|
|
{
|
|
*lpfIsRoot = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uniRealName = *FullFileName;
|
|
}
|
|
|
|
OrdinaryExchange->pPathArgument1 = &uniRealName;
|
|
|
|
Status = SmbPseInitiateOrdinaryExchange(OrdinaryExchange);
|
|
|
|
ASSERT (Status!=RX_MAP_STATUS(PENDING));
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
RtlCopyMemory(Find32, OrdinaryExchange->Find32WithinSmbbuf,sizeof(*Find32));
|
|
|
|
}
|
|
|
|
FinalizationComplete = SmbPseFinalizeOrdinaryExchange(OrdinaryExchange);
|
|
ASSERT(FinalizationComplete);
|
|
|
|
if (smbFcb->uniDfsPrefix.Buffer){
|
|
RxFreePool(uniRealName.Buffer);
|
|
}
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbGetFileInfoFromServer exit with status=%08lx\n", Status ));
|
|
return(Status);
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
MRxSmbCscIsFatNameValid (
|
|
IN PUNICODE_STRING FileName,
|
|
IN BOOLEAN WildCardsPermissible
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if the specified file name is conformant to the
|
|
Fat 8.3 file naming rules.
|
|
|
|
Arguments:
|
|
|
|
FileName - Supplies the name to check.
|
|
|
|
WildCardsPermissible - Tells us if wild card characters are ok.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the name is valid, FALSE otherwise.
|
|
|
|
Notes:
|
|
|
|
i just lifted this routine from ntfs (jll-7-30-97)
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN Results;
|
|
STRING DbcsName;
|
|
USHORT i;
|
|
CHAR Buffer[24];
|
|
WCHAR wc;
|
|
BOOLEAN AllowExtendedChars = TRUE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If the name is more than 24 bytes then it can't be a valid Fat name.
|
|
//
|
|
|
|
if (FileName->Length > 24) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// We will do some extra checking ourselves because we really want to be
|
|
// fairly restrictive of what an 8.3 name contains. That way
|
|
// we will then generate an 8.3 name for some nomially valid 8.3
|
|
// names (e.g., names that contain DBCS characters). The extra characters
|
|
// we'll filter off are those characters less than and equal to the space
|
|
// character and those beyond lowercase z.
|
|
//
|
|
|
|
if (AllowExtendedChars) {
|
|
|
|
for (i = 0; i < FileName->Length / sizeof( WCHAR ); i += 1) {
|
|
|
|
wc = FileName->Buffer[i];
|
|
|
|
if ((wc <= 0x0020) || (wc == 0x007c)) { return FALSE; }
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < FileName->Length / sizeof( WCHAR ); i += 1) {
|
|
|
|
wc = FileName->Buffer[i];
|
|
|
|
if ((wc <= 0x0020) || (wc >= 0x007f) || (wc == 0x007c)) { return FALSE; }
|
|
}
|
|
}
|
|
|
|
//
|
|
// The characters match up okay so now build up the dbcs string to call
|
|
// the fsrtl routine to check for legal 8.3 formation
|
|
//
|
|
|
|
Results = FALSE;
|
|
|
|
DbcsName.MaximumLength = 24;
|
|
DbcsName.Buffer = Buffer;
|
|
|
|
if (NT_SUCCESS(RtlUnicodeStringToCountedOemString( &DbcsName, FileName, FALSE))) {
|
|
|
|
if (FsRtlIsFatDbcsLegal( DbcsName, WildCardsPermissible, FALSE, FALSE )) {
|
|
|
|
Results = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// And return to our caller
|
|
//
|
|
|
|
return Results;
|
|
}
|
|
|
|
VOID
|
|
MRxSmbCscGenerate83NameAsNeeded(
|
|
IN CSC_SHADOW_HANDLE hDir,
|
|
PWCHAR FileName,
|
|
PWCHAR SFN
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine generates a SFN for a filename if it's not already
|
|
an SFN.
|
|
|
|
Arguments:
|
|
SFN - Not checking the allocated size of SFN in this function, but it
|
|
is always called from within core CSC. It is always pointing to
|
|
Find32->cAlternateFileName.
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
UNICODE_STRING FileNameU;
|
|
WCHAR ShortNameBuffer[14];
|
|
UNICODE_STRING ShortUnicodeName;
|
|
GENERATE_NAME_CONTEXT Context;
|
|
|
|
//set up for no short name
|
|
*SFN = 0;
|
|
|
|
RtlInitUnicodeString(&FileNameU,FileName);
|
|
if (MRxSmbCscIsFatNameValid (&FileNameU,FALSE)) {
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscGenerate83NameAsNeeded no SFN needed for ...<%ws>\n",
|
|
FileName));
|
|
return;
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscGenerate83NameAsNeeded need SFN for ...<%ws>\n",
|
|
FileName));
|
|
|
|
// Now generate a short name.
|
|
//
|
|
|
|
ShortUnicodeName.Length = 0;
|
|
ShortUnicodeName.MaximumLength = 12 * sizeof(WCHAR);
|
|
ShortUnicodeName.Buffer = ShortNameBuffer;
|
|
|
|
RtlZeroMemory( &Context, sizeof( GENERATE_NAME_CONTEXT ) );
|
|
|
|
while ( TRUE ) {
|
|
|
|
NTSTATUS Status;
|
|
ULONG StatusOfShadowApiCall;
|
|
CSC_SHADOW_HANDLE hNew;
|
|
ULONG ShadowStatus;
|
|
|
|
RtlGenerate8dot3Name( &FileNameU, TRUE, &Context, &ShortUnicodeName );
|
|
|
|
//add the zero.....sigh......
|
|
ShortUnicodeName.Buffer[ShortUnicodeName.Length/sizeof(WCHAR)] = 0;
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscGenerate83NameAsNeeded tryinh SFN <%ws>\n",
|
|
ShortUnicodeName.Buffer));
|
|
//look for existing shadow by that name
|
|
hNew = 0;
|
|
StatusOfShadowApiCall = GetShadow(
|
|
hDir, // HSHADOW hDir,
|
|
ShortUnicodeName.Buffer,
|
|
// USHORT *lpName,
|
|
&hNew, // LPHSHADOW lphShadow,
|
|
NULL, // LPFIND32 lpFind32,
|
|
&ShadowStatus,
|
|
// ULONG far *lpuShadowStatus,
|
|
NULL // LPOTHERINFO lpOI
|
|
);
|
|
|
|
if (hNew == 0) {
|
|
//the name was not found.....we're in business
|
|
RtlCopyMemory(SFN,
|
|
ShortUnicodeName.Buffer,
|
|
ShortUnicodeName.Length+sizeof(WCHAR));
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscGenerate83NameAsNeeded using SFN <%ws>\n",
|
|
SFN));
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
DEBUG_ONLY_DECL(ULONG MRxSmbCscCreateShadowEarlyExits = 0;)
|
|
|
|
|
|
NTSTATUS
|
|
MRxSmbCscCreateShadowFromPath (
|
|
IN PUNICODE_STRING AlreadyPrefixedName,
|
|
IN PCSC_ROOT_INFO pCscRootInfo,
|
|
OUT _WIN32_FIND_DATA *Find32,
|
|
OUT PBOOLEAN Created OPTIONAL,
|
|
IN ULONG Controls,
|
|
IN OUT PMINIMAL_CSC_SMBFCB MinimalCscSmbFcb,
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN BOOLEAN fDisconnected,
|
|
OUT ULONG *pulInheritedHintFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine walks down the current name creating/verifying shadows as it goes.
|
|
|
|
Arguments:
|
|
|
|
AlreadyPrefixedName - the filename for which is a shadow is found/created
|
|
|
|
pNetRootEntry - the netroot which is the base for the shadow
|
|
|
|
Find32 - a FIND32 structure filled in with the stored info for the shadow
|
|
|
|
Created OPT - (NULL or) a PBOOLEANset to TRUE if a new shadow is created
|
|
|
|
Controls - some special flags controlling when shadows are created
|
|
|
|
MinimalCscSmbFcb - the place where the shadow info is reported
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Disconnected - indicates the mode of operation
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status=STATUS_SUCCESS, LocalStatus;
|
|
UNICODE_STRING PathName, ComponentName;
|
|
PWCHAR PreviousSlash,NextSlash,Limit;
|
|
CSC_SHADOW_HANDLE hNew;
|
|
CSC_SHADOW_HANDLE hDir = pCscRootInfo->hRootDir;
|
|
ULONG StatusOfShadowApiCall;
|
|
ULONG ShadowStatus;
|
|
BOOLEAN LastComponentInName = FALSE, fRootHintFlagsObtained=FALSE;
|
|
ULONG DirectoryLevel, ulHintFlags=0;
|
|
OTHERINFO sOI; //hint/refpri data
|
|
BOOLEAN JunkCreated;
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
|
|
//CODE.IMPROVEMENT this is a little dangerous.....not everyone who
|
|
// calls this routine has an actual smbFcb. we should get some asserts
|
|
// going wheever we use this that it's the same as the one that
|
|
// we could have gotten from the RxContext.
|
|
PMRX_SMB_FCB smbFcb = CONTAINING_RECORD(MinimalCscSmbFcb,
|
|
MRX_SMB_FCB,
|
|
MinimalCscSmbFcb);
|
|
|
|
BEGIN_TIMING(MRxSmbCscCreateShadowFromPath);
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbCscCreateShadowFromPath...<%wZ> %08lx %08lx\n",
|
|
AlreadyPrefixedName,hDir,Controls));
|
|
|
|
DbgDoit(ASSERT(vfInShadowCrit));
|
|
|
|
ASSERT(hDir);
|
|
|
|
if (Created == NULL) {
|
|
Created = &JunkCreated;
|
|
}
|
|
*Created = FALSE;
|
|
|
|
PathName = *AlreadyPrefixedName;
|
|
|
|
// Fix for Bug# 554061 CSC should not handle loopback
|
|
if(RxContext->pRelevantSrvOpen) {
|
|
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(RxContext->pRelevantSrvOpen->pVNetRoot->pNetRoot->pSrvCall);
|
|
}
|
|
else {
|
|
// RxContext->pRelevantSrvOpen is NULL only in the case of a tree connect
|
|
// to a directory on a disconnected server.
|
|
ASSERT(RxContext->MajorFunction == IRP_MJ_CREATE);
|
|
ASSERT(RxContext->Create.ThisIsATreeConnectOpen);
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(RxContext->Create.pSrvCall);
|
|
}
|
|
|
|
if(pServerEntry->Server.IsLoopBack)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto bailout;
|
|
}
|
|
|
|
if (FlagOn(Controls, CREATESHADOW_CONTROL_STRIP_SHARE_NAME))
|
|
{
|
|
ASSERT(!fDisconnected);
|
|
|
|
if(CscDfsStripLeadingServerShare(&PathName) != STATUS_SUCCESS)
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
Limit = (PWCHAR)(((PBYTE)PathName.Buffer)+ PathName.Length);
|
|
|
|
// strip out trailing 0s and slash
|
|
if (PathName.Length > 2)
|
|
{
|
|
while ((*(Limit-1)==0)||(*(Limit-1)=='\\'))
|
|
{
|
|
--Limit;
|
|
PathName.Length -= 2;
|
|
ASSERT((*Limit == 0) || (*Limit == '\\'));
|
|
if (Limit == PathName.Buffer)
|
|
{
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
PreviousSlash = PathName.Buffer;
|
|
|
|
// in connected mode apply the character exclusion list + filetype exclusion list
|
|
// in disconnected mode only apply the character exclusion list
|
|
|
|
MinimalCscSmbFcb->fDoBitCopy = FALSE;
|
|
|
|
if (CheckForBandwidthConservation(PathName.Buffer, // name
|
|
PathName.Length/sizeof(USHORT))) // size in bytes
|
|
{
|
|
MinimalCscSmbFcb->fDoBitCopy = TRUE;
|
|
HookKdPrint(BITCOPY, ("Bitcopy enabled for %wZ \n", &PathName));
|
|
}
|
|
else if (ExcludeFromCreateShadow(PathName.Buffer, // name
|
|
PathName.Length/sizeof(USHORT), // size in bytes
|
|
(fDisconnected==0))) // Check filetype Exclusion List
|
|
{
|
|
Controls |= CREATESHADOW_CONTROL_NOCREATE;
|
|
}
|
|
|
|
|
|
|
|
if ((PathName.Length == 0) ||
|
|
((PathName.Length == 2) &&
|
|
(*PreviousSlash == OBJ_NAME_PATH_SEPARATOR))) {
|
|
//in disconnected mode, we have to handle opening the root dir
|
|
RxDbgTrace(0,
|
|
Dbg,
|
|
("MRxSmbCscCreateShadowFromPath basdir ret/handles...<%08lx>\n",
|
|
hDir));
|
|
|
|
//fill in the stuff that we have.....
|
|
MinimalCscSmbFcb->hParentDir = 0;
|
|
MinimalCscSmbFcb->hShadow = hDir;
|
|
MinimalCscSmbFcb->LastComponentOffset = 0;
|
|
MinimalCscSmbFcb->LastComponentLength = 0;
|
|
|
|
if (!FlagOn(Controls,CREATESHADOW_CONTROL_NOREVERSELOOKUP)
|
|
&& (smbFcb->ShadowReverseTranslationLinks.Flink == 0)) {
|
|
ValidateSmbFcbList();
|
|
smbFcb->ContainingFcb->fMiniInited = TRUE;
|
|
MRxSmbCscAddReverseFcbTranslation(smbFcb);
|
|
}
|
|
|
|
//fill in a vacuous find32structure
|
|
RtlZeroMemory(Find32,sizeof(_WIN32_FIND_DATA));
|
|
Find32->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
|
|
|
|
|
|
//make sure that we avoid the loop below
|
|
PreviousSlash = Limit;
|
|
//we're outta here......
|
|
}
|
|
|
|
// stop the guy if he doesn't have access
|
|
if (FlagOn(Controls,CREATESHADOW_CONTROL_DO_SHARE_ACCESS_CHECK))
|
|
{
|
|
ASSERT(fDisconnected);
|
|
|
|
if(!CscAccessCheck(
|
|
0,
|
|
hDir,
|
|
RxContext,
|
|
RxContext->Create.NtCreateParameters.DesiredAccess,
|
|
NULL,
|
|
NULL
|
|
))
|
|
{
|
|
Status = STATUS_ACCESS_DENIED;
|
|
HookKdPrint(BADERRORS, ("CSFP: Access Check failed on root directory %x", hDir));
|
|
goto bailout;
|
|
}
|
|
}
|
|
|
|
for (DirectoryLevel=1;;DirectoryLevel++) {
|
|
BOOLEAN UsingExistingShadow;
|
|
|
|
if (PreviousSlash >= Limit) {
|
|
break;
|
|
}
|
|
|
|
NextSlash = PreviousSlash + 1;
|
|
|
|
for (;;NextSlash++) {
|
|
if (NextSlash >= Limit) {
|
|
LastComponentInName = TRUE;
|
|
break;
|
|
}
|
|
if (*NextSlash == OBJ_NAME_PATH_SEPARATOR) {
|
|
|
|
// assert that we don't have a trailing slash at the end
|
|
ASSERT((NextSlash+1) < Limit);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
ComponentName.Buffer = PreviousSlash+1;
|
|
ComponentName.Length =
|
|
(USHORT)(((PBYTE)(NextSlash)) - ((PBYTE)ComponentName.Buffer));
|
|
|
|
PreviousSlash = NextSlash;
|
|
|
|
RtlZeroMemory(Find32,sizeof(_WIN32_FIND_DATA));
|
|
RtlCopyMemory(&Find32->cFileName[0],
|
|
ComponentName.Buffer,
|
|
ComponentName.Length);
|
|
|
|
//lastcomponentname stuff for connected has been moved below.....
|
|
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscCreateShadowFromPath name from find32 for GetShadow...<%ws>\n",
|
|
&Find32->cFileName[0]));
|
|
|
|
hNew = 0;
|
|
|
|
UsingExistingShadow = FALSE;
|
|
|
|
ASSERT(Find32->cFileName[0]);
|
|
|
|
StatusOfShadowApiCall = GetShadow(
|
|
hDir, // HSHADOW hDir,
|
|
&Find32->cFileName[0], // USHORT *lpName,
|
|
&hNew, // LPHSHADOW lphShadow,
|
|
Find32, // LPFIND32 lpFind32,
|
|
&ShadowStatus, // ULONG far *lpuShadowStatus,
|
|
&sOI // LPOTHERINFO lpOI
|
|
);
|
|
|
|
if (StatusOfShadowApiCall != SRET_OK) {
|
|
//no need to fail the open but we get no shadow info
|
|
break;
|
|
}
|
|
|
|
if (hNew) {
|
|
// accumulate pin inheritance flags
|
|
ulHintFlags |= (sOI.ulHintFlags & FLAG_CSC_HINT_INHERIT_MASK);
|
|
}
|
|
|
|
//we will have to do something about it if a directory turns
|
|
// a file or viceversa for connected
|
|
|
|
if (hNew==0) {
|
|
LPOTHERINFO lpOI=NULL;
|
|
UNICODE_STRING ComponentPath;
|
|
|
|
if (FlagOn(Controls,CREATESHADOW_CONTROL_NOCREATE)) {
|
|
//if no creates...we're outta here.......
|
|
if (FALSE) {
|
|
DbgDoit({
|
|
if ( ((MRxSmbCscCreateShadowEarlyExits++)&0x7f) == 0x7f ) {
|
|
RxLog(("Csc EarlyExit no create %d\n",
|
|
MRxSmbCscCreateShadowEarlyExits));
|
|
}
|
|
})
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (LastComponentInName && FlagOn(Controls,CREATESHADOW_CONTROL_NOCREATELEAF)) {
|
|
//if no creates...we're outta here.......but we still need to set what
|
|
//would be the hParentDir......and the name offsets
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscCreateShadowFromPath noleaf ret/handles..."
|
|
"<%08lx><%08lx><%08lx>\n",
|
|
StatusOfShadowApiCall,hDir,hNew));
|
|
MinimalCscSmbFcb->hParentDir = hDir;
|
|
MinimalCscSmbFcb->LastComponentOffset = (USHORT)(ComponentName.Buffer - AlreadyPrefixedName->Buffer);
|
|
MinimalCscSmbFcb->LastComponentLength = ComponentName.Length;
|
|
break;
|
|
}
|
|
|
|
if (!LastComponentInName && FlagOn(Controls,CREATESHADOW_CONTROL_NOCREATENONLEAF)) {
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscCreateShadowFromPath nocreatenonleaf ret/handles..."
|
|
"<%08lx><%08lx><%08lx>\n",
|
|
StatusOfShadowApiCall,hDir,hNew));
|
|
break;
|
|
}
|
|
|
|
ASSERT(RxContext!=NULL);
|
|
|
|
ShadowStatus = 0;
|
|
if (!fDisconnected){ //ok for dcon start of big dcon blob 1
|
|
BOOLEAN fIsRoot = FALSE;
|
|
BEGIN_TIMING(MRxSmbGetFileInfoFromServer);
|
|
|
|
ComponentPath.Buffer = PathName.Buffer;
|
|
ComponentPath.Length =
|
|
(USHORT)(((PBYTE)(NextSlash)) - ((PBYTE)ComponentPath.Buffer));
|
|
LeaveShadowCritRx(RxContext);
|
|
Status = MRxSmbGetFileInfoFromServer(RxContext,&ComponentPath,Find32, NULL, &fIsRoot);
|
|
EnterShadowCritRx(RxContext);
|
|
|
|
END_TIMING(MRxSmbGetFileInfoFromServer);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
// if this is a DFS path and we couldn't reverse map, it
|
|
// just create a directory or file with fake info and mark it as stale
|
|
if (smbFcb->uniDfsPrefix.Buffer && (Status == STATUS_NO_SUCH_FILE))
|
|
{
|
|
ShadowStatus |= SHADOW_STALE;
|
|
CreateFakeFind32(hDir, Find32, RxContext, LastComponentInName);
|
|
HookKdPrint(NAME, ("Fake win32 for DFS share %ls\n", Find32->cFileName));
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
HookKdPrint(BADERRORS, (" MRxSmbGetFileInfoFromServer failed %ls Status=%x\n", Find32->cFileName, Status));
|
|
// we change the STATUS_RETRY to something worse. STATUS_RETRY is used
|
|
if (Status == STATUS_RETRY)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// in case of DFS, this could be a root, in which case the naem we get back won't be
|
|
// correct. Restore it to the original name
|
|
if (smbFcb->uniDfsPrefix.Buffer && fIsRoot)
|
|
{
|
|
ShadowStatus |= SHADOW_STALE;
|
|
|
|
RtlCopyMemory(&Find32->cFileName[0],
|
|
ComponentName.Buffer,
|
|
ComponentName.Length);
|
|
|
|
Find32->cFileName[ComponentName.Length/sizeof(USHORT)] = 0;
|
|
|
|
MRxSmbCscGenerate83NameAsNeeded(hDir,
|
|
&Find32->cFileName[0],
|
|
&Find32->cAlternateFileName[0]);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
ShadowStatus = SHADOW_LOCALLY_CREATED;
|
|
//CODE.IMPROVEMENT...should we check for 0-length as well
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscCreateShadowFromPath setting to locallycreated...<%ws>\n",
|
|
&Find32->cFileName[0],ShadowStatus));
|
|
CreateFakeFind32(hDir, Find32, RxContext, LastComponentInName);
|
|
}
|
|
|
|
if (!LastComponentInName ||
|
|
FlagOn(Controls,CREATESHADOW_CONTROL_SPARSECREATE) ||
|
|
FlagOn(Find32->dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY) ) {
|
|
|
|
ShadowStatus |= SHADOW_SPARSE;
|
|
//CODE.IMPROVEMENT...should we check for 0-length as well
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscCreateShadowFromPath setting to sparse...<%ws>\n",
|
|
&Find32->cFileName[0],ShadowStatus));
|
|
}
|
|
|
|
// check for pin flag inheritance when creating anything
|
|
|
|
if(!fRootHintFlagsObtained) {
|
|
StatusOfShadowApiCall = GetShadowInfo(
|
|
0,
|
|
pCscRootInfo->hRootDir,
|
|
NULL,
|
|
NULL,
|
|
&sOI
|
|
);
|
|
|
|
if(StatusOfShadowApiCall != SRET_OK) {
|
|
break;
|
|
}
|
|
|
|
fRootHintFlagsObtained = TRUE;
|
|
|
|
// or the inheritance bits
|
|
ulHintFlags |= (sOI.ulHintFlags & FLAG_CSC_HINT_INHERIT_MASK);
|
|
|
|
}
|
|
|
|
// If there is any tunnelling info then use it to create this guy
|
|
if (RetrieveTunnelInfo(
|
|
hDir,
|
|
&Find32->cFileName[0], // potential SFN OK for red/yellow
|
|
(fDisconnected)?Find32:NULL, // get LFN only when disconnected
|
|
&sOI)) {
|
|
lpOI = &sOI;
|
|
}
|
|
|
|
// are we supposed to do any inheritance?
|
|
if (ulHintFlags & (FLAG_CSC_HINT_INHERIT_MASK)) {
|
|
if (!lpOI) {
|
|
InitOtherInfo(&sOI);
|
|
lpOI = &sOI;
|
|
lpOI->ulHintFlags = 0;
|
|
}
|
|
|
|
if (ulHintFlags & FLAG_CSC_HINT_PIN_INHERIT_USER) {
|
|
lpOI->ulHintFlags |= FLAG_CSC_HINT_PIN_USER;
|
|
}
|
|
|
|
if (ulHintFlags & FLAG_CSC_HINT_PIN_INHERIT_SYSTEM) {
|
|
lpOI->ulHintFlags |= FLAG_CSC_HINT_PIN_SYSTEM;
|
|
}
|
|
}
|
|
|
|
// if this is a file on which special heuristic needs to be applied
|
|
// and none of it's parents have system pin inheritance bit set
|
|
// then we do not create the file.
|
|
// Thus on remoteboot shares, we will create entries for these files
|
|
// even if they are opend without the execute flag set.
|
|
// This takes care of the upgrade NT50 to an RB machine scenario
|
|
|
|
if ((Controls & CREATESHADOW_CONTROL_FILE_WITH_HEURISTIC)&&
|
|
!(ulHintFlags & FLAG_CSC_HINT_PIN_INHERIT_SYSTEM))
|
|
{
|
|
break;
|
|
}
|
|
|
|
#if defined(REMOTE_BOOT)
|
|
//
|
|
// In the remote boot case, there was an extra PVOID lpContext
|
|
// parameter to CreateShadowInternal, to which we passed a pointer
|
|
// to a structure. The structure held the cp value (NT_CREATE_PARAMETERS)
|
|
// from &RxContext->Create.NtCreateParameters and the address of
|
|
// a local NTSTATUS value. Eventually this caused the underlying
|
|
// call to IoCreateFile to be done while impersonating the current
|
|
// user, and the status from IoCreateFile was readable upon
|
|
// return from the local value.
|
|
//
|
|
#endif
|
|
|
|
StatusOfShadowApiCall = CreateShadowInternal (
|
|
hDir, // HSHADOW hDir,
|
|
Find32, // LPFIND32 lpFind32,
|
|
ShadowStatus,// ULONG uFlags,
|
|
lpOI, // LPOTHERINFO lpOI,
|
|
&hNew // LPHSHADOW lphNew
|
|
);
|
|
|
|
HookKdPrint(NAME, ("Create %ws in hDir=%x, hShadow=%x Status=%x StatusOfShadowApiCall=%x\n\n", Find32->cFileName, hDir, hNew, ShadowStatus, StatusOfShadowApiCall));
|
|
|
|
if (StatusOfShadowApiCall != SRET_OK) {
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscCreateShadowFromPath createshadowinternal failed!!!...<%ws>\n",
|
|
&Find32->cFileName[0],ShadowStatus));
|
|
break; //no need to fail the open but we get no shadow info
|
|
}
|
|
|
|
*Created = LastComponentInName;
|
|
|
|
RxLog(("Created %ws in hDir=%x, hShadow=%x Status=%x\n\n", Find32->cFileName, hDir, hNew, ShadowStatus));
|
|
|
|
} else {
|
|
|
|
RxDbgTrace(0,Dbg,
|
|
("MRxSmbCscCreateShadowFromPath name from getsh <%ws>\n",
|
|
&Find32->cFileName[0]));
|
|
|
|
if (!fDisconnected) // nothing in connected mode
|
|
{
|
|
// Check if this file should be invisible in connected state
|
|
// We won't want to do this for VDO
|
|
|
|
if( (!(Find32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) &&
|
|
mShadowNeedReint(ShadowStatus))
|
|
{
|
|
HookKdPrint(BADERRORS, ("File needs merge %x %x Stts=%x %ls\n", hDir, hNew, ShadowStatus, Find32->cFileName));
|
|
Status = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
}
|
|
else // lots in disconnected mode
|
|
{
|
|
if (LastComponentInName && (FlagOn(Controls,CREATESHADOW_CONTROL_NOCREATELEAF)||
|
|
FlagOn(Controls,CREATESHADOW_CONTROL_NOCREATE)))
|
|
{
|
|
// if this is the last component and we are not supposed to
|
|
// create it then skip it.
|
|
}
|
|
else
|
|
{
|
|
// If it is marked deleted then it is time to recreate it
|
|
if (mShadowDeleted(ShadowStatus))
|
|
{
|
|
PNT_CREATE_PARAMETERS cp = &RxContext->Create.NtCreateParameters;
|
|
|
|
// Check for a type change from a deleted filename
|
|
// to a directory name; the other way around is not possible
|
|
// in our scheme where shadowed directories are not deletable
|
|
|
|
// just bailout
|
|
if(((IsFile(Find32->dwFileAttributes) != 0) // pre type
|
|
!= ((LastComponentInName && !FlagOn(cp->CreateOptions,FILE_DIRECTORY_FILE))!=0)))
|
|
{
|
|
RxLog(("MRxSmbCscCreateShadowFromPath: type change, failing\n"));
|
|
HookKdPrint(BADERRORS, ("MRxSmbCscCreateShadowFromPath: type change, failing\n"));
|
|
Status = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
|
|
KeQuerySystemTime(((PLARGE_INTEGER)(&Find32->ftCreationTime)));
|
|
Find32->ftLastAccessTime = Find32->ftLastWriteTime = Find32->ftCreationTime;
|
|
//already zero Find32->nFileSizeHigh = Find32->nFileSizeLow = 0;
|
|
|
|
ShadowStatus = SHADOW_DIRTY|SHADOW_TIME_CHANGE|SHADOW_ATTRIB_CHANGE|SHADOW_REUSED;
|
|
|
|
// Update the shadow info without changing the version stamp
|
|
|
|
if(SetShadowInfo(hDir, hNew, Find32, ShadowStatus,
|
|
SHADOW_FLAGS_ASSIGN|SHADOW_FLAGS_DONT_UPDATE_ORGTIME
|
|
) < SRET_OK)
|
|
{
|
|
hNew = 0;
|
|
break;
|
|
}
|
|
// set created flag to true. Based on the Recreated bit, we will know whether
|
|
// this entry was resurrected or not
|
|
*Created = TRUE;
|
|
|
|
}
|
|
else if (IsFile(Find32->dwFileAttributes) && (mShadowSparse(ShadowStatus)))
|
|
{
|
|
ShadowStatus = SHADOW_DIRTY|SHADOW_TIME_CHANGE|SHADOW_ATTRIB_CHANGE|SHADOW_REUSED;
|
|
|
|
Find32->ftLastAccessTime = Find32->ftLastWriteTime = Find32->ftCreationTime;
|
|
Find32->nFileSizeHigh = Find32->nFileSizeLow = 0;
|
|
|
|
if ((TruncateDataHSHADOW(hDir, hNew)>=SRET_OK)&&
|
|
(SetShadowInfo(hDir, hNew, Find32, ShadowStatus,SHADOW_FLAGS_ASSIGN)>=SRET_OK))
|
|
{
|
|
// set created flag to true. Based on the Recreated bit, we will know whether
|
|
// this entry was resurrected or not
|
|
*Created = TRUE;
|
|
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
hNew = 0;
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (LastComponentInName && (hNew!=0)) {
|
|
LONG nFileSizeLow, nFileSizeHigh;
|
|
|
|
// if we are here from the createepilogue then we need to see whether there is an
|
|
// FCB floating around for this name that has a delete_on_close issued on one of the fobxs
|
|
// and is ready for purge. If it isn't ready for purging, ie. there are some
|
|
// outstanding opens on it then this current create is an invalid operation
|
|
|
|
if(FlagOn(Controls,CREATESHADOW_CONTROL_FAIL_IF_MARKED_FOR_DELETION))
|
|
{
|
|
PMRX_SMB_FCB pSmbFcb = MRxSmbCscRecoverMrxFcbFromFdb(MRxSmbCscFindFdbFromHShadow(hNew));
|
|
|
|
|
|
if (pSmbFcb && (pSmbFcb->LocalFlags & FLAG_FDB_DELETE_ON_CLOSE))
|
|
{
|
|
RxCaptureFcb;
|
|
RxLog(("delonclose FCB=%x\n", pSmbFcb));
|
|
RxLog(("prgrelfobx \n"));
|
|
LeaveShadowCritRx(RxContext);
|
|
RxScavengeFobxsForNetRoot((PNET_ROOT)(capFcb->pNetRoot),(PFCB)capFcb);
|
|
EnterShadowCritRx(RxContext);
|
|
if (MRxSmbCscFindFdbFromHShadow(hNew))
|
|
{
|
|
RxLog(("ACCESS_DENIED FCB=%x \n", capFcb));
|
|
HookKdPrint(BADERRORS, ("ACCESS_DENIED FCB=%x \n", capFcb));
|
|
Status = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
|
|
// we potentially have an inode which has been deleted
|
|
// let us try to get the inode again
|
|
|
|
RxLog(("purged relfobx \n"));
|
|
Status = STATUS_RETRY;
|
|
hNew = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (hNew!=0) {
|
|
// When any local changes are made ensure that the share in the
|
|
// CSC database is marked dirty if it has not been prevoiously
|
|
// marked. This facilitates the easy detection of changes for
|
|
// reintegration by the agent.
|
|
|
|
if (ShadowStatus & SHADOW_MODFLAGS) {
|
|
MarkShareDirty(&pCscRootInfo->ShareStatus, (ULONG)(pCscRootInfo->hShare));
|
|
}
|
|
|
|
//okay, lets remember this in the fcb
|
|
smbFcb->hParentDir = hDir;
|
|
smbFcb->hShadow = hNew;
|
|
smbFcb->ShadowStatus = (USHORT)ShadowStatus;
|
|
|
|
//it's excellent if we can find the last component again...fast
|
|
smbFcb->LastComponentOffset = (USHORT)(ComponentName.Buffer -
|
|
AlreadyPrefixedName->Buffer);
|
|
smbFcb->LastComponentLength = ComponentName.Length;
|
|
|
|
if (!FlagOn(Controls,CREATESHADOW_CONTROL_NOREVERSELOOKUP)
|
|
&& (smbFcb->ShadowReverseTranslationLinks.Flink == 0)) {
|
|
ValidateSmbFcbList();
|
|
smbFcb->ContainingFcb->fMiniInited = TRUE;
|
|
MRxSmbCscAddReverseFcbTranslation(smbFcb);
|
|
smbFcb->OriginalShadowSize.LowPart = Find32->nFileSizeLow;
|
|
smbFcb->OriginalShadowSize.HighPart = Find32->nFileSizeHigh;
|
|
}
|
|
|
|
// Initialize the serialization mechanism used for reads/writes
|
|
ExInitializeFastMutex(&smbFcb->CscShadowReadWriteMutex);
|
|
}
|
|
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscCreateShadowFromPath ret/handles...<%08lx><%08lx><%08lx><%08lx>\n",
|
|
StatusOfShadowApiCall,hDir,hNew));
|
|
|
|
hDir = hNew;
|
|
}
|
|
|
|
if (pulInheritedHintFlags)
|
|
{
|
|
*pulInheritedHintFlags = ulHintFlags;
|
|
}
|
|
bailout:
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbCscCreateShadowFromPath -> %08lx\n", Status ));
|
|
|
|
END_TIMING(MRxSmbCscCreateShadowFromPath);
|
|
return Status;
|
|
}
|
|
|
|
//CODE.IMPROVEMENT this routine should be in cshadow.c in the record
|
|
// manager.....but it's in hook.c for the shadow VxD so maybe hookcmmn.c
|
|
int RefreshShadow( HSHADOW hDir,
|
|
IN HSHADOW hShadow,
|
|
IN LPFIND32 lpFind32,
|
|
OUT ULONG *lpuShadowStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine, checks whether the local copy is current or not. If it isn't then it
|
|
stamps the local copy as being stale.
|
|
|
|
Arguments:
|
|
|
|
hShadow Inode representing the local copy
|
|
|
|
lpFind32 The new find32 info as obtained from the Share
|
|
|
|
lpuShadowStatus Returns the new status of the inode
|
|
|
|
Return Value:
|
|
|
|
Success if >= 0, failed other wise
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
int iRet = -1;
|
|
int iLocalRet;
|
|
ULONG uShadowStatus;
|
|
|
|
// ACHTUNG never called in disconnected state
|
|
|
|
RxLog(("Refresh %x \n", hShadow));
|
|
|
|
if (ChkUpdtStatusHSHADOW(hDir, hShadow, lpFind32, &uShadowStatus) < 0)
|
|
{
|
|
goto bailout;
|
|
}
|
|
if (uShadowStatus & SHADOW_STALE)
|
|
{
|
|
long nFileSizeHigh, nFileSizeLow;
|
|
|
|
if (uShadowStatus & SHADOW_DIRTY)
|
|
{
|
|
KdPrint(("RefreshShadow: conflict on %x\r\n", hShadow));
|
|
iRet = -2;
|
|
goto bailout;// conflict
|
|
}
|
|
// DbgPrint("Tuncating %ws %x \n", lpFind32->cFileName, hShadow);
|
|
// Truncate the data to 0, this also adjusts the shadow space usage
|
|
TruncateDataHSHADOW(hDir, hShadow);
|
|
// Set status flags to indicate sparse file
|
|
uShadowStatus = SHADOW_SPARSE;
|
|
|
|
// ACHTUNG!!! We know we are connected,
|
|
// hence we don't use SHADOW_FLAG_DONT_UPDATE_ORGTIME
|
|
iLocalRet = SetShadowInfo(hDir,
|
|
hShadow,
|
|
lpFind32,
|
|
uShadowStatus,
|
|
SHADOW_FLAGS_ASSIGN
|
|
);
|
|
if (iLocalRet < SRET_OK)
|
|
{
|
|
goto bailout;
|
|
}
|
|
#ifdef MAYBE
|
|
MakeSpace(lpFind32->nFileSizeHigh, lpFind32->nFileSizeLow);
|
|
#endif //MAYBE
|
|
// AllocShadowSpace(lpFind32->nFileSizeHigh, lpFind32->nFileSizeLow, TRUE);
|
|
iRet = 1;
|
|
}
|
|
else
|
|
{
|
|
iRet = 0;
|
|
}
|
|
|
|
*lpuShadowStatus = uShadowStatus;
|
|
|
|
bailout:
|
|
|
|
return (iRet);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
MRxSmbCscIsThisACopyChunkOpen (
|
|
IN PRX_CONTEXT RxContext,
|
|
BOOLEAN *lpfAgent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the open described by the RxContext is
|
|
a open-with-chunk intent.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN IsChunkOpen = FALSE;
|
|
|
|
RxCaptureFcb;
|
|
PNT_CREATE_PARAMETERS CreateParameters = &RxContext->Create.NtCreateParameters;
|
|
|
|
if ((CreateParameters->DesiredAccess == (FILE_READ_ATTRIBUTES | SYNCHRONIZE)) &&
|
|
(CreateParameters->Disposition == FILE_OPEN) &&
|
|
(CreateParameters->AllocationSize.HighPart ==
|
|
MRxSmbSpecialCopyChunkAllocationSizeMarker)) {
|
|
IsChunkOpen = (TRUE);
|
|
if (lpfAgent)
|
|
{
|
|
*lpfAgent = (CreateParameters->AllocationSize.LowPart != 0);
|
|
}
|
|
}
|
|
|
|
return IsChunkOpen;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmbPseExchangeStart_CloseCopyChunk(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the start routine for close.
|
|
|
|
Arguments:
|
|
|
|
pExchange - the exchange instance
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState;
|
|
|
|
RxCaptureFcb;RxCaptureFobx;
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
NODE_TYPE_CODE TypeOfOpen = NodeType(capFcb);
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry= SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);
|
|
|
|
PAGED_CODE();
|
|
RxDbgTrace(+1, Dbg, ("SmbPseExchangeStart_CloseCopyChunk %08lx\n", RxContext ));
|
|
|
|
ASSERT(OrdinaryExchange->Type == ORDINARY_EXCHANGE);
|
|
|
|
MRxSmbSetInitialSMB(StufferState STUFFERTRACE(Dbg,'FC'));
|
|
|
|
Status = MRxSmbBuildClose(StufferState);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
// Ensure that the Fid is validated....
|
|
SetFlag(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_VALIDATE_FID);
|
|
|
|
Status = SmbPseOrdinaryExchange(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS,
|
|
SMBPSE_OETYPE_CLOSE
|
|
);
|
|
// Ensure that the Fid validation is disabled
|
|
ClearFlag(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_VALIDATE_FID);
|
|
ASSERT (!FlagOn(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_WRITE_ONLY_HANDLE));
|
|
}
|
|
|
|
//even if it didn't work there's nothing i can do......keep going
|
|
SetFlag(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_NOT_REALLY_OPEN);
|
|
|
|
MRxSmbDecrementSrvOpenCount(pServerEntry,smbSrvOpen->Version,SrvOpen);
|
|
|
|
RxDbgTrace(-1, Dbg, ("SmbPseExchangeStart_CloseCopyChunk %08lx exit w %08lx\n", RxContext, Status ));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbCscCloseExistingThruOpen(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine closes the existing copychunk thru open and marks it as not open
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PUNICODE_STRING RemainingName;
|
|
|
|
RxCaptureFcb;
|
|
PMRX_FOBX SaveFobxFromContext = RxContext->pFobx;
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
PMRX_FOBX capFobx = smbFcb->CopyChunkThruOpen;
|
|
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT ( NodeTypeIsFcb(capFcb) );
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbCscCloseExistingThruOpen %08lx %08lx %wZ\n",
|
|
RxContext,SrvOpen,GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext) ));
|
|
|
|
if (FlagOn(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_NOT_REALLY_OPEN)) {
|
|
ASSERT(smbSrvOpen->hfShadow == 0);
|
|
RxDbgTrace(-1, Dbg, ("CopyChunkOpen already closed\n"));
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//briefly shanghai the capfobx field in the RxContext
|
|
ASSERT(SaveFobxFromContext==NULL);
|
|
RxContext->pFobx = capFobx;
|
|
|
|
if (smbSrvOpen->hfShadow != 0){
|
|
MRxSmbCscCloseShadowHandle(RxContext);
|
|
}
|
|
|
|
Status = SmbPseCreateOrdinaryExchange(
|
|
RxContext,
|
|
SrvOpen->pVNetRoot,
|
|
SMBPSE_OE_FROM_CLOSECOPYCHUNKSRVCALL,
|
|
SmbPseExchangeStart_CloseCopyChunk,
|
|
&OrdinaryExchange);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RxDbgTrace(-1, Dbg, ("Couldn't get the smb buf!\n"));
|
|
RxContext->pFobx = SaveFobxFromContext;
|
|
return(Status);
|
|
}
|
|
|
|
Status = SmbPseInitiateOrdinaryExchange(OrdinaryExchange);
|
|
|
|
ASSERT (Status != (STATUS_PENDING));
|
|
|
|
SmbPseFinalizeOrdinaryExchange(OrdinaryExchange);
|
|
|
|
RxDbgTrace(-1, Dbg,
|
|
("MRxSmbCscCloseExistingThruOpen exit w/ status=%08lx\n", Status ));
|
|
|
|
RxContext->pFobx = SaveFobxFromContext;
|
|
|
|
if (smbSrvOpen->Flags & SMB_SRVOPEN_FLAG_AGENT_COPYCHUNK_OPEN)
|
|
{
|
|
smbFcb->CopyChunkThruOpen = NULL;
|
|
smbSrvOpen->Flags &= ~SMB_SRVOPEN_FLAG_AGENT_COPYCHUNK_OPEN;
|
|
}
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscCloseExistingThruOpen status=%x\n", Status));
|
|
|
|
return(Status);
|
|
}
|
|
|
|
ULONG SuccessfulSurrogateOpens = 0;
|
|
|
|
NTSTATUS
|
|
MRxSmbCscCreatePrologue (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
OUT SMBFCB_HOLDING_STATE *SmbFcbHoldingState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the correct synchronization among opens. This
|
|
synchronization required because CopyChunk-thru opens are not allowed
|
|
to exist alongside any other kind.
|
|
|
|
So, we first must identify copychunk opens and fixup the access,
|
|
allocationsize, etc.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
RxCaptureFcb;
|
|
|
|
PMRX_SMB_FCB smbFcb;
|
|
PMRX_SRV_OPEN SrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen;
|
|
|
|
PMRX_NET_ROOT NetRoot;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
|
|
|
BOOLEAN IsCopyChunkOpen, IsThisTheAgent=FALSE;
|
|
PNT_CREATE_PARAMETERS CreateParameters;
|
|
|
|
BOOLEAN EnteredCriticalSection = FALSE;
|
|
NTSTATUS AcquireStatus = STATUS_UNSUCCESSFUL;
|
|
ULONG AcquireOptions;
|
|
BOOLEAN Disconnected = FALSE;
|
|
DWORD dwEarlyOut = 0;
|
|
|
|
ASSERT(*SmbFcbHoldingState == SmbFcb_NotHeld);
|
|
|
|
if(!MRxSmbIsCscEnabled ||
|
|
(fShadow == 0)) {
|
|
return Status;
|
|
}
|
|
|
|
SrvOpen = RxContext->pRelevantSrvOpen;
|
|
pVNetRootContext = SmbCeGetAssociatedVNetRootContext(SrvOpen->pVNetRoot);
|
|
|
|
if (FlagOn(
|
|
pVNetRootContext->Flags,
|
|
SMBCE_V_NET_ROOT_CONTEXT_CSCAGENT_INSTANCE)) {
|
|
RxLog(("%wZ is a an AgentInstance\n", &(pVNetRootContext->pNetRootEntry->Name)));
|
|
HookKdPrint(AGENT, ("%wZ is a an AgentInstance\n", &(pVNetRootContext->pNetRootEntry->Name)));
|
|
ASSERT(SrvOpen->pVNetRoot->Flags & VNETROOT_FLAG_CSCAGENT_INSTANCE);
|
|
// DbgPrint("Skipping agent instances\n");
|
|
return Status;
|
|
}
|
|
|
|
|
|
if (RxContext->MajorFunction == IRP_MJ_CREATE) {
|
|
PDFS_NAME_CONTEXT pDfsNameContext = NULL;
|
|
|
|
pDfsNameContext = CscIsValidDfsNameContext(RxContext->Create.NtCreateParameters.DfsNameContext);
|
|
|
|
if (pDfsNameContext && (pDfsNameContext->NameContextType == DFS_CSCAGENT_NAME_CONTEXT))
|
|
{
|
|
RxLog(("%wZ is a a DFS AgentInstance\n", &(pVNetRootContext->pNetRootEntry->Name)));
|
|
HookKdPrint(NAME, ("%wZ is a DFS AgentInstance\n", &(pVNetRootContext->pNetRootEntry->Name)));
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
NetRoot = capFcb->pNetRoot;
|
|
pNetRootEntry = SmbCeGetAssociatedNetRootEntry(NetRoot);
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(NetRoot->pSrvCall);
|
|
|
|
if (pNetRootEntry->NetRoot.NetRootType != NET_ROOT_DISK) {
|
|
if (!SmbCeIsServerInDisconnectedMode(pServerEntry))
|
|
{
|
|
return Status;
|
|
}
|
|
else
|
|
{
|
|
return STATUS_NETWORK_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
// Check with CSC whether any opens need to fail on this share
|
|
if(hShareReint &&
|
|
((pNetRootEntry->NetRoot.sCscRootInfo.hShare == hShareReint)||
|
|
(CscDfsShareIsInReint(RxContext))))
|
|
{
|
|
HookKdPrint(BADERRORS, ("Share %x merging \n", hShareReint));
|
|
return STATUS_ACCESS_DENIED;
|
|
|
|
}
|
|
|
|
smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
|
|
CreateParameters = &RxContext->Create.NtCreateParameters;
|
|
|
|
Disconnected = SmbCeIsServerInDisconnectedMode(pServerEntry);
|
|
|
|
RxDbgTrace(+1, Dbg,
|
|
("MRxSmbCscCreatePrologue(%08lx)...%08lx\n",
|
|
RxContext,Disconnected ));
|
|
|
|
HookKdPrint(NAME, ("CreatePrologue: Create %wZ Disposition=%x Options=%x DCON=%d \n",
|
|
GET_ALREADY_PREFIXED_NAME(NULL,capFcb),
|
|
CreateParameters->Disposition,
|
|
CreateParameters->CreateOptions,
|
|
Disconnected));
|
|
|
|
if (smbFcb->ContainingFcb == NULL) {
|
|
smbFcb->ContainingFcb = capFcb;
|
|
} else {
|
|
ASSERT(smbFcb->ContainingFcb == capFcb);
|
|
}
|
|
|
|
IsCopyChunkOpen = MRxSmbCscIsThisACopyChunkOpen(RxContext, &IsThisTheAgent);
|
|
|
|
if (IsCopyChunkOpen) {
|
|
PLIST_ENTRY ListEntry;
|
|
ULONG NumNonCopyChunkOpens = 0;
|
|
|
|
HookKdPrint(NAME, ("CreatePrologue: Copychunk Open \n"));
|
|
CreateParameters->AllocationSize.QuadPart = 0;
|
|
CreateParameters->DesiredAccess |= FILE_READ_DATA;
|
|
SetFlag(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_COPYCHUNK_OPEN);
|
|
if (IsThisTheAgent)
|
|
{
|
|
HookKdPrint(NAME, ("CreatePrologue: Agent Copychunk Open \n"));
|
|
SetFlag(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_AGENT_COPYCHUNK_OPEN);
|
|
}
|
|
SetFlag(SrvOpen->Flags,SRVOPEN_FLAG_COLLAPSING_DISABLED);
|
|
|
|
if (Disconnected) {
|
|
Status = STATUS_NETWORK_UNREACHABLE;
|
|
// RxDbgTrace(0, Dbg, ("Network Unreacheable, aborting copychunk\n"));
|
|
dwEarlyOut = 1;
|
|
goto FINALLY;
|
|
}
|
|
|
|
//check for a surrogate.....this would be studly.....
|
|
|
|
RxLog(("Checking for surrogate\n"));
|
|
|
|
for ( ListEntry = capFcb->SrvOpenList.Flink;
|
|
ListEntry != &capFcb->SrvOpenList;
|
|
ListEntry = ListEntry->Flink
|
|
) {
|
|
PMRX_SRV_OPEN SurrogateSrvOpen = CONTAINING_RECORD(
|
|
ListEntry,
|
|
MRX_SRV_OPEN,
|
|
SrvOpenQLinks);
|
|
PMRX_SMB_SRV_OPEN smbSurrogateSrvOpen = MRxSmbGetSrvOpenExtension(SurrogateSrvOpen);
|
|
|
|
if (smbSurrogateSrvOpen == NULL)
|
|
continue;
|
|
|
|
if (smbFcb->hShadow == 0) {
|
|
// if we don't have the shadow handle...just blow it off....
|
|
RxLog(("No shadow handle, quitting\n"));
|
|
break;
|
|
}
|
|
|
|
if (smbFcb->SurrogateSrvOpen != NULL) {
|
|
// if we already have a surrogate, just use it....
|
|
SurrogateSrvOpen = smbFcb->SurrogateSrvOpen;
|
|
}
|
|
|
|
ASSERT(SurrogateSrvOpen && NodeType(SurrogateSrvOpen) == RDBSS_NTC_SRVOPEN);
|
|
if (FlagOn(smbSurrogateSrvOpen->Flags,SMB_SRVOPEN_FLAG_COPYCHUNK_OPEN)) {
|
|
//cant surrogate on a copychunk open!
|
|
continue;
|
|
}
|
|
|
|
NumNonCopyChunkOpens++;
|
|
|
|
// if it's not open or not open successfully...cant surrogate
|
|
if (FlagOn(smbSurrogateSrvOpen->Flags,SMB_SRVOPEN_FLAG_NOT_REALLY_OPEN)) {
|
|
continue;
|
|
}
|
|
|
|
if (!FlagOn(smbSurrogateSrvOpen->Flags,SMB_SRVOPEN_FLAG_SUCCESSFUL_OPEN)) {
|
|
continue;
|
|
}
|
|
|
|
// if it doesn't have access for read or execute, cant surrogate
|
|
if ((SurrogateSrvOpen->DesiredAccess &
|
|
(FILE_READ_DATA|FILE_EXECUTE)) == 0) {
|
|
continue;
|
|
}
|
|
|
|
ASSERT( (smbFcb->SurrogateSrvOpen == SurrogateSrvOpen)
|
|
|| (smbFcb->SurrogateSrvOpen == NULL));
|
|
SetFlag(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_OPEN_SURROGATED);
|
|
smbFcb->SurrogateSrvOpen = SurrogateSrvOpen;
|
|
smbSrvOpen->Fid = smbSurrogateSrvOpen->Fid;
|
|
smbSrvOpen->Version = smbSurrogateSrvOpen->Version;
|
|
|
|
RxContext->pFobx = (PMRX_FOBX)RxCreateNetFobx(RxContext, SrvOpen);
|
|
|
|
if (RxContext->pFobx == NULL) {
|
|
Status = (STATUS_INSUFFICIENT_RESOURCES);
|
|
RxDbgTrace(0, Dbg, ("Failed fobx create, aborting copychunk\n"));
|
|
goto FINALLY;
|
|
}
|
|
|
|
// i thought about just using the surrogate's handle here but
|
|
// decided against it. the surrogate's localopen may have failed
|
|
// for some reason that no longer obtains...so i get my own
|
|
// handle.
|
|
|
|
#if defined(BITCOPY)
|
|
OpenFileHSHADOWAndCscBmp(
|
|
smbFcb->hShadow,
|
|
0,
|
|
0,
|
|
(CSCHFILE *)(&(smbSrvOpen->hfShadow)),
|
|
smbFcb->fDoBitCopy,
|
|
0,
|
|
NULL
|
|
);
|
|
#else
|
|
OpenFileHSHADOW(
|
|
smbFcb->hShadow,
|
|
0,
|
|
0,
|
|
(CSCHFILE *)(&(smbSrvOpen->hfShadow))
|
|
);
|
|
#endif // defined(BITCOPY)
|
|
|
|
if (smbSrvOpen->hfShadow == 0) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
RxLog(("Couldn't find a file to piggyback on, failing copychunk\n"));
|
|
} else {
|
|
SuccessfulSurrogateOpens++;
|
|
Status = STATUS_SUCCESS;
|
|
RxLog(("Found a file to piggyback on, succeeding copychunk\n"));
|
|
}
|
|
|
|
dwEarlyOut = 2;
|
|
goto FINALLY;
|
|
}
|
|
|
|
#if 0
|
|
//couldn't find a surrogate.......if there are existing opens then blowoff
|
|
//this open...the agent will come back later
|
|
#endif
|
|
|
|
if (NumNonCopyChunkOpens>0) {
|
|
RxLog(("CscCrPro Creating thru open when NonNumCopyChunkOpens is non-zero %d for hShadow=%x\n",
|
|
NumNonCopyChunkOpens, smbFcb->hShadow));
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscCreatePrologue Creating thru open when NonNumCopyChunkOpens is non-zero %d for hShadow=%x\n",
|
|
NumNonCopyChunkOpens, smbFcb->hShadow));
|
|
|
|
}
|
|
} else {
|
|
NTSTATUS LocalStatus;
|
|
|
|
LocalStatus = CscInitializeServerEntryDfsRoot(
|
|
RxContext,
|
|
pServerEntry);
|
|
|
|
if (LocalStatus != STATUS_SUCCESS) {
|
|
Status = LocalStatus;
|
|
goto FINALLY;
|
|
}
|
|
|
|
LocalStatus = MRxSmbCscLocalFileOpen(RxContext);
|
|
|
|
if (LocalStatus == STATUS_SUCCESS)
|
|
{
|
|
RxLog(("LocalOpen\n"));
|
|
Status = STATUS_SUCCESS;
|
|
Disconnected = TRUE; // do a fake disconnected open
|
|
}
|
|
else if (LocalStatus != STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
RxLog(("LocalOpen Failed Status=%x\n", LocalStatus));
|
|
Status = LocalStatus;
|
|
goto FINALLY;
|
|
}
|
|
|
|
}
|
|
|
|
if (IsCopyChunkOpen) {
|
|
AcquireOptions = (Exclusive_SmbFcbAcquire |
|
|
DroppingFcbLock_SmbFcbAcquire |
|
|
FailImmediately_SmbFcbAcquire);
|
|
|
|
} else {
|
|
AcquireOptions = (Shared_SmbFcbAcquire |
|
|
DroppingFcbLock_SmbFcbAcquire);
|
|
}
|
|
|
|
ASSERT(RxIsFcbAcquiredExclusive( capFcb ));
|
|
|
|
AcquireStatus = MRxSmbCscAcquireSmbFcb(
|
|
RxContext,
|
|
AcquireOptions,
|
|
SmbFcbHoldingState);
|
|
|
|
ASSERT(RxIsFcbAcquiredExclusive( capFcb ));
|
|
|
|
if (AcquireStatus != STATUS_SUCCESS) {
|
|
//we couldn't acquire.....get out
|
|
Status = AcquireStatus;
|
|
ASSERT(*SmbFcbHoldingState == SmbFcb_NotHeld);
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscCreatePrologue couldn't acquire!!!-> %08lx %08lx\n",
|
|
RxContext,Status ));
|
|
RxLog(("CSC AcquireStatus=%x\n", AcquireStatus));
|
|
dwEarlyOut = 3;
|
|
goto FINALLY;
|
|
}
|
|
|
|
ASSERT( IsCopyChunkOpen?(smbFcb->CscOutstandingReaders == -1)
|
|
:(smbFcb->CscOutstandingReaders > 0));
|
|
|
|
// There are two cases in which the open request can be satisfied locally.
|
|
// Either we are in a disconnected mode of operation for this share or the
|
|
// share has been marked for a mode of operation which calls for the
|
|
// suppression of opens and closes ( hereafter we will refer to it as
|
|
// (Suppress Opens Client Side Caching (SOCSC)) mode of operation to
|
|
// distinguish it from VDO ( virtual disconnected operation ) which is
|
|
// slated for subsequent releases of NT
|
|
// Note that in the SOCSC mode it is likely that we do not have the
|
|
// corresponding file cached locally. In such cases the open needs to be
|
|
// propagated to the client, i.e., you cannot create a local file without
|
|
// checking with the server for the existence of a file with the same name
|
|
|
|
if (Disconnected) {
|
|
SMBFCB_HOLDING_STATE FakeSmbFcbHoldingState = SmbFcb_NotHeld;
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscCreatePrologue calling epilog directly!!!-> %08lx %08lx\n",
|
|
RxContext,Status ));
|
|
|
|
smbSrvOpen->Flags |= SMB_SRVOPEN_FLAG_DISCONNECTED_OPEN;
|
|
|
|
//we pass a fake holdingstate and do the release from out finally
|
|
MRxSmbCscCreateEpilogue(
|
|
RxContext,
|
|
&Status,
|
|
&FakeSmbFcbHoldingState);
|
|
|
|
dwEarlyOut = 4;
|
|
goto FINALLY;
|
|
}
|
|
|
|
// deal with any existing thru-open
|
|
|
|
if (smbFcb->CopyChunkThruOpen != NULL) {
|
|
if (IsCopyChunkOpen && IsThisTheAgent){
|
|
// here we're a thruopen and there's an existing one...
|
|
// fail the new one....
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
// DbgPrint("Agent being turned away while attempting fill on %x\n", smbFcb->hShadow);
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscCreatePrologue failing new thru open!!!-> %08lx %08lx\n",
|
|
RxContext,Status ));
|
|
dwEarlyOut = 5;
|
|
goto FINALLY;
|
|
} else {
|
|
// the new open if not a thru open or it is a thruopen from the agent.
|
|
// get rid of it now
|
|
#ifdef DBG
|
|
if (IsCopyChunkOpen)
|
|
{
|
|
// This is a copychunk open by the sync manager
|
|
// assert that the thruopen being nuked is that of the agent
|
|
|
|
PMRX_SMB_SRV_OPEN psmbSrvOpenT = MRxSmbGetSrvOpenExtension(smbFcb->CopyChunkThruOpen->pSrvOpen);
|
|
// ASSERT(psmbSrvOpenT->Flags & SMB_SRVOPEN_FLAG_AGENT_COPYCHUNK_OPEN);
|
|
}
|
|
#endif
|
|
|
|
MRxSmbCscCloseExistingThruOpen(RxContext);
|
|
}
|
|
}
|
|
|
|
FINALLY:
|
|
if (EnteredCriticalSection) {
|
|
LeaveShadowCrit();
|
|
}
|
|
|
|
if (Status!=STATUS_MORE_PROCESSING_REQUIRED) {
|
|
if (AcquireStatus == STATUS_SUCCESS) {
|
|
MRxSmbCscReleaseSmbFcb(RxContext,SmbFcbHoldingState);
|
|
}
|
|
ASSERT(*SmbFcbHoldingState == SmbFcb_NotHeld);
|
|
}
|
|
|
|
if (Disconnected) {
|
|
// at shutdown, there can be a situation where an open comes down
|
|
// but CSC has already been shutdown. If CSC is shutdown, return the appropriate error
|
|
|
|
if (fShadow)
|
|
{
|
|
if (Status==STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscCreatePrologue: STATUS_MORE_PROCESSING_REQUIRED, dwEarlyOut=%d\r\n", dwEarlyOut));
|
|
}
|
|
ASSERT(Status!=STATUS_MORE_PROCESSING_REQUIRED);
|
|
}
|
|
else
|
|
{
|
|
if (AcquireStatus == STATUS_SUCCESS &&
|
|
*SmbFcbHoldingState!=SmbFcb_NotHeld) {
|
|
MRxSmbCscReleaseSmbFcb(RxContext,SmbFcbHoldingState);
|
|
}
|
|
ASSERT(*SmbFcbHoldingState == SmbFcb_NotHeld);
|
|
Status = STATUS_NETWORK_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
RxLog(("CscCrPro %x %x\n", RxContext, Status ));
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbCscCreatePrologue -> %08lx %08lx\n",
|
|
RxContext, Status ));
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbCscObtainShadowHandles (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PNTSTATUS Status,
|
|
IN OUT _WIN32_FIND_DATA *Find32,
|
|
OUT PBOOLEAN Created,
|
|
IN ULONG CreateShadowControls,
|
|
IN BOOLEAN Disconnected
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tries to obtain the shadow handles for a given fcb. If it can,
|
|
it also will get the share handle as part of the process.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
The netroot entry may have the rootinode for the actual share or it's DFS alternate.
|
|
If it is a root for the DFS alternate, the appropritae bit in the Flags field of the
|
|
sCscRootInfo structure in NetRootEntry is set.
|
|
|
|
This routine essentially manufactures the correct root inode for the incoming path
|
|
and stuffs it in the sCscRootInfo for the smbfcb. This root indoe is used for
|
|
all subsequent operations on this file.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS LocalStatus;
|
|
|
|
RxCaptureFcb;
|
|
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
PMRX_SRV_OPEN SrvOpen;
|
|
|
|
PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry = SmbCeGetAssociatedNetRootEntry(NetRoot);
|
|
|
|
CSC_SHARE_HANDLE hShare;
|
|
CSC_SHADOW_HANDLE hShadow;
|
|
CSC_ROOT_INFO *pCscRootInfo, sCscRootInfoSav;
|
|
|
|
BOOLEAN DisconnectedMode;
|
|
|
|
UNICODE_STRING ShadowPath,SharePath,ServerPath;
|
|
|
|
PDFS_NAME_CONTEXT pDfsNameContext = NULL;
|
|
DWORD cntRetry = 0;
|
|
BOOLEAN fSaved = FALSE;
|
|
|
|
DbgDoit(ASSERT(vfInShadowCrit));
|
|
|
|
LocalStatus = STATUS_SUCCESS;
|
|
|
|
SrvOpen = RxContext->pRelevantSrvOpen;
|
|
if (RxContext->MajorFunction == IRP_MJ_CREATE) {
|
|
pDfsNameContext = CscIsValidDfsNameContext(RxContext->Create.NtCreateParameters.DfsNameContext);
|
|
|
|
//SrvOpen = RxContext->Create.pSrvOpen;
|
|
} else {
|
|
RxCaptureFobx;
|
|
|
|
if (SrvOpen)
|
|
{
|
|
ASSERT((SrvOpen == capFobx->pSrvOpen));
|
|
}
|
|
}
|
|
|
|
if (smbFcb->ContainingFcb == NULL) {
|
|
smbFcb->ContainingFcb = capFcb;
|
|
} else {
|
|
ASSERT(smbFcb->ContainingFcb == capFcb);
|
|
}
|
|
|
|
if (pDfsNameContext) {
|
|
LocalStatus = CscDfsParseDfsPath(
|
|
&pDfsNameContext->UNCFileName,
|
|
&ServerPath,
|
|
&SharePath,
|
|
&ShadowPath);
|
|
|
|
HookKdPrint(NAME, ("OSHH: DfsName %wZ %wZ\n", &SharePath, &ShadowPath));
|
|
DisconnectedMode = FALSE;
|
|
|
|
pCscRootInfo = &(smbFcb->sCscRootInfo);
|
|
|
|
if (pNetRootEntry->NetRoot.sCscRootInfo.hShare)
|
|
{
|
|
sCscRootInfoSav = pNetRootEntry->NetRoot.sCscRootInfo;
|
|
fSaved = TRUE;
|
|
}
|
|
|
|
// clear this so ObtainSharehandles is forced to obtain them
|
|
memset( &(pNetRootEntry->NetRoot.sCscRootInfo),
|
|
0,
|
|
sizeof(pNetRootEntry->NetRoot.sCscRootInfo));
|
|
|
|
} else {
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(NetRoot->pSrvCall);
|
|
|
|
DisconnectedMode = SmbCeIsServerInDisconnectedMode(pServerEntry);
|
|
|
|
SharePath = *(NetRoot->pNetRootName);
|
|
ShadowPath = *GET_ALREADY_PREFIXED_NAME(SrvOpen,capFcb);
|
|
HookKdPrint(NAME, ("OSHH: NormalName %wZ %wZ\n", &SharePath, &ShadowPath));
|
|
|
|
if (pNetRootEntry->NetRoot.sCscRootInfo.Flags & CSC_ROOT_INFO_FLAG_DFS_ROOT)
|
|
|
|
{
|
|
// ASSERT(pNetRootEntry->NetRoot.sCscRootInfo.hShare);
|
|
sCscRootInfoSav = pNetRootEntry->NetRoot.sCscRootInfo;
|
|
fSaved = TRUE;
|
|
// clear this so ObtainSharehandles is forced to obtain them
|
|
memset( &(pNetRootEntry->NetRoot.sCscRootInfo),
|
|
0,
|
|
sizeof(pNetRootEntry->NetRoot.sCscRootInfo));
|
|
}
|
|
|
|
pCscRootInfo = &(pNetRootEntry->NetRoot.sCscRootInfo);
|
|
}
|
|
|
|
|
|
HookKdPrint(NAME, ("hShare=%x, hRoot=%x, hDir=%x hShadow=%x \n",
|
|
smbFcb->sCscRootInfo.hShare,
|
|
smbFcb->sCscRootInfo.hRootDir,
|
|
smbFcb->hParentDir,
|
|
smbFcb->hShadow));
|
|
|
|
|
|
if (LocalStatus == STATUS_SUCCESS){
|
|
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen;
|
|
|
|
if(pCscRootInfo->hShare == 0)
|
|
{
|
|
smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
LocalStatus = MRxSmbCscObtainShareHandles(
|
|
&SharePath,
|
|
DisconnectedMode,
|
|
BooleanFlagOn(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_COPYCHUNK_OPEN),
|
|
SmbCeGetAssociatedNetRootEntry(NetRoot)
|
|
);
|
|
|
|
if (LocalStatus != STATUS_SUCCESS) {
|
|
|
|
if (pDfsNameContext) {
|
|
pNetRootEntry->NetRoot.sCscRootInfo = sCscRootInfoSav;
|
|
}
|
|
|
|
RxLog(("CscObtShdH no share handle %x %x\n", RxContext,LocalStatus ));
|
|
RxDbgTrace(0, Dbg,("MRxSmbCscObtainShadowHandles no share handle -> %08xl %08lx\n",
|
|
RxContext,LocalStatus ));
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
else {
|
|
|
|
// if this is DFS name get the reverse mapping
|
|
|
|
if (pDfsNameContext && !smbFcb->uniDfsPrefix.Buffer)
|
|
{
|
|
UNICODE_STRING uniTemp;
|
|
uniTemp = *GET_ALREADY_PREFIXED_NAME(SrvOpen,capFcb);
|
|
if (RxContext->Create.NtCreateParameters.DfsContext == UIntToPtr(DFS_OPEN_CONTEXT))
|
|
{
|
|
int i, cntSlashes=0;
|
|
|
|
// Strip the first two elements
|
|
|
|
for (i=0; i<uniTemp.Length; i+=2, uniTemp.Buffer++)
|
|
{
|
|
if (*uniTemp.Buffer == L'\\')
|
|
{
|
|
if (++cntSlashes > 2)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (uniTemp.Length > (USHORT)i)
|
|
{
|
|
uniTemp.Length -= (USHORT)i;
|
|
}
|
|
else
|
|
{
|
|
uniTemp.Length = 0;
|
|
}
|
|
|
|
}
|
|
if ((LocalStatus = CscDfsObtainReverseMapping(&ShadowPath,
|
|
&uniTemp,
|
|
&smbFcb->uniDfsPrefix,
|
|
&smbFcb->uniActualPrefix)) != STATUS_SUCCESS)
|
|
{
|
|
pNetRootEntry->NetRoot.sCscRootInfo = sCscRootInfoSav;
|
|
return LocalStatus;
|
|
}
|
|
|
|
HookKdPrint(NAME, ("%wZ %wZ DfsPrefix=%wZ ActualPrefix=%wZ\n", &ShadowPath, &uniTemp, &smbFcb->uniDfsPrefix, &smbFcb->uniActualPrefix));
|
|
|
|
}
|
|
|
|
// note the fact that the NetRootEntry has the
|
|
// root inode corresponding to the DFS
|
|
if (pDfsNameContext)
|
|
{
|
|
if (pNetRootEntry->NetRoot.sCscRootInfo.hShare)
|
|
{
|
|
pNetRootEntry->NetRoot.sCscRootInfo.Flags = CSC_ROOT_INFO_FLAG_DFS_ROOT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!(pNetRootEntry->NetRoot.sCscRootInfo.Flags & CSC_ROOT_INFO_FLAG_DFS_ROOT));
|
|
}
|
|
|
|
// stuff the FCB with the correct root info
|
|
smbFcb->sCscRootInfo = pNetRootEntry->NetRoot.sCscRootInfo;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if this is a normal share or we are operating in disconnected state
|
|
// then we need to make sure that the info that is in the NETROOT entry
|
|
// is stuffed into the FCB
|
|
if (!pDfsNameContext && (smbFcb->sCscRootInfo.hShare == 0))
|
|
{
|
|
// stuff the FCB with the correct root info
|
|
smbFcb->sCscRootInfo = pNetRootEntry->NetRoot.sCscRootInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if saved, restore the original rootinfo on the netroot
|
|
if (fSaved)
|
|
{
|
|
pNetRootEntry->NetRoot.sCscRootInfo = sCscRootInfoSav;
|
|
}
|
|
|
|
if ((LocalStatus == STATUS_SUCCESS)&&(smbFcb->sCscRootInfo.hRootDir != 0)) {
|
|
|
|
if (smbFcb->sCscRootInfo.hShare == hShareReint)
|
|
{
|
|
smbFcb->hShadow = 0;
|
|
smbFcb->hParentDir = 0;
|
|
LocalStatus = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
|
|
RxDbgTrace( 0, Dbg,
|
|
("MRxSmbCscObtainShadowHandles h's= %08lx %08lx\n",
|
|
pCscRootInfo->hShare, pCscRootInfo->hRootDir));
|
|
|
|
HookKdPrint(NAME, ("Obtainshdowhandles %wZ Controls=%x\n", &ShadowPath, CreateShadowControls));
|
|
|
|
// due to a race condition in the way RDBSS handles FCBs
|
|
// we may get a retyr from CreateShadowFromPath.
|
|
// see the comments in that routine near the
|
|
// check for CREATESHADOW_CONTROL_FAIL_IF_MARKED_FOR_DELETION
|
|
do
|
|
{
|
|
LocalStatus = MRxSmbCscCreateShadowFromPath(
|
|
&ShadowPath,
|
|
&smbFcb->sCscRootInfo,
|
|
Find32,
|
|
Created,
|
|
CreateShadowControls,
|
|
&smbFcb->MinimalCscSmbFcb,
|
|
RxContext,
|
|
Disconnected,
|
|
NULL // don't want inherited hint flags
|
|
);
|
|
|
|
if (LocalStatus != STATUS_RETRY)
|
|
{
|
|
LocalStatus=STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
if (++cntRetry > 4)
|
|
{
|
|
LocalStatus=STATUS_SUCCESS;
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
while (TRUE);
|
|
}
|
|
}
|
|
#if 0
|
|
DbgPrint("hShare=%x, hRoot=%x, hDir=%x hShadow=%x \n",
|
|
smbFcb->sCscRootInfo.hShare,
|
|
smbFcb->sCscRootInfo.hRootDir,
|
|
smbFcb->hParentDir,
|
|
smbFcb->hShadow);
|
|
#endif
|
|
|
|
return LocalStatus;
|
|
}
|
|
|
|
|
|
#if defined(REMOTE_BOOT)
|
|
NTSTATUS
|
|
MRxSmbCscSetSecurityOnShadow(
|
|
HSHADOW hShadow,
|
|
SECURITY_INFORMATION SecurityInformation,
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given an HSHADOW, this routine opens the file and sets the
|
|
security information passed in.
|
|
|
|
Arguments:
|
|
|
|
hShadow - the handle to the shadow file
|
|
|
|
SecurityInformation - the security information to set
|
|
|
|
SecurityDescriptor - the security descriptor to set
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PWCHAR fileName;
|
|
DWORD fileNameSize;
|
|
UNICODE_STRING fileNameString;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
HANDLE fileHandle;
|
|
NTSTATUS ZwStatus;
|
|
int iRet;
|
|
|
|
fileNameSize = sizeof(WCHAR) * MAX_PATH;
|
|
fileName = RxAllocatePoolWithTag(NonPagedPool, fileNameSize, RX_MISC_POOLTAG);
|
|
if (fileName == NULL) {
|
|
|
|
ZwStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
} else {
|
|
|
|
iRet = GetWideCharLocalNameHSHADOW(
|
|
hShadow,
|
|
fileName,
|
|
&fileNameSize,
|
|
FALSE);
|
|
|
|
if (iRet == SRET_OK) {
|
|
|
|
//
|
|
// Open the file and set the security descriptor on it.
|
|
//
|
|
|
|
RtlInitUnicodeString(&fileNameString, fileName);
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&fileNameString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
ZwStatus = ZwOpenFile(
|
|
&fileHandle,
|
|
FILE_GENERIC_WRITE,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
|
|
if (!NT_SUCCESS(ZwStatus) || !NT_SUCCESS(ioStatusBlock.Status)) {
|
|
//
|
|
// We've been getting bogus names from CSC, ignore this error for now.
|
|
//
|
|
if (ZwStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
ZwStatus = STATUS_SUCCESS;
|
|
} else {
|
|
KdPrint(("MRxSmbCscSetSecurityOnShadow: Could not ZwOpenFile %ws %lx %lx\n", fileName, ZwStatus, ioStatusBlock.Status));
|
|
}
|
|
} else {
|
|
|
|
HANDLE TokenHandle = NULL;
|
|
BOOLEAN Impersonated = FALSE;
|
|
BOOLEAN WasEnabled;
|
|
|
|
//
|
|
// If we are going to set the owner, need to have privilege.
|
|
//
|
|
|
|
if (SecurityInformation & OWNER_SECURITY_INFORMATION) {
|
|
|
|
ZwStatus = ZwOpenThreadToken(NtCurrentThread(),
|
|
TOKEN_QUERY,
|
|
FALSE,
|
|
&TokenHandle);
|
|
|
|
if (ZwStatus == STATUS_NO_TOKEN) {
|
|
TokenHandle = NULL;
|
|
ZwStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!NT_SUCCESS(ZwStatus)) {
|
|
KdPrint(("MRxSmbCscSetSecurityOnShadow: Could not NtOpenThread %ws %lx\n", fileName, ZwStatus));
|
|
} else {
|
|
ZwStatus = ZwImpersonateSelf(SecurityImpersonation);
|
|
if (!NT_SUCCESS(ZwStatus)) {
|
|
KdPrint(("MRxSmbCscSetSecurityOnShadow: Could not RtlImpersonateSelf for %ws %lx\n", fileName, ZwStatus));
|
|
} else {
|
|
Impersonated = TRUE;
|
|
ZwStatus = ZwAdjustPrivilege(
|
|
SE_RESTORE_PRIVILEGE,
|
|
TRUE,
|
|
TRUE,
|
|
&WasEnabled);
|
|
if (!NT_SUCCESS(ZwStatus)) {
|
|
KdPrint(("MRxSmbCscSetSecurityOnShadow: Could not RtlAdjustPrivilege for %ws %lx %d\n", fileName, ZwStatus, WasEnabled));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(ZwStatus)) {
|
|
|
|
ZwStatus = ZwSetSecurityObject(
|
|
fileHandle,
|
|
SecurityInformation,
|
|
SecurityDescriptor);
|
|
|
|
if (!NT_SUCCESS(ZwStatus)) {
|
|
KdPrint(("MRxSmbCscSetSecurityOnShadow: Could not ZwSetSecurityObject %ws %lx\n", fileName, ZwStatus));
|
|
}
|
|
}
|
|
|
|
if (Impersonated) {
|
|
NTSTATUS TmpStatus;
|
|
TmpStatus = ZwSetInformationThread(NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
&TokenHandle,
|
|
sizeof(HANDLE));
|
|
if (!NT_SUCCESS(TmpStatus)) {
|
|
KdPrint(("MRxSmbCscSetSecurityOnShadow: Could not revert thread %lx!\n", TmpStatus));
|
|
}
|
|
|
|
}
|
|
|
|
if (TokenHandle != NULL) {
|
|
ZwClose(TokenHandle);
|
|
}
|
|
|
|
ZwClose(fileHandle);
|
|
}
|
|
|
|
} else {
|
|
|
|
ZwStatus = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
RxFreePool(fileName);
|
|
|
|
}
|
|
|
|
return ZwStatus;
|
|
|
|
}
|
|
#endif
|
|
|
|
VOID
|
|
MRxSmbCscCreateEpilogue (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PNTSTATUS Status,
|
|
IN SMBFCB_HOLDING_STATE *SmbFcbHoldingState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the tail of a create operation for CSC.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Status - in disconnected mode, we return the overall status of the open
|
|
|
|
SmbFcbHoldingState - indicates whether a ReleaseSmbFcb is required or not
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
This is a workhorse of a routine for most of the CSC operations. It has become unwieldy and
|
|
very messy but at this point in time we don't want to mess with it (SPP)
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS LocalStatus;
|
|
RxCaptureFcb;RxCaptureFobx;
|
|
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
BOOLEAN ThisIsAPseudoOpen =
|
|
BooleanFlagOn(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_NOT_REALLY_OPEN);
|
|
|
|
PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
|
|
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
|
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry =
|
|
SmbCeGetAssociatedNetRootEntry(NetRoot);
|
|
|
|
BOOLEAN Disconnected;
|
|
|
|
CSC_SHARE_HANDLE hShare;
|
|
CSC_SHADOW_HANDLE hRootDir,hShadow,hParentDir;
|
|
|
|
BOOLEAN ShadowWasRefreshed = FALSE;
|
|
PNT_CREATE_PARAMETERS CreateParameters = &RxContext->Create.NtCreateParameters;
|
|
|
|
ULONG ReturnedCreateInformation
|
|
= RxContext->Create.ReturnedCreateInformation;
|
|
ULONG CreateInformation; //the new stuff for disconnected mode......
|
|
ULONG CreateDisposition = RxContext->Create.NtCreateParameters.Disposition;
|
|
ULONG CreateOptions = RxContext->Create.NtCreateParameters.CreateOptions;
|
|
|
|
_WIN32_FIND_DATA *lpFind32=NULL; //this should not be on the stack CODE.IMPROVEMENT
|
|
OTHERINFO oSI;
|
|
BOOLEAN bGotOtherInfo = FALSE;
|
|
|
|
BOOLEAN CreatedShadow = FALSE;
|
|
BOOLEAN NeedTruncate = FALSE; //ok for red/yellow
|
|
BOOLEAN EnteredCriticalSection = FALSE;
|
|
DWORD dwEarlyOuts=0, dwNotifyFilter=0, dwFileAction=0;
|
|
|
|
ASSERT(RxContext!=NULL);
|
|
ASSERT(RxContext->MajorFunction == IRP_MJ_CREATE);
|
|
|
|
pVNetRootContext = SmbCeGetAssociatedVNetRootContext(SrvOpen->pVNetRoot);
|
|
|
|
// If we shouldn't be doing CSC, quit right from here
|
|
|
|
if(pVNetRootContext == NULL ||
|
|
!MRxSmbIsCscEnabled || // MrxSmb not enabled for csc
|
|
(fShadow == 0)) // record manager not enabled for csc
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (FlagOn( // agent call
|
|
pVNetRootContext->Flags,
|
|
SMBCE_V_NET_ROOT_CONTEXT_CSCAGENT_INSTANCE)
|
|
) {
|
|
RxLog(("%wZ AgntInst\n", &(pVNetRootContext->pNetRootEntry->Name)));
|
|
ASSERT(SrvOpen->pVNetRoot->Flags & VNETROOT_FLAG_CSCAGENT_INSTANCE);
|
|
// DbgPrint("Skipping agent instances\n");
|
|
goto EarlyOut;
|
|
}
|
|
|
|
{
|
|
// we know that this is a create
|
|
PDFS_NAME_CONTEXT pDfsNameContext = CscIsValidDfsNameContext(
|
|
RxContext->Create.NtCreateParameters.DfsNameContext);
|
|
|
|
if (pDfsNameContext && (pDfsNameContext->NameContextType == DFS_CSCAGENT_NAME_CONTEXT)) {
|
|
RxLog(("%wZ DFS AgntInst\n", &(pVNetRootContext->pNetRootEntry->Name)));
|
|
goto EarlyOut;
|
|
}
|
|
}
|
|
|
|
// check whether this is a disconnected open or not
|
|
Disconnected = BooleanFlagOn(
|
|
smbSrvOpen->Flags,
|
|
SMB_SRVOPEN_FLAG_DISCONNECTED_OPEN);
|
|
|
|
HookKdPrint(NAME, ("CreateEpilogue: Create %wZ Disposition=%x Options=%x DCON=%d \n",
|
|
GET_ALREADY_PREFIXED_NAME(NULL,capFcb),
|
|
CreateDisposition,
|
|
CreateOptions,
|
|
Disconnected));
|
|
|
|
// If the open is for a netroot which is not a disk, then we quit from here
|
|
// and let the redir handle it in connected state
|
|
// CSC is a filesystem cache.
|
|
|
|
if (pNetRootEntry->NetRoot.NetRootType != NET_ROOT_DISK) {
|
|
|
|
if (Disconnected) {
|
|
*Status = STATUS_ONLY_IF_CONNECTED;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// quit if we are doing sparse filling via copychunks
|
|
if (!Disconnected &&
|
|
(!pNetRootEntry->NetRoot.CscEnabled) &&
|
|
!FlagOn(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_COPYCHUNK_OPEN)) {
|
|
goto EarlyOut;
|
|
}
|
|
|
|
if (*SmbFcbHoldingState != SmbFcb_NotHeld) {
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscCreateEpilogue...early release %08lx\n",
|
|
RxContext));
|
|
MRxSmbCscReleaseSmbFcb(RxContext,SmbFcbHoldingState);
|
|
ASSERT(*SmbFcbHoldingState == SmbFcb_NotHeld);
|
|
}
|
|
|
|
ASSERT(RxIsFcbAcquiredExclusive( capFcb ));
|
|
|
|
if ((*Status != STATUS_SUCCESS) &&
|
|
(*Status != STATUS_ACCESS_DENIED)) {
|
|
if (!Disconnected ||
|
|
(*Status != STATUS_MORE_PROCESSING_REQUIRED)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Shadow database locked
|
|
|
|
EnterShadowCritRx(RxContext);
|
|
EnteredCriticalSection = TRUE;
|
|
|
|
lpFind32 = RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(_WIN32_FIND_DATA),
|
|
MRXSMB_MISC_POOLTAG );
|
|
|
|
if (!lpFind32)
|
|
{
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscCreateShadowFromPath: Failed allocation of find32 structure \n"));
|
|
dwEarlyOuts=1;
|
|
*Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FINALLY;
|
|
}
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbCscCreateEpilogue...%08lx %wZ %wZ\n",
|
|
RxContext,NetRoot->pNetRootName,GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext)));
|
|
|
|
// if this is a copychunk thru-open....say so in the fcb
|
|
if (FlagOn(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_COPYCHUNK_OPEN)) {
|
|
// ASSERT(smbFcb->CopyChunkThruOpen == NULL);
|
|
smbFcb->CopyChunkThruOpen = capFobx;
|
|
RxDbgTrace( 0, Dbg,
|
|
("MRxSmbCscCreateEpilogue set ccto %08lx %08lx %08lx %08lx\n",
|
|
RxContext, capFcb, capFobx, SrvOpen));
|
|
}
|
|
|
|
if (ThisIsAPseudoOpen && !Disconnected) {
|
|
|
|
|
|
PDFS_NAME_CONTEXT pDfsNameContext = NULL;
|
|
|
|
|
|
pDfsNameContext = CscIsValidDfsNameContext(RxContext->Create.NtCreateParameters.DfsNameContext);
|
|
|
|
if (pDfsNameContext)
|
|
{
|
|
LocalStatus = MRxSmbCscObtainShadowHandles(
|
|
RxContext,
|
|
Status,
|
|
lpFind32,
|
|
&CreatedShadow,
|
|
CREATESHADOW_CONTROL_NOCREATE,
|
|
FALSE);
|
|
}
|
|
|
|
hShadow = 0;
|
|
} else {
|
|
// if we have no shadow, make one as required..........ok red/yellow
|
|
|
|
if (smbFcb->hShadow == 0){
|
|
ULONG CreateShadowControl;
|
|
|
|
if (!Disconnected) {
|
|
if (*Status == STATUS_ACCESS_DENIED) {
|
|
CreateShadowControl = CREATESHADOW_CONTROL_NOCREATE;
|
|
} else {
|
|
|
|
CreateShadowControl = (pNetRootEntry->NetRoot.CscShadowable)
|
|
? CREATESHADOW_NO_SPECIAL_CONTROLS
|
|
: CREATESHADOW_CONTROL_NOCREATE;
|
|
|
|
// The step below is done to save bandwidth and make some apps
|
|
// like edit.exe work.
|
|
// It essentially creates an entry in the database for a file which
|
|
// is being created from this client on the server.
|
|
// Thus the temp files created by apps like word get created
|
|
// in the database and filled up during the writes.
|
|
|
|
if((ReturnedCreateInformation<= FILE_MAXIMUM_DISPOSITION) &&
|
|
(ReturnedCreateInformation!=FILE_OPENED)) {
|
|
|
|
|
|
CreateShadowControl &= ~CREATESHADOW_CONTROL_NOCREATE;
|
|
}
|
|
|
|
// disallow autocaching of encrypted files if the database is
|
|
// not encrypted.
|
|
if ((vulDatabaseStatus & FLAG_DATABASESTATUS_ENCRYPTED) == 0
|
|
&&
|
|
smbFcb->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED
|
|
) {
|
|
CreateShadowControl |= CREATESHADOW_CONTROL_NOCREATE;
|
|
}
|
|
|
|
//
|
|
// The Windows Explorer likes to open .DLL and .EXE files to extract
|
|
// the ICON -- and it does this whenever it is trying to show the contents
|
|
// of a directory. We don't want this explorer activity to force the files
|
|
// to be cached. So we only automatically cache .DLL and .EXE files if they
|
|
// are being opened for execution
|
|
// We make an exception for VDO shares. The rational being that most
|
|
// often the users would run apps without opening a folders
|
|
|
|
if( CreateShadowControl == CREATESHADOW_NO_SPECIAL_CONTROLS &&
|
|
!(CreateParameters->DesiredAccess & FILE_EXECUTE)
|
|
&&(pNetRootEntry->NetRoot.CscFlags != SMB_CSC_CACHE_VDO)) {
|
|
|
|
PUNICODE_STRING fileName = GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext);
|
|
|
|
UNICODE_STRING exe = { 3*sizeof(WCHAR), 3*sizeof(WCHAR), L"exe" };
|
|
UNICODE_STRING dll = { 3*sizeof(WCHAR), 3*sizeof(WCHAR), L"dll" };
|
|
UNICODE_STRING s;
|
|
|
|
//
|
|
// If the filename ends in .DLL or .EXE, then we do not cache it this time
|
|
//
|
|
if( fileName->Length > 4 * sizeof(WCHAR) &&
|
|
fileName->Buffer[ fileName->Length/sizeof(WCHAR) - 4 ] == L'.'){
|
|
|
|
s.Length = s.MaximumLength = 3 * sizeof( WCHAR );
|
|
s.Buffer = &fileName->Buffer[ (fileName->Length - s.Length)/sizeof(WCHAR) ];
|
|
|
|
if( RtlCompareUnicodeString( &s, &exe, TRUE ) == 0 ||
|
|
RtlCompareUnicodeString( &s, &dll, TRUE ) == 0 ) {
|
|
|
|
CreateShadowControl = CREATESHADOW_CONTROL_FILE_WITH_HEURISTIC;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else { //disconnected
|
|
switch (CreateDisposition) {
|
|
case FILE_OVERWRITE:
|
|
case FILE_OPEN:
|
|
CreateShadowControl = CREATESHADOW_CONTROL_NOCREATE;
|
|
break;
|
|
case FILE_CREATE:
|
|
case FILE_SUPERSEDE: //NTRAID-455238-1/31/2000-shishirp supersede not implemented
|
|
case FILE_OVERWRITE_IF:
|
|
case FILE_OPEN_IF:
|
|
default:
|
|
CreateShadowControl = CREATESHADOW_NO_SPECIAL_CONTROLS;
|
|
break;
|
|
}
|
|
|
|
if (*Status != STATUS_MORE_PROCESSING_REQUIRED) {
|
|
dwEarlyOuts=2;
|
|
goto FINALLY;
|
|
}
|
|
|
|
// make sure we do share access check before we do any damage
|
|
CreateShadowControl |= CREATESHADOW_CONTROL_DO_SHARE_ACCESS_CHECK;
|
|
|
|
} // if (!Disconnected)
|
|
|
|
CreateShadowControl |= CREATESHADOW_CONTROL_FAIL_IF_MARKED_FOR_DELETION;
|
|
|
|
if (!Disconnected &&
|
|
((ReturnedCreateInformation==FILE_OPENED) ||
|
|
(ReturnedCreateInformation==FILE_OVERWRITTEN) ) ){
|
|
CreateShadowControl |= CREATESHADOW_CONTROL_SPARSECREATE;
|
|
}
|
|
|
|
LocalStatus = MRxSmbCscObtainShadowHandles(
|
|
RxContext,
|
|
Status,
|
|
lpFind32,
|
|
&CreatedShadow,
|
|
CreateShadowControl,
|
|
Disconnected);
|
|
|
|
if (LocalStatus != STATUS_SUCCESS) {
|
|
RxDbgTrace(-1, Dbg,
|
|
("MRxSmbCscCreateEpilogue no handles-> %08lx %08lx\n",RxContext,LocalStatus ));
|
|
dwEarlyOuts=3;
|
|
*Status = LocalStatus;
|
|
goto FINALLY;
|
|
}
|
|
|
|
// if we got an inode for a file which was opened or created recently
|
|
// and there have been some writes on it before this
|
|
// then we need to truncate the data so we don't give stale data to the user
|
|
|
|
if (smbFcb->hShadow &&
|
|
IsFile(lpFind32->dwFileAttributes) &&
|
|
!CreatedShadow &&
|
|
FlagOn(smbFcb->MFlags, SMB_FCB_FLAG_WRITES_PERFORMED))
|
|
{
|
|
if(TruncateDataHSHADOW(smbFcb->hParentDir, smbFcb->hShadow) < SRET_OK)
|
|
{
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscCreateEpilogue: Failed to get shadowinfo for hDir=%x hShadow=%x \r\n", smbFcb->hParentDir, smbFcb->hShadow));
|
|
dwEarlyOuts=31;
|
|
goto FINALLY;
|
|
}
|
|
}
|
|
|
|
} else { //
|
|
ULONG uShadowStatus;
|
|
int iRet;
|
|
|
|
RxDbgTrace( 0, Dbg,
|
|
("MRxSmbCscCreateEpilogue found existing hdir/hshadow= %08lx %08lx\n",
|
|
smbFcb->hParentDir, smbFcb->hShadow));
|
|
|
|
iRet = GetShadowInfo(
|
|
smbFcb->hParentDir,
|
|
smbFcb->hShadow,
|
|
lpFind32,
|
|
&uShadowStatus,
|
|
&oSI);
|
|
|
|
if (iRet < SRET_OK) {
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscCreateEpilogue: Failed to get shadowinfo for hDir=%x hShadow=%x \r\n", smbFcb->hParentDir, smbFcb->hShadow));
|
|
dwEarlyOuts=4;
|
|
goto FINALLY;
|
|
}
|
|
|
|
bGotOtherInfo = TRUE;
|
|
|
|
//
|
|
// Notepad bug (175322) - quick open/close/open can lose bits - OR in the disk bits
|
|
// with the in-memory bits.
|
|
//
|
|
smbFcb->ShadowStatus |= (USHORT)uShadowStatus;
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscCreateEpilogue name from lpFind32..<%ws>\n",lpFind32->cFileName));
|
|
|
|
}
|
|
|
|
hShadow = smbFcb->hShadow;
|
|
hParentDir = smbFcb->hParentDir;
|
|
|
|
//
|
|
// If a file is encrypted, but the cache is not, we only allow a file
|
|
// to be cached when the user explicitly asks to do so.
|
|
//
|
|
// Unless we're in the middle of an inode transaction...
|
|
//
|
|
|
|
if (
|
|
!Disconnected
|
|
&&
|
|
hShadow != 0
|
|
&&
|
|
cntInodeTransactions == 0
|
|
&&
|
|
(vulDatabaseStatus & FLAG_DATABASESTATUS_ENCRYPTED) == 0
|
|
&&
|
|
(smbFcb->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0
|
|
) {
|
|
int iRet = SRET_OK;
|
|
ULONG uShadowStatus;
|
|
|
|
if (bGotOtherInfo == FALSE) {
|
|
iRet = GetShadowInfo(
|
|
smbFcb->hParentDir,
|
|
smbFcb->hShadow,
|
|
lpFind32,
|
|
&uShadowStatus,
|
|
&oSI);
|
|
}
|
|
if (
|
|
iRet >= SRET_OK
|
|
&&
|
|
(oSI.ulHintFlags & (FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_SYSTEM)) == 0
|
|
&&
|
|
oSI.ulHintPri == 0
|
|
) {
|
|
DeleteShadow(hParentDir, hShadow);
|
|
hShadow = smbFcb->hShadow = 0;
|
|
}
|
|
}
|
|
|
|
if (hShadow != 0) {
|
|
|
|
if (Disconnected)
|
|
{
|
|
if ((CreateOptions & FILE_DIRECTORY_FILE) && !(lpFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
*Status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
goto FINALLY;
|
|
}
|
|
if ((CreateOptions & FILE_NON_DIRECTORY_FILE) && (lpFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
*Status = STATUS_FILE_IS_A_DIRECTORY;
|
|
goto FINALLY;
|
|
}
|
|
|
|
|
|
// don't allow writing to a readonly file
|
|
if (!(lpFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && // a file
|
|
(lpFind32->dwFileAttributes & FILE_ATTRIBUTE_READONLY) && // readonly attribute set
|
|
(CreateParameters->DesiredAccess & (FILE_WRITE_DATA|FILE_APPEND_DATA))) // wants to modify
|
|
{
|
|
if (!CreatedShadow)
|
|
{
|
|
HookKdPrint(NAME, ("Modifying RO file %x %x\n", hParentDir, hShadow));
|
|
*Status = STATUS_ACCESS_DENIED;
|
|
goto FINALLY;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the Shadow handle was successfully manufactured we have one of
|
|
// two possibilities -- In a disconnected state the access check
|
|
// needs to be made and in the connected state the access rights need
|
|
// to be updated.
|
|
#if defined(REMOTE_BOOT)
|
|
// For remote boot, this whole next section (the if(Disconnected)
|
|
// and the else clause) was not done, since we later impersonated
|
|
// the user while opening the file.
|
|
#endif
|
|
if (Disconnected) {
|
|
BOOLEAN AccessGranted;
|
|
CACHED_SECURITY_INFORMATION CachedSecurityInformationForShare;
|
|
|
|
memset(&CachedSecurityInformationForShare, 0, sizeof(CachedSecurityInformationForShare));
|
|
|
|
AccessGranted = CscAccessCheck(
|
|
hParentDir,
|
|
hShadow,
|
|
RxContext,
|
|
CreateParameters->DesiredAccess,
|
|
NULL,
|
|
&CachedSecurityInformationForShare
|
|
);
|
|
|
|
if (!AccessGranted) {
|
|
HookKdPrint(BADERRORS, ("Security access denied %x %x\n", hParentDir, hShadow));
|
|
*Status = STATUS_ACCESS_DENIED;
|
|
hShadow = 0;
|
|
}
|
|
else if (CreatedShadow && !(lpFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
DWORD CscStatus, i;
|
|
SID_CONTEXT SidContext;
|
|
CSC_SID_ACCESS_RIGHTS AccessRights[2];
|
|
|
|
// if the file has been created in offline mode, give the
|
|
// creator all the rights
|
|
|
|
if (CscRetrieveSid(RxContext,&SidContext) == STATUS_SUCCESS) {
|
|
if (SidContext.pSid != NULL) {
|
|
|
|
AccessRights[0].pSid = SidContext.pSid;
|
|
AccessRights[0].SidLength = RtlLengthSid(SidContext.pSid);
|
|
|
|
AccessRights[0].MaximalAccessRights = FILE_ALL_ACCESS;
|
|
AccessRights[1].pSid = CSC_GUEST_SID;
|
|
AccessRights[1].SidLength = CSC_GUEST_SID_LENGTH;
|
|
AccessRights[1].MaximalAccessRights = 0;
|
|
|
|
#if 0
|
|
for (i=0;i<CSC_MAXIMUM_NUMBER_OF_CACHED_SID_INDEXES;++i)
|
|
{
|
|
if(CachedSecurityInformationForShare.AccessRights[i].SidIndex == CSC_GUEST_SID_INDEX)
|
|
{
|
|
AccessRights[1].MaximalAccessRights = CachedSecurityInformationForShare.AccessRights[i].MaximalRights;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
CscStatus = CscAddMaximalAccessRightsForSids(
|
|
hParentDir,
|
|
hShadow,
|
|
2,
|
|
AccessRights);
|
|
|
|
if (CscStatus != ERROR_SUCCESS) {
|
|
RxDbgTrace(
|
|
0,
|
|
Dbg,
|
|
("MRxSmbCscCreateEpilogue Error Updating Access rights %lx\n",Status));
|
|
}
|
|
} // if (SidContext.pSid != NULL)
|
|
|
|
CscDiscardSid(&SidContext);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
CSC_SID_ACCESS_RIGHTS AccessRights[2];
|
|
DWORD CscStatus;
|
|
SID_CONTEXT SidContext;
|
|
|
|
if (CscRetrieveSid(RxContext,&SidContext) == STATUS_SUCCESS) {
|
|
if (SidContext.pSid != NULL) {
|
|
|
|
AccessRights[0].pSid = SidContext.pSid;
|
|
AccessRights[0].SidLength = RtlLengthSid(SidContext.pSid);
|
|
|
|
// update the share right if necessary
|
|
if (pNetRootEntry->NetRoot.UpdateCscShareRights)
|
|
{
|
|
AccessRights[0].MaximalAccessRights = pNetRootEntry->MaximalAccessRights;
|
|
|
|
AccessRights[1].pSid = CSC_GUEST_SID;
|
|
AccessRights[1].SidLength = CSC_GUEST_SID_LENGTH;
|
|
AccessRights[1].MaximalAccessRights = pNetRootEntry->GuestMaximalAccessRights;
|
|
|
|
CscStatus = CscAddMaximalAccessRightsForShare(
|
|
smbFcb->sCscRootInfo.hShare,
|
|
2,
|
|
AccessRights);
|
|
if (CscStatus != ERROR_SUCCESS) {
|
|
RxDbgTrace(
|
|
0,
|
|
Dbg,
|
|
("MRxSmbCscCreateEpilogue Error Updating Access rights %lx\n",
|
|
Status));
|
|
}
|
|
else
|
|
{
|
|
pNetRootEntry->NetRoot.UpdateCscShareRights = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
if (!(lpFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
|
|
AccessRights[0].MaximalAccessRights = smbSrvOpen->MaximalAccessRights;
|
|
|
|
AccessRights[1].pSid = CSC_GUEST_SID;
|
|
AccessRights[1].SidLength = CSC_GUEST_SID_LENGTH;
|
|
AccessRights[1].MaximalAccessRights = smbSrvOpen->GuestMaximalAccessRights;
|
|
|
|
if (*Status == STATUS_ACCESS_DENIED) {
|
|
AccessRights[0].MaximalAccessRights = 0;
|
|
AccessRights[1].MaximalAccessRights = 0;
|
|
} else {
|
|
AccessRights[0].MaximalAccessRights = smbSrvOpen->MaximalAccessRights;
|
|
AccessRights[1].MaximalAccessRights = smbSrvOpen->GuestMaximalAccessRights;
|
|
}
|
|
|
|
CscStatus = CscAddMaximalAccessRightsForSids(
|
|
hParentDir,
|
|
hShadow,
|
|
2,
|
|
AccessRights);
|
|
|
|
if (CscStatus != ERROR_SUCCESS) {
|
|
RxDbgTrace(
|
|
0,
|
|
Dbg,
|
|
("MRxSmbCscCreateEpilogue Error Updating Access rights %lx\n",Status));
|
|
}
|
|
}
|
|
} // if (SidContext.pSid != NULL)
|
|
|
|
CscDiscardSid(&SidContext);
|
|
} // if (CscRetrieveSid(RxContext,&SidContext) == STATUS_SUCCESS)
|
|
|
|
// having updated the access fields, we quit from here.
|
|
if (*Status == STATUS_ACCESS_DENIED)
|
|
{
|
|
goto FINALLY;
|
|
}
|
|
}
|
|
} // if (hShadow != 0)
|
|
|
|
if ((hShadow != 0) &&
|
|
!Disconnected && //ok for red/yellow
|
|
(NodeType(capFcb) == RDBSS_NTC_STORAGE_TYPE_FILE)) {
|
|
|
|
// here we check to see if the timestamp of the file has changed
|
|
// if so, the shadow needs to be truncated so we don't use it anymore
|
|
ULONG ShadowStatus;
|
|
LONG ShadowApiReturn;
|
|
|
|
lpFind32->ftLastWriteTime.dwHighDateTime = smbFcb->LastCscTimeStampHigh;
|
|
lpFind32->ftLastWriteTime.dwLowDateTime = smbFcb->LastCscTimeStampLow;
|
|
lpFind32->nFileSizeHigh = smbFcb->NewShadowSize.HighPart;
|
|
lpFind32->nFileSizeLow = smbFcb->NewShadowSize.LowPart;
|
|
lpFind32->dwFileAttributes = smbFcb->dwFileAttributes;
|
|
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscCreateEpilogue trying for refresh...<%ws>\n",lpFind32->cFileName));
|
|
|
|
ShadowApiReturn = RefreshShadow(
|
|
hParentDir, //HSHADOW hDir,
|
|
hShadow, //HSHADOW hShadow,
|
|
lpFind32, //LPFIND32 lpFind32,
|
|
&ShadowStatus //ULONG *lpuShadowStatus
|
|
);
|
|
|
|
if (ShadowApiReturn<0) {
|
|
hShadow = 0;
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscCreateEpilogue refresh failed..%08lx.<%ws>\n",RxContext,lpFind32->cFileName));
|
|
} else {
|
|
smbFcb->ShadowStatus = (USHORT)ShadowStatus;
|
|
if ( ShadowApiReturn==1)
|
|
{
|
|
ShadowWasRefreshed = 1;
|
|
//WinSE Bug 28543
|
|
//Set this flag so that we remember that we truncated the file
|
|
//and so in MrxSmbCSCUpdateShadowFromClose we don't reset the
|
|
//SPARSE flag. - NavjotV
|
|
SetFlag(smbFcb->MFlags,SMB_FCB_FLAG_CSC_TRUNCATED_SHADOW);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
NeedTruncate = FALSE;
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscCreateEpilogue trying for truncate...%08lx %08lx %08lx %08lx\n",
|
|
RxContext,hShadow,CreatedShadow,
|
|
RxContext->Create.ReturnedCreateInformation));
|
|
|
|
if (hShadow != 0) {
|
|
|
|
if (!Disconnected) {
|
|
CreateInformation = ReturnedCreateInformation;
|
|
|
|
if (!CreatedShadow &&
|
|
(ReturnedCreateInformation<= FILE_MAXIMUM_DISPOSITION) &&
|
|
(ReturnedCreateInformation!=FILE_OPENED) ) {
|
|
if ((NodeType(capFcb) == RDBSS_NTC_STORAGE_TYPE_FILE)) {
|
|
NeedTruncate = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else { // Disconnected
|
|
ULONG ShadowStatus = smbFcb->ShadowStatus;
|
|
BOOLEAN ItsAFile = !BooleanFlagOn(lpFind32->dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY);
|
|
|
|
CreateInformation = FILE_OPENED;
|
|
|
|
switch (CreateDisposition) {
|
|
case FILE_OPEN:
|
|
NOTHING;
|
|
break;
|
|
|
|
case FILE_OPEN_IF:
|
|
if (CreatedShadow) {
|
|
CreateInformation = FILE_CREATED;
|
|
} else if (FlagOn(ShadowStatus,SHADOW_SPARSE)) {
|
|
NeedTruncate = ItsAFile;
|
|
CreateInformation = FILE_CREATED;
|
|
}
|
|
break;
|
|
|
|
case FILE_OVERWRITE:
|
|
case FILE_OVERWRITE_IF:
|
|
if (CreatedShadow) {
|
|
ASSERT(CreateDisposition==FILE_OVERWRITE_IF);
|
|
CreateInformation = FILE_CREATED;
|
|
} else {
|
|
NeedTruncate = ItsAFile;
|
|
CreateInformation = FILE_OVERWRITTEN;
|
|
}
|
|
break;
|
|
|
|
case FILE_CREATE:
|
|
if (!CreatedShadow)
|
|
{
|
|
*Status = STATUS_OBJECT_NAME_COLLISION;
|
|
goto FINALLY;
|
|
|
|
}
|
|
case FILE_SUPERSEDE:
|
|
if (!CreatedShadow) {
|
|
NeedTruncate = ItsAFile;
|
|
};
|
|
CreateInformation = FILE_CREATED;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
// In disconnected state, note down the changes that have occurred
|
|
// in order to notify them to the fsrtl package.
|
|
|
|
if (CreatedShadow)
|
|
{
|
|
dwNotifyFilter = (IsFile(smbFcb->dwFileAttributes)?FILE_NOTIFY_CHANGE_FILE_NAME:FILE_NOTIFY_CHANGE_DIR_NAME);
|
|
dwFileAction = FILE_ACTION_ADDED;
|
|
}
|
|
else if (NeedTruncate)
|
|
{
|
|
dwNotifyFilter = FILE_NOTIFY_CHANGE_SIZE;
|
|
dwFileAction = FILE_ACTION_MODIFIED;
|
|
}
|
|
} // if (!Disconnected)
|
|
}
|
|
|
|
if (NeedTruncate) {
|
|
int iLocalRet;
|
|
ULONG uShadowStatus = smbFcb->ShadowStatus;
|
|
|
|
uShadowStatus &= ~SHADOW_SPARSE;
|
|
lpFind32->nFileSizeLow = lpFind32->nFileSizeHigh = 0;
|
|
|
|
ASSERT(hShadow!=0);
|
|
|
|
HookKdPrint(NAME, ("CreateEpilogue needtruncate %ws %08lx\n",lpFind32->cFileName,uShadowStatus));
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscCreateEpilogue needtruncate...<%ws> %08lx\n",
|
|
lpFind32->cFileName,uShadowStatus));
|
|
|
|
TruncateDataHSHADOW(hParentDir, hShadow);
|
|
iLocalRet = SetShadowInfo(
|
|
hParentDir,
|
|
hShadow,
|
|
lpFind32,
|
|
uShadowStatus,
|
|
SHADOW_FLAGS_ASSIGN |
|
|
((Disconnected) ?SHADOW_FLAGS_DONT_UPDATE_ORGTIME :0)
|
|
);
|
|
|
|
if (iLocalRet < SRET_OK) {
|
|
hShadow = 0;
|
|
} else {
|
|
smbFcb->ShadowStatus = (USHORT)uShadowStatus;
|
|
}
|
|
} // if (NeedTruncate)
|
|
|
|
if (Disconnected) {
|
|
ULONG ShadowStatus = smbFcb->ShadowStatus;
|
|
if (*Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
|
|
CreateDisposition = RxContext->Create.NtCreateParameters.Disposition;
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscCreateEpilogue lastDCON...<%ws> %08lx %08lx %08lx\n",
|
|
lpFind32->cFileName,ShadowStatus,CreateDisposition,lpFind32->dwFileAttributes));
|
|
|
|
switch (CreateDisposition) {
|
|
case FILE_OPEN:
|
|
case FILE_OVERWRITE:
|
|
if ((hShadow==0) ||
|
|
( FlagOn(ShadowStatus,SHADOW_SPARSE) &&
|
|
!FlagOn(lpFind32->dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY))
|
|
) {
|
|
|
|
*Status = STATUS_NO_SUCH_FILE;
|
|
} else {
|
|
// Bypass the shadow if it is not visibile for this connection
|
|
if (!IsShadowVisible(Disconnected,
|
|
lpFind32->dwFileAttributes,
|
|
ShadowStatus)) {
|
|
|
|
*Status = STATUS_NO_SUCH_FILE;
|
|
}
|
|
else
|
|
{
|
|
*Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FILE_OPEN_IF:
|
|
case FILE_OVERWRITE_IF:
|
|
case FILE_CREATE:
|
|
case FILE_SUPERSEDE:
|
|
if (hShadow==0) {
|
|
|
|
*Status = STATUS_NO_SUCH_FILE;
|
|
|
|
} else {
|
|
*Status = STATUS_SUCCESS;
|
|
//CreateInformation == FILE_OPENED|CREATED set in switch above;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
if (*Status == STATUS_SUCCESS) {
|
|
//next, we have to do everything that the create code would have done...
|
|
//specifically, we have to build a fobx and we have to do a initfcb.
|
|
//basically, we have to do the create success tail........
|
|
BOOLEAN MustRegainExclusiveResource = FALSE;
|
|
PSMBPSE_FILEINFO_BUNDLE FileInfo = &smbSrvOpen->FileInfo;
|
|
SMBFCB_HOLDING_STATE FakeSmbFcbHoldingState = SmbFcb_NotHeld;
|
|
RX_FILE_TYPE StorageType;
|
|
|
|
//RtlZeroMemory(FileInfo,sizeof(FileInfo));
|
|
|
|
FileInfo->Basic.FileAttributes = lpFind32->dwFileAttributes;
|
|
COPY_STRUCTFILETIME_TO_LARGEINTEGER(
|
|
FileInfo->Basic.CreationTime,
|
|
lpFind32->ftCreationTime);
|
|
COPY_STRUCTFILETIME_TO_LARGEINTEGER(
|
|
FileInfo->Basic.LastAccessTime,
|
|
lpFind32->ftLastAccessTime);
|
|
COPY_STRUCTFILETIME_TO_LARGEINTEGER(
|
|
FileInfo->Basic.LastWriteTime,
|
|
lpFind32->ftLastWriteTime);
|
|
|
|
FileInfo->Standard.NumberOfLinks = 1;
|
|
FileInfo->Standard.EndOfFile.HighPart = lpFind32->nFileSizeHigh;
|
|
FileInfo->Standard.EndOfFile.LowPart = lpFind32->nFileSizeLow;
|
|
FileInfo->Standard.AllocationSize = FileInfo->Standard.EndOfFile; //rdr1 actually rounds up based of svr disk attribs
|
|
FileInfo->Standard.Directory = BooleanFlagOn(lpFind32->dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY);
|
|
|
|
//CODE.IMRPOVEMENT successtail should figure out the storage type
|
|
StorageType = FlagOn(lpFind32->dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY)
|
|
?(FileTypeDirectory)
|
|
:(FileTypeFile);
|
|
|
|
*Status = MRxSmbCreateFileSuccessTail (
|
|
RxContext,
|
|
&MustRegainExclusiveResource,
|
|
&FakeSmbFcbHoldingState,
|
|
StorageType,
|
|
0xf00d,
|
|
0xbaad,
|
|
SMB_OPLOCK_LEVEL_BATCH,
|
|
CreateInformation,
|
|
FileInfo
|
|
);
|
|
|
|
HookKdPrint(NAME, ("CreateEpilogue %ws attrib=%x \n",lpFind32->cFileName,lpFind32->dwFileAttributes));
|
|
|
|
}
|
|
|
|
if (*Status != STATUS_SUCCESS){
|
|
hShadow = 0;
|
|
}
|
|
} // if (Disconnected)
|
|
|
|
if (hShadow != 0) {
|
|
|
|
PUNICODE_STRING pName = GET_ALREADY_PREFIXED_NAME(SrvOpen,capFcb);
|
|
|
|
// upcase it so change notification will get it right
|
|
// this works because rdbss always does caseinsensitive compare
|
|
UniToUpper(pName->Buffer, pName->Buffer, pName->Length);
|
|
|
|
// here we get a local handle on behalf of this srvopen; we only do this
|
|
// open if it's a file (not a directory) AND if the access rights specified
|
|
// indicate that we might use/modify the data in the shadow.
|
|
|
|
|
|
if (NodeType(capFcb) == RDBSS_NTC_STORAGE_TYPE_FILE) {
|
|
PNT_CREATE_PARAMETERS CreateParameters = &RxContext->Create.NtCreateParameters;
|
|
|
|
// why are we not using macros provided in ntioapi.h for access rights?
|
|
ULONG NeedShadowAccessRights = FILE_READ_DATA
|
|
| FILE_WRITE_DATA
|
|
| FILE_READ_ATTRIBUTES
|
|
| FILE_WRITE_ATTRIBUTES
|
|
| FILE_APPEND_DATA
|
|
| FILE_EXECUTE;
|
|
|
|
if ( (CreateParameters->DesiredAccess & NeedShadowAccessRights) != 0 ) {
|
|
// ASSERT( sizeof(HFILE) == sizeof(HANDLE) );
|
|
ASSERT( hShadow == smbFcb->hShadow );
|
|
ASSERT( hParentDir == smbFcb->hParentDir );
|
|
|
|
#if defined(REMOTE_BOOT)
|
|
//
|
|
// In the remote boot case, there was an extra context
|
|
// parameter to OpenFileHSHADOW which held a pointer
|
|
// to CreateParameters and a local status value.
|
|
//
|
|
#endif
|
|
|
|
#if !defined(BITCOPY)
|
|
OpenFileHSHADOW(
|
|
hShadow,
|
|
0,
|
|
0,
|
|
(CSCHFILE *)(&(smbSrvOpen->hfShadow))
|
|
);
|
|
#else
|
|
OpenFileHSHADOWAndCscBmp(
|
|
smbFcb->hShadow,
|
|
0,
|
|
0,
|
|
(CSCHFILE *)(&(smbSrvOpen->hfShadow)),
|
|
smbFcb->fDoBitCopy,
|
|
0,
|
|
NULL
|
|
);
|
|
// Check if needed to Open a CSC_BITMAP
|
|
if (
|
|
smbFcb->fDoBitCopy == TRUE
|
|
&&
|
|
smbFcb->NewShadowSize.HighPart == 0 // no 64-bit
|
|
&&
|
|
Disconnected // only in dcon mode
|
|
&&
|
|
!FlagOn(smbFcb->ShadowStatus,SHADOW_SPARSE)
|
|
&&
|
|
smbFcb->lpDirtyBitmap == NULL // shadow file not sparse
|
|
&&
|
|
// have not been created before
|
|
(FlagOn(
|
|
CreateParameters->DesiredAccess,
|
|
FILE_WRITE_DATA|FILE_APPEND_DATA))
|
|
// opened for writes
|
|
// && is NTFS -- see below
|
|
) {
|
|
BOOL fHasStreams;
|
|
|
|
if (HasStreamSupport(smbSrvOpen->hfShadow, &fHasStreams) &&
|
|
(fHasStreams == TRUE))
|
|
{
|
|
OpenCscBmp(hShadow, &((LPCSC_BITMAP)(smbFcb->lpDirtyBitmap)));
|
|
}
|
|
}
|
|
#endif // defined(BITCOPY)
|
|
|
|
|
|
|
|
#if defined(REMOTE_BOOT)
|
|
//
|
|
// Here we checked the local status value and set
|
|
// *Status from it if (iRet != SRET_OK).
|
|
//
|
|
#endif
|
|
|
|
if (smbSrvOpen->hfShadow != 0) {
|
|
HookKdPrint(NAME, ("Opened file %ws, hShadow=%x handle=%x \n", lpFind32->cFileName, hShadow, smbSrvOpen->hfShadow));
|
|
RxLog(("CSC Opened file %ws, hShadow=%x capFcb=%x SrvOpen=%x\n", lpFind32->cFileName, hShadow, capFcb, SrvOpen));
|
|
|
|
// NeedToReportFileOpen = TRUE;
|
|
SetPriorityHSHADOW(hParentDir, hShadow, MAX_PRI, RETAIN_VALUE);
|
|
|
|
if (Disconnected)
|
|
{
|
|
MRxSmbCSCObtainRightsForUserOnFile(RxContext,
|
|
hParentDir,
|
|
hShadow,
|
|
&smbSrvOpen->MaximalAccessRights,
|
|
&smbSrvOpen->GuestMaximalAccessRights);
|
|
}
|
|
}
|
|
|
|
}
|
|
} // if (NodeType(capFcb) == RDBSS_NTC_STORAGE_TYPE_FILE)
|
|
|
|
IF_DEBUG {
|
|
if (FALSE) {
|
|
BOOL thisone, nextone;
|
|
PFDB smbLookedUpFcb,smbLookedUpFcbNext;
|
|
|
|
smbLookedUpFcb = PFindFdbFromHShadow(hShadow);
|
|
smbLookedUpFcbNext = PFindFdbFromHShadow(hShadow+1);
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscCreateEpilogue lookups -> %08lx %08lx %08lx\n",
|
|
smbFcb,
|
|
MRxSmbCscRecoverMrxFcbFromFdb(smbLookedUpFcb),
|
|
MRxSmbCscRecoverMrxFcbFromFdb(smbLookedUpFcbNext) ));
|
|
}
|
|
}
|
|
} // if (hShadow != 0)
|
|
|
|
FINALLY:
|
|
|
|
if (lpFind32)
|
|
{
|
|
RxFreePool(lpFind32);
|
|
}
|
|
if (EnteredCriticalSection) {
|
|
LeaveShadowCritRx(RxContext);
|
|
}
|
|
|
|
#if 0
|
|
if (NeedToReportFileOpen) {
|
|
//this is a bit strange....it's done this way because MRxSmbCscReportFileOpens
|
|
//enters the critsec for himself.....it is also called from within MRxSmbCollapseOpen
|
|
//if instead MRxSmbCollapseOpen called a wrapper routine, we could have
|
|
//MRxSmbCscReportFileOpens not enter and we could do this above
|
|
MRxSmbCscReportFileOpens(); //CODE.IMPROVEMENT this guy shouldn't enter
|
|
}
|
|
#endif
|
|
|
|
if (Disconnected)
|
|
{
|
|
if(*Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
// if we ever get here after CSC is shutdown, we need to return appropriate error
|
|
if (fShadow)
|
|
{
|
|
RxLog(("EarlyOut = %d \r\n", dwEarlyOuts));
|
|
// should never get here
|
|
DbgPrint("MRxSmbCscCreateEpilogue: EarlyOut = %d \r\n", dwEarlyOuts);
|
|
ASSERT(FALSE);
|
|
}
|
|
else
|
|
{
|
|
*Status = STATUS_NETWORK_UNREACHABLE;
|
|
}
|
|
}
|
|
else if (*Status == STATUS_SUCCESS)
|
|
{
|
|
// report changes to the notification package
|
|
if (dwNotifyFilter)
|
|
{
|
|
ASSERT(dwFileAction);
|
|
|
|
RxLog(("chngnot hShadow=%x filter=%x\n",smbFcb->hShadow, dwNotifyFilter));
|
|
|
|
FsRtlNotifyFullReportChange(
|
|
pNetRootEntry->NetRoot.pNotifySync,
|
|
&pNetRootEntry->NetRoot.DirNotifyList,
|
|
(PSTRING)GET_ALREADY_PREFIXED_NAME(SrvOpen,capFcb),
|
|
(USHORT)(GET_ALREADY_PREFIXED_NAME(SrvOpen, capFcb)->Length -
|
|
smbFcb->MinimalCscSmbFcb.LastComponentLength),
|
|
NULL,
|
|
NULL,
|
|
dwNotifyFilter,
|
|
dwFileAction,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
HookKdPrint(NAME, ("CreateEpilogue Out: returnedCreateInfo=%x Status=%x\n",
|
|
ReturnedCreateInformation, *Status));
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbCscCreateEpilogue ->%08lx %08lx\n",RxContext, *Status ));
|
|
RxLog(("CscCrEpi %x %x\n",RxContext, *Status ));
|
|
return;
|
|
|
|
EarlyOut:
|
|
if (*SmbFcbHoldingState != SmbFcb_NotHeld) {
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscCreateEpilogue...early release %08lx\n",
|
|
RxContext));
|
|
MRxSmbCscReleaseSmbFcb(RxContext,SmbFcbHoldingState);
|
|
ASSERT(*SmbFcbHoldingState == SmbFcb_NotHeld);
|
|
}
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
MRxSmbCscDeleteAfterCloseEpilogue (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PNTSTATUS Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the tail of a delete-after-close operation for CSC.
|
|
Basically, it deletes the file from the cache.
|
|
|
|
The status of the operation is passed in case we someday find
|
|
things are so messed up that we want to return a failure even if the
|
|
nonshadowed operations was successful. not today however...
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS LocalStatus=STATUS_UNSUCCESSFUL;
|
|
int iRet = -1;
|
|
ULONG ShadowFileLength;
|
|
|
|
RxCaptureFcb;RxCaptureFobx;
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry =
|
|
SmbCeGetAssociatedNetRootEntry(NetRoot);
|
|
|
|
BOOLEAN EnteredCriticalSection = FALSE;
|
|
_WIN32_FIND_DATA Find32; //this should not be on the stack CODE.IMPROVEMENT
|
|
BOOLEAN Disconnected;
|
|
ULONG uShadowStatus;
|
|
OTHERINFO sOI;
|
|
|
|
if(!MRxSmbIsCscEnabled) {
|
|
return;
|
|
}
|
|
if (fShadow == 0) {
|
|
return;
|
|
}
|
|
|
|
Disconnected = MRxSmbCSCIsDisconnectedOpen(capFcb, smbSrvOpen);
|
|
|
|
RxDbgTrace(+1, Dbg,
|
|
("MRxSmbCscDeleteAfterCloseEpilogue entry %08lx...%08lx on handle %08lx\n",
|
|
RxContext, SrvOpen, smbSrvOpen->hfShadow ));
|
|
|
|
if (*Status != STATUS_SUCCESS) {
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscDeleteAfterCloseEpilogue exit(status) w/o deleteing -> %08lx %08lx\n", RxContext, Status ));
|
|
goto FINALLY;
|
|
}
|
|
|
|
EnterShadowCritRx(RxContext);
|
|
EnteredCriticalSection = TRUE;
|
|
|
|
//if we don't have a shadow...go look for one
|
|
if (smbFcb->hShadow == 0) {
|
|
if (!smbFcb->hShadowRenamed)
|
|
{
|
|
LocalStatus = MRxSmbCscObtainShadowHandles(
|
|
RxContext,
|
|
Status,
|
|
&Find32,
|
|
NULL,
|
|
CREATESHADOW_CONTROL_NOCREATE,
|
|
Disconnected);
|
|
|
|
if (LocalStatus != STATUS_SUCCESS) {
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscDeleteAfterCloseEpilogue couldn't get handles-> %08lx %08lx\n",RxContext,LocalStatus ));
|
|
goto FINALLY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
smbFcb->hShadow = smbFcb->hShadowRenamed;
|
|
smbFcb->hParentDir = smbFcb->hParentDirRenamed;
|
|
}
|
|
|
|
if (smbFcb->hShadow == 0) {
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscDeleteAfterCloseEpilogue no handles-> %08lx %08lx\n",RxContext,LocalStatus ));
|
|
goto FINALLY;
|
|
}
|
|
}
|
|
|
|
if(GetShadowInfo(smbFcb->hParentDir,
|
|
smbFcb->hShadow,
|
|
&Find32,
|
|
&uShadowStatus, &sOI) < SRET_OK) {
|
|
goto FINALLY;
|
|
}
|
|
|
|
if (IsShadowVisible(
|
|
Disconnected,
|
|
Find32.dwFileAttributes,
|
|
uShadowStatus)) {
|
|
BOOLEAN fMarkDeleted = (Disconnected && !mShadowLocallyCreated(uShadowStatus));
|
|
|
|
LocalStatus = OkToDeleteObject(smbFcb->hParentDir, smbFcb->hShadow, &Find32, uShadowStatus, Disconnected);
|
|
|
|
// If it is not OK to delete, the quit
|
|
if (LocalStatus != STATUS_SUCCESS)
|
|
{
|
|
iRet = -1;
|
|
goto FINALLY;
|
|
}
|
|
|
|
if (!fMarkDeleted)
|
|
{
|
|
if (capFcb->OpenCount != 0)
|
|
{
|
|
DbgPrint("Marking for delete hDir=%x hShadow=%x %ws \n\n", smbFcb->hParentDir, smbFcb->hShadow, Find32.cFileName);
|
|
ASSERT(FALSE);
|
|
RxLog(("Marking for delete hDir=%x hShadow=%x %ws \n\n", smbFcb->hParentDir, smbFcb->hShadow, Find32.cFileName));
|
|
|
|
// if we are supposed to really delete this file, note this fact on the FCB
|
|
// we will delete it when the FCB is deallocated
|
|
smbFcb->LocalFlags |= FLAG_FDB_DELETE_ON_CLOSE;
|
|
iRet = 0;
|
|
}
|
|
else
|
|
{
|
|
iRet = DeleteShadowHelper(FALSE, smbFcb->hParentDir, smbFcb->hShadow);
|
|
smbFcb->hShadow = 0;
|
|
|
|
if (iRet < 0)
|
|
{
|
|
goto FINALLY;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
iRet = DeleteShadowHelper(TRUE, smbFcb->hParentDir, smbFcb->hShadow);
|
|
smbFcb->hShadow = 0;
|
|
}
|
|
|
|
if (iRet >= 0)
|
|
{
|
|
PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry = SmbCeGetAssociatedNetRootEntry(NetRoot);
|
|
|
|
InsertTunnelInfo(smbFcb->hParentDir,
|
|
Find32.cFileName,
|
|
Find32.cAlternateFileName,
|
|
&sOI);
|
|
if (fMarkDeleted) {
|
|
MarkShareDirty(&smbFcb->sCscRootInfo.ShareStatus, smbFcb->sCscRootInfo.hShare);
|
|
}
|
|
// when disconnected, report the change
|
|
if (Disconnected)
|
|
{
|
|
FsRtlNotifyFullReportChange(
|
|
pNetRootEntry->NetRoot.pNotifySync,
|
|
&pNetRootEntry->NetRoot.DirNotifyList,
|
|
(PSTRING)GET_ALREADY_PREFIXED_NAME(NULL,capFcb),
|
|
(USHORT)(GET_ALREADY_PREFIXED_NAME(NULL, capFcb)->Length -
|
|
smbFcb->MinimalCscSmbFcb.LastComponentLength),
|
|
NULL,
|
|
NULL,
|
|
IsFile(Find32.dwFileAttributes)?FILE_NOTIFY_CHANGE_FILE_NAME
|
|
:FILE_NOTIFY_CHANGE_DIR_NAME,
|
|
FILE_ACTION_REMOVED,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
FINALLY:
|
|
if (EnteredCriticalSection) {
|
|
LeaveShadowCritRx(RxContext);
|
|
}
|
|
|
|
if (Disconnected) {
|
|
if (iRet < 0) {
|
|
*Status = LocalStatus; //do we need a better error mapping?
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbCscDeleteAfterCloseEpilogue exit-> %08lx %08lx\n", RxContext, Status ));
|
|
return;
|
|
}
|
|
|
|
ULONG
|
|
GetPathLevelFromUnicodeString (
|
|
PUNICODE_STRING Name
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine counts the number of L'\\' in a string. It is used to set
|
|
the priority level for a directory on a rename operation.
|
|
|
|
Arguments:
|
|
|
|
Name -
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PWCHAR t = Name->Buffer;
|
|
LONG l = Name->Length;
|
|
ULONG Count = 1;
|
|
|
|
for (;l<2;l--) {
|
|
if (*t++ == L'\\') {
|
|
Count++;
|
|
}
|
|
}
|
|
return(Count);
|
|
}
|
|
|
|
VOID
|
|
MRxSmbCscRenameEpilogue (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PNTSTATUS Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the tail of a rename operation for CSC.
|
|
Basically, it renames the file in the record manager datastructures.
|
|
Unfortunately, there is no "basically" to it.
|
|
|
|
The status of the operation is passed in case we someday find
|
|
things are so messed up that we want to return a failure even if the
|
|
nonshadowed operations was successful. not today however...
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
The routine does the following operations
|
|
- Get the Inode for the source ie. If \foo.dir\bar.txt is the source, we traverse the path
|
|
to get the inode for bar.txt.
|
|
- If necessary create the destination namespace in the CSC database i.e., if \foo.dir\bar.txt
|
|
is being renamed to \xxx.dir\yyy.dir\abc.txt, then ensure that the hierarchy \xxx.dir\yyy.dir
|
|
exists in the local namespace.
|
|
|
|
- If the destination inode exists, then apply the visibility and replace_if_exists logic to it.
|
|
If the operation is still valid, then get all the info about the destination inode and
|
|
delete it. Apply the info on the destination inode to the source.
|
|
- Do a rename
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS LocalStatus = STATUS_UNSUCCESSFUL;
|
|
ULONG ShadowFileLength;
|
|
int iRet = -1;
|
|
|
|
RxCaptureFcb;RxCaptureFobx;
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry =
|
|
SmbCeGetAssociatedNetRootEntry(NetRoot);
|
|
|
|
UNICODE_STRING RenameName={0,0,NULL};
|
|
UNICODE_STRING LastComponentName = {0,0,NULL};
|
|
PFILE_RENAME_INFORMATION RenameInformation = RxContext->Info.Buffer;
|
|
MRX_SMB_FCB CscSmbFcb;
|
|
|
|
BOOLEAN EnteredCriticalSection = FALSE;
|
|
_WIN32_FIND_DATA Find32; //this should not be on the stack CODE.IMPROVEMENT
|
|
ULONG ulInheritedHintFlags=0;
|
|
|
|
BOOL fDoTunneling = FALSE;
|
|
HSHADOW hDir, hDirTo, hShadow=0, hShadowTo=0;
|
|
ULONG uShadowStatusFrom, uShadowStatusTo, uRenameFlags, attrSav;
|
|
OTHERINFO sOI, sOIFrom;
|
|
LPOTHERINFO lpOI=NULL;
|
|
BOOLEAN Disconnected, fAlreadyStripped = FALSE;
|
|
RETRIEVE_TUNNEL_INFO_RETURNS TunnelType;
|
|
_FILETIME ftLWTime;
|
|
BOOL fFile;
|
|
USHORT *lpcFileNameTuna = NULL, *lpcAlternateFileNameTuna = NULL;
|
|
|
|
if(!MRxSmbIsCscEnabled ||
|
|
(fShadow == 0)) {
|
|
RxLog(("RenCSC disabled \n"));
|
|
return;
|
|
}
|
|
|
|
Disconnected = MRxSmbCSCIsDisconnectedOpen(capFcb, smbSrvOpen);
|
|
|
|
// an open for rename should never be a local open
|
|
ASSERT(!BooleanFlagOn(
|
|
smbSrvOpen->Flags,
|
|
SMB_SRVOPEN_FLAG_LOCAL_OPEN));
|
|
|
|
if (!Disconnected &&
|
|
!pNetRootEntry->NetRoot.CscEnabled) {
|
|
RxLog(("NR %x Not cscenabled %x\n", pNetRootEntry));
|
|
return;
|
|
}
|
|
|
|
RxDbgTrace(+1, Dbg,
|
|
("MRxSmbCscRenameEpilogue entry %08lx...%08lx on handle %08lx\n",
|
|
RxContext, SrvOpen, smbSrvOpen->hfShadow ));
|
|
|
|
if (*Status != STATUS_SUCCESS) {
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue exit(status) w/o deleteing -> %08lx %08lx\n", RxContext, Status ));
|
|
goto FINALLY;
|
|
}
|
|
|
|
uRenameFlags = 0;
|
|
|
|
EnterShadowCritRx(RxContext);
|
|
EnteredCriticalSection = TRUE;
|
|
|
|
//if we don't have a shadow...go look for one
|
|
if (smbFcb->hShadow == 0) {
|
|
LocalStatus = MRxSmbCscObtainShadowHandles(
|
|
RxContext,
|
|
Status,
|
|
&Find32,
|
|
NULL,
|
|
CREATESHADOW_CONTROL_NOCREATE|
|
|
((capFobx->Flags & FOBX_FLAG_DFS_OPEN)?CREATESHADOW_CONTROL_STRIP_SHARE_NAME:0),
|
|
Disconnected);
|
|
|
|
if (LocalStatus != STATUS_SUCCESS) {
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscRenameEpilogue couldn't get handles-> %08lx %08lx\n",RxContext,LocalStatus ));
|
|
goto FINALLY;
|
|
}
|
|
|
|
if (smbFcb->hShadow == 0) {
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscRenameEpilogue no handles-> %08lx %08lx\n",RxContext,LocalStatus ));
|
|
goto FINALLY;
|
|
}
|
|
}
|
|
|
|
if (GetShadowInfo(
|
|
smbFcb->hParentDir,
|
|
smbFcb->hShadow,
|
|
&Find32,
|
|
&uShadowStatusFrom, &sOIFrom) < SRET_OK) {
|
|
LocalStatus = STATUS_NO_SUCH_FILE;
|
|
goto FINALLY;
|
|
}
|
|
|
|
hShadow = smbFcb->hShadow;
|
|
hDir = smbFcb->hParentDir;
|
|
if (!hShadow ||
|
|
!IsShadowVisible(
|
|
Disconnected,
|
|
Find32.dwFileAttributes,
|
|
uShadowStatusFrom)) {
|
|
RxDbgTrace(0, Dbg,
|
|
("MRxSmbCscRenameEpilogue no shadoworinvisible-> %08lx %08lx\n",RxContext,LocalStatus ));
|
|
LocalStatus = STATUS_NO_SUCH_FILE;
|
|
goto FINALLY;
|
|
}
|
|
|
|
// DbgPrint("Renaming %ws ", Find32.cFileName);
|
|
RxLog(("Renaming hDir=%x hShadow=%x %ws ", hDir, hShadow, Find32.cFileName));
|
|
|
|
fFile = IsFile(Find32.dwFileAttributes);
|
|
|
|
// NB begin temp use of uRenameFlags
|
|
uRenameFlags = (wstrlen(Find32.cFileName)+1)*sizeof(USHORT);
|
|
|
|
lpcFileNameTuna = AllocMem(uRenameFlags);
|
|
|
|
lpcAlternateFileNameTuna = AllocMem(sizeof(Find32.cAlternateFileName));
|
|
|
|
if (!lpcFileNameTuna || ! lpcAlternateFileNameTuna) {
|
|
LocalStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FINALLY; //bailout;
|
|
}
|
|
|
|
// save the alternate filename for tunnelling purposes
|
|
memcpy(lpcAlternateFileNameTuna,
|
|
Find32.cAlternateFileName,
|
|
sizeof(Find32.cAlternateFileName));
|
|
|
|
memcpy(lpcFileNameTuna, Find32.cFileName, uRenameFlags);
|
|
|
|
// end temp use of uRenameFlags
|
|
uRenameFlags = 0;
|
|
|
|
|
|
// Save the last write time
|
|
ftLWTime = Find32.ftLastWriteTime;
|
|
|
|
if (Disconnected) {
|
|
|
|
// if this is a file and we are trying to delete it
|
|
// without permissions, then bail
|
|
|
|
if (fFile &&
|
|
!(FILE_WRITE_DATA & smbSrvOpen->MaximalAccessRights)&&
|
|
!(FILE_WRITE_DATA & smbSrvOpen->GuestMaximalAccessRights))
|
|
{
|
|
LocalStatus = STATUS_ACCESS_DENIED;
|
|
RxLog(("No rights to rename %x in dcon Status=%x\n", smbFcb->hShadow, LocalStatus));
|
|
HookKdPrint(BADERRORS, ("No rights to rename %x in dcon Status=%x\n", smbFcb->hShadow, LocalStatus));
|
|
goto FINALLY; //bailout;
|
|
}
|
|
|
|
// In disconnected state we don't allow directory renames on
|
|
// remotely obtained directories
|
|
// This is consistent with not allowing delete on these directories
|
|
|
|
if (!fFile && !mShadowLocallyCreated(uShadowStatusFrom)) {
|
|
LocalStatus = STATUS_ONLY_IF_CONNECTED;
|
|
goto FINALLY; //bailout;
|
|
}
|
|
|
|
if (!mShadowLocallyCreated(uShadowStatusFrom)) { // remote object
|
|
|
|
// Ask RenameShadow to mark the source as deleted, it will have to
|
|
// be reintegrated later
|
|
uRenameFlags |= RNMFLGS_MARK_SOURCE_DELETED;
|
|
|
|
// if this is the copy of the original,
|
|
// ie. it has not gone through delete/create cycles,
|
|
// we need to save it's value in the new shadow structure
|
|
// so that while reintegrating, we can do rename operations
|
|
// before merging.
|
|
// The SHADOW_REUSE flag is set in CreateShadowFromPath, and
|
|
// in his routine while dealing with hShadowTo
|
|
// when the shadow of a remote object that has been marked deleted
|
|
// is reused during disconnected operation.
|
|
// When a reused object is renamed, the new object is NOT a
|
|
// true alias of the original object on the Share
|
|
|
|
if (!mShadowReused(uShadowStatusFrom)) {
|
|
// not been reused, give his rename alias, if any, to the new one
|
|
uRenameFlags |= RNMFLGS_SAVE_ALIAS;
|
|
}
|
|
}
|
|
else
|
|
{ // locally created
|
|
|
|
// This is a locally created shadow that is being renamed
|
|
// we wan't to retain it's alias, if any, so we know if it
|
|
// was renamed from a remote object or was created locally
|
|
uRenameFlags |= RNMFLGS_RETAIN_ALIAS;
|
|
}
|
|
}
|
|
|
|
// Let us create the directory hierarchy in which the file/directory
|
|
// is to be renamed
|
|
// ASSERT((RenameInformation->FileName[0] == L'\\') || (RenameInformation->FileName[0] == L':'))
|
|
RenameName.Buffer = &RenameInformation->FileName[0];
|
|
RenameName.Length = (USHORT)RenameInformation->FileNameLength;
|
|
if (smbFcb->uniDfsPrefix.Buffer)
|
|
{
|
|
UNICODE_STRING DfsName;
|
|
|
|
if (capFobx->Flags & FOBX_FLAG_DFS_OPEN)
|
|
{
|
|
LocalStatus = CscDfsStripLeadingServerShare(&RenameName);
|
|
|
|
if (LocalStatus != STATUS_SUCCESS)
|
|
{
|
|
RenameName.Buffer = NULL;
|
|
goto FINALLY;
|
|
}
|
|
|
|
fAlreadyStripped = TRUE;
|
|
}
|
|
|
|
if(CscDfsDoDfsNameMapping(&smbFcb->uniDfsPrefix,
|
|
&smbFcb->uniActualPrefix,
|
|
&RenameName,
|
|
TRUE, //fResolvedNameToDFSName
|
|
&DfsName
|
|
) != STATUS_SUCCESS)
|
|
{
|
|
LocalStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
RenameName.Buffer = NULL;
|
|
goto FINALLY;
|
|
}
|
|
|
|
RenameName = DfsName;
|
|
}
|
|
|
|
|
|
RtlZeroMemory(&CscSmbFcb, sizeof(CscSmbFcb));
|
|
|
|
CscSmbFcb.MinimalCscSmbFcb.sCscRootInfo = smbFcb->sCscRootInfo;
|
|
|
|
MRxSmbCscCreateShadowFromPath(
|
|
&RenameName,
|
|
&CscSmbFcb.MinimalCscSmbFcb.sCscRootInfo,
|
|
&Find32,
|
|
NULL,
|
|
(CREATESHADOW_CONTROL_NOREVERSELOOKUP |
|
|
CREATESHADOW_CONTROL_NOCREATELEAF|
|
|
((!fAlreadyStripped && (capFobx->Flags & FOBX_FLAG_DFS_OPEN))?CREATESHADOW_CONTROL_STRIP_SHARE_NAME:0)
|
|
),
|
|
&CscSmbFcb.MinimalCscSmbFcb,
|
|
RxContext,
|
|
Disconnected,
|
|
&ulInheritedHintFlags
|
|
);
|
|
|
|
hDirTo = CscSmbFcb.MinimalCscSmbFcb.hParentDir;
|
|
hShadowTo = CscSmbFcb.MinimalCscSmbFcb.hShadow;
|
|
uShadowStatusTo = CscSmbFcb.MinimalCscSmbFcb.ShadowStatus;
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue...000=%08lx %08lx %08lx\n",
|
|
hDirTo,hShadowTo,uShadowStatusTo));
|
|
|
|
if (!hDirTo) {
|
|
HookKdPrint(BADERRORS, ("Cannot rename root %x in dcon \n", hShadowTo));
|
|
LocalStatus = STATUS_ACCESS_DENIED;
|
|
goto FINALLY; //bailout;
|
|
}
|
|
|
|
//
|
|
// allocate a buffer that's the right size: one extra char is
|
|
// for a trailing null....this buffer is used to get the tunnelling
|
|
// information
|
|
|
|
LastComponentName.Length = CscSmbFcb.MinimalCscSmbFcb.LastComponentLength;
|
|
LastComponentName.Buffer = (PWCHAR)RxAllocatePoolWithTag(
|
|
PagedPool,
|
|
LastComponentName.Length + (1 * sizeof(WCHAR)),
|
|
RX_MISC_POOLTAG);
|
|
|
|
if (LastComponentName.Buffer==NULL) {
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue -> noalloc\n"));
|
|
} else {
|
|
PWCHAR t;
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue...lastcomponentinhex=%08lx\n",LastComponentName.Buffer));
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue...renamebuffer=%08lx\n",RenameName.Buffer));
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue...lcofff=%08lx,%08lx\n",
|
|
CscSmbFcb.MinimalCscSmbFcb.LastComponentOffset,CscSmbFcb.MinimalCscSmbFcb.LastComponentLength));
|
|
|
|
RtlCopyMemory(
|
|
LastComponentName.Buffer,
|
|
RenameName.Buffer + CscSmbFcb.MinimalCscSmbFcb.LastComponentOffset,
|
|
LastComponentName.Length);
|
|
|
|
t = (PWCHAR)( ((PBYTE)(LastComponentName.Buffer)) + LastComponentName.Length );
|
|
|
|
*t = 0;
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue...lastcomponent=%ws (%08lx)\n",
|
|
LastComponentName.Buffer,LastComponentName.Buffer));
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue...hdirto 1111=(%08lx)\n", hDirTo));
|
|
|
|
// Do we have a destination shadow for rename?
|
|
if (hShadowTo) {
|
|
|
|
BOOLEAN fDestShadowVisible = FALSE;
|
|
|
|
if (IsShadowVisible(
|
|
Disconnected,
|
|
Find32.dwFileAttributes,
|
|
uShadowStatusTo))
|
|
{
|
|
fDestShadowVisible = TRUE;
|
|
}
|
|
|
|
// If it is visible and this is not a replace_if_exists operation
|
|
// then this is an illegal rename
|
|
|
|
if (fDestShadowVisible && !RxContext->Info.ReplaceIfExists)
|
|
{
|
|
LocalStatus = STATUS_OBJECT_NAME_COLLISION;
|
|
goto FINALLY; //bailout;
|
|
}
|
|
|
|
if (!Disconnected) {// connected
|
|
|
|
if (!RxContext->Info.ReplaceIfExists)
|
|
{
|
|
// When we are connected, we are doing rename in caching mode
|
|
// If the renameTo exists and needs reintegration,
|
|
// we protect it by nuking the renameFrom and succeeding
|
|
// the rename
|
|
|
|
KdPrint(("SfnRename:unary op %x, \r\n", hShadow));
|
|
|
|
ASSERT(mShadowOutofSync(uShadowStatusTo));
|
|
|
|
ASSERT(!fFile || !mShadowOutofSync(uShadowStatusFrom));
|
|
ASSERT(!mShadowBusy(uShadowStatusFrom));
|
|
|
|
if(DeleteShadow(hDir, hShadow) < SRET_OK) {
|
|
LocalStatus = STATUS_UNSUCCESSFUL;
|
|
} else {
|
|
iRet = 0;
|
|
smbFcb->hShadow = 0;
|
|
}
|
|
|
|
goto FINALLY; //bailout;
|
|
}
|
|
}
|
|
|
|
// Nuke the renameTo shadow
|
|
if(DeleteShadow(hDirTo, hShadowTo) < SRET_OK) {
|
|
LocalStatus = STATUS_UNSUCCESSFUL;
|
|
goto FINALLY; //bailout;
|
|
} else {
|
|
//hunt up the fcb, if any and zero the hshadow
|
|
PFDB smbLookedUpFdbTo;
|
|
PMRX_SMB_FCB smbFcbTo;
|
|
|
|
smbLookedUpFdbTo = PFindFdbFromHShadow(hShadowTo);
|
|
if (smbLookedUpFdbTo!=NULL) {
|
|
smbFcbTo = MRxSmbCscRecoverMrxFcbFromFdb(smbLookedUpFdbTo);
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue lookups -> %08lx %08lx %08lx\n",
|
|
smbFcbTo,
|
|
smbLookedUpFdbTo,
|
|
hShadowTo ));
|
|
ASSERT((smbFcbTo->hShadow == hShadowTo)||(smbFcbTo->hShadowRenamed == hShadowTo));
|
|
smbFcbTo->hShadow = 0;
|
|
}
|
|
}
|
|
|
|
if (Disconnected && !mShadowLocallyCreated(uShadowStatusTo)) {
|
|
// When the renameTo is a remote object
|
|
// it is possible for two remote objects to get crosslinked, ie. be each others
|
|
// aliases. The reintegrator needs to deal with this case.
|
|
|
|
// Cleanup the locally created status of the hShadowFrom if it
|
|
// did exist because it is being renamed to a remote object
|
|
mClearBits(uShadowStatusFrom, SHADOW_LOCALLY_CREATED);
|
|
|
|
// Mark the original object as having been reused so a rename
|
|
// on it will not point back to this object.
|
|
// Also set all the CHANGE attributes to indicate that this is a
|
|
// spanking new object. Actually doesn't SHADOW_REUSED flag do that
|
|
// already?
|
|
mSetBits(uShadowStatusFrom, SHADOW_REUSED|SHADOW_DIRTY|SHADOW_ATTRIB_CHANGE);
|
|
}
|
|
}
|
|
|
|
if (!hShadowTo)
|
|
{
|
|
if (Disconnected) {
|
|
// we don't have a renameTo. If we are in disconnected state, let us
|
|
// mark it as locally created, which it is.
|
|
mSetBits(uShadowStatusFrom, SHADOW_LOCALLY_CREATED);
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue...hdirto 3333= (%08lx)\n", hDirTo));
|
|
ASSERT(hDir && hShadow && hDirTo);
|
|
|
|
if (!Disconnected) {// connected
|
|
//get the names and attributes from the Share
|
|
MRxSmbGetFileInfoFromServer(RxContext,&RenameName,&Find32,NULL,NULL);
|
|
} else {// disconnected
|
|
if (hShadowTo) {
|
|
// Find32 contains the orgtime of hShadowTo
|
|
// we tell the RenameShadow routine to reuse it.
|
|
|
|
Find32.ftLastWriteTime = ftLWTime;
|
|
uRenameFlags |= RNMFLGS_USE_FIND32_TIMESTAMPS;
|
|
} else {
|
|
Find32.cFileName[0] = 0;
|
|
Find32.cAlternateFileName[0] = 0;
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue...hdirto 4444= (%08lx)\n", hDirTo));
|
|
|
|
if (!fFile) {
|
|
// We set the reference priority of the directories to their level
|
|
// in the hierarchiy starting 1 for the root. That way we
|
|
// walk the PQ in reverse priority order for directories. to create
|
|
// all the direcotry objects hierarchically. But with hints
|
|
// this is going to be a problem.
|
|
Find32.dwReserved0 = GetPathLevelFromUnicodeString(&RenameName);
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue...hdirto 5555= (%08lx)\n", hDirTo));
|
|
// If there is any tunnelling info then use it to create this guy
|
|
if (LastComponentName.Buffer &&
|
|
(TunnelType = RetrieveTunnelInfo(
|
|
hDirTo, // directory where the renames are happenieng
|
|
LastComponentName.Buffer, // potential SFN
|
|
(Disconnected)? &Find32:NULL, // get LFN only when disconnected
|
|
&sOI))) {
|
|
lpOI = &sOI;
|
|
}
|
|
|
|
if (Disconnected) {
|
|
//muck with the names.......
|
|
switch (TunnelType) {
|
|
case TUNNEL_RET_SHORTNAME_TUNNEL:
|
|
//we tunneled on the shortname.....retrievetunnelinfo did the copies....
|
|
break;
|
|
|
|
case TUNNEL_RET_NOTFOUND:
|
|
//no tunnel. use the name passed as the long name
|
|
RtlCopyMemory( &Find32.cFileName[0],
|
|
LastComponentName.Buffer,
|
|
LastComponentName.Length + sizeof(WCHAR) );
|
|
//lack of break intentional;
|
|
|
|
case TUNNEL_RET_LONGNAME_TUNNEL:
|
|
//if we tunneled on the longname....retrievetunnelinfo did the copies....
|
|
//but we may still need a shortname
|
|
MRxSmbCscGenerate83NameAsNeeded(hDirTo,
|
|
&Find32.cFileName[0],
|
|
&Find32.cAlternateFileName[0]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue...hdirto 6666=(%08lx)\n", hDirTo));
|
|
|
|
|
|
if (RenameShadow(
|
|
hDir,
|
|
hShadow,
|
|
hDirTo,
|
|
&Find32,
|
|
uShadowStatusFrom,
|
|
lpOI,
|
|
uRenameFlags,
|
|
&hShadowTo) < SRET_OK)
|
|
{
|
|
LocalStatus = STATUS_UNSUCCESSFUL;
|
|
goto FINALLY; //bailout;
|
|
}
|
|
|
|
smbFcb->hShadow = 0;
|
|
|
|
ASSERT(hShadowTo);
|
|
|
|
smbFcb->hShadowRenamed = hShadowTo;
|
|
smbFcb->hParentDirRenamed = hDirTo;
|
|
|
|
fDoTunneling = TRUE;
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscRenameEpilogue...hdirto 7777= (%08lx)\n", hDirTo));
|
|
if (Disconnected) {
|
|
// Mark it on the Share, so reintegration can proceed
|
|
MarkShareDirty(&smbFcb->sCscRootInfo.ShareStatus,
|
|
(ULONG)(smbFcb->sCscRootInfo.hShare));
|
|
}
|
|
|
|
if (mPinInheritFlags(ulInheritedHintFlags))
|
|
{
|
|
RxDbgTrace(0, Dbg, ("RenameEpilogue: Setting inherited hintflags on hShadow=%x \n", hShadowTo));
|
|
|
|
if(GetShadowInfo(hDirTo, hShadowTo, NULL, NULL, &sOI) >= SRET_OK)
|
|
{
|
|
if (ulInheritedHintFlags & FLAG_CSC_HINT_PIN_INHERIT_USER) {
|
|
sOI.ulHintFlags |= FLAG_CSC_HINT_PIN_USER;
|
|
}
|
|
|
|
if (ulInheritedHintFlags & FLAG_CSC_HINT_PIN_INHERIT_SYSTEM) {
|
|
sOI.ulHintFlags |= FLAG_CSC_HINT_PIN_SYSTEM;
|
|
}
|
|
|
|
SetShadowInfoEx(hDirTo, hShadowTo, NULL, 0, SHADOW_FLAGS_OR, &sOI, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
iRet = 0;
|
|
|
|
// This effectively bumps up the version number of the current CSC namespace
|
|
// if this share is a remoteboot share and is being merged, then the reintegration code will
|
|
// backoff
|
|
|
|
IncrementActivityCountForShare(smbFcb->sCscRootInfo.hShare);
|
|
|
|
FINALLY:
|
|
if (fDoTunneling) {
|
|
// We succeeded the rename, let use keep the tunneling info for the source
|
|
InsertTunnelInfo(
|
|
hDir,
|
|
lpcFileNameTuna,
|
|
lpcAlternateFileNameTuna,
|
|
&sOIFrom);
|
|
}
|
|
|
|
if (EnteredCriticalSection) {
|
|
LeaveShadowCritRx(RxContext);
|
|
}
|
|
|
|
if (LastComponentName.Buffer) {
|
|
RxFreePool(LastComponentName.Buffer);
|
|
}
|
|
|
|
if (lpcFileNameTuna) {
|
|
FreeMem(lpcFileNameTuna);
|
|
}
|
|
|
|
if (lpcAlternateFileNameTuna) {
|
|
FreeMem(lpcAlternateFileNameTuna);
|
|
}
|
|
|
|
if (Disconnected) {
|
|
if (iRet!=0) {
|
|
*Status = LocalStatus;
|
|
}
|
|
else
|
|
{
|
|
// when disconnected, report the change
|
|
// we can be smart about this and report only once if this is not across
|
|
// directories
|
|
FsRtlNotifyFullReportChange(
|
|
pNetRootEntry->NetRoot.pNotifySync,
|
|
&pNetRootEntry->NetRoot.DirNotifyList,
|
|
(PSTRING)GET_ALREADY_PREFIXED_NAME(NULL,capFcb),
|
|
(USHORT)(GET_ALREADY_PREFIXED_NAME(NULL, capFcb)->Length -
|
|
smbFcb->MinimalCscSmbFcb.LastComponentLength),
|
|
NULL,
|
|
NULL,
|
|
(fFile)?FILE_NOTIFY_CHANGE_FILE_NAME:FILE_NOTIFY_CHANGE_DIR_NAME,
|
|
FILE_ACTION_RENAMED_OLD_NAME,
|
|
NULL);
|
|
|
|
// upcase it so change notification will get it right
|
|
|
|
UniToUpper(RenameName.Buffer, RenameName.Buffer, RenameName.Length);
|
|
FsRtlNotifyFullReportChange(
|
|
pNetRootEntry->NetRoot.pNotifySync,
|
|
&pNetRootEntry->NetRoot.DirNotifyList,
|
|
(PSTRING)&RenameName,
|
|
(USHORT)(RenameName.Length -
|
|
CscSmbFcb.MinimalCscSmbFcb.LastComponentLength),
|
|
NULL,
|
|
NULL,
|
|
(fFile)?FILE_NOTIFY_CHANGE_FILE_NAME:FILE_NOTIFY_CHANGE_DIR_NAME,
|
|
FILE_ACTION_RENAMED_NEW_NAME,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
if (smbFcb->uniDfsPrefix.Buffer && RenameName.Buffer)
|
|
{
|
|
RxFreePool(RenameName.Buffer);
|
|
}
|
|
|
|
// DbgPrint("to %ws\n", Find32.cFileName);
|
|
RxLog(("to hDirTo=%x hShadowTo=%x %ws uShadowStatusFrom=%x\n", hDirTo, hShadowTo, Find32.cFileName, uShadowStatusFrom));
|
|
|
|
RxLog(("Status=%x \n\n", *Status));
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbCscRenameEpilogue exit-> %08lx %08lx\n", RxContext, *Status ));
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
MRxSmbCscCloseShadowHandle (
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine closes the filehandle opened for CSC.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
RxCaptureFcb;RxCaptureFobx;
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
// DbgPrint("MRxSmbCscCloseShadowHandle %x\n", smbSrvOpen->hfShadow);
|
|
|
|
ASSERT(smbSrvOpen->hfShadow != 0);
|
|
|
|
CloseFileLocal((CSCHFILE)(smbSrvOpen->hfShadow));
|
|
|
|
smbSrvOpen->hfShadow = 0;
|
|
|
|
if (smbSrvOpen->Flags & SMB_SRVOPEN_FLAG_LOCAL_OPEN)
|
|
{
|
|
ASSERT(smbFcb->cntLocalOpens);
|
|
smbFcb->cntLocalOpens--;
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscCloseShadowHandle\n"));
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbCscFixupFindFirst (
|
|
PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called from the simplesyncT2 builder/sender to fixup
|
|
the t2 request before it's sent. the deal is that the parameters section
|
|
has to be built up in part but i don't want to preallocate. so, i pass in
|
|
a dummy (but valid pointer) and then drop in the actual parameters
|
|
here.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
We will use the smbbuf for everything here. First, we use it for sending
|
|
and receiving. at the end of receiving, the FILE_BOTH_INFORMATION will be
|
|
in the buffer and this will be recomposed into a w32_find buffer in the
|
|
same buffer. if we find for any reason that we can't do this (buffer too
|
|
small, send/rcv doesn't work, whatever) then we'll nuke the shadow and it
|
|
will have to be reloaded.
|
|
|
|
|
|
--*/
|
|
{
|
|
PRX_CONTEXT RxContext = OrdinaryExchange->RxContext;
|
|
PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState;
|
|
PSMB_HEADER SmbHeader = (PSMB_HEADER)StufferState->BufferBase;
|
|
PREQ_TRANSACTION TransactRequest = (PREQ_TRANSACTION)(SmbHeader+1);
|
|
ULONG ParameterCount = SmbGetUshort(&TransactRequest->ParameterCount);
|
|
ULONG ParameterOffset = SmbGetUshort(&TransactRequest->ParameterOffset);
|
|
PBYTE Parameters = ((PBYTE)(SmbHeader))+ParameterOffset;
|
|
REQ_FIND_FIRST2 FindFirst;
|
|
NTSTATUS Status=STATUS_SUCCESS;
|
|
PSMBCE_SERVER pServer = NULL;
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscFixupFindFirst %08lx %08lx %08lx %08lx\n",
|
|
OrdinaryExchange,ParameterCount,ParameterOffset,Parameters));
|
|
|
|
// SearchAttributes is hardcoded to the magic number 0x16
|
|
FindFirst.SearchAttributes = (SMB_FILE_ATTRIBUTE_DIRECTORY |
|
|
SMB_FILE_ATTRIBUTE_SYSTEM |
|
|
SMB_FILE_ATTRIBUTE_HIDDEN);
|
|
|
|
FindFirst.SearchCount = 1;
|
|
|
|
FindFirst.Flags = (SMB_FIND_CLOSE_AFTER_REQUEST |
|
|
SMB_FIND_CLOSE_AT_EOS |
|
|
SMB_FIND_RETURN_RESUME_KEYS);
|
|
|
|
FindFirst.InformationLevel = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
|
|
|
|
FindFirst.SearchStorageType = 0;
|
|
|
|
RtlCopyMemory (
|
|
Parameters,
|
|
&FindFirst,
|
|
FIELD_OFFSET(REQ_FIND_FIRST2,Buffer[0]));
|
|
pServer = SmbCeGetExchangeServer(OrdinaryExchange);
|
|
|
|
ASSERT(pServer);
|
|
|
|
if (FlagOn(pServer->DialectFlags,DF_UNICODE))
|
|
{
|
|
|
|
ASSERT(FlagOn(SmbHeader->Flags2,SMB_FLAGS2_UNICODE));
|
|
|
|
RtlCopyMemory(
|
|
Parameters + FIELD_OFFSET(REQ_FIND_FIRST2,Buffer[0]),
|
|
OrdinaryExchange->pPathArgument1->Buffer,
|
|
OrdinaryExchange->pPathArgument1->Length);
|
|
|
|
SmbPutUshort(
|
|
(Parameters +
|
|
FIELD_OFFSET(REQ_FIND_FIRST2,Buffer[0]) +
|
|
OrdinaryExchange->pPathArgument1->Length),
|
|
0); //traiing null
|
|
}
|
|
else
|
|
{
|
|
OEM_STRING OemString;
|
|
|
|
OemString.Length =
|
|
OemString.MaximumLength =
|
|
(USHORT)( StufferState->BufferLimit - Parameters - sizeof(CHAR));
|
|
|
|
OemString.Buffer = (Parameters + FIELD_OFFSET(REQ_FIND_FIRST2,Buffer[0]));
|
|
|
|
if (FlagOn(SmbHeader->Flags,SMB_FLAGS_CASE_INSENSITIVE) &&
|
|
!FlagOn(SmbHeader->Flags2,SMB_FLAGS2_KNOWS_LONG_NAMES)) {
|
|
Status = RtlUpcaseUnicodeStringToOemString(
|
|
&OemString,
|
|
OrdinaryExchange->pPathArgument1,
|
|
FALSE);
|
|
} else {
|
|
Status = RtlUnicodeStringToOemString(
|
|
&OemString,
|
|
OrdinaryExchange->pPathArgument1,
|
|
FALSE);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ASSERT(!"BufferOverrun");
|
|
return(RX_MAP_STATUS(BUFFER_OVERFLOW));
|
|
}
|
|
|
|
OemString.Length = (USHORT)RtlxUnicodeStringToOemSize(OrdinaryExchange->pPathArgument1);
|
|
|
|
ASSERT(OemString.Length);
|
|
|
|
*(Parameters + FIELD_OFFSET(REQ_FIND_FIRST2,Buffer[0])+OemString.Length-1) = 0;
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
typedef FILE_BOTH_DIR_INFORMATION SMB_UNALIGNED *MRXSMBCSC_FILE_BOTH_DIR_INFORMATION;
|
|
VOID
|
|
MRxSmbCscLocateAndFillFind32WithinSmbbuf(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
When this routine is called, the associated smbbuf contains a findfirst
|
|
response with the FILE_BOTH_INFORMATION aboard. This routine first locates
|
|
a position in the smbbuf to hold a find32. Then, the information is
|
|
converted from the NT format for use with the shadow routines.
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
Find32 - The ptr to the find32 (actually, the smbbuf!)
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState;
|
|
PSMB_HEADER SmbHeader = (PSMB_HEADER)StufferState->BufferBase;
|
|
PRESP_TRANSACTION TransactResponse = (PRESP_TRANSACTION)(SmbHeader+1);
|
|
ULONG DataOffset,FileNameLength,ShortNameLength;
|
|
_WIN32_FIND_DATA *Find32;
|
|
PBYTE AlternateName;
|
|
MRXSMBCSC_FILE_BOTH_DIR_INFORMATION BothDirInfo;
|
|
PSMBCE_SERVER pServer = NULL;
|
|
|
|
//first, we have to get a potentially unaligned ptr to the bothdirinfo
|
|
DataOffset = SmbGetUshort(&TransactResponse->DataOffset);
|
|
|
|
AlternateName = ((PBYTE)SmbHeader);
|
|
BothDirInfo = (MRXSMBCSC_FILE_BOTH_DIR_INFORMATION)(((PBYTE)SmbHeader)+DataOffset);
|
|
FileNameLength = SmbGetUlong(&BothDirInfo->FileNameLength);
|
|
ShortNameLength = BothDirInfo->ShortNameLength;
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscLocateAndFillFind32WithinSmbbuf offset=%08lx %08lx %08lx\n",
|
|
DataOffset,FileNameLength,ShortNameLength));
|
|
|
|
|
|
//save the alternate name info in the very beginning of the buffer..that
|
|
//way, the copy of the full name will not destroy it
|
|
if (ShortNameLength != 0) {
|
|
RtlCopyMemory(
|
|
AlternateName,
|
|
&BothDirInfo->ShortName[0],
|
|
ShortNameLength);
|
|
}
|
|
|
|
|
|
Find32 = (_WIN32_FIND_DATA *)(AlternateName + LongAlign(sizeof(Find32->cAlternateFileName)));
|
|
Find32->dwFileAttributes = SmbGetUlong(&BothDirInfo->FileAttributes);
|
|
Find32->ftCreationTime.dwLowDateTime = SmbGetUlong(&BothDirInfo->CreationTime.LowPart);
|
|
Find32->ftCreationTime.dwHighDateTime = SmbGetUlong(&BothDirInfo->CreationTime.HighPart);
|
|
Find32->ftLastAccessTime.dwLowDateTime = SmbGetUlong(&BothDirInfo->LastAccessTime.LowPart);
|
|
Find32->ftLastAccessTime.dwHighDateTime = SmbGetUlong(&BothDirInfo->LastAccessTime.HighPart);
|
|
Find32->ftLastWriteTime.dwLowDateTime = SmbGetUlong(&BothDirInfo->LastWriteTime.LowPart);
|
|
Find32->ftLastWriteTime.dwHighDateTime = SmbGetUlong(&BothDirInfo->LastWriteTime.HighPart);
|
|
Find32->nFileSizeLow = SmbGetUlong(&BothDirInfo->EndOfFile.LowPart);
|
|
Find32->nFileSizeHigh = SmbGetUlong(&BothDirInfo->EndOfFile.HighPart);
|
|
|
|
pServer = SmbCeGetExchangeServer(OrdinaryExchange);
|
|
|
|
ASSERT(pServer);
|
|
|
|
if (FlagOn(pServer->DialectFlags,DF_UNICODE))
|
|
{
|
|
//copy the full name....don't forget the NULL
|
|
RtlCopyMemory (
|
|
&Find32->cFileName[0],
|
|
&BothDirInfo->FileName[0],
|
|
FileNameLength );
|
|
|
|
Find32->cFileName[FileNameLength/sizeof(WCHAR)] = 0;
|
|
|
|
//finally, copy the shortname...don't forget the null
|
|
RtlCopyMemory(
|
|
&Find32->cAlternateFileName[0],
|
|
AlternateName,
|
|
ShortNameLength );
|
|
|
|
Find32->cAlternateFileName[ShortNameLength/sizeof(WCHAR)] = 0;
|
|
}
|
|
else
|
|
{
|
|
UNICODE_STRING strUni;
|
|
OEM_STRING strOem;
|
|
NTSTATUS Status;
|
|
|
|
strOem.Length = strOem.MaximumLength = (USHORT)FileNameLength;
|
|
strOem.Buffer = (PBYTE)&BothDirInfo->FileName[0];
|
|
|
|
strUni.Length = (USHORT)RtlxOemStringToUnicodeSize(&strOem);
|
|
strUni.MaximumLength = (USHORT)sizeof(Find32->cFileName);
|
|
strUni.Buffer = Find32->cFileName;
|
|
|
|
Status = RtlOemStringToUnicodeString(&strUni, &strOem, FALSE);
|
|
ASSERT(Status == STATUS_SUCCESS);
|
|
|
|
Find32->cFileName[strUni.Length/sizeof(WCHAR)];
|
|
|
|
strOem.Length = strOem.MaximumLength = (USHORT)ShortNameLength;
|
|
strOem.Buffer = AlternateName;
|
|
|
|
strUni.Length = (USHORT)RtlxOemStringToUnicodeSize(&strOem);
|
|
strUni.MaximumLength = (USHORT)sizeof(Find32->cAlternateFileName);
|
|
strUni.Buffer = Find32->cAlternateFileName;
|
|
|
|
Status = RtlOemStringToUnicodeString(&strUni, &strOem, FALSE);
|
|
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
DbgPrint("oem=%x, uni=%x Status=%x\n", &strUni, &strOem, Status);
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
Find32->cAlternateFileName[strUni.Length/sizeof(WCHAR)];
|
|
ASSERT(Find32->cFileName[0]);
|
|
}
|
|
|
|
OrdinaryExchange->Find32WithinSmbbuf = Find32;
|
|
|
|
RxDbgTrace(0, Dbg, ("MRxSmbCscLocateAndFillFind32WithinSmbbuf size,name=%08lx %ws\n",
|
|
Find32->nFileSizeLow, &Find32->cFileName[0]));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MRxSmbCscGetFileInfoFromServerWithinExchange (
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE,
|
|
PUNICODE_STRING FileName OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads information about a file; it may have been locally
|
|
modified or it may be in the process of having a showdow created. in any
|
|
case, the info is returned as a pointer to a Find32 structure. The find32
|
|
structure is contained within the smbbuf of the exchange.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
We will use the smbbuf for everything here. First, we use it for sending
|
|
and receiving. at the end of receiving, the FILE_BOTH_INFORMATION will be
|
|
in the buffer and this will be recomposed into a w32_find buffer in the
|
|
same buffer. if we find for any reason that we can't do this (buffer too
|
|
small, send/rcv doesn't work, whatever) then we'll nuke the shadow and it
|
|
will have to be reloaded.
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
ULONG ParameterLength, TotalLength;
|
|
PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState;
|
|
|
|
if (FileName!=NULL) {
|
|
OrdinaryExchange->pPathArgument1 = FileName; //so it can be found later
|
|
} else {
|
|
ASSERT(OrdinaryExchange->pPathArgument1 != NULL);
|
|
FileName = OrdinaryExchange->pPathArgument1;
|
|
}
|
|
|
|
ParameterLength = FileName->Length + sizeof(WCHAR);
|
|
ParameterLength += FIELD_OFFSET(REQ_FIND_FIRST2,Buffer[0]);
|
|
|
|
TotalLength = sizeof(SMB_HEADER) + FIELD_OFFSET(REQ_TRANSACTION,Buffer[0]); //basic
|
|
TotalLength += sizeof(WORD); //bytecount
|
|
TotalLength = LongAlign(TotalLength); //move past pad
|
|
TotalLength += LongAlign(ParameterLength);
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbCscGetFileInfoFromServerWithinExchange %08lx %08lx %08lx\n",
|
|
RxContext,TotalLength,ParameterLength));
|
|
|
|
if (TotalLength > OrdinaryExchange->SmbBufSize) {
|
|
goto FINALLY;
|
|
}
|
|
|
|
//note that the parameter buffer is not the actual parameters BUT
|
|
//it is a valid buffer. CODE.IMPROVEMENT perhaps the called routine
|
|
//should take cognizance of the passed fixup routine and not require
|
|
//a valid param buffer and not do the copy.
|
|
|
|
Status = __MRxSmbSimpleSyncTransact2(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS,
|
|
SMBPSE_OETYPE_T2_FOR_ONE_FILE_DIRCTRL,
|
|
TRANS2_FIND_FIRST2,
|
|
StufferState->ActualBufferBase,ParameterLength,
|
|
NULL,0,
|
|
MRxSmbCscFixupFindFirst
|
|
);
|
|
|
|
if (Status!=STATUS_SUCCESS) {
|
|
goto FINALLY;
|
|
}
|
|
|
|
MRxSmbCscLocateAndFillFind32WithinSmbbuf(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS);
|
|
|
|
FINALLY:
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbCscGetFileInfoFromServerWithinExchange %08lx %08lx\n",
|
|
RxContext,Status));
|
|
|
|
return(Status);
|
|
|
|
}
|
|
|
|
VOID
|
|
MRxSmbCscUpdateShadowFromClose (
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates the shadow information after a close.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
If anything every goes wrong, we just don't update. this will cause
|
|
the shadow to be reload later.
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
RxCaptureFcb;RxCaptureFobx;
|
|
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
|
|
_WIN32_FIND_DATA *Find32 = NULL;
|
|
ULONG uStatus;
|
|
int TruncateRetVal = -1;
|
|
_WIN32_FIND_DATA *AllocatedFind32 = NULL;
|
|
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
|
|
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
BOOLEAN Disconnected;
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbCscUpdateShadowFromClose %08lx\n",
|
|
RxContext));
|
|
|
|
pNetRootEntry = SmbCeGetAssociatedNetRootEntry(capFcb->pNetRoot);
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);
|
|
|
|
Disconnected = MRxSmbCSCIsDisconnectedOpen(capFcb, smbSrvOpen);
|
|
|
|
if (smbFcb->hShadow==0) {
|
|
if (Disconnected) {
|
|
if (smbSrvOpen->hfShadow != 0){
|
|
MRxSmbCscCloseShadowHandle(RxContext);
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg,
|
|
("MRxSmbCscUpdateShadowFromClose shadowzero %08lx\n",RxContext));
|
|
|
|
return;
|
|
}
|
|
|
|
if (smbFcb->ContainingFcb->FcbState & FCB_STATE_ORPHANED)
|
|
{
|
|
if (smbSrvOpen->hfShadow != 0){
|
|
MRxSmbCscCloseShadowHandle(RxContext);
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg,
|
|
("MRxSmbCscUpdateShadowFromClose Orphaned FCB %x\n", smbFcb->ContainingFcb));
|
|
|
|
return;
|
|
}
|
|
|
|
EnterShadowCritRx(RxContext);
|
|
|
|
if (!Disconnected) {
|
|
// If the file has been modified, we need to get the new timestamp
|
|
// from the server. By the time we come here the file has already been
|
|
// closed on the server, so we can safely get the new timestamp.
|
|
|
|
if (FlagOn(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_SHADOW_DATA_MODIFIED)){
|
|
NTSTATUS LocalStatus;
|
|
|
|
LeaveShadowCritRx(RxContext);
|
|
|
|
LocalStatus = MRxSmbCscGetFileInfoFromServerWithinExchange(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS,
|
|
GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext));
|
|
|
|
if (LocalStatus == STATUS_SUCCESS) {
|
|
Find32 = OrdinaryExchange->Find32WithinSmbbuf;
|
|
RxLog(("Fromclose hShadow=%x l=%x h=%x\n", smbFcb->hShadow, Find32->ftLastWriteTime.dwLowDateTime, Find32->ftLastWriteTime.dwHighDateTime));
|
|
} else {
|
|
RxLog(("MRxSmbCscGetFileInfoFromServerWithinExchange returned LocalStatus\n"));
|
|
}
|
|
|
|
EnterShadowCritRx(RxContext);
|
|
}
|
|
|
|
if(GetShadowInfo(smbFcb->hParentDir,
|
|
smbFcb->hShadow,
|
|
NULL,
|
|
&uStatus, NULL) < SRET_OK)
|
|
{
|
|
goto FINALLY;
|
|
}
|
|
|
|
|
|
// in connected mode for a sparsely filled file that is not corrupt
|
|
// ie. no writes have failed on it, if the original size and the
|
|
// current size match, remove the sparse marking
|
|
|
|
// This optimization was added for notepad case in which when the app
|
|
// opens the file it reads the entire file. Therefore CSC does not need
|
|
// to fill the file and therefore we should clear the SPARSE flag. - NavjotV
|
|
|
|
//WinSE Bug- 25843
|
|
// We want to do this only if file is not Truncated in an earlier
|
|
// create. When we truncate the file we want it to stay SPARSE till
|
|
// we actually fill the file - NavjotV
|
|
|
|
|
|
if((NodeType(capFcb) == RDBSS_NTC_STORAGE_TYPE_FILE) &&
|
|
!smbFcb->ShadowIsCorrupt &&
|
|
!FlagOn(smbFcb->MFlags, SMB_FCB_FLAG_CSC_TRUNCATED_SHADOW) &&
|
|
(uStatus & SHADOW_SPARSE))
|
|
{
|
|
LARGE_INTEGER liTemp;
|
|
|
|
if(GetSizeHSHADOW(
|
|
smbFcb->hShadow,
|
|
&(liTemp.HighPart),
|
|
&(liTemp.LowPart))>=0)
|
|
{
|
|
|
|
if ((liTemp.HighPart == smbFcb->OriginalShadowSize.HighPart)&&
|
|
(liTemp.LowPart == smbFcb->OriginalShadowSize.LowPart))
|
|
{
|
|
uStatus &= ~SHADOW_SPARSE;
|
|
smbFcb->ShadowStatus &= ~SHADOW_SPARSE;
|
|
// RxDbgTrace(0, Dbg, ("hShadow=%x unsparsed\r\n", smbFcb->hShadow));
|
|
RxLog(("hShadow=%x unsparsed\r\n", smbFcb->hShadow));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// disconnected operation
|
|
|
|
if (smbFcb->hParentDir==0xffffffff) {
|
|
goto FINALLY;
|
|
}
|
|
|
|
AllocatedFind32 = (_WIN32_FIND_DATA *)RxAllocatePoolWithTag(
|
|
PagedPool | POOL_COLD_ALLOCATION,
|
|
sizeof(_WIN32_FIND_DATA),
|
|
RX_MISC_POOLTAG);
|
|
if (AllocatedFind32==NULL) {
|
|
goto FINALLY;
|
|
}
|
|
|
|
Find32 = AllocatedFind32;
|
|
|
|
if (GetShadowInfo(
|
|
smbFcb->hParentDir,
|
|
smbFcb->hShadow,
|
|
Find32,
|
|
&uStatus,
|
|
NULL) < SRET_OK) {
|
|
goto FINALLY; //bailout;
|
|
}
|
|
|
|
if (IsFile(Find32->dwFileAttributes))
|
|
{
|
|
GetSizeHSHADOW(
|
|
smbFcb->hShadow,
|
|
&(Find32->nFileSizeHigh),
|
|
&(Find32->nFileSizeLow));
|
|
}
|
|
else
|
|
{
|
|
Find32->nFileSizeHigh = Find32->nFileSizeLow = 0;
|
|
}
|
|
|
|
}
|
|
|
|
// If the shadow has become stale due to write errors
|
|
// or has become dirty because of writes on a complete file
|
|
// or sparse because of writes beyond what we have cached
|
|
// we need to mark it as such
|
|
|
|
uStatus |= (smbFcb->ShadowStatus & SHADOW_MODFLAGS);
|
|
|
|
if (Disconnected && FlagOn(smbSrvOpen->Flags, SMB_SRVOPEN_FLAG_SHADOW_DATA_MODIFIED))
|
|
{
|
|
uStatus |= SHADOW_DIRTY;
|
|
}
|
|
|
|
if (Disconnected && FlagOn(smbFcb->LocalFlags, FLAG_FDB_SHADOW_SNAPSHOTTED))
|
|
{
|
|
uStatus |= SHADOW_DIRTY;
|
|
}
|
|
|
|
if (Find32) {
|
|
smbFcb->OriginalShadowSize.LowPart = Find32->nFileSizeLow;
|
|
smbFcb->OriginalShadowSize.HighPart = Find32->nFileSizeHigh;
|
|
}
|
|
|
|
if (smbFcb->ShadowIsCorrupt) {
|
|
TruncateRetVal = TruncateDataHSHADOW(smbFcb->hParentDir, smbFcb->hShadow);
|
|
if (TruncateRetVal>=SRET_OK) {
|
|
// Set status flags to indicate sparse file
|
|
uStatus |= SHADOW_SPARSE;
|
|
}
|
|
}
|
|
|
|
|
|
if (Disconnected &&
|
|
FlagOn(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_SHADOW_DATA_MODIFIED) &&
|
|
!FlagOn(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_SHADOW_LWT_MODIFIED))
|
|
{
|
|
GetSystemTime(&(Find32->ftLastWriteTime));
|
|
}
|
|
|
|
if (SetShadowInfo(
|
|
smbFcb->hParentDir,
|
|
smbFcb->hShadow,
|
|
Find32,
|
|
uStatus,
|
|
( SHADOW_FLAGS_ASSIGN |
|
|
((Disconnected) ?
|
|
SHADOW_FLAGS_DONT_UPDATE_ORGTIME :
|
|
0)
|
|
)) < SRET_OK) {
|
|
|
|
goto FINALLY;
|
|
}
|
|
|
|
if (TruncateRetVal>=SRET_OK) {
|
|
smbFcb->ShadowIsCorrupt = FALSE;
|
|
}
|
|
|
|
if (Disconnected) {
|
|
if (FlagOn(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_SHADOW_MODIFIED)) {
|
|
MarkShareDirty(&smbFcb->sCscRootInfo.ShareStatus, smbFcb->sCscRootInfo.hShare);
|
|
}
|
|
|
|
if (smbSrvOpen->hfShadow != 0){
|
|
MRxSmbCscCloseShadowHandle(RxContext);
|
|
}
|
|
}
|
|
|
|
FINALLY:
|
|
LeaveShadowCritRx(RxContext);
|
|
|
|
if (AllocatedFind32!=NULL) {
|
|
RxFreePool(AllocatedFind32);
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbCscUpdateShadowFromClose %08lx\n",
|
|
RxContext));
|
|
|
|
}
|
|
|
|
VOID
|
|
MRxSmbCscDeallocateForFcb (
|
|
IN OUT PMRX_FCB pFcb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tears down the Csc part of a netroot. Currently, all it does is
|
|
to pull the netroot out of the list of csc netroots.
|
|
|
|
Arguments:
|
|
|
|
pNetRootEntry -
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(pFcb);
|
|
PMRX_NET_ROOT NetRoot = pFcb->pNetRoot;
|
|
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry = NULL;
|
|
|
|
if(!MRxSmbIsCscEnabled ||
|
|
(fShadow == 0)
|
|
) {
|
|
pFcb->fMiniInited = FALSE; // clean it up on shutdown
|
|
return;
|
|
}
|
|
|
|
if( !(NodeType(pFcb)== RDBSS_NTC_STORAGE_TYPE_DIRECTORY ||
|
|
NodeType(pFcb)== RDBSS_NTC_STORAGE_TYPE_FILE ||
|
|
NodeType(pFcb)== RDBSS_NTC_STORAGE_TYPE_UNKNOWN))
|
|
{
|
|
return;
|
|
}
|
|
|
|
pNetRootEntry = SmbCeGetAssociatedNetRootEntry(NetRoot);
|
|
|
|
if (pNetRootEntry->NetRoot.NetRootType != NET_ROOT_DISK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
EnterShadowCrit();
|
|
|
|
#if defined(BITCOPY)
|
|
|
|
if (smbFcb && smbFcb->lpDirtyBitmap) {
|
|
LPSTR strmName;
|
|
// Save Bitmap to disk and Delete Bitmap
|
|
strmName = FormAppendNameString(lpdbShadow,
|
|
smbFcb->hShadow,
|
|
CscBmpAltStrmName);
|
|
if (strmName != NULL) {
|
|
if (smbFcb->hShadow >= 0x80000000) {
|
|
// Write only to file inodes
|
|
CscBmpWrite(smbFcb->lpDirtyBitmap, strmName);
|
|
}
|
|
CscBmpDelete(&((LPCSC_BITMAP)(smbFcb->lpDirtyBitmap)));
|
|
ExFreePool(strmName);
|
|
}
|
|
}
|
|
#endif // defined(BITCOPY)
|
|
|
|
try
|
|
{
|
|
if (smbFcb->ShadowReverseTranslationLinks.Flink != 0) {
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbCscDeallocateForFcb...%08lx %08lx %08lx %08lx\n",
|
|
pFcb,
|
|
smbFcb->hShadow,
|
|
smbFcb->hParentDir,
|
|
smbFcb->ShadowReverseTranslationLinks.Flink ));
|
|
|
|
ValidateSmbFcbList();
|
|
ASSERT(pFcb->fMiniInited);
|
|
pFcb->fMiniInited = FALSE;
|
|
MRxSmbCscRemoveReverseFcbTranslation(smbFcb);
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbCscDeallocateForFcb exit\n"));
|
|
} else {
|
|
ASSERT(smbFcb->ShadowReverseTranslationLinks.Flink == 0);
|
|
}
|
|
|
|
if(FlagOn(smbFcb->LocalFlags, FLAG_FDB_DELETE_ON_CLOSE))
|
|
{
|
|
RxLog(("Dealloc: Deleting hShadow=%x %x %x \n", smbFcb->hShadow, pFcb, smbFcb));
|
|
DeleteShadowHelper(FALSE, smbFcb->hParentDir, smbFcb->hShadow);
|
|
smbFcb->hParentDir = smbFcb->hShadow = 0;
|
|
}
|
|
// if there are any Dfs reverse mapping structures, free them
|
|
if (smbFcb->uniDfsPrefix.Buffer)
|
|
{
|
|
FreeMem(smbFcb->uniDfsPrefix.Buffer);
|
|
ASSERT(smbFcb->uniActualPrefix.Buffer);
|
|
FreeMem(smbFcb->uniActualPrefix.Buffer);
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
if(pFcb->fMiniInited)
|
|
{
|
|
DbgPrint("CSCFcbList messed up \n");
|
|
ASSERT(FALSE);
|
|
}
|
|
goto FINALLY;
|
|
}
|
|
FINALLY:
|
|
LeaveShadowCrit();
|
|
|
|
return;
|
|
}
|
|
|
|
PMRX_SMB_FCB
|
|
MRxSmbCscRecoverMrxFcbFromFdb (
|
|
IN PFDB Fdb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
if (Fdb==NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return CONTAINING_RECORD(
|
|
&Fdb->usFlags,
|
|
MRX_SMB_FCB,
|
|
ShadowStatus
|
|
);
|
|
|
|
}
|
|
|
|
PFDB MRxSmbCscFindFdbFromHShadow (
|
|
IN HSHADOW hShadow
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks thru the current open mrxsmbfcbs and returns a mrxsmbfcb
|
|
that corresponds to the HSHADOW passed. In the interest of not mucking with
|
|
the w95 code, we cast this pointer into a PFDB in such a way that
|
|
the the shadowstatus in the mrxsmbfcb lines up with the usFlags in PFDB.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY pListEntry;
|
|
|
|
DbgDoit(ASSERT(vfInShadowCrit));
|
|
|
|
pListEntry = xCscFcbsList.Flink;
|
|
while (pListEntry != &xCscFcbsList) {
|
|
PMRX_SMB_FCB smbFcb;
|
|
|
|
smbFcb = (PMRX_SMB_FCB)CONTAINING_RECORD(
|
|
pListEntry,
|
|
MRX_SMB_FCB,
|
|
ShadowReverseTranslationLinks);
|
|
|
|
if (((smbFcb->hShadow == hShadow)||(smbFcb->hShadowRenamed == hShadow)) &&
|
|
(!(smbFcb->ContainingFcb->FcbState & FCB_STATE_ORPHANED)))
|
|
{
|
|
PFDB Fdb;
|
|
PUSHORT pShadowStatus = &smbFcb->ShadowStatus;
|
|
ASSERT ( sizeof(Fdb->usFlags) == sizeof (USHORT) );
|
|
Fdb = CONTAINING_RECORD(pShadowStatus,FDB,usFlags);
|
|
return Fdb;
|
|
}
|
|
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PRESOURCE
|
|
MRxSmbCscFindResourceFromHandlesWithModify (
|
|
IN HSHARE hShare,
|
|
IN HSHADOW hRoot,
|
|
IN USHORT usLocalFlagsIncl,
|
|
IN USHORT usLocalFlagsExcl,
|
|
OUT PULONG ShareStatus,
|
|
OUT PULONG DriveMap,
|
|
IN ULONG uStatus,
|
|
IN ULONG uOp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks thru the currently connected netrootentries and
|
|
returns the one that corresponds EITHER to the HSHADOW passed or to
|
|
the HSHARE passed. In the interest of not mucking with the w95 code,
|
|
we cast this pointer into a PRESOURCE. we also return the share
|
|
status as well as the drivemap.....we dont know the drivemap
|
|
but we could get it by walking the list of vnetroots. It is not used anywhere by the UI
|
|
|
|
If the uOp is not 0xffffffff, then we modify the status as well as
|
|
returning it.
|
|
|
|
Arguments:
|
|
|
|
IN HSHARE hShare - the share handle to look for
|
|
IN HSHADOW hRoot - the rootdir handle to look for
|
|
IN USHORT usLocalFlagsIncl - make sure that some of these flags are included (0xffff mean
|
|
include any flags)
|
|
IN USHORT usLocalFlagsExcl - make sure that none of these flags are included
|
|
OUT PULONG ShareStatus - a pointer to where we will store the status
|
|
OUT PULONG DriveMap - a pointer to where we will store the drivemap info
|
|
IN ULONG uStatus - input status to use in the "bit operations"
|
|
IN ULONG uOp - which operation
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
The flags passed here are used in one ioctl to either exclude or include
|
|
connected or disconnected resources as appropriate.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PRESOURCE pResource = NULL;
|
|
BOOLEAN TableLockHeld = FALSE;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbCscFindResourceFromRoot...%08lx\n",hRoot));
|
|
DbgDoit(ASSERT(vfInShadowCrit));
|
|
if ((hRoot==0) && (hShare==0)) {
|
|
return NULL;
|
|
}
|
|
|
|
//we can't hold this....sigh
|
|
LeaveShadowCrit();
|
|
|
|
try {
|
|
|
|
RxAcquirePrefixTableLockExclusive( &RxNetNameTable, TRUE);
|
|
TableLockHeld = TRUE;
|
|
|
|
if (IsListEmpty( &RxNetNameTable.MemberQueue )) {
|
|
try_return(pResource = NULL);
|
|
}
|
|
|
|
if (ShareStatus)
|
|
{
|
|
*ShareStatus = 0;
|
|
}
|
|
|
|
ListEntry = RxNetNameTable.MemberQueue.Flink;
|
|
for (;ListEntry != &RxNetNameTable.MemberQueue;) {
|
|
PVOID Container;
|
|
PRX_PREFIX_ENTRY PrefixEntry;
|
|
PMRX_NET_ROOT NetRoot;
|
|
PMRX_V_NET_ROOT VNetRoot;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
PUSHORT ThisShareStatus;
|
|
|
|
PrefixEntry = CONTAINING_RECORD( ListEntry,
|
|
RX_PREFIX_ENTRY,
|
|
MemberQLinks );
|
|
ListEntry = ListEntry->Flink;
|
|
ASSERT (NodeType(PrefixEntry) == RDBSS_NTC_PREFIX_ENTRY);
|
|
Container = PrefixEntry->ContainingRecord;
|
|
RxDbgTrace(0, Dbg,
|
|
("---> ListE PfxE Container Name %08lx %08lx %08lx %wZ\n",
|
|
ListEntry, PrefixEntry, Container, &PrefixEntry->Prefix));
|
|
|
|
switch (NodeType(Container)) {
|
|
case RDBSS_NTC_NETROOT :
|
|
NetRoot = (PMRX_NET_ROOT)Container;
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("NetRoot->pSrvCall=0x%x, NetRoot->Type=%d, NetRoot->Context=0x%x, NetRoot->pSrvCall->RxDeviceObject=0x%x\n",
|
|
NetRoot->pSrvCall, NetRoot->Type, NetRoot->Context, NetRoot->pSrvCall->RxDeviceObject));
|
|
|
|
if ((NetRoot->pSrvCall == NULL) ||
|
|
(NetRoot->Type != NET_ROOT_DISK) ||
|
|
(NetRoot->Context == NULL) ||
|
|
(NetRoot->pSrvCall->RxDeviceObject != MRxSmbDeviceObject)) {
|
|
RxDbgTrace(0, Dbg,("Skipping \n"));
|
|
continue;
|
|
}
|
|
|
|
pNetRootEntry = SmbCeGetAssociatedNetRootEntry(NetRoot);
|
|
ThisShareStatus = &pNetRootEntry->NetRoot.sCscRootInfo.ShareStatus;
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(NetRoot->pSrvCall);
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("pNetRootEntry->NetRoot.CscEnabled=%d, pNetRootEntry->NetRoot.sCscRootInfo.hRootDir=0x%x, pNetRootEntry->NetRoot.sCscRootInfo.hShare=0x%x\n",
|
|
pNetRootEntry->NetRoot.CscEnabled, pNetRootEntry->NetRoot.sCscRootInfo.hRootDir, pNetRootEntry->NetRoot.sCscRootInfo.hShare
|
|
));
|
|
|
|
if ((hRoot!=0xffffffff)||(hShare != 0xffffffff))
|
|
{
|
|
if (pNetRootEntry->NetRoot.CscEnabled &&
|
|
((pNetRootEntry->NetRoot.sCscRootInfo.hRootDir == hRoot) ||
|
|
(pNetRootEntry->NetRoot.sCscRootInfo.hShare == hShare) )) {
|
|
|
|
if (*ThisShareStatus & usLocalFlagsExcl) {
|
|
|
|
RxDbgTrace(0, Dbg,("Skipping *ThisShareStatus=\n", *ThisShareStatus));
|
|
continue;
|
|
}
|
|
|
|
if ((usLocalFlagsIncl==0xffff)
|
|
|| (*ThisShareStatus & usLocalFlagsIncl)) {
|
|
|
|
switch (mBitOpShadowFlags(uOp)) {
|
|
case SHADOW_FLAGS_ASSIGN:
|
|
*ThisShareStatus = (USHORT)uStatus;
|
|
break;
|
|
|
|
case SHADOW_FLAGS_OR:
|
|
*ThisShareStatus |= (USHORT)uStatus;
|
|
break;
|
|
|
|
case SHADOW_FLAGS_AND:
|
|
*ThisShareStatus &= (USHORT)uStatus;
|
|
break;
|
|
}
|
|
|
|
*ShareStatus |= (*ThisShareStatus | SHARE_CONNECTED);
|
|
|
|
if(SmbCeIsServerInDisconnectedMode(pServerEntry))
|
|
{
|
|
*ShareStatus |= SHARE_DISCONNECTED_OP;
|
|
|
|
}
|
|
|
|
if (pServerEntry->Server.IsPinnedOffline == TRUE)
|
|
*ShareStatus |= SHARE_PINNED_OFFLINE;
|
|
|
|
RxDbgTrace(0, Dbg,("Count of srvopens=%d\n", pServerEntry->Server.NumberOfSrvOpens));
|
|
|
|
*DriveMap = 0; //not used anywhere
|
|
|
|
try_return (pResource = (PRESOURCE)pNetRootEntry);
|
|
}
|
|
}
|
|
}
|
|
else // hShare and hRoot are 0xffffffff, this means we are looping
|
|
{
|
|
if (mBitOpShadowFlags(uOp) == SHADOW_FLAGS_AND)
|
|
{
|
|
if (pNetRootEntry->NetRoot.sCscRootInfo.hRootDir)
|
|
{
|
|
pNetRootEntry->NetRoot.sCscRootInfo.hRootDir = 0;
|
|
pNetRootEntry->NetRoot.sCscRootInfo.hShare = 0;
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
case RDBSS_NTC_SRVCALL :
|
|
continue;
|
|
|
|
case RDBSS_NTC_V_NETROOT :
|
|
VNetRoot = (PMRX_V_NET_ROOT)Container;
|
|
|
|
// NTRAID#455236-1/31/2000-shishirp we should'nt be using this field here, it is strictly meant
|
|
// for the wrapper
|
|
if (((PV_NET_ROOT)Container)->Condition == Condition_Good)
|
|
{
|
|
if (VNetRoot->Context != NULL) {
|
|
pNetRootEntry = ((PSMBCE_V_NET_ROOT_CONTEXT)VNetRoot->Context)->pNetRootEntry;
|
|
RxDbgTrace(0, Dbg,("RDBSS_NTC_V_NETROOT: VNetRoot=%x, pNetRootEntry=%x\r\n",
|
|
VNetRoot, pNetRootEntry));
|
|
|
|
if ((hRoot!=0xffffffff)||(hShare != 0xffffffff))
|
|
{
|
|
if ((pNetRootEntry != NULL) &&
|
|
pNetRootEntry->NetRoot.CscEnabled &&
|
|
((pNetRootEntry->NetRoot.sCscRootInfo.hRootDir == hRoot) ||
|
|
(pNetRootEntry->NetRoot.sCscRootInfo.hShare == hShare))) {
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pNetRootEntry->NetRoot.sCscRootInfo.hRootDir)
|
|
{
|
|
pNetRootEntry->NetRoot.sCscRootInfo.hRootDir = 0;
|
|
pNetRootEntry->NetRoot.sCscRootInfo.hShare = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
}
|
|
|
|
try_return(pResource = NULL);
|
|
|
|
try_exit:NOTHING;
|
|
|
|
} finally {
|
|
|
|
if (TableLockHeld) {
|
|
RxReleasePrefixTableLock( &RxNetNameTable );
|
|
}
|
|
|
|
EnterShadowCrit();
|
|
if (pResource && ((hShare != 0xffffffff) || (hRoot != 0xffffffff)))
|
|
{
|
|
if (ShareStatus)
|
|
{
|
|
SetOfflineOpenStatusForShare(hShare, hRoot, ShareStatus);
|
|
}
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbCscFindResourceFromRoot...%08lx\n",pResource));
|
|
|
|
return(pResource);
|
|
}
|
|
|
|
#undef GetShadowInfo
|
|
#undef SetShadowInfo
|
|
|
|
// this is just a simple wrapper function except that we pick off the rootdir case.
|
|
// ...see the recordmanager code for args
|
|
int PUBLIC
|
|
MRxSmbCscWrappedGetShadowInfo(
|
|
HSHADOW hDir,
|
|
HSHADOW hNew,
|
|
LPFIND32 lpFind32,
|
|
ULONG far *lpuFlags,
|
|
LPOTHERINFO lpOI)
|
|
{
|
|
if (hDir != -1) {
|
|
return(GetShadowInfo(hDir, hNew, lpFind32, lpuFlags, lpOI));
|
|
}
|
|
|
|
//otherwise....just make it up...........
|
|
RtlZeroMemory(
|
|
lpFind32,
|
|
sizeof(*lpFind32));
|
|
|
|
lpFind32->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
|
|
*lpuFlags = SHADOW_SPARSE;
|
|
|
|
return(SRET_OK);
|
|
}
|
|
|
|
|
|
// this is just a simple wrapper function except that we pick off the rootdir case.
|
|
// ...see the recordmanager code for args
|
|
int PUBLIC
|
|
MRxSmbCscWrappedSetShadowInfo(
|
|
HSHADOW hDir,
|
|
HSHADOW hNew,
|
|
LPFIND32 lpFind32,
|
|
ULONG uFlags,
|
|
ULONG uOp)
|
|
{
|
|
if (hDir == -1) {
|
|
return(SRET_OK);
|
|
}
|
|
|
|
return(SetShadowInfo(hDir, hNew, lpFind32, uFlags, uOp));
|
|
}
|
|
|
|
USHORT *
|
|
MRxSmbCscFindLocalFlagsFromFdb(
|
|
PFDB pFdb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PMRX_SMB_FCB smbFcb;
|
|
PUSHORT pShadowStatus = &(pFdb->usFlags);
|
|
|
|
DbgDoit(ASSERT(vfInShadowCrit));
|
|
|
|
smbFcb = CONTAINING_RECORD(pShadowStatus,MRX_SMB_FCB,ShadowStatus);
|
|
return (&(smbFcb->LocalFlags));
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbCscSetSecurityPrologue (
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a set security call is made. It tries
|
|
to set the ACL on the CSC cached version of the file.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
|
|
#if defined(REMOTE_BOOT)
|
|
RxCaptureFcb;
|
|
|
|
_WIN32_FIND_DATA Find32;
|
|
PMRX_SMB_FCB smbFcb;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// First we need to set the security descriptor on the CSC
|
|
// version of the file, if one exists.
|
|
//
|
|
smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
|
|
EnterShadowCritRx(RxContext);
|
|
|
|
Status = MRxSmbCscCreateShadowFromPath(
|
|
GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext),
|
|
SmbCeGetAssociatedNetRootEntry(capFcb->pNetRoot),
|
|
&Find32,
|
|
NULL,
|
|
CREATESHADOW_CONTROL_NOCREATE,
|
|
&smbFcb->MinimalCscSmbFcb,
|
|
RxContext,
|
|
FALSE,
|
|
NULL
|
|
); // not disconnected
|
|
|
|
LeaveShadowCritRx(RxContext);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
Status = MRxSmbCscSetSecurityOnShadow(
|
|
smbFcb->MinimalCscSmbFcb.hShadow,
|
|
RxContext->SetSecurity.SecurityInformation,
|
|
RxContext->SetSecurity.SecurityDescriptor);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("MRxSmbCscSetSecurityPrologue: Could not set security (%lx) for %wZ: %lx\n", RxContext,
|
|
GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext), Status));
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
return Status;
|
|
#else
|
|
return STATUS_SUCCESS;
|
|
#endif
|
|
|
|
}
|
|
|
|
VOID
|
|
MRxSmbCscSetSecurityEpilogue (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PNTSTATUS Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the tail of a set security operation for CSC.
|
|
|
|
If the set failed, it tries to restore the old ACL on the file.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Status - the overall status of the open
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Table off which the parameter validation is driven
|
|
// there is some redundancy in this table, specifically, we could have only the flags
|
|
// and get rid of the other two fields.
|
|
|
|
CSC_IOCTL_ENTRY rgCscIoctlTable[] =
|
|
{
|
|
{IOCTL_SHADOW_GETVERSION, 0, 0},
|
|
{IOCTL_SHADOW_REGISTER_AGENT, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_SHADOW_UNREGISTER_AGENT, 0, 0},
|
|
{IOCTL_SHADOW_GET_UNC_PATH, FLAG_CSC_IOCTL_COPYPARAMS, sizeof(COPYPARAMS)},
|
|
{IOCTL_SHADOW_BEGIN_PQ_ENUM, FLAG_CSC_IOCTL_PQPARAMS, sizeof(PQPARAMS)},
|
|
{IOCTL_SHADOW_END_PQ_ENUM, FLAG_CSC_IOCTL_PQPARAMS, sizeof(PQPARAMS)},
|
|
{IOCTL_SHADOW_NEXT_PRI_SHADOW, FLAG_CSC_IOCTL_PQPARAMS, sizeof(PQPARAMS)},
|
|
{IOCTL_SHADOW_PREV_PRI_SHADOW, FLAG_CSC_IOCTL_PQPARAMS, sizeof(PQPARAMS)},
|
|
{IOCTL_SHADOW_GET_SHADOW_INFO, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_SHADOW_SET_SHADOW_INFO, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_SHADOW_CHK_UPDT_STATUS, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_DO_SHADOW_MAINTENANCE, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_SHADOW_COPYCHUNK, FLAG_CSC_IOCTL_COPYCHUNKCONTEXT, sizeof(COPYCHUNKCONTEXT)},
|
|
{IOCTL_SHADOW_BEGIN_REINT, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_SHADOW_END_REINT, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_SHADOW_CREATE, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_SHADOW_DELETE, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_GET_SHARE_STATUS, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_SET_SHARE_STATUS, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_ADDUSE, 0, 0}, // not applicable on NT
|
|
{IOCTL_DELUSE, 0, 0}, // not applicable on NT
|
|
{IOCTL_GETUSE, 0, 0}, // not applicable on NT
|
|
{IOCTL_SWITCHES, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_GETSHADOW, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_GETGLOBALSTATUS, FLAG_CSC_IOCTL_GLOBALSTATUS,sizeof(GLOBALSTATUS)},
|
|
{IOCTL_FINDOPEN_SHADOW, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_FINDNEXT_SHADOW, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_FINDCLOSE_SHADOW, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_GETPRIORITY_SHADOW, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_SETPRIORITY_SHADOW, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_ADD_HINT, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_DELETE_HINT, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_FINDOPEN_HINT, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_FINDNEXT_HINT, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_FINDCLOSE_HINT, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_GET_IH_PRIORITY, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_GETALIAS_HSHADOW, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{_SHADOW_IOCTL_CODE(37), 0, 0}, // hole in the ioctl range
|
|
{_SHADOW_IOCTL_CODE(38), 0, 0}, // hole in the ioctl range
|
|
{_SHADOW_IOCTL_CODE(39), 0, 0}, // hole in the ioctl range
|
|
{IOCTL_OPENFORCOPYCHUNK, FLAG_CSC_IOCTL_COPYCHUNKCONTEXT, sizeof(COPYCHUNKCONTEXT)},
|
|
{IOCTL_CLOSEFORCOPYCHUNK, FLAG_CSC_IOCTL_COPYCHUNKCONTEXT, sizeof(COPYCHUNKCONTEXT)},
|
|
{IOCTL_IS_SERVER_OFFLINE, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_TRANSITION_SERVER_TO_ONLINE, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_TRANSITION_SERVER_TO_OFFLINE, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_TAKE_SERVER_OFFLINE, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_NAME_OF_SERVER_GOING_OFFLINE, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)},
|
|
{IOCTL_SHAREID_TO_SHARENAME, FLAG_CSC_IOCTL_SHADOWINFO, sizeof(SHADOWINFO)}
|
|
};
|
|
|
|
NTSTATUS
|
|
CaptureInputBufferIfNecessaryAndProbe(
|
|
IN DWORD IoControlCode,
|
|
IN PRX_CONTEXT pRxContext,
|
|
IN PBYTE InputBuffer,
|
|
IN LPCAPTURE_BUFFERS lpCapBuff,
|
|
OUT PBYTE *ppAuxBuf,
|
|
OUT PBYTE *ppOrgBuf,
|
|
OUT PBYTE *ppReturnBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does the capturing if necessary and probing of the buffers.
|
|
Note that because the csc ioctls are always called with METHOD_NEITHER
|
|
buffering mode, we always execute the code below.
|
|
|
|
Arguments:
|
|
|
|
IoControlCode Ioctl code
|
|
|
|
pRxContext context which has all the info of the original ioctl call for IO subsystem
|
|
|
|
InputBuffer Input Buffer
|
|
|
|
lpCapBuff capture buffer passed in by the caller. If this ioctl needs capturing
|
|
then this buffer is used to capture the input buffer.
|
|
We use this in case of SHADOWINFO and COPYPARAMS structures being passed in
|
|
as only in these two case there are embedded pointers
|
|
|
|
ppAuxBuf if we needed to capture another part of the buffer (lpFind32 or lpBuffer),
|
|
this routine will allocate a buffer which will be passed back here, and
|
|
must be freed by the caller.
|
|
|
|
ppReturnBuffer either the input buffer itself, or lpCapBuff (if inputbuffer is captured)
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
int indx;
|
|
BOOL fRet = FALSE;
|
|
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
|
KPROCESSOR_MODE requestorMode;
|
|
|
|
indx = ((IoControlCode >> 2) & 0xfff) - SHADOW_IOCTL_ENUM_BASE;
|
|
|
|
if((indx >=0 ) && (indx < sizeof(rgCscIoctlTable)/sizeof(CSC_IOCTL_ENTRY)))
|
|
{
|
|
|
|
*ppReturnBuffer = InputBuffer;
|
|
|
|
if (rgCscIoctlTable[indx].dwFlags & FLAG_CSC_IOCTL_COPYCHUNKCONTEXT)
|
|
{
|
|
return(ValidateCopyChunkContext(pRxContext, IoControlCode));
|
|
}
|
|
|
|
if (rgCscIoctlTable[indx].dwFlags & FLAG_CSC_IOCTL_BUFFERTYPE_MASK)
|
|
{
|
|
try
|
|
{
|
|
ProbeForRead(InputBuffer,
|
|
rgCscIoctlTable[indx].dwLength,
|
|
1);
|
|
|
|
ProbeForWrite(InputBuffer,
|
|
rgCscIoctlTable[indx].dwLength,
|
|
1);
|
|
|
|
if (rgCscIoctlTable[indx].dwFlags & FLAG_CSC_IOCTL_COPYPARAMS)
|
|
{
|
|
lpCapBuff->sCP = *(LPCOPYPARAMS)InputBuffer;
|
|
*ppReturnBuffer = (PBYTE)&(lpCapBuff->sCP);
|
|
Status = ValidateCopyParams(&(lpCapBuff->sCP));
|
|
}
|
|
else if (rgCscIoctlTable[indx].dwFlags & FLAG_CSC_IOCTL_SHADOWINFO)
|
|
{
|
|
lpCapBuff->sSI = *(LPSHADOWINFO)InputBuffer;
|
|
*ppReturnBuffer = (PBYTE)&(lpCapBuff->sSI);
|
|
Status = ValidateShadowInfo(
|
|
IoControlCode,
|
|
&(lpCapBuff->sSI),
|
|
ppAuxBuf,
|
|
ppOrgBuf);
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ValidateCopyParams(
|
|
LPCOPYPARAMS lpCP
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
if((CscProbeForReadWrite((PBYTE)lpCP->lpLocalPath, MAX_PATH*sizeof(USHORT)) == STATUS_SUCCESS)&&
|
|
(CscProbeForReadWrite((PBYTE)lpCP->lpRemotePath, MAX_PATH*sizeof(USHORT)) == STATUS_SUCCESS)&&
|
|
(CscProbeForReadWrite((PBYTE)lpCP->lpSharePath, MAX_SERVER_SHARE_NAME_FOR_CSC*2) == STATUS_SUCCESS))
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
NTSTATUS
|
|
ValidateShadowInfo(
|
|
DWORD IoControlCode,
|
|
LPSHADOWINFO lpSI,
|
|
LPBYTE *ppAuxBuf,
|
|
LPBYTE *ppOrgBuf
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
// by the time we get here, the SHADOWINFO strucuture has already been
|
|
// probed
|
|
|
|
// IOCTL_DO_SHADOW_MAINTENANCE has multiple suboperations, so we
|
|
// deal them seperately.
|
|
|
|
if (IoControlCode == IOCTL_DO_SHADOW_MAINTENANCE)
|
|
{
|
|
// DbgPrint("SHADOW_OP:0x%x\n", lpSI->uOp);
|
|
switch(lpSI->uOp)
|
|
{
|
|
case SHADOW_REDUCE_REFPRI: // 2
|
|
// case SHADOW_RECALC_IHPRI: // 5
|
|
case SHADOW_PER_THREAD_DISABLE: // 7
|
|
case SHADOW_PER_THREAD_ENABLE: // 8
|
|
case SHADOW_ADDHINT_FROM_INODE: // 10
|
|
case SHADOW_DELETEHINT_FROM_INODE: // 11
|
|
case SHADOW_BEGIN_INODE_TRANSACTION: // 13
|
|
case SHADOW_END_INODE_TRANSACTION: // 14
|
|
case SHADOW_TRANSITION_SERVER_TO_OFFLINE: // 19
|
|
case SHADOW_CHANGE_HANDLE_CACHING_STATE: // 20
|
|
case SHADOW_RECREATE: // 21
|
|
case SHADOW_SPARSE_STALE_DETECTION_COUNTER: // 23
|
|
case SHADOW_DISABLE_CSC_FOR_USER: // 25
|
|
case SHADOW_SET_DATABASE_STATUS: // 26
|
|
case SHADOW_MANUAL_FILE_DETECTION_COUNTER: // 28
|
|
return STATUS_SUCCESS;
|
|
|
|
case SHADOW_FIND_CREATE_PRINCIPAL_ID: // 15
|
|
case SHADOW_GET_SECURITY_INFO: // 16
|
|
case SHADOW_SET_EXCLUSION_LIST: // 17
|
|
case SHADOW_SET_BW_CONSERVE_LIST: // 18
|
|
case SHADOW_GET_SPACE_STATS: // 5
|
|
if (!lpSI->lpBuffer || !lpSI->cbBufferSize)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
Status = CscProbeAndCaptureForReadWrite(
|
|
lpSI->lpBuffer,
|
|
lpSI->cbBufferSize,
|
|
ppAuxBuf);
|
|
if (Status == STATUS_SUCCESS) {
|
|
*ppOrgBuf =(PBYTE) lpSI->lpBuffer;
|
|
lpSI->lpBuffer = (PBYTE) *ppAuxBuf;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
case SHADOW_REINIT_DATABASE: // 9
|
|
case SHADOW_MAKE_SPACE: // 1
|
|
case SHADOW_ADD_SPACE: // 3
|
|
case SHADOW_FREE_SPACE: // 4
|
|
case SHADOW_SET_MAX_SPACE: // 6
|
|
case SHADOW_COPY_INODE_FILE: // 12
|
|
case SHADOW_RENAME: // 22
|
|
case SHADOW_ENABLE_CSC_FOR_USER: // 24
|
|
case SHADOW_PURGE_UNPINNED_FILES: // 27
|
|
Status = CscProbeAndCaptureForReadWrite(
|
|
(PBYTE)(lpSI->lpFind32),
|
|
sizeof(WIN32_FIND_DATA),
|
|
ppAuxBuf);
|
|
if (Status == STATUS_SUCCESS) {
|
|
*ppOrgBuf =(PBYTE) lpSI->lpFind32;
|
|
lpSI->lpFind32 = (LPFIND32) *ppAuxBuf;
|
|
}
|
|
return Status;
|
|
|
|
default:
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
} else if (
|
|
IoControlCode == IOCTL_GET_SHARE_STATUS
|
|
||
|
|
IoControlCode == IOCTL_SET_SHARE_STATUS
|
|
) {
|
|
Status = CscProbeAndCaptureForReadWrite(
|
|
(PBYTE)(lpSI->lpFind32),
|
|
sizeof(SHAREINFOW),
|
|
ppAuxBuf);
|
|
if (Status == STATUS_SUCCESS) {
|
|
*ppOrgBuf =(PBYTE) lpSI->lpFind32;
|
|
lpSI->lpFind32 = (LPFIND32) *ppAuxBuf;
|
|
}
|
|
return Status;
|
|
} else if (
|
|
IoControlCode == IOCTL_IS_SERVER_OFFLINE
|
|
||
|
|
IoControlCode == IOCTL_TAKE_SERVER_OFFLINE
|
|
||
|
|
IoControlCode == IOCTL_NAME_OF_SERVER_GOING_OFFLINE
|
|
||
|
|
IoControlCode == IOCTL_SHAREID_TO_SHARENAME
|
|
) {
|
|
Status = CscProbeAndCaptureForReadWrite(
|
|
lpSI->lpBuffer,
|
|
lpSI->cbBufferSize,
|
|
ppAuxBuf);
|
|
if (Status == STATUS_SUCCESS) {
|
|
*ppOrgBuf =(PBYTE) lpSI->lpBuffer;
|
|
lpSI->lpBuffer = (PBYTE) *ppAuxBuf;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
// for all other ioctls which take SHADOWINFO structure, there may be an embedded
|
|
// find32 structure, which must be probed
|
|
|
|
ASSERT(IoControlCode != IOCTL_DO_SHADOW_MAINTENANCE);
|
|
Status = CscProbeAndCaptureForReadWrite(
|
|
(PBYTE)(lpSI->lpFind32),
|
|
sizeof(WIN32_FIND_DATA),
|
|
ppAuxBuf);
|
|
if (Status == STATUS_SUCCESS) {
|
|
*ppOrgBuf =(PBYTE) lpSI->lpFind32;
|
|
lpSI->lpFind32 = (LPFIND32) *ppAuxBuf;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ValidateCopyChunkContext(
|
|
PRX_CONTEXT RxContext,
|
|
DWORD IoControlCode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
|
|
// on open, validate the name
|
|
if (IoControlCode == IOCTL_OPENFORCOPYCHUNK)
|
|
{
|
|
PBYTE FileName = (PBYTE)LowIoContext->ParamsFor.IoCtl.pInputBuffer;
|
|
ULONG FileNameLength = LowIoContext->ParamsFor.IoCtl.InputBufferLength - 1;
|
|
|
|
// let us varify that the name passed in is within our limits
|
|
if ((FileNameLength > ((MAX_PATH+MAX_SERVER_SHARE_NAME_FOR_CSC)*sizeof(USHORT)))||
|
|
CscProbeForReadWrite(FileName, FileNameLength) != STATUS_SUCCESS)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// on copychunk or close we need to validate the chunk structure.
|
|
// we don't need to validate the handle in it because the
|
|
// object manager does that in MrxSmbCscCopyChunk and MrxSmbCscCloseForCopyChunk
|
|
// routines
|
|
|
|
COPYCHUNKCONTEXT *CopyChunkContext =
|
|
(COPYCHUNKCONTEXT *)(LowIoContext->ParamsFor.IoCtl.pOutputBuffer);
|
|
|
|
if (!CopyChunkContext)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
// for all copychunk calls, validate the copychunkcontext buffer
|
|
return CscProbeForReadWrite((PBYTE)CopyChunkContext, sizeof(COPYCHUNKCONTEXT));
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CscProbeForReadWrite(
|
|
PBYTE pBuffer,
|
|
DWORD dwSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
try
|
|
{
|
|
if (pBuffer != NULL) {
|
|
ProbeForRead(
|
|
pBuffer,
|
|
dwSize,
|
|
1);
|
|
|
|
ProbeForWrite(
|
|
pBuffer,
|
|
dwSize,
|
|
1);
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status=STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CscProbeAndCaptureForReadWrite(
|
|
PBYTE pBuffer,
|
|
DWORD dwSize,
|
|
PBYTE *ppAuxBuf
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PBYTE pBuf = NULL;
|
|
|
|
try {
|
|
if (pBuffer != NULL && dwSize > 0) {
|
|
ProbeForRead(pBuffer, dwSize, 1);
|
|
ProbeForWrite(pBuffer, dwSize, 1);
|
|
pBuf = RxAllocatePoolWithTag(PagedPool, dwSize, 'xXRM');
|
|
if (pBuf != NULL) {
|
|
RtlCopyMemory(pBuf, pBuffer, dwSize);
|
|
*ppAuxBuf = pBuf;
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS && pBuf != NULL) {
|
|
RxFreePool(pBuf);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
CopyBackIfNecessary(
|
|
IN DWORD IoControlCode,
|
|
IN OUT PBYTE InputBuffer,
|
|
IN LPCAPTURE_BUFFERS lpCapBuff,
|
|
IN PBYTE pAuxBuf,
|
|
IN PBYTE pOrgBuf,
|
|
BOOL fSuccess
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies back the capture buffer to the inout buffer, in ioctls which
|
|
expect output.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
int indx;
|
|
BOOL fRet = FALSE;
|
|
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
|
LPSHADOWINFO lpSI = NULL;
|
|
|
|
indx = ((IoControlCode >> 2) & 0xfff) - SHADOW_IOCTL_ENUM_BASE;
|
|
|
|
ASSERT((indx >=0 ) && (indx < sizeof(rgCscIoctlTable)/sizeof(CSC_IOCTL_ENTRY)));
|
|
|
|
if (fSuccess)
|
|
{
|
|
|
|
if (rgCscIoctlTable[indx].dwFlags & FLAG_CSC_IOCTL_SHADOWINFO) {
|
|
*(LPSHADOWINFO)InputBuffer = lpCapBuff->sSI;
|
|
lpSI = &lpCapBuff->sSI;
|
|
if (pAuxBuf != NULL && pOrgBuf != NULL) {
|
|
//
|
|
// Some ioctls have embedded pointers. We have to copy the 2nd buffer
|
|
// back, too, and set the embedded pointer back to that buffer.
|
|
//
|
|
if (IoControlCode == IOCTL_DO_SHADOW_MAINTENANCE) {
|
|
// DbgPrint("SHADOW_OP(2):0x%x\n", lpSI->uOp);
|
|
switch(lpSI->uOp) {
|
|
case SHADOW_FIND_CREATE_PRINCIPAL_ID: // 15
|
|
case SHADOW_GET_SECURITY_INFO: // 16
|
|
case SHADOW_SET_EXCLUSION_LIST: // 17
|
|
case SHADOW_SET_BW_CONSERVE_LIST: // 18
|
|
case SHADOW_GET_SPACE_STATS: // 5
|
|
RtlMoveMemory(pOrgBuf, pAuxBuf, lpSI->cbBufferSize);
|
|
lpSI->lpBuffer = (PBYTE) pOrgBuf;
|
|
break;
|
|
case SHADOW_REINIT_DATABASE: // 9
|
|
case SHADOW_MAKE_SPACE: // 1
|
|
case SHADOW_ADD_SPACE: // 3
|
|
case SHADOW_FREE_SPACE: // 4
|
|
case SHADOW_SET_MAX_SPACE: // 6
|
|
case SHADOW_COPY_INODE_FILE: // 12
|
|
case SHADOW_RENAME: // 22
|
|
case SHADOW_ENABLE_CSC_FOR_USER: // 24
|
|
case SHADOW_PURGE_UNPINNED_FILES: // 27
|
|
RtlMoveMemory(pOrgBuf, pAuxBuf, sizeof(WIN32_FIND_DATA));
|
|
lpSI->lpFind32 = (LPFIND32) pOrgBuf;
|
|
break;
|
|
}
|
|
} else if (
|
|
IoControlCode == IOCTL_GET_SHARE_STATUS
|
|
||
|
|
IoControlCode == IOCTL_SET_SHARE_STATUS
|
|
) {
|
|
RtlMoveMemory(pOrgBuf, pAuxBuf, sizeof(SHAREINFOW));
|
|
lpSI->lpFind32 = (LPFIND32) pOrgBuf;
|
|
} else if (
|
|
IoControlCode == IOCTL_IS_SERVER_OFFLINE
|
|
||
|
|
IoControlCode == IOCTL_TAKE_SERVER_OFFLINE
|
|
||
|
|
IoControlCode == IOCTL_NAME_OF_SERVER_GOING_OFFLINE
|
|
||
|
|
IoControlCode == IOCTL_SHAREID_TO_SHARENAME
|
|
) {
|
|
RtlMoveMemory(pOrgBuf, pAuxBuf, lpSI->cbBufferSize);
|
|
lpSI->lpBuffer = (PBYTE) pOrgBuf;
|
|
} else {
|
|
RtlMoveMemory(pOrgBuf, pAuxBuf, sizeof(WIN32_FIND_DATA));
|
|
lpSI->lpFind32 = (LPFIND32) pOrgBuf;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (rgCscIoctlTable[indx].dwFlags & FLAG_CSC_IOCTL_SHADOWINFO) {
|
|
((LPSHADOWINFO)InputBuffer)->dwError = lpCapBuff->sSI.dwError;
|
|
} else if (rgCscIoctlTable[indx].dwFlags & FLAG_CSC_IOCTL_COPYPARAMS) {
|
|
((LPCOPYPARAMS)InputBuffer)->dwError = lpCapBuff->sCP.dwError;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID ValidateSmbFcbList(
|
|
VOID)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine validates the smbfcb reverse lookup list
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
This validation code must be called from within the shadow critical section
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY pListEntry;
|
|
DWORD cntFlink, cntBlink;
|
|
|
|
DbgDoit(ASSERT(vfInShadowCrit));
|
|
|
|
cntFlink = cntBlink = 0;
|
|
|
|
// check forward list validity
|
|
|
|
pListEntry = xCscFcbsList.Flink;
|
|
|
|
while (pListEntry != &xCscFcbsList) {
|
|
PMRX_SMB_FCB smbFcb;
|
|
|
|
smbFcb = (PMRX_SMB_FCB)CONTAINING_RECORD(
|
|
pListEntry,
|
|
MRX_SMB_FCB,
|
|
ShadowReverseTranslationLinks);
|
|
|
|
|
|
try
|
|
{
|
|
if((NodeType(smbFcb->ContainingFcb) != RDBSS_NTC_STORAGE_TYPE_FILE) &&
|
|
(NodeType(smbFcb->ContainingFcb) != RDBSS_NTC_STORAGE_TYPE_DIRECTORY)&&
|
|
(NodeType(smbFcb->ContainingFcb) != RDBSS_NTC_STORAGE_TYPE_UNKNOWN)
|
|
)
|
|
{
|
|
DbgPrint("ValidateSmbFcbList:Invalid nodetype %x fcb=%x smbfcb=%x\n",
|
|
NodeType(smbFcb->ContainingFcb),smbFcb->ContainingFcb, smbFcb);
|
|
// DbgBreakPoint();
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DbgPrint("ValidateSmbFcbList:Invalid smbFcb %x \n", smbFcb);
|
|
//Bug - 578682
|
|
//DbgBreakPoint();
|
|
}
|
|
|
|
|
|
++cntFlink;
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
// check backward list validity
|
|
|
|
pListEntry = xCscFcbsList.Blink;
|
|
|
|
while (pListEntry != &xCscFcbsList) {
|
|
PMRX_SMB_FCB smbFcb;
|
|
|
|
smbFcb = (PMRX_SMB_FCB)CONTAINING_RECORD(
|
|
pListEntry,
|
|
MRX_SMB_FCB,
|
|
ShadowReverseTranslationLinks);
|
|
|
|
|
|
try
|
|
{
|
|
if((NodeType(smbFcb->ContainingFcb) != RDBSS_NTC_STORAGE_TYPE_FILE) &&
|
|
(NodeType(smbFcb->ContainingFcb) != RDBSS_NTC_STORAGE_TYPE_DIRECTORY)&&
|
|
(NodeType(smbFcb->ContainingFcb) != RDBSS_NTC_STORAGE_TYPE_UNKNOWN))
|
|
{
|
|
DbgPrint("ValidateSmbFcbList:Invalid nodetype %x fcb=%x smbfcb=%x\n",
|
|
NodeType(smbFcb->ContainingFcb),smbFcb->ContainingFcb, smbFcb);
|
|
// DbgBreakPoint();
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DbgPrint("ValidateSmbFcbList:Invalid smbFcb %x \n", smbFcb);
|
|
// DbgBreakPoint();
|
|
}
|
|
|
|
++cntBlink;
|
|
pListEntry = pListEntry->Blink;
|
|
}
|
|
|
|
// both counts should be the same
|
|
ASSERT(cntFlink == cntBlink);
|
|
}
|
|
|
|
BOOL SetOfflineOpenStatusForShare(
|
|
CSC_SHARE_HANDLE hShare,
|
|
CSC_SHADOW_HANDLE hRootDir,
|
|
OUT PULONG pShareStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY pListEntry;
|
|
|
|
DbgDoit(ASSERT(vfInShadowCrit));
|
|
|
|
if ((hRootDir==0) && (hShare==0)) {
|
|
return 0;
|
|
}
|
|
|
|
ASSERT((hShare!=0xffffffff) || (hRootDir!=0xffffffff));
|
|
|
|
pListEntry = xCscFcbsList.Flink;
|
|
|
|
while (pListEntry != &xCscFcbsList) {
|
|
PMRX_SMB_FCB smbFcb;
|
|
|
|
smbFcb = (PMRX_SMB_FCB)CONTAINING_RECORD(
|
|
pListEntry,
|
|
MRX_SMB_FCB,
|
|
ShadowReverseTranslationLinks);
|
|
|
|
if (((smbFcb->sCscRootInfo.hShare == hShare)
|
|
||(smbFcb->sCscRootInfo.hRootDir == hRootDir)))
|
|
|
|
{
|
|
if(smbFcb->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
*pShareStatus |= SHARE_FINDS_IN_PROGRESS;
|
|
}
|
|
else
|
|
{
|
|
*pShareStatus |= SHARE_FILES_OPEN;
|
|
}
|
|
|
|
}
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
EnterShadowCritRx(
|
|
PRX_CONTEXT pRxContext
|
|
)
|
|
{
|
|
EnterShadowCrit();
|
|
|
|
#if DBG
|
|
if (pRxContext)
|
|
{
|
|
ASSERT( !pRxContext->ShadowCritOwner );
|
|
pRxContext->ShadowCritOwner = GetCurThreadHandle();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
LeaveShadowCritRx(
|
|
PRX_CONTEXT pRxContext
|
|
)
|
|
{
|
|
|
|
#if DBG
|
|
if (pRxContext)
|
|
{
|
|
ASSERT( pRxContext->ShadowCritOwner );
|
|
pRxContext->ShadowCritOwner = 0;
|
|
}
|
|
#endif
|
|
LeaveShadowCrit();
|
|
}
|
|
|
|
NTSTATUS
|
|
CscInitializeServerEntryDfsRoot(
|
|
PRX_CONTEXT pRxContext,
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry
|
|
)
|
|
{
|
|
PDFS_NAME_CONTEXT pDfsNameContext = NULL;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
UNICODE_STRING ServerPath;
|
|
|
|
if (pRxContext &&
|
|
pRxContext->CurrentIrpSp &&
|
|
(pRxContext->CurrentIrpSp->MajorFunction == IRP_MJ_CREATE)) {
|
|
pDfsNameContext = CscIsValidDfsNameContext(pRxContext->Create.NtCreateParameters.DfsNameContext);
|
|
|
|
if (pDfsNameContext){
|
|
Status = CscDfsParseDfsPath(
|
|
&pDfsNameContext->UNCFileName,
|
|
&ServerPath,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
if (pServerEntry->DfsRootName.Buffer == NULL) {
|
|
pServerEntry->DfsRootName.Buffer = RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
ServerPath.Length,
|
|
RX_MISC_POOLTAG);
|
|
|
|
if (pServerEntry->DfsRootName.Buffer == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(pServerEntry->DfsRootName.Buffer,
|
|
ServerPath.Buffer,
|
|
ServerPath.Length);
|
|
|
|
pServerEntry->DfsRootName.MaximumLength = ServerPath.Length;
|
|
pServerEntry->DfsRootName.Length = ServerPath.Length;
|
|
|
|
// DbgPrint("Initialized %x with DfsRoot %wZ\n", pServerEntry, &pServerEntry->DfsRootName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MRxSmbCscLocalFileOpen(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the conserving bandwidth for remote boot client. The bandwidth
|
|
is saved by the way of reducing the files opened on the boot server, instead the local
|
|
copy of the file on CSC is used.
|
|
|
|
There is a set the rules that the file has to meet in order to be opened locally.
|
|
|
|
* file tried to open on VDO share
|
|
* a local copy of the file has been created on CSC, which is not sparse
|
|
* the write or name space operations have to go through the server except
|
|
* Only execute operations are allowed through
|
|
|
|
The routine checks the file whether it meets those rules. The actual open happens on
|
|
MRxSmbCscCreateEpilogue.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
|
|
Return Value:
|
|
|
|
Status - we return the local open status
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
NTSTATUS LocalStatus;
|
|
|
|
RxCaptureFcb;
|
|
|
|
PMRX_SMB_FCB smbFcb;
|
|
PMRX_SRV_OPEN SrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen;
|
|
|
|
PMRX_NET_ROOT NetRoot;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
|
|
|
ULONG CreateDisposition = RxContext->Create.NtCreateParameters.Disposition;
|
|
ULONG CreateOptions = RxContext->Create.NtCreateParameters.CreateOptions;
|
|
ACCESS_MASK DesiredAccess = RxContext->Create.NtCreateParameters.DesiredAccess;
|
|
|
|
BOOLEAN CreatedShadow = FALSE;
|
|
ULONG uShadowStatus;
|
|
_WIN32_FIND_DATA Find32, CscFind32;
|
|
PUNICODE_STRING PathName;
|
|
int iRet;
|
|
int EarlyOut = 0;
|
|
LARGE_INTEGER CurrentTime;
|
|
LARGE_INTEGER DeadLine;
|
|
BOOL fLocalOpens = FALSE;
|
|
|
|
NetRoot = capFcb->pNetRoot;
|
|
pNetRootEntry = SmbCeGetAssociatedNetRootEntry(NetRoot);
|
|
|
|
SrvOpen = RxContext->pRelevantSrvOpen;
|
|
smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
PathName = GET_ALREADY_PREFIXED_NAME(SrvOpen,capFcb);
|
|
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(NetRoot->pSrvCall);
|
|
|
|
// don't do local open if the share is either in disconnected state
|
|
// or the share is not a VDO share
|
|
|
|
if (SmbCeIsServerInDisconnectedMode(pServerEntry) ||
|
|
(pNetRootEntry->NetRoot.CscFlags != SMB_CSC_CACHE_VDO))
|
|
{
|
|
RxDbgTrace( 0, Dbg, ("Server disconnected or not VDO share, CscFlags=%x\n", pNetRootEntry->NetRoot.CscFlags));
|
|
return Status;
|
|
}
|
|
|
|
EnterShadowCritRx(RxContext);
|
|
|
|
|
|
LocalStatus = MRxSmbCscObtainShadowHandles(
|
|
RxContext,
|
|
&Status,
|
|
&CscFind32,
|
|
&CreatedShadow,
|
|
CREATESHADOW_CONTROL_NOCREATE,
|
|
FALSE);
|
|
|
|
if (LocalStatus != STATUS_SUCCESS) {
|
|
EarlyOut = 1;
|
|
goto FINALLY;
|
|
}
|
|
|
|
if ((smbFcb->hShadow == 0) ||
|
|
(smbFcb->ShadowStatus == SHADOW_SPARSE) ||
|
|
(smbFcb->ShadowStatus & SHADOW_MODFLAGS) ||
|
|
(CscFind32.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
// if no local copy or file is sparse, or modified, or it is a directory,
|
|
// file cannot open locally
|
|
EarlyOut = 2;
|
|
goto FINALLY;
|
|
}
|
|
|
|
LeaveShadowCritRx(RxContext);
|
|
fLocalOpens = CSCCheckLocalOpens(RxContext);
|
|
EnterShadowCritRx(RxContext);
|
|
|
|
if (DesiredAccess &
|
|
( GENERIC_WRITE |
|
|
FILE_WRITE_EA |
|
|
FILE_ADD_FILE |
|
|
FILE_WRITE_DATA |
|
|
FILE_APPEND_DATA |
|
|
FILE_DELETE_CHILD |
|
|
FILE_ADD_SUBDIRECTORY)) { // FILE_WRITE_ATTRIBUTE is OK
|
|
if (fLocalOpens)
|
|
{
|
|
HookKdPrint(BADERRORS, ("VDO not allowed for this desired access %x %x\n", smbFcb->hShadow, DesiredAccess));
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
EarlyOut = 3;
|
|
goto FINALLY;
|
|
}
|
|
|
|
if (CreateOptions & FILE_DELETE_ON_CLOSE)
|
|
{
|
|
if (fLocalOpens)
|
|
{
|
|
HookKdPrint(BADERRORS, ("DeletOnClose not allowed %x %x\n", smbFcb->hShadow, DesiredAccess));
|
|
Status = STATUS_ACCESS_DENIED;
|
|
EarlyOut = 30;
|
|
goto FINALLY;
|
|
}
|
|
}
|
|
|
|
if (CreateDisposition != FILE_OPEN)
|
|
{
|
|
// name space operations should go to the server
|
|
if (fLocalOpens)
|
|
{
|
|
Status = STATUS_SHARING_VIOLATION;
|
|
}
|
|
|
|
EarlyOut = 4;
|
|
goto FINALLY;
|
|
}
|
|
|
|
if (!(DesiredAccess & FILE_EXECUTE))
|
|
{
|
|
// DbgPrint("FILE_EXECUTE not set (0x%x) on %wZ\n", DesiredAccess, PathName);
|
|
EarlyOut = 5;
|
|
goto FINALLY;
|
|
}
|
|
|
|
|
|
#if 0
|
|
KeQuerySystemTime( &CurrentTime );
|
|
|
|
// system time is based on 100ns
|
|
DeadLine.QuadPart = smbFcb->LastSyncTime.QuadPart + (LONGLONG) (CscSyncInterval * 10 * 1000 * 1000);
|
|
|
|
if (CurrentTime.QuadPart < DeadLine.QuadPart) {
|
|
Status = STATUS_SUCCESS;
|
|
goto FINALLY;
|
|
}
|
|
#endif
|
|
|
|
// do a check on the server only when there is no outstanding local open
|
|
if (!fLocalOpens)
|
|
{
|
|
LeaveShadowCritRx(RxContext);
|
|
LocalStatus = MRxSmbGetFileInfoFromServer(RxContext,PathName,&Find32,SrvOpen,NULL);
|
|
EnterShadowCritRx(RxContext);
|
|
|
|
if (LocalStatus != STATUS_SUCCESS) {
|
|
// if cannot get file information from the server, file cannot open locally
|
|
EarlyOut = 6;
|
|
goto FINALLY;
|
|
}
|
|
|
|
iRet = RefreshShadow(
|
|
smbFcb->hParentDir,
|
|
smbFcb->hShadow,
|
|
&Find32,
|
|
&uShadowStatus
|
|
);
|
|
|
|
if (iRet < SRET_OK) {
|
|
// if refresh shadow fails, file cannot open locally
|
|
EarlyOut = 7;
|
|
goto FINALLY;
|
|
} else {
|
|
SetShadowInfo(smbFcb->hParentDir,
|
|
smbFcb->hShadow,
|
|
NULL,
|
|
0,
|
|
SHADOW_FLAGS_OR|SHADOW_FLAGS_SET_REFRESH_TIME);
|
|
}
|
|
|
|
if (uShadowStatus == SHADOW_SPARSE) {
|
|
// if the file is sparse, it cannot open locally
|
|
EarlyOut = 8;
|
|
goto FINALLY;
|
|
} else {
|
|
// no more rule, file can open locally
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
FINALLY:
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
SetFlag(smbSrvOpen->Flags, SMB_SRVOPEN_FLAG_LOCAL_OPEN);
|
|
smbFcb->cntLocalOpens++;
|
|
//RxDbgTrace(0, Dbg, ("Local : %wZ\n",PathName));
|
|
RxLog(("Local Open %lx %lx %lx\n",smbFcb->hParentDir, smbFcb->hShadow, capFcb));
|
|
|
|
RxDbgTrace( 0, Dbg,
|
|
("MRxSmbCscLocalFileOpen hdir/hshadow= %08lx %08lx\n",
|
|
smbFcb->hParentDir, smbFcb->hShadow));
|
|
} else {
|
|
RxDbgTrace(0, Dbg, ("Remote: %d %wZ\n",EarlyOut,PathName));
|
|
}
|
|
|
|
LeaveShadowCritRx(RxContext);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
CSCCheckLocalOpens(
|
|
IN PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine checks whether there is any fcb in the fcb list which has our inode.
|
|
The reason why this has to be done is because of rename.
|
|
Thus if a file cat.exe is opened the smbFcb->hShadow field had the inode.
|
|
Then if a rename to dog.exe is done while the file is open, the smbfcb->hShadow field is
|
|
set to 0 and smbFcb->hShadowRename is set to the inode value.
|
|
After that when a delete comes through, RDBSS cannot check the sharing violation because
|
|
it doesn'nt change the name in the FCB to dog.exe. So it creates a new FCB for dog.exe.
|
|
Yet we do have to give sharing violation in this scenario. We accomplish this
|
|
by detecting just such a scenario in the routine below.
|
|
|
|
It essentially goes though the FCB reverselookup list and if it finds an FCB which
|
|
has the same hShadow or hShadowRenamed as this one, and it's cntLocalOpens is
|
|
non-zero, then it gives sharing violation.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Status - miniredir status
|
|
|
|
Return Value:
|
|
|
|
Status - Passed in status, or STATUS_SHARING_VILOATION
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
BOOL fRet = FALSE;
|
|
RxCaptureFcb;
|
|
PMRX_SMB_FCB smbFcb, pSmbFcbT;
|
|
PMRX_NET_ROOT NetRoot;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
|
CSC_SHADOW_HANDLE hShadow;
|
|
PLIST_ENTRY pListEntry;
|
|
|
|
|
|
NetRoot = capFcb->pNetRoot;
|
|
pNetRootEntry = SmbCeGetAssociatedNetRootEntry(NetRoot);
|
|
smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
|
|
hShadow = (smbFcb->hShadow)?smbFcb->hShadow:smbFcb->hShadowRenamed;
|
|
|
|
|
|
if (!hShadow || (pNetRootEntry->NetRoot.CscFlags != SMB_CSC_CACHE_VDO))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
EnterShadowCritRx(RxContext);
|
|
|
|
pListEntry = xCscFcbsList.Flink;
|
|
|
|
while (pListEntry != &xCscFcbsList) {
|
|
|
|
pSmbFcbT = (PMRX_SMB_FCB)CONTAINING_RECORD(
|
|
pListEntry,
|
|
MRX_SMB_FCB,
|
|
ShadowReverseTranslationLinks);
|
|
if ((pSmbFcbT->hShadow==smbFcb->hShadow) ||
|
|
(pSmbFcbT->hShadowRenamed==smbFcb->hShadow))
|
|
{
|
|
if (pSmbFcbT->cntLocalOpens)
|
|
{
|
|
RxLog(("smbfcb=%x has local opens for hShadow=%x\n", pSmbFcbT, smbFcb->hShadow));
|
|
fRet = TRUE;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
LeaveShadowCritRx(RxContext);
|
|
return fRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsCSCBusy(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks whether any files are being shadowed by CSC
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE if any files are being shadowed, FALSE otherwise
|
|
|
|
Notes:
|
|
|
|
Used by the diableCSC ioctl
|
|
|
|
--*/
|
|
{
|
|
DbgDoit(ASSERT(vfInShadowCrit));
|
|
return (xCscFcbsList.Flink != &xCscFcbsList);
|
|
}
|
|
|
|
VOID
|
|
ClearCSCStateOnRedirStructures(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine clears the csc state on netroots
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Notes:
|
|
|
|
Used by the diableCSC ioctl
|
|
|
|
--*/
|
|
{
|
|
|
|
DbgDoit(ASSERT(vfInShadowCrit));
|
|
ASSERT(!IsCSCBusy());
|
|
ClearAllResourcesOfShadowingState();
|
|
|
|
DbgDoit(ASSERT(vfInShadowCrit));
|
|
CscTransitionServerToOnline(0); // transition all servers
|
|
}
|
|
|
|
BOOL
|
|
CscDfsShareIsInReint(
|
|
IN PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PDFS_NAME_CONTEXT pDfsNameContext = NULL;
|
|
UNICODE_STRING SharePath;
|
|
NTSTATUS LocalStatus;
|
|
CSC_SHARE_HANDLE CscShareHandle;
|
|
ULONG ulRootHintFlags;
|
|
|
|
if (RxContext->MajorFunction == IRP_MJ_CREATE) {
|
|
|
|
pDfsNameContext = CscIsValidDfsNameContext(RxContext->Create.NtCreateParameters.DfsNameContext);
|
|
|
|
if (pDfsNameContext)
|
|
{
|
|
LocalStatus = CscDfsParseDfsPath(
|
|
&pDfsNameContext->UNCFileName,
|
|
NULL,
|
|
&SharePath,
|
|
NULL);
|
|
|
|
if (LocalStatus == STATUS_SUCCESS)
|
|
{
|
|
GetHShareFromUNCString(
|
|
SharePath.Buffer,
|
|
SharePath.Length,
|
|
1,
|
|
TRUE,
|
|
&CscShareHandle,
|
|
&ulRootHintFlags);
|
|
|
|
|
|
if (CscShareHandle && (CscShareHandle == hShareReint))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
LONG CSCBeginReint(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT LPSHADOWINFO lpSI
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
begins merge. This routine needs to be in pagelocked memory because
|
|
it takes cancel spinlock.
|
|
|
|
We pend the Irp that issued the beginreint ioctl and set our cancel routine in it.
|
|
If the thread doing the merge dies for some reason, the Ps code calls our cancel routine
|
|
to cacncel this irp, this is when we cleanup the merge state.
|
|
|
|
Arguments:
|
|
|
|
RxContext
|
|
|
|
lpSI Buffer passed down by the caller
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
LONG ShadowIRet;
|
|
KIRQL CancelIrql;
|
|
BOOL fCancelled = FALSE;
|
|
|
|
|
|
ShadowIRet = IoctlBeginReint(lpSI);
|
|
|
|
if (ShadowIRet >= 1)
|
|
{
|
|
CloseOpenFiles(lpSI->hShare, NULL, 0);
|
|
IoAcquireCancelSpinLock( &CancelIrql);
|
|
if (RxContext->CurrentIrp->Cancel)
|
|
{
|
|
vIrpReint = NULL;
|
|
IoReleaseCancelSpinLock( CancelIrql );
|
|
IoctlEndReint(lpSI);
|
|
|
|
}
|
|
else
|
|
{
|
|
// succeeded begin merge on this share
|
|
vIrpReint = RxContext->CurrentIrp;
|
|
|
|
IoSetCancelRoutine( RxContext->CurrentIrp, CSCCancelReint );
|
|
IoReleaseCancelSpinLock( CancelIrql );
|
|
|
|
// Returning STATUS_PENDING
|
|
IoMarkIrpPending( RxContext->CurrentIrp );
|
|
|
|
// as we hijacked the Irp, let us make sure that rdbss gets rid of the rxcontext
|
|
RxCompleteRequest_Real( RxContext, NULL, STATUS_PENDING );
|
|
}
|
|
}
|
|
|
|
return ShadowIRet;
|
|
}
|
|
|
|
ULONG CSCEndReint(
|
|
LPSHADOWINFO lpSI
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ends merge. This routine needs to be in pagelocked memory because it takes cancel spinlock
|
|
This is normal termination. We cleanup our merge state and complete the irp we pended
|
|
during begin
|
|
|
|
Arguments:
|
|
|
|
lpSI
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
int ShadowIRet=-1;
|
|
KIRQL CancelIrql;
|
|
PIRP pIrp;
|
|
|
|
// check if reint was actualy going on on this share
|
|
ShadowIRet = IoctlEndReint(lpSI);
|
|
|
|
if (ShadowIRet >= 0)
|
|
{
|
|
IoAcquireCancelSpinLock( &CancelIrql);
|
|
|
|
pIrp = vIrpReint;
|
|
vIrpReint = NULL;
|
|
|
|
if (pIrp)
|
|
{
|
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
pIrp->IoStatus.Information = 0;
|
|
IoSetCancelRoutine(pIrp, NULL);
|
|
}
|
|
|
|
IoReleaseCancelSpinLock( CancelIrql );
|
|
|
|
if (pIrp)
|
|
{
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
}
|
|
}
|
|
return ShadowIRet;
|
|
}
|
|
|
|
VOID CSCCancelReint(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP ThisIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cancels a merge begun by the user. This routine needs to be in pagelocked memory because
|
|
it takes cancel spinlock.
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Ignored.
|
|
|
|
ThisIrp - This is the Irp to cancel.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
SHADOWINFO sSI;
|
|
|
|
memset(&sSI, 0, sizeof(sSI));
|
|
sSI.hShare = hShareReint;
|
|
IoSetCancelRoutine( ThisIrp, NULL );
|
|
vIrpReint = NULL;
|
|
IoReleaseCancelSpinLock( ThisIrp->CancelIrql );
|
|
ThisIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
ThisIrp->IoStatus.Information = 0;
|
|
IoCompleteRequest(ThisIrp, IO_NO_INCREMENT);
|
|
IoctlEndReint(&sSI);
|
|
}
|
|
|
|
BOOL
|
|
CloseOpenFiles(
|
|
HSHARE hShare,
|
|
PUNICODE_STRING pServerName,
|
|
int lenSkip
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes all open files for CSC. Does this by issuing a foceclose on the vneteroot
|
|
This an equivalent of wnetcancelconnection on a share with forced close of files
|
|
|
|
Arguments:
|
|
|
|
hShare CSC handle to the share to close, ignored if pServerName is non-NULL
|
|
|
|
pServerName All open files on on shares belonging to this server
|
|
|
|
lenskip #of backslashes in servername (usually one)
|
|
|
|
Return Value:
|
|
|
|
Whether atleast one open file was found
|
|
|
|
--*/
|
|
{
|
|
BOOL fFoundAtleastOne=FALSE, fFound;
|
|
PLIST_ENTRY pListEntry;
|
|
SHAREINFOW sSR;
|
|
UNICODE_STRING uniShare;
|
|
|
|
EnterShadowCrit();
|
|
pListEntry = xCscFcbsList.Flink;
|
|
|
|
while (pListEntry != &xCscFcbsList) {
|
|
PMRX_SMB_FCB smbFcb;
|
|
|
|
smbFcb = (PMRX_SMB_FCB)CONTAINING_RECORD(
|
|
pListEntry,
|
|
MRX_SMB_FCB,
|
|
ShadowReverseTranslationLinks);
|
|
|
|
fFound = FALSE;
|
|
if (pServerName)
|
|
{
|
|
ASSERT(smbFcb->sCscRootInfo.hShare);
|
|
GetShareInfo(smbFcb->sCscRootInfo.hShare, &sSR, NULL);
|
|
|
|
uniShare.Buffer = sSR.rgSharePath+lenSkip;
|
|
uniShare.Length = uniShare.MaximumLength = pServerName->Length;
|
|
|
|
// DbgPrint("matching %wZ with Servername\n", &uniShare);
|
|
if(RtlEqualUnicodeString(pServerName, &uniShare, TRUE)&&
|
|
(uniShare.Buffer[pServerName->Length/sizeof(WCHAR)]==(WCHAR)'\\'))
|
|
{
|
|
// DbgPrint("matched \n");
|
|
fFound=TRUE;
|
|
}
|
|
}
|
|
else if ((smbFcb->sCscRootInfo.hShare == hShare))
|
|
{
|
|
fFound = TRUE;
|
|
}
|
|
|
|
if (fFound)
|
|
{
|
|
if (!(smbFcb->ContainingFcb->FcbState & FCB_STATE_ORPHANED))
|
|
{
|
|
PNET_ROOT pNetRoot = (PNET_ROOT)((PFCB)(smbFcb->ContainingFcb))->pNetRoot;
|
|
|
|
|
|
fFoundAtleastOne = TRUE;
|
|
LeaveShadowCrit();
|
|
RxAcquirePrefixTableLockExclusive( &RxNetNameTable, TRUE);
|
|
RxForceFinalizeAllVNetRoots(pNetRoot);
|
|
RxReleasePrefixTableLock( &RxNetNameTable );
|
|
EnterShadowCrit();
|
|
pListEntry = xCscFcbsList.Flink;
|
|
//
|
|
// ...start again
|
|
//
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// DbgPrint("Skipping orphaned FCB for hShadow=%x \n", smbFcb->hShadow);
|
|
}
|
|
}
|
|
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
LeaveShadowCrit();
|
|
return fFoundAtleastOne;
|
|
}
|
|
|
|
VOID
|
|
CreateFakeFind32(
|
|
HSHADOW hDir,
|
|
_WIN32_FIND_DATA *Find32,
|
|
PRX_CONTEXT RxContext,
|
|
BOOLEAN LastComponentInName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a win32 structure for offline use. This is also created for DFS directories
|
|
|
|
Arguments:
|
|
|
|
hDir directory inode where the item is to be created
|
|
|
|
Find32 win32 data to be fixed up
|
|
|
|
RxContext
|
|
|
|
LastComponentInname If this is not true, then this must be a directory
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
KeQuerySystemTime(((PLARGE_INTEGER)(&Find32->ftCreationTime)));
|
|
Find32->ftLastAccessTime = Find32->ftLastWriteTime = Find32->ftCreationTime;
|
|
//already zero Find32->nFileSizeHigh = Find32->nFileSizeLow = 0;
|
|
|
|
if (!LastComponentInName) {
|
|
|
|
// must be a directory....don't know the other attribs without going to get them
|
|
Find32->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
|
|
|
|
} else {
|
|
PNT_CREATE_PARAMETERS cp = &RxContext->Create.NtCreateParameters;
|
|
|
|
Find32->dwFileAttributes = cp->FileAttributes;
|
|
Find32->dwFileAttributes &= (FILE_ATTRIBUTE_READONLY |
|
|
FILE_ATTRIBUTE_HIDDEN |
|
|
FILE_ATTRIBUTE_SYSTEM |
|
|
FILE_ATTRIBUTE_ARCHIVE );
|
|
if (FlagOn(cp->CreateOptions,FILE_DIRECTORY_FILE)) {
|
|
Find32->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
|
|
}
|
|
}
|
|
|
|
MRxSmbCscGenerate83NameAsNeeded(hDir,
|
|
&Find32->cFileName[0],
|
|
&Find32->cAlternateFileName[0]);
|
|
}
|
|
|
|
NTSTATUS
|
|
OkToDeleteObject(
|
|
HSHADOW hDir,
|
|
HSHADOW hShadow,
|
|
_WIN32_FIND_DATA *Find32,
|
|
ULONG uShadowStatus,
|
|
BOOLEAN fDisconnected
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check to see if the file can be deleted.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if Ok to delete, some appropriate status otherwise
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN fHasDescendents = FALSE;
|
|
NTSTATUS LocalStatus = STATUS_SUCCESS;
|
|
|
|
// in disconnected mode, we don't allow deletions of directories
|
|
// which have been cached while online
|
|
// This automatically takes care of the roots
|
|
|
|
if (fDisconnected)
|
|
{
|
|
if (!IsFile(Find32->dwFileAttributes))
|
|
{
|
|
if(!mShadowLocallyCreated(uShadowStatus)) {
|
|
LocalStatus = STATUS_ONLY_IF_CONNECTED;
|
|
goto FINALLY; //bailout;
|
|
}
|
|
}
|
|
|
|
ASSERT(hDir);
|
|
}
|
|
|
|
// if we are deleting a directory, and it has descendents
|
|
// then fail with appropriate error
|
|
if (!IsFile(Find32->dwFileAttributes))
|
|
{
|
|
if(HasDescendentsHShadow(hDir, hShadow, &fHasDescendents) >= 0)
|
|
{
|
|
if (fHasDescendents)
|
|
{
|
|
LocalStatus = STATUS_DIRECTORY_NOT_EMPTY;
|
|
goto FINALLY; //bailout;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto FINALLY; //bailout;
|
|
}
|
|
}
|
|
|
|
// don't delete if readonly
|
|
if (Find32->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
|
|
{
|
|
LocalStatus = STATUS_CANNOT_DELETE;
|
|
goto FINALLY; //bailout;
|
|
|
|
}
|
|
|
|
FINALLY:
|
|
return LocalStatus;
|
|
}
|
|
|
|
int IoctlGetGlobalStatus(
|
|
ULONG SessionId,
|
|
LPGLOBALSTATUS lpGS
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parameters:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
#if 0
|
|
if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_NO_NET)
|
|
DbgPrint("IoctlGetGlobalStatus: FLAG_GLOBALSTATUS_NO_NET\r\n");
|
|
if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_SHARE_DISCONNECTED)
|
|
DbgPrint("IoctlGetGlobalStatus: FLAG_GLOBALSTATUS_SHARE_DISCONNECTED share=%d\r\n",
|
|
sGS.hShareDisconnected);
|
|
#endif
|
|
|
|
// DbgPrint("IOCTL_GETGLOBALSTATUS Transitioning 0x%x sess 0x%x vs 0x%x\n",
|
|
// CscServerEntryBeingTransitioned,
|
|
// SessionId,
|
|
// CscSessionIdCausingTransition);
|
|
|
|
EnterShadowCrit();
|
|
GetShadowSpaceInfo(&(sGS.sST));
|
|
*lpGS = sGS;
|
|
lpGS->uDatabaseErrorFlags = QueryDatabaseErrorFlags();
|
|
if ((sGS.uFlagsEvents & FLAG_GLOBALSTATUS_SHARE_DISCONNECTED) != 0) {
|
|
// Only the session causing a transition will see the SHARE_DISCONNECT bit, and
|
|
// reset it.
|
|
if (SessionId == CscSessionIdCausingTransition)
|
|
sGS.uFlagsEvents = 0;
|
|
else
|
|
lpGS->uFlagsEvents &= ~FLAG_GLOBALSTATUS_SHARE_DISCONNECTED;
|
|
} else {
|
|
sGS.uFlagsEvents = 0;
|
|
}
|
|
LeaveShadowCrit();
|
|
return (1);
|
|
}
|
|
|
|
NTSTATUS
|
|
IoctlGetDebugInfo(
|
|
PRX_CONTEXT RxContext,
|
|
PBYTE InputBuffer,
|
|
ULONG InputBufferLength,
|
|
PBYTE OutputBuffer,
|
|
ULONG OutputBufferLength)
|
|
{
|
|
ULONG Cmd = 0;
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
PIOCTL_GET_DEBUG_INFO_ARG pInfoArg = NULL;
|
|
PBYTE pOutBuf = OutputBuffer;
|
|
KPROCESSOR_MODE RequestorMode;
|
|
|
|
// DbgPrint("In IoctlGetDebugInfo(IP=0x%x,IL=0x%x,OP=0x%x,OL=0x%x)\n",
|
|
// InputBuffer,
|
|
// InputBufferLength,
|
|
// OutputBuffer,
|
|
// OutputBufferLength);
|
|
|
|
if (
|
|
InputBufferLength < sizeof(ULONG)
|
|
||
|
|
OutputBufferLength < FIELD_OFFSET(IOCTL_GET_DEBUG_INFO_ARG, ServerEntryObject)
|
|
) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
RequestorMode = RxContext->CurrentIrp->RequestorMode;
|
|
|
|
if (RequestorMode != KernelMode) {
|
|
try {
|
|
ProbeForRead(InputBuffer, InputBufferLength, 1);
|
|
Cmd = *(PULONG)InputBuffer;
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
NtStatus = STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (NtStatus != STATUS_SUCCESS)
|
|
return NtStatus;
|
|
pOutBuf = RxAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION, OutputBufferLength, RX_MISC_POOLTAG);
|
|
if (pOutBuf == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// DbgPrint("Cmd=%d\n", Cmd);
|
|
if (Cmd == DEBUG_INFO_SERVERLIST) {
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry = NULL;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry = NULL;
|
|
PSMBCEDB_SERVER_ENTRY_ARG pServerEntryArg = NULL;
|
|
PSMBCEDB_NETROOT_ENTRY_ARG pNetRootEntryArg = NULL;
|
|
ULONG Size = 0;
|
|
ULONG ServerEntryCount = 0;
|
|
ULONG NetRootEntryCount = 0;
|
|
PCHAR pCh = NULL;
|
|
ULONG i;
|
|
ULONG j;
|
|
|
|
//
|
|
// Two passes - 1st to check size, 2nd to marshal the info in
|
|
//
|
|
SmbCeAcquireResource();
|
|
try {
|
|
Size = 0;
|
|
ServerEntryCount = 0;
|
|
pServerEntry = SmbCeGetFirstServerEntry();
|
|
while (pServerEntry != NULL) {
|
|
ServerEntryCount++;
|
|
Size += pServerEntry->Name.Length + sizeof(WCHAR) +
|
|
pServerEntry->DomainName.Length + sizeof(WCHAR) +
|
|
pServerEntry->DfsRootName.Length + sizeof(WCHAR) +
|
|
pServerEntry->DnsName.Length + sizeof(WCHAR);
|
|
NetRootEntryCount = 0;
|
|
pNetRootEntry = SmbCeGetFirstNetRootEntry(pServerEntry);
|
|
while (pNetRootEntry != NULL) {
|
|
NetRootEntryCount++;
|
|
Size += pNetRootEntry->Name.Length + sizeof(WCHAR);
|
|
pNetRootEntry = SmbCeGetNextNetRootEntry(pServerEntry,pNetRootEntry);
|
|
}
|
|
Size += sizeof(SMBCEDB_NETROOT_ENTRY_ARG) * NetRootEntryCount;
|
|
pServerEntry = SmbCeGetNextServerEntry(pServerEntry);
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
NtStatus = STATUS_INVALID_PARAMETER;
|
|
}
|
|
// DbgPrint("Sizecheck1: ServerEntryCount=%d,NtStatus=0x%x\n", ServerEntryCount, NtStatus);
|
|
if (NtStatus != STATUS_SUCCESS || ServerEntryCount == 0) {
|
|
SmbCeReleaseResource();
|
|
RtlZeroMemory(pOutBuf, OutputBufferLength);
|
|
pInfoArg = (PIOCTL_GET_DEBUG_INFO_ARG) pOutBuf;
|
|
pInfoArg->Status = NtStatus;
|
|
goto AllDone;
|
|
}
|
|
Size += FIELD_OFFSET(IOCTL_GET_DEBUG_INFO_ARG, ServerEntryObject[ServerEntryCount]);
|
|
// DbgPrint("Sizecheck2: Size=%d(0x%x)\n", Size, Size);
|
|
if (Size > OutputBufferLength) {
|
|
RtlZeroMemory(pOutBuf, OutputBufferLength);
|
|
pInfoArg = (PIOCTL_GET_DEBUG_INFO_ARG) pOutBuf;
|
|
NtStatus = STATUS_BUFFER_TOO_SMALL;
|
|
pInfoArg->Status = NtStatus;
|
|
pInfoArg->EntryCount = Size;
|
|
SmbCeReleaseResource();
|
|
goto AllDone;
|
|
}
|
|
//
|
|
// Marshal it in
|
|
//
|
|
// Start of buffer is the array of server entries
|
|
// Middle are the arrays of netroots
|
|
// End contains all the strings
|
|
//
|
|
RtlZeroMemory(pOutBuf, OutputBufferLength);
|
|
pInfoArg = (PIOCTL_GET_DEBUG_INFO_ARG) pOutBuf;
|
|
pInfoArg->Status = 0;
|
|
pInfoArg->Version = 4;
|
|
pInfoArg->EntryCount = ServerEntryCount;
|
|
pCh = (PCHAR)(pOutBuf + OutputBufferLength);
|
|
pNetRootEntryArg = (PSMBCEDB_NETROOT_ENTRY_ARG)
|
|
&pInfoArg->ServerEntryObject[ServerEntryCount];
|
|
ServerEntryCount = 0;
|
|
pServerEntry = SmbCeGetFirstServerEntry();
|
|
while (pServerEntry != NULL) {
|
|
pServerEntryArg = &pInfoArg->ServerEntryObject[ServerEntryCount];
|
|
pServerEntryArg->ServerStatus = pServerEntry->ServerStatus;
|
|
pServerEntryArg->SecuritySignaturesEnabled = pServerEntry->SecuritySignaturesEnabled;
|
|
pServerEntryArg->CscState = pServerEntry->Server.CscState;
|
|
pServerEntryArg->IsFakeDfsServerForOfflineUse =
|
|
pServerEntry->Server.IsFakeDfsServerForOfflineUse;
|
|
pServerEntryArg->IsPinnedOffline = pServerEntry->Server.IsPinnedOffline;
|
|
pServerEntryArg->pNetRoots = pNetRootEntryArg;
|
|
pCh -= pServerEntry->Name.Length + sizeof(WCHAR);
|
|
pServerEntryArg->Name = (PWCHAR) pCh;
|
|
RtlCopyMemory(pCh, pServerEntry->Name.Buffer, pServerEntry->Name.Length);
|
|
pCh -= pServerEntry->DomainName.Length + sizeof(WCHAR);
|
|
pServerEntryArg->DomainName = (PWCHAR)pCh;
|
|
RtlCopyMemory(pCh, pServerEntry->DomainName.Buffer, pServerEntry->DomainName.Length);
|
|
pCh -= pServerEntry->DfsRootName.Length + sizeof(WCHAR) + 1;;
|
|
pServerEntryArg->DfsRootName = (PWCHAR)pCh;
|
|
RtlCopyMemory(pCh, pServerEntry->DfsRootName.Buffer, pServerEntry->DfsRootName.Length);
|
|
pCh -= pServerEntry->DnsName.Length + sizeof(WCHAR);
|
|
pServerEntryArg->DnsName = (PWCHAR)pCh;
|
|
RtlCopyMemory(pCh, pServerEntry->DnsName.Buffer, pServerEntry->DnsName.Length);
|
|
NetRootEntryCount = 0;
|
|
pNetRootEntry = SmbCeGetFirstNetRootEntry(pServerEntry);
|
|
while (pNetRootEntry != NULL) {
|
|
pNetRootEntryArg->MaximalAccessRights = pNetRootEntry->MaximalAccessRights;
|
|
pNetRootEntryArg->GuestMaximalAccessRights=pNetRootEntry->GuestMaximalAccessRights;
|
|
pNetRootEntryArg->DfsAware = pNetRootEntry->NetRoot.DfsAware;
|
|
pNetRootEntryArg->hShare = pNetRootEntry->NetRoot.sCscRootInfo.hShare;
|
|
pNetRootEntryArg->hRootDir = pNetRootEntry->NetRoot.sCscRootInfo.hRootDir;
|
|
pNetRootEntryArg->ShareStatus = pNetRootEntry->NetRoot.sCscRootInfo.ShareStatus;
|
|
pNetRootEntryArg->CscEnabled = pNetRootEntry->NetRoot.CscEnabled;
|
|
pNetRootEntryArg->CscFlags = pNetRootEntry->NetRoot.CscFlags;
|
|
pNetRootEntryArg->CscShadowable = pNetRootEntry->NetRoot.CscShadowable;
|
|
pNetRootEntryArg->Disconnected = pNetRootEntry->NetRoot.Disconnected;
|
|
pCh -= pNetRootEntry->Name.Length + sizeof(WCHAR);
|
|
pNetRootEntryArg->Name = (PWCHAR)pCh;
|
|
RtlCopyMemory(pCh, pNetRootEntry->Name.Buffer, pNetRootEntry->Name.Length);
|
|
NetRootEntryCount++;
|
|
pNetRootEntryArg++;
|
|
pNetRootEntry = SmbCeGetNextNetRootEntry(pServerEntry,pNetRootEntry);
|
|
}
|
|
pServerEntryArg->NetRootEntryCount = NetRootEntryCount;
|
|
ServerEntryCount++;
|
|
pServerEntry = SmbCeGetNextServerEntry(pServerEntry);
|
|
}
|
|
SmbCeReleaseResource();
|
|
//
|
|
// Now do fixups
|
|
//
|
|
for (i = 0; i < pInfoArg->EntryCount; i++) {
|
|
POINTER_TO_OFFSET(pInfoArg->ServerEntryObject[i].Name, pOutBuf);
|
|
POINTER_TO_OFFSET(pInfoArg->ServerEntryObject[i].DomainName, pOutBuf);
|
|
POINTER_TO_OFFSET(pInfoArg->ServerEntryObject[i].DfsRootName, pOutBuf);
|
|
POINTER_TO_OFFSET(pInfoArg->ServerEntryObject[i].DnsName, pOutBuf);
|
|
for (j = 0; j < pInfoArg->ServerEntryObject[i].NetRootEntryCount; j++)
|
|
POINTER_TO_OFFSET(pInfoArg->ServerEntryObject[i].pNetRoots[j].Name, pOutBuf);
|
|
POINTER_TO_OFFSET(pInfoArg->ServerEntryObject[i].pNetRoots, pOutBuf);
|
|
}
|
|
} else if (Cmd == DEBUG_INFO_CSCFCBSLIST) {
|
|
PIOCTL_GET_DEBUG_INFO_ARG pInfoArg = NULL;
|
|
PMRX_SMB_FCB_ENTRY_ARG pFcbEntryArg = NULL;
|
|
PMRX_SMB_FCB pSmbFcb = NULL;
|
|
PLIST_ENTRY pListEntry = NULL;
|
|
ULONG Size = 0;
|
|
PCHAR pCh = NULL;
|
|
ULONG FcbCount = 0;
|
|
ULONG i;
|
|
|
|
EnterShadowCritRx(RxContext);
|
|
pListEntry = xCscFcbsList.Flink;
|
|
FcbCount = 0;
|
|
while (pListEntry != &xCscFcbsList) {
|
|
FcbCount++;
|
|
pSmbFcb = (PMRX_SMB_FCB)CONTAINING_RECORD(
|
|
pListEntry,
|
|
MRX_SMB_FCB,
|
|
ShadowReverseTranslationLinks);
|
|
Size += pSmbFcb->MinimalCscSmbFcb.uniDfsPrefix.Length + sizeof(WCHAR) +
|
|
pSmbFcb->MinimalCscSmbFcb.uniActualPrefix.Length + sizeof(WCHAR);
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
Size += FIELD_OFFSET(IOCTL_GET_DEBUG_INFO_ARG, FcbEntryObject[FcbCount]);
|
|
if (Size > OutputBufferLength) {
|
|
RtlZeroMemory(pOutBuf, OutputBufferLength);
|
|
pInfoArg = (PIOCTL_GET_DEBUG_INFO_ARG) pOutBuf;
|
|
NtStatus = STATUS_BUFFER_TOO_SMALL;
|
|
pInfoArg->Status = NtStatus;
|
|
pInfoArg->EntryCount = Size;
|
|
LeaveShadowCritRx(RxContext);
|
|
goto AllDone;
|
|
}
|
|
RtlZeroMemory(pOutBuf, OutputBufferLength);
|
|
pInfoArg = (PIOCTL_GET_DEBUG_INFO_ARG) pOutBuf;
|
|
pInfoArg->Status = 0;
|
|
pInfoArg->Version = 1;
|
|
pInfoArg->EntryCount = FcbCount;
|
|
FcbCount = 0;
|
|
pCh = (PCHAR)(pOutBuf + OutputBufferLength);
|
|
pListEntry = xCscFcbsList.Flink;
|
|
while (pListEntry != &xCscFcbsList) {
|
|
pFcbEntryArg = &pInfoArg->FcbEntryObject[FcbCount];
|
|
pSmbFcb = (PMRX_SMB_FCB)CONTAINING_RECORD(
|
|
pListEntry,
|
|
MRX_SMB_FCB,
|
|
ShadowReverseTranslationLinks);
|
|
pFcbEntryArg->MFlags = pSmbFcb->MFlags;
|
|
pFcbEntryArg->Tid = pSmbFcb->Tid;
|
|
pFcbEntryArg->ShadowIsCorrupt = pSmbFcb->ShadowIsCorrupt;
|
|
pFcbEntryArg->hShadow = pSmbFcb->hShadow;
|
|
pFcbEntryArg->hParentDir = pSmbFcb->hParentDir;
|
|
pFcbEntryArg->hShadowRenamed = pSmbFcb->hShadowRenamed;
|
|
pFcbEntryArg->ShadowStatus = pSmbFcb->ShadowStatus;
|
|
pFcbEntryArg->LocalFlags = pSmbFcb->LocalFlags;
|
|
pFcbEntryArg->LastComponentOffset = pSmbFcb->LastComponentOffset;
|
|
pFcbEntryArg->LastComponentLength = pSmbFcb->LastComponentLength;
|
|
pFcbEntryArg->hShare = pSmbFcb->sCscRootInfo.hShare;
|
|
pFcbEntryArg->hRootDir = pSmbFcb->sCscRootInfo.hRootDir;
|
|
pFcbEntryArg->ShareStatus = pSmbFcb->sCscRootInfo.ShareStatus;
|
|
pFcbEntryArg->Flags = pSmbFcb->sCscRootInfo.Flags;
|
|
pCh -= pSmbFcb->MinimalCscSmbFcb.uniDfsPrefix.Length + sizeof(WCHAR);
|
|
pFcbEntryArg->DfsPrefix = (PWCHAR)pCh;
|
|
RtlCopyMemory(
|
|
pCh,
|
|
pSmbFcb->MinimalCscSmbFcb.uniDfsPrefix.Buffer,
|
|
pSmbFcb->MinimalCscSmbFcb.uniDfsPrefix.Length);
|
|
pCh -= pSmbFcb->MinimalCscSmbFcb.uniActualPrefix.Length + sizeof(WCHAR);
|
|
pFcbEntryArg->ActualPrefix = (PWCHAR)pCh;
|
|
RtlCopyMemory(
|
|
pCh,
|
|
pSmbFcb->MinimalCscSmbFcb.uniActualPrefix.Buffer,
|
|
pSmbFcb->MinimalCscSmbFcb.uniActualPrefix.Length);
|
|
FcbCount++;
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
LeaveShadowCritRx(RxContext);
|
|
for (i = 0; i < pInfoArg->EntryCount; i++) {
|
|
POINTER_TO_OFFSET(pInfoArg->FcbEntryObject[i].DfsPrefix, pOutBuf);
|
|
POINTER_TO_OFFSET(pInfoArg->FcbEntryObject[i].ActualPrefix, pOutBuf);
|
|
}
|
|
} else {
|
|
RtlZeroMemory(pOutBuf, OutputBufferLength);
|
|
pInfoArg = (PIOCTL_GET_DEBUG_INFO_ARG) pOutBuf;
|
|
NtStatus = STATUS_INVALID_PARAMETER;
|
|
pInfoArg->Status = NtStatus;
|
|
goto AllDone;
|
|
}
|
|
|
|
AllDone:
|
|
|
|
if (RequestorMode != KernelMode) {
|
|
try {
|
|
ProbeForWrite(OutputBuffer, OutputBufferLength, 1);
|
|
RtlCopyMemory(OutputBuffer, pOutBuf, OutputBufferLength);
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
NtStatus = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if (pOutBuf != OutputBuffer)
|
|
RxFreePool(pOutBuf);
|
|
|
|
return NtStatus;
|
|
}
|