|
36 | 36 |
|
37 | 37 | /* FUNCTIONS ****************************************************************/
|
38 | 38 |
|
| 39 | +/** |
| 40 | +* @name AddBitmap |
| 41 | +* @implemented |
| 42 | +* |
| 43 | +* Adds a $BITMAP attribute to a given FileRecord. |
| 44 | +* |
| 45 | +* @param Vcb |
| 46 | +* Pointer to an NTFS_VCB for the destination volume. |
| 47 | +* |
| 48 | +* @param FileRecord |
| 49 | +* Pointer to a complete file record to add the attribute to. |
| 50 | +* |
| 51 | +* @param AttributeAddress |
| 52 | +* Pointer to the region of memory that will receive the $INDEX_ALLOCATION attribute. |
| 53 | +* This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord). |
| 54 | +* |
| 55 | +* @param Name |
| 56 | +* Pointer to a string of 16-bit Unicode characters naming the attribute. Most often L"$I30". |
| 57 | +* |
| 58 | +* @param NameLength |
| 59 | +* The number of wide-characters in the name. L"$I30" Would use 4 here. |
| 60 | +* |
| 61 | +* @return |
| 62 | +* STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end |
| 63 | +* of the given file record, or if the file record isn't large enough for the attribute. |
| 64 | +* |
| 65 | +* @remarks |
| 66 | +* Only adding the attribute to the end of the file record is supported; AttributeAddress must |
| 67 | +* be of type AttributeEnd. |
| 68 | +* This could be improved by adding an $ATTRIBUTE_LIST to the file record if there's not enough space. |
| 69 | +* |
| 70 | +*/ |
| 71 | +NTSTATUS |
| 72 | +AddBitmap(PNTFS_VCB Vcb, |
| 73 | + PFILE_RECORD_HEADER FileRecord, |
| 74 | + PNTFS_ATTR_RECORD AttributeAddress, |
| 75 | + PCWSTR Name, |
| 76 | + USHORT NameLength) |
| 77 | +{ |
| 78 | + ULONG AttributeLength; |
| 79 | + // Calculate the header length |
| 80 | + ULONG ResidentHeaderLength = FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.Reserved) + sizeof(UCHAR); |
| 81 | + ULONG FileRecordEnd = AttributeAddress->Length; |
| 82 | + ULONG NameOffset; |
| 83 | + ULONG ValueOffset; |
| 84 | + // We'll start out with 8 bytes of bitmap data |
| 85 | + ULONG ValueLength = 8; |
| 86 | + ULONG BytesAvailable; |
| 87 | + |
| 88 | + if (AttributeAddress->Type != AttributeEnd) |
| 89 | + { |
| 90 | + DPRINT1("FIXME: Can only add $BITMAP attribute to the end of a file record.\n"); |
| 91 | + return STATUS_NOT_IMPLEMENTED; |
| 92 | + } |
| 93 | + |
| 94 | + NameOffset = ResidentHeaderLength; |
| 95 | + |
| 96 | + // Calculate ValueOffset, which will be aligned to a 4-byte boundary |
| 97 | + ValueOffset = ALIGN_UP_BY(NameOffset + (sizeof(WCHAR) * NameLength), VALUE_OFFSET_ALIGNMENT); |
| 98 | + |
| 99 | + // Calculate length of attribute |
| 100 | + AttributeLength = ValueOffset + ValueLength; |
| 101 | + AttributeLength = ALIGN_UP_BY(AttributeLength, ATTR_RECORD_ALIGNMENT); |
| 102 | + |
| 103 | + // Make sure the file record is large enough for the new attribute |
| 104 | + BytesAvailable = Vcb->NtfsInfo.BytesPerFileRecord - FileRecord->BytesInUse; |
| 105 | + if (BytesAvailable < AttributeLength) |
| 106 | + { |
| 107 | + DPRINT1("FIXME: Not enough room in file record for index allocation attribute!\n"); |
| 108 | + return STATUS_NOT_IMPLEMENTED; |
| 109 | + } |
| 110 | + |
| 111 | + // Set Attribute fields |
| 112 | + RtlZeroMemory(AttributeAddress, AttributeLength); |
| 113 | + |
| 114 | + AttributeAddress->Type = AttributeBitmap; |
| 115 | + AttributeAddress->Length = AttributeLength; |
| 116 | + AttributeAddress->NameLength = NameLength; |
| 117 | + AttributeAddress->NameOffset = NameOffset; |
| 118 | + AttributeAddress->Instance = FileRecord->NextAttributeNumber++; |
| 119 | + |
| 120 | + AttributeAddress->Resident.ValueLength = ValueLength; |
| 121 | + AttributeAddress->Resident.ValueOffset = ValueOffset; |
| 122 | + |
| 123 | + // Set the name |
| 124 | + RtlCopyMemory((PCHAR)((ULONG_PTR)AttributeAddress + NameOffset), Name, NameLength * sizeof(WCHAR)); |
| 125 | + |
| 126 | + // move the attribute-end and file-record-end markers to the end of the file record |
| 127 | + AttributeAddress = (PNTFS_ATTR_RECORD)((ULONG_PTR)AttributeAddress + AttributeAddress->Length); |
| 128 | + SetFileRecordEnd(FileRecord, AttributeAddress, FileRecordEnd); |
| 129 | + |
| 130 | + return STATUS_SUCCESS; |
| 131 | +} |
| 132 | + |
39 | 133 | /**
|
40 | 134 | * @name AddData
|
41 | 135 | * @implemented
|
@@ -258,6 +352,105 @@ AddFileName(PFILE_RECORD_HEADER FileRecord,
|
258 | 352 | return Status;
|
259 | 353 | }
|
260 | 354 |
|
| 355 | +/** |
| 356 | +* @name AddIndexAllocation |
| 357 | +* @implemented |
| 358 | +* |
| 359 | +* Adds an $INDEX_ALLOCATION attribute to a given FileRecord. |
| 360 | +* |
| 361 | +* @param Vcb |
| 362 | +* Pointer to an NTFS_VCB for the destination volume. |
| 363 | +* |
| 364 | +* @param FileRecord |
| 365 | +* Pointer to a complete file record to add the attribute to. |
| 366 | +* |
| 367 | +* @param AttributeAddress |
| 368 | +* Pointer to the region of memory that will receive the $INDEX_ALLOCATION attribute. |
| 369 | +* This address must reside within FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord). |
| 370 | +* |
| 371 | +* @param Name |
| 372 | +* Pointer to a string of 16-bit Unicode characters naming the attribute. Most often, this will be L"$I30". |
| 373 | +* |
| 374 | +* @param NameLength |
| 375 | +* The number of wide-characters in the name. L"$I30" Would use 4 here. |
| 376 | +* |
| 377 | +* @return |
| 378 | +* STATUS_SUCCESS on success. STATUS_NOT_IMPLEMENTED if target address isn't at the end |
| 379 | +* of the given file record, or if the file record isn't large enough for the attribute. |
| 380 | +* |
| 381 | +* @remarks |
| 382 | +* Only adding the attribute to the end of the file record is supported; AttributeAddress must |
| 383 | +* be of type AttributeEnd. |
| 384 | +* This could be improved by adding an $ATTRIBUTE_LIST to the file record if there's not enough space. |
| 385 | +* |
| 386 | +*/ |
| 387 | +NTSTATUS |
| 388 | +AddIndexAllocation(PNTFS_VCB Vcb, |
| 389 | + PFILE_RECORD_HEADER FileRecord, |
| 390 | + PNTFS_ATTR_RECORD AttributeAddress, |
| 391 | + PCWSTR Name, |
| 392 | + USHORT NameLength) |
| 393 | +{ |
| 394 | + ULONG RecordLength; |
| 395 | + ULONG FileRecordEnd; |
| 396 | + ULONG NameOffset; |
| 397 | + ULONG DataRunOffset; |
| 398 | + ULONG BytesAvailable; |
| 399 | + |
| 400 | + if (AttributeAddress->Type != AttributeEnd) |
| 401 | + { |
| 402 | + DPRINT1("FIXME: Can only add $INDEX_ALLOCATION attribute to the end of a file record.\n"); |
| 403 | + return STATUS_NOT_IMPLEMENTED; |
| 404 | + } |
| 405 | + |
| 406 | + // Calculate the name offset |
| 407 | + NameOffset = FIELD_OFFSET(NTFS_ATTR_RECORD, NonResident.CompressedSize); |
| 408 | + |
| 409 | + // Calculate the offset to the first data run |
| 410 | + DataRunOffset = (sizeof(WCHAR) * NameLength) + NameOffset; |
| 411 | + // The data run offset must be aligned to a 4-byte boundary |
| 412 | + DataRunOffset = ALIGN_UP_BY(DataRunOffset, DATA_RUN_ALIGNMENT); |
| 413 | + |
| 414 | + // Calculate the length of the new attribute; the empty data run will consist of a single byte |
| 415 | + RecordLength = DataRunOffset + 1; |
| 416 | + |
| 417 | + // The size of the attribute itself must be aligned to an 8 - byte boundary |
| 418 | + RecordLength = ALIGN_UP_BY(RecordLength, ATTR_RECORD_ALIGNMENT); |
| 419 | + |
| 420 | + // Back up the last 4-bytes of the file record (even though this value doesn't matter) |
| 421 | + FileRecordEnd = AttributeAddress->Length; |
| 422 | + |
| 423 | + // Make sure the file record can contain the new attribute |
| 424 | + BytesAvailable = Vcb->NtfsInfo.BytesPerFileRecord - FileRecord->BytesInUse; |
| 425 | + if (BytesAvailable < RecordLength) |
| 426 | + { |
| 427 | + DPRINT1("FIXME: Not enough room in file record for index allocation attribute!\n"); |
| 428 | + return STATUS_NOT_IMPLEMENTED; |
| 429 | + } |
| 430 | + |
| 431 | + // Set fields of attribute header |
| 432 | + RtlZeroMemory(AttributeAddress, RecordLength); |
| 433 | + |
| 434 | + AttributeAddress->Type = AttributeIndexAllocation; |
| 435 | + AttributeAddress->Length = RecordLength; |
| 436 | + AttributeAddress->IsNonResident = TRUE; |
| 437 | + AttributeAddress->NameLength = NameLength; |
| 438 | + AttributeAddress->NameOffset = NameOffset; |
| 439 | + AttributeAddress->Instance = FileRecord->NextAttributeNumber++; |
| 440 | + |
| 441 | + AttributeAddress->NonResident.MappingPairsOffset = DataRunOffset; |
| 442 | + AttributeAddress->NonResident.HighestVCN = (LONGLONG)-1; |
| 443 | + |
| 444 | + // Set the name |
| 445 | + RtlCopyMemory((PCHAR)((ULONG_PTR)AttributeAddress + NameOffset), Name, NameLength * sizeof(WCHAR)); |
| 446 | + |
| 447 | + // move the attribute-end and file-record-end markers to the end of the file record |
| 448 | + AttributeAddress = (PNTFS_ATTR_RECORD)((ULONG_PTR)AttributeAddress + AttributeAddress->Length); |
| 449 | + SetFileRecordEnd(FileRecord, AttributeAddress, FileRecordEnd); |
| 450 | + |
| 451 | + return STATUS_SUCCESS; |
| 452 | +} |
| 453 | + |
261 | 454 | /**
|
262 | 455 | * @name AddIndexRoot
|
263 | 456 | * @implemented
|
|
0 commit comments