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