NT4/private/ntos/cntfs/vattrsup.c
2020-09-30 17:12:29 +02:00

628 lines
16 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
VAttrSup.c
Abstract:
This module implements the attribute routines for NtOfs
Author:
Tom Miller [TomM] 10-Apr-1996
Revision History:
--*/
#include "NtfsProc.h"
//
// Define a tag for general pool allocations from this module
//
#undef MODULE_POOL_TAG
#define MODULE_POOL_TAG ('vFtN')
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtOfsCreateAttribute)
#endif
NTFSAPI
NTSTATUS
NtOfsCreateAttribute (
IN PIRP_CONTEXT IrpContext,
IN PFCB Fcb,
IN UNICODE_STRING Name,
IN CREATE_OPTIONS CreateOptions,
IN ULONG LogNonresidentToo,
OUT PSCB *ReturnScb
)
/*++
Routine Description:
This routine may be called to create / open a named data attribute
within a given file, which may or may not be recoverable.
Arguments:
Fcb - File in which the attribute is to be created. It is acquired exclusive
Name - Name of the attribute for all related Scbs and attributes on disk.
CreateOptions - Standard create flags.
LogNonresidentToo - Supplies nonzero if updates to the attribute should
be logged.
ReturnScb - Returns an Scb as handle for the attribute.
Return Value:
STATUS_OBJECT_NAME_COLLISION -- if CreateNew and attribute already exists
STATUS_OBJECT_NAME_NOT_FOUND -- if OpenExisting and attribute does not exist
--*/
{
ATTRIBUTE_ENUMERATION_CONTEXT LocalContext;
BOOLEAN FoundAttribute;
NTSTATUS Status = STATUS_SUCCESS;
PSCB Scb = NULL;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT( NtfsIsExclusiveFcb( Fcb ));
PAGED_CODE();
//
// Now, just create the Data Attribute.
//
NtfsInitializeAttributeContext( &LocalContext );
try {
//
// First see if the attribute already exists, by searching for the root
// attribute.
//
FoundAttribute = NtfsLookupAttributeByName( IrpContext,
Fcb,
&Fcb->FileReference,
$DATA,
&Name,
NULL,
TRUE,
&LocalContext );
//
// If it is not there, and the CreateOptions allow, then let's create
// the attribute root now. (First cleaning up the attribute context from
// the lookup).
//
if (!FoundAttribute && (CreateOptions <= CREATE_OR_OPEN)) {
NtfsCleanupAttributeContext( &LocalContext );
NtfsCreateAttributeWithValue( IrpContext,
Fcb,
$DATA,
&Name,
NULL,
0,
0,
NULL,
TRUE,
&LocalContext );
//
// If the attribute is already there, and we were asked to create it, then
// return an error.
//
} else if (FoundAttribute && (CreateOptions == CREATE_NEW)) {
Status = STATUS_OBJECT_NAME_COLLISION;
leave;
//
// If the attribute is not there, and we were supposed to open existing, then
// return an error.
//
} else if (!FoundAttribute) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
leave;
}
//
// Otherwise create/find the Scb and reference it.
//
Scb = NtfsCreateScb( IrpContext, Fcb, $DATA, &Name, FALSE, &FoundAttribute );
//
// Make sure things are correctly reference counted
//
NtfsIncrementCloseCounts( Scb, TRUE, FALSE );
//
// Make sure the stream can be mapped internally
//
if (Scb->FileObject == NULL) {
NtfsCreateInternalAttributeStream( IrpContext, Scb, TRUE );
}
//
// If we created the Scb, then get the no modified write set correctly.
//
ASSERT( !FoundAttribute ||
(LogNonresidentToo == BooleanFlagOn(Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE)) );
if (!FoundAttribute && LogNonresidentToo) {
SetFlag( Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE );
}
NtfsUpdateScbFromAttribute( IrpContext, Scb, NtfsFoundAttribute(&LocalContext) );
NtfsExpandQuotaToAllocationSize( IrpContext, Scb );
} finally {
if (AbnormalTermination( )) {
if (Scb != NULL) {
NtOfsCloseAttribute( IrpContext, Scb );
}
}
NtfsCleanupAttributeContext( &LocalContext );
}
*ReturnScb = Scb;
return Status;
}
NTFSAPI
VOID
NtOfsCloseAttribute (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb
)
/*++
Routine Description:
This routine may be called to close a previously returned handle on an attribute.
Arguments:
Scb - Supplies an Scb as the previously returned handle for this attribute.
Return Value:
None.
--*/
{
ASSERT( NtfsIsExclusiveFcb( Scb->Fcb ));
NtfsDecrementCloseCounts( IrpContext, Scb, NULL, TRUE, FALSE, TRUE );
}
NTFSAPI
VOID
NtOfsDeleteAttribute (
IN PIRP_CONTEXT IrpContext,
IN PFCB Fcb,
IN PSCB Scb
)
/*++
Routine Description:
This routine may be called to delete an attribute.
Arguments:
Fcb - Supplies an Fcb as the previously returned object handle for the file
Scb - Supplies an Scb as the previously returned handle for this attribute.
Return Value:
None (Deleting a nonexistant index is benign).
--*/
{
ATTRIBUTE_ENUMERATION_CONTEXT LocalContext;
BOOLEAN FoundAttribute;
ASSERT_IRP_CONTEXT( IrpContext );
PAGED_CODE();
ASSERT( NtfsIsExclusiveFcb( Fcb ));
try {
//
// First see if there is some attribute allocation, and if so truncate it
// away allowing this operation to be broken up.
//
NtfsInitializeAttributeContext( &LocalContext );
if (NtfsLookupAttributeByName( IrpContext,
Fcb,
&Fcb->FileReference,
$DATA,
&Scb->AttributeName,
NULL,
FALSE,
&LocalContext )
&&
!NtfsIsAttributeResident(NtfsFoundAttribute(&LocalContext))) {
ASSERT(Scb->FileObject != NULL);
NtfsDeleteAllocation( IrpContext, NULL, Scb, 0, MAXLONGLONG, TRUE, TRUE );
}
NtfsCleanupAttributeContext( &LocalContext );
//
// Initialize the attribute context on each trip through the loop.
//
NtfsInitializeAttributeContext( &LocalContext );
//
// Now there should be a single attribute record, so look it up and delete it.
//
FoundAttribute = NtfsLookupAttributeByName( IrpContext,
Fcb,
&Fcb->FileReference,
$DATA,
&Scb->AttributeName,
NULL,
TRUE,
&LocalContext );
ASSERT(FlagOn( Scb->ScbState, SCB_STATE_QUOTA_ENLARGED ));
NtfsDeleteAttributeRecord( IrpContext, Fcb, TRUE, FALSE, &LocalContext );
SetFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
} finally {
NtfsCleanupAttributeContext( &LocalContext );
}
}
NTFSAPI
LONGLONG
NtOfsQueryLength (
IN PSCB Scb
)
/*++
Routine Description:
This routine may be called to query the Length (FileSize) of an attribute.
Arguments:
Scb - Supplies an Scb as the previously returned handle for this attribute.
Length - Returns the current Length of the attribute.
Return Value:
None (Deleting a nonexistant index is benign).
--*/
{
LONGLONG Length;
ExAcquireFastMutex( Scb->Header.FastMutex );
Length = Scb->Header.FileSize.QuadPart;
ExReleaseFastMutex( Scb->Header.FastMutex );
return Length;
}
NTFSAPI
VOID
NtOfsSetLength (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb,
IN LONGLONG Length
)
/*++
Routine Description:
This routine may be called to set the Length (FileSize) of an attribute.
Arguments:
Scb - Supplies an Scb as the previously returned handle for this attribute.
Length - Supplies the new Length for the attribute.
Return Value:
None (Deleting a nonexistant index is benign).
--*/
{
ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
EOF_WAIT_BLOCK EofWaitBlock;
PFILE_OBJECT FileObject = Scb->FileObject;
PFCB Fcb = Scb->Fcb;
BOOLEAN DoingIoAtEof = FALSE;
BOOLEAN Truncating = FALSE;
BOOLEAN CleanupAttrContext = FALSE;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_SCB( Scb );
ASSERT( NtfsIsExclusiveScb( Scb ));
ASSERT(FileObject != NULL);
PAGED_CODE();
try {
//
// If this is a resident attribute we will try to keep it resident.
//
if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
//
// If the new file size is larger than a file record then convert
// to non-resident and use the non-resident code below. Otherwise
// call ChangeAttributeValue which may also convert to nonresident.
//
NtfsInitializeAttributeContext( &AttrContext );
CleanupAttrContext = TRUE;
NtfsLookupAttributeForScb( IrpContext,
Scb,
NULL,
&AttrContext );
//
// Either convert or change the attribute value.
//
if (Length >= Scb->Vcb->BytesPerFileRecordSegment) {
NtfsConvertToNonresident( IrpContext,
Fcb,
NtfsFoundAttribute( &AttrContext ),
FALSE,
&AttrContext );
} else {
ULONG AttributeOffset;
//
// We are sometimes called by MM during a create section, so
// for right now the best way we have of detecting a create
// section is whether or not the requestor mode is kernel.
//
if ((ULONG)Length > Scb->Header.FileSize.LowPart) {
AttributeOffset = Scb->Header.ValidDataLength.LowPart;
} else {
AttributeOffset = (ULONG) Length;
}
//
// ****TEMP Ideally we would do this simple case by hand.
//
NtfsChangeAttributeValue( IrpContext,
Fcb,
AttributeOffset,
NULL,
(ULONG)Length - AttributeOffset,
TRUE,
FALSE,
FALSE,
FALSE,
&AttrContext );
ExAcquireFastMutex( Scb->Header.FastMutex );
Scb->Header.FileSize.QuadPart = Length;
//
// If the file went non-resident, then the allocation size in
// the Scb is correct. Otherwise we quad-align the new file size.
//
if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
Scb->Header.AllocationSize.LowPart = QuadAlign( Scb->Header.FileSize.LowPart );
Scb->Header.ValidDataLength.QuadPart = Length;
Scb->TotalAllocated = Scb->Header.AllocationSize.QuadPart;
} else {
SetFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
}
ExReleaseFastMutex( Scb->Header.FastMutex );
//
// Now update Cc.
//
CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Scb->Header.AllocationSize );
//
// ****TEMP**** This hack is awaiting our actually doing this change
// in CcSetFileSizes.
//
*((PLONGLONG)(Scb->NonpagedScb->SegmentObject.SharedCacheMap) + 5) = Length;
leave;
}
}
//
// Nonresident path
//
// Now determine where the new file size lines up with the
// current file layout. The two cases we need to consider are
// where the new file size is less than the current file size and
// valid data length, in which case we need to shrink them.
// Or we new file size is greater than the current allocation,
// in which case we need to extend the allocation to match the
// new file size.
//
if (Length > Scb->Header.AllocationSize.QuadPart) {
//
// Add the allocation.
//
NtfsAddAllocation( IrpContext,
FileObject,
Scb,
LlClustersFromBytes( Scb->Vcb, Scb->Header.AllocationSize.QuadPart ),
LlClustersFromBytes(Scb->Vcb, (Length - Scb->Header.AllocationSize.QuadPart)),
FALSE );
ExAcquireFastMutex( Scb->Header.FastMutex );
Scb->Header.FileSize.QuadPart = Length;
ExReleaseFastMutex( Scb->Header.FastMutex );
//
// Otherwise see if we have to knock these numbers down...
//
} else {
ExAcquireFastMutex( Scb->Header.FastMutex );
if (Length < Scb->Header.ValidDataLength.QuadPart) {
Scb->Header.ValidDataLength.QuadPart = Length;
}
if (Length < Scb->ValidDataToDisk) {
Scb->ValidDataToDisk = Length;
}
Scb->Header.FileSize.QuadPart = Length;
ExReleaseFastMutex( Scb->Header.FastMutex );
}
//
// Call our common routine to modify the file sizes. We are now
// done with Length and NewValidDataLength, and we have
// PagingIo + main exclusive (so no one can be working on this Scb).
// NtfsWriteFileSizes uses the sizes in the Scb, and this is the
// one place where in Ntfs where we wish to use a different value
// for ValidDataLength. Therefore, we save the current ValidData
// and plug it with our desired value and restore on return.
//
NtfsWriteFileSizes( IrpContext,
Scb,
&Scb->Header.ValidDataLength.QuadPart,
FALSE,
TRUE );
//
// Now update Cc.
//
CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Scb->Header.AllocationSize );
} finally {
if (CleanupAttrContext) {
NtfsCleanupAttributeContext( &AttrContext );
}
}
}
NTFSAPI
VOID
NtOfsFlushAttribute (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb,
IN ULONG Purge
)
/*++
Routine Description:
This routine flushes the specified attribute, and optionally purges it from the cache.
Arguments:
Scb - Supplies an Scb as the previously returned handle for this attribute.
Purge - Supplies TRUE if the attribute is to be purged.
Return Value:
None (Deleting a nonexistant index is benign).
--*/
{
if (Purge) {
NtfsFlushAndPurgeScb( IrpContext, Scb, NULL );
} else {
NtfsFlushUserStream( IrpContext, Scb, NULL, 0 );
}
}