|
|
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
VolInfo.c
Abstract:
This module implements the set and query volume information routines for Ntfs called by the dispatch driver.
Author:
Your Name [Email] dd-Mon-Year
Revision History:
--*/
#include "NtfsProc.h"
//
// The local debug trace level
//
#define Dbg (DEBUG_TRACE_VOLINFO)
//
// Local procedure prototypes
//
NTSTATUS NtfsQueryFsVolumeInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_VOLUME_INFORMATION Buffer, IN OUT PULONG Length );
NTSTATUS NtfsQueryFsSizeInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_SIZE_INFORMATION Buffer, IN OUT PULONG Length );
NTSTATUS NtfsQueryFsDeviceInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_DEVICE_INFORMATION Buffer, IN OUT PULONG Length );
NTSTATUS NtfsQueryFsAttributeInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer, IN OUT PULONG Length );
NTSTATUS NtfsQueryFsControlInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_CONTROL_INFORMATION Buffer, IN OUT PULONG Length );
NTSTATUS NtfsQueryFsFullSizeInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_FULL_SIZE_INFORMATION Buffer, IN OUT PULONG Length );
NTSTATUS NtfsQueryFsVolumeObjectIdInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_OBJECTID_INFORMATION Buffer, IN OUT PULONG Length ); NTSTATUS NtfsSetFsLabelInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_LABEL_INFORMATION Buffer );
NTSTATUS NtfsSetFsControlInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_CONTROL_INFORMATION Buffer );
NTSTATUS NtfsSetFsVolumeObjectIdInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_OBJECTID_INFORMATION Buffer ); #ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtfsCommonQueryVolumeInfo)
#pragma alloc_text(PAGE, NtfsCommonSetVolumeInfo)
#pragma alloc_text(PAGE, NtfsQueryFsAttributeInfo)
#pragma alloc_text(PAGE, NtfsQueryFsDeviceInfo)
#pragma alloc_text(PAGE, NtfsQueryFsSizeInfo)
#pragma alloc_text(PAGE, NtfsQueryFsVolumeInfo)
#pragma alloc_text(PAGE, NtfsQueryFsControlInfo)
#pragma alloc_text(PAGE, NtfsQueryFsFullSizeInfo)
#pragma alloc_text(PAGE, NtfsQueryFsVolumeObjectIdInfo)
#pragma alloc_text(PAGE, NtfsSetFsLabelInfo)
#pragma alloc_text(PAGE, NtfsSetFsControlInfo)
#pragma alloc_text(PAGE, NtfsSetFsVolumeObjectIdInfo)
#endif
NTSTATUS NtfsCommonQueryVolumeInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp )
/*++
Routine Description:
This is the common routine for query Volume Information 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;
ULONG Length; FS_INFORMATION_CLASS FsInformationClass; PVOID Buffer; BOOLEAN AcquiredVcb = FALSE;
ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_IRP( Irp ); ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
PAGED_CODE();
//
// Get the current stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace( +1, Dbg, ("NtfsCommonQueryVolumeInfo...\n") ); DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) ); DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) ); DebugTrace( 0, Dbg, ("Length = %08lx\n", IrpSp->Parameters.QueryVolume.Length) ); DebugTrace( 0, Dbg, ("FsInformationClass = %08lx\n", IrpSp->Parameters.QueryVolume.FsInformationClass) ); DebugTrace( 0, Dbg, ("Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer) );
//
// Reference our input parameters to make things easier
//
Length = IrpSp->Parameters.QueryVolume.Length; FsInformationClass = IrpSp->Parameters.QueryVolume.FsInformationClass; Buffer = Irp->AssociatedIrp.SystemBuffer;
//
// Extract and decode the file object to get the Vcb, we don't really
// care what the type of open is.
//
FileObject = IrpSp->FileObject; TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
//
// Let's kill invalid vol. query requests.
//
if (UnopenedFileObject == TypeOfOpen) {
DebugTrace( 0, Dbg, ("Invalid file object for write\n") ); DebugTrace( -1, Dbg, ("NtfsCommonQueryVolume: Exit -> %08lx\n", STATUS_INVALID_DEVICE_REQUEST) );
NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); return STATUS_INVALID_DEVICE_REQUEST; }
//
// Get the Vcb shared and raise if we can't wait for the resource.
// We're only using $Volume Scb for the query size calls because the info
// it gets is static and we only need to protect against dismount
// Doing this prevents a deadlock with commit extensions from mm which use
// this call. However for system files like the mft we always need the vcb to avoid deadlock
//
if ((FsInformationClass != FileFsSizeInformation) || (FlagOn( Scb->Fcb->FcbState, FCB_STATE_SYSTEM_FILE ))) { NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE ); AcquiredVcb = TRUE; } else { NtfsAcquireSharedScb( IrpContext, Scb ); }
try {
//
// Make sure the volume is mounted.
//
if ((AcquiredVcb && !FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) || (!AcquiredVcb && FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED))) { Irp->IoStatus.Information = 0; Status = STATUS_VOLUME_DISMOUNTED; leave; }
//
// Based on the information class we'll do different actions. Each
// of the procedures that we're calling fills up the output buffer
// if possible and returns true if it successfully filled the buffer
// and false if it couldn't wait for any I/O to complete.
//
switch (FsInformationClass) {
case FileFsVolumeInformation:
Status = NtfsQueryFsVolumeInfo( IrpContext, Vcb, Buffer, &Length ); break;
case FileFsSizeInformation:
Status = NtfsQueryFsSizeInfo( IrpContext, Vcb, Buffer, &Length ); break;
case FileFsDeviceInformation:
Status = NtfsQueryFsDeviceInfo( IrpContext, Vcb, Buffer, &Length ); break;
case FileFsAttributeInformation:
Status = NtfsQueryFsAttributeInfo( IrpContext, Vcb, Buffer, &Length ); break;
case FileFsControlInformation:
Status = NtfsQueryFsControlInfo( IrpContext, Vcb, Buffer, &Length ); break;
case FileFsFullSizeInformation: Status = NtfsQueryFsFullSizeInfo( IrpContext, Vcb, Buffer, &Length ); break;
case FileFsObjectIdInformation: Status = NtfsQueryFsVolumeObjectIdInfo( IrpContext, Vcb, Buffer, &Length ); break;
default:
Status = STATUS_INVALID_PARAMETER; break; }
//
// Set the information field to the number of bytes actually filled in
//
Irp->IoStatus.Information = IrpSp->Parameters.QueryVolume.Length - Length;
//
// Abort transaction on error by raising.
//
NtfsCleanupTransaction( IrpContext, Status, FALSE );
} finally {
DebugUnwind( NtfsCommonQueryVolumeInfo );
if (AcquiredVcb) { NtfsReleaseVcb( IrpContext, Vcb ); } else { NtfsReleaseScb( IrpContext, Scb ); }
DebugTrace( -1, Dbg, ("NtfsCommonQueryVolumeInfo -> %08lx\n", Status) ); }
NtfsCompleteRequest( IrpContext, Irp, Status ); return Status; }
NTSTATUS NtfsCommonSetVolumeInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp )
/*++
Routine Description:
This is the common routine for set Volume Information 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;
ULONG Length; FS_INFORMATION_CLASS FsInformationClass; PVOID Buffer;
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, ("NtfsCommonSetVolumeInfo\n") ); DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) ); DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) ); DebugTrace( 0, Dbg, ("Length = %08lx\n", IrpSp->Parameters.SetVolume.Length) ); DebugTrace( 0, Dbg, ("FsInformationClass = %08lx\n", IrpSp->Parameters.SetVolume.FsInformationClass) ); DebugTrace( 0, Dbg, ("Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer) );
//
// Reference our input parameters to make things easier
//
Length = IrpSp->Parameters.SetVolume.Length; FsInformationClass = IrpSp->Parameters.SetVolume.FsInformationClass; Buffer = Irp->AssociatedIrp.SystemBuffer;
//
// Extract and decode the file object to get the Vcb, we don't really
// care what the type of open is.
//
FileObject = IrpSp->FileObject; TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
if (TypeOfOpen != UserVolumeOpen && (TypeOfOpen != UserViewIndexOpen || FsInformationClass != FileFsControlInformation || Fcb != Vcb->QuotaTableScb->Fcb)) {
NtfsCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );
DebugTrace( -1, Dbg, ("NtfsCommonSetVolumeInfo -> STATUS_ACCESS_DENIED\n") );
return STATUS_ACCESS_DENIED; }
//
// The volume must be writable.
//
if (NtfsIsVolumeReadOnly( Vcb )) {
Status = STATUS_MEDIA_WRITE_PROTECTED; NtfsCompleteRequest( IrpContext, Irp, Status );
DebugTrace( -1, Dbg, ("NtfsCommonSetVolumeInfo -> %08lx\n", Status) ); return Status; }
//
// Acquire exclusive access to the Vcb
//
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
try {
//
// Proceed only if the volume is mounted.
//
if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
//
// Based on the information class we'll do different actions. Each
// of the procedures that we're calling performs the action if
// possible and returns true if it successful and false if it couldn't
// wait for any I/O to complete.
//
switch (FsInformationClass) {
case FileFsLabelInformation:
Status = NtfsSetFsLabelInfo( IrpContext, Vcb, Buffer ); break;
case FileFsControlInformation:
Status = NtfsSetFsControlInfo( IrpContext, Vcb, Buffer ); break;
case FileFsObjectIdInformation:
Status = NtfsSetFsVolumeObjectIdInfo( IrpContext, Vcb, Buffer ); break;
default:
Status = STATUS_INVALID_PARAMETER; break; }
} else {
Status = STATUS_FILE_INVALID; }
//
// Abort transaction on error by raising.
//
NtfsCleanupTransaction( IrpContext, Status, FALSE );
} finally {
DebugUnwind( NtfsCommonSetVolumeInfo );
NtfsReleaseVcb( IrpContext, Vcb );
DebugTrace( -1, Dbg, ("NtfsCommonSetVolumeInfo -> %08lx\n", Status) ); }
NtfsCompleteRequest( IrpContext, Irp, Status ); return Status; }
//
// Internal Support Routine
//
NTSTATUS NtfsQueryFsVolumeInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_VOLUME_INFORMATION Buffer, IN OUT PULONG Length )
/*++
Routine Description:
This routine implements the query volume info call
Arguments:
Vcb - Supplies the Vcb being queried
Buffer - Supplies a pointer to the output buffer where the information is to be returned
Length - Supplies the length of the buffer in byte. This variable upon return recieves the remaining bytes free in the buffer
Return Value:
NTSTATUS - Returns the status for the query
--*/
{ NTSTATUS Status;
ULONG BytesToCopy;
ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_VCB( Vcb );
PAGED_CODE();
DebugTrace( 0, Dbg, ("NtfsQueryFsVolumeInfo...\n") );
//
// Get the volume creation time from the Vcb.
//
Buffer->VolumeCreationTime.QuadPart = Vcb->VolumeCreationTime;
//
// Fill in the serial number and indicate that we support objects
//
Buffer->VolumeSerialNumber = Vcb->Vpb->SerialNumber; Buffer->SupportsObjects = TRUE;
Buffer->VolumeLabelLength = Vcb->Vpb->VolumeLabelLength;
//
// Update the length field with how much we have filled in so far.
//
*Length -= FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel[0]);
//
// See how many bytes of volume label we can copy
//
if (*Length >= (ULONG)Vcb->Vpb->VolumeLabelLength) {
Status = STATUS_SUCCESS;
BytesToCopy = Vcb->Vpb->VolumeLabelLength;
} else {
Status = STATUS_BUFFER_OVERFLOW;
BytesToCopy = *Length; }
//
// Copy over the volume label (if there is one).
//
RtlCopyMemory( &Buffer->VolumeLabel[0], &Vcb->Vpb->VolumeLabel[0], BytesToCopy);
//
// Update the buffer length by the amount we copied.
//
*Length -= BytesToCopy;
return Status; }
//
// Internal Support Routine
//
NTSTATUS NtfsQueryFsSizeInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_SIZE_INFORMATION Buffer, IN OUT PULONG Length )
/*++
Routine Description:
This routine implements the query size information call
Arguments:
Vcb - Supplies the Vcb being queried
Buffer - Supplies a pointer to the output buffer where the information is to be returned
Length - Supplies the length of the buffer in byte. This variable upon return recieves the remaining bytes free in the buffer
Return Value:
NTSTATUS - Returns the status for the query
--*/
{ ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_VCB( Vcb );
PAGED_CODE();
DebugTrace( 0, Dbg, ("NtfsQueryFsSizeInfo...\n") );
//
// Make sure the buffer is large enough and zero it out
//
if (*Length < sizeof(FILE_FS_SIZE_INFORMATION)) {
return STATUS_BUFFER_OVERFLOW; }
RtlZeroMemory( Buffer, sizeof(FILE_FS_SIZE_INFORMATION) );
//
// Check if we need to rescan the bitmap. Don't try this
// if we have started to teardown the volume.
//
if (FlagOn( Vcb->VcbState, VCB_STATE_RELOAD_FREE_CLUSTERS ) && FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
//
// Acquire the volume bitmap shared to rescan the bitmap.
//
NtfsAcquireExclusiveScb( IrpContext, Vcb->BitmapScb );
try {
NtfsScanEntireBitmap( IrpContext, Vcb, FALSE );
} finally {
NtfsReleaseScb( IrpContext, Vcb->BitmapScb ); } }
//
// Set the output buffer
//
Buffer->TotalAllocationUnits.QuadPart = Vcb->TotalClusters; Buffer->AvailableAllocationUnits.QuadPart = Vcb->FreeClusters - Vcb->TotalReserved; Buffer->SectorsPerAllocationUnit = Vcb->BytesPerCluster / Vcb->BytesPerSector; Buffer->BytesPerSector = Vcb->BytesPerSector;
if (Buffer->AvailableAllocationUnits.QuadPart < 0) { Buffer->AvailableAllocationUnits.QuadPart = 0; }
//
// If quota enforcement is enabled then the available allocation
// units. must be reduced by the available quota.
//
if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_ENFORCEMENT_ENABLED )) {
PCCB Ccb; ULONGLONG Quota; ULONGLONG QuotaLimit;
//
// Go grab the ccb out of the Irp.
//
Ccb = (PCCB) (IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)-> FileObject->FsContext2);
if (Ccb != NULL && Ccb->OwnerId != 0) {
NtfsGetRemainingQuota( IrpContext, Ccb->OwnerId, &Quota, &QuotaLimit, NULL );
} else {
NtfsGetRemainingQuota( IrpContext, NtfsGetCallersUserId( IrpContext ), &Quota, &QuotaLimit, NULL ); }
//
// Do not use LlClustersFromBytesTruncate it is signed and this must be
// an unsigned operation.
//
Quota = Int64ShrlMod32( Quota, Vcb->ClusterShift ); QuotaLimit = Int64ShrlMod32( QuotaLimit, Vcb->ClusterShift );
if (Quota < (ULONGLONG) Buffer->AvailableAllocationUnits.QuadPart) {
Buffer->AvailableAllocationUnits.QuadPart = Quota; DebugTrace( 0, Dbg, (" QQQQQ AvailableAllocation is quota limited to %I64x\n", Quota) ); }
if (QuotaLimit < (ULONGLONG) Vcb->TotalClusters) { Buffer->TotalAllocationUnits.QuadPart = QuotaLimit; DebugTrace( 0, Dbg, (" QQQQQ TotalAllocation is quota limited to %I64x\n", QuotaLimit) ); } }
//
// Adjust the length variable
//
DebugTrace( 0, Dbg, ("AvailableAllocation is %I64x\n", Buffer->AvailableAllocationUnits.QuadPart) ); DebugTrace( 0, Dbg, ("TotalAllocation is %I64x\n", Buffer->TotalAllocationUnits.QuadPart) ); *Length -= sizeof(FILE_FS_SIZE_INFORMATION);
return STATUS_SUCCESS; }
//
// Internal Support Routine
//
NTSTATUS NtfsQueryFsDeviceInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_DEVICE_INFORMATION Buffer, IN OUT PULONG Length )
/*++
Routine Description:
This routine implements the query device information call
Arguments:
Vcb - Supplies the Vcb being queried
Buffer - Supplies a pointer to the output buffer where the information is to be returned
Length - Supplies the length of the buffer in byte. This variable upon return recieves the remaining bytes free in the buffer
Return Value:
NTSTATUS - Returns the status for the query
--*/
{ ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_VCB( Vcb );
PAGED_CODE();
DebugTrace( 0, Dbg, ("NtfsQueryFsDeviceInfo...\n") );
//
// Make sure the buffer is large enough and zero it out
//
if (*Length < sizeof(FILE_FS_DEVICE_INFORMATION)) {
return STATUS_BUFFER_OVERFLOW; }
RtlZeroMemory( Buffer, sizeof(FILE_FS_DEVICE_INFORMATION) );
//
// Set the output buffer
//
Buffer->DeviceType = FILE_DEVICE_DISK; Buffer->Characteristics = Vcb->TargetDeviceObject->Characteristics;
//
// Adjust the length variable
//
*Length -= sizeof(FILE_FS_DEVICE_INFORMATION);
return STATUS_SUCCESS; }
//
// Internal Support Routine
//
NTSTATUS NtfsQueryFsAttributeInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer, IN OUT PULONG Length )
/*++
Routine Description:
This routine implements the query attribute information call
Arguments:
Vcb - Supplies the Vcb being queried
Buffer - Supplies a pointer to the output buffer where the information is to be returned
Length - Supplies the length of the buffer in byte. This variable upon return recieves the remaining bytes free in the buffer
Return Value:
NTSTATUS - Returns the status for the query
--*/
{ NTSTATUS Status; ULONG BytesToCopy;
ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_VCB( Vcb );
PAGED_CODE();
DebugTrace( 0, Dbg, ("NtfsQueryFsAttributeInfo...\n") );
//
// See how many bytes of the name we can copy.
//
*Length -= FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName[0]);
if ( *Length >= 8 ) {
Status = STATUS_SUCCESS;
BytesToCopy = 8;
} else {
Status = STATUS_BUFFER_OVERFLOW;
BytesToCopy = *Length; }
//
// Set the output buffer
//
Buffer->FileSystemAttributes = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK | FILE_FILE_COMPRESSION | FILE_PERSISTENT_ACLS | FILE_NAMED_STREAMS;
//
// This may be a version 1.x volume that has not been upgraded yet.
// It may also be an upgraded volume where we somehow failed to
// open the quota index. In either case, we should only tell the
// quota ui that this volume supports quotas if it really does.
//
if (Vcb->QuotaTableScb != NULL) {
SetFlag( Buffer->FileSystemAttributes, FILE_VOLUME_QUOTAS ); }
//
// Ditto for object ids.
//
if (Vcb->ObjectIdTableScb != NULL) {
SetFlag( Buffer->FileSystemAttributes, FILE_SUPPORTS_OBJECT_IDS ); }
//
// Encryption is trickier than quotas and object ids. It requires an
// upgraded volume as well as a registered encryption driver.
//
if (NtfsVolumeVersionCheck( Vcb, NTFS_ENCRYPTION_VERSION ) && FlagOn( NtfsData.Flags, NTFS_FLAGS_ENCRYPTION_DRIVER )) {
SetFlag( Buffer->FileSystemAttributes, FILE_SUPPORTS_ENCRYPTION ); }
//
// Reparse points and sparse files are supported in 5.0 volumes.
//
// For reparse points we verify whether the Vcb->ReparsePointTableScb has
// been initialized or not.
//
if (Vcb->ReparsePointTableScb != NULL) {
SetFlag( Buffer->FileSystemAttributes, FILE_SUPPORTS_REPARSE_POINTS ); } if (NtfsVolumeVersionCheck( Vcb, NTFS_SPARSE_FILE_VERSION )) {
SetFlag( Buffer->FileSystemAttributes, FILE_SUPPORTS_SPARSE_FILES ); }
//
// Clear the compression flag if we don't allow compression on this drive
// (i.e. large clusters)
//
if (!FlagOn( Vcb->AttributeFlagsMask, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
ClearFlag( Buffer->FileSystemAttributes, FILE_FILE_COMPRESSION ); }
if (NtfsIsVolumeReadOnly( Vcb )) {
SetFlag( Buffer->FileSystemAttributes, FILE_READ_ONLY_VOLUME ); } Buffer->MaximumComponentNameLength = 255; Buffer->FileSystemNameLength = BytesToCopy;; RtlCopyMemory( &Buffer->FileSystemName[0], L"NTFS", BytesToCopy );
//
// Adjust the length variable
//
*Length -= BytesToCopy;
return Status; }
//
// Internal Support Routine
//
NTSTATUS NtfsQueryFsControlInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_CONTROL_INFORMATION Buffer, IN OUT PULONG Length )
/*++
Routine Description:
This routine implements the query control information call
Arguments:
Vcb - Supplies the Vcb being queried
Buffer - Supplies a pointer to the output buffer where the information is to be returned
Length - Supplies the length of the buffer in byte. This variable upon return recieves the remaining bytes free in the buffer
Return Value:
NTSTATUS - Returns the status for the query
--*/
{ INDEX_ROW IndexRow; INDEX_KEY IndexKey; QUOTA_USER_DATA QuotaBuffer; PQUOTA_USER_DATA UserData; ULONG OwnerId; ULONG Count = 1; PREAD_CONTEXT ReadContext = NULL; NTSTATUS Status = STATUS_SUCCESS;
ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_VCB( Vcb );
PAGED_CODE();
DebugTrace( 0, Dbg, ("NtfsQueryFsControlInfo...\n") );
RtlZeroMemory( Buffer, sizeof( FILE_FS_CONTROL_INFORMATION ));
PAGED_CODE();
try {
//
// Fill in the quota information if quotas are running.
//
if (Vcb->QuotaTableScb != NULL) {
OwnerId = QUOTA_DEFAULTS_ID; IndexKey.KeyLength = sizeof( OwnerId ); IndexKey.Key = &OwnerId;
Status = NtOfsReadRecords( IrpContext, Vcb->QuotaTableScb, &ReadContext, &IndexKey, NtOfsMatchUlongExact, &IndexKey, &Count, &IndexRow, sizeof( QuotaBuffer ), &QuotaBuffer );
if (NT_SUCCESS( Status )) {
UserData = IndexRow.DataPart.Data;
Buffer->DefaultQuotaThreshold.QuadPart = UserData->QuotaThreshold; Buffer->DefaultQuotaLimit.QuadPart = UserData->QuotaLimit;
//
// If the quota info is corrupt or has not been rebuilt
// yet then indicate the information is incomplete.
//
if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_OUT_OF_DATE | QUOTA_FLAG_CORRUPT )) {
SetFlag( Buffer->FileSystemControlFlags, FILE_VC_QUOTAS_INCOMPLETE ); }
if ((Vcb->QuotaState & VCB_QUOTA_REPAIR_RUNNING) > VCB_QUOTA_REPAIR_POSTED ) {
SetFlag( Buffer->FileSystemControlFlags, FILE_VC_QUOTAS_REBUILDING ); }
//
// Set the quota information basied on where we want
// to be rather than where we are.
//
if (FlagOn( UserData->QuotaFlags, QUOTA_FLAG_ENFORCEMENT_ENABLED )) {
SetFlag( Buffer->FileSystemControlFlags, FILE_VC_QUOTA_ENFORCE );
} else if (FlagOn( UserData->QuotaFlags, QUOTA_FLAG_TRACKING_REQUESTED )) {
SetFlag( Buffer->FileSystemControlFlags, FILE_VC_QUOTA_TRACK ); }
if (FlagOn( UserData->QuotaFlags, QUOTA_FLAG_LOG_LIMIT)) {
SetFlag( Buffer->FileSystemControlFlags, FILE_VC_LOG_QUOTA_LIMIT );
}
if (FlagOn( UserData->QuotaFlags, QUOTA_FLAG_LOG_THRESHOLD)) {
SetFlag( Buffer->FileSystemControlFlags, FILE_VC_LOG_QUOTA_THRESHOLD );
} } }
} finally {
if (ReadContext != NULL) { NtOfsFreeReadContext( ReadContext ); }
}
//
// Adjust the length variable
//
*Length -= sizeof( FILE_FS_CONTROL_INFORMATION );
return Status; }
//
// Internal Support Routine
//
NTSTATUS NtfsQueryFsFullSizeInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_FULL_SIZE_INFORMATION Buffer, IN OUT PULONG Length )
/*++
Routine Description:
This routine implements the query full size information call
Arguments:
Vcb - Supplies the Vcb being queried
Buffer - Supplies a pointer to the output buffer where the information is to be returned
Length - Supplies the length of the buffer in byte. This variable upon return recieves the remaining bytes free in the buffer
Return Value:
NTSTATUS - Returns the status for the query
--*/
{ ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_VCB( Vcb );
PAGED_CODE();
DebugTrace( 0, Dbg, ("NtfsQueryFsFullSizeInfo...\n") );
//
// Make sure the buffer is large enough and zero it out
//
if (*Length < sizeof(FILE_FS_FULL_SIZE_INFORMATION)) {
return STATUS_BUFFER_OVERFLOW; }
RtlZeroMemory( Buffer, sizeof(FILE_FS_FULL_SIZE_INFORMATION) );
//
// Check if we need to rescan the bitmap. Don't try this
// if we have started to teardown the volume.
//
if (FlagOn( Vcb->VcbState, VCB_STATE_RELOAD_FREE_CLUSTERS ) && FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
//
// Acquire the volume bitmap shared to rescan the bitmap.
//
NtfsAcquireExclusiveScb( IrpContext, Vcb->BitmapScb );
try {
NtfsScanEntireBitmap( IrpContext, Vcb, FALSE );
} finally {
NtfsReleaseScb( IrpContext, Vcb->BitmapScb ); } }
//
// Set the output buffer
//
Buffer->TotalAllocationUnits.QuadPart = Vcb->TotalClusters; Buffer->CallerAvailableAllocationUnits.QuadPart = Vcb->FreeClusters - Vcb->TotalReserved; Buffer->ActualAvailableAllocationUnits.QuadPart = Vcb->FreeClusters - Vcb->TotalReserved; Buffer->SectorsPerAllocationUnit = Vcb->BytesPerCluster / Vcb->BytesPerSector; Buffer->BytesPerSector = Vcb->BytesPerSector;
if (Buffer->CallerAvailableAllocationUnits.QuadPart < 0) { Buffer->CallerAvailableAllocationUnits.QuadPart = 0; } if (Buffer->ActualAvailableAllocationUnits.QuadPart < 0) { Buffer->ActualAvailableAllocationUnits.QuadPart = 0; }
//
// If quota enforcement is enabled then the available allocation
// units. must be reduced by the available quota.
//
if (FlagOn(Vcb->QuotaFlags, QUOTA_FLAG_ENFORCEMENT_ENABLED)) { ULONGLONG Quota; ULONGLONG QuotaLimit; PCCB Ccb;
//
// Go grab the ccb out of the Irp.
//
Ccb = (PCCB) (IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)-> FileObject->FsContext2);
if (Ccb != NULL && Ccb->OwnerId != 0) {
NtfsGetRemainingQuota( IrpContext, Ccb->OwnerId, &Quota, &QuotaLimit, NULL );
} else {
NtfsGetRemainingQuota( IrpContext, NtfsGetCallersUserId( IrpContext ), &Quota, &QuotaLimit, NULL );
}
//
// Do not use LlClustersFromBytesTruncate it is signed and this must be
// an unsigned operation.
//
Quota = Int64ShrlMod32( Quota, Vcb->ClusterShift ); QuotaLimit = Int64ShrlMod32( QuotaLimit, Vcb->ClusterShift );
if (Quota < (ULONGLONG) Buffer->CallerAvailableAllocationUnits.QuadPart) {
Buffer->CallerAvailableAllocationUnits.QuadPart = Quota; } if (QuotaLimit < (ULONGLONG) Vcb->TotalClusters) { Buffer->TotalAllocationUnits.QuadPart = QuotaLimit; } }
//
// Adjust the length variable
//
*Length -= sizeof(FILE_FS_FULL_SIZE_INFORMATION);
return STATUS_SUCCESS; }
//
// Internal Support Routine
//
NTSTATUS NtfsQueryFsVolumeObjectIdInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_OBJECTID_INFORMATION Buffer, IN OUT PULONG Length )
/*++
Routine Description:
This routine implements the query volume object id information call
Arguments:
Vcb - Supplies the Vcb being queried
Buffer - Supplies a pointer to the output buffer where the information is to be returned
Length - Supplies the length of the buffer in byte. This variable upon return recieves the remaining bytes free in the buffer
Return Value:
NTSTATUS - Returns the status for the query
--*/
{ FILE_OBJECTID_BUFFER ObjectIdBuffer; NTSTATUS Status; ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_VCB( Vcb ); PAGED_CODE();
//
// The Vcb should be held so a dismount can't sneak in.
//
ASSERT_SHARED_RESOURCE( &(Vcb->Resource) );
//
// Fail for version 1.x volumes.
//
if (Vcb->ObjectIdTableScb == NULL) {
return STATUS_VOLUME_NOT_UPGRADED; }
if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
//
// Only try this if the volume has an object id.
//
if (!FlagOn( Vcb->VcbState, VCB_STATE_VALID_OBJECT_ID )) {
return STATUS_OBJECT_NAME_NOT_FOUND; }
//
// Get the object id extended info for the $Volume file. We
// can cheat a little because we have the key part of the object
// id stored in the Vcb.
//
Status = NtfsGetObjectIdExtendedInfo( IrpContext, Vcb, Vcb->VolumeObjectId, ObjectIdBuffer.ExtendedInfo ); //
// Copy both the indexed part and the extended info part out to the
// user's buffer.
//
if (Status == STATUS_SUCCESS) { RtlCopyMemory( Buffer->ObjectId, Vcb->VolumeObjectId, OBJECT_ID_KEY_LENGTH );
RtlCopyMemory( Buffer->ExtendedInfo, ObjectIdBuffer.ExtendedInfo, OBJECT_ID_EXT_INFO_LENGTH );
*Length -= (OBJECT_ID_EXT_INFO_LENGTH + OBJECT_ID_KEY_LENGTH); } } else {
Status = STATUS_VOLUME_DISMOUNTED; } return Status; }
//
// Internal Support Routine
//
NTSTATUS NtfsSetFsLabelInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_LABEL_INFORMATION Buffer )
/*++
Routine Description:
This routine implements the set label call
Arguments:
Vcb - Supplies the Vcb being altered
Buffer - Supplies a pointer to the input buffer containing the new label
Return Value:
NTSTATUS - Returns the status for the operation
--*/
{ ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_VCB( Vcb );
PAGED_CODE();
DebugTrace( 0, Dbg, ("NtfsSetFsLabelInfo...\n") );
//
// Check that the volume label length is supported by the system.
//
if (Buffer->VolumeLabelLength > MAXIMUM_VOLUME_LABEL_LENGTH) {
return STATUS_INVALID_VOLUME_LABEL; }
try {
//
// Initialize the attribute context and then lookup the volume name
// attribute for on the volume dasd file
//
NtfsInitializeAttributeContext( &AttributeContext );
if (NtfsLookupAttributeByCode( IrpContext, Vcb->VolumeDasdScb->Fcb, &Vcb->VolumeDasdScb->Fcb->FileReference, $VOLUME_NAME, &AttributeContext )) {
//
// We found the volume name so now simply update the label
//
NtfsChangeAttributeValue( IrpContext, Vcb->VolumeDasdScb->Fcb, 0, &Buffer->VolumeLabel[0], Buffer->VolumeLabelLength, TRUE, FALSE, FALSE, FALSE, &AttributeContext );
} else {
//
// We didn't find the volume name so now create a new label
//
NtfsCleanupAttributeContext( IrpContext, &AttributeContext ); NtfsInitializeAttributeContext( &AttributeContext );
NtfsCreateAttributeWithValue( IrpContext, Vcb->VolumeDasdScb->Fcb, $VOLUME_NAME, NULL, &Buffer->VolumeLabel[0], Buffer->VolumeLabelLength, 0, // Attributeflags
NULL, TRUE, &AttributeContext ); }
Vcb->Vpb->VolumeLabelLength = (USHORT)Buffer->VolumeLabelLength;
if ( Vcb->Vpb->VolumeLabelLength > MAXIMUM_VOLUME_LABEL_LENGTH) {
Vcb->Vpb->VolumeLabelLength = MAXIMUM_VOLUME_LABEL_LENGTH; }
RtlCopyMemory( &Vcb->Vpb->VolumeLabel[0], &Buffer->VolumeLabel[0], Vcb->Vpb->VolumeLabelLength );
} finally {
DebugUnwind( NtfsSetFsLabelInfo );
NtfsCleanupAttributeContext( IrpContext, &AttributeContext ); }
//
// and return to our caller
//
return STATUS_SUCCESS; }
//
// Internal Support Routine
//
NTSTATUS NtfsSetFsControlInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_CONTROL_INFORMATION Buffer )
/*++
Routine Description:
This routine implements the set volume quota control info call
Arguments:
Vcb - Supplies the Vcb being altered
Buffer - Supplies a pointer to the input buffer containing the new label
Return Value:
NTSTATUS - Returns the status for the operation
--*/
{ ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_VCB( Vcb );
PAGED_CODE();
if (Vcb->QuotaTableScb == NULL) { return( STATUS_INVALID_PARAMETER ); }
//
// Process the quota part of the control structure.
//
NtfsUpdateQuotaDefaults( IrpContext, Vcb, Buffer );
return STATUS_SUCCESS; }
//
// Internal Support Routine
//
NTSTATUS NtfsSetFsVolumeObjectIdInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_OBJECTID_INFORMATION Buffer ) /*++
Routine Description:
This routine implements the set volume object id call.
Arguments:
Vcb - Supplies the Vcb being altered
Buffer - Supplies a pointer to the input buffer containing the new label
Return Value:
NTSTATUS - Returns the status for the operation
--*/
{ FILE_OBJECTID_BUFFER ObjectIdBuffer; FILE_OBJECTID_BUFFER OldObjectIdBuffer; NTSTATUS Status = STATUS_SUCCESS; PFCB DasdFcb; ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_VCB( Vcb );
PAGED_CODE();
//
// The Vcb should be held so a dismount can't sneak in.
//
ASSERT_EXCLUSIVE_RESOURCE( &(Vcb->Resource) ); ASSERT( FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )); //
// Every mounted volume should have the dasd scb open.
//
ASSERT( Vcb->VolumeDasdScb != NULL );
//
// Fail for version 1.x volumes.
//
if (Vcb->ObjectIdTableScb == NULL) {
return STATUS_VOLUME_NOT_UPGRADED; }
DasdFcb = Vcb->VolumeDasdScb->Fcb;
//
// Make sure the volume doesn't already have an object id.
//
Status = NtfsGetObjectIdInternal( IrpContext, DasdFcb, FALSE, &OldObjectIdBuffer );
if (NT_SUCCESS( Status )) {
//
// This volume apparently has an object id, so we need to delete it.
//
Status = NtfsDeleteObjectIdInternal( IrpContext, DasdFcb, Vcb, TRUE ); //
// The volume currently has no object id, so update the in-memory object id.
//
if (NT_SUCCESS( Status )) { RtlZeroMemory( Vcb->VolumeObjectId, OBJECT_ID_KEY_LENGTH );
ClearFlag( Vcb->VcbState, VCB_STATE_VALID_OBJECT_ID ); } } else if ((Status == STATUS_OBJECTID_NOT_FOUND) || (Status == STATUS_OBJECT_NAME_NOT_FOUND)) {
//
// This volume does not have an object id, but nothing else went wrong
// while we were checking, so let's proceed normally.
//
Status = STATUS_SUCCESS; } else {
//
// The object id lookup failed for some unexpected reason.
// Let's get out of here and return that status to our caller.
//
return Status; }
//
// If we either didn't find an object id, or successfully deleted one,
// let's set the new object id.
//
if (NT_SUCCESS( Status )) { //
// I'd rather do one copy for the entire structure than one for
// the indexed part, and another for the extended info. I'd
// like to assert that the strucutres are still the same and I
// can safely do that.
//
ASSERT( sizeof( ObjectIdBuffer ) == sizeof( *Buffer ) );
RtlCopyMemory( &ObjectIdBuffer, Buffer, sizeof( ObjectIdBuffer ) ); //
// Set this object id for the $Volume file.
//
Status = NtfsSetObjectIdInternal( IrpContext, DasdFcb, Vcb, &ObjectIdBuffer );
//
// If all went well, update the in-memory object id.
//
if (NT_SUCCESS( Status )) { RtlCopyMemory( Vcb->VolumeObjectId, &ObjectIdBuffer.ObjectId, OBJECT_ID_KEY_LENGTH ); SetFlag( Vcb->VcbState, VCB_STATE_VALID_OBJECT_ID ); } } return Status; }
|