/*++ Copyright (c) 1991 Microsoft Corporation Module Name: Quota.c Abstract: This module implements the File set and query Quota routines for Ntfs called by the dispatch driver. Author: Jeff Havens [Jhavens] 12-Jul-1996 Revision History: --*/ #include "NtfsProc.h" // // The local debug trace level // #define Dbg (DEBUG_TRACE_QUOTA) // // Define a tag for general pool allocations from this module // #undef MODULE_POOL_TAG #define MODULE_POOL_TAG ('QFtN') #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, NtfsCommonQueryQuota) #pragma alloc_text(PAGE, NtfsCommonSetQuota) #endif NTSTATUS NtfsCommonQueryQuota ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for query Quota called by both the fsd and fsp threads. Arguments: Irp - Supplies the Irp to process Return Value: NTSTATUS - The return status for the operation --*/ { NTSTATUS Status; PIO_STACK_LOCATION IrpSp; PFILE_OBJECT FileObject; TYPE_OF_OPEN TypeOfOpen; PVCB Vcb; PFCB Fcb; PSCB Scb; PCCB Ccb; PFILE_GET_QUOTA_INFORMATION UserSidList; PFILE_QUOTA_INFORMATION QuotaBuffer = NULL; PFILE_QUOTA_INFORMATION MappedQuotaBuffer = NULL; PFILE_QUOTA_INFORMATION OriginalQuotaBuffer; ULONG OriginalBufferLength; ULONG UserBufferLength; ULONG UserSidListLength; PSID UserStartSid; ULONG OwnerId; BOOLEAN RestartScan; BOOLEAN ReturnSingleEntry; BOOLEAN IndexSpecified; BOOLEAN TempBufferAllocated = FALSE; ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_IRP( Irp ); ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL )); PAGED_CODE(); // // Get the current Irp stack location // IrpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace( +1, Dbg, ("NtfsCommonQueryQuota\n") ); DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) ); DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) ); DebugTrace( 0, Dbg, ("SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer) ); DebugTrace( 0, Dbg, ("Length = %08lx\n", IrpSp->Parameters.QueryQuota.Length) ); DebugTrace( 0, Dbg, ("SidList = %08lx\n", IrpSp->Parameters.QueryQuota.SidList) ); DebugTrace( 0, Dbg, ("SidListLength = %08lx\n", IrpSp->Parameters.QueryQuota.SidListLength) ); DebugTrace( 0, Dbg, ("StartSid = %08lx\n", IrpSp->Parameters.QueryQuota.StartSid) ); DebugTrace( 0, Dbg, ("RestartScan = %08lx\n", FlagOn(IrpSp->Flags, SL_RESTART_SCAN)) ); DebugTrace( 0, Dbg, ("ReturnSingleEntry = %08lx\n", FlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY)) ); DebugTrace( 0, Dbg, ("IndexSpecified = %08lx\n", FlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED)) ); // // Extract and decode the file object // FileObject = IrpSp->FileObject; UserBufferLength = IrpSp->Parameters.QueryQuota.Length; UserSidList = IrpSp->Parameters.QueryQuota.SidList; UserSidListLength = IrpSp->Parameters.QueryQuota.SidListLength; TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE ); // // This must be a user file or directory and the Ccb must indicate that // the caller opened the entire file. We don't like zero length user buffers or SidLists either. // if (((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen) && (TypeOfOpen != UserVolumeOpen) && (TypeOfOpen != UserViewIndexOpen)) || (UserBufferLength == 0) || ((UserSidList != NULL) && (UserSidListLength == 0)) || (Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) { NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); DebugTrace( -1, Dbg, ("NtfsCommonQueryQuota -> %08lx\n", STATUS_INVALID_PARAMETER) ); return STATUS_INVALID_PARAMETER; } // // Return nothing if quotas are not enabled. // if (Vcb->QuotaTableScb == NULL) { NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); return STATUS_INVALID_DEVICE_REQUEST; } // // Acquire the Vcb Shared. // NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE ); // // Use a try-finally to facilitate cleanup. // try { if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) { Status = STATUS_VOLUME_DISMOUNTED; leave; } // // Reference our input parameters to make things easier // UserStartSid = IrpSp->Parameters.QueryQuota.StartSid; RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN); ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY); IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED); // // Initialize our local variables. // Status = STATUS_SUCCESS; // // Map the user's buffer. // QuotaBuffer = NtfsMapUserBuffer( Irp, NormalPagePriority ); // // Allocate our own output buffer out of paranoia. // if (Irp->RequestorMode != KernelMode) { MappedQuotaBuffer = QuotaBuffer; QuotaBuffer = NtfsAllocatePool( PagedPool, UserBufferLength ); TempBufferAllocated = TRUE; } OriginalBufferLength = UserBufferLength; OriginalQuotaBuffer = QuotaBuffer; // // Let's clear the output buffer. // RtlZeroMemory( QuotaBuffer, UserBufferLength ); // // We now satisfy the user's request depending on whether he // specified an Quota name list, an Quota index or restarting the // search. // // // The user has supplied a list of Quota names. // if (UserSidList != NULL) { Status = NtfsQueryQuotaUserSidList( IrpContext, Vcb, UserSidList, QuotaBuffer, &UserBufferLength, ReturnSingleEntry ); } else { // // The user supplied an index into the Quota list. // if (IndexSpecified) { OwnerId = NtfsGetOwnerId( IrpContext, UserStartSid, FALSE, NULL ); if (OwnerId == QUOTA_INVALID_ID) { // // Fail the request. // Status = STATUS_INVALID_PARAMETER; leave; } } else { // // Start at the begining of the list if restart specified. // OwnerId = RestartScan ? QUOTA_FISRT_USER_ID - 1 : Ccb->LastOwnerId; } Status = NtfsFsQuotaQueryInfo( IrpContext, Vcb, OwnerId, ReturnSingleEntry, &QuotaBuffer, &UserBufferLength, Ccb ); // // If we specified SingleEntry, NextEntryOffset would still be uninitialized. // if (NT_SUCCESS( Status ) && ReturnSingleEntry) { QuotaBuffer->NextEntryOffset = 0; } } // // Copy the data onto the user buffer if we ended up allocating // a temporary buffer to work on. Check if there's anything to copy, too. // UserBufferLength reflects how much of the buffer is left. // if (TempBufferAllocated && (UserBufferLength < OriginalBufferLength)) { try { RtlCopyMemory( MappedQuotaBuffer, OriginalQuotaBuffer, OriginalBufferLength - UserBufferLength ); } except( EXCEPTION_EXECUTE_HANDLER ) { try_return( Status = STATUS_INVALID_USER_BUFFER ); } } if (UserBufferLength <= OriginalBufferLength) { Irp->IoStatus.Information = OriginalBufferLength - UserBufferLength; } else { ASSERT( FALSE ); Irp->IoStatus.Information = 0; } Irp->IoStatus.Status = Status; try_exit: NOTHING; } finally { DebugUnwind( NtfsCommonQueryQuota ); // // Release the Vcb. // NtfsReleaseVcb( IrpContext, Vcb ); if (TempBufferAllocated) { NtfsFreePool( OriginalQuotaBuffer ); } if (!AbnormalTermination()) { NtfsCompleteRequest( IrpContext, Irp, Status ); } // // And return to our caller // DebugTrace( -1, Dbg, ("NtfsCommonQueryQuota -> %08lx\n", Status) ); } return Status; } NTSTATUS NtfsCommonSetQuota ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for set Quota called by both the fsd and fsp threads. Arguments: Irp - Supplies the Irp to process Return Value: NTSTATUS - The return status for the operation --*/ { NTSTATUS Status; PIO_STACK_LOCATION IrpSp; PFILE_OBJECT FileObject; TYPE_OF_OPEN TypeOfOpen; PVCB Vcb; PFCB Fcb; PSCB Scb; PCCB Ccb; PFILE_QUOTA_INFORMATION Buffer; PFILE_QUOTA_INFORMATION SafeBuffer = NULL; ULONG UserBufferLength; ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_IRP( Irp ); ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL )); PAGED_CODE(); // // Get the current Irp stack location // IrpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace( +1, Dbg, ("NtfsCommonSetQuota\n") ); DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) ); DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) ); // // Extract and decode the file object // FileObject = IrpSp->FileObject; TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE ); // // Initialize the IoStatus values. // Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; UserBufferLength = IrpSp->Parameters.SetQuota.Length; // // Check that the file object is associated with either a user file or // user directory open or an open by file ID. // if ((Ccb == NULL) || (!FlagOn( Ccb->AccessFlags, MANAGE_VOLUME_ACCESS) && ((TypeOfOpen != UserViewIndexOpen) || (Fcb != Vcb->QuotaTableScb->Fcb))) || (UserBufferLength == 0) || !FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) { if (UserBufferLength != 0) { Status = STATUS_ACCESS_DENIED; } else { Status = STATUS_INVALID_PARAMETER; } NtfsCompleteRequest( IrpContext, Irp, Status ); DebugTrace( -1, Dbg, ("NtfsCommonQueryQuota -> %08lx\n", Status) ); return Status; } // // We must be writable. // if (NtfsIsVolumeReadOnly( Vcb )) { Status = STATUS_MEDIA_WRITE_PROTECTED; NtfsCompleteRequest( IrpContext, Irp, Status ); DebugTrace( -1, Dbg, ("NtfsCommonSetQuota -> %08lx\n", Status) ); return Status; } // // We must be waitable. // if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )) { Status = NtfsPostRequest( IrpContext, Irp ); DebugTrace( -1, Dbg, ("NtfsCommonSetQuota -> %08lx\n", Status) ); return Status; } // // Acquire the vcb shared. // NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE ); // // Use a try-finally to facilitate cleanup. // try { if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) { Status = STATUS_VOLUME_DISMOUNTED; leave; } // // Map the user's Quota buffer. // Buffer = NtfsMapUserBuffer( Irp, NormalPagePriority ); // // Be paranoid and copy the user buffer into kernel space. // if (Irp->RequestorMode != KernelMode) { SafeBuffer = NtfsAllocatePool( PagedPool, UserBufferLength ); try { RtlCopyMemory( SafeBuffer, Buffer, UserBufferLength ); } except( EXCEPTION_EXECUTE_HANDLER ) { Status = STATUS_INVALID_USER_BUFFER; leave; } Buffer = SafeBuffer; } // // Update the caller's Iosb. // Irp->IoStatus.Information = 0; Status = STATUS_SUCCESS; Status = NtfsFsQuotaSetInfo( IrpContext, Vcb, Buffer, UserBufferLength ); // // Check if there are transactions to cleanup. // NtfsCleanupTransaction( IrpContext, Status, FALSE ); } finally { DebugUnwind( NtfsCommonSetQuota ); // // Release the Vcb. // NtfsReleaseVcb( IrpContext, Vcb ); // // If we allocated a temporary buffer, free it. // if (SafeBuffer != NULL) { NtfsFreePool( SafeBuffer ); } // // Complete the Irp. // if (!AbnormalTermination()) { NtfsCompleteRequest( IrpContext, Irp, Status ); } DebugTrace( -1, Dbg, ("NtfsCommonSetQuota -> %08lx\n", Status) ); } return Status; }