diff options
Diffstat (limited to 'private/nw/rdr/fileinfo.c')
-rw-r--r-- | private/nw/rdr/fileinfo.c | 2905 |
1 files changed, 2905 insertions, 0 deletions
diff --git a/private/nw/rdr/fileinfo.c b/private/nw/rdr/fileinfo.c new file mode 100644 index 000000000..e9453a317 --- /dev/null +++ b/private/nw/rdr/fileinfo.c @@ -0,0 +1,2905 @@ +/*++ + +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 +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)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)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; + + 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)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)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); + } + + } 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; + + // + // 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; +} +#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. + // + + 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; + + } +} +#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.LowPart = (ULONG)Icb->NpFcb; + Buffer->IndexNumber.HighPart = 0; + + 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 +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; + +#if 0 // BUGBUG Was I on drugs? Below seems to be false, remove the check + ASSERT ( BooleanFlagOn( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ) ); + + // + // Do not allow delete of read-only file, the netware server will + // allow it. + // + + if ( Icb->NpFcb->Attributes & NW_ATTRIBUTE_READ_ONLY ) { + return( STATUS_ACCESS_DENIED ); + } +#endif + + 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 ) ) { + + 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 ); +} |