windows-nt/Source/XPSP1/NT/ds/nw/rdr/fileinfo.c
2020-09-26 16:20:57 +08:00

3066 lines
78 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) 1993 Microsoft Corporation
Module Name:
fileinfo.c
Abstract:
This module implements the get / set file information routines for
Netware Redirector.
Author:
Manny Weiser (mannyw) 4-Mar-1993
Revision History:
--*/
#include "procs.h"
//
// The debug trace level
//
#define Dbg (DEBUG_TRACE_FILEINFO)
//
// local procedure prototypes
//
NTSTATUS
NwCommonQueryInformation (
IN PIRP_CONTEXT pIrpContext
);
NTSTATUS
NwCommonSetInformation (
IN PIRP_CONTEXT pIrpContet
);
NTSTATUS
NwQueryBasicInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_BASIC_INFORMATION Buffer
);
NTSTATUS
NwQueryStandardInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_STANDARD_INFORMATION Buffer
);
NTSTATUS
NwQueryInternalInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_INTERNAL_INFORMATION Buffer
);
NTSTATUS
NwQueryEaInfo (
IN PIRP_CONTEXT IrpContext,
IN PFILE_EA_INFORMATION Buffer
);
NTSTATUS
NwQueryNameInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_NAME_INFORMATION Buffer,
IN OUT PULONG Length
);
NTSTATUS
NwQueryAltNameInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_NAME_INFORMATION Buffer,
IN OUT PULONG Length
);
NTSTATUS
NwQueryPositionInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_POSITION_INFORMATION Buffer
);
NTSTATUS
NwSetBasicInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_BASIC_INFORMATION Buffer
);
NTSTATUS
NwSetDispositionInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_DISPOSITION_INFORMATION Buffer
);
NTSTATUS
NwSetRenameInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_RENAME_INFORMATION Buffer
);
NTSTATUS
NwSetPositionInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_POSITION_INFORMATION Buffer
);
NTSTATUS
NwSetAllocationInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_ALLOCATION_INFORMATION Buffer
);
NTSTATUS
NwSetEndOfFileInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_END_OF_FILE_INFORMATION Buffer
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, NwFsdQueryInformation )
#pragma alloc_text( PAGE, NwFsdSetInformation )
#pragma alloc_text( PAGE, NwCommonQueryInformation )
#pragma alloc_text( PAGE, NwCommonSetInformation )
#pragma alloc_text( PAGE, NwQueryStandardInfo )
#pragma alloc_text( PAGE, NwQueryInternalInfo )
#pragma alloc_text( PAGE, NwQueryEaInfo )
#pragma alloc_text( PAGE, NwQueryNameInfo )
#pragma alloc_text( PAGE, NwQueryPositionInfo )
#pragma alloc_text( PAGE, NwSetBasicInfo )
#pragma alloc_text( PAGE, NwSetDispositionInfo )
#pragma alloc_text( PAGE, NwDeleteFile )
#pragma alloc_text( PAGE, NwSetRenameInfo )
#pragma alloc_text( PAGE, NwSetPositionInfo )
#pragma alloc_text( PAGE, NwSetAllocationInfo )
#pragma alloc_text( PAGE, NwSetEndOfFileInfo )
#pragma alloc_text( PAGE, OccurenceCount )
#ifndef QFE_BUILD
#pragma alloc_text( PAGE1, NwQueryBasicInfo )
#endif
#endif
#if 0 // Not pageable
// see ifndef QFE_BUILD above
#endif
NTSTATUS
NwFsdQueryInformation (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine implements the FSD part of the NtQueryInformationFile API
calls.
Arguments:
DeviceObject - Supplies a pointer to the device object to use.
Irp - Supplies a pointer to the Irp to process.
Return Value:
NTSTATUS - The Fsd status for the Irp
--*/
{
NTSTATUS status;
PIRP_CONTEXT pIrpContext = NULL;
BOOLEAN TopLevel;
PAGED_CODE();
DebugTrace(+1, Dbg, "NwFsdQueryInformation\n", 0);
//
// Call the common query information routine.
//
FsRtlEnterFileSystem();
TopLevel = NwIsIrpTopLevel( Irp );
try {
pIrpContext = AllocateIrpContext( Irp );
status = NwCommonQueryInformation( pIrpContext );
} except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
if ( pIrpContext == NULL ) {
//
// If we couldn't allocate an irp context, just complete
// irp without any fanfare.
//
status = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
} else {
//
// We had some trouble trying to perform the requested
// operation, so we'll abort the I/O request with
// the error Status that we get back from the
// execption code
//
status = NwProcessException( pIrpContext, GetExceptionCode() );
}
}
if ( pIrpContext ) {
if ( status != STATUS_PENDING ) {
NwDequeueIrpContext( pIrpContext, FALSE );
}
NwCompleteRequest( pIrpContext, status );
}
if ( TopLevel ) {
NwSetTopLevelIrp( NULL );
}
FsRtlExitFileSystem();
//
// Return to the caller.
//
DebugTrace(-1, Dbg, "NwFsdQueryInformation -> %08lx\n", status );
return status;
}
NTSTATUS
NwFsdSetInformation (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine implements the FSD part of the NtSetInformationFile API
calls.
Arguments:
DeviceObject - Supplies the device object to use.
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The Fsd status for the Irp
--*/
{
NTSTATUS status;
PIRP_CONTEXT pIrpContext = NULL;
BOOLEAN TopLevel;
PAGED_CODE();
DebugTrace(+1, Dbg, "NwFsdSetInformation\n", 0);
//
// Call the common Set Information routine.
//
FsRtlEnterFileSystem();
TopLevel = NwIsIrpTopLevel( Irp );
try {
pIrpContext = AllocateIrpContext( Irp );
status = NwCommonSetInformation( pIrpContext );
} except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
if ( pIrpContext == NULL ) {
//
// If we couldn't allocate an irp context, just complete
// irp without any fanfare.
//
status = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
} else {
//
// We had some trouble trying to perform the requested
// operation, so we'll abort the I/O request with
// the error Status that we get back from the
// execption code
//
status = NwProcessException( pIrpContext, GetExceptionCode() );
}
}
if ( pIrpContext ) {
if ( status != STATUS_PENDING ) {
NwDequeueIrpContext( pIrpContext, FALSE );
}
NwCompleteRequest( pIrpContext, status );
}
if ( TopLevel ) {
NwSetTopLevelIrp( NULL );
}
FsRtlExitFileSystem();
//
// Return to the caller.
//
DebugTrace(-1, Dbg, "NwFsdSetInformation -> %08lx\n", status );
return status;
}
NTSTATUS
NwCommonQueryInformation (
IN PIRP_CONTEXT pIrpContext
)
/*++
Routine Description:
This is the common routine for querying information on a file.
Arguments:
pIrpContext - Supplies Irp context information.
Return Value:
NTSTATUS - the return status for the operation.
--*/
{
PIRP Irp;
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
ULONG length;
FILE_INFORMATION_CLASS fileInformationClass;
PVOID buffer;
NODE_TYPE_CODE nodeTypeCode;
PICB icb;
PFCB fcb;
PVOID fsContext, fsContext2;
PFILE_ALL_INFORMATION AllInfo;
PAGED_CODE();
//
// Get the current stack location.
//
Irp = pIrpContext->pOriginalIrp;
irpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "NwCommonQueryInformation...\n", 0);
DebugTrace( 0, Dbg, " Irp = %08lx\n", (ULONG_PTR)Irp);
DebugTrace( 0, Dbg, " ->Length = %08lx\n", irpSp->Parameters.QueryFile.Length);
DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", irpSp->Parameters.QueryFile.FileInformationClass);
DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", (ULONG_PTR)Irp->AssociatedIrp.SystemBuffer);
//
// Find out who are.
//
if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
&fsContext,
&fsContext2 )) == NTC_UNDEFINED) {
status = STATUS_INVALID_HANDLE;
DebugTrace(-1, Dbg, "NwCommonQueryInformation -> %08lx\n", status );
return status;
}
//
// Make sure that this the user is querying an ICB.
//
switch (nodeTypeCode) {
case NW_NTC_ICB:
icb = (PICB)fsContext2;
break;
default: // This is an illegal file object to query
DebugTrace(0, Dbg, "Node type code is not incorrect\n", 0);
DebugTrace(-1, Dbg, "NwCommonQueryInformation -> STATUS_INVALID_PARAMETER\n", 0);
return STATUS_INVALID_PARAMETER;
}
pIrpContext->Icb = icb;
//
// Make local copies of the input parameters.
//
length = irpSp->Parameters.QueryFile.Length;
fileInformationClass = irpSp->Parameters.QueryFile.FileInformationClass;
buffer = Irp->AssociatedIrp.SystemBuffer;
//
// Now acquire shared access to the FCB
//
fcb = icb->SuperType.Fcb;
try {
NwVerifyIcbSpecial( icb );
//
// Based on the information class we'll do different actions. Each
// of the procedure that we're calling fill up as much of the
// buffer as possible and return the remaining length, and status
// This is done so that we can use them to build up the
// FileAllInformation request. These procedures do not complete the
// IRP, instead this procedure must complete the IRP.
//
status = STATUS_SUCCESS;
switch (fileInformationClass) {
case FileAllInformation:
AllInfo = buffer;
//
// First call all the Query Info handlers we can call
// synchronously.
//
NwQueryInternalInfo( pIrpContext, icb, &AllInfo->InternalInformation );
NwQueryEaInfo( pIrpContext, &AllInfo->EaInformation );
NwQueryPositionInfo( pIrpContext, icb, &AllInfo->PositionInformation );
length -= FIELD_OFFSET( FILE_ALL_INFORMATION, NameInformation );
status = NwQueryNameInfo( pIrpContext, icb, &AllInfo->NameInformation, &length );
if ( !NT_ERROR( status ) ) {
status = NwQueryStandardInfo( pIrpContext, icb, &AllInfo->StandardInformation );
}
if ( !NT_ERROR( status ) ) {
status = NwQueryBasicInfo( pIrpContext, icb, &AllInfo->BasicInformation );
}
break;
case FileBasicInformation:
length -= sizeof( FILE_BASIC_INFORMATION );
status = NwQueryBasicInfo( pIrpContext, icb, buffer );
break;
case FileStandardInformation:
//
// We will handle this call for information asynchronously.
// The callback routine will fill in the missing data, and
// complete the IRP.
//
// Remember the buffer length, and status to return.
//
length -= sizeof( FILE_STANDARD_INFORMATION );
status = NwQueryStandardInfo( pIrpContext, icb, buffer );
break;
case FileInternalInformation:
status = NwQueryInternalInfo( pIrpContext, icb, buffer );
length -= sizeof( FILE_INTERNAL_INFORMATION );
break;
case FileEaInformation:
status = NwQueryEaInfo( pIrpContext, buffer );
length -= sizeof( FILE_EA_INFORMATION );
break;
case FilePositionInformation:
status = NwQueryPositionInfo( pIrpContext, icb, buffer );
length -= sizeof( FILE_POSITION_INFORMATION );
break;
case FileNameInformation:
status = NwQueryNameInfo( pIrpContext, icb, buffer, &length );
break;
case FileAlternateNameInformation:
if (!DisableAltFileName) {
status = NwQueryAltNameInfo( pIrpContext, icb, buffer, &length);
break;
}
default:
status = STATUS_INVALID_PARAMETER;
break;
}
//
// Set the information field to the number of bytes actually
// filled in and then complete the request. (This is
// irrelavent if the Query worker function returned
// STATUS_PENDING).
//
if ( status != STATUS_PENDING ) {
Irp->IoStatus.Information =
irpSp->Parameters.QueryFile.Length - length;
}
} finally {
DebugTrace(-1, Dbg, "NwCommonQueryInformation -> %08lx\n", status );
}
return status;
}
NTSTATUS
NwCommonSetInformation (
IN PIRP_CONTEXT IrpContext
)
/*++
Routine Description:
This is the common routine for setting information on a file.
Arguments:
IrpContext - Supplies the Irp to process
Return Value:
NTSTATUS - the return status for the operation
--*/
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
ULONG length;
FILE_INFORMATION_CLASS fileInformationClass;
PVOID buffer;
NODE_TYPE_CODE nodeTypeCode;
PICB icb;
PFCB fcb;
PVOID fsContext;
//
// Get the current Irp stack location.
//
irp = IrpContext->pOriginalIrp;
irpSp = IoGetCurrentIrpStackLocation( irp );
DebugTrace(+1, Dbg, "NwCommonSetInformation...\n", 0);
DebugTrace( 0, Dbg, " Irp = %08lx\n", (ULONG_PTR)irp);
DebugTrace( 0, Dbg, " ->Length = %08lx\n", irpSp->Parameters.SetFile.Length);
DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", irpSp->Parameters.SetFile.FileInformationClass);
DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", (ULONG_PTR)irp->AssociatedIrp.SystemBuffer);
//
// Get a pointer to the FCB and ensure that this is a server side
// handler to a file.
//
if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
&fsContext,
(PVOID *)&icb )) == NTC_UNDEFINED ) {
status = STATUS_INVALID_HANDLE;
DebugTrace(-1, Dbg, "NwCommonSetInformation -> %08lx\n", status );
return status;
}
//
// Make sure that this the user is querying an ICB.
//
switch (nodeTypeCode) {
case NW_NTC_ICB:
fcb = icb->SuperType.Fcb;
break;
default: // This is an illegal file object to query
DebugTrace(0, Dbg, "Node type code is not incorrect\n", 0);
DebugTrace(-1, Dbg, "NwCommonSetInformation -> STATUS_INVALID_PARAMETER\n", 0);
return STATUS_INVALID_PARAMETER;
}
IrpContext->Icb = icb;
//
// Make local copies of the input parameters.
//
length = irpSp->Parameters.SetFile.Length;
fileInformationClass = irpSp->Parameters.SetFile.FileInformationClass;
buffer = irp->AssociatedIrp.SystemBuffer;
try {
NwVerifyIcb( icb );
//
// Based on the information class we'll do different actions. Each
// procedure that we're calling will complete the request.
//
switch (fileInformationClass) {
case FileBasicInformation:
status = NwSetBasicInfo( IrpContext, icb, buffer );
break;
case FileDispositionInformation:
status = NwSetDispositionInfo( IrpContext, icb, buffer );
break;
case FileRenameInformation:
status = NwSetRenameInfo( IrpContext, icb, buffer );
break;
case FilePositionInformation:
status = NwSetPositionInfo( IrpContext, icb, buffer );
break;
case FileLinkInformation:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
case FileAllocationInformation:
status = NwSetAllocationInfo( IrpContext, icb, buffer );
break;
case FileEndOfFileInformation:
status = NwSetEndOfFileInfo( IrpContext, icb, buffer );
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
} finally {
DebugTrace(-1, Dbg, "NwCommonSetInformation -> %08lx\n", status);
}
return status;
}
NTSTATUS
NwQueryBasicInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
OUT PFILE_BASIC_INFORMATION Buffer
)
/*++
Routine Description:
This routine performs the query basic information operation.
This routine cannot be paged, it is called from QueryStandardInfoCallback.
Arguments:
Icb - Supplies a pointer the ICB for the file being querying.
Buffer - Supplies a pointer to the buffer where the information is
to be returned.
Return Value:
VOID
--*/
{
PFCB Fcb;
NTSTATUS Status;
ULONG Attributes;
USHORT CreationDate;
USHORT CreationTime = DEFAULT_TIME;
USHORT LastAccessDate;
USHORT LastModifiedDate;
USHORT LastModifiedTime;
BOOLEAN FirstTime = TRUE;
DebugTrace(0, Dbg, "QueryBasicInfo...\n", 0);
//
// Zero out the buffer.
//
RtlZeroMemory( Buffer, sizeof(FILE_BASIC_INFORMATION) );
Fcb = Icb->SuperType.Fcb;
//
// It is ok to attempt a reconnect if this request fails with a
// connection error.
//
SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
NwAcquireSharedFcb( Fcb->NonPagedFcb, TRUE );
//
// If we already know the file attributes, simply return them.
//
if ( FlagOn( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ) ) {
//
// Set the various fields in the record
//
Buffer->CreationTime = NwDateTimeToNtTime(
Fcb->CreationDate,
Fcb->CreationTime
);
Buffer->LastAccessTime = NwDateTimeToNtTime(
Fcb->LastAccessDate,
DEFAULT_TIME
);
Buffer->LastWriteTime = NwDateTimeToNtTime(
Fcb->LastModifiedDate,
Fcb->LastModifiedTime
);
DebugTrace(0, Dbg, "QueryBasic known %wZ\n", &Fcb->RelativeFileName);
DebugTrace(0, Dbg, "LastModifiedDate %x\n", Fcb->LastModifiedDate);
DebugTrace(0, Dbg, "LastModifiedTime %x\n", Fcb->LastModifiedTime);
DebugTrace(0, Dbg, "CreationDate %x\n", Fcb->CreationDate );
DebugTrace(0, Dbg, "CreationTime %x\n", Fcb->CreationTime );
DebugTrace(0, Dbg, "LastAccessDate %x\n", Fcb->LastAccessDate );
Buffer->FileAttributes = Fcb->NonPagedFcb->Attributes;
if ( Buffer->FileAttributes == 0 ) {
Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
}
NwReleaseFcb( Fcb->NonPagedFcb );
return STATUS_SUCCESS;
} else if ( Fcb->RelativeFileName.Length == 0 ) {
//
// Allow 'cd \' to work.
//
Buffer->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
Buffer->CreationTime = NwDateTimeToNtTime(
DEFAULT_DATE,
DEFAULT_TIME
);
Buffer->LastAccessTime = Buffer->CreationTime;
Buffer->LastWriteTime = Buffer->CreationTime;
NwReleaseFcb( Fcb->NonPagedFcb );
return STATUS_SUCCESS;
} else {
NwReleaseFcb( Fcb->NonPagedFcb );
IrpContext->pNpScb = Fcb->Scb->pNpScb;
Retry:
if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
DebugTrace(0, Dbg, "QueryBasic short %wZ\n", &Fcb->RelativeFileName);
Status = ExchangeWithWait (
IrpContext,
SynchronousResponseCallback,
"FwbbJ",
NCP_SEARCH_FILE,
-1,
Fcb->Vcb->Specific.Disk.Handle,
Fcb->NodeTypeCode == NW_NTC_FCB ?
SEARCH_ALL_FILES : SEARCH_ALL_DIRECTORIES,
&Icb->SuperType.Fcb->RelativeFileName );
if ( NT_SUCCESS( Status ) ) {
Status = ParseResponse(
IrpContext,
IrpContext->rsp,
IrpContext->ResponseLength,
"N==_b-==wwww",
14,
&Attributes,
&CreationDate,
&LastAccessDate,
&LastModifiedDate,
&LastModifiedTime);
//
// If this was a directory, there's no usable
// time/date info from the server.
//
if ( ( NT_SUCCESS( Status ) ) &&
( Attributes & NW_ATTRIBUTE_DIRECTORY ) ) {
CreationDate = DEFAULT_DATE;
LastAccessDate = DEFAULT_DATE;
LastModifiedDate = DEFAULT_DATE;
LastModifiedTime = DEFAULT_TIME;
}
}
} else {
DebugTrace(0, Dbg, "QueryBasic long %wZ\n", &Fcb->RelativeFileName);
Status = ExchangeWithWait (
IrpContext,
SynchronousResponseCallback,
"LbbWDbDbC",
NCP_LFN_GET_INFO,
Fcb->Vcb->Specific.Disk.LongNameSpace,
Fcb->Vcb->Specific.Disk.LongNameSpace,
Fcb->NodeTypeCode == NW_NTC_FCB ?
SEARCH_ALL_FILES : SEARCH_ALL_DIRECTORIES,
LFN_FLAG_INFO_ATTRIBUTES |
LFN_FLAG_INFO_MODIFY_TIME |
LFN_FLAG_INFO_CREATION_TIME,
Fcb->Vcb->Specific.Disk.VolumeNumber,
Fcb->Vcb->Specific.Disk.Handle,
0,
&Icb->SuperType.Fcb->RelativeFileName );
if ( NT_SUCCESS( Status ) ) {
Status = ParseResponse(
IrpContext,
IrpContext->rsp,
IrpContext->ResponseLength,
"N_e_xx_xx_x",
4,
&Attributes,
12,
&CreationTime,
&CreationDate,
4,
&LastModifiedTime,
&LastModifiedDate,
4,
&LastAccessDate );
}
}
if ( NT_SUCCESS( Status ) ) {
//
// Set the various fields in the record
//
Buffer->CreationTime = NwDateTimeToNtTime(
CreationDate,
CreationTime
);
Buffer->LastAccessTime = NwDateTimeToNtTime(
LastAccessDate,
DEFAULT_TIME
);
Buffer->LastWriteTime = NwDateTimeToNtTime(
LastModifiedDate,
LastModifiedTime
);
DebugTrace(0, Dbg, "CreationDate %x\n", CreationDate );
DebugTrace(0, Dbg, "CreationTime %x\n", CreationTime );
DebugTrace(0, Dbg, "LastAccessDate %x\n", LastAccessDate );
DebugTrace(0, Dbg, "LastModifiedDate %x\n", LastModifiedDate);
DebugTrace(0, Dbg, "LastModifiedTime %x\n", LastModifiedTime);
Buffer->FileAttributes = (UCHAR)Attributes;
if ( Buffer->FileAttributes == 0 ) {
Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
}
} else if ((Status == STATUS_INVALID_HANDLE) &&
(FirstTime)) {
//
// Check to see if Volume handle is invalid. Caused when volume
// is unmounted and then remounted.
//
FirstTime = FALSE;
NwReopenVcbHandle( IrpContext, Fcb->Vcb );
goto Retry;
}
return( Status );
}
}
#if NWFASTIO
BOOLEAN
NwFastQueryBasicInfo (
IN PFILE_OBJECT FileObject,
IN BOOLEAN Wait,
IN OUT PFILE_BASIC_INFORMATION Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine is for the fast query call for standard file information.
Arguments:
FileObject - Supplies the file object used in this operation
Wait - Indicates if we are allowed to wait for the information
Buffer - Supplies the output buffer to receive the basic information
IoStatus - Receives the final status of the operation
Return Value:
BOOLEAN - TRUE if the operation succeeded and FALSE if the caller
needs to take the long route.
--*/
{
NODE_TYPE_CODE NodeTypeCode;
PICB Icb;
PFCB Fcb;
PVOID FsContext;
FsRtlEnterFileSystem();
try {
//
// Find out who are.
//
if ((NodeTypeCode = NwDecodeFileObject( FileObject,
&FsContext,
&Icb )) != NW_NTC_ICB ) {
DebugTrace(-1, Dbg, "NwFastQueryStandardInfo -> FALSE\n", 0 );
return FALSE;
}
Fcb = Icb->SuperType.Fcb;
NwAcquireExclusiveFcb( Fcb->NonPagedFcb, TRUE );
//
// If we don't have the info handy, we can't use the fast path.
//
if ( !FlagOn( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ) ) {
NwReleaseFcb( Fcb->NonPagedFcb );
return( FALSE );
}
//
// Set the various fields in the record
//
Buffer->CreationTime = NwDateTimeToNtTime(
Fcb->CreationDate,
Fcb->CreationTime
);
Buffer->LastAccessTime = NwDateTimeToNtTime(
Fcb->LastAccessDate,
DEFAULT_TIME
);
Buffer->LastWriteTime = NwDateTimeToNtTime(
Fcb->LastModifiedDate,
Fcb->LastModifiedTime
);
DebugTrace(0, Dbg, "QueryBasic known %wZ\n", &Fcb->RelativeFileName);
DebugTrace(0, Dbg, "LastModifiedDate %x\n", Fcb->LastModifiedDate);
DebugTrace(0, Dbg, "LastModifiedTime %x\n", Fcb->LastModifiedTime);
DebugTrace(0, Dbg, "CreationDate %x\n", Fcb->CreationDate );
DebugTrace(0, Dbg, "CreationTime %x\n", Fcb->CreationTime );
DebugTrace(0, Dbg, "LastAccessDate %x\n", Fcb->LastAccessDate );
Buffer->FileAttributes = Fcb->NonPagedFcb->Attributes;
if ( Buffer->FileAttributes == 0 ) {
Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
}
IoStatus->Status = STATUS_SUCCESS;
IoStatus->Information = sizeof( *Buffer );
NwReleaseFcb( Fcb->NonPagedFcb );
return TRUE;
} finally {
FsRtlExitFileSystem();
}
}
#endif
NTSTATUS
NwQueryStandardInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_STANDARD_INFORMATION Buffer
)
/*++
Routine Description:
This routine perforNw the query standard information operation.
Arguments:
Fcb - Supplies the FCB of the being queried
Buffer - Supplies a pointer to the buffer where the information is
to be returned
Return Value:
VOID
--*/
{
NTSTATUS Status;
PFCB Fcb;
ULONG FileSize;
BOOLEAN FirstTime = TRUE;
PAGED_CODE();
Fcb = Icb->SuperType.Fcb;
//
// Zero out the buffer.
//
RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) );
//
// Fill in the answers we already know.
//
Buffer->NumberOfLinks = 1;
Buffer->DeletePending = (BOOLEAN)FlagOn( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
if ( Fcb->NodeTypeCode == NW_NTC_FCB ) {
Buffer->Directory = FALSE;
} else {
Buffer->Directory = TRUE;
}
if ( !Icb->HasRemoteHandle ) {
//
// It is ok to attempt a reconnect if this request fails with a
// connection error.
//
SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
if ( Fcb->NodeTypeCode == NW_NTC_DCB ||
FlagOn( Fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
//
// Allow 'cd \' to work.
//
Buffer->AllocationSize.QuadPart = 0;
Buffer->EndOfFile.QuadPart = 0;
return STATUS_SUCCESS;
} else {
//
// No open handle for this file. Use a path based NCP
// to get the file size.
//
Retry:
IrpContext->pNpScb = Fcb->Scb->pNpScb;
if ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
Status = ExchangeWithWait (
IrpContext,
SynchronousResponseCallback,
"FwbbJ",
NCP_SEARCH_FILE,
-1,
Fcb->Vcb->Specific.Disk.Handle,
SEARCH_ALL_FILES,
&Fcb->RelativeFileName );
if ( NT_SUCCESS( Status ) ) {
Status = ParseResponse(
IrpContext,
IrpContext->rsp,
IrpContext->ResponseLength,
"N_d",
20,
&FileSize );
}
} else {
Status = ExchangeWithWait (
IrpContext,
SynchronousResponseCallback,
"LbbWDbDbC",
NCP_LFN_GET_INFO,
Fcb->Vcb->Specific.Disk.LongNameSpace,
Fcb->Vcb->Specific.Disk.LongNameSpace,
SEARCH_ALL_FILES,
LFN_FLAG_INFO_FILE_SIZE,
Fcb->Vcb->Specific.Disk.VolumeNumber,
Fcb->Vcb->Specific.Disk.Handle,
0,
&Fcb->RelativeFileName );
if ( NT_SUCCESS( Status ) ) {
Status = ParseResponse(
IrpContext,
IrpContext->rsp,
IrpContext->ResponseLength,
"N_e",
10,
&FileSize );
}
}
if ((Status == STATUS_INVALID_HANDLE) &&
(FirstTime)) {
//
// Check to see if Volume handle is invalid. Caused when volume
// is unmounted and then remounted.
//
FirstTime = FALSE;
NwReopenVcbHandle( IrpContext, Fcb->Vcb );
goto Retry;
}
Buffer->AllocationSize.QuadPart = FileSize;
Buffer->EndOfFile.QuadPart = FileSize;
}
} else {
//
// Start a Get file size NCP
//
IrpContext->pNpScb = Fcb->Scb->pNpScb;
if ( Fcb->NodeTypeCode == NW_NTC_FCB ) {
AcquireFcbAndFlushCache( IrpContext, Fcb->NonPagedFcb );
}
Status = ExchangeWithWait(
IrpContext,
SynchronousResponseCallback,
"F-r",
NCP_GET_FILE_SIZE,
&Icb->Handle, sizeof(Icb->Handle ) );
if ( NT_SUCCESS( Status ) ) {
//
// Get the data from the response.
//
Status = ParseResponse(
IrpContext,
IrpContext->rsp,
IrpContext->ResponseLength,
"Nd",
&FileSize );
}
if ( NT_SUCCESS( Status ) ) {
//
// Fill in Allocation size and EOF, based on the response.
//
Buffer->AllocationSize.QuadPart = FileSize;
Buffer->EndOfFile.QuadPart = Buffer->AllocationSize.QuadPart;
}
}
return( Status );
}
#if NWFASTIO
BOOLEAN
NwFastQueryStandardInfo (
IN PFILE_OBJECT FileObject,
IN BOOLEAN Wait,
IN OUT PFILE_STANDARD_INFORMATION Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine is for the fast query call for standard file information.
Arguments:
FileObject - Supplies the file object used in this operation
Wait - Indicates if we are allowed to wait for the information
Buffer - Supplies the output buffer to receive the basic information
IoStatus - Receives the final status of the operation
Return Value:
BOOLEAN - TRUE if the operation succeeded and FALSE if the caller
needs to take the long route.
--*/
{
NODE_TYPE_CODE NodeTypeCode;
PICB Icb;
PFCB Fcb;
PVOID FsContext;
//
// Find out who are.
//
try {
FsRtlEnterFileSystem();
if ((NodeTypeCode = NwDecodeFileObject( FileObject,
&FsContext,
&Icb )) != NW_NTC_ICB ) {
DebugTrace(-1, Dbg, "NwFastQueryStandardInfo -> FALSE\n", 0 );
return FALSE;
}
Fcb = Icb->SuperType.Fcb;
//
// If we have the info handy, we can use the fast path.
//
if ( Fcb->NodeTypeCode == NW_NTC_DCB ||
FlagOn( Fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
Buffer->AllocationSize.QuadPart = 0;
Buffer->EndOfFile.QuadPart = 0;
Buffer->NumberOfLinks = 1;
Buffer->DeletePending = (BOOLEAN)FlagOn( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
Buffer->Directory = TRUE;
IoStatus->Status = STATUS_SUCCESS;
IoStatus->Information = sizeof( *Buffer );
return TRUE;
} else {
return FALSE;
}
} finally {
FsRtlExitFileSystem();
}
}
#endif
NTSTATUS
NwQueryInternalInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_INTERNAL_INFORMATION Buffer
)
/*++
Routine Description:
This routine perforNw the query internal information operation.
Arguments:
Fcb - Supplies the FCB of the being queried.
Buffer - Supplies a pointer to the buffer where the information is
to be returned.
Return Value:
VOID
--*/
{
PAGED_CODE();
DebugTrace(0, Dbg, "QueryInternalInfo...\n", 0);
//
// Zero out the buffer.
//
RtlZeroMemory( Buffer, sizeof(FILE_INTERNAL_INFORMATION) );
//
// Set the internal index number to be the address of the ICB.
//
Buffer->IndexNumber.HighPart = 0;
Buffer->IndexNumber.QuadPart = (ULONG_PTR)Icb->NpFcb;
return( STATUS_SUCCESS );
}
NTSTATUS
NwQueryEaInfo (
IN PIRP_CONTEXT IrpContext,
IN PFILE_EA_INFORMATION Buffer
)
/*++
Routine Description:
This routine performs the query Ea information operation.
Arguments:
Buffer - Supplies a pointer to the buffer where the information is
to be returned
Return Value:
VOID - The result of this query
--*/
{
PAGED_CODE();
DebugTrace(0, Dbg, "QueryEaInfo...\n", 0);
//
// Zero out the buffer.
//
RtlZeroMemory(Buffer, sizeof(FILE_EA_INFORMATION));
return STATUS_SUCCESS;
}
NTSTATUS
NwQueryNameInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_NAME_INFORMATION Buffer,
IN PULONG Length
)
/*++
Routine Description:
This routine performs the query name information operation.
Arguments:
Fcb - Supplies the FCB of the file to query.
Buffer - Supplies a pointer to the buffer where the information is
to be returned
Length - Supplies and receives the length of the buffer in bytes.
Return Value:
NTSTATUS - The result of this query.
--*/
{
ULONG bytesToCopy;
ULONG fileNameSize;
PFCB Fcb = Icb->SuperType.Fcb;
NTSTATUS status;
PAGED_CODE();
DebugTrace(0, Dbg, "QueryNameInfo...\n", 0);
//
// Win32 expects the root directory name to be '\' terminated,
// the netware server does not. So if this is a root directory,
// (i.e RelativeFileName length is 0) append a '\' to the path name.
//
//
// See if the buffer is large enough, and decide how many bytes to copy.
//
*Length -= FIELD_OFFSET( FILE_NAME_INFORMATION, FileName[0] );
fileNameSize = Fcb->FullFileName.Length;
if ( Fcb->RelativeFileName.Length == 0 ) {
fileNameSize += sizeof(L'\\');
}
Buffer->FileNameLength = fileNameSize;
if ( *Length >= fileNameSize ) {
status = STATUS_SUCCESS;
bytesToCopy = fileNameSize;
} else {
status = STATUS_BUFFER_OVERFLOW;
bytesToCopy = *Length;
}
//
// Copy over the file name and its length.
//
RtlMoveMemory(
Buffer->FileName,
Fcb->FullFileName.Buffer,
bytesToCopy);
//
// If this is a root directory, and there is space in the buffer
// append a '\' to make win32 happy.
//
if ( Fcb->RelativeFileName.Length == 0 && status == STATUS_SUCCESS ) {
Buffer->FileName[ fileNameSize/sizeof(WCHAR) - 1 ] = L'\\';
}
*Length -= bytesToCopy;
return status;
}
NTSTATUS
NwQueryAltNameInfo (
IN PIRP_CONTEXT pIrpContext,
IN PICB Icb,
IN PFILE_NAME_INFORMATION Buffer,
IN PULONG Length
)
/*++
Routine Description:
This routine performs the AltName query name information operation.
Arguments:
Fcb - Supplies the FCB of the file to query.
Buffer - Supplies a pointer to the buffer where the information is
to be returned
Length - Supplies and receives the length of the buffer in bytes.
Return Value:
NTSTATUS - The result of this query.
--*/
{
ULONG bytesToCopy;
ULONG fileNameSize;
PFCB Fcb = Icb->SuperType.Fcb;
UNICODE_STRING ShortName;
NTSTATUS status;
PAGED_CODE();
DebugTrace(0, Dbg, "QueryAltNameInfo...\n", 0);
pIrpContext->pNpScb = Fcb->Scb->pNpScb;
//
// See if the buffer is large enough, and decide how many bytes to copy.
//
*Length -= FIELD_OFFSET( FILE_NAME_INFORMATION, FileName[0] );
ShortName.MaximumLength = MAX_PATH;
ShortName.Buffer=NULL;
ShortName.Length = 0;
status = ExchangeWithWait (
pIrpContext,
SynchronousResponseCallback,
"LbbWDbDbC",
NCP_LFN_GET_INFO,
Fcb->Vcb->Specific.Disk.LongNameSpace,
0x0, //0x0 DOS Nam
SEARCH_ALL_DIRECTORIES,
LFN_FLAG_INFO_NAME,
Fcb->Vcb->Specific.Disk.VolumeNumber,
Fcb->Vcb->Specific.Disk.Handle,
0,
&Fcb->RelativeFileName );
if (!NT_SUCCESS( status ) ){
return status;
}
ShortName.Buffer= ALLOCATE_POOL(NonPagedPool,
ShortName.MaximumLength+sizeof(WCHAR));
if (ShortName.Buffer == NULL){
return STATUS_INSUFFICIENT_RESOURCES;
}
status = ParseResponse(
pIrpContext,
pIrpContext->rsp,
pIrpContext->ResponseLength,
"N_P",
76,
&ShortName);
if ( NT_SUCCESS( status ) ) {
fileNameSize = ShortName.Length;
if ( *Length >= fileNameSize ) {
status = STATUS_SUCCESS;
bytesToCopy = fileNameSize;
} else {
status = STATUS_BUFFER_OVERFLOW;
bytesToCopy = *Length;
}
Buffer->FileNameLength = fileNameSize;
RtlMoveMemory(
Buffer->FileName,
ShortName.Buffer,
bytesToCopy);
*Length -= bytesToCopy;
}
FREE_POOL(ShortName.Buffer);
return status;
}
NTSTATUS
NwQueryPositionInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_POSITION_INFORMATION Buffer
)
/*++
Routine Description:
This routine performs the query position information operation.
Arguments:
Fcb - Supplies the FCB of the file being queried.
Buffer - Supplies a pointer to the buffer where the information is
to be returned.
Return Value:
VOID
--*/
{
PAGED_CODE();
DebugTrace(0, Dbg, "QueryPositionInfo...\n", 0);
//
// Return the current byte offset. This info is totally
// bogus for asynchronous files. Also note that we don't
// use the FilePosition member of the ICB for anything.
//
if ( Icb->FileObject ) {
Buffer->CurrentByteOffset.QuadPart = Icb->FileObject->CurrentByteOffset.QuadPart;
}
return STATUS_SUCCESS;
}
NTSTATUS
NwSetBasicInfo (
IN PIRP_CONTEXT pIrpContext,
IN PICB Icb,
IN PFILE_BASIC_INFORMATION Buffer
)
/*++
Routine Description:
This routine sets the basic information for a file.
Arguments:
pIrpContext - Supplies Irp context information.
Icb - Supplies the ICB for the file being modified.
Buffer - Supplies the buffer containing the data being set.
Return Value:
NTSTATUS - Returns our completion status.
--*/
{
PFCB Fcb;
NTSTATUS Status;
BOOLEAN SetTime = FALSE;
BOOLEAN SetAttributes = FALSE;
ULONG LfnFlag = 0;
PAGED_CODE();
DebugTrace(0, Dbg, "SetBasicInfo...\n", 0);
Fcb = Icb->SuperType.Fcb;
pIrpContext->pNpScb = Fcb->Scb->pNpScb;
//
// Append this IRP context and wait to get to the front.
// then grab from FCB
//
NwAppendToQueueAndWait( pIrpContext );
NwAcquireExclusiveFcb( Fcb->NonPagedFcb, TRUE );
//
// It is ok to attempt a reconnect if this request fails with a
// connection error.
//
SetFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
if (Buffer->CreationTime.QuadPart != 0) {
//
// Modify the creation time.
//
Status = NwNtTimeToNwDateTime(
Buffer->CreationTime,
&Fcb->CreationDate,
&Fcb->CreationTime );
if ( !NT_SUCCESS( Status ) ) {
NwReleaseFcb( Fcb->NonPagedFcb );
return( Status );
}
SetTime = TRUE;
LfnFlag |= LFN_FLAG_SET_INFO_CREATE_DATE | LFN_FLAG_SET_INFO_CREATE_TIME;
}
if (Buffer->LastAccessTime.QuadPart != 0) {
USHORT Dummy;
//
// Modify the last access time.
//
Status = NwNtTimeToNwDateTime(
Buffer->LastAccessTime,
&Fcb->LastAccessDate,
&Dummy );
if ( !NT_SUCCESS( Status ) ) {
NwReleaseFcb( Fcb->NonPagedFcb );
return( Status );
}
SetTime = TRUE;
LfnFlag |= LFN_FLAG_SET_INFO_LASTACCESS_DATE;
// Set the last access flag in the ICB so that we update
// last access time for real when we close this handle!
Icb->UserSetLastAccessTime = TRUE;
}
if (Buffer->LastWriteTime.QuadPart != 0) {
//
// Modify the last write time
//
Status = NwNtTimeToNwDateTime(
Buffer->LastWriteTime,
&Fcb->LastModifiedDate,
&Fcb->LastModifiedTime );
if ( !NT_SUCCESS( Status ) ) {
NwReleaseFcb( Fcb->NonPagedFcb );
return( Status );
}
LfnFlag |= LFN_FLAG_SET_INFO_MODIFY_DATE | LFN_FLAG_SET_INFO_MODIFY_TIME;
}
if (Buffer->FileAttributes != 0) {
LfnFlag |= LFN_FLAG_SET_INFO_ATTRIBUTES;
}
if ( LfnFlag == 0 ) {
//
// Nothing to set, simply return success.
//
Status = STATUS_SUCCESS;
}
if ( Fcb->NodeTypeCode == NW_NTC_FCB ) {
//
// Call plain FlushCache - we don't want to acquire and
// release the NpFcb. We are already at the front and have the Fcb
// exclusive.
//
FlushCache( pIrpContext, Fcb->NonPagedFcb );
}
if ( BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
Status = ExchangeWithWait(
pIrpContext,
SynchronousResponseCallback,
"LbbWDW--WW==WW==_W_bDbC",
NCP_LFN_SET_INFO,
Fcb->Vcb->Specific.Disk.LongNameSpace,
Fcb->Vcb->Specific.Disk.LongNameSpace,
Fcb->NodeTypeCode == NW_NTC_FCB ?
SEARCH_ALL_FILES : SEARCH_ALL_DIRECTORIES,
LfnFlag,
NtAttributesToNwAttributes( Buffer->FileAttributes ),
Fcb->CreationDate,
Fcb->CreationTime,
Fcb->LastModifiedDate,
Fcb->LastModifiedTime,
8,
Fcb->LastAccessDate,
8,
Fcb->Vcb->Specific.Disk.VolumeNumber,
Fcb->Vcb->Specific.Disk.Handle,
0,
&Fcb->RelativeFileName );
} else {
if ( LfnFlag & LFN_FLAG_SET_INFO_ATTRIBUTES ) {
Status = ExchangeWithWait(
pIrpContext,
SynchronousResponseCallback,
"FbbbU",
NCP_SET_FILE_ATTRIBUTES,
NtAttributesToNwAttributes( Buffer->FileAttributes ),
Fcb->Vcb->Specific.Disk.Handle,
Fcb->NodeTypeCode == NW_NTC_FCB ?
SEARCH_ALL_FILES : SEARCH_ALL_DIRECTORIES,
&Fcb->RelativeFileName );
if ( !NT_SUCCESS( Status ) ) {
NwReleaseFcb( Fcb->NonPagedFcb );
return( Status );
}
}
#if 0
//
// We could conceivably use ScanDir/SetDir to update last access
// and create time. Not supported yet.
//
if ( LfnFlag & ( LFN_FLAG_SET_INFO_LASTACCESS_DATE | LFN_FLAG_SET_INFO_CREATE_DATE ) ) {
ULONG SearchIndex;
ULONG Directory;
Status = ExchangeWithWait(
pIrpContext,
SynchronousResponseCallback,
"SbbdU",
0x16, 0x1E, // Scan dir entry
Fcb->Vcb->Specific.Disk.Handle,
0x06, // Search attributes
-1, // Search index
&Fcb->RelativeFileName );
if ( NT_SUCCESS( Status ) ) {
Status = ParseResponse(
pIrpContext,
pIrpContext->rsp,
pIrpContext->ResponseLength,
"Ndd",
&SearchIndex,
&Directory );
}
if ( NT_SUCCESS( Status ) ) {
Status = ExchangeWithWait(
pIrpContext,
SynchronousResponseCallback,
"Sbbdddw=----_ww==ww==ww",
0x16, 0x25, // Set dir entry
Fcb->Vcb->Specific.Disk.Handle,
0x06, // Search attributes
SearchIndex,
0, // Change Bits?
Directory,
12,
Fcb->CreationDate,
0,
Fcb->LastAccessDate,
0,
Fcb->LastModifiedDate,
Fcb->LastModifiedTime );
}
}
#endif
if ( LfnFlag & LFN_FLAG_SET_INFO_MODIFY_DATE ) {
Status = ExchangeWithWait(
pIrpContext,
SynchronousResponseCallback,
"F-rww-",
NCP_SET_FILE_TIME,
&Icb->Handle, sizeof( Icb->Handle ),
Fcb->LastModifiedTime,
Fcb->LastModifiedDate );
}
}
NwReleaseFcb( Fcb->NonPagedFcb );
//
// And return to our caller
//
return Status;
}
NTSTATUS
NwSetDispositionInfo (
IN PIRP_CONTEXT pIrpContext,
IN PICB Icb,
IN PFILE_DISPOSITION_INFORMATION Buffer
)
/*++
Routine Description:
This routine sets the disposition information for a file.
Arguments:
pIrpContext - Supplies Irp context information.
Icb - Supplies the ICB for the file being modified.
Buffer - Supplies the buffer containing the data being set.
Return Value:
NTSTATUS - Returns our completion status.
--*/
{
PFCB Fcb;
NTSTATUS Status;
PAGED_CODE();
DebugTrace(0, Dbg, "SetDispositionInfo...\n", 0);
Fcb = Icb->SuperType.Fcb;
if ( FlagOn( Fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
//
// This is a print queue, just pretend this IRP succeeded.
//
Status = STATUS_SUCCESS;
} else {
//
// This is a real file or directory. Mark it delete pending.
//
SetFlag( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
pIrpContext->pNpScb = Fcb->Scb->pNpScb;
pIrpContext->Icb = Icb;
Icb->State = ICB_STATE_CLOSE_PENDING;
//
// Go ahead, delete the file.
//
Status = NwDeleteFile( pIrpContext );
}
return( Status );
}
NTSTATUS
NwDeleteFile(
PIRP_CONTEXT pIrpContext
)
/*++
Routine Description:
This routine continues processing of the SetDispositionInfo request.
It must run in the redirector FSP.
Arguments:
pIrpContext - A pointer to the IRP context information for the
request in progress.
Return Value:
The status of the operation.
--*/
{
PICB Icb;
PFCB Fcb;
NTSTATUS Status;
PAGED_CODE();
Icb = pIrpContext->Icb;
Fcb = Icb->SuperType.Fcb;
ClearFlag( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
//
// To a delete a file, first close the remote handle.
//
if ( Icb->HasRemoteHandle ) {
Icb->HasRemoteHandle = FALSE;
Status = ExchangeWithWait(
pIrpContext,
SynchronousResponseCallback,
"F-r",
NCP_CLOSE,
Icb->Handle, sizeof( Icb->Handle ) );
}
//
// Note that this request cannot be reconnectable since, it can
// be called via NwCloseIcb(). See comment in that routine for
// more info.
//
if ( Fcb->NodeTypeCode == NW_NTC_FCB ) {
if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
Status = ExchangeWithWait(
pIrpContext,
SynchronousResponseCallback,
"FbbJ",
NCP_DELETE_FILE,
Fcb->Vcb->Specific.Disk.Handle,
SEARCH_ALL_FILES,
&Fcb->RelativeFileName );
} else {
Status = ExchangeWithWait(
pIrpContext,
SynchronousResponseCallback,
"LbbW-DbC",
NCP_LFN_DELETE_FILE,
Fcb->Vcb->Specific.Disk.LongNameSpace,
Fcb->Vcb->Specific.Disk.VolumeNumber,
NW_ATTRIBUTE_SYSTEM | NW_ATTRIBUTE_HIDDEN,
Fcb->Vcb->Specific.Disk.Handle,
LFN_FLAG_SHORT_DIRECTORY,
&Fcb->RelativeFileName );
}
} else {
ASSERT( Fcb->NodeTypeCode == NW_NTC_DCB );
if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
Status = ExchangeWithWait(
pIrpContext,
SynchronousResponseCallback,
"SbbJ",
NCP_DIR_FUNCTION, NCP_DELETE_DIRECTORY,
Fcb->Vcb->Specific.Disk.Handle,
SEARCH_ALL_DIRECTORIES,
&Fcb->RelativeFileName );
} else {
Status = ExchangeWithWait(
pIrpContext,
SynchronousResponseCallback,
"LbbW-DbC",
NCP_LFN_DELETE_FILE,
Fcb->Vcb->Specific.Disk.LongNameSpace,
Fcb->Vcb->Specific.Disk.VolumeNumber,
SEARCH_ALL_DIRECTORIES,
Fcb->Vcb->Specific.Disk.Handle,
LFN_FLAG_SHORT_DIRECTORY,
&Fcb->RelativeFileName );
}
}
if ( NT_SUCCESS( Status )) {
Status = ParseResponse(
pIrpContext,
pIrpContext->rsp,
pIrpContext->ResponseLength,
"N" );
} else {
//
// We can map all failures to STATUS_NO_SUCH_FILE
// except ACCESS_DENIED, which happens with a read
// only file.
//
if ( Status != STATUS_ACCESS_DENIED ) {
Status = STATUS_NO_SUCH_FILE;
}
}
return Status;
}
NTSTATUS
NwSetRenameInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_RENAME_INFORMATION Buffer
)
/*++
Routine Description:
This routine set rename information for a file.
Arguments:
pIrpContext - A pointer to the IRP context information for the
request in progress.
Icb - A pointer to the ICB of the file to set.
Buffer - The request buffer.
Return Value:
The status of the operation.
--*/
{
PIRP Irp;
PIO_STACK_LOCATION irpSp;
NTSTATUS Status;
NTSTATUS Status2;
PFCB Fcb;
PFCB TargetFcb;
BOOLEAN HandleAllocated = FALSE;
BYTE Handle;
PICB TargetIcb = NULL;
UNICODE_STRING OldDrive;
UNICODE_STRING OldServer;
UNICODE_STRING OldVolume;
UNICODE_STRING OldPath;
UNICODE_STRING OldFileName;
UNICODE_STRING OldFullName;
WCHAR OldDriveLetter;
UNICODE_STRING OldFcbFullName;
UNICODE_STRING NewDrive;
UNICODE_STRING NewServer;
UNICODE_STRING NewVolume;
UNICODE_STRING NewPath;
UNICODE_STRING NewFileName;
UNICODE_STRING NewFullName;
WCHAR NewDriveLetter;
UNICODE_STRING NewFcbFullName;
USHORT i;
PAGED_CODE();
DebugTrace(+1, Dbg, "SetRenameInfo...\n", 0);
//
// Can't try to set rename info on a print queue.
//
Fcb = Icb->SuperType.Fcb;
if ( FlagOn( Fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
return( STATUS_INVALID_PARAMETER );
}
//
// It is ok to attempt a reconnect if this request fails with a
// connection error.
//
SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
//
// Get the current stack location.
//
Irp = IrpContext->pOriginalIrp;
irpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace( 0, Dbg, " ->FullFileName = %wZ\n",
&Fcb->FullFileName);
if (irpSp->Parameters.SetFile.FileObject != NULL) {
TargetIcb = irpSp->Parameters.SetFile.FileObject->FsContext2;
DebugTrace( 0, Dbg, " ->FullFileName = %wZ\n",
&TargetIcb->SuperType.Fcb->FullFileName);
if ( TargetIcb->SuperType.Fcb->Scb != Icb->SuperType.Fcb->Scb ) {
return STATUS_NOT_SAME_DEVICE;
}
} else {
DebugTrace( 0, Dbg, " ->FullFileName in users buffer\n", 0);
DebugTrace(-1, Dbg, "SetRenameInfo %08lx\n", STATUS_NOT_IMPLEMENTED);
return STATUS_NOT_IMPLEMENTED;
}
DebugTrace( 0, Dbg, " ->TargetFileName = %wZ\n",
&irpSp->Parameters.SetFile.FileObject->FileName);
TargetFcb = ((PNONPAGED_FCB)irpSp->Parameters.SetFile.FileObject->FsContext)->Fcb;
IrpContext->pNpScb = Fcb->Scb->pNpScb;
NwAppendToQueueAndWait( IrpContext );
NwAcquireExclusiveFcb( Fcb->NonPagedFcb, TRUE );
try {
//
// If either source or destination is a long name, use
// the long name path.
//
if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) &&
IsFatNameValid( &TargetFcb->RelativeFileName ) &&
!BooleanFlagOn( Fcb->Vcb->Flags, VCB_FLAG_LONG_NAME ) ) {
//
// Strip to UID portion of the FCB name.
//
for ( i = 0 ; i < Fcb->FullFileName.Length / sizeof(WCHAR) ; i++ ) {
if ( Fcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR ) {
break;
}
}
ASSERT( Fcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR );
OldFcbFullName.Length = Fcb->FullFileName.Length - i*sizeof(WCHAR);
OldFcbFullName.Buffer = Fcb->FullFileName.Buffer + i;
Status = CrackPath (
&OldFcbFullName,
&OldDrive,
&OldDriveLetter,
&OldServer,
&OldVolume,
&OldPath,
&OldFileName,
&OldFullName );
ASSERT(NT_SUCCESS(Status));
//
// Strip to UID portion of the FCB name.
//
TargetFcb = ((PNONPAGED_FCB)(irpSp->Parameters.SetFile.FileObject->FsContext))->Fcb;
for ( i = 0 ; i < TargetFcb->FullFileName.Length / sizeof(WCHAR) ; i++ ) {
if ( TargetFcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR ) {
break;
}
}
ASSERT( TargetFcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR );
NewFcbFullName.Length = TargetFcb->FullFileName.Length - i*sizeof(WCHAR);
NewFcbFullName.Buffer = TargetFcb->FullFileName.Buffer + i;
Status = CrackPath (
&NewFcbFullName,
&NewDrive,
&NewDriveLetter,
&NewServer,
&NewVolume,
&NewPath,
&NewFileName,
&NewFullName );
ASSERT(NT_SUCCESS(Status));
//
// Make sure that this is the same volume.
//
if ( RtlCompareUnicodeString( &NewVolume, &OldVolume, TRUE ) != 0 ) {
try_return( Status = STATUS_NOT_SAME_DEVICE );
}
if (Icb->SuperType.Fcb->IcbCount != 1) {
try_return( Status = STATUS_ACCESS_DENIED );
}
//
// After a rename, the only operation allowed on the handle is an
// NtClose.
//
Icb->State = ICB_STATE_CLOSE_PENDING;
if ((irpSp->Parameters.SetFile.ReplaceIfExists ) &&
(TargetIcb->Exists)) {
// Delete the file
Status2 = ExchangeWithWait(
IrpContext,
SynchronousResponseCallback,
"Fb-J",
NCP_DELETE_FILE,
TargetFcb->Vcb->Specific.Disk.Handle,
&TargetFcb->RelativeFileName );
#ifdef NWDBG
if ( NT_SUCCESS( Status2 ) ) {
Status2 = ParseResponse(
IrpContext,
IrpContext->rsp,
IrpContext->ResponseLength,
"N" );
}
ASSERT(NT_SUCCESS(Status2));
#endif
}
//
// Need to create a handle to the directory containing the old
// file/directory name because directory rename does not contain a
// path and there might not be room for two paths in a file rename.
//
// The way we do this is to allocate a temporary handle on the server.
// This request is at the front of the Scb->Requests queue and so can
// use the temporary handle and delete it without affecting any other
// requests.
//
if ( OldPath.Length == 0 ) {
// In the root so use the VCB handle.
Handle = Fcb->Vcb->Specific.Disk.Handle;
} else {
Status = ExchangeWithWait (
IrpContext,
SynchronousResponseCallback,
"SbbJ", // NCP Allocate temporary directory handle
NCP_DIR_FUNCTION, NCP_ALLOCATE_TEMP_DIR_HANDLE,
Fcb->Vcb->Specific.Disk.Handle,
'[',
&OldPath );
if ( NT_SUCCESS( Status ) ) {
Status = ParseResponse(
IrpContext,
IrpContext->rsp,
IrpContext->ResponseLength,
"Nb",
&Handle );
}
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
HandleAllocated = TRUE;
}
if ( Fcb->NodeTypeCode == NW_NTC_DCB ) {
//
// We can only rename files in the same directory
//
if ( RtlCompareUnicodeString( &NewPath, &OldPath, TRUE ) != 0 ) {
try_return(Status = STATUS_NOT_SUPPORTED);
} else {
Status = ExchangeWithWait ( IrpContext,
SynchronousResponseCallback,
"SbJJ",
NCP_DIR_FUNCTION, NCP_RENAME_DIRECTORY,
Handle,
&OldFileName,
&NewFileName);
}
} else {
//
// We have to close the handle associated with the Icb that
// is doing the rename. Close that handle or the rename will
// fail for sure.
//
if ( Icb->HasRemoteHandle ) {
Status2 = ExchangeWithWait(
IrpContext,
SynchronousResponseCallback,
"F-r",
NCP_CLOSE,
Icb->Handle, sizeof( Icb->Handle ) );
Icb->HasRemoteHandle = FALSE;
#ifdef NWDBG
if ( NT_SUCCESS( Status2 ) ) {
Status2 = ParseResponse(
IrpContext,
IrpContext->rsp,
IrpContext->ResponseLength,
"N" );
}
ASSERT(NT_SUCCESS(Status2));
#endif
}
//
// Do the file rename Ncp.
//
Status = ExchangeWithWait (
IrpContext,
SynchronousResponseCallback,
"FbbJbJ",
NCP_RENAME_FILE,
Handle,
SEARCH_ALL_FILES,
&OldFileName,
Fcb->Vcb->Specific.Disk.Handle,
&NewFullName);
}
} else {
//
// We are going through the long name path. Ensure that the
// VCB supports long names.
//
if ( Icb->SuperType.Fcb->Vcb->Specific.Disk.LongNameSpace ==
LFN_NO_OS2_NAME_SPACE) {
try_return( Status = STATUS_OBJECT_PATH_SYNTAX_BAD );
}
if (Icb->SuperType.Fcb->IcbCount != 1) {
try_return( Status = STATUS_ACCESS_DENIED);
}
//
// After a rename, the only operation allowed on the handle is an
// NtClose.
//
Icb->State = ICB_STATE_CLOSE_PENDING;
if ((irpSp->Parameters.SetFile.ReplaceIfExists ) &&
(TargetIcb->Exists)) {
// Delete the file
Status = ExchangeWithWait(
IrpContext,
SynchronousResponseCallback,
"LbbW-DbC",
NCP_LFN_DELETE_FILE,
TargetFcb->Vcb->Specific.Disk.LongNameSpace,
TargetFcb->Vcb->Specific.Disk.VolumeNumber,
SEARCH_ALL_FILES,
TargetFcb->Vcb->Specific.Disk.Handle,
LFN_FLAG_SHORT_DIRECTORY,
&TargetFcb->RelativeFileName );
#ifdef NWDBG
if ( NT_SUCCESS( Status ) ) {
Status2 = ParseResponse(
IrpContext,
IrpContext->rsp,
IrpContext->ResponseLength,
"N" );
}
ASSERT(NT_SUCCESS(Status2));
#endif
}
if ( Fcb->NodeTypeCode == NW_NTC_DCB ) {
//
// We can only rename files in the same directory
//
if ( Fcb->Vcb != TargetFcb->Vcb ) {
try_return(Status = STATUS_NOT_SUPPORTED);
} else {
Status = ExchangeWithWait (
IrpContext,
SynchronousResponseCallback,
"LbbWbDbbbDbbNN",
NCP_LFN_RENAME_FILE,
Fcb->Vcb->Specific.Disk.LongNameSpace,
0, // Rename flag
SEARCH_ALL_DIRECTORIES,
Fcb->Vcb->Specific.Disk.VolumeNumber,
Fcb->Vcb->Specific.Disk.Handle,
LFN_FLAG_SHORT_DIRECTORY,
OccurenceCount( &Fcb->RelativeFileName, OBJ_NAME_PATH_SEPARATOR ) + 1,
Fcb->Vcb->Specific.Disk.VolumeNumber,
Fcb->Vcb->Specific.Disk.Handle,
LFN_FLAG_SHORT_DIRECTORY,
OccurenceCount( &TargetFcb->RelativeFileName, OBJ_NAME_PATH_SEPARATOR ) + 1,
&Fcb->RelativeFileName,
&TargetFcb->RelativeFileName );
}
} else {
//
// We have to close the handle associated with the Icb that
// is doing the rename. Close that handle or the rename will
// fail for sure.
//
if ( Icb->HasRemoteHandle ) {
Status2 = ExchangeWithWait(
IrpContext,
SynchronousResponseCallback,
"F-r",
NCP_CLOSE,
Icb->Handle, sizeof( Icb->Handle ) );
Icb->HasRemoteHandle = FALSE;
#ifdef NWDBG
if ( NT_SUCCESS( Status2 ) ) {
Status2 = ParseResponse(
IrpContext,
IrpContext->rsp,
IrpContext->ResponseLength,
"N" );
}
ASSERT(NT_SUCCESS(Status2));
#endif
}
//
// Do the file rename Ncp.
//
Status = ExchangeWithWait (
IrpContext,
SynchronousResponseCallback,
"LbbWbDbbbDbbNN",
NCP_LFN_RENAME_FILE,
Fcb->Vcb->Specific.Disk.LongNameSpace,
0, // Rename flag
SEARCH_ALL_FILES,
Fcb->Vcb->Specific.Disk.VolumeNumber,
Fcb->Vcb->Specific.Disk.Handle,
LFN_FLAG_SHORT_DIRECTORY,
OccurenceCount( &Fcb->RelativeFileName, OBJ_NAME_PATH_SEPARATOR ) + 1,
Fcb->Vcb->Specific.Disk.VolumeNumber,
Fcb->Vcb->Specific.Disk.Handle,
LFN_FLAG_SHORT_DIRECTORY,
OccurenceCount( &TargetFcb->RelativeFileName, OBJ_NAME_PATH_SEPARATOR ) + 1,
&Fcb->RelativeFileName,
&TargetFcb->RelativeFileName );
}
}
try_exit: NOTHING;
} finally {
if (HandleAllocated) {
Status2 = ExchangeWithWait (
IrpContext,
SynchronousResponseCallback,
"Sb", // NCP Deallocate directory handle
NCP_DIR_FUNCTION, NCP_DEALLOCATE_DIR_HANDLE,
Handle);
#ifdef NWDBG
if ( NT_SUCCESS( Status2 ) ) {
Status2 = ParseResponse(
IrpContext,
IrpContext->rsp,
IrpContext->ResponseLength,
"N" );
}
ASSERT(NT_SUCCESS(Status2));
#endif
}
NwReleaseFcb( Fcb->NonPagedFcb );
}
DebugTrace(-1, Dbg, "SetRenameInfo %08lx\n", Status );
//
// We're done with this request. Dequeue the IRP context from
// SCB and complete the request.
//
if ( Status != STATUS_PENDING ) {
NwDequeueIrpContext( IrpContext, FALSE );
}
return Status;
}
NTSTATUS
NwSetPositionInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_POSITION_INFORMATION Buffer
)
/*++
Routine Description:
This routine sets position information for a file.
Arguments:
pIrpContext - A pointer to the IRP context information for the
request in progress.
Icb - A pointer to the ICB of the file to set.
Buffer - The request buffer.
Return Value:
The status of the operation.
--*/
{
PAGED_CODE();
ASSERT( Buffer->CurrentByteOffset.HighPart == 0 );
if ( Icb->FileObject ) {
Icb->FileObject->CurrentByteOffset.QuadPart = Buffer->CurrentByteOffset.QuadPart;
}
return( STATUS_SUCCESS );
}
NTSTATUS
NwSetAllocationInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_ALLOCATION_INFORMATION Buffer
)
/*++
Routine Description:
This routine sets allocation information for a file.
Arguments:
pIrpContext - A pointer to the IRP context information for the
request in progress.
Icb - A pointer to the ICB of the file to set.
Buffer - The request buffer.
Return Value:
The status of the operation.
--*/
{
NTSTATUS Status;
PIRP irp;
PIO_STACK_LOCATION irpSp;
PFCB fcb = (PFCB)Icb->SuperType.Fcb;
PULONG pFileSize;
PAGED_CODE();
ASSERT( Buffer->AllocationSize.HighPart == 0);
if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
pFileSize = &Icb->NpFcb->Header.FileSize.LowPart;
IrpContext->pNpScb = fcb->Scb->pNpScb;
if (BooleanFlagOn( fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
if (IsTerminalServer()) {
// 2/10/97 cjc Fix problem for binary files not printing correctly
// if done via the COPY command. Works with NT RDR so
// changed this to behave same way.
return(STATUS_INVALID_PARAMETER);
} else {
return STATUS_SUCCESS;
}
}
} else if ( fcb->NodeTypeCode == NW_NTC_SCB ) {
pFileSize = &Icb->FileSize;
IrpContext->pNpScb = ((PSCB)fcb)->pNpScb;
} else {
DebugTrace(0, Dbg, "Not a file or a server\n", 0);
DebugTrace( 0, Dbg, "NwSetAllocationInfo -> %08lx\n", STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
NwAppendToQueueAndWait( IrpContext );
if ( !Icb->HasRemoteHandle ) {
Status = STATUS_INVALID_PARAMETER;
} else if ( Buffer->AllocationSize.LowPart == *pFileSize ) {
Status = STATUS_SUCCESS;
} else {
irp = IrpContext->pOriginalIrp;
irpSp = IoGetCurrentIrpStackLocation( irp );
#ifndef QFE_BUILD
if ( Buffer->AllocationSize.LowPart < *pFileSize ) {
//
// Before we actually truncate, check to see if the purge
// is going to fail.
//
if (!MmCanFileBeTruncated( irpSp->FileObject->SectionObjectPointer,
&Buffer->AllocationSize )) {
return( STATUS_USER_MAPPED_FILE );
}
}
#endif
if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
AcquireFcbAndFlushCache( IrpContext, fcb->NonPagedFcb );
}
Status = ExchangeWithWait(
IrpContext,
SynchronousResponseCallback,
"F-rd=",
NCP_WRITE_FILE,
&Icb->Handle, sizeof( Icb->Handle ),
Buffer->AllocationSize.LowPart );
if ( NT_SUCCESS( Status ) ) {
*pFileSize = Buffer->AllocationSize.LowPart;
}
}
NwDequeueIrpContext( IrpContext, FALSE );
return( Status );
}
NTSTATUS
NwSetEndOfFileInfo (
IN PIRP_CONTEXT IrpContext,
IN PICB Icb,
IN PFILE_END_OF_FILE_INFORMATION Buffer
)
/*++
Routine Description:
This routine sets end of file information for a file.
Arguments:
pIrpContext - A pointer to the IRP context information for the
request in progress.
Icb - A pointer to the ICB of the file to set.
Buffer - The request buffer.
Return Value:
The status of the operation.
--*/
{
NTSTATUS Status;
PIRP irp;
PIO_STACK_LOCATION irpSp;
PFCB fcb = (PFCB)Icb->SuperType.Fcb;
PULONG pFileSize;
PAGED_CODE();
ASSERT( Buffer->EndOfFile.HighPart == 0);
if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
pFileSize = &Icb->NpFcb->Header.FileSize.LowPart;
IrpContext->pNpScb = fcb->Scb->pNpScb;
if (BooleanFlagOn( fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
return STATUS_SUCCESS;
}
} else if ( fcb->NodeTypeCode == NW_NTC_SCB ) {
pFileSize = &Icb->FileSize;
IrpContext->pNpScb = ((PSCB)fcb)->pNpScb;
} else {
DebugTrace(0, Dbg, "Not a file or a server\n", 0);
DebugTrace( 0, Dbg, "NwSetAllocationInfo -> %08lx\n", STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
NwAppendToQueueAndWait( IrpContext );
if ( !Icb->HasRemoteHandle ) {
Status = STATUS_INVALID_PARAMETER;
} else if ( Buffer->EndOfFile.LowPart == *pFileSize ) {
Status = STATUS_SUCCESS;
} else {
irp = IrpContext->pOriginalIrp;
irpSp = IoGetCurrentIrpStackLocation( irp );
#ifndef QFE_BUILD
if ( Buffer->EndOfFile.LowPart < *pFileSize ) {
//
// Before we actually truncate, check to see if the purge
// is going to fail.
//
if (!MmCanFileBeTruncated( irpSp->FileObject->SectionObjectPointer,
&Buffer->EndOfFile )) {
return( STATUS_USER_MAPPED_FILE );
}
}
#endif
if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
AcquireFcbAndFlushCache( IrpContext, fcb->NonPagedFcb );
}
Status = ExchangeWithWait(
IrpContext,
SynchronousResponseCallback,
"F-rd=",
NCP_WRITE_FILE,
&Icb->Handle, sizeof( Icb->Handle ),
Buffer->EndOfFile.LowPart );
if ( NT_SUCCESS( Status ) ) {
*pFileSize = Buffer->EndOfFile.LowPart;
}
}
NwDequeueIrpContext( IrpContext, FALSE );
return( Status );
}
ULONG
OccurenceCount (
IN PUNICODE_STRING String,
IN WCHAR SearchChar
)
/*++
Routine Description:
This routine counts the number of occurences of a search character
in a string
Arguments:
String - The string to search
SearchChar - The character to search for.
Return Value:
The occurence count.
--*/
{
PWCH currentChar;
PWCH endOfString;
ULONG count = 0;
PAGED_CODE();
currentChar = String->Buffer;
endOfString = &String->Buffer[ String->Length / sizeof(WCHAR) ];
while ( currentChar < endOfString ) {
if ( *currentChar == SearchChar ) {
count++;
}
currentChar++;
}
return( count );
}