/*++ Copyright (c) 1989 Microsoft Corporation Module Name: fileinfo.c Abstract: This module implements the get / set file information routines for MSFS called by the dispatch driver. Author: Manny Weiser (mannyw) 31-Jan-1991 Revision History: --*/ #include "mailslot.h" // // The debug trace level // #define Dbg (DEBUG_TRACE_FILEINFO) // // local procedure prototypes // NTSTATUS MsCommonQueryInformation ( IN PMSFS_DEVICE_OBJECT MsfsDeviceObject, IN PIRP Irp ); NTSTATUS MsCommonSetInformation ( IN PMSFS_DEVICE_OBJECT MsfsDeviceObject, IN PIRP Irp ); VOID MsQueryBasicInfo ( IN PFCB Fcb, IN PFILE_BASIC_INFORMATION Buffer ); VOID MsQueryStandardInfo ( IN PFCB Fcb, IN PFILE_STANDARD_INFORMATION Buffer ); VOID MsQueryInternalInfo ( IN PFCB Fcb, IN PFILE_INTERNAL_INFORMATION Buffer ); VOID MsQueryEaInfo ( IN PFILE_EA_INFORMATION Buffer ); NTSTATUS MsQueryNameInfo ( IN PFCB Fcb, IN PFILE_NAME_INFORMATION Buffer, IN OUT PULONG Length ); VOID MsQueryPositionInfo ( IN PFCB Fcb, IN PFILE_POSITION_INFORMATION Buffer ); VOID MsQueryMailslotInfo ( IN PFCB Fcb, IN PFILE_MAILSLOT_QUERY_INFORMATION Buffer ); NTSTATUS MsSetBasicInfo ( IN PFCB Fcb, IN PFILE_BASIC_INFORMATION Buffer ); NTSTATUS MsSetMailslotInfo ( IN PIRP Irp, IN PFCB Fcb, IN PFILE_MAILSLOT_SET_INFORMATION Buffer ); #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, MsCommonQueryInformation ) #pragma alloc_text( PAGE, MsCommonSetInformation ) #pragma alloc_text( PAGE, MsFsdQueryInformation ) #pragma alloc_text( PAGE, MsFsdSetInformation ) #pragma alloc_text( PAGE, MsQueryBasicInfo ) #pragma alloc_text( PAGE, MsQueryEaInfo ) #pragma alloc_text( PAGE, MsQueryInternalInfo ) #pragma alloc_text( PAGE, MsQueryMailslotInfo ) #pragma alloc_text( PAGE, MsQueryNameInfo ) #pragma alloc_text( PAGE, MsQueryPositionInfo ) #pragma alloc_text( PAGE, MsQueryStandardInfo ) #pragma alloc_text( PAGE, MsSetBasicInfo ) #pragma alloc_text( PAGE, MsSetMailslotInfo ) #endif NTSTATUS MsFsdQueryInformation ( IN PMSFS_DEVICE_OBJECT MsfsDeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine implements the FSD part of the NtQueryInformationFile API calls. Arguments: MsfsDeviceObject - 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; PAGED_CODE(); DebugTrace(+1, Dbg, "MsFsdQueryInformation\n", 0); // // Call the common query information routine. // FsRtlEnterFileSystem(); status = MsCommonQueryInformation( MsfsDeviceObject, Irp ); FsRtlExitFileSystem(); // // Return to the caller. // DebugTrace(-1, Dbg, "MsFsdQueryInformation -> %08lx\n", status ); return status; } NTSTATUS MsFsdSetInformation ( IN PMSFS_DEVICE_OBJECT MsfsDeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine implements the FSD part of the NtSetInformationFile API calls. Arguments: MsfsDeviceObject - Supplies the device object to use. Irp - Supplies the Irp being processed Return Value: NTSTATUS - The Fsd status for the Irp --*/ { NTSTATUS status; PAGED_CODE(); DebugTrace(+1, Dbg, "MsFsdSetInformation\n", 0); // // Call the common Set Information routine. // FsRtlEnterFileSystem(); status = MsCommonSetInformation( MsfsDeviceObject, Irp ); FsRtlExitFileSystem(); // // Return to the caller. // DebugTrace(-1, Dbg, "MsFsdSetInformation -> %08lx\n", status ); return status; } NTSTATUS MsCommonQueryInformation ( IN PMSFS_DEVICE_OBJECT MsfsDeviceObject, IN PIRP Irp ) /*++ Routine Description: This is the common routine for querying information on a file. Arguments: MsfsDeviceObject - The device object to use. Irp - Supplies the Irp to process Return Value: NTSTATUS - the return status for the operation. --*/ { PIO_STACK_LOCATION irpSp; NTSTATUS status; ULONG length; FILE_INFORMATION_CLASS fileInformationClass; PVOID buffer; NODE_TYPE_CODE nodeTypeCode; PFCB fcb; PVOID fsContext, fsContext2; PFILE_ALL_INFORMATION AllInfo; PAGED_CODE(); // // Get the current stack location. // irpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace(+1, Dbg, "MsCommonQueryInformation...\n", 0); DebugTrace( 0, Dbg, " Irp = %08lx\n", (ULONG)Irp); DebugTrace( 0, Dbg, " ->Length = %08lx\n", irpSp->Parameters.QueryFile.Length); DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", irpSp->Parameters.QueryFile.FileInformationClass); DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", (ULONG)Irp->AssociatedIrp.SystemBuffer); // // Find out who are. // if ((nodeTypeCode = MsDecodeFileObject( irpSp->FileObject, &fsContext, &fsContext2 )) == NTC_UNDEFINED) { DebugTrace(0, Dbg, "Mailslot is disconnected from us\n", 0); MsCompleteRequest( Irp, STATUS_FILE_FORCED_CLOSED ); status = STATUS_FILE_FORCED_CLOSED; DebugTrace(-1, Dbg, "MsCommonQueryInformation -> %08lx\n", status ); return status; } // // Decide how to handle this request. A user can query information // on a DCB, ROOT_DCB, FCB, or CCB only. // switch (nodeTypeCode) { case MSFS_NTC_FCB: // This is a server side handle to a mailslot file case MSFS_NTC_ROOT_DCB: // This is the MSFS root directory fcb = (PFCB)fsContext; break; default: // This is an illegal file object to query DebugTrace(0, Dbg, "Node type code is not incorrect\n", 0); MsDereferenceNode( (PNODE_HEADER)fsContext ); MsCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); DebugTrace(-1, Dbg, "MsCommonQueryInformation -> STATUS_INVALID_PARAMETER\n", 0); return STATUS_INVALID_PARAMETER; } // // 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 // MsAcquireSharedFcb( fcb ); try { // // 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; MsQueryBasicInfo( fcb, &AllInfo->BasicInformation ); MsQueryStandardInfo( fcb, &AllInfo->StandardInformation ); MsQueryInternalInfo( fcb, &AllInfo->InternalInformation ); MsQueryEaInfo( &AllInfo->EaInformation ); MsQueryPositionInfo( fcb, &AllInfo->PositionInformation ); length -= FIELD_OFFSET( FILE_ALL_INFORMATION, NameInformation ); status = MsQueryNameInfo( fcb, &AllInfo->NameInformation, &length ); break; case FileBasicInformation: MsQueryBasicInfo( fcb, buffer ); length -= sizeof( FILE_BASIC_INFORMATION ); break; case FileStandardInformation: MsQueryStandardInfo( fcb, buffer ); length -= sizeof( FILE_STANDARD_INFORMATION ); break; case FileInternalInformation: MsQueryInternalInfo( fcb, buffer ); length -= sizeof( FILE_INTERNAL_INFORMATION ); break; case FileEaInformation: MsQueryEaInfo( buffer ); length -= sizeof( FILE_EA_INFORMATION ); break; case FilePositionInformation: MsQueryPositionInfo( fcb, buffer ); length -= sizeof( FILE_POSITION_INFORMATION ); break; case FileNameInformation: status = MsQueryNameInfo( fcb, buffer, &length ); break; case FileMailslotQueryInformation: if( nodeTypeCode == MSFS_NTC_FCB ) { MsQueryMailslotInfo( fcb, buffer ); length -= sizeof( FILE_MAILSLOT_QUERY_INFORMATION ); } else { status = STATUS_INVALID_PARAMETER; } break; default: status = STATUS_INVALID_PARAMETER; break; } } finally { MsReleaseFcb( fcb ); MsDereferenceFcb( fcb ); // // Set the information field to the number of bytes actually // filled in and then complete the request. // Irp->IoStatus.Information = irpSp->Parameters.QueryFile.Length - length; MsCompleteRequest( Irp, status ); DebugTrace(-1, Dbg, "MsCommonQueryInformation -> %08lx\n", status ); } return status; } NTSTATUS MsCommonSetInformation ( IN PMSFS_DEVICE_OBJECT MsfsDeviceObject, IN PIRP Irp ) /*++ Routine Description: This is the common routine for setting information on a mailslot file. Arguments: Irp - Supplies the Irp to process Return Value: NTSTATUS - the return status for the operation --*/ { PIO_STACK_LOCATION irpSp; NTSTATUS status; ULONG length; FILE_INFORMATION_CLASS fileInformationClass; PVOID buffer; NODE_TYPE_CODE nodeTypeCode; PFCB fcb; PVOID fsContext2; PAGED_CODE(); // // Get the current Irp stack location. // irpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace(+1, Dbg, "MsCommonSetInformation...\n", 0); DebugTrace( 0, Dbg, " Irp = %08lx\n", (ULONG)Irp); DebugTrace( 0, Dbg, " ->Length = %08lx\n", irpSp->Parameters.SetFile.Length); DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", irpSp->Parameters.SetFile.FileInformationClass); DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", (ULONG)Irp->AssociatedIrp.SystemBuffer); // // Get a pointer to the FCB and ensure that this is a server side // handler to a mailslot file. // if ((nodeTypeCode = MsDecodeFileObject( irpSp->FileObject, (PVOID *)&fcb, &fsContext2 )) == NTC_UNDEFINED) { DebugTrace(0, Dbg, "The mailslot is disconnected from us\n", 0); MsCompleteRequest( Irp, STATUS_FILE_FORCED_CLOSED ); status = STATUS_FILE_FORCED_CLOSED; DebugTrace(-1, Dbg, "MsCommonSetInformation -> %08lx\n", status ); return status; } // // Case on the type of the context, We can only set information // on an FCB. // if (nodeTypeCode != MSFS_NTC_FCB) { MsDereferenceNode( &fcb->Header ); MsCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); DebugTrace(-1, Dbg, "MsCommonQueryInformation -> STATUS_INVALID_PARAMETER\n", 0); return STATUS_INVALID_PARAMETER; } // // Make local copies of the input parameters. // length = irpSp->Parameters.SetFile.Length; fileInformationClass = irpSp->Parameters.SetFile.FileInformationClass; buffer = Irp->AssociatedIrp.SystemBuffer; // // Acquire exclusive access to the FCB. // MsAcquireExclusiveFcb( fcb ); try { // // 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 = MsSetBasicInfo( fcb, buffer ); break; case FileMailslotSetInformation: status = MsSetMailslotInfo( Irp, fcb, buffer ); break; default: status = STATUS_INVALID_PARAMETER; break; } // // Directory information has changed. Complete any notify change // directory requests. // MsCheckForNotify( fcb->ParentDcb, FALSE, STATUS_SUCCESS ); } finally { MsReleaseFcb( fcb ); MsDereferenceFcb( fcb ); // // Complete the request. // MsCompleteRequest( Irp, status ); DebugTrace(-1, Dbg, "MsCommonSetInformation -> %08lx\n", status); } return status; } VOID MsQueryBasicInfo ( IN PFCB Fcb, IN PFILE_BASIC_INFORMATION Buffer ) /*++ Routine Description: This routine performs the query basic information operation. Arguments: Fcb - Supplies a pointer the FCB of mailslot being queried. Buffer - Supplies a pointer to the buffer where the information is to be returned. Return Value: VOID --*/ { PAGED_CODE(); DebugTrace(0, Dbg, "QueryBasicInfo...\n", 0); // // Zero out the buffer. // RtlZeroMemory( Buffer, sizeof(FILE_BASIC_INFORMATION) ); // // Set the various fields in the record. These times are not maintained for the root DCB0 // if( Fcb->Header.NodeTypeCode == MSFS_NTC_FCB ) { Buffer->CreationTime = Fcb->Specific.Fcb.CreationTime; Buffer->LastAccessTime = Fcb->Specific.Fcb.LastAccessTime; Buffer->LastWriteTime = Fcb->Specific.Fcb.LastModificationTime; Buffer->ChangeTime = Fcb->Specific.Fcb.LastChangeTime; } Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; return; } VOID MsQueryStandardInfo ( IN PFCB Fcb, IN PFILE_STANDARD_INFORMATION Buffer ) /*++ Routine Description: This routine performs the query standard information operation. Arguments: Fcb - Supplies the FCB of the mailslot being queried Buffer - Supplies a pointer to the buffer where the information is to be returned Return Value: VOID --*/ { PDATA_QUEUE dataQueue; PAGED_CODE(); DebugTrace(0, Dbg, "MsQueryStandardInfo...\n", 0); // // Zero out the buffer. // RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) ); // // The allocation size is the amount of quota we've charged the mailslot // creator. // if( Fcb->Header.NodeTypeCode == MSFS_NTC_FCB ) { dataQueue = &Fcb->DataQueue; Buffer->AllocationSize.QuadPart = dataQueue->Quota; // // The EOF is the number of written bytes ready to be read from the // mailslot. // Buffer->EndOfFile.QuadPart = dataQueue->BytesInQueue; Buffer->Directory = FALSE; } else { Buffer->Directory = TRUE; } Buffer->NumberOfLinks = 1; Buffer->DeletePending = TRUE; return; } VOID MsQueryInternalInfo ( IN PFCB Fcb, IN PFILE_INTERNAL_INFORMATION Buffer ) /*++ Routine Description: This routine performs the query internal information operation. Arguments: Fcb - Supplies the FCB of the mailslot 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 FCB. // Buffer->IndexNumber.QuadPart = (ULONG_PTR)Fcb; return; } VOID MsQueryEaInfo ( 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; } NTSTATUS MsQueryNameInfo ( IN PFCB Fcb, 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 mailslot 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; NTSTATUS status; PAGED_CODE(); DebugTrace(0, Dbg, "QueryNameInfo...\n", 0); // // 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 ( *Length >= fileNameSize ) { status = STATUS_SUCCESS; bytesToCopy = fileNameSize; } else { status = STATUS_BUFFER_OVERFLOW; bytesToCopy = *Length; } // // Copy over the file name and its length. // RtlCopyMemory (Buffer->FileName, Fcb->FullFileName.Buffer, bytesToCopy); Buffer->FileNameLength = bytesToCopy; *Length -= bytesToCopy; return status; } VOID MsQueryPositionInfo ( IN PFCB Fcb, IN PFILE_POSITION_INFORMATION Buffer ) /*++ Routine Description: This routine performs the query position information operation. Arguments: Fcb - Supplies the FCB of the mailslot being queried. Buffer - Supplies a pointer to the buffer where the information is to be returned. Return Value: VOID --*/ { PDATA_QUEUE dataQueue; PAGED_CODE(); DebugTrace(0, Dbg, "QueryPositionInfo...\n", 0); // // The current byte offset is the number of bytes available to read // in the mailslot buffer. // if( Fcb->Header.NodeTypeCode == MSFS_NTC_FCB ) { dataQueue = &Fcb->DataQueue; Buffer->CurrentByteOffset.QuadPart = dataQueue->BytesInQueue; } else { Buffer->CurrentByteOffset.QuadPart = 0; } return; } VOID MsQueryMailslotInfo ( IN PFCB Fcb, IN PFILE_MAILSLOT_QUERY_INFORMATION Buffer ) /*++ Routine Description: This routine performs the query mailslot information operation. Arguments: Fcb - Supplies the Fcb of the mailslot to query. Buffer - Supplies a pointer to the buffer where the information is to be returned. Return Value: VOID --*/ { PDATA_QUEUE dataQueue; PDATA_ENTRY dataEntry; PAGED_CODE(); DebugTrace(0, Dbg, "QueryMailslotInfo...\n", 0); // // Set the fields in the record. // dataQueue = &Fcb->DataQueue; Buffer->MaximumMessageSize = dataQueue->MaximumMessageSize; Buffer->MailslotQuota = dataQueue->Quota; Buffer->MessagesAvailable = dataQueue->EntriesInQueue; Buffer->ReadTimeout = Fcb->Specific.Fcb.ReadTimeout; if ( dataQueue->EntriesInQueue == 0 ) { Buffer->NextMessageSize = MAILSLOT_NO_MESSAGE; } else { dataEntry = CONTAINING_RECORD( dataQueue->DataEntryList.Flink, DATA_ENTRY, ListEntry ); Buffer->NextMessageSize = dataEntry->DataSize; } return; } NTSTATUS MsSetBasicInfo ( IN PFCB Fcb, IN PFILE_BASIC_INFORMATION Buffer ) /*++ Routine Description: This routine sets the basic information for a mailslot. Arguments: Fcb - Supplies the FCB for the mailslot being modified. Buffer - Supplies the buffer containing the data being set. Return Value: NTSTATUS - Returns our completion status. --*/ { PAGED_CODE(); DebugTrace(0, Dbg, "SetBasicInfo...\n", 0); if (((PLARGE_INTEGER)&Buffer->CreationTime)->QuadPart != 0) { // // Modify the creation time // Fcb->Specific.Fcb.CreationTime = Buffer->CreationTime; } if (((PLARGE_INTEGER)&Buffer->LastAccessTime)->QuadPart != 0) { // // Modify the last access time // Fcb->Specific.Fcb.LastAccessTime = Buffer->LastAccessTime; } if (((PLARGE_INTEGER)&Buffer->LastWriteTime)->QuadPart != 0) { // // Modify the last write time // Fcb->Specific.Fcb.LastModificationTime = Buffer->LastWriteTime; } if (((PLARGE_INTEGER)&Buffer->ChangeTime)->QuadPart != 0) { // // Modify the change time // Fcb->Specific.Fcb.LastChangeTime = Buffer->ChangeTime; } // // And return to our caller // return STATUS_SUCCESS; } NTSTATUS MsSetMailslotInfo ( IN PIRP Irp, IN PFCB Fcb, IN PFILE_MAILSLOT_SET_INFORMATION Buffer ) /*++ Routine Description: This routine sets the mailslot information for a mailslot. Arguments: Irp - Pointer to an irp that contains the requestor's mode. Fcb - Supplies the FCB for the mailslot being modified. Buffer - Supplies the buffer containing the data being set. Return Value: NTSTATUS - Returns our completion status. --*/ { BOOLEAN fileUpdated; PAGED_CODE(); DebugTrace(0, Dbg, "SetMaislotInfo...\n", 0); fileUpdated = FALSE; // // Check whether or not the DefaultTimeout parameter was specified. If // so, then set it in the FCB. // if (ARGUMENT_PRESENT( Buffer->ReadTimeout )) { // // A read timeout parameter was specified. Check to see whether // the caller's mode is kernel and if not capture the parameter inside // of a try...except clause. // if (Irp->RequestorMode != KernelMode) { try { ProbeForRead( Buffer->ReadTimeout, sizeof( LARGE_INTEGER ), sizeof( ULONG ) ); Fcb->Specific.Fcb.ReadTimeout = *(Buffer->ReadTimeout); } except(EXCEPTION_EXECUTE_HANDLER) { // // Something went awry attempting to access the parameter. // Get the reason for the error and return it as the status // value from this service. // return GetExceptionCode(); } } else { // // The caller's mode was kernel so simply store the parameter. // Fcb->Specific.Fcb.ReadTimeout = *(Buffer->ReadTimeout); } fileUpdated = TRUE; } // // Update the last change time, if necessary // if ( fileUpdated ) { KeQuerySystemTime( &Fcb->Specific.Fcb.LastChangeTime); } // // And return to our caller // return STATUS_SUCCESS; }