mirror of https://github.com/lianthony/NT4.0
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.
1166 lines
34 KiB
1166 lines
34 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
close.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the NtClose API in the NT redirector.
|
|
|
|
Author:
|
|
|
|
Larry Osterman (LarryO) 16-Jul-1990
|
|
|
|
Revision History:
|
|
|
|
16-Jul-1990 LarryO
|
|
|
|
Created
|
|
|
|
--*/
|
|
|
|
#define INCLUDE_SMB_OPEN_CLOSE
|
|
#define INCLUDE_SMB_READ_WRITE
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include "stdarg.h"
|
|
|
|
NTSTATUS
|
|
RdrProcessDeleteOnClose(
|
|
IN PIRP Irp,
|
|
IN PICB Icb
|
|
);
|
|
|
|
VOID
|
|
RdrFlushFileObjectForClose(
|
|
IN PIRP Irp,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PICB Icb
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, RdrFsdCleanup)
|
|
#pragma alloc_text(PAGE, RdrProcessDeleteOnClose)
|
|
#pragma alloc_text(PAGE, RdrFsdClose)
|
|
#pragma alloc_text(PAGE, RdrFlushFileObjectForClose)
|
|
#endif
|
|
|
|
NTSTATUS
|
|
RdrFsdCleanup (
|
|
IN PFS_DEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the CLEANUP IRP request in the redirector FSD.
|
|
|
|
This routine is called when all references to an existing handle
|
|
have gone away. On a disk file, it will send the close SMB, on a
|
|
file being used for a directory search the search will be closed
|
|
on all other types of files, it is ignored.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies a pointer to the redirector driver object.
|
|
Irp - Supplies a pointer to the IRP to be processed.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The FSD status for this Irp.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
|
PICB Icb = ICB_OF(IrpSp);
|
|
PFCB Fcb = FCB_OF(IrpSp);
|
|
PSHARE_ACCESS ShareAccess;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (DeviceObject == (PFS_DEVICE_OBJECT)BowserDeviceObject) {
|
|
return BowserFsdCleanup(BowserDeviceObject, Irp);
|
|
}
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
//RdrLog(( "cleanup", &Fcb->FileName, 0 ));
|
|
|
|
dprintf(DPRT_CLEANUP|DPRT_DISPATCH, ("RdrFsdCleanup: FileObject: %lx File: %lx (%wZ)\n", FileObject, Fcb, &Fcb->FileName));
|
|
|
|
//
|
|
// Lets assume that close is synchronous....
|
|
//
|
|
|
|
ASSERT(CanFsdWait(Irp)==TRUE);
|
|
|
|
ASSERT(Icb->Signature==STRUCTURE_SIGNATURE_ICB);
|
|
|
|
ASSERT(Fcb->Header.NodeTypeCode==STRUCTURE_SIGNATURE_FCB);
|
|
|
|
RdrReferenceDiscardableCode(RdrFileDiscardableSection);
|
|
|
|
//
|
|
// We must have the file locked for some form of access before we close the file
|
|
//
|
|
|
|
RdrAcquireFcbLock(Fcb, ExclusiveLock, TRUE);
|
|
|
|
if (Icb->Flags & ICB_TCONCREATED) {
|
|
ULONG NumberOfTreeConnections;
|
|
ULONG NumberOfOpenDirectories;
|
|
ULONG NumberOfOpenFiles;
|
|
|
|
|
|
//
|
|
// If this file object caused the default security entry to be set
|
|
// on a file, remove the default security entry.
|
|
//
|
|
|
|
if (Icb->Flags & ICB_SET_DEFAULT_SE) {
|
|
|
|
RdrUnsetDefaultSecurityEntry(Icb->Se);
|
|
|
|
}
|
|
|
|
//
|
|
// Now count the number of tree connections outstanding on this
|
|
// connection (ie count the number of NET USEs to this connection.
|
|
//
|
|
|
|
RdrGetConnectionReferences(Fcb->Connection, NULL, Icb->Se,
|
|
&NumberOfTreeConnections,
|
|
&NumberOfOpenDirectories,
|
|
&NumberOfOpenFiles);
|
|
|
|
//
|
|
// If this is the last tree connection, mark the connection as no
|
|
// longer being tree connected (and thus eligable for enumeration via
|
|
// the Enumerate_Connections FsCtl).
|
|
//
|
|
|
|
if ((NumberOfTreeConnections <= 1)) {
|
|
dprintf(DPRT_CONNECT, ("Close Tree Connection. No connections left, turning off TREECONNECTED bit for \\%wZ\\%wZ\n", &Fcb->Connection->Server->Text, &Fcb->Connection->Text));
|
|
|
|
RdrResetConnectlistFlag(Fcb->Connection, CLE_TREECONNECTED);
|
|
|
|
}
|
|
}
|
|
|
|
if ((Icb->Type == Directory) &&
|
|
FlagOn(Fcb->Connection->Server->Capabilities, DF_NT_SMBS)) {
|
|
|
|
//
|
|
// Mark the file as being forced closed in case a posted notify
|
|
// comes in before we get the FCB lock and is processed after we
|
|
// release the FCB lock.
|
|
//
|
|
|
|
Icb->Flags |= ICB_FORCECLOSED;
|
|
|
|
//
|
|
// Cancel any outstanding change notify requests for this directory.
|
|
//
|
|
|
|
RdrAbandonOutstandingRequests( FileObject );
|
|
|
|
//
|
|
// Release the FCB lock and wait for the outstanding directory
|
|
// control change directories to complete.
|
|
//
|
|
// It is safe to release the FCB lock, because no other requests will
|
|
// be coming in on this handle - there are no user references to this
|
|
// handle, thus there can be no activity on this handle.
|
|
//
|
|
|
|
RdrReleaseFcbLock(Fcb);
|
|
|
|
RdrWaitForAndXBehindOperation(&Icb->u.d.DirCtrlOutstanding);
|
|
|
|
//
|
|
// Re-acquire the FCB lock now.
|
|
//
|
|
|
|
RdrAcquireFcbLock(Fcb, ExclusiveLock, TRUE);
|
|
}
|
|
|
|
|
|
//
|
|
// Mark that there is one less open outstanding on this file.
|
|
//
|
|
|
|
Fcb->NumberOfOpens -= 1 ;
|
|
|
|
ASSERT (Fcb->NumberOfOpens >= 0);
|
|
|
|
//
|
|
// Update the last write time on the file to indicate when
|
|
// we last modified the contents of the file.
|
|
//
|
|
|
|
if ((Icb->Flags & ICB_USER_SET_TIMES) == 0) {
|
|
|
|
if (FileObject->Flags &FO_FILE_MODIFIED ) {
|
|
LARGE_INTEGER CurrentTime;
|
|
|
|
KeQuerySystemTime(&CurrentTime);
|
|
|
|
Fcb->LastWriteTime = CurrentTime;
|
|
Fcb->ChangeTime = CurrentTime;
|
|
}
|
|
|
|
}
|
|
|
|
switch (Icb->Type) {
|
|
|
|
case Unknown:
|
|
InternalError(("Unknown file type passed into Cleanup\n"));
|
|
break;
|
|
|
|
case Redirector:
|
|
|
|
//
|
|
// Stop SmbTrace if it's running and the one closing is the
|
|
// client who started it.
|
|
//
|
|
|
|
SmbTraceStop(FileObject, SMBTRACE_REDIRECTOR);
|
|
|
|
// fall through
|
|
|
|
case NetRoot:
|
|
case Mailslot:
|
|
break;
|
|
|
|
case TreeConnect:
|
|
break;
|
|
|
|
case ServerRoot:
|
|
case PrinterFile:
|
|
case FileOrDirectory:
|
|
break;
|
|
|
|
case Directory:
|
|
|
|
//
|
|
// Complete any notify Irps on this file handle.
|
|
//
|
|
#ifdef NOTIFY
|
|
FsRtlNotifyCleanup( Fcb->Connection->NotifySync,
|
|
&Fcb->Connection->DirNotifyList,
|
|
Icb );
|
|
#endif
|
|
break;
|
|
|
|
case NamedPipe:
|
|
case Com:
|
|
|
|
ASSERT(NT_SUCCESS(RdrIsOperationValid(Icb, IRP_MJ_CLEANUP, FileObject)));
|
|
|
|
// If we have write behind then flush the buffer.
|
|
if ((Icb->Type == NamedPipe) &&
|
|
( Icb->NonPagedFcb->FileType == FileTypeByteModePipe ) &&
|
|
( Icb->u.p.PipeState & SMB_PIPE_NOWAIT )){
|
|
// Prevent 2 threads corrupting Icb->u.p.WriteData
|
|
|
|
if ( !RdrNpAcquireExclusive ( TRUE, &Icb->u.p.WriteData.Semaphore ) ) {
|
|
// Another thread is accessing the pipe handle and !Wait
|
|
InternalError(("Failed Exclusive access with Wait==TRUE"));
|
|
}
|
|
|
|
if ( RdrNpWriteFlush ( Irp, Icb, TRUE ) == STATUS_DRIVER_INTERNAL_ERROR ) {
|
|
InternalError(("CancelTimer failed during close"));
|
|
}
|
|
|
|
RdrNpRelease ( &Icb->u.p.WriteData.Semaphore );
|
|
|
|
ASSERT( Icb->u.p.TimeoutRunning == FALSE ); // All should be idle now
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// We want to send the close on this file from the cleanup IRP.
|
|
//
|
|
// If the file has a blocking read outstanding on this file,
|
|
// the close IRP won't get generated until after the read completes,
|
|
// thus we have to send the close SMB to allow outstanding reads
|
|
// to unwind.
|
|
//
|
|
|
|
if ( !(Icb->Flags & ICB_DEFERREDOPEN) ) {
|
|
NTSTATUS Status;
|
|
// Only send close if we actually opened the remote file
|
|
Status = RdrCloseFile(NULL, Icb, FileObject, TRUE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DiskFile:
|
|
|
|
//
|
|
// If the file has any locks outstanding on the file,
|
|
// make sure they are unlocked.
|
|
//
|
|
|
|
(VOID) FsRtlFastUnlockAll( &Fcb->FileLock,
|
|
FileObject,
|
|
IoGetRequestorProcess( Irp ),
|
|
Icb );
|
|
|
|
//
|
|
// Wait for any unlock behind operations on the file to complete.
|
|
//
|
|
|
|
RdrWaitForAndXBehindOperation(&Icb->u.f.AndXBehind);
|
|
|
|
//
|
|
// Set the CLEANUP_COMPLETE flag so that we don't try to
|
|
// reinitialize the cache map. If a read or write initializes
|
|
// the cache map after we uninitialize it below, the
|
|
// reinitialized cache map will hang around forever, and maybe
|
|
// cause a crash in the cache manager after the file object
|
|
// disappears.
|
|
//
|
|
|
|
FileObject->Flags |= FO_CLEANUP_COMPLETE;
|
|
|
|
//
|
|
// If the file is cached, remove it from the cache.
|
|
//
|
|
|
|
if (Icb->Flags & ICB_HASHANDLE) {
|
|
|
|
if (!FlagOn(FileObject->Flags, FO_TEMPORARY_FILE)) {
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Don't use this IRP for the write flush, since we can't use
|
|
// write behind if we use this IRP.
|
|
//
|
|
|
|
Status = RdrFlushWriteBufferForFile(NULL, Icb, (BOOLEAN)!RdrUseAsyncWriteBehind);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
#if MAGIC_BULLET
|
|
if ( RdrEnableMagic ) {
|
|
RdrSendMagicBullet(NULL);
|
|
DbgPrint( "RDR: About to raise close behind hard error for IRP %x\n", Irp );
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
IoRaiseInformationalHardError(Status, NULL, Irp->Tail.Overlay.Thread);
|
|
|
|
RdrWriteErrorLogEntry(Fcb->Connection->Server,
|
|
IO_ERR_LAYERED_FAILURE,
|
|
EVENT_RDR_CLOSE_BEHIND,
|
|
Status,
|
|
NULL,
|
|
0
|
|
);
|
|
}
|
|
|
|
//
|
|
// Wait for any write behind operations on the file to complete.
|
|
//
|
|
|
|
RdrWaitForWriteBehindOperation(Icb);
|
|
//RdrLog(( "clean2", &Fcb->FileName, 0 ));
|
|
|
|
}
|
|
|
|
//
|
|
// If the file is not oplocked with a batch file oplock, we
|
|
// want to purge the file from the cache, otherwise we want
|
|
// to simply tell the cache manager it can remove the file
|
|
// when it wants to.
|
|
//
|
|
//
|
|
// We also don't want to allow alternate data streams to live
|
|
// in the cache after the file is closed, since we can't easily
|
|
// detect when they are re-opened.
|
|
//
|
|
|
|
if ((Icb->u.f.Flags & ICBF_OPLOCKED) &&
|
|
(Icb->u.f.OplockLevel == SMB_OPLOCK_LEVEL_BATCH) &&
|
|
(Icb->NonPagedFcb->SharingCheckFcb == NULL)) {
|
|
|
|
dprintf(DPRT_CACHE, ("Removing file %lx (Fcb %lx) from the cache (soft)\n", FileObject, Fcb));
|
|
|
|
|
|
//
|
|
// Remove the file from the cache. We call
|
|
// CcUninitializeCacheMap because we want to enable the "Lazy
|
|
// Delete" logic.
|
|
//
|
|
|
|
//RdrLog(( "ccunini2", &Fcb->FileName, 1, 0xffffffff ));
|
|
CcUninitializeCacheMap(FileObject, NULL, NULL);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Flush the file from the cache.
|
|
//
|
|
|
|
//RdrLog(( "rdflshc2", &Fcb->FileName, 0 ));
|
|
RdrFlushFileObjectForClose(Irp, FileObject, Icb);
|
|
|
|
//
|
|
// Otherwise, we need to pull the file from the cache
|
|
// right now.
|
|
//
|
|
|
|
if (CcIsFileCached(FileObject)) {
|
|
|
|
dprintf(DPRT_CACHE, ("Removing file %lx (Fcb %lx) from the cache (hard)\n", FileObject, Fcb));
|
|
|
|
//
|
|
// WARNING: This will release and re-acquire the FCB lock
|
|
//
|
|
|
|
//RdrLog(( "rdunini2", &Fcb->FileName, 0 ));
|
|
RdrUninitializeCacheMap(FileObject, &RdrZero);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Make sure that the cache manager cleans up from this file. Even
|
|
// though it is currently not cached, maybe it was at one time and
|
|
// somehow got PrivateCacheMap set to non NULL.
|
|
//
|
|
|
|
dprintf(DPRT_CACHE, ("Removing file %lx (Fcb %lx) from the cache (soft)\n", FileObject, Fcb));
|
|
|
|
//
|
|
// WARNING: This will release and re-acquire the FCB lock
|
|
//
|
|
|
|
//RdrLog(( "rdunini3", &Fcb->FileName, 1, 0xffffffff ));
|
|
RdrUninitializeCacheMap(FileObject, NULL);
|
|
}
|
|
|
|
//
|
|
// If this is an executable opened over the net, then
|
|
// its possible that the executables image section
|
|
// might still be kept open.
|
|
//
|
|
// Ask MM to flush the section closed. This will fail
|
|
// if the executable in question is still running.
|
|
//
|
|
|
|
//RdrLog(( "mmflush2", &Fcb->FileName, 1, MmFlushForWrite ));
|
|
MmFlushImageSection(&Fcb->NonPagedFcb->SectionObjectPointer,
|
|
MmFlushForWrite);
|
|
|
|
//
|
|
// There is also a possiblity that there is a user section
|
|
// open on this file, in which case we need to force the
|
|
// section closed to make sure that they are cleaned up.
|
|
//
|
|
|
|
//RdrLog(( "mmforce2", &Fcb->FileName, 1, TRUE ));
|
|
MmForceSectionClosed(&Fcb->NonPagedFcb->SectionObjectPointer, TRUE);
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// This file has been invalidated.
|
|
//
|
|
// Check to see if it is still in the cache, and if it is,
|
|
// blow it away.
|
|
//
|
|
// This can happen if a read request comes in while the redir
|
|
// is tearing down a connection (in RdrInvalidateConnectionFiles),
|
|
// and re-initializes the cache map.
|
|
//
|
|
|
|
//
|
|
// WARNING: This will release and re-acquire the FCB lock
|
|
//
|
|
|
|
//RdrLog(( "rdunini3", &Fcb->FileName, 0 ));
|
|
RdrUninitializeCacheMap(FileObject, &RdrZero);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
dprintf(DPRT_CLEANUP, ("Unsupported file type passed into RdrFsdCleanup\n"));
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Remove the sharing semantics for this file, it's now closed
|
|
//
|
|
|
|
if ((Icb->Type != NamedPipe) &&
|
|
(Icb->Type != Mailslot) &&
|
|
(Icb->Type != PrinterFile) &&
|
|
(Icb->Type != Com)) {
|
|
|
|
if (Icb->NonPagedFcb->SharingCheckFcb != NULL) {
|
|
ShareAccess = &Icb->NonPagedFcb->SharingCheckFcb->ShareAccess;
|
|
} else {
|
|
ShareAccess = &Icb->Fcb->ShareAccess;
|
|
}
|
|
|
|
dprintf(DPRT_CLEANUP, ("Removing share access for file object %08lx, Fcb = %08lx, ShareAccess=%08lx\n", FileObject, Fcb, ShareAccess));
|
|
|
|
RdrRemoveShareAccess(FileObject, ShareAccess);
|
|
}
|
|
|
|
//
|
|
// If there are no other user handles to this file, and the file is still
|
|
// oplocked, then mark a timeout for the file to expire.
|
|
//
|
|
|
|
if ((Fcb->NumberOfOpens == 0)
|
|
|
|
&&
|
|
|
|
(Icb->u.f.Flags & ICBF_OPLOCKED)) {
|
|
|
|
RdrReleaseFcbLock(Fcb);
|
|
|
|
RdrSetDormantCachedFile(Fcb);
|
|
|
|
} else {
|
|
|
|
RdrReleaseFcbLock(Fcb);
|
|
|
|
}
|
|
|
|
dprintf(DPRT_CLEANUP, ("Completing IRP with status= %X\n", STATUS_SUCCESS));
|
|
|
|
RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
|
|
|
|
RdrCompleteRequest(Irp, STATUS_SUCCESS);
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
}
|
|
|
|
VOID
|
|
RdrFlushFileObjectForClose(
|
|
IN PIRP Irp,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PICB Icb
|
|
)
|
|
{
|
|
IO_STATUS_BLOCK IoSb;
|
|
PFCB Fcb = Icb->Fcb;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If this is not a temporary file, we need to flush the data
|
|
// on the file on close.
|
|
//
|
|
// If it IS a temporary file, we can skip doing the flush, and
|
|
// toss any write behind data.
|
|
//
|
|
|
|
if (!FlagOn(FileObject->Flags, FO_TEMPORARY_FILE)) {
|
|
|
|
//
|
|
// First flush the file's dirty data from the cache.
|
|
//
|
|
|
|
//RdrLog(( "ccflush3", &Fcb->FileName, 1, 0xffffffff ));
|
|
CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &IoSb);
|
|
|
|
if (!NT_SUCCESS(IoSb.Status)) {
|
|
|
|
#if MAGIC_BULLET
|
|
if ( RdrEnableMagic ) {
|
|
RdrSendMagicBullet(NULL);
|
|
DbgPrint( "RDR: About to raise close behind lost data hard error for IRP %x\n", Irp );
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
IoRaiseInformationalHardError(IoSb.Status, NULL,
|
|
Irp->Tail.Overlay.Thread);
|
|
|
|
KdPrint(("RDR: Data lost on close behind: %X\n", IoSb.Status));
|
|
|
|
RdrWriteErrorLogEntry(
|
|
Fcb->Connection->Server,
|
|
IO_ERR_LAYERED_FAILURE,
|
|
EVENT_RDR_CLOSE_BEHIND,
|
|
IoSb.Status,
|
|
NULL,
|
|
0
|
|
);
|
|
} else {
|
|
|
|
//
|
|
// Serialize behind paging I/O to ensure flush is done.
|
|
//
|
|
|
|
ExAcquireResourceExclusive(Icb->Fcb->Header.PagingIoResource, TRUE);
|
|
ExReleaseResource(Icb->Fcb->Header.PagingIoResource);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RdrFsdClose (
|
|
IN PFS_DEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the NtClose request in the redirector FSD.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies a pointer to the redirector driver object.
|
|
Irp - Supplies a pointer to the IRP to be processed.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The FSD status for this Irp.
|
|
|
|
Note:
|
|
There is a race condition with close behind here.
|
|
|
|
The problem is as follows:
|
|
|
|
When we close behind a file, it is possible that the close will
|
|
complete before the CLOSE IRP comes into the redirector. In order to
|
|
close the race condition, we have to reference the file object to prevent
|
|
the close from coming into the redirector until the file has been completely
|
|
closed.
|
|
|
|
We also have to have the file locked before the close comes in because it
|
|
is possible that another open will come in for this file before the close is
|
|
processed. If this is the case, then the sharing modes of the two openers
|
|
may be incompatable, so we want to block the open from proceeding until the
|
|
close has finally completed.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
|
PICB Icb = ICB_OF(IrpSp);
|
|
PFCB Fcb = FCB_OF(IrpSp);
|
|
PSECURITY_ENTRY Se;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN DereferenceDiscardableCode = TRUE;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (DeviceObject == (PFS_DEVICE_OBJECT)BowserDeviceObject) {
|
|
return BowserFsdClose(BowserDeviceObject, Irp);
|
|
}
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
//RdrLog(( "close", &Fcb->FileName, 0 ));
|
|
|
|
Se = Icb->Se;
|
|
|
|
dprintf(DPRT_DISPATCH|DPRT_CLOSE, ("RdrFsdClose, FileObject: %08lx Fcb:%08lx (%wZ)\n", FileObject, Fcb, &Fcb->FileName));
|
|
|
|
ASSERT(CanFsdWait(Irp)==TRUE);
|
|
|
|
ASSERT(Icb->Fcb == Fcb);
|
|
|
|
ASSERT(Icb->Signature==STRUCTURE_SIGNATURE_ICB);
|
|
|
|
ASSERT(Fcb->Header.NodeTypeCode==STRUCTURE_SIGNATURE_FCB);
|
|
|
|
//
|
|
// We must have the file locked for some form of access
|
|
// before we initiate the close of the file, because
|
|
// of the problem described in RdrFsdCleanup.
|
|
//
|
|
// This will have the side effect of waiting for any
|
|
// write&unlock operations to complete
|
|
//
|
|
|
|
|
|
RdrAcquireFcbLock(Fcb, ExclusiveLock, TRUE);
|
|
|
|
try {
|
|
|
|
RdrReferenceDiscardableCode(RdrFileDiscardableSection);
|
|
|
|
switch (Icb->Type) {
|
|
|
|
case Redirector:
|
|
case NetRoot:
|
|
|
|
//
|
|
// All that is necessary to do when closing a redirector file
|
|
// is to free up the ICB pointer.
|
|
//
|
|
|
|
RdrFreeIcb(Icb);
|
|
|
|
if (Se != NULL) {
|
|
RdrDereferenceSecurityEntryForFile(Se);
|
|
}
|
|
|
|
RdrDereferenceFcb(Irp, Fcb->NonPagedFcb, TRUE, 0, Se);
|
|
|
|
if (Se != NULL) {
|
|
RdrDereferenceSecurityEntry(Se->NonPagedSecurityEntry);
|
|
}
|
|
|
|
DereferenceDiscardableCode = FALSE;
|
|
|
|
try_return(Status = STATUS_SUCCESS);
|
|
break;
|
|
|
|
case Directory:
|
|
case DiskFile:
|
|
|
|
if (Icb->Flags & ICB_HASHANDLE ) {
|
|
LONG NumberOfOpenFiles = 0;
|
|
LONG TotalNumberOfFiles = 0;
|
|
PLIST_ENTRY IcbEntry;
|
|
|
|
|
|
|
|
#ifdef _CAIRO_ // OFS STORAGE
|
|
//
|
|
// Release any pending searches. We do this on all file
|
|
// and dir handles since OFS supports enumeration of embeddings
|
|
// through file handles
|
|
//
|
|
|
|
Status = RdrFindClose (Irp, Icb, Icb->u.d.Scb);
|
|
#else
|
|
//
|
|
// If this is a directory, close any outstanding searches on
|
|
// the file.
|
|
//
|
|
|
|
if (Icb->Type == Directory) {
|
|
Status = RdrFindClose(Irp, Icb, Icb->u.d.Scb);
|
|
}
|
|
#endif
|
|
//
|
|
// Count the number of ICB's associated with this FCB that have
|
|
// the same file id as the one we are trying to close. If it is the
|
|
// only file with that file id, then it is safe to close this file.
|
|
//
|
|
// If there are other ICB's that share this file's ICB, then it is unsafe
|
|
// to close the remote file, so we should simply close the file.
|
|
//
|
|
|
|
for (IcbEntry = Fcb->InstanceChain.Flink ;
|
|
IcbEntry != &Fcb->InstanceChain ;
|
|
IcbEntry = IcbEntry->Flink) {
|
|
PICB IcbToFlush = CONTAINING_RECORD(IcbEntry, ICB, InstanceNext);
|
|
|
|
if ((IcbToFlush->Flags & ICB_HASHANDLE)
|
|
|
|
&&
|
|
|
|
(IcbToFlush->FileId == Icb->FileId)) {
|
|
|
|
NumberOfOpenFiles += 1;
|
|
}
|
|
|
|
if (IcbToFlush->Flags & ICB_HASHANDLE) {
|
|
|
|
TotalNumberOfFiles += 1;
|
|
|
|
}
|
|
}
|
|
|
|
ASSERT (NumberOfOpenFiles >= 1);
|
|
|
|
if (NumberOfOpenFiles == 1) {
|
|
|
|
//
|
|
// If this is not a level II oplock, then flag that there
|
|
// are going to be no more oplock breaks on this file.
|
|
//
|
|
|
|
if (Fcb->NonPagedFcb->OplockLevel != SMB_OPLOCK_LEVEL_II) {
|
|
Fcb->NonPagedFcb->Flags &= ~FCB_OPLOCKED;
|
|
}
|
|
|
|
//
|
|
// We want to turn of the HasOplockHandle if
|
|
// we're closing the oplocked file id.
|
|
//
|
|
|
|
if (Icb->FileId == Fcb->NonPagedFcb->OplockedFileId) {
|
|
Fcb->NonPagedFcb->Flags &= ~FCB_HASOPLOCKHANDLE;
|
|
|
|
//
|
|
// Blast the oplocked file id on the file - it's
|
|
// no longer good.
|
|
//
|
|
|
|
Fcb->NonPagedFcb->OplockedFileId = 0;
|
|
|
|
//
|
|
// If there's an oplocked security entry for this
|
|
// file, dereference it and reset the pointer - it
|
|
// can no longer be good.
|
|
//
|
|
|
|
if (Fcb->NonPagedFcb->OplockedSecurityEntry != NULL) {
|
|
RdrDereferenceSecurityEntry(Fcb->NonPagedFcb->OplockedSecurityEntry);
|
|
|
|
Fcb->NonPagedFcb->OplockedSecurityEntry = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Status = RdrCloseFile(Irp, Icb, FileObject, TRUE);
|
|
|
|
if ((TotalNumberOfFiles == 1) &&
|
|
(FlagOn(Fcb->NonPagedFcb->Flags,FCB_DELETEONCLOSE))) {
|
|
RdrProcessDeleteOnClose( Irp, Icb );
|
|
}
|
|
#ifdef NOTIFY
|
|
//
|
|
// We call the notify package to report that the
|
|
// attribute and last modification times have both
|
|
// changed.
|
|
//
|
|
|
|
FsRtlNotifyReportChange( Fcb->Connection->NotifySync,
|
|
&Fcb->Connection->DirNotifyList,
|
|
(PANSI_STRING)&Fcb->FileName,
|
|
(PANSI_STRING)&Fcb->LastFileName,
|
|
FILE_NOTIFY_CHANGE_LAST_WRITE );
|
|
|
|
#endif
|
|
if (Icb->Flags & ICB_SETATTRONCLOSE) {
|
|
FILE_BASIC_INFORMATION BasicInfo;
|
|
|
|
RtlZeroMemory(&BasicInfo, sizeof(BasicInfo));
|
|
|
|
BasicInfo.FileAttributes = Icb->Fcb->Attribute;
|
|
|
|
Status = RdrSetFileAttributes(Irp, Icb, &BasicInfo);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RdrWriteErrorLogEntry(
|
|
Fcb->Connection->Server,
|
|
IO_ERR_LAYERED_FAILURE,
|
|
EVENT_RDR_DELAYED_SET_ATTRIBUTES_FAILED,
|
|
Status,
|
|
Icb->Fcb->FileName.Buffer,
|
|
Icb->Fcb->FileName.Length
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// RdrUnlinkAndFreeIcb will release the FCB lock.
|
|
//
|
|
|
|
RdrUnlinkAndFreeIcb (Irp, Icb, FileObject);
|
|
|
|
try_return(Status);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Some other file handle is active on this file,
|
|
// so we just want to unlink this from the FCB.
|
|
//
|
|
|
|
RdrUnlinkAndFreeIcb(Irp, Icb, FileObject);
|
|
|
|
try_return(Status = STATUS_SUCCESS);
|
|
|
|
break;
|
|
}
|
|
|
|
ASSERT (FALSE);
|
|
}
|
|
|
|
if (Icb->Flags & ICB_DELETEONCLOSE) {
|
|
Status = RdrProcessDeleteOnClose(Irp, Icb);
|
|
}
|
|
|
|
//
|
|
// NOTE: FALL THROUGH
|
|
//
|
|
// On disk files, if there is no associated remote file id, this means that
|
|
// we want to simply free up the storage associated with the file.
|
|
//
|
|
|
|
case TreeConnect:
|
|
//
|
|
// We are closing a tree connection with a special IPC connection
|
|
// on it. Release the synchronization event protecting
|
|
// access to the special IPC connection.
|
|
//
|
|
|
|
// if (Se->Transport != NULL) {
|
|
// ASSERT (Se->Flags & SE_USE_SPECIAL_IPC);
|
|
//
|
|
// KeSetEvent(&Fcb->Connection->Server->SpecialIpcSynchronizationLock, 0, FALSE);
|
|
// }
|
|
|
|
case Mailslot:
|
|
case ServerRoot:
|
|
|
|
//
|
|
// If this is a directory, close any outstanding searches on
|
|
// the file.
|
|
//
|
|
|
|
if (Icb->Type == Directory || Icb->Type == TreeConnect) {
|
|
Status = RdrFindClose(Irp, Icb, Icb->u.d.Scb);
|
|
}
|
|
|
|
//
|
|
// All that the redirector has to do to close a tree connection
|
|
// is to remove the reference to the connection structure.
|
|
//
|
|
|
|
if (FlagOn(Icb->Flags, ICB_TCONCREATED)) {
|
|
DereferenceDiscardableCode = FALSE;
|
|
}
|
|
|
|
RdrUnlinkAndFreeIcb (Irp, Icb, FileObject);
|
|
|
|
try_return(Status = STATUS_SUCCESS);
|
|
|
|
break;
|
|
|
|
case PrinterFile:
|
|
|
|
ASSERT (!FlagOn(Icb->Flags, ICB_DELETEONCLOSE));
|
|
ASSERT(NT_SUCCESS(RdrIsOperationValid(Icb, IRP_MJ_CLOSE, FileObject)));
|
|
|
|
//
|
|
// We want to send the close on this file from the cleanup IRP.
|
|
//
|
|
// If the file has a blocking read outstanding on this file,
|
|
// the close IRP won't get generated until after the read completes,
|
|
// thus we have to send the close SMB to allow outstanding reads
|
|
// to unwind.
|
|
//
|
|
|
|
if ( !(Icb->Flags & ICB_DEFERREDOPEN) ) {
|
|
NTSTATUS Status;
|
|
// Only send close if we actually opened the remote file
|
|
Status = RdrCloseFile(NULL, Icb, FileObject, TRUE);
|
|
|
|
}
|
|
|
|
RdrUnlinkAndFreeIcb (Irp, Icb, FileObject);
|
|
|
|
try_return(Status);
|
|
|
|
break;
|
|
|
|
case NamedPipe:
|
|
case Com:
|
|
case FileOrDirectory:
|
|
|
|
ASSERT (!FlagOn(Icb->Flags, ICB_DELETEONCLOSE));
|
|
|
|
//
|
|
// All that the redirector has to do to close one of these files
|
|
// is to remove the reference to the connection structure.
|
|
//
|
|
|
|
RdrUnlinkAndFreeIcb (Irp, Icb, FileObject);
|
|
|
|
try_return(Status);
|
|
|
|
break;
|
|
|
|
case Unknown:
|
|
InternalError(("Unknown file type passed into NtCloseFile\n"));
|
|
|
|
try_return(Status = STATUS_INVALID_DEVICE_REQUEST);
|
|
|
|
break;
|
|
|
|
default:
|
|
InternalError(("Unsupported file type passed into RdrFsdClose\n"));
|
|
|
|
try_return(Status = STATUS_INVALID_DEVICE_REQUEST);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
try_exit:NOTHING;
|
|
} finally {
|
|
|
|
dprintf(DPRT_CLOSE, ("Completing IRP with status= %X\n", Status));
|
|
|
|
if (Status != STATUS_PENDING) {
|
|
|
|
//
|
|
// If the close failed, we want to unwind from the close operation
|
|
// and release the resources associated with the file, since
|
|
// we can't recover properly anyway.
|
|
//
|
|
|
|
RdrCompleteRequest(Irp, Status);
|
|
}
|
|
|
|
RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
|
|
|
|
if (DereferenceDiscardableCode) {
|
|
RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
|
|
}
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return Status;
|
|
|
|
if (DeviceObject);
|
|
}
|
|
|
|
NTSTATUS
|
|
RdrProcessDeleteOnClose(
|
|
IN PIRP Irp,
|
|
IN PICB Icb
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PFCB fcb=Icb->Fcb;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If this is a deleteonclose file handle, and the FCB in question
|
|
// exists, then we want to delete the file now.
|
|
//
|
|
|
|
if (!FlagOn(fcb->NonPagedFcb->Flags, FCB_DOESNTEXIST)) {
|
|
if (Icb->Type == DiskFile) {
|
|
status = RdrDeleteFile(
|
|
Irp, &fcb->FileName,
|
|
BooleanFlagOn(Icb->NonPagedFcb->Flags, FCB_DFSFILE),
|
|
fcb->Connection, Icb->Se);
|
|
} else if (Icb->Type == Directory) {
|
|
status = RdrGenericPathSmb(Irp,
|
|
SMB_COM_DELETE_DIRECTORY,
|
|
BooleanFlagOn(Icb->NonPagedFcb->Flags, FCB_DFSFILE),
|
|
&fcb->FileName,
|
|
fcb->Connection,
|
|
Icb->Se);
|
|
} else {
|
|
InternalError(("Unknown file type passed into RdrProcessDeleteOnClose: %d\n", Icb->Type));
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
RdrWriteErrorLogEntry(fcb->Connection->Server,
|
|
IO_ERR_LAYERED_FAILURE,
|
|
EVENT_RDR_DELETEONCLOSE_FAILED,
|
|
status,
|
|
Icb->Fcb->FileName.Buffer,
|
|
Icb->Fcb->FileName.Length
|
|
);
|
|
} else {
|
|
|
|
//
|
|
// The file specified doesn't exist anymore.
|
|
//
|
|
|
|
fcb->NonPagedFcb->Flags |= FCB_DOESNTEXIST;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#if DBG || RDRDBG_LOG
|
|
#define RDR_LOG_MAX 2048
|
|
#define RDR_LOG_EVENT_LENGTH 8
|
|
#define RDR_LOG_DWORDS_LENGTH 12
|
|
#define RDR_LOG_TEXT_LENGTH 4
|
|
|
|
ULONG RdrLogIndex = 0;
|
|
typedef struct {
|
|
UCHAR Event[RDR_LOG_EVENT_LENGTH];
|
|
ULONG Dwords[RDR_LOG_DWORDS_LENGTH];
|
|
WCHAR Text[RDR_LOG_TEXT_LENGTH];
|
|
} RDR_LOG, *PRDR_LOG;
|
|
RDR_LOG RdrLogBuffer[RDR_LOG_MAX] = {0};
|
|
|
|
BOOLEAN RdrLogDisabled = FALSE;
|
|
|
|
VOID
|
|
RdrLog2 (
|
|
IN PSZ Event,
|
|
IN PUNICODE_STRING Text,
|
|
IN ULONG DwordCount,
|
|
...
|
|
)
|
|
{
|
|
PRDR_LOG log;
|
|
KIRQL oldIrql;
|
|
PWCH buff;
|
|
ULONG len;
|
|
ULONG index;
|
|
PULONG dword;
|
|
va_list arglist;
|
|
|
|
if (RdrLogDisabled) return;
|
|
|
|
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
|
log = &RdrLogBuffer[RdrLogIndex];
|
|
if ( ++RdrLogIndex >= RDR_LOG_MAX ) {
|
|
RdrLogIndex = 0;
|
|
}
|
|
KeLowerIrql( oldIrql );
|
|
|
|
RtlZeroMemory( log, sizeof(RDR_LOG) );
|
|
|
|
strncpy( log->Event, Event, RDR_LOG_EVENT_LENGTH );
|
|
|
|
if ( Text != NULL ) {
|
|
buff = Text->Buffer;
|
|
len = Text->Length/sizeof(WCHAR);
|
|
if ( len > RDR_LOG_TEXT_LENGTH ) {
|
|
buff += len - RDR_LOG_TEXT_LENGTH;
|
|
len = RDR_LOG_TEXT_LENGTH;
|
|
}
|
|
wcsncpy( log->Text, buff, len );
|
|
}
|
|
|
|
va_start( arglist, DwordCount );
|
|
dword = log->Dwords;
|
|
for ( index = 0; index < MIN(DwordCount,RDR_LOG_DWORDS_LENGTH); index++ ) {
|
|
*dword++ = va_arg( arglist, ULONG );
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|