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