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