3428 lines
97 KiB
C
3428 lines
97 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1990 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
smbfuncs.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements SMB specific functions in the NT redirector.
|
|||
|
|
|||
|
It is intended to provide "core functionality" in a single location.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Larry Osterman (LarryO) 11-Sep-1990
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
11-Sep-1990 LarryO
|
|||
|
|
|||
|
Created
|
|||
|
|
|||
|
--*/
|
|||
|
#define INCLUDE_SMB_MISC
|
|||
|
#define INCLUDE_SMB_TRANSACTION
|
|||
|
#define INCLUDE_SMB_QUERY_SET
|
|||
|
#define INCLUDE_SMB_DIRECTORY
|
|||
|
#define INCLUDE_SMB_OPEN_CLOSE
|
|||
|
#define INCLUDE_SMB_FILE_CONTROL
|
|||
|
#define INCLUDE_SMB_LOCK
|
|||
|
#define INCLUDE_SMB_PRINT
|
|||
|
#define INCLUDE_SMB_READ_WRITE
|
|||
|
|
|||
|
#include "precomp.h"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
typedef struct _GetExpandedAttribs {
|
|||
|
TRANCEIVE_HEADER Header;
|
|||
|
PQSFILEATTRIB Attributes;
|
|||
|
} GETEXPANDEDATTRIBS, *PGETEXPANDEDATTRIBS;
|
|||
|
|
|||
|
typedef struct _QEndOfFileContext {
|
|||
|
TRANCEIVE_HEADER Header;
|
|||
|
ULONG FileSize;
|
|||
|
} QENDOFFILECONTEXT, *PQENDOFFILECONTEXT;
|
|||
|
|
|||
|
typedef
|
|||
|
struct _CloseContext {
|
|||
|
TRANCEIVE_HEADER Header; // Header of transaction structure.
|
|||
|
WORK_QUEUE_ITEM WorkHeader; // Header for passing to work thread
|
|||
|
PMPX_ENTRY MpxEntry; // MPX table associated with close.
|
|||
|
PICB Icb; // ICB to close.
|
|||
|
PFCB Fcb; // Fcb associated with ICB
|
|||
|
PFILE_OBJECT FileObject; // File object of file to close.
|
|||
|
PSMB_BUFFER SmbBuffer; // SMB buffer sent with close request
|
|||
|
#ifdef IMPLEMENT_CLOSE_BEHIND
|
|||
|
BOOLEAN SynchronousClose; // TRUE if close is synchronous.
|
|||
|
#endif
|
|||
|
PIRP Irp; // Irp to complete.
|
|||
|
} CLOSE_CONTEXT, *PCLOSE_CONTEXT;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
typedef struct _StatContext {
|
|||
|
TRANCEIVE_HEADER Header; // Standard NetTranceive context header
|
|||
|
ULONG FileAttributes; // Remote File attributes.
|
|||
|
LARGE_INTEGER LastWriteTime;
|
|||
|
BOOLEAN FileIsDirectory; // TRUE iff file specified is a directory
|
|||
|
} STATCONTEXT, *PSTATCONTEXT;
|
|||
|
|
|||
|
|
|||
|
typedef struct _DSKATTRIBCONTEXT {
|
|||
|
TRANCEIVE_HEADER Header; // Standard NetTranceive context header
|
|||
|
ULONG TotalAllocationUnits; // Total Number of clusters
|
|||
|
ULONG AvailableAllocationUnits; // Available clusters
|
|||
|
ULONG SectorsPerAllocationUnit; // Sectors per cluster
|
|||
|
ULONG BytesPerSector; // Bytes per sector
|
|||
|
} DSKATTRIBCONTEXT, *PDSKATTRIBCONTEXT;
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
VOID
|
|||
|
CompleteCloseOperation(
|
|||
|
PVOID Ctx
|
|||
|
);
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
STANDARD_CALLBACK_HEADER(
|
|||
|
CloseCallback
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
STANDARD_CALLBACK_HEADER(
|
|||
|
GetAttribs2Callback
|
|||
|
);
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
STANDARD_CALLBACK_HEADER (
|
|||
|
GetAttribsCallback
|
|||
|
);
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
STANDARD_CALLBACK_HEADER (
|
|||
|
DoesFileExistCallback
|
|||
|
);
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
STANDARD_CALLBACK_HEADER (
|
|||
|
QueryEndOfFileCallback
|
|||
|
);
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
STANDARD_CALLBACK_HEADER (
|
|||
|
GetAttribsCallback
|
|||
|
);
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
STANDARD_CALLBACK_HEADER (
|
|||
|
QueryDiskAttributesCallback
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, RdrCloseFile)
|
|||
|
#pragma alloc_text(PAGE, CompleteCloseOperation)
|
|||
|
#pragma alloc_text(PAGE, RdrCloseFileFromFileId)
|
|||
|
#pragma alloc_text(PAGE, RdrDeleteFile)
|
|||
|
#pragma alloc_text(PAGE, RdrDoesFileExist)
|
|||
|
#pragma alloc_text(PAGE, RdrGenericPathSmb)
|
|||
|
#pragma alloc_text(PAGE, RdrQueryDiskAttributes)
|
|||
|
#pragma alloc_text(PAGE, RdrQueryEndOfFile)
|
|||
|
#pragma alloc_text(PAGE, RdrDetermineFileAllocation)
|
|||
|
#pragma alloc_text(PAGE, RdrQueryFileAttributes)
|
|||
|
#pragma alloc_text(PAGE3FILE, QueryDiskAttributesCallback)
|
|||
|
#pragma alloc_text(PAGE, RdrRenameFile)
|
|||
|
#pragma alloc_text(PAGE, RdrSetEndOfFile)
|
|||
|
#pragma alloc_text(PAGE, RdrSetFileAttributes)
|
|||
|
|
|||
|
#pragma alloc_text(PAGE3FILE, CloseCallback)
|
|||
|
#pragma alloc_text(PAGE3FILE, DoesFileExistCallback)
|
|||
|
#pragma alloc_text(PAGE3FILE, QueryEndOfFileCallback)
|
|||
|
#pragma alloc_text(PAGE3FILE, GetAttribsCallback)
|
|||
|
#pragma alloc_text(PAGE3FILE, GetAttribs2Callback)
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// RdrCloseFile
|
|||
|
//
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RdrCloseFile (
|
|||
|
IN PIRP Irp OPTIONAL,
|
|||
|
IN PICB Icb,
|
|||
|
IN PFILE_OBJECT FileObject,
|
|||
|
IN BOOLEAN WaitForCompletion
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine processes an NtClose request. It will close a file
|
|||
|
created remotely.
|
|||
|
|
|||
|
Since close cannot fail under NT, the redirector implements a
|
|||
|
performance enhancement known as "Close Behind". The close request
|
|||
|
will be transmitted from the Fsc routine, but the redirector will not
|
|||
|
wait for it to complete.
|
|||
|
|
|||
|
Instead, the redirector's completion routine will "kick" a generic
|
|||
|
worker thread to perform the completion of the request.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PIRP Irp - Supplies an IRP to use for the close operation.
|
|||
|
|
|||
|
IN PICB Icb - Supplies the ICB to close.
|
|||
|
|
|||
|
IN BOOLEAN WaitForCompletion - TRUE if this is a synchronous close
|
|||
|
operation
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Note:
|
|||
|
If the close is a synchronous operation, then it is assumed that the caller
|
|||
|
will unlink the ICB and return quota for the structures associated with
|
|||
|
the ICB. This is because the close is probably NOT being generated via
|
|||
|
a CLEANUP IRP, but instead is coming from an operation such as
|
|||
|
NtSetInformationFile.
|
|||
|
|
|||
|
Note:
|
|||
|
The Irp parameter passed in is treated somewhat specially. If an
|
|||
|
IRP is specified, this code assumes that it is for a close request.
|
|||
|
If there is no IRP specified, but close behind is requested, we need to
|
|||
|
reference the file object to prevent the close IRP from being generated
|
|||
|
before the close SMB completes.
|
|||
|
|
|||
|
|
|||
|
Basically, there are three cases for close:
|
|||
|
|
|||
|
1) Synchonous close.
|
|||
|
In this case, if an IRP is provided, it is used to send the close SMB,
|
|||
|
otherwise an IRP is allocated for the request.
|
|||
|
|
|||
|
2) Asynchronous close, with an IRP supplied.
|
|||
|
In this case, the request is assumed to be in response to a close
|
|||
|
IRP, in which case, the close code will deallocate the ICB upon
|
|||
|
completion of the close request.
|
|||
|
|
|||
|
3) Asynchronous close, no IRP supplied.
|
|||
|
In this case, the request is assuemd to be in response to a cleanup
|
|||
|
IRP, in which case, the close code will reference the file object
|
|||
|
before sending the close, and will dereference it upon completion of
|
|||
|
the close SMB.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSMB_BUFFER SmbBuffer = NULL;
|
|||
|
PSMB_HEADER SmbHeader;
|
|||
|
PCLOSE_CONTEXT CloseContext = NULL;
|
|||
|
NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
dprintf(DPRT_CLOSE|DPRT_CACHE, ("RdrCloseFile, FileObject: %08lx, FileId: %lx\n", FileObject, Icb->FileId));
|
|||
|
|
|||
|
ASSERT(FileObject->FsContext2 == Icb);
|
|||
|
|
|||
|
#if DBG
|
|||
|
if (ARGUMENT_PRESENT(Irp) && !WaitForCompletion) {
|
|||
|
ASSERT(IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_CLOSE);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
if ((Icb->Flags & ICB_HASHANDLE) == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// With no handle there is no need to tell the server of the close!
|
|||
|
//
|
|||
|
|
|||
|
return STATUS_SUCCESS ;
|
|||
|
}
|
|||
|
|
|||
|
CloseContext = ALLOCATE_POOL(NonPagedPool, sizeof(CLOSE_CONTEXT), POOL_CLOSECTX);
|
|||
|
|
|||
|
if (CloseContext==NULL) {
|
|||
|
goto CloseFailed;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the close context structure for the close behind operation.
|
|||
|
//
|
|||
|
|
|||
|
CloseContext->Header.Type = CONTEXT_CLOSE;
|
|||
|
CloseContext->Header.TransferSize = sizeof(PREQ_CLOSE) + sizeof(PRESP_CLOSE);
|
|||
|
ExInitializeWorkItem(&CloseContext->WorkHeader, CompleteCloseOperation, CloseContext);
|
|||
|
CloseContext->MpxEntry = NULL;
|
|||
|
CloseContext->Icb = Icb;
|
|||
|
CloseContext->Fcb = NULL;
|
|||
|
CloseContext->FileObject = FileObject;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the kernel event in the header to the Not-Signalled state.
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeEvent(&CloseContext->Header.KernelEvent, NotificationEvent, 0);
|
|||
|
|
|||
|
if ((SmbBuffer = RdrAllocateSMBBuffer())==NULL) {
|
|||
|
goto CloseFailed;
|
|||
|
}
|
|||
|
|
|||
|
CloseContext->SmbBuffer = SmbBuffer;
|
|||
|
|
|||
|
#ifdef IMPLEMENT_CLOSE_BEHIND
|
|||
|
CloseContext->SynchronousClose = WaitForCompletion;
|
|||
|
#endif
|
|||
|
|
|||
|
CloseContext->Irp = Irp;
|
|||
|
|
|||
|
SmbHeader = (PSMB_HEADER )SmbBuffer->Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// Down level print servers need the Close Print File SMB
|
|||
|
//
|
|||
|
|
|||
|
if (( Icb->NonPagedFcb->FileType == FileTypePrinter ) &&
|
|||
|
!(Icb->Fcb->Connection->Server->Capabilities & DF_LANMAN10)) {
|
|||
|
PREQ_CLOSE_PRINT_FILE Close;
|
|||
|
|
|||
|
Close = (PREQ_CLOSE_PRINT_FILE )(SmbHeader+1);
|
|||
|
|
|||
|
SmbHeader->Command = SMB_COM_CLOSE_PRINT_FILE;
|
|||
|
|
|||
|
Close->WordCount = 1;
|
|||
|
|
|||
|
SmbPutUshort(&Close->Fid, Icb->FileId);
|
|||
|
|
|||
|
SmbPutUshort(&Close->ByteCount, 0);
|
|||
|
|
|||
|
SmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER)+sizeof(REQ_CLOSE_PRINT_FILE);
|
|||
|
|
|||
|
} else {
|
|||
|
PREQ_CLOSE Close;
|
|||
|
ULONG SecondsSince1970;
|
|||
|
|
|||
|
SmbHeader->Command = SMB_COM_CLOSE;
|
|||
|
|
|||
|
Close = (PREQ_CLOSE )(SmbHeader+1);
|
|||
|
|
|||
|
Close->WordCount = 3;
|
|||
|
|
|||
|
SmbPutUshort(&Close->Fid, Icb->FileId);
|
|||
|
|
|||
|
if (Icb->Flags & ICB_DELETE_PENDING) {
|
|||
|
SmbPutUlong(&Close->LastWriteTimeInSeconds, 0xffffffff);
|
|||
|
} else {
|
|||
|
BOOLEAN UpdateTimeInSmb = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// If the file has been written to, and the user didn't call
|
|||
|
// SetInformationFile to set the file times manually, set
|
|||
|
// the LastWrite time now. If the user called SetInformationFile,
|
|||
|
// and this is an MS-NET server, SETDATEONCLOSE will be set
|
|||
|
// because we couldn't update the times then, so do it now.
|
|||
|
//
|
|||
|
|
|||
|
if (FileObject->Flags & FO_FILE_MODIFIED) {
|
|||
|
UpdateTimeInSmb = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the user set the time on the file, and this is an NT server,
|
|||
|
// then we don't want to update the time in the SMB, since the
|
|||
|
// server has a more accurate file time than the client does.
|
|||
|
//
|
|||
|
|
|||
|
if (Icb->Flags & ICB_USER_SET_TIMES) {
|
|||
|
if (Icb->Fcb->Connection->Server->Capabilities & DF_NT_SMBS) {
|
|||
|
UpdateTimeInSmb = FALSE;
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Otherwise (this is not an NT server), we want to set
|
|||
|
// the time in the SMB to match the updated time.
|
|||
|
//
|
|||
|
|
|||
|
UpdateTimeInSmb = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we have the SetDateOnClose flag set, we want to update the
|
|||
|
// time regardless.
|
|||
|
//
|
|||
|
|
|||
|
if (Icb->Flags & ICB_SETDATEONCLOSE) {
|
|||
|
UpdateTimeInSmb = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (UpdateTimeInSmb) {
|
|||
|
|
|||
|
RdrTimeToSecondsSince1970(&Icb->Fcb->LastWriteTime, Icb->Fcb->Connection->Server, &SecondsSince1970);
|
|||
|
|
|||
|
SmbPutUlong(&Close->LastWriteTimeInSeconds, SecondsSince1970);
|
|||
|
|
|||
|
} else {
|
|||
|
SmbPutUlong(&Close->LastWriteTimeInSeconds, 0xffffffff);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
SmbPutUshort(&Close->ByteCount, 0);
|
|||
|
|
|||
|
SmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER)+sizeof(REQ_CLOSE);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Flag that this ICB no longer has a valid handle.
|
|||
|
//
|
|||
|
|
|||
|
Icb->Flags &= ~ICB_HASHANDLE;
|
|||
|
|
|||
|
#ifdef IMPLEMENT_CLOSE_BEHIND
|
|||
|
if (!WaitForCompletion) {
|
|||
|
|
|||
|
//
|
|||
|
// Remember the thread that is closing the file so we can release
|
|||
|
// the file lock correctly when we come and close the file.
|
|||
|
//
|
|||
|
|
|||
|
Icb->ClosersThread = ExGetCurrentResourceThread();
|
|||
|
|
|||
|
//
|
|||
|
// Recursively acquire the FCB lock to guarantee that no process
|
|||
|
// gains access until after the close has completed.
|
|||
|
//
|
|||
|
|
|||
|
RdrAcquireFcbLock(Icb->Fcb, ExclusiveLock, TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// Reference the FCB to allow us to release the FCB lock (otherwise it might go away).
|
|||
|
//
|
|||
|
|
|||
|
RdrReferenceFcb(Icb->Fcb);
|
|||
|
|
|||
|
CloseContext->Fcb = Icb->Fcb;
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(Irp)) {
|
|||
|
|
|||
|
ASSERT(IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_CLOSE);
|
|||
|
|
|||
|
ASSERT(Icb->Fcb->Header.Resource->ActiveCount != 0);
|
|||
|
|
|||
|
//
|
|||
|
// Flag this packet pending before returning.
|
|||
|
//
|
|||
|
|
|||
|
IoMarkIrpPending(Irp);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Establish a reference to the file object. This
|
|||
|
// prevents the file object from being deleted until
|
|||
|
// after we have completed all of the close processing.
|
|||
|
//
|
|||
|
|
|||
|
ObReferenceObject(FileObject);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
Status = RdrNetTranceiveNoWait(NT_NORMAL | NT_NORECONNECT,
|
|||
|
Irp,
|
|||
|
Icb->Fcb->Connection,// Connection
|
|||
|
SmbBuffer->Mdl, // MDL to send.
|
|||
|
CloseContext, // Context structure for close.
|
|||
|
CloseCallback, // Completion for close.
|
|||
|
Icb->Se, // Security entry
|
|||
|
&CloseContext->MpxEntry);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(Status) && WaitForCompletion) {
|
|||
|
//
|
|||
|
// Wait for the close operation to complete. We emulate what the
|
|||
|
// generic worker thread would do when closing the request.
|
|||
|
//
|
|||
|
|
|||
|
CompleteCloseOperation(CloseContext);
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
#if DBG
|
|||
|
} else {
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
ASSERT(Status == STATUS_PENDING);
|
|||
|
}
|
|||
|
}
|
|||
|
#else
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// If we sent the close, return success.
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
CloseFailed:
|
|||
|
|
|||
|
if (SmbBuffer!=NULL) {
|
|||
|
RdrFreeSMBBuffer(SmbBuffer);
|
|||
|
}
|
|||
|
|
|||
|
if (CloseContext!=NULL) {
|
|||
|
FREE_POOL(CloseContext);
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
STANDARD_CALLBACK_HEADER(
|
|||
|
CloseCallback
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the asynchronous callback called when a close operation
|
|||
|
completes.
|
|||
|
|
|||
|
Please note that all errors are ignored when the close operation is
|
|||
|
completing, we don't care that the close failed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PSMB_HEADER Smb - Supplies the SMB that completed
|
|||
|
IN ULONG SmbLength - Supplies the size of the SMB that completed.
|
|||
|
IN PMPX_ENTRY MpxTable - Supplies the MPX table entry in the request
|
|||
|
IN PCLOSE_CONTEXT Context - Supplies the supplied context structure
|
|||
|
IN BOOLEAN ErrorIndicator - TRUE iff there was an error on the op.
|
|||
|
IN NTSTATUS NetworkErrorCode OPTIONAL - Supplies the error if ErrorInd.
|
|||
|
IN OUT PIRP *Irp OPTIONAL - Ignored
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Returns STATUS_SUCCESS always
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PCLOSE_CONTEXT Context = Ctx;
|
|||
|
|
|||
|
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
|||
|
|
|||
|
dprintf(DPRT_CLOSE, ("CompleteClose - Indication\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Close requests should never fail.
|
|||
|
//
|
|||
|
|
|||
|
//#if DBG
|
|||
|
// if (!ErrorIndicator) {
|
|||
|
// if ((RdrMapSmbError(Smb, Server) != STATUS_SUCCESS) &&
|
|||
|
// ((*(PULONG)NtGlobalFlag) & 0x20000)) {
|
|||
|
// RdrSendMagicBullet(Server->Connection.TransportProvider);
|
|||
|
// }
|
|||
|
// }
|
|||
|
//#endif
|
|||
|
|
|||
|
#ifdef IMPLEMENT_CLOSE_BEHIND
|
|||
|
//
|
|||
|
// If this is an asynchronous close, post this request to a generic worker
|
|||
|
// thread, otherwise, just kick the event in the context block
|
|||
|
// and return.
|
|||
|
//
|
|||
|
|
|||
|
if (!Context->SynchronousClose) {
|
|||
|
//
|
|||
|
// Simply pass the close operation to a worker thread, we need to
|
|||
|
// do no processing for this request.
|
|||
|
//
|
|||
|
dprintf(DPRT_CLOSE, ("CompleteClose: Post to worker thread.\n"));
|
|||
|
RdrQueueWorkItem(&Context->WorkHeader, DelayedWorkQueue);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// The close has completed. Indicate to any waiters that it has finished
|
|||
|
// and return to the caller.
|
|||
|
//
|
|||
|
|
|||
|
KeSetEvent(&Context->Header.KernelEvent, IO_NETWORK_INCREMENT, FALSE);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(MpxEntry);
|
|||
|
UNREFERENCED_PARAMETER(Irp);
|
|||
|
UNREFERENCED_PARAMETER(SmbLength);
|
|||
|
UNREFERENCED_PARAMETER(Server);
|
|||
|
UNREFERENCED_PARAMETER(Smb);
|
|||
|
UNREFERENCED_PARAMETER(ErrorIndicator);
|
|||
|
UNREFERENCED_PARAMETER(NetworkErrorCode);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
VOID
|
|||
|
CompleteCloseOperation (
|
|||
|
IN PVOID Ctx
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to finally complete a close operation.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PVOID Ctx - Supplies the header describing the operation.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PCLOSE_CONTEXT CloseContext;
|
|||
|
PICB Icb;
|
|||
|
PFILE_OBJECT FileObject;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
CloseContext = Ctx;
|
|||
|
|
|||
|
ASSERT(CloseContext->Header.Type == CONTEXT_CLOSE);
|
|||
|
|
|||
|
FileObject = CloseContext->FileObject;
|
|||
|
|
|||
|
Icb = CloseContext->Icb;
|
|||
|
|
|||
|
ASSERT(Icb == FileObject->FsContext2);
|
|||
|
|
|||
|
dprintf(DPRT_CLOSE, ("CompleteCloseOperation, File: %wZ, File Object: %08lx\n", &Icb->Fcb->FileName, FileObject));
|
|||
|
|
|||
|
ASSERT(Icb->Signature == STRUCTURE_SIGNATURE_ICB);
|
|||
|
|
|||
|
//
|
|||
|
// Wait for the close operation to complete. This should return
|
|||
|
// immediately.
|
|||
|
//
|
|||
|
|
|||
|
RdrWaitTranceive(CloseContext->MpxEntry);
|
|||
|
|
|||
|
//
|
|||
|
// Free up the MPX table entry.
|
|||
|
//
|
|||
|
|
|||
|
RdrEndTranceive(CloseContext->MpxEntry);
|
|||
|
|
|||
|
#ifdef IMPLEMENT_CLOSE_BEHIND
|
|||
|
//
|
|||
|
// If this is not a synchronous close, this is being performed as a part
|
|||
|
// of a close behind operation, so we should unlink the ICB from the chain
|
|||
|
// now, since it's no longer being used.
|
|||
|
//
|
|||
|
|
|||
|
if (!CloseContext->SynchronousClose) {
|
|||
|
|
|||
|
//
|
|||
|
// This code assumes that if there was an IRP supplied to
|
|||
|
// RdrCloseFile, that it is for a CLOSE request. This
|
|||
|
// means that we are performing the final close for a file,
|
|||
|
// and thus that we should complete the close IRP as soon as we
|
|||
|
// have freed up the ICB.
|
|||
|
//
|
|||
|
|
|||
|
if (!ARGUMENT_PRESENT(CloseContext->Irp)) {
|
|||
|
ERESOURCE_THREAD ClosersThread;
|
|||
|
|
|||
|
//
|
|||
|
// This FCB had better still be owned for exclusive access
|
|||
|
//
|
|||
|
|
|||
|
ASSERT (CloseContext->Fcb->Header.Resource->Flag & ResourceOwnedExclusive);
|
|||
|
|
|||
|
//
|
|||
|
// Save the ClosersThread pointer in the ICB, then clear it.
|
|||
|
// Clearing it prevents RdrUnlinkAndFreeIcb from inadvertently
|
|||
|
// releasing the wrong lock.
|
|||
|
//
|
|||
|
// On entry to this routine, an exclusive lock on the FCB is
|
|||
|
// held; this lock was acquired when the Close SMB was
|
|||
|
// sent.
|
|||
|
//
|
|||
|
|
|||
|
ClosersThread = Icb->ClosersThread;
|
|||
|
|
|||
|
Icb->ClosersThread = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Remove the reference to the FCB that we applied in RdrCloseFile
|
|||
|
// and remove the lock that was applied in RdrFsdCleanup. At this
|
|||
|
// point the file has been truely closed, so we are safe to remove
|
|||
|
// this reference, we can't conflict with other opens.
|
|||
|
//
|
|||
|
|
|||
|
RdrDereferenceFcb (NULL,
|
|||
|
CloseContext->Fcb,
|
|||
|
TRUE, // FcbLocked.
|
|||
|
ClosersThread,
|
|||
|
NULL);
|
|||
|
|
|||
|
//
|
|||
|
// Mark the file as being force closed, removing the reference
|
|||
|
// we applied to the file object earlier.
|
|||
|
//
|
|||
|
|
|||
|
ObDereferenceObject(FileObject);
|
|||
|
|
|||
|
//
|
|||
|
// WARNING!!!!!! Do not touch either the ICB, or the file
|
|||
|
// object from this point on, since the storage associated
|
|||
|
// with them has been deleted!.
|
|||
|
//
|
|||
|
|
|||
|
} else {
|
|||
|
ERESOURCE_THREAD ClosersThread;
|
|||
|
|
|||
|
ASSERT(IoGetCurrentIrpStackLocation(CloseContext->Irp)->MajorFunction == IRP_MJ_CLOSE);
|
|||
|
|
|||
|
//
|
|||
|
// Save the ClosersThread pointer in the ICB, then clear it.
|
|||
|
// Clearing it prevents RdrUnlinkAndFreeIcb from inadvertently
|
|||
|
// releasing the wrong lock.
|
|||
|
//
|
|||
|
// On entry to this routine, an exclusive lock on the FCB is
|
|||
|
// held; this lock was acquired when the Close SMB was
|
|||
|
// sent.
|
|||
|
//
|
|||
|
|
|||
|
ClosersThread = Icb->ClosersThread;
|
|||
|
|
|||
|
Icb->ClosersThread = 0;
|
|||
|
|
|||
|
//
|
|||
|
// This is a close behind operation from the close IRP that has now completed.
|
|||
|
//
|
|||
|
// We now want to free the ICB and dereference the FCB.
|
|||
|
//
|
|||
|
// Even though the FCB is locked, we don't want to remove the
|
|||
|
// FCB lock until we dereference the FCB later.
|
|||
|
//
|
|||
|
|
|||
|
RdrUnlinkAndFreeIcb(NULL, Icb, FileObject);
|
|||
|
|
|||
|
//
|
|||
|
// Remove the reference to the FCB that we applied in RdrCloseFile
|
|||
|
// and remove the lock that was applied in RdrFsdClose. At this
|
|||
|
// point the file has been truely closed, so we are safe to remove
|
|||
|
// this reference, we can't conflict with other opens.
|
|||
|
//
|
|||
|
|
|||
|
RdrDereferenceFcb (NULL,
|
|||
|
CloseContext->Fcb,
|
|||
|
FALSE, // FcbLocked.
|
|||
|
ClosersThread,
|
|||
|
NULL);
|
|||
|
|
|||
|
// dprintf(DPRT_CLOSE, ("Completing IRP %lx with STATUS_SUCCESS\n", CloseContext->Irp));
|
|||
|
|
|||
|
FileObject->FsContext = FileObject->FsContext2 = NULL;
|
|||
|
|
|||
|
RdrCompleteRequest(CloseContext->Irp, STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
#endif
|
|||
|
dprintf(DPRT_CLOSE, ("Synchronous close\n"));
|
|||
|
|
|||
|
//
|
|||
|
// If the file is to be kept open, flag the ICB as being
|
|||
|
// immutable.
|
|||
|
//
|
|||
|
|
|||
|
Icb->Flags |= ICB_FORCECLOSED;
|
|||
|
|
|||
|
//
|
|||
|
// The ICB no longer has a handle associated with it.
|
|||
|
//
|
|||
|
|
|||
|
Icb->Flags &= ~ICB_HASHANDLE;
|
|||
|
|
|||
|
#ifdef IMPLEMENT_CLOSE_BEHIND
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Free up the pool associated with the close context block.
|
|||
|
//
|
|||
|
|
|||
|
RdrFreeSMBBuffer(CloseContext->SmbBuffer);
|
|||
|
|
|||
|
FREE_POOL(CloseContext);
|
|||
|
|
|||
|
dprintf(DPRT_CLOSE, ("CompleteClose returning.\n"));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RdrCloseFileFromFileId (
|
|||
|
IN PIRP Irp OPTIONAL,
|
|||
|
IN USHORT FileId,
|
|||
|
IN ULONG LastWriteTimeInSeconds,
|
|||
|
IN PSECURITY_ENTRY Se,
|
|||
|
IN PCONNECTLISTENTRY Cle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will close the file specified by the provided FileId on
|
|||
|
the remote server.
|
|||
|
|
|||
|
This close operation is a totally synchronous close (as opposed to the
|
|||
|
ICB based close in RdrCloseFile).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN USHORT FileId - SMB_FID to close.
|
|||
|
|
|||
|
IN PSECURITY_ENTRY Se - Supplies the security entry the file was opened with.
|
|||
|
|
|||
|
IN PCONNECTLISTENTRY Cle - Supplies the connection to close the file on.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status of close SMB.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSMB_BUFFER SmbBuffer = NULL;
|
|||
|
PSMB_HEADER SmbHeader;
|
|||
|
PREQ_CLOSE Close;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
dprintf(DPRT_CLOSE|DPRT_CACHE, ("RdrCloseFileFromFid, FileId: %04lx\n", FileId));
|
|||
|
|
|||
|
try {
|
|||
|
if ((SmbBuffer = RdrAllocateSMBBuffer())==NULL) {
|
|||
|
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
|
|||
|
}
|
|||
|
|
|||
|
SmbHeader = (PSMB_HEADER )SmbBuffer->Buffer;
|
|||
|
|
|||
|
SmbHeader->Command = SMB_COM_CLOSE;
|
|||
|
|
|||
|
Close = (PREQ_CLOSE )(SmbHeader+1);
|
|||
|
|
|||
|
Close->WordCount = 3;
|
|||
|
|
|||
|
SmbPutUshort(&Close->Fid, FileId);
|
|||
|
|
|||
|
SmbPutUlong(&Close->LastWriteTimeInSeconds, LastWriteTimeInSeconds);
|
|||
|
|
|||
|
SmbPutUshort(&Close->ByteCount, 0);
|
|||
|
|
|||
|
SmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER)+sizeof(REQ_CLOSE);
|
|||
|
|
|||
|
Status = RdrNetTranceive(NT_NORMAL | NT_NORECONNECT,
|
|||
|
Irp,
|
|||
|
Cle, // Connection
|
|||
|
SmbBuffer->Mdl, // MDL to send.
|
|||
|
NULL, // MDL for receive operation
|
|||
|
Se); // Security entry
|
|||
|
|
|||
|
// ASSERT (Status != STATUS_INVALID_HANDLE);
|
|||
|
|
|||
|
try_exit:NOTHING;
|
|||
|
} finally {
|
|||
|
if (SmbBuffer!=NULL) {
|
|||
|
RdrFreeSMBBuffer(SmbBuffer);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// RdrDeleteFile
|
|||
|
//
|
|||
|
NTSTATUS
|
|||
|
RdrDeleteFile (
|
|||
|
IN PIRP Irp OPTIONAL,
|
|||
|
IN PUNICODE_STRING FileName,
|
|||
|
IN BOOLEAN DfsFile,
|
|||
|
IN PCONNECTLISTENTRY Connection,
|
|||
|
IN PSECURITY_ENTRY Se
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sends a DELETE SMB to the remote server.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PIRP Irp OPTIONAL - Supplies an IRP to use in the transaction.
|
|||
|
IN PUNICODE_STRING FileName - Supplies the name to delete.
|
|||
|
IN PCONNECTLISTENTRY Connection - Supplies the connection the file is
|
|||
|
open on.
|
|||
|
|
|||
|
IN PSECURITY_ENTRY Se - Security context file is opened on.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Status of delete operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSMB_BUFFER SMBBuffer;
|
|||
|
PSMB_HEADER Smb;
|
|||
|
NTSTATUS Status;
|
|||
|
PREQ_DELETE DeleteFile;
|
|||
|
PUCHAR Bufferp;
|
|||
|
PMDL SendMDL;
|
|||
|
ULONG SendLength;
|
|||
|
ULONG TranceiveFlags = 0;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if ((SMBBuffer = RdrAllocateSMBBuffer())==NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
Smb = (PSMB_HEADER ) SMBBuffer->Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// Build the SMB
|
|||
|
//
|
|||
|
|
|||
|
Smb->Command = SMB_COM_DELETE;
|
|||
|
|
|||
|
if (DfsFile) {
|
|||
|
SmbPutUshort(&Smb->Flags2, SMB_FLAGS2_DFS);
|
|||
|
TranceiveFlags |= NT_DFSFILE;
|
|||
|
}
|
|||
|
|
|||
|
DeleteFile = (PREQ_DELETE ) (Smb+1);
|
|||
|
|
|||
|
DeleteFile->WordCount = 1;
|
|||
|
|
|||
|
SmbPutUshort(&DeleteFile->SearchAttributes, SMB_FILE_ATTRIBUTE_SYSTEM | SMB_FILE_ATTRIBUTE_HIDDEN);
|
|||
|
|
|||
|
Bufferp = (PUCHAR)DeleteFile->Buffer;
|
|||
|
|
|||
|
Status = RdrCopyNetworkPath((PVOID *)&Bufferp, FileName, Connection->Server, SMB_FORMAT_ASCII, SKIP_SERVER_SHARE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
RdrFreeSMBBuffer(SMBBuffer);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the BCC field in the SMB to indicate the number of bytes of
|
|||
|
// protocol we've put in the negotiate.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutUshort( &DeleteFile->ByteCount,
|
|||
|
(USHORT )(Bufferp-(PUCHAR )(DeleteFile->Buffer)));
|
|||
|
|
|||
|
SendLength = Bufferp-(PUCHAR )(Smb);
|
|||
|
|
|||
|
SendMDL = SMBBuffer->Mdl;
|
|||
|
|
|||
|
SendMDL->ByteCount = SendLength;
|
|||
|
|
|||
|
TranceiveFlags |= NT_NORMAL;
|
|||
|
|
|||
|
Status = RdrNetTranceive(TranceiveFlags, // Flags
|
|||
|
Irp,
|
|||
|
Connection,
|
|||
|
SendMDL,
|
|||
|
NULL, // Only interested in the error code.
|
|||
|
Se);
|
|||
|
|
|||
|
#if RDRDBG
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
dprintf(DPRT_FILEINFO, ("DeleteFile failed: %X\n", Status));
|
|||
|
}
|
|||
|
#endif // RDRDBG
|
|||
|
|
|||
|
RdrFreeSMBBuffer(SMBBuffer);
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
//
|
|||
|
//
|
|||
|
// RdrDoesFileExist
|
|||
|
//
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RdrDoesFileExist (
|
|||
|
IN PIRP Irp OPTIONAL,
|
|||
|
IN PUNICODE_STRING FileName,
|
|||
|
IN PCONNECTLISTENTRY Connection,
|
|||
|
IN PSECURITY_ENTRY Se,
|
|||
|
IN BOOLEAN DfsFile,
|
|||
|
OUT PULONG FileAttributes,
|
|||
|
OUT PBOOLEAN FileIsDirectory,
|
|||
|
OUT PLARGE_INTEGER LastWriteTime OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns whether or not a specified file exists on the
|
|||
|
specified remote server. It is roughly equivilant to the unix stat()
|
|||
|
system call.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PIRP Irp - Supplies an I/O Request packet to use for the FindUnique
|
|||
|
request.
|
|||
|
IN PUNICODE_STRING FileName - Supplies the file to check
|
|||
|
|
|||
|
IN PCONNECTLISTENTRY Connection - Supplies the connection the file is
|
|||
|
open on.
|
|||
|
|
|||
|
IN PSECURITY_ENTRY Se - Security context file is opened on.
|
|||
|
|
|||
|
OUT PULONG FileAttributes - The specified attributes of the requested file
|
|||
|
|
|||
|
OUT PBOOLEAN FileIsDirectory - TRUE if the file specified is a directory.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - SUCCESS if the file exists, status otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
UNICODE_STRING ServerName, ShareName;
|
|||
|
UNICODE_STRING PathName;
|
|||
|
ULONG TranceiveFlags;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
Status = RdrExtractServerShareAndPath(FileName, &ServerName, &ShareName, &PathName);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the name specified by the caller is just a \server\share or
|
|||
|
// \server\share\, we can assume that the path exists.
|
|||
|
//
|
|||
|
|
|||
|
if (PathName.Length == 0 ||
|
|||
|
(PathName.Length == sizeof(WCHAR) && PathName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) {
|
|||
|
|
|||
|
//
|
|||
|
// We should assume that the root is a normal directory. Technically,
|
|||
|
// the directory might have a different attribute than "normal", but
|
|||
|
// we cannot reliably determine the true attribute.
|
|||
|
//
|
|||
|
|
|||
|
*FileIsDirectory = TRUE;
|
|||
|
|
|||
|
*FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(LastWriteTime)) {
|
|||
|
|
|||
|
LastWriteTime->HighPart =
|
|||
|
|
|||
|
LastWriteTime->LowPart = 0;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
PREQ_QUERY_INFORMATION GetAttr;
|
|||
|
PSMB_BUFFER SmbBuffer;
|
|||
|
PSMB_HEADER Smb;
|
|||
|
STATCONTEXT Context;
|
|||
|
PUCHAR TrailingBytes;
|
|||
|
|
|||
|
if ((SmbBuffer = RdrAllocateSMBBuffer()) == NULL) {
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
Smb = (PSMB_HEADER )SmbBuffer->Buffer;
|
|||
|
GetAttr = (PREQ_QUERY_INFORMATION)(Smb+1);
|
|||
|
|
|||
|
Smb->Command = SMB_COM_QUERY_INFORMATION;
|
|||
|
|
|||
|
TranceiveFlags = DfsFile ? (NT_NORMAL | NT_DFSFILE) : NT_NORMAL;
|
|||
|
|
|||
|
GetAttr->WordCount = 0;
|
|||
|
|
|||
|
TrailingBytes = (PUCHAR)GetAttr + FIELD_OFFSET(REQ_QUERY_INFORMATION, Buffer[0]);
|
|||
|
|
|||
|
//
|
|||
|
// TrailingBytes now points to where the 0x04 of FileName is to go.
|
|||
|
//
|
|||
|
|
|||
|
Status = RdrCopyNetworkPath((PVOID *)&TrailingBytes,
|
|||
|
FileName,
|
|||
|
Connection->Server,
|
|||
|
SMB_FORMAT_ASCII,
|
|||
|
SKIP_SERVER_SHARE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
RdrFreeSMBBuffer(SmbBuffer);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
SmbPutUshort(&GetAttr->ByteCount, (USHORT)(
|
|||
|
(ULONG)(TrailingBytes-((PUCHAR)GetAttr+FIELD_OFFSET(REQ_QUERY_INFORMATION, Buffer)))
|
|||
|
));
|
|||
|
|
|||
|
SmbBuffer->Mdl->ByteCount = (ULONG)(TrailingBytes - (PUCHAR)(Smb));
|
|||
|
|
|||
|
Context.Header.Type = CONTEXT_STAT;
|
|||
|
Context.Header.TransferSize =
|
|||
|
SmbBuffer->Mdl->ByteCount + sizeof(PRESP_QUERY_INFORMATION);
|
|||
|
|
|||
|
Status = RdrNetTranceiveWithCallback(TranceiveFlags, Irp,
|
|||
|
Connection,
|
|||
|
SmbBuffer->Mdl,
|
|||
|
&Context,
|
|||
|
DoesFileExistCallback,
|
|||
|
Se,
|
|||
|
NULL);
|
|||
|
|
|||
|
*FileAttributes = Context.FileAttributes;
|
|||
|
|
|||
|
*FileIsDirectory = Context.FileIsDirectory;
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(LastWriteTime)) {
|
|||
|
*LastWriteTime = Context.LastWriteTime;
|
|||
|
}
|
|||
|
|
|||
|
RdrFreeSMBBuffer(SmbBuffer);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if ( Status == STATUS_NO_MORE_FILES ) {
|
|||
|
|
|||
|
//
|
|||
|
// Servers return NO_MORE_FILES when there is not a match on
|
|||
|
// a Find request.
|
|||
|
//
|
|||
|
|
|||
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|||
|
}
|
|||
|
|
|||
|
#if RDRDBG
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
dprintf(DPRT_SMB|DPRT_CREATE, ("RdrDoesFileExist(%wZ): Succeeded: %X,Directory %lx,Attributes %lx\n",
|
|||
|
FileName, Status, *FileIsDirectory, *FileAttributes));
|
|||
|
} else {
|
|||
|
dprintf(DPRT_SMB|DPRT_CREATE, ("RdrDoesFileExist(%wZ): Failed: %X", FileName, Status));
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
STANDARD_CALLBACK_HEADER (
|
|||
|
DoesFileExistCallback
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the callback routine for the processing of a GetAttr SMB.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
|
|||
|
IN PSMB_HEADER Smb - SMB response from server.
|
|||
|
IN PMPX_ENTRY MpxTable - MPX table entry for request.
|
|||
|
IN PSTATCONTEXT Context - Context from caller.
|
|||
|
IN BOOLEAN ErrorIndicator - TRUE if error indication
|
|||
|
IN NTSTATUS NetworkErrorCode OPTIONAL - Network error if error indication.
|
|||
|
IN OUT PIRP *Irp - IRP from TDI
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - STATUS_PENDING if we are to complete the request
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSTATCONTEXT Context = Ctx;
|
|||
|
PRESP_QUERY_INFORMATION QueryResponse;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
|||
|
|
|||
|
ASSERT(Context->Header.Type == CONTEXT_STAT);
|
|||
|
|
|||
|
dprintf(DPRT_FILEINFO, ("DoesFileExistCallback"));
|
|||
|
|
|||
|
Context->Header.ErrorType = NoError; // Assume no error at first.
|
|||
|
|
|||
|
//
|
|||
|
// If we are called because the VC dropped, indicate it in the response
|
|||
|
//
|
|||
|
|
|||
|
if (ErrorIndicator) {
|
|||
|
Context->Header.ErrorType = NetError;
|
|||
|
Context->Header.ErrorCode = RdrMapNetworkError(NetworkErrorCode);
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status = RdrMapSmbError(Smb, Server))) {
|
|||
|
Context->Header.ErrorType = SMBError;
|
|||
|
Context->Header.ErrorCode = Status;
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
QueryResponse = (PRESP_QUERY_INFORMATION )(Smb+1);
|
|||
|
|
|||
|
Context->FileAttributes =
|
|||
|
RdrMapSmbAttributes(SmbGetUshort(&QueryResponse->FileAttributes));
|
|||
|
|
|||
|
|
|||
|
RdrSecondsSince1970ToTime(SmbGetUlong(&QueryResponse->LastWriteTimeInSeconds), Server, &Context->LastWriteTime);
|
|||
|
|
|||
|
Context->FileIsDirectory = (BOOLEAN)
|
|||
|
((SmbGetUshort(&QueryResponse->FileAttributes) & SMB_FILE_ATTRIBUTE_DIRECTORY)
|
|||
|
!= 0);
|
|||
|
|
|||
|
ReturnStatus:
|
|||
|
KeSetEvent(&Context->Header.KernelEvent, IO_NETWORK_INCREMENT, FALSE);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(MpxEntry);
|
|||
|
UNREFERENCED_PARAMETER(Irp);
|
|||
|
UNREFERENCED_PARAMETER(SmbLength);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RdrGenericPathSmb(
|
|||
|
IN PIRP Irp OPTIONAL,
|
|||
|
IN UCHAR Command,
|
|||
|
IN BOOLEAN DfsFile,
|
|||
|
IN PUNICODE_STRING RemotePathName,
|
|||
|
IN PCONNECTLISTENTRY Connection,
|
|||
|
IN PSECURITY_ENTRY Se
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Build a generic path based core SMB and send it.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - Supplies a pointer to the Users IRP which we will use
|
|||
|
to supply the request to the TDI.
|
|||
|
Command - Supplies the difference between ChDir and CreateDir
|
|||
|
RemotePathName - Supplies the directory name to be accessed
|
|||
|
Connection - Supplies the \\Server\Share etc.
|
|||
|
Se - Supplies the security entry which includes the uid
|
|||
|
to be used.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - The status for this Irp.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSMB_BUFFER SMBBuffer;
|
|||
|
PSMB_HEADER Smb;
|
|||
|
NTSTATUS Status;
|
|||
|
PREQ_CREATE_DIRECTORY Create_Directory;
|
|||
|
PUCHAR Bufferp;
|
|||
|
PMDL SendMDL;
|
|||
|
ULONG SendLength;
|
|||
|
ULONG TranceiveFlags;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if ((SMBBuffer = RdrAllocateSMBBuffer())==NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
Smb = (PSMB_HEADER ) SMBBuffer->Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// Build the SMB
|
|||
|
//
|
|||
|
|
|||
|
Smb->Command = Command;
|
|||
|
|
|||
|
Create_Directory = (PREQ_CREATE_DIRECTORY ) (Smb+1);
|
|||
|
|
|||
|
Create_Directory->WordCount = 0;
|
|||
|
|
|||
|
Bufferp = (PUCHAR)Create_Directory->Buffer;
|
|||
|
|
|||
|
Status = RdrCopyNetworkPath((PVOID *)&Bufferp,
|
|||
|
RemotePathName,
|
|||
|
Connection->Server,
|
|||
|
SMB_FORMAT_ASCII,
|
|||
|
SKIP_SERVER_SHARE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
RdrFreeSMBBuffer(SMBBuffer);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the BCC field in the SMB to indicate the number of bytes of
|
|||
|
// protocol we've put in the negotiate.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutUshort( &Create_Directory->ByteCount,
|
|||
|
(USHORT )(Bufferp-(PUCHAR )(Create_Directory->Buffer)));
|
|||
|
|
|||
|
SendLength = Bufferp-(PUCHAR )(Smb);
|
|||
|
|
|||
|
SendMDL = SMBBuffer->Mdl;
|
|||
|
|
|||
|
SendMDL->ByteCount = SendLength;
|
|||
|
|
|||
|
TranceiveFlags = DfsFile ? NT_NORMAL | NT_DFSFILE : NT_NORMAL;
|
|||
|
|
|||
|
Status = RdrNetTranceive(TranceiveFlags, // Flags
|
|||
|
Irp,
|
|||
|
Connection,
|
|||
|
SendMDL,
|
|||
|
NULL, // Only interested in the error code.
|
|||
|
Se);
|
|||
|
|
|||
|
#if RDRDBG
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
dprintf(DPRT_CONNECT, ("Create_Directory failed: %X\n", Status));
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
RdrFreeSMBBuffer(SMBBuffer);
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
//
|
|||
|
// RdrQueryDiskAttributes
|
|||
|
//
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RdrQueryDiskAttributes (
|
|||
|
IN PIRP Irp OPTIONAL,
|
|||
|
IN PICB Icb,
|
|||
|
OUT PLARGE_INTEGER TotalAllocationUnits,
|
|||
|
OUT PLARGE_INTEGER AvailableAllocationUnits,
|
|||
|
OUT PULONG SectorsPerAllocationUnit,
|
|||
|
OUT PULONG BytesPerSector
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns information about the file system backing a share
|
|||
|
on the specified remote server.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PIRP Irp - Supplies an optional I/O Request packet to use for the
|
|||
|
SMBdskattr request.
|
|||
|
IN PICB Icb - Supplies an ICB associated with the file to check.
|
|||
|
|
|||
|
OUT PULONG TotalAllocationUnits - Returns the total number of clusters on
|
|||
|
the remote disk.
|
|||
|
OUT PULONG AvailableAllocationUnits - Returns the number of free clusters
|
|||
|
on the remote disk
|
|||
|
OUT PULONG SectorsPerAllocationUnit - Returns the number of sectors per
|
|||
|
cluster on the remote disk.
|
|||
|
OUT PULONG BytesPerSector - Returns the number of bytes per sector on the
|
|||
|
remote disk.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - SUCCESS if the file exists, status otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PREQ_QUERY_INFORMATION_DISK DskAttr;
|
|||
|
PSMB_BUFFER SmbBuffer;
|
|||
|
PSMB_HEADER Smb;
|
|||
|
DSKATTRIBCONTEXT Context;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if (Icb->Fcb->Connection->Server->Capabilities & DF_LANMAN20) {
|
|||
|
|
|||
|
//
|
|||
|
// Use TRANSACT2_QFSINFO to get the volume size information
|
|||
|
//
|
|||
|
|
|||
|
USHORT Setup[] = {TRANS2_QUERY_FS_INFORMATION};
|
|||
|
|
|||
|
REQ_QUERY_FS_INFORMATION Parameters;
|
|||
|
|
|||
|
union {
|
|||
|
FILE_FS_SIZE_INFORMATION FsSizeInfo;
|
|||
|
QFSALLOCATE FsAllocate;
|
|||
|
} Buffer;
|
|||
|
|
|||
|
CLONG OutParameterCount = sizeof(REQ_QUERY_FS_INFORMATION);
|
|||
|
|
|||
|
CLONG OutDataCount = sizeof(Buffer);
|
|||
|
|
|||
|
CLONG OutSetupCount = 0;
|
|||
|
|
|||
|
if (Icb->Fcb->Connection->Server->Capabilities & DF_NT_SMBS) {
|
|||
|
SmbPutAlignedUshort(&Parameters.InformationLevel, SMB_QUERY_FS_SIZE_INFO);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Build and initialize the Parameters
|
|||
|
//
|
|||
|
|
|||
|
SmbPutAlignedUshort(&Parameters.InformationLevel, SMB_INFO_ALLOCATION);
|
|||
|
}
|
|||
|
|
|||
|
Status = RdrTransact(Irp, // Irp,PICB
|
|||
|
Icb->Fcb->Connection,
|
|||
|
Icb->Se,
|
|||
|
Setup,
|
|||
|
(CLONG) sizeof(Setup), // InSetupCount,
|
|||
|
&OutSetupCount,
|
|||
|
NULL, // Name,
|
|||
|
&Parameters,
|
|||
|
sizeof(Parameters), // InParameterCount,
|
|||
|
&OutParameterCount,
|
|||
|
NULL, // InData,
|
|||
|
0, // InDataCount,
|
|||
|
&Buffer, // OutData,
|
|||
|
&OutDataCount,
|
|||
|
NULL, // Fid
|
|||
|
0, // Timeout
|
|||
|
0, // Flags
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
if (Icb->Fcb->Connection->Server->Capabilities & DF_NT_SMBS) {
|
|||
|
PFILE_FS_SIZE_INFORMATION SizeInfo = &Buffer.FsSizeInfo;
|
|||
|
|
|||
|
*TotalAllocationUnits = SizeInfo->TotalAllocationUnits;
|
|||
|
|
|||
|
*AvailableAllocationUnits = SizeInfo->AvailableAllocationUnits;
|
|||
|
|
|||
|
*SectorsPerAllocationUnit = SizeInfo->SectorsPerAllocationUnit;
|
|||
|
*BytesPerSector = SizeInfo->BytesPerSector;
|
|||
|
|
|||
|
Icb->Fcb->Connection->FileSystemGranularity =
|
|||
|
SizeInfo->SectorsPerAllocationUnit * SizeInfo->BytesPerSector;
|
|||
|
|
|||
|
Icb->Fcb->Connection->FileSystemSize.QuadPart = SizeInfo->TotalAllocationUnits.QuadPart *
|
|||
|
Icb->Fcb->Connection->FileSystemGranularity;
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
} else {
|
|||
|
PQFSALLOCATE FsInfo = &Buffer.FsAllocate;
|
|||
|
|
|||
|
(*TotalAllocationUnits).QuadPart = FsInfo->cUnit;
|
|||
|
|
|||
|
(*AvailableAllocationUnits).QuadPart = FsInfo->cUnitAvail;
|
|||
|
|
|||
|
*SectorsPerAllocationUnit = FsInfo->cSectorUnit;
|
|||
|
|
|||
|
*BytesPerSector = FsInfo->cbSector;
|
|||
|
|
|||
|
Icb->Fcb->Connection->FileSystemGranularity =
|
|||
|
FsInfo->cSectorUnit * FsInfo->cbSector;
|
|||
|
|
|||
|
Icb->Fcb->Connection->FileSystemSize.QuadPart =
|
|||
|
FsInfo->cUnit * FsInfo->cSectorUnit * FsInfo->cbSector;
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
if ((SmbBuffer = RdrAllocateSMBBuffer()) == NULL) {
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
Smb = (PSMB_HEADER )SmbBuffer->Buffer;
|
|||
|
|
|||
|
Smb->Command = SMB_COM_QUERY_INFORMATION_DISK;
|
|||
|
|
|||
|
DskAttr = (PREQ_QUERY_INFORMATION_DISK)(Smb+1);
|
|||
|
|
|||
|
DskAttr->WordCount = 0;
|
|||
|
|
|||
|
SmbPutUshort(&DskAttr->ByteCount, 0);
|
|||
|
|
|||
|
SmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER) + FIELD_OFFSET(REQ_QUERY_INFORMATION, Buffer);
|
|||
|
|
|||
|
Context.Header.Type = CONTEXT_QDISKATTR;
|
|||
|
Context.Header.TransferSize =
|
|||
|
sizeof(PREQ_QUERY_INFORMATION_DISK) +
|
|||
|
sizeof(PRESP_QUERY_INFORMATION_DISK);
|
|||
|
|
|||
|
Status = RdrNetTranceiveWithCallback(NT_NORMAL, Irp,
|
|||
|
Icb->Fcb->Connection,
|
|||
|
SmbBuffer->Mdl,
|
|||
|
&Context,
|
|||
|
QueryDiskAttributesCallback,
|
|||
|
Icb->Se,
|
|||
|
NULL);
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
(*TotalAllocationUnits).QuadPart = Context.TotalAllocationUnits;
|
|||
|
(*AvailableAllocationUnits).QuadPart = Context.AvailableAllocationUnits;
|
|||
|
*SectorsPerAllocationUnit = Context.SectorsPerAllocationUnit;
|
|||
|
*BytesPerSector = Context.BytesPerSector;
|
|||
|
|
|||
|
Icb->Fcb->Connection->FileSystemGranularity =
|
|||
|
Context.BytesPerSector * Context.SectorsPerAllocationUnit;
|
|||
|
|
|||
|
Icb->Fcb->Connection->FileSystemSize.QuadPart =
|
|||
|
Context.TotalAllocationUnits * Context.SectorsPerAllocationUnit * Context.BytesPerSector;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
RdrFreeSMBBuffer(SmbBuffer);
|
|||
|
|
|||
|
}
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
STANDARD_CALLBACK_HEADER (
|
|||
|
QueryDiskAttributesCallback
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the callback routine for the processing of an Open&X SMB.
|
|||
|
|
|||
|
It copies the resulting information from the Open&X SMB into either
|
|||
|
the context block or the ICB supplied directly.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
|
|||
|
IN PSMB_HEADER Smb - SMB response from server.
|
|||
|
IN PMPX_ENTRY MpxEntry - MPX table entry for request.
|
|||
|
IN PSTATCONTEXT Context - Context from caller.
|
|||
|
IN BOOLEAN ErrorIndicator - TRUE if error indication
|
|||
|
IN NTSTATUS NetworkErrorCode OPTIONAL - Network error if error indication.
|
|||
|
IN OUT PIRP *Irp - IRP from TDI
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - STATUS_PENDING if we are to complete the request
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDSKATTRIBCONTEXT Context = Ctx;
|
|||
|
PRESP_QUERY_INFORMATION_DISK DskAttrResponse;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
|||
|
|
|||
|
ASSERT(Context->Header.Type == CONTEXT_QDISKATTR);
|
|||
|
|
|||
|
dprintf(DPRT_FILEINFO, ("QueryDiskAttributesCallback"));
|
|||
|
|
|||
|
Context->Header.ErrorType = NoError; // Assume no error at first.
|
|||
|
|
|||
|
//
|
|||
|
// If we are called because the VC dropped, indicate it in the response
|
|||
|
//
|
|||
|
|
|||
|
if (ErrorIndicator) {
|
|||
|
Context->Header.ErrorType = NetError;
|
|||
|
Context->Header.ErrorCode = RdrMapNetworkError(NetworkErrorCode);
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status = RdrMapSmbError(Smb, Server))) {
|
|||
|
Context->Header.ErrorType = SMBError;
|
|||
|
Context->Header.ErrorCode = Status;
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
DskAttrResponse = (PRESP_QUERY_INFORMATION_DISK )(Smb+1);
|
|||
|
|
|||
|
Context->TotalAllocationUnits = SmbGetUshort(&DskAttrResponse->TotalUnits);
|
|||
|
Context->AvailableAllocationUnits =
|
|||
|
SmbGetUshort(&DskAttrResponse->FreeUnits);
|
|||
|
Context->SectorsPerAllocationUnit =
|
|||
|
SmbGetUshort(&DskAttrResponse->BlocksPerUnit);
|
|||
|
Context->BytesPerSector = SmbGetUshort(&DskAttrResponse->BlockSize);
|
|||
|
|
|||
|
ReturnStatus:
|
|||
|
KeSetEvent(&Context->Header.KernelEvent, IO_NETWORK_INCREMENT, FALSE);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(MpxEntry);
|
|||
|
UNREFERENCED_PARAMETER(Irp);
|
|||
|
UNREFERENCED_PARAMETER(SmbLength);
|
|||
|
UNREFERENCED_PARAMETER(Server);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// RdrQueryEndOfFile
|
|||
|
//
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RdrQueryEndOfFile (
|
|||
|
IN PIRP Irp,
|
|||
|
IN PICB Icb,
|
|||
|
OUT PLARGE_INTEGER EndOfFile
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets the end of a specified file on the remote file system.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PIRP Irp - Supplies an IRP to use for the request
|
|||
|
IN PICB Icb - Supplies a pointer to an open instance of the file.
|
|||
|
OUT LARGE_INTEGER EndOfFile - Supplies the file's actual size.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSMB_BUFFER SMBBuffer;
|
|||
|
PSMB_HEADER Smb;
|
|||
|
NTSTATUS Status;
|
|||
|
PREQ_SEEK Seek;
|
|||
|
PMDL SendMDL;
|
|||
|
QENDOFFILECONTEXT Context;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
Context.Header.Type = CONTEXT_SEEK;
|
|||
|
Context.Header.TransferSize = sizeof(REQ_SEEK) + sizeof(RESP_SEEK);
|
|||
|
|
|||
|
if ((SMBBuffer = RdrAllocateSMBBuffer())==NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
Smb = (PSMB_HEADER ) SMBBuffer->Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// Build the SMB
|
|||
|
//
|
|||
|
|
|||
|
Smb->Command = SMB_COM_SEEK;
|
|||
|
|
|||
|
//
|
|||
|
// We implement SetEndOfFile by issuing a SEEK SMB of 0 bytes.
|
|||
|
//
|
|||
|
|
|||
|
Seek = (PREQ_SEEK ) (Smb+1);
|
|||
|
|
|||
|
Seek->WordCount = 4;
|
|||
|
|
|||
|
SmbPutUshort(&Seek->Fid, Icb->FileId);
|
|||
|
SmbPutUshort(&Seek->Mode, 2);
|
|||
|
SmbPutUlong(&Seek->Offset, 0);
|
|||
|
SmbPutUshort(&Seek->ByteCount, (USHORT )0);
|
|||
|
|
|||
|
SendMDL = SMBBuffer->Mdl;
|
|||
|
|
|||
|
SendMDL->ByteCount = sizeof(SMB_HEADER) + sizeof(REQ_SEEK);
|
|||
|
|
|||
|
Status = RdrNetTranceiveWithCallback(NT_NORMAL | NT_NORECONNECT, // Flags
|
|||
|
Irp,
|
|||
|
Icb->Fcb->Connection,
|
|||
|
SendMDL,
|
|||
|
&Context,
|
|||
|
QueryEndOfFileCallback,
|
|||
|
Icb->Se,
|
|||
|
NULL);
|
|||
|
if (Status == STATUS_INVALID_HANDLE) {
|
|||
|
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
|||
|
}
|
|||
|
|
|||
|
#if RDRDBG
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
dprintf(DPRT_CONNECT, ("QueryEndOfFile failed: %X\n", Status));
|
|||
|
}
|
|||
|
#endif // RDRDBG
|
|||
|
|
|||
|
RdrFreeSMBBuffer(SMBBuffer);
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
EndOfFile->HighPart = 0;
|
|||
|
EndOfFile->LowPart = Context.FileSize;
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
STANDARD_CALLBACK_HEADER (
|
|||
|
QueryEndOfFileCallback
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the callback routine for the processing of an Open&X SMB.
|
|||
|
|
|||
|
It copies the resulting information from the Open&X SMB into either
|
|||
|
the context block or the ICB supplied directly.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
|
|||
|
IN PSMB_HEADER Smb - SMB response from server.
|
|||
|
IN PMPX_ENTRY MpxEntry - MPX table entry for request.
|
|||
|
IN PQENDOFFILECONTEXT Context- Context from caller.
|
|||
|
IN BOOLEAN ErrorIndicator - TRUE if error indication
|
|||
|
IN NTSTATUS NetworkErrorCode OPTIONAL - Network error if error indication.
|
|||
|
IN OUT PIRP *Irp - IRP from TDI
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - STATUS_PENDING if we are to complete the request
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PRESP_SEEK Seek;
|
|||
|
PQENDOFFILECONTEXT Context = Ctx;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
|||
|
|
|||
|
ASSERT(Context->Header.Type == CONTEXT_SEEK);
|
|||
|
|
|||
|
dprintf(DPRT_FILEINFO, ("QueryEndOfFileCallback"));
|
|||
|
|
|||
|
Context->Header.ErrorType = NoError; // Assume no error at first.
|
|||
|
|
|||
|
//
|
|||
|
// If we are called because the VC dropped, indicate it in the response
|
|||
|
//
|
|||
|
|
|||
|
if (ErrorIndicator) {
|
|||
|
Context->Header.ErrorType = NetError;
|
|||
|
Context->Header.ErrorCode = RdrMapNetworkError(NetworkErrorCode);
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status = RdrMapSmbError(Smb, Server))) {
|
|||
|
Context->Header.ErrorType = SMBError;
|
|||
|
Context->Header.ErrorCode = Status;
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
Seek = (PRESP_SEEK )(Smb+1);
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the context block with the information from the SMB.
|
|||
|
//
|
|||
|
Context->FileSize = SmbGetUlong(&Seek->Offset);
|
|||
|
|
|||
|
ReturnStatus:
|
|||
|
KeSetEvent(&Context->Header.KernelEvent, IO_NETWORK_INCREMENT, FALSE);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(MpxEntry);
|
|||
|
UNREFERENCED_PARAMETER(Irp);
|
|||
|
UNREFERENCED_PARAMETER(SmbLength);
|
|||
|
UNREFERENCED_PARAMETER(Server);
|
|||
|
}
|
|||
|
NTSTATUS
|
|||
|
RdrDetermineFileAllocation (
|
|||
|
IN PIRP Irp,
|
|||
|
IN PICB Icb,
|
|||
|
OUT PLARGE_INTEGER FileAllocation,
|
|||
|
OUT PLARGE_INTEGER TotalFilesystemSize OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the callback routine for the processing of an Open&X SMB.
|
|||
|
|
|||
|
It copies the resulting information from the Open&X SMB into either
|
|||
|
the context block or the ICB supplied directly.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
|
|||
|
IN PIRP Irp - I/O request packet to use for the operation.
|
|||
|
IN PICB Icb - File ICB to perform operation on.
|
|||
|
OUT PLARGE_INTEGER - Allocation for specified file.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Success or failure of operation.
|
|||
|
|
|||
|
Note:
|
|||
|
Please note that this DETERMINES the allocation of the file from the file
|
|||
|
size. It does NOT query the true value of the file allocation, but instead
|
|||
|
relies on other file system specific information from the remote server
|
|||
|
to determine the information (such as the remote file system granularity).
|
|||
|
|
|||
|
This routine is not 100% reliable if it is called on a file that is not
|
|||
|
opened for some form of exclusive access (either oplocked, or truely
|
|||
|
exclusive)
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
ULONG FileGranularity;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Read the connection's "cached" file system granularity and,
|
|||
|
// if it has been set, calculate the file's size from our local
|
|||
|
// information.
|
|||
|
//
|
|||
|
|
|||
|
FileGranularity = Icb->Fcb->Connection->FileSystemGranularity;
|
|||
|
|
|||
|
if (FileGranularity == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// If the cached granularity hasn't been set, then
|
|||
|
// find out what it is and cache it away.
|
|||
|
//
|
|||
|
|
|||
|
LARGE_INTEGER TotalAllocationUnits, AvailableAllocationUnits;
|
|||
|
ULONG SectorsPerAllocationUnit, BytesPerSector;
|
|||
|
|
|||
|
Status = RdrQueryDiskAttributes(Irp, Icb,
|
|||
|
&TotalAllocationUnits,
|
|||
|
&AvailableAllocationUnits,
|
|||
|
&SectorsPerAllocationUnit,
|
|||
|
&BytesPerSector);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
FileGranularity =
|
|||
|
Icb->Fcb->Connection->FileSystemGranularity =
|
|||
|
BytesPerSector * SectorsPerAllocationUnit;
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(TotalFilesystemSize)) {
|
|||
|
(*TotalFilesystemSize).QuadPart = TotalAllocationUnits.QuadPart * FileGranularity;
|
|||
|
Icb->Fcb->Connection->FileSystemSize = *TotalFilesystemSize;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
if (ARGUMENT_PRESENT(TotalFilesystemSize)) {
|
|||
|
*TotalFilesystemSize = Icb->Fcb->Connection->FileSystemSize;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Sometimes the server can lie and return a granularity of 0. In
|
|||
|
// that case, we can't determine the true allocation so just use the file
|
|||
|
// size.
|
|||
|
//
|
|||
|
|
|||
|
if (FileGranularity == 0) {
|
|||
|
*FileAllocation = Icb->Fcb->Header.FileSize;
|
|||
|
//RdrLog(( "determin", &Icb->Fcb->FileName, 2, FileGranularity, FileAllocation->LowPart ));
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We get VERY sneaky here.
|
|||
|
//
|
|||
|
// The expression (X & (X - 1)) reduces the number of bits
|
|||
|
// in X by one, so if Granularity&(Granularity-1) is 0, then
|
|||
|
// the granularity is a power of two.
|
|||
|
//
|
|||
|
// Rounding up by a power of two is significantly easier than
|
|||
|
// rounding up by an arbitrary number, so if the file system
|
|||
|
// granularity is a power of two, we can do this rounding quickly.
|
|||
|
//
|
|||
|
//
|
|||
|
// %99.9999999999 (insert a lot of 9's here) of the time, the
|
|||
|
// remote file system granularity will be a power of two, so
|
|||
|
// this check is worth the effort of calculating it.
|
|||
|
//
|
|||
|
|
|||
|
if ((FileGranularity & (FileGranularity-1)) == 0) {
|
|||
|
|
|||
|
(*FileAllocation).QuadPart =
|
|||
|
(Icb->Fcb->Header.FileSize.QuadPart + (FileGranularity - 1)) & ~(FileGranularity - 1);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// (((Icb->Fcb->Header.FileSize + (FileGranularity - 1)) /
|
|||
|
// FileGranularity) * FileGranularity));
|
|||
|
//
|
|||
|
|
|||
|
if (FileGranularity == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// If we are unable to determine a reasonable value for
|
|||
|
// the granularity, just return the file size.
|
|||
|
//
|
|||
|
|
|||
|
*FileAllocation = Icb->Fcb->Header.FileSize;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
(*FileAllocation).QuadPart =
|
|||
|
((Icb->Fcb->Header.FileSize.QuadPart + (FileGranularity - 1)) /
|
|||
|
FileGranularity) * FileGranularity;
|
|||
|
}
|
|||
|
}
|
|||
|
//RdrLog(( "determin", &Icb->Fcb->FileName, 2, FileGranularity, FileAllocation->LowPart ));
|
|||
|
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
NTSTATUS
|
|||
|
RdrQueryFileAttributes (
|
|||
|
IN PIRP Irp OPTIONAL,
|
|||
|
IN PICB Icb,
|
|||
|
OUT PQSFILEATTRIB Attributes
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function will return the specified attributes from the remote server.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PIRP Irp - Supplies an IRP to use for the request
|
|||
|
IN PICB Icb - Supplies the ICB representing the file we are reading info.
|
|||
|
OUT PQSFILEATTRIB Attributes - Supplies a structure to fill in.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Final status of operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSMB_BUFFER SmbBuffer;
|
|||
|
PSMB_HEADER Smb;
|
|||
|
PREQ_QUERY_INFORMATION2 QInfo2;
|
|||
|
PCONNECTLISTENTRY Connection = Icb->Fcb->Connection;
|
|||
|
|
|||
|
NTSTATUS Status;
|
|||
|
GETEXPANDEDATTRIBS Context;
|
|||
|
ULONG TranceiveFlags;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
Context.Header.Type = CONTEXT_GETATTRIBS;
|
|||
|
Context.Attributes = Attributes;
|
|||
|
|
|||
|
//
|
|||
|
// The only protocol that supports the ChangeTime is the NT protocol,
|
|||
|
// so zero out the ChangeTime if it cannot be supported.
|
|||
|
//
|
|||
|
|
|||
|
if (!(Connection->Server->Capabilities & DF_NT_SMBS)) {
|
|||
|
Attributes->ChangeTime.HighPart =
|
|||
|
Attributes->ChangeTime.LowPart = 0;
|
|||
|
}
|
|||
|
|
|||
|
if ((SmbBuffer = RdrAllocateSMBBuffer())==NULL) {
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
Smb = (PSMB_HEADER )SmbBuffer->Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// If this is either a non handle based operation (on a directory), or
|
|||
|
// this request is to a non lanman 1.0 server, or the file was
|
|||
|
// pseudo-opened, issue this request as a QPathInfo SMB. If the
|
|||
|
// request is for a directory file, we have a 0 allocation, but
|
|||
|
// if it is a core server, we need to determine the file system
|
|||
|
// granularity, so check the connectlist, and if it is not
|
|||
|
// set, issue an SMBDskAttr and find out the file system's granularity.
|
|||
|
//
|
|||
|
if (!(Icb->Flags & ICB_HASHANDLE) ||
|
|||
|
!(Connection->Server->Capabilities & DF_LANMAN10) ) {
|
|||
|
|
|||
|
PREQ_QUERY_INFORMATION QInfo = (PREQ_QUERY_INFORMATION ) (Smb+1);
|
|||
|
PUCHAR Bufferp = (PUCHAR)QInfo->Buffer;
|
|||
|
PWSTR WPathStart;
|
|||
|
PUCHAR PathStart;
|
|||
|
ULONG Capabilities = Connection->Server->Capabilities;
|
|||
|
|
|||
|
Smb->Command = SMB_COM_QUERY_INFORMATION;
|
|||
|
|
|||
|
if (FlagOn(Icb->NonPagedFcb->Flags, FCB_DFSFILE)) {
|
|||
|
TranceiveFlags = NT_NORMAL | NT_DFSFILE;
|
|||
|
} else {
|
|||
|
TranceiveFlags = NT_NORMAL;
|
|||
|
}
|
|||
|
|
|||
|
Status = RdrCopyNetworkPath((PVOID *)&Bufferp,
|
|||
|
&Icb->Fcb->FileName,
|
|||
|
Connection->Server,
|
|||
|
SMB_FORMAT_ASCII,
|
|||
|
SKIP_SERVER_SHARE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
RdrFreeSMBBuffer(SmbBuffer);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the name specified by the caller is just a \server\share or
|
|||
|
// \server\share\, we can assume that the path exists and we know
|
|||
|
// all the answers. Core servers return errors on the root directory!
|
|||
|
//
|
|||
|
|
|||
|
if (Capabilities & DF_UNICODE) {
|
|||
|
WPathStart = ALIGN_SMB_WSTR(&QInfo->Buffer[1]);
|
|||
|
} else {
|
|||
|
PathStart = (PUCHAR)&QInfo->Buffer[1];
|
|||
|
}
|
|||
|
|
|||
|
if ( ((Capabilities & DF_UNICODE)
|
|||
|
&&
|
|||
|
((WPathStart[0] == L'\\') && (WPathStart[1] == '\0')))
|
|||
|
||
|
|||
|
(!(Capabilities & DF_UNICODE)
|
|||
|
&&
|
|||
|
((PathStart[0] == '\\') && (PathStart[1] == '\0')))) {
|
|||
|
|
|||
|
RdrFreeSMBBuffer(SmbBuffer);
|
|||
|
|
|||
|
ZERO_TIME(Attributes->ChangeTime);
|
|||
|
Attributes->AllocationSize = 0;
|
|||
|
Attributes->FileSize = 0;
|
|||
|
ZERO_TIME(Attributes->CreationTime);
|
|||
|
ZERO_TIME(Attributes->LastAccessTime);
|
|||
|
ZERO_TIME(Attributes->LastWriteTime);
|
|||
|
ZERO_TIME(Attributes->ChangeTime);
|
|||
|
|
|||
|
if (Icb->Fcb->Connection->Type == CONNECT_DISK) {
|
|||
|
Attributes->Attributes = RdrMapSmbAttributes(
|
|||
|
SMB_FILE_ATTRIBUTE_DIRECTORY | SMB_FILE_ATTRIBUTE_ARCHIVE);
|
|||
|
} else if (Icb->Fcb->Connection->Type == CONNECT_PRINT) {
|
|||
|
return( STATUS_INVALID_DEVICE_REQUEST );
|
|||
|
} else {
|
|||
|
Attributes->Attributes = RdrMapSmbAttributes(
|
|||
|
SMB_FILE_ATTRIBUTE_ARCHIVE);
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Set the word count in the SMB correctly.
|
|||
|
//
|
|||
|
|
|||
|
QInfo->WordCount = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Set the BCC field in the SMB to indicate the number of bytes of
|
|||
|
// protocol we've put in the negotiate.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutUshort( &QInfo->ByteCount,
|
|||
|
(USHORT )(Bufferp-(PUCHAR)QInfo-FIELD_OFFSET(REQ_QUERY_INFORMATION, Buffer)));
|
|||
|
|
|||
|
SmbBuffer->Mdl->ByteCount = Bufferp-(PUCHAR )(Smb);
|
|||
|
|
|||
|
Context.Header.TransferSize =
|
|||
|
SmbBuffer->Mdl->ByteCount + sizeof(RESP_QUERY_INFORMATION);
|
|||
|
|
|||
|
Status = RdrNetTranceiveWithCallback(TranceiveFlags, Irp,
|
|||
|
Connection,
|
|||
|
SmbBuffer->Mdl,
|
|||
|
&Context,
|
|||
|
GetAttribsCallback,
|
|||
|
Icb->Se,
|
|||
|
NULL);
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
if (Icb->Type != Directory) {
|
|||
|
ULONG FileGranularity;
|
|||
|
|
|||
|
//
|
|||
|
// This is not a directory.
|
|||
|
//
|
|||
|
// In order to fully fill in the Qfileattribs structure,
|
|||
|
// we have to send an SMBdskattr SMB to the remote server
|
|||
|
// to determine the remote file system's cluster
|
|||
|
// granularity.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// There are some VAX servers out there that don't return
|
|||
|
// accurate information for the file size, so if this is
|
|||
|
// a core server, regenerate the file size.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
if (!FlagOn(Connection->Server->Capabilities, DF_LANMAN10) &&
|
|||
|
FlagOn(Icb->Flags, ICB_HASHANDLE)) {
|
|||
|
LARGE_INTEGER FileSize;
|
|||
|
|
|||
|
Status = RdrQueryEndOfFile(Irp, Icb, &FileSize);
|
|||
|
|
|||
|
//
|
|||
|
// If the query end of file failed, return an error now.
|
|||
|
//
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
RdrFreeSMBBuffer(SmbBuffer);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
Attributes->FileSize = FileSize.LowPart;
|
|||
|
}
|
|||
|
|
|||
|
FileGranularity = Connection->FileSystemGranularity;
|
|||
|
|
|||
|
if (FileGranularity==0) {
|
|||
|
LARGE_INTEGER TotalAllocationUnits;
|
|||
|
LARGE_INTEGER AvailableAllocationUnits;
|
|||
|
ULONG SectorsPerAllocationUnit;
|
|||
|
ULONG BytesPerSector;
|
|||
|
|
|||
|
Status = RdrQueryDiskAttributes(Irp, Icb,
|
|||
|
&TotalAllocationUnits,
|
|||
|
&AvailableAllocationUnits,
|
|||
|
&SectorsPerAllocationUnit,
|
|||
|
&BytesPerSector);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
RdrFreeSMBBuffer(SmbBuffer);
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
FileGranularity = Connection->FileSystemGranularity =
|
|||
|
BytesPerSector * SectorsPerAllocationUnit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Sometimes the server can lie and return a granularity of 0. In
|
|||
|
// that case, we can't determine the true allocation so just use the file
|
|||
|
// size.
|
|||
|
//
|
|||
|
|
|||
|
if (FileGranularity == 0) {
|
|||
|
Attributes->AllocationSize = Attributes->FileSize;
|
|||
|
|
|||
|
RdrFreeSMBBuffer(SmbBuffer);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We round the file size up by the remote file system
|
|||
|
// granularity, and we're done.
|
|||
|
//
|
|||
|
|
|||
|
Attributes->AllocationSize =
|
|||
|
(((Attributes->FileSize + FileGranularity - 1) /
|
|||
|
FileGranularity) * FileGranularity);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
QInfo2 = (PREQ_QUERY_INFORMATION2 )(Smb+1);
|
|||
|
|
|||
|
Smb->Command = SMB_COM_QUERY_INFORMATION2;
|
|||
|
QInfo2->WordCount = 1;
|
|||
|
SmbPutUshort(&QInfo2->Fid, Icb->FileId);
|
|||
|
SmbPutUshort(&QInfo2->ByteCount, 0);
|
|||
|
|
|||
|
SmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER) +
|
|||
|
sizeof(REQ_QUERY_INFORMATION2);
|
|||
|
|
|||
|
Context.Header.TransferSize =
|
|||
|
SmbBuffer->Mdl->ByteCount + sizeof(RESP_QUERY_INFORMATION2);
|
|||
|
|
|||
|
Status = RdrNetTranceiveWithCallback(NT_NORMAL | NT_NORECONNECT, Irp,
|
|||
|
Connection,
|
|||
|
SmbBuffer->Mdl,
|
|||
|
&Context,
|
|||
|
GetAttribs2Callback,
|
|||
|
Icb->Se,
|
|||
|
NULL);
|
|||
|
|
|||
|
if (Status == STATUS_INVALID_HANDLE) {
|
|||
|
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
RdrFreeSMBBuffer(SmbBuffer);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
STANDARD_CALLBACK_HEADER (
|
|||
|
GetAttribsCallback
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the callback routine for the processing of an Open&X SMB.
|
|||
|
|
|||
|
It copies the resulting information from the Open&X SMB into either
|
|||
|
the context block or the ICB supplied directly.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
|
|||
|
IN PSMB_HEADER Smb - SMB response from server.
|
|||
|
IN PMPX_ENTRY MpxEntry - MPX table entry for request.
|
|||
|
IN struct _OpenAndXContext *Context- Context from caller.
|
|||
|
IN BOOLEAN ErrorIndicator - TRUE if error indication
|
|||
|
IN NTSTATUS NetworkErrorCode OPTIONAL - Network error if error indication.
|
|||
|
IN OUT PIRP *Irp - IRP from TDI
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - STATUS_PENDING if we are to complete the request
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PGETEXPANDEDATTRIBS Context = Ctx;
|
|||
|
PRESP_QUERY_INFORMATION GetAttr;
|
|||
|
ULONG Time;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
|||
|
|
|||
|
ASSERT(Context->Header.Type == CONTEXT_GETATTRIBS);
|
|||
|
|
|||
|
dprintf(DPRT_FILEINFO, ("GetAttribsCallback"));
|
|||
|
|
|||
|
Context->Header.ErrorType = NoError; // Assume no error at first.
|
|||
|
|
|||
|
//
|
|||
|
// If we are called because the VC dropped, indicate it in the response
|
|||
|
//
|
|||
|
|
|||
|
if (ErrorIndicator) {
|
|||
|
Context->Header.ErrorType = NetError;
|
|||
|
Context->Header.ErrorCode = RdrMapNetworkError(NetworkErrorCode);
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status = RdrMapSmbError(Smb, Server))) {
|
|||
|
Context->Header.ErrorType = SMBError;
|
|||
|
Context->Header.ErrorCode = Status;
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
GetAttr = (PRESP_QUERY_INFORMATION )(Smb+1);
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the context block with the information from the SMB.
|
|||
|
//
|
|||
|
Context->Attributes->AllocationSize = 0;
|
|||
|
|
|||
|
Context->Attributes->FileSize = SmbGetUlong(&GetAttr->FileSize);
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the attributes block in the SMB with a mapped version of
|
|||
|
// the SMB attributes.
|
|||
|
//
|
|||
|
|
|||
|
Context->Attributes->Attributes =
|
|||
|
RdrMapSmbAttributes(SmbGetUshort(&GetAttr->FileAttributes));
|
|||
|
|
|||
|
//
|
|||
|
// Copy in the NT date corresponding to the date and time in the SMB.
|
|||
|
//
|
|||
|
|
|||
|
Context->Attributes->CreationTime.HighPart =
|
|||
|
Context->Attributes->CreationTime.LowPart = 0;
|
|||
|
|
|||
|
Context->Attributes->LastAccessTime.HighPart =
|
|||
|
Context->Attributes->LastAccessTime.LowPart = 0;
|
|||
|
|
|||
|
Time = SmbGetUlong (&GetAttr->LastWriteTimeInSeconds);
|
|||
|
|
|||
|
RdrSecondsSince1970ToTime (Time, Server, &Context->Attributes->LastWriteTime);
|
|||
|
|
|||
|
ReturnStatus:
|
|||
|
KeSetEvent(&Context->Header.KernelEvent, IO_NETWORK_INCREMENT, FALSE);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(MpxEntry);
|
|||
|
UNREFERENCED_PARAMETER(Irp);
|
|||
|
UNREFERENCED_PARAMETER(SmbLength);
|
|||
|
}
|
|||
|
|
|||
|
DBGSTATIC
|
|||
|
STANDARD_CALLBACK_HEADER (
|
|||
|
GetAttribs2Callback
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the callback routine for the processing of an Open&X SMB.
|
|||
|
|
|||
|
It copies the resulting information from the Open&X SMB into either
|
|||
|
the context block or the ICB supplied directly.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
|
|||
|
IN PSMB_HEADER Smb - SMB response from server.
|
|||
|
IN PMPX_ENTRY MpxEntry - MPX table entry for request.
|
|||
|
IN struct _OpenAndXContext *Context- Context from caller.
|
|||
|
IN BOOLEAN ErrorIndicator - TRUE if error indication
|
|||
|
IN NTSTATUS NetworkErrorCode OPTIONAL - Network error if error indication.
|
|||
|
IN OUT PIRP *Irp - IRP from TDI
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - STATUS_PENDING if we are to complete the request
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PRESP_QUERY_INFORMATION2 GetAttr;
|
|||
|
PGETEXPANDEDATTRIBS Context = Ctx;
|
|||
|
SMB_TIME Time;
|
|||
|
SMB_DATE Date;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
|||
|
|
|||
|
ASSERT(Context->Header.Type == CONTEXT_GETATTRIBS);
|
|||
|
|
|||
|
dprintf(DPRT_FILEINFO, ("GetAttribs2Callback"));
|
|||
|
|
|||
|
Context->Header.ErrorType = NoError; // Assume no error at first.
|
|||
|
|
|||
|
//
|
|||
|
// If we are called because the VC dropped, indicate it in the response
|
|||
|
//
|
|||
|
|
|||
|
if (ErrorIndicator) {
|
|||
|
Context->Header.ErrorType = NetError;
|
|||
|
Context->Header.ErrorCode = RdrMapNetworkError(NetworkErrorCode);
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status = RdrMapSmbError(Smb, Server))) {
|
|||
|
Context->Header.ErrorType = SMBError;
|
|||
|
Context->Header.ErrorCode = Status;
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
GetAttr = (PRESP_QUERY_INFORMATION2 )(Smb+1);
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the context block with the information from the SMB.
|
|||
|
//
|
|||
|
Context->Attributes->AllocationSize =
|
|||
|
SmbGetUlong(&GetAttr->FileAllocationSize);
|
|||
|
|
|||
|
Context->Attributes->FileSize = SmbGetUlong(&GetAttr->FileDataSize);
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the attributes block in the SMB with a mapped version of
|
|||
|
// the SMB attributes.
|
|||
|
//
|
|||
|
|
|||
|
Context->Attributes->Attributes =
|
|||
|
RdrMapSmbAttributes(SmbGetUshort(&GetAttr->FileAttributes));
|
|||
|
|
|||
|
//
|
|||
|
// Copy in the NT date corresponding to the date and time in the SMB.
|
|||
|
//
|
|||
|
|
|||
|
SmbMoveTime (&Time, &GetAttr->CreationTime);
|
|||
|
SmbMoveDate (&Date, &GetAttr->CreationDate);
|
|||
|
Context->Attributes->CreationTime = RdrConvertSmbTimeToTime(Time, Date, Server);
|
|||
|
|
|||
|
SmbMoveTime (&Time, &GetAttr->LastAccessTime);
|
|||
|
SmbMoveDate (&Date, &GetAttr->LastAccessDate);
|
|||
|
Context->Attributes->LastAccessTime = RdrConvertSmbTimeToTime(Time, Date, Server);
|
|||
|
|
|||
|
SmbMoveTime (&Time, &GetAttr->LastWriteTime);
|
|||
|
SmbMoveDate (&Date, &GetAttr->LastWriteDate);
|
|||
|
Context->Attributes->LastWriteTime = RdrConvertSmbTimeToTime(Time, Date, Server);
|
|||
|
|
|||
|
ReturnStatus:
|
|||
|
KeSetEvent(&Context->Header.KernelEvent, IO_NETWORK_INCREMENT, FALSE);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(MpxEntry);
|
|||
|
UNREFERENCED_PARAMETER(Irp);
|
|||
|
UNREFERENCED_PARAMETER(SmbLength);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
//
|
|||
|
// RdrRenameFile
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RdrRenameFile (
|
|||
|
IN PIRP Irp OPTIONAL,
|
|||
|
IN PICB Icb,
|
|||
|
IN PUNICODE_STRING OriginalFileName,
|
|||
|
IN PUNICODE_STRING NewFileName,
|
|||
|
IN USHORT NtInformationLevel,
|
|||
|
IN ULONG ClusterCount
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sends a RENAME (SMBmv) SMB to the remote server.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PIRP Irp OPTIONAL - Supplies an IRP to use in the transaction.
|
|||
|
IN PUNICODE_STRING OriginalFileName - Supplies the original (current) file name.
|
|||
|
IN PUNICODE_STRING NewFileName - Supplies the new file name.
|
|||
|
IN USHORT NtInformationLevel - Nt info level
|
|||
|
IN ULONG ClusterCount - MoveCluster count of clusters
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Status of delete operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSMB_BUFFER SMBBuffer;
|
|||
|
PSMB_HEADER Smb;
|
|||
|
NTSTATUS Status;
|
|||
|
PREQ_RENAME RenameFile;
|
|||
|
PREQ_NTRENAME NtRenameFile;
|
|||
|
PUCHAR RenameBuffer;
|
|||
|
PUCHAR Buffer = NULL;
|
|||
|
PUCHAR Bufferp;
|
|||
|
PMDL SendMDL;
|
|||
|
BOOLEAN isNtRename;
|
|||
|
ULONG BufferLength;
|
|||
|
PSERVERLISTENTRY Server = Icb->Fcb->Connection->Server;
|
|||
|
ULONG TranceiveFlag = FlagOn(Icb->NonPagedFcb->Flags, FCB_DFSFILE) ? NT_DFSFILE : 0;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if ((SMBBuffer = RdrAllocateSMBBuffer())==NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
Smb = (PSMB_HEADER ) SMBBuffer->Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// Build the SMB
|
|||
|
//
|
|||
|
|
|||
|
RenameFile = (PREQ_RENAME )(Smb+1);
|
|||
|
NtRenameFile = (PREQ_NTRENAME)(Smb+1);
|
|||
|
|
|||
|
isNtRename = NtInformationLevel != FileRenameInformation;
|
|||
|
|
|||
|
if (!isNtRename) {
|
|||
|
Smb->Command = SMB_COM_RENAME;
|
|||
|
RenameFile->WordCount = 1;
|
|||
|
RenameBuffer = RenameFile->Buffer;
|
|||
|
} else {
|
|||
|
USHORT infolevel;
|
|||
|
|
|||
|
Smb->Command = SMB_COM_NT_RENAME;
|
|||
|
NtRenameFile->WordCount = 4;
|
|||
|
RenameBuffer = NtRenameFile->Buffer;
|
|||
|
switch (NtInformationLevel)
|
|||
|
{
|
|||
|
case FileCopyOnWriteInformation:
|
|||
|
infolevel = SMB_NT_RENAME_SET_COPY_ON_WRITE;
|
|||
|
break;
|
|||
|
case FileMoveClusterInformation:
|
|||
|
infolevel = SMB_NT_RENAME_MOVE_CLUSTER_INFO;
|
|||
|
break;
|
|||
|
case FileLinkInformation:
|
|||
|
infolevel = SMB_NT_RENAME_SET_LINK_INFO;
|
|||
|
break;
|
|||
|
default:
|
|||
|
ASSERT(FALSE);
|
|||
|
}
|
|||
|
if (NtInformationLevel != FileMoveClusterInformation) {
|
|||
|
ClusterCount = 0;
|
|||
|
}
|
|||
|
SmbPutUshort(&NtRenameFile->InformationLevel, infolevel);
|
|||
|
SmbPutUlong(&NtRenameFile->ClusterCount, ClusterCount);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We put a hard coded search attributes of 0x16 in the
|
|||
|
// SMB.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(FIELD_OFFSET(REQ_RENAME, SearchAttributes) ==
|
|||
|
FIELD_OFFSET(REQ_NTRENAME, SearchAttributes));
|
|||
|
|
|||
|
SmbPutUshort(&RenameFile->SearchAttributes, SMB_FILE_ATTRIBUTE_SYSTEM | SMB_FILE_ATTRIBUTE_HIDDEN | SMB_FILE_ATTRIBUTE_DIRECTORY);
|
|||
|
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// How much do we need to pad the start of Buffer so that Bufferp
|
|||
|
// has the same word/byte alignment as FileRename->Buffer[0]
|
|||
|
//
|
|||
|
|
|||
|
ULONG Pad = (ULONG)ALIGN_SMB_WSTR(RenameBuffer) - (ULONG)RenameBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a buffer for the worst case rename
|
|||
|
//
|
|||
|
|
|||
|
Buffer = ALLOCATE_POOL (NonPagedPool,
|
|||
|
OriginalFileName->Length +
|
|||
|
NewFileName->Length +
|
|||
|
(4 * sizeof(WCHAR)) +
|
|||
|
Pad, POOL_DUPSTRING);
|
|||
|
|
|||
|
if ( Buffer == NULL ) {
|
|||
|
try_return( Status = STATUS_INSUFFICIENT_RESOURCES);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Put the start of the Bufferp to the same alignment as the next
|
|||
|
// character position in the Smb packet. This allows
|
|||
|
// RdrCopyNetworkPath to naturally align the unicode characters
|
|||
|
// in the packet.
|
|||
|
//
|
|||
|
|
|||
|
Bufferp = Buffer + Pad;
|
|||
|
|
|||
|
Status = RdrCopyNetworkPath((PVOID *)&Bufferp, OriginalFileName, Server, SMB_FORMAT_ASCII, SKIP_SERVER_SHARE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
RdrFreeSMBBuffer(SMBBuffer);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
Status = RdrCopyNetworkPath((PVOID *)&Bufferp, NewFileName, Server, SMB_FORMAT_ASCII, SKIP_SERVER_SHARE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
RdrFreeSMBBuffer(SMBBuffer);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
SendMDL = SMBBuffer->Mdl;
|
|||
|
|
|||
|
//
|
|||
|
// Set the BCC field in the SMB to indicate the number of bytes of
|
|||
|
// protocol we've put in the negotiate.
|
|||
|
//
|
|||
|
|
|||
|
if (isNtRename) {
|
|||
|
SmbPutUshort(
|
|||
|
&NtRenameFile->ByteCount,
|
|||
|
(USHORT)(Bufferp - Buffer) - (USHORT)Pad);
|
|||
|
SendMDL->ByteCount = (NtRenameFile->Buffer - (PUCHAR)(Smb));
|
|||
|
} else {
|
|||
|
SmbPutUshort(
|
|||
|
&RenameFile->ByteCount,
|
|||
|
(USHORT)(Bufferp - Buffer) - (USHORT)Pad);
|
|||
|
SendMDL->ByteCount = (RenameFile->Buffer - (PUCHAR)(Smb));
|
|||
|
}
|
|||
|
|
|||
|
BufferLength = Bufferp - (Buffer + Pad);
|
|||
|
|
|||
|
SendMDL->Next = IoAllocateMdl(Buffer + Pad, BufferLength, FALSE, FALSE, NULL);
|
|||
|
|
|||
|
if ( SendMDL->Next == NULL ) {
|
|||
|
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
|
|||
|
}
|
|||
|
|
|||
|
if ( SendMDL->ByteCount + BufferLength > Icb->Fcb->Connection->Server->BufferSize) {
|
|||
|
|
|||
|
// Resulting packet too large for server to handle
|
|||
|
try_return (Status = STATUS_OBJECT_NAME_INVALID);
|
|||
|
}
|
|||
|
|
|||
|
MmBuildMdlForNonPagedPool(SendMDL->Next);
|
|||
|
|
|||
|
Status = RdrNetTranceive(
|
|||
|
TranceiveFlag | NT_NORMAL, // Flags
|
|||
|
Irp,
|
|||
|
Icb->Fcb->Connection,
|
|||
|
SendMDL,
|
|||
|
NULL, // Only interested in the error code.
|
|||
|
Icb->Se);
|
|||
|
|
|||
|
try_exit:NOTHING;
|
|||
|
} finally {
|
|||
|
|
|||
|
#if RDRDBG
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
dprintf(DPRT_CONNECT, ("RenameFile failed: %X\n", Status));
|
|||
|
}
|
|||
|
#endif // RDRDBG
|
|||
|
|
|||
|
if (SendMDL->Next != NULL) {
|
|||
|
IoFreeMdl( SendMDL->Next );
|
|||
|
}
|
|||
|
|
|||
|
if (Buffer != NULL) {
|
|||
|
FREE_POOL(Buffer);
|
|||
|
}
|
|||
|
|
|||
|
RdrFreeSMBBuffer(SMBBuffer);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
//
|
|||
|
// RdrSetEndOfFile
|
|||
|
//
|
|||
|
//
|
|||
|
NTSTATUS
|
|||
|
RdrSetEndOfFile (
|
|||
|
IN PIRP Irp OPTIONAL,
|
|||
|
IN PICB Icb,
|
|||
|
IN LARGE_INTEGER EndOfFile
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets the end of a specified file on the remote file system.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PIRP Irp - Supplies an IRP to use for the request
|
|||
|
IN PICB Icb - Supplies a pointer to an open instance of the file.
|
|||
|
IN LARGE_INTEGER EndOfFile - Supplies the file's size.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSMB_BUFFER SMBBuffer;
|
|||
|
PSMB_HEADER Smb;
|
|||
|
NTSTATUS Status;
|
|||
|
PREQ_WRITE Write;
|
|||
|
PMDL SendMDL;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if (!FlagOn(Icb->Flags, ICB_HASHANDLE)) {
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is an NT server, and we are growing the file to more than 4G,
|
|||
|
// then extend the file using the T2 SetEndOfFile API. Otherwise, use
|
|||
|
// a 0 length write SMB.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn(Icb->Fcb->Connection->Server->Capabilities, DF_NT_SMBS) &&
|
|||
|
EndOfFile.HighPart != 0) {
|
|||
|
//
|
|||
|
// Use TRANSACT2_SETFILEINFO to set FILE_END_OF_FILE_INFORMATION
|
|||
|
// for NT servers.
|
|||
|
//
|
|||
|
|
|||
|
USHORT Setup[] = {TRANS2_SET_FILE_INFORMATION};
|
|||
|
|
|||
|
REQ_SET_FILE_INFORMATION Parameters;
|
|||
|
FILE_END_OF_FILE_INFORMATION EndOfFileInfo;
|
|||
|
|
|||
|
CLONG OutParameterCount = sizeof(REQ_SET_FILE_INFORMATION);
|
|||
|
|
|||
|
CLONG OutDataCount = 0;
|
|||
|
|
|||
|
CLONG OutSetupCount = 0;
|
|||
|
|
|||
|
EndOfFileInfo.EndOfFile = EndOfFile;
|
|||
|
|
|||
|
SmbPutAlignedUshort(&Parameters.InformationLevel, SMB_SET_FILE_END_OF_FILE_INFO);
|
|||
|
|
|||
|
SmbPutAlignedUshort(&Parameters.Fid, Icb->FileId);
|
|||
|
|
|||
|
Status = RdrTransact(Irp, // Irp,
|
|||
|
Icb->Fcb->Connection,
|
|||
|
Icb->Se,
|
|||
|
Setup,
|
|||
|
(CLONG) sizeof(Setup), // InSetupCount,
|
|||
|
&OutSetupCount,
|
|||
|
NULL, // Name,
|
|||
|
&Parameters,
|
|||
|
sizeof(Parameters), // InParameterCount,
|
|||
|
&OutParameterCount,
|
|||
|
&EndOfFileInfo, // InData,
|
|||
|
sizeof(FILE_END_OF_FILE_INFORMATION), // InDataCount,
|
|||
|
NULL, // OutData,
|
|||
|
&OutDataCount, // OutDataCount
|
|||
|
&Icb->FileId, // Fid
|
|||
|
0, // Timeout
|
|||
|
0, // Flags
|
|||
|
0, // NtTransact function
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
if (EndOfFile.HighPart != 0) {
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
if ((SMBBuffer = RdrAllocateSMBBuffer())==NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
Smb = (PSMB_HEADER ) SMBBuffer->Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// Build the SMB
|
|||
|
//
|
|||
|
|
|||
|
Smb->Command = SMB_COM_WRITE;
|
|||
|
|
|||
|
//
|
|||
|
// We implement SetEndOfFile by issuing a WRITE SMB of 0 bytes.
|
|||
|
//
|
|||
|
|
|||
|
Write = (PREQ_WRITE ) (Smb+1);
|
|||
|
|
|||
|
Write->WordCount = 5;
|
|||
|
|
|||
|
SmbPutUshort(&Write->Fid, Icb->FileId);
|
|||
|
SmbPutUshort(&Write->Count, 0);
|
|||
|
SmbPutUlong(&Write->Offset, EndOfFile.LowPart);
|
|||
|
SmbPutUshort(&Write->Remaining, 0);
|
|||
|
SmbPutUshort(&Write->ByteCount, (USHORT )3);
|
|||
|
|
|||
|
Write->BufferFormat = SMB_FORMAT_DATA;
|
|||
|
|
|||
|
SmbPutUshort(&Write->DataLength, 0);// No data to send.
|
|||
|
|
|||
|
SendMDL = SMBBuffer->Mdl;
|
|||
|
|
|||
|
SendMDL->ByteCount = sizeof(SMB_HEADER) + FIELD_OFFSET(REQ_WRITE, Buffer[0]);
|
|||
|
|
|||
|
Status = RdrNetTranceive(NT_NORMAL | NT_NORECONNECT | NT_PREFER_LONGTERM, // Flags
|
|||
|
Irp,
|
|||
|
Icb->Fcb->Connection,
|
|||
|
SendMDL,
|
|||
|
NULL, // Only interested in the error code.
|
|||
|
Icb->Se);
|
|||
|
|
|||
|
#if RDRDBG
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
dprintf(DPRT_CONNECT, ("SetEndOfFile failed: %X\n", Status));
|
|||
|
}
|
|||
|
#endif // RDRDBG
|
|||
|
|
|||
|
if (Status == STATUS_INVALID_HANDLE) {
|
|||
|
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
|||
|
}
|
|||
|
|
|||
|
RdrFreeSMBBuffer(SMBBuffer);
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// RdrSetFileAttributes
|
|||
|
//
|
|||
|
NTSTATUS
|
|||
|
RdrSetFileAttributes (
|
|||
|
IN PIRP Irp,
|
|||
|
IN PICB Icb,
|
|||
|
IN PFILE_BASIC_INFORMATION FileAttribs
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will perform whatever operations are necessary to set the
|
|||
|
supplied file attributes on the remote file. If the supplied file is
|
|||
|
a directory, it will generate a core SMBsetattr SMB, if it is a file,
|
|||
|
it will use a SMBsetattre SMB.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PICB Icb - Supplies the file to set the attributes on.
|
|||
|
IN PFILE_BASIC_INFORMATION FileAttribs - Supplies the attributes to change
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Final status of operation
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSMB_BUFFER SmbBuffer = NULL;
|
|||
|
PSMB_HEADER Smb;
|
|||
|
NTSTATUS Status = STATUS_NOT_SUPPORTED;
|
|||
|
PREQ_SET_INFORMATION SInfo;
|
|||
|
PUCHAR Bufferp;
|
|||
|
|
|||
|
#ifdef NOTIFY
|
|||
|
ULONG NotifyFilter = 0;
|
|||
|
#endif
|
|||
|
PSERVERLISTENTRY Server = Icb->Fcb->Connection->Server;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
|
|||
|
if ((Server->Capabilities & DF_NT_SMBS) &&
|
|||
|
(Icb->Flags & ICB_HASHANDLE)) {
|
|||
|
|
|||
|
//
|
|||
|
// Use TRANSACT2_SETFILEINFO to set FILE_BASIC_INFORMATION.
|
|||
|
//
|
|||
|
|
|||
|
USHORT Setup[] = {TRANS2_SET_FILE_INFORMATION};
|
|||
|
|
|||
|
REQ_SET_FILE_INFORMATION Parameters;
|
|||
|
|
|||
|
CLONG OutParameterCount = sizeof(REQ_SET_FILE_INFORMATION);
|
|||
|
|
|||
|
CLONG OutDataCount = 0;
|
|||
|
|
|||
|
CLONG OutSetupCount = 0;
|
|||
|
|
|||
|
SmbPutAlignedUshort(&Parameters.InformationLevel, SMB_SET_FILE_BASIC_INFO);
|
|||
|
|
|||
|
SmbPutAlignedUshort(&Parameters.Fid, Icb->FileId);
|
|||
|
|
|||
|
Status = RdrTransact(Irp, // Irp,
|
|||
|
Icb->Fcb->Connection,
|
|||
|
Icb->Se,
|
|||
|
Setup,
|
|||
|
(CLONG) sizeof(Setup), // InSetupCount,
|
|||
|
&OutSetupCount,
|
|||
|
NULL, // Name,
|
|||
|
&Parameters,
|
|||
|
sizeof(Parameters), // InParameterCount,
|
|||
|
&OutParameterCount,
|
|||
|
FileAttribs, // InData,
|
|||
|
sizeof(FILE_BASIC_INFORMATION),
|
|||
|
NULL, // OutData,
|
|||
|
&OutDataCount, // OutDataCount,
|
|||
|
&Icb->FileId, // Fid
|
|||
|
0, // Timeout
|
|||
|
(USHORT) (FlagOn(Icb->NonPagedFcb->Flags, FCB_DFSFILE) ? SMB_TRANSACTION_DFSFILE : 0),
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
if (Status == STATUS_INVALID_HANDLE) {
|
|||
|
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// If the user doesn't want us to change the file attributes, or
|
|||
|
// this is on a core server, use the core SMB.
|
|||
|
//
|
|||
|
|
|||
|
if ((SmbBuffer = RdrAllocateSMBBuffer())==NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
Smb = (PSMB_HEADER )SmbBuffer->Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// If we are trying to set the file attributes,
|
|||
|
// or this is a core (or LM 1.0) server, use the SET_INFORMATION SMB to
|
|||
|
// set the file times.
|
|||
|
//
|
|||
|
|
|||
|
if (FileAttribs->FileAttributes != 0 ||
|
|||
|
!FlagOn(Server->Capabilities, DF_LANMAN10)) {
|
|||
|
|
|||
|
//
|
|||
|
// First we set the fields of the file information that we can set for
|
|||
|
// all servers. We set the attributes and LastWrite time for the file
|
|||
|
// here.
|
|||
|
//
|
|||
|
|
|||
|
SInfo = (PREQ_SET_INFORMATION ) (Smb+1);
|
|||
|
|
|||
|
Smb->Command = SMB_COM_SET_INFORMATION;
|
|||
|
|
|||
|
SInfo->WordCount = 8;
|
|||
|
|
|||
|
SmbPutUshort(&SInfo->FileAttributes,
|
|||
|
RdrMapFileAttributes(FileAttribs->FileAttributes));
|
|||
|
|
|||
|
if (!FlagOn(Server->Capabilities, DF_LANMAN10) &&
|
|||
|
(FileAttribs->LastWriteTime.QuadPart != 0)) {
|
|||
|
ULONG SecondsSince1970;
|
|||
|
|
|||
|
if (!RdrTimeToSecondsSince1970(&FileAttribs->LastWriteTime,
|
|||
|
Server,
|
|||
|
&SecondsSince1970)) {
|
|||
|
try_return(Status = STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
SmbPutUlong(&SInfo->LastWriteTimeInSeconds, SecondsSince1970);
|
|||
|
} else {
|
|||
|
SmbPutUlong(&SInfo->LastWriteTimeInSeconds, 0);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the reserved fields in the SMB to 0.
|
|||
|
//
|
|||
|
// Please note that we assume that a ULONG takes 4 bytes and
|
|||
|
// that the reserved field is 10 bytes in total length.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( sizeof ( SInfo->Reserved ) == 10 );
|
|||
|
|
|||
|
SmbPutUlong((PULONG)SInfo->Reserved, 0);
|
|||
|
SmbPutUlong((PULONG)&SInfo->Reserved[2], 0);
|
|||
|
SmbPutUshort(&SInfo->Reserved[4], 0);
|
|||
|
|
|||
|
Bufferp = (PUCHAR)SInfo->Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// Stick the path of the file specified into the SMB.
|
|||
|
// This is a core protocol request, so indicate it as such.
|
|||
|
//
|
|||
|
|
|||
|
Status = RdrCopyNetworkPath((PVOID *)&Bufferp,
|
|||
|
&Icb->Fcb->FileName,
|
|||
|
Server,
|
|||
|
SMB_FORMAT_ASCII,
|
|||
|
SKIP_SERVER_SHARE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
try_return(Status);
|
|||
|
}
|
|||
|
|
|||
|
if (!FlagOn(Server->Capabilities, DF_LANMAN10)) {
|
|||
|
//
|
|||
|
// It appears that the PCLP 1.3 server requires an additional
|
|||
|
// empty path at the end of the SMB.
|
|||
|
//
|
|||
|
// The OS/2 redirector sticks this here, but we don't know why.
|
|||
|
//
|
|||
|
|
|||
|
*Bufferp ++ = SMB_FORMAT_ASCII;
|
|||
|
|
|||
|
*Bufferp ++ = '\0';
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the BCC field in the SMB to indicate the number of bytes of
|
|||
|
// protocol we've put in the negotiate.
|
|||
|
//
|
|||
|
//
|
|||
|
// Please note that the ByteCount field is WORD ALIGNED because
|
|||
|
// the reserved fields are an odd length.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutUshort(&SInfo->ByteCount, (USHORT)(Bufferp-(PUCHAR)SInfo-FIELD_OFFSET(REQ_SET_INFORMATION, Buffer)));
|
|||
|
|
|||
|
SmbBuffer->Mdl->ByteCount = Bufferp-(PUCHAR )(Smb);
|
|||
|
|
|||
|
Status = RdrNetTranceive(NT_NORMAL, Irp,
|
|||
|
Icb->Fcb->Connection,
|
|||
|
SmbBuffer->Mdl,
|
|||
|
NULL,
|
|||
|
Icb->Se);
|
|||
|
//
|
|||
|
// If the setinfo failed, return the error right now.
|
|||
|
//
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
try_return(Status);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the file times are going to be changing, update
|
|||
|
// them appropriately.
|
|||
|
//
|
|||
|
|
|||
|
if ((FileAttribs->CreationTime.QuadPart != 0) ||
|
|||
|
(FileAttribs->LastWriteTime.QuadPart != 0) ||
|
|||
|
(FileAttribs->LastAccessTime.QuadPart != 0)) {
|
|||
|
|
|||
|
//
|
|||
|
// Now that we have set the file's attributes if appropriate,
|
|||
|
// check to see if the user has requested that we change the file's
|
|||
|
// creation time or last access time. If so, then we want to set them
|
|||
|
// using the SetInformation2 SMB.
|
|||
|
//
|
|||
|
|
|||
|
if (Icb->Flags & ICB_HASHANDLE) {
|
|||
|
|
|||
|
if (Server->Capabilities & DF_LANMAN10) {
|
|||
|
|
|||
|
//
|
|||
|
// This file was opened "for real", and has an open instance on
|
|||
|
// the remote server, it was a lanman 1.0 server, and the user
|
|||
|
// wanted to update one of the file times.
|
|||
|
//
|
|||
|
|
|||
|
PREQ_SET_INFORMATION2 SInfo2;
|
|||
|
SMB_TIME Time;
|
|||
|
SMB_DATE Date;
|
|||
|
|
|||
|
SInfo2 = (PREQ_SET_INFORMATION2 )(Smb+1);
|
|||
|
|
|||
|
Smb->Command = SMB_COM_SET_INFORMATION2;
|
|||
|
SInfo2->WordCount = 7;
|
|||
|
SmbPutUshort(&SInfo2->Fid, Icb->FileId);
|
|||
|
SmbPutUshort(&SInfo2->ByteCount, 0);
|
|||
|
|
|||
|
//
|
|||
|
// The user wants us to update one of the times on the file.
|
|||
|
//
|
|||
|
// Issue a SetAttributes2 SMB to process his request.
|
|||
|
//
|
|||
|
//
|
|||
|
if (!RdrConvertTimeToSmbTime(&FileAttribs->CreationTime, Server, &Time, &Date)){
|
|||
|
try_return(Status = STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
SmbPutTime(&SInfo2->CreationTime, Time);
|
|||
|
SmbPutDate(&SInfo2->CreationDate, Date);
|
|||
|
|
|||
|
if (!RdrConvertTimeToSmbTime(&FileAttribs->LastAccessTime,Server, &Time, &Date)) {
|
|||
|
try_return(Status = STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
SmbPutTime(&SInfo2->LastAccessTime, Time);
|
|||
|
SmbPutDate(&SInfo2->LastAccessDate, Date);
|
|||
|
|
|||
|
if (!RdrConvertTimeToSmbTime(&FileAttribs->LastWriteTime, Server, &Time, &Date)) {
|
|||
|
try_return(Status = STATUS_INVALID_PARAMETER);
|
|||
|
};
|
|||
|
|
|||
|
SmbPutTime(&SInfo2->LastWriteTime, Time);
|
|||
|
SmbPutDate(&SInfo2->LastWriteDate, Date);
|
|||
|
|
|||
|
|
|||
|
SmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER) +
|
|||
|
sizeof(REQ_SET_INFORMATION2);
|
|||
|
|
|||
|
Status = RdrNetTranceive(NT_NORMAL | NT_NORECONNECT, Irp,
|
|||
|
Icb->Fcb->Connection,
|
|||
|
SmbBuffer->Mdl,
|
|||
|
NULL,
|
|||
|
Icb->Se);
|
|||
|
if (Status == STATUS_INVALID_HANDLE) {
|
|||
|
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
|||
|
}
|
|||
|
|
|||
|
try_return(Status);
|
|||
|
} else {
|
|||
|
//
|
|||
|
// This is an MS-NET server and we are going to
|
|||
|
// be updating the file times, and we have a handle
|
|||
|
// to the file.
|
|||
|
//
|
|||
|
|
|||
|
if (FileAttribs->LastWriteTime.QuadPart != 0) {
|
|||
|
|
|||
|
//
|
|||
|
// Update the file time on the server when we
|
|||
|
// close the file.
|
|||
|
//
|
|||
|
|
|||
|
Icb->Fcb->LastWriteTime = FileAttribs->LastWriteTime;
|
|||
|
|
|||
|
Icb->Flags |= ICB_SETDATEONCLOSE;
|
|||
|
|
|||
|
try_return(Status = STATUS_SUCCESS);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// We don't have a handle to this file. Set the file
|
|||
|
// times in a manner appropriate to the server.
|
|||
|
//
|
|||
|
|
|||
|
if (Server->Capabilities & DF_LANMAN20) {
|
|||
|
|
|||
|
//
|
|||
|
// If we don't have a handle to this directory,
|
|||
|
// and this is a Lan Manager 2.0 server, we can set the
|
|||
|
// times using a T2SetFileInformation call.
|
|||
|
//
|
|||
|
|
|||
|
USHORT Setup[] = {TRANS2_SET_PATH_INFORMATION};
|
|||
|
|
|||
|
ULONG OutSetupCount = sizeof(Setup);
|
|||
|
CLONG OutParameterCount = sizeof(RESP_SET_PATH_INFORMATION);
|
|||
|
|
|||
|
PUCHAR TempBuffer;
|
|||
|
|
|||
|
FILESTATUS FileStatus;
|
|||
|
ULONG StatusSize = sizeof(FILESTATUS);
|
|||
|
|
|||
|
//
|
|||
|
// The same buffer is used for request and response parameters
|
|||
|
//
|
|||
|
|
|||
|
union {
|
|||
|
struct _Q {
|
|||
|
REQ_SET_PATH_INFORMATION Q;
|
|||
|
UCHAR PathName[MAXIMUM_PATHLEN_LANMAN12];
|
|||
|
} Q;
|
|||
|
RESP_SET_PATH_INFORMATION R;
|
|||
|
} Parameters;
|
|||
|
|
|||
|
RtlZeroMemory(&FileStatus, sizeof(FILESTATUS));
|
|||
|
|
|||
|
SmbPutAlignedUshort( &Parameters.Q.Q.InformationLevel, SMB_INFO_STANDARD);
|
|||
|
SmbPutUlong(&Parameters.Q.Q.Reserved,0);
|
|||
|
|
|||
|
TempBuffer = Parameters.Q.Q.Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// Strip \Server\Share and copy just PATH
|
|||
|
//
|
|||
|
|
|||
|
Status = RdrCopyNetworkPath((PVOID *)&TempBuffer,
|
|||
|
&Icb->Fcb->FileName,
|
|||
|
Server,
|
|||
|
FALSE,
|
|||
|
SKIP_SERVER_SHARE);
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
SMB_TIME Time;
|
|||
|
SMB_DATE Date;
|
|||
|
|
|||
|
if (!RdrConvertTimeToSmbTime(&FileAttribs->CreationTime,
|
|||
|
Server,
|
|||
|
&Time, &Date)) {
|
|||
|
try_return(Status = STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
SmbPutTime(&FileStatus.CreationDate, Time);
|
|||
|
SmbPutDate(&FileStatus.CreationTime, Date);
|
|||
|
|
|||
|
if (!RdrConvertTimeToSmbTime(&FileAttribs->LastAccessTime,
|
|||
|
Server,
|
|||
|
&Time,
|
|||
|
&Date)) {
|
|||
|
try_return(Status = STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
SmbPutTime(&FileStatus.LastAccessTime, Time);
|
|||
|
SmbPutDate(&FileStatus.LastAccessDate, Date);
|
|||
|
|
|||
|
if (!RdrConvertTimeToSmbTime(&FileAttribs->LastWriteTime,
|
|||
|
Server,
|
|||
|
&Time,
|
|||
|
&Date)) {
|
|||
|
try_return(Status = STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
SmbPutTime(&FileStatus.LastWriteTime, Time);
|
|||
|
SmbPutDate(&FileStatus.LastWriteDate, Date);
|
|||
|
|
|||
|
Status = RdrTransact(Irp, // Irp,
|
|||
|
Icb->Fcb->Connection,
|
|||
|
Icb->Se,
|
|||
|
Setup,
|
|||
|
(CLONG) sizeof(Setup), // InSetupCount,
|
|||
|
&OutSetupCount,
|
|||
|
NULL, // Name,
|
|||
|
&Parameters.Q,
|
|||
|
TempBuffer-(PUCHAR)&Parameters, // InParameterCount,
|
|||
|
&OutParameterCount,
|
|||
|
&FileStatus, // InData,
|
|||
|
sizeof(FILESTATUS),
|
|||
|
&FileStatus, // OutData,
|
|||
|
&StatusSize,
|
|||
|
NULL, // Fid
|
|||
|
0, // Timeout
|
|||
|
0, // Flags
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ASSERT(OutParameterCount == sizeof(RESP_SET_PATH_INFORMATION));
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
try_exit:NOTHING;
|
|||
|
} finally {
|
|||
|
|
|||
|
if (SmbBuffer != NULL) {
|
|||
|
RdrFreeSMBBuffer(SmbBuffer);
|
|||
|
}
|
|||
|
|
|||
|
#ifdef NOTIFY
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
if (FileAttribs->FileAttributes) {
|
|||
|
NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
|
|||
|
}
|
|||
|
|
|||
|
if (FileAttribs->LastWriteTime.QuadPart != 0) {
|
|||
|
NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
|
|||
|
}
|
|||
|
|
|||
|
if (FileAttribs->CreationTime.QuadPart != 0) {
|
|||
|
NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION;
|
|||
|
}
|
|||
|
|
|||
|
if (FileAttribs->LastAccessTime.QuadPart != 0) {
|
|||
|
NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
|
|||
|
}
|
|||
|
|
|||
|
if (NotifyFilter) {
|
|||
|
FsRtlNotifyReportChange( Icb->Fcb->Connection->NotifySync,
|
|||
|
&Icb->Fcb->Connection->DirNotifyList,
|
|||
|
(PANSI_STRING)&Icb->Fcb->FileName,
|
|||
|
(PANSI_STRING)&Icb->Fcb->LastFileName,
|
|||
|
NotifyFilter );
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// RdrSetGeneric
|
|||
|
//
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RdrSetGeneric(
|
|||
|
IN PIRP Irp,
|
|||
|
IN PICB Icb,
|
|||
|
USHORT NtInformationLevel,
|
|||
|
ULONG cbBuffer,
|
|||
|
IN VOID *pvBuffer)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will perform whatever operations are necessary to set the
|
|||
|
extended Cairo information on the remote file.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PICB Icb - Supplies the file to set the attributes on.
|
|||
|
IN ULONG NtInformationLevel - Supplies the NtInformationLevel to use
|
|||
|
IN ULONG cbBuffer - Supplies the size of the information to change
|
|||
|
IN VOID *pvBuffer - Supplies the information to change
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Final status of operation
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if ((Icb->Fcb->Connection->Server->Capabilities & DF_NT_SMBS) == 0) {
|
|||
|
|
|||
|
Status = STATUS_NOT_SUPPORTED;
|
|||
|
|
|||
|
} else if ((Icb->Flags & ICB_HASHANDLE) == 0) {
|
|||
|
|
|||
|
Status = STATUS_INVALID_HANDLE;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Use TRANSACT2_SETFILEINFO to set the extended information
|
|||
|
//
|
|||
|
|
|||
|
USHORT Setup[] = {TRANS2_SET_FILE_INFORMATION};
|
|||
|
REQ_SET_FILE_INFORMATION Parameters;
|
|||
|
CLONG OutParameterCount = sizeof(REQ_SET_FILE_INFORMATION);
|
|||
|
CLONG OutDataCount = 0;
|
|||
|
CLONG OutSetupCount = 0;
|
|||
|
|
|||
|
SmbPutAlignedUshort(&Parameters.InformationLevel, NtInformationLevel);
|
|||
|
SmbPutAlignedUshort(&Parameters.Fid, Icb->FileId);
|
|||
|
|
|||
|
Status = RdrTransact(Irp, // Irp,
|
|||
|
Icb->Fcb->Connection,
|
|||
|
Icb->Se,
|
|||
|
Setup,
|
|||
|
(CLONG) sizeof(Setup), // InSetupCount,
|
|||
|
&OutSetupCount,
|
|||
|
NULL, // Name,
|
|||
|
&Parameters,
|
|||
|
sizeof(Parameters), // InParameterCount,
|
|||
|
&OutParameterCount,
|
|||
|
pvBuffer, // InData,
|
|||
|
cbBuffer,
|
|||
|
NULL, // OutData,
|
|||
|
&OutDataCount, // OutDataCount,
|
|||
|
&Icb->FileId, // Fid
|
|||
|
0, // Timeout
|
|||
|
0, // Flags
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
if (Status == STATUS_INVALID_HANDLE) {
|
|||
|
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
|||
|
}
|
|||
|
}
|
|||
|
return(Status);
|
|||
|
}
|