windows-nt/Source/XPSP1/NT/base/fs/ntfs/volinfo.c

1726 lines
42 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
}