NT4/private/ntos/rdr/smbfuncs.c
2020-09-30 17:12:29 +02:00

3428 lines
97 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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);
}