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