Author: tthompson Date: Tue Jul 5 07:00:43 2016 New Revision: 71820 URL: http://svn.reactos.org/svn/reactos?rev=71820&view=rev Log: [NTFS] Add ability to write to resident attributes. SetAttributeDataLength() - Check if the file is memory mapped before truncating +InternalSetResidentAttributeLength() - Used by SetAttributeDataLength()
Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/volinfo.c Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c URL: http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c?rev=71820&r1=71819&r2=71820&view=diff ============================================================================== --- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c [iso-8859-1] (original) +++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c [iso-8859-1] Tue Jul 5 07:00:43 2016 @@ -509,6 +509,8 @@ DbgPrint(" logical clusters: %I64u - %I64u\n", lcn, lcn + runcount - 1); } + else + DbgPrint(" %u bytes of data\n", Attribute->Resident.ValueLength); } } Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c URL: http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c?rev=71820&r1=71819&r2=71820&view=diff ============================================================================== --- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c [iso-8859-1] (original) +++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c [iso-8859-1] Tue Jul 5 07:00:43 2016 @@ -135,6 +135,8 @@ DPRINT("Found context\n"); *AttrCtx = PrepareAttributeContext(Attribute); + (*AttrCtx)->FileMFTIndex = MftRecord->MFTRecordNumber; + if (Offset != NULL) *Offset = Context.Offset; @@ -170,6 +172,50 @@ return AttrRecord->Resident.ValueLength; } +void +InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext, + PFILE_RECORD_HEADER FileRecord, + ULONG AttrOffset, + ULONG DataSize) +{ + ULONG EndMarker = AttributeEnd; + ULONG FinalMarker = FILE_RECORD_END; + ULONG NextAttributeOffset; + ULONG Offset; + USHORT Padding; + + DPRINT("InternalSetResidentAttributeLength( %p, %p, %lu, %lu )\n", AttrContext, FileRecord, AttrOffset, DataSize); + + // update ValueLength Field + AttrContext->Record.Resident.ValueLength = DataSize; + Offset = AttrOffset + FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.ValueLength); + RtlCopyMemory((PCHAR)FileRecord + Offset, &DataSize, sizeof(ULONG)); + + // calculate the record length and end marker offset + AttrContext->Record.Length = DataSize + AttrContext->Record.Resident.ValueOffset; + NextAttributeOffset = AttrOffset + AttrContext->Record.Length; + + // Ensure NextAttributeOffset is aligned to an 8-byte boundary + if (NextAttributeOffset % 8 != 0) + { + Padding = 8 - (NextAttributeOffset % 8); + NextAttributeOffset += Padding; + AttrContext->Record.Length += Padding; + } + + // update the record length + Offset = AttrOffset + FIELD_OFFSET(NTFS_ATTR_RECORD, Length); + RtlCopyMemory((PCHAR)FileRecord + Offset, &AttrContext->Record.Length, sizeof(ULONG)); + + // write the end marker + RtlCopyMemory((PCHAR)FileRecord + NextAttributeOffset, &EndMarker, sizeof(ULONG)); + + // write the final marker + Offset = NextAttributeOffset + sizeof(ULONG); + RtlCopyMemory((PCHAR)FileRecord + Offset, &FinalMarker, sizeof(ULONG)); + + FileRecord->BytesInUse = Offset + sizeof(ULONG); +} NTSTATUS SetAttributeDataLength(PFILE_OBJECT FileObject, @@ -179,6 +225,18 @@ PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize) { + NTSTATUS Status = STATUS_SUCCESS; + + // are we truncating the file? + if (DataSize->QuadPart < AttributeDataLength(&AttrContext->Record) + { + if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer, (PLARGE_INTEGER)&AllocationSize)) + { + DPRINT1("Can't truncate a memory-mapped file!\n"); + return STATUS_USER_MAPPED_FILE; + } + } + if (AttrContext->Record.IsNonResident) { ULONG BytesPerCluster = Fcb->Vcb->NtfsInfo.BytesPerCluster; @@ -238,28 +296,59 @@ AttrContext->Record.NonResident.DataSize = DataSize->QuadPart; AttrContext->Record.NonResident.InitializedSize = DataSize->QuadPart; + // copy the attribute record back into the FileRecord + RtlCopyMemory((PCHAR)FileRecord + AttrOffset, &AttrContext->Record, AttrContext->Record.Length); + } + else + { + // resident attribute + + // find the next attribute + ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length; + PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((PCHAR)FileRecord + NextAttributeOffset); + + //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord); + + // Do we need to increase the data length? + if (DataSize->QuadPart > AttrContext->Record.Resident.ValueLength) + { + // There's usually padding at the end of a record. Do we need to extend past it? + ULONG MaxValueLength = AttrContext->Record.Length - AttrContext->Record.Resident.ValueOffset; + if (MaxValueLength < DataSize->LowPart) + { + // If this is the last attribute, we could move the end marker to the very end of the file record + MaxValueLength += Fcb->Vcb->NtfsInfo.BytesPerFileRecord - NextAttributeOffset - (sizeof(ULONG) * 2); + + if (MaxValueLength < DataSize->LowPart || NextAttribute->Type != AttributeEnd) + { + DPRINT1("FIXME: Need to convert attribute to non-resident!\n"); + return STATUS_NOT_IMPLEMENTED; + } + } + } + else if (DataSize->LowPart < AttrContext->Record.Resident.ValueLength) + { + // we need to decrease the length + if (NextAttribute->Type != AttributeEnd) + { + DPRINT1("FIXME: Don't know how to decrease length of resident attribute unless it's the final attribute!\n"); + return STATUS_NOT_IMPLEMENTED; + } + } + + InternalSetResidentAttributeLength(AttrContext, FileRecord, AttrOffset, DataSize->LowPart); + } + + //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord); + + // write the updated file record back to disk + Status = UpdateFileRecord(Fcb->Vcb, Fcb->MFTIndex, FileRecord); + + if (NT_SUCCESS(Status)) + { Fcb->RFCB.FileSize = *DataSize; Fcb->RFCB.ValidDataLength = *DataSize; - - DPRINT("Data Size: %I64u\n", Fcb->RFCB.FileSize.QuadPart); - - //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord); - - // copy the attribute back into the FileRecord - RtlCopyMemory((PCHAR)FileRecord + AttrOffset, &AttrContext->Record, AttrContext->Record.Length); - - //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord); - - // write the updated file record back to disk - UpdateFileRecord(Fcb->Vcb, Fcb->MFTIndex, FileRecord); - CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize); - } - else - { - // we can't yet handle resident attributes - DPRINT1("FixMe: Can't handle increasing length of resident attribute\n"); - return STATUS_NOT_IMPLEMENTED; } return STATUS_SUCCESS; @@ -501,28 +590,75 @@ DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p)\n", Vcb, Context, Offset, Buffer, Length, RealLengthWritten); + *RealLengthWritten = 0; + // is this a resident attribute? if (!Context->Record.IsNonResident) { - DPRINT1("FIXME: Writing to resident NTFS records (small files) is not supported at this time.\n"); - // (TODO: This should be really easy to implement) - - /* LeftOver code from ReadAttribute(), may be helpful: - if (Offset > Context->Record.Resident.ValueLength) - return 0; + ULONG AttributeOffset; + PNTFS_ATTR_CONTEXT FoundContext; + PFILE_RECORD_HEADER FileRecord; + if (Offset + Length > Context->Record.Resident.ValueLength) - Length = (ULONG)(Context->Record.Resident.ValueLength - Offset); - RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length); - return Length;*/ - - return STATUS_NOT_IMPLEMENTED; // until we implement it + { + DPRINT1("DRIVER ERROR: Attribute is too small!\n"); + return STATUS_INVALID_PARAMETER; + } + + FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS); + + if (!FileRecord) + { + DPRINT1("Error: Couldn't allocate file record!\n"); + return STATUS_NO_MEMORY; + } + + // read the file record + ReadFileRecord(Vcb, Context->FileMFTIndex, FileRecord); + + // find where to write the attribute data to + Status = FindAttribute(Vcb, FileRecord, + Context->Record.Type, + (PCWSTR)((PCHAR)&Context->Record + Context->Record.NameOffset), + Context->Record.NameLength, + &FoundContext, + &AttributeOffset); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR: Couldn't find matching attribute!\n"); + ExFreePoolWithTag(FileRecord, TAG_NTFS); + return Status; + } + + DPRINT("Offset: %I64u, AttributeOffset: %u, ValueOffset: %u\n", Offset, AttributeOffset, Context->Record.Resident.ValueLength); + Offset += AttributeOffset + Context->Record.Resident.ValueOffset; + + if (Offset + Length > Vcb->NtfsInfo.BytesPerFileRecord) + { + DPRINT1("DRIVER ERROR: Data being written extends past end of file record!\n"); + ReleaseAttributeContext(FoundContext); + ExFreePoolWithTag(FileRecord, TAG_NTFS); + return STATUS_INVALID_PARAMETER; + } + + // copy the data being written into the file record + RtlCopyMemory((PCHAR)FileRecord + Offset, Buffer, Length); + + Status = UpdateFileRecord(Vcb, Context->FileMFTIndex, FileRecord); + + ReleaseAttributeContext(FoundContext); + ExFreePoolWithTag(FileRecord, TAG_NTFS); + + if (NT_SUCCESS(Status)) + *RealLengthWritten = Length; + + return Status; } // This is a non-resident attribute. // I. Find the corresponding start data run. - - *RealLengthWritten = 0; // FIXME: Cache seems to be non-working. Disable it for now //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize) @@ -718,6 +854,7 @@ } /* Apply update sequence array fixups. */ + DPRINT("Sequence number: %u\n", file->SequenceNumber); return FixupUpdateSequenceArray(Vcb, &file->Ntfs); } @@ -948,8 +1085,6 @@ // write the file record to the master file table Status = WriteAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (const PUCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord, &BytesWritten); - // TODO: Update MFT mirror - if (!NT_SUCCESS(Status)) { DPRINT1("UpdateFileRecord failed: %I64u written, %u expected\n", BytesWritten, Vcb->NtfsInfo.BytesPerFileRecord); Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h URL: http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h?rev=71820&r1=71819&r2=71820&view=diff ============================================================================== --- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h [iso-8859-1] (original) +++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h [iso-8859-1] Tue Jul 5 07:00:43 2016 @@ -174,6 +174,10 @@ AttributeLoggedUtilityStream = 0x100, AttributeEnd = 0xFFFFFFFF } ATTRIBUTE_TYPE, *PATTRIBUTE_TYPE; + +// FILE_RECORD_END seems to follow AttributeEnd in every file record starting with $Quota. +// No clue what data is being represented here. +#define FILE_RECORD_END 0x11477982 #define NTFS_FILE_MFT 0 #define NTFS_FILE_MFTMIRR 1 @@ -433,6 +437,7 @@ ULONGLONG CacheRunLength; LONGLONG CacheRunLastLCN; ULONGLONG CacheRunCurrentOffset; + ULONGLONG FileMFTIndex; NTFS_ATTR_RECORD Record; } NTFS_ATTR_CONTEXT, *PNTFS_ATTR_CONTEXT; Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/volinfo.c URL: http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/volinfo.c?rev=71820&r1=71819&r2=71820&view=diff ============================================================================== --- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/volinfo.c [iso-8859-1] (original) +++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/volinfo.c [iso-8859-1] Tue Jul 5 07:00:43 2016 @@ -116,7 +116,6 @@ ULONGLONG BitmapDataSize; PCHAR BitmapData; ULONGLONG FreeClusters = 0; - ULONG Read = 0; RTL_BITMAP Bitmap; DPRINT1("NtfsAllocateClusters(%p, %lu, %lu, %p, %p)\n", DeviceExt, FirstDesiredCluster, DesiredClusters, FirstAssignedCluster, AssignedClusters); @@ -158,7 +157,7 @@ DPRINT1("Total clusters in bitmap: %I64x\n", BitmapDataSize * 8); DPRINT1("Diff in size: %I64d B\n", ((BitmapDataSize * 8) - DeviceExt->NtfsInfo.ClusterCount) * DeviceExt->NtfsInfo.SectorsPerCluster * DeviceExt->NtfsInfo.BytesPerSector); - ReadAttribute(DeviceExt, DataContext, Read, (PCHAR)((ULONG_PTR)BitmapData + Read), (ULONG)BitmapDataSize); + ReadAttribute(DeviceExt, DataContext, 0, (PCHAR)BitmapData, (ULONG)BitmapDataSize); RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, DeviceExt->NtfsInfo.ClusterCount); FreeClusters = RtlNumberOfClearBits(&Bitmap);