[NTFS]
[reactos.git] / drivers / filesystems / ntfs / attrib.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2002,2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystem/ntfs/attrib.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMERS: Eric Kohl
24 * Valentin Verkhovsky
25 * Hervé Poussineau (hpoussin@reactos.org)
26 * Pierre Schweitzer (pierre@reactos.org)
27 */
28
29 /* INCLUDES *****************************************************************/
30
31 #include "ntfs.h"
32
33 #define NDEBUG
34 #include <debug.h>
35
36 /* FUNCTIONS ****************************************************************/
37
38 /**
39 * @name AddRun
40 * @implemented
41 *
42 * Adds a run of allocated clusters to a non-resident attribute.
43 *
44 * @param Vcb
45 * Pointer to an NTFS_VCB for the destination volume.
46 *
47 * @param AttrContext
48 * Pointer to an NTFS_ATTR_CONTEXT describing the destination attribute.
49 *
50 * @param AttrOffset
51 * Byte offset of the destination attribute relative to its file record.
52 *
53 * @param FileRecord
54 * Pointer to a complete copy of the file record containing the destination attribute. Must be at least
55 * Vcb->NtfsInfo.BytesPerFileRecord bytes long.
56 *
57 * @param NextAssignedCluster
58 * Logical cluster number of the start of the data run being added.
59 *
60 * @param RunLength
61 * How many clusters are in the data run being added. Can't be 0.
62 *
63 * @return
64 * STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if AttrContext describes a resident attribute.
65 * STATUS_INSUFFICIENT_RESOURCES if ConvertDataRunsToLargeMCB() fails or if we fail to allocate a
66 * buffer for the new data runs.
67 * STATUS_INSUFFICIENT_RESOURCES or STATUS_UNSUCCESSFUL if FsRtlAddLargeMcbEntry() fails.
68 * STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
69 * STATUS_NOT_IMPLEMENTED if we need to migrate the attribute to an attribute list (TODO).
70 *
71 * @remarks
72 * Clusters should have been allocated previously with NtfsAllocateClusters().
73 *
74 *
75 */
76 NTSTATUS
77 AddRun(PNTFS_VCB Vcb,
78 PNTFS_ATTR_CONTEXT AttrContext,
79 ULONG AttrOffset,
80 PFILE_RECORD_HEADER FileRecord,
81 ULONGLONG NextAssignedCluster,
82 ULONG RunLength)
83 {
84 NTSTATUS Status;
85 PUCHAR DataRun = (PUCHAR)&AttrContext->Record + AttrContext->Record.NonResident.MappingPairsOffset;
86 int DataRunMaxLength;
87 PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
88 LARGE_MCB DataRunsMCB;
89 ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
90 ULONGLONG NextVBN = AttrContext->Record.NonResident.LowestVCN;
91
92 // Allocate some memory for the RunBuffer
93 PUCHAR RunBuffer;
94 ULONG RunBufferOffset = 0;
95
96 if (!AttrContext->Record.IsNonResident)
97 return STATUS_INVALID_PARAMETER;
98
99 RunBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
100 if (!RunBuffer)
101 {
102 DPRINT1("ERROR: Couldn't allocate memory for data runs!\n");
103 return STATUS_INSUFFICIENT_RESOURCES;
104 }
105
106 // Convert the data runs to a map control block
107 Status = ConvertDataRunsToLargeMCB(DataRun, &DataRunsMCB, &NextVBN);
108 if (!NT_SUCCESS(Status))
109 {
110 DPRINT1("Unable to convert data runs to MCB (probably ran out of memory)!\n");
111 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
112 return Status;
113 }
114
115 // Add newly-assigned clusters to mcb
116 _SEH2_TRY{
117 if (!FsRtlAddLargeMcbEntry(&DataRunsMCB,
118 NextVBN,
119 NextAssignedCluster,
120 RunLength))
121 {
122 ExRaiseStatus(STATUS_UNSUCCESSFUL);
123 }
124 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
125 FsRtlUninitializeLargeMcb(&DataRunsMCB);
126 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
127 _SEH2_YIELD(_SEH2_GetExceptionCode());
128 } _SEH2_END;
129
130
131 // Convert the map control block back to encoded data runs
132 ConvertLargeMCBToDataRuns(&DataRunsMCB, RunBuffer, Vcb->NtfsInfo.BytesPerCluster, &RunBufferOffset);
133
134 // Get the amount of free space between the start of the of the first data run and the attribute end
135 DataRunMaxLength = AttrContext->Record.Length - AttrContext->Record.NonResident.MappingPairsOffset;
136
137 // Do we need to extend the attribute (or convert to attribute list)?
138 if (DataRunMaxLength < RunBufferOffset)
139 {
140 PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + NextAttributeOffset);
141 DataRunMaxLength += Vcb->NtfsInfo.BytesPerFileRecord - NextAttributeOffset - (sizeof(ULONG) * 2);
142
143 // Can we move the end of the attribute?
144 if (NextAttribute->Type != AttributeEnd || DataRunMaxLength < RunBufferOffset - 1)
145 {
146 DPRINT1("FIXME: Need to create attribute list! Max Data Run Length available: %d\n", DataRunMaxLength);
147 if (NextAttribute->Type != AttributeEnd)
148 DPRINT1("There's another attribute after this one with type %0xlx\n", NextAttribute->Type);
149 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
150 FsRtlUninitializeLargeMcb(&DataRunsMCB);
151 return STATUS_NOT_IMPLEMENTED;
152 }
153
154 // calculate position of end markers
155 NextAttributeOffset = AttrOffset + AttrContext->Record.NonResident.MappingPairsOffset + RunBufferOffset;
156 NextAttributeOffset = ALIGN_UP_BY(NextAttributeOffset, 8);
157
158 // Write the end markers
159 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + NextAttributeOffset);
160 NextAttribute->Type = AttributeEnd;
161 NextAttribute->Length = FILE_RECORD_END;
162
163 // Update the length
164 DestinationAttribute->Length = NextAttributeOffset - AttrOffset;
165 AttrContext->Record.Length = DestinationAttribute->Length;
166
167 // We need to increase the FileRecord size
168 FileRecord->BytesInUse = NextAttributeOffset + (sizeof(ULONG) * 2);
169 }
170
171 // NOTE: from this point on the original attribute record will contain invalid data in it's runbuffer
172 // TODO: Elegant fix? Could we free the old Record and allocate a new one without issue?
173
174 // Update HighestVCN
175 DestinationAttribute->NonResident.HighestVCN =
176 AttrContext->Record.NonResident.HighestVCN = max(NextVBN - 1 + RunLength,
177 AttrContext->Record.NonResident.HighestVCN);
178
179 // Write data runs to destination attribute
180 RtlCopyMemory((PVOID)((ULONG_PTR)DestinationAttribute + DestinationAttribute->NonResident.MappingPairsOffset),
181 RunBuffer,
182 RunBufferOffset);
183
184 // Update the file record
185 Status = UpdateFileRecord(Vcb, AttrContext->FileMFTIndex, FileRecord);
186
187 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
188 FsRtlUninitializeLargeMcb(&DataRunsMCB);
189
190 NtfsDumpDataRuns((PUCHAR)((ULONG_PTR)DestinationAttribute + DestinationAttribute->NonResident.MappingPairsOffset), 0);
191
192 return Status;
193 }
194
195 /**
196 * @name ConvertDataRunsToLargeMCB
197 * @implemented
198 *
199 * Converts binary data runs to a map control block.
200 *
201 * @param DataRun
202 * Pointer to the run data
203 *
204 * @param DataRunsMCB
205 * Pointer to an unitialized LARGE_MCB structure.
206 *
207 * @return
208 * STATUS_SUCCESS on success, STATUS_INSUFFICIENT_RESOURCES or STATUS_UNSUCCESSFUL if we fail to
209 * initialize the mcb or add an entry.
210 *
211 * @remarks
212 * Initializes the LARGE_MCB pointed to by DataRunsMCB. If this function succeeds, you
213 * need to call FsRtlUninitializeLargeMcb() when you're done with DataRunsMCB. This
214 * function will ensure the LargeMCB has been unitialized in case of failure.
215 *
216 */
217 NTSTATUS
218 ConvertDataRunsToLargeMCB(PUCHAR DataRun,
219 PLARGE_MCB DataRunsMCB,
220 PULONGLONG pNextVBN)
221 {
222 LONGLONG DataRunOffset;
223 ULONGLONG DataRunLength;
224 LONGLONG DataRunStartLCN;
225 ULONGLONG LastLCN = 0;
226
227 // Initialize the MCB, potentially catch an exception
228 _SEH2_TRY{
229 FsRtlInitializeLargeMcb(DataRunsMCB, NonPagedPool);
230 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
231 _SEH2_YIELD(return _SEH2_GetExceptionCode());
232 } _SEH2_END;
233
234 while (*DataRun != 0)
235 {
236 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
237
238 if (DataRunOffset != -1)
239 {
240 // Normal data run.
241 DataRunStartLCN = LastLCN + DataRunOffset;
242 LastLCN = DataRunStartLCN;
243
244 _SEH2_TRY{
245 if (!FsRtlAddLargeMcbEntry(DataRunsMCB,
246 *pNextVBN,
247 DataRunStartLCN,
248 DataRunLength))
249 {
250 ExRaiseStatus(STATUS_UNSUCCESSFUL);
251 }
252 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
253 FsRtlUninitializeLargeMcb(DataRunsMCB);
254 _SEH2_YIELD(return _SEH2_GetExceptionCode());
255 } _SEH2_END;
256
257 }
258
259 *pNextVBN += DataRunLength;
260 }
261
262 return STATUS_SUCCESS;
263 }
264
265 /**
266 * @name ConvertLargeMCBToDataRuns
267 * @implemented
268 *
269 * Converts a map control block to a series of encoded data runs (used by non-resident attributes).
270 *
271 * @param DataRunsMCB
272 * Pointer to a LARGE_MCB structure describing the data runs.
273 *
274 * @param RunBuffer
275 * Pointer to the buffer that will receive the encoded data runs.
276 *
277 * @param MaxBufferSize
278 * Size of RunBuffer, in bytes.
279 *
280 * @param UsedBufferSize
281 * Pointer to a ULONG that will receive the size of the data runs in bytes. Can't be NULL.
282 *
283 * @return
284 * STATUS_SUCCESS on success, STATUS_BUFFER_TOO_SMALL if RunBuffer is too small to contain the
285 * complete output.
286 *
287 */
288 NTSTATUS
289 ConvertLargeMCBToDataRuns(PLARGE_MCB DataRunsMCB,
290 PUCHAR RunBuffer,
291 ULONG MaxBufferSize,
292 PULONG UsedBufferSize)
293 {
294 NTSTATUS Status = STATUS_SUCCESS;
295 ULONG RunBufferOffset = 0;
296 LONGLONG DataRunOffset;
297 ULONGLONG LastLCN = 0;
298 LONGLONG Vbn, Lbn, Count;
299 ULONG i;
300
301
302 DPRINT("\t[Vbn, Lbn, Count]\n");
303
304 // convert each mcb entry to a data run
305 for (i = 0; FsRtlGetNextLargeMcbEntry(DataRunsMCB, i, &Vbn, &Lbn, &Count); i++)
306 {
307 UCHAR DataRunOffsetSize = 0;
308 UCHAR DataRunLengthSize = 0;
309 UCHAR ControlByte = 0;
310
311 // [vbn, lbn, count]
312 DPRINT("\t[%I64d, %I64d,%I64d]\n", Vbn, Lbn, Count);
313
314 // TODO: check for holes and convert to sparse runs
315 DataRunOffset = Lbn - LastLCN;
316 LastLCN = Lbn;
317
318 // now we need to determine how to represent DataRunOffset with the minimum number of bytes
319 DPRINT("Determining how many bytes needed to represent %I64x\n", DataRunOffset);
320 DataRunOffsetSize = GetPackedByteCount(DataRunOffset, TRUE);
321 DPRINT("%d bytes needed.\n", DataRunOffsetSize);
322
323 // determine how to represent DataRunLengthSize with the minimum number of bytes
324 DPRINT("Determining how many bytes needed to represent %I64x\n", Count);
325 DataRunLengthSize = GetPackedByteCount(Count, TRUE);
326 DPRINT("%d bytes needed.\n", DataRunLengthSize);
327
328 // ensure the next data run + end marker would be > Max buffer size
329 if (RunBufferOffset + 2 + DataRunLengthSize + DataRunOffsetSize > MaxBufferSize)
330 {
331 Status = STATUS_BUFFER_TOO_SMALL;
332 DPRINT1("FIXME: Ran out of room in buffer for data runs!\n");
333 break;
334 }
335
336 // pack and copy the control byte
337 ControlByte = (DataRunOffsetSize << 4) + DataRunLengthSize;
338 RunBuffer[RunBufferOffset++] = ControlByte;
339
340 // copy DataRunLength
341 RtlCopyMemory(RunBuffer + RunBufferOffset, &Count, DataRunLengthSize);
342 RunBufferOffset += DataRunLengthSize;
343
344 // copy DataRunOffset
345 RtlCopyMemory(RunBuffer + RunBufferOffset, &DataRunOffset, DataRunOffsetSize);
346 RunBufferOffset += DataRunOffsetSize;
347 }
348
349 // End of data runs
350 RunBuffer[RunBufferOffset++] = 0;
351
352 *UsedBufferSize = RunBufferOffset;
353 DPRINT("New Size of DataRuns: %ld\n", *UsedBufferSize);
354
355 return Status;
356 }
357
358 PUCHAR
359 DecodeRun(PUCHAR DataRun,
360 LONGLONG *DataRunOffset,
361 ULONGLONG *DataRunLength)
362 {
363 UCHAR DataRunOffsetSize;
364 UCHAR DataRunLengthSize;
365 CHAR i;
366
367 DataRunOffsetSize = (*DataRun >> 4) & 0xF;
368 DataRunLengthSize = *DataRun & 0xF;
369 *DataRunOffset = 0;
370 *DataRunLength = 0;
371 DataRun++;
372 for (i = 0; i < DataRunLengthSize; i++)
373 {
374 *DataRunLength += ((ULONG64)*DataRun) << (i * 8);
375 DataRun++;
376 }
377
378 /* NTFS 3+ sparse files */
379 if (DataRunOffsetSize == 0)
380 {
381 *DataRunOffset = -1;
382 }
383 else
384 {
385 for (i = 0; i < DataRunOffsetSize - 1; i++)
386 {
387 *DataRunOffset += ((ULONG64)*DataRun) << (i * 8);
388 DataRun++;
389 }
390 /* The last byte contains sign so we must process it different way. */
391 *DataRunOffset = ((LONG64)(CHAR)(*(DataRun++)) << (i * 8)) + *DataRunOffset;
392 }
393
394 DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize);
395 DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize);
396 DPRINT("DataRunOffset: %x\n", *DataRunOffset);
397 DPRINT("DataRunLength: %x\n", *DataRunLength);
398
399 return DataRun;
400 }
401
402 BOOLEAN
403 FindRun(PNTFS_ATTR_RECORD NresAttr,
404 ULONGLONG vcn,
405 PULONGLONG lcn,
406 PULONGLONG count)
407 {
408 if (vcn < NresAttr->NonResident.LowestVCN || vcn > NresAttr->NonResident.HighestVCN)
409 return FALSE;
410
411 DecodeRun((PUCHAR)((ULONG_PTR)NresAttr + NresAttr->NonResident.MappingPairsOffset), (PLONGLONG)lcn, count);
412
413 return TRUE;
414 }
415
416 /**
417 * @name FreeClusters
418 * @implemented
419 *
420 * Shrinks the allocation size of a non-resident attribute by a given number of clusters.
421 * Frees the clusters from the volume's $BITMAP file as well as the attribute's data runs.
422 *
423 * @param Vcb
424 * Pointer to an NTFS_VCB for the destination volume.
425 *
426 * @param AttrContext
427 * Pointer to an NTFS_ATTR_CONTEXT describing the attribute from which the clusters will be freed.
428 *
429 * @param AttrOffset
430 * Byte offset of the destination attribute relative to its file record.
431 *
432 * @param FileRecord
433 * Pointer to a complete copy of the file record containing the attribute. Must be at least
434 * Vcb->NtfsInfo.BytesPerFileRecord bytes long.
435 *
436 * @param ClustersToFree
437 * Number of clusters that should be freed from the end of the data stream. Must be no more
438 * Than the number of clusters assigned to the attribute (HighestVCN + 1).
439 *
440 * @return
441 * STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if AttrContext describes a resident attribute,
442 * or if the caller requested more clusters be freed than the attribute has been allocated.
443 * STATUS_INSUFFICIENT_RESOURCES if allocating a buffer for the data runs fails or
444 * if ConvertDataRunsToLargeMCB() fails.
445 * STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
446 *
447 *
448 */
449 NTSTATUS
450 FreeClusters(PNTFS_VCB Vcb,
451 PNTFS_ATTR_CONTEXT AttrContext,
452 ULONG AttrOffset,
453 PFILE_RECORD_HEADER FileRecord,
454 ULONG ClustersToFree)
455 {
456 NTSTATUS Status = STATUS_SUCCESS;
457 ULONG ClustersLeftToFree = ClustersToFree;
458
459 // convert data runs to mcb
460 PUCHAR DataRun = (PUCHAR)&AttrContext->Record + AttrContext->Record.NonResident.MappingPairsOffset;
461 PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
462 LARGE_MCB DataRunsMCB;
463 ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
464 PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + NextAttributeOffset);
465 ULONGLONG NextVBN = AttrContext->Record.NonResident.LowestVCN;
466
467 // Allocate some memory for the RunBuffer
468 PUCHAR RunBuffer;
469 ULONG RunBufferOffset = 0;
470
471 PFILE_RECORD_HEADER BitmapRecord;
472 PNTFS_ATTR_CONTEXT DataContext;
473 ULONGLONG BitmapDataSize;
474 PUCHAR BitmapData;
475 RTL_BITMAP Bitmap;
476 ULONG LengthWritten;
477
478 if (!AttrContext->Record.IsNonResident)
479 {
480 return STATUS_INVALID_PARAMETER;
481 }
482
483 RunBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
484 if (!RunBuffer)
485 {
486 DPRINT1("ERROR: Couldn't allocate memory for data runs!\n");
487 return STATUS_INSUFFICIENT_RESOURCES;
488 }
489
490 // Convert the data runs to a map control block
491 Status = ConvertDataRunsToLargeMCB(DataRun, &DataRunsMCB, &NextVBN);
492 if (!NT_SUCCESS(Status))
493 {
494 DPRINT1("Unable to convert data runs to MCB (probably ran out of memory)!\n");
495 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
496 return Status;
497 }
498
499 BitmapRecord = ExAllocatePoolWithTag(NonPagedPool,
500 Vcb->NtfsInfo.BytesPerFileRecord,
501 TAG_NTFS);
502 if (BitmapRecord == NULL)
503 {
504 DPRINT1("Error: Unable to allocate memory for bitmap file record!\n");
505 FsRtlUninitializeLargeMcb(&DataRunsMCB);
506 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
507 return STATUS_NO_MEMORY;
508 }
509
510 Status = ReadFileRecord(Vcb, NTFS_FILE_BITMAP, BitmapRecord);
511 if (!NT_SUCCESS(Status))
512 {
513 DPRINT1("Error: Unable to read file record for bitmap!\n");
514 FsRtlUninitializeLargeMcb(&DataRunsMCB);
515 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
516 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
517 return 0;
518 }
519
520 Status = FindAttribute(Vcb, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL);
521 if (!NT_SUCCESS(Status))
522 {
523 DPRINT1("Error: Unable to find data attribute for bitmap file!\n");
524 FsRtlUninitializeLargeMcb(&DataRunsMCB);
525 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
526 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
527 return 0;
528 }
529
530 BitmapDataSize = AttributeDataLength(&DataContext->Record);
531 BitmapDataSize = min(BitmapDataSize, 0xffffffff);
532 ASSERT((BitmapDataSize * 8) >= Vcb->NtfsInfo.ClusterCount);
533 BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, Vcb->NtfsInfo.BytesPerSector), TAG_NTFS);
534 if (BitmapData == NULL)
535 {
536 DPRINT1("Error: Unable to allocate memory for bitmap file data!\n");
537 ReleaseAttributeContext(DataContext);
538 FsRtlUninitializeLargeMcb(&DataRunsMCB);
539 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
540 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
541 return 0;
542 }
543
544 ReadAttribute(Vcb, DataContext, 0, (PCHAR)BitmapData, (ULONG)BitmapDataSize);
545
546 RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, Vcb->NtfsInfo.ClusterCount);
547
548 // free clusters in $BITMAP file
549 while (ClustersLeftToFree > 0)
550 {
551 LONGLONG LargeVbn, LargeLbn;
552
553 if (!FsRtlLookupLastLargeMcbEntry(&DataRunsMCB, &LargeVbn, &LargeLbn))
554 {
555 Status = STATUS_INVALID_PARAMETER;
556 DPRINT1("DRIVER ERROR: FreeClusters called to free %lu clusters, which is %lu more clusters than are assigned to attribute!",
557 ClustersToFree,
558 ClustersLeftToFree);
559 break;
560 }
561
562 if( LargeLbn != -1)
563 {
564 // deallocate this cluster
565 RtlClearBits(&Bitmap, LargeLbn, 1);
566 }
567 FsRtlTruncateLargeMcb(&DataRunsMCB, AttrContext->Record.NonResident.HighestVCN);
568 AttrContext->Record.NonResident.HighestVCN = min(AttrContext->Record.NonResident.HighestVCN, AttrContext->Record.NonResident.HighestVCN - 1);
569 ClustersLeftToFree--;
570 }
571
572 // update $BITMAP file on disk
573 Status = WriteAttribute(Vcb, DataContext, 0, BitmapData, (ULONG)BitmapDataSize, &LengthWritten);
574 if (!NT_SUCCESS(Status))
575 {
576 ReleaseAttributeContext(DataContext);
577 FsRtlUninitializeLargeMcb(&DataRunsMCB);
578 ExFreePoolWithTag(BitmapData, TAG_NTFS);
579 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
580 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
581 return Status;
582 }
583
584 ReleaseAttributeContext(DataContext);
585 ExFreePoolWithTag(BitmapData, TAG_NTFS);
586 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
587
588 // Convert the map control block back to encoded data runs
589 ConvertLargeMCBToDataRuns(&DataRunsMCB, RunBuffer, Vcb->NtfsInfo.BytesPerCluster, &RunBufferOffset);
590
591 // Update HighestVCN
592 DestinationAttribute->NonResident.HighestVCN = AttrContext->Record.NonResident.HighestVCN;
593
594 // Write data runs to destination attribute
595 RtlCopyMemory((PVOID)((ULONG_PTR)DestinationAttribute + DestinationAttribute->NonResident.MappingPairsOffset),
596 RunBuffer,
597 RunBufferOffset);
598
599 if (NextAttribute->Type == AttributeEnd)
600 {
601 // update attribute length
602 AttrContext->Record.Length = ALIGN_UP_BY(AttrContext->Record.NonResident.MappingPairsOffset + RunBufferOffset, 8);
603 DestinationAttribute->Length = AttrContext->Record.Length;
604
605 // write end markers
606 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)DestinationAttribute + DestinationAttribute->Length);
607 NextAttribute->Type = AttributeEnd;
608 NextAttribute->Length = FILE_RECORD_END;
609
610 // update file record length
611 FileRecord->BytesInUse = AttrOffset + DestinationAttribute->Length + (sizeof(ULONG) * 2);
612 }
613
614 // Update the file record
615 Status = UpdateFileRecord(Vcb, AttrContext->FileMFTIndex, FileRecord);
616
617 FsRtlUninitializeLargeMcb(&DataRunsMCB);
618 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
619
620 NtfsDumpDataRuns((PUCHAR)((ULONG_PTR)DestinationAttribute + DestinationAttribute->NonResident.MappingPairsOffset), 0);
621
622 return Status;
623 }
624
625 static
626 NTSTATUS
627 InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context)
628 {
629 ULONGLONG ListSize;
630 PNTFS_ATTR_RECORD Attribute;
631 PNTFS_ATTR_CONTEXT ListContext;
632
633 DPRINT("InternalReadNonResidentAttributes(%p)\n", Context);
634
635 Attribute = Context->CurrAttr;
636 ASSERT(Attribute->Type == AttributeAttributeList);
637
638 if (Context->OnlyResident)
639 {
640 Context->NonResidentStart = NULL;
641 Context->NonResidentEnd = NULL;
642 return STATUS_SUCCESS;
643 }
644
645 if (Context->NonResidentStart != NULL)
646 {
647 return STATUS_FILE_CORRUPT_ERROR;
648 }
649
650 ListContext = PrepareAttributeContext(Attribute);
651 ListSize = AttributeDataLength(&ListContext->Record);
652 if (ListSize > 0xFFFFFFFF)
653 {
654 ReleaseAttributeContext(ListContext);
655 return STATUS_BUFFER_OVERFLOW;
656 }
657
658 Context->NonResidentStart = ExAllocatePoolWithTag(NonPagedPool, (ULONG)ListSize, TAG_NTFS);
659 if (Context->NonResidentStart == NULL)
660 {
661 ReleaseAttributeContext(ListContext);
662 return STATUS_INSUFFICIENT_RESOURCES;
663 }
664
665 if (ReadAttribute(Context->Vcb, ListContext, 0, (PCHAR)Context->NonResidentStart, (ULONG)ListSize) != ListSize)
666 {
667 ExFreePoolWithTag(Context->NonResidentStart, TAG_NTFS);
668 Context->NonResidentStart = NULL;
669 ReleaseAttributeContext(ListContext);
670 return STATUS_FILE_CORRUPT_ERROR;
671 }
672
673 ReleaseAttributeContext(ListContext);
674 Context->NonResidentEnd = (PNTFS_ATTR_RECORD)((PCHAR)Context->NonResidentStart + ListSize);
675 return STATUS_SUCCESS;
676 }
677
678 static
679 PNTFS_ATTR_RECORD
680 InternalGetNextAttribute(PFIND_ATTR_CONTXT Context)
681 {
682 PNTFS_ATTR_RECORD NextAttribute;
683
684 if (Context->CurrAttr == (PVOID)-1)
685 {
686 return NULL;
687 }
688
689 if (Context->CurrAttr >= Context->FirstAttr &&
690 Context->CurrAttr < Context->LastAttr)
691 {
692 if (Context->CurrAttr->Length == 0)
693 {
694 DPRINT1("Broken length!\n");
695 Context->CurrAttr = (PVOID)-1;
696 return NULL;
697 }
698
699 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Context->CurrAttr + Context->CurrAttr->Length);
700
701 if (NextAttribute > Context->LastAttr || NextAttribute < Context->FirstAttr)
702 {
703 DPRINT1("Broken length: 0x%lx!\n", Context->CurrAttr->Length);
704 Context->CurrAttr = (PVOID)-1;
705 return NULL;
706 }
707
708 Context->Offset += ((ULONG_PTR)NextAttribute - (ULONG_PTR)Context->CurrAttr);
709 Context->CurrAttr = NextAttribute;
710
711 if (Context->CurrAttr < Context->LastAttr &&
712 Context->CurrAttr->Type != AttributeEnd)
713 {
714 return Context->CurrAttr;
715 }
716 }
717
718 if (Context->NonResidentStart == NULL)
719 {
720 Context->CurrAttr = (PVOID)-1;
721 return NULL;
722 }
723
724 if (Context->CurrAttr < Context->NonResidentStart ||
725 Context->CurrAttr >= Context->NonResidentEnd)
726 {
727 Context->CurrAttr = Context->NonResidentStart;
728 }
729 else if (Context->CurrAttr->Length != 0)
730 {
731 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Context->CurrAttr + Context->CurrAttr->Length);
732 Context->Offset += ((ULONG_PTR)NextAttribute - (ULONG_PTR)Context->CurrAttr);
733 Context->CurrAttr = NextAttribute;
734 }
735 else
736 {
737 DPRINT1("Broken length!\n");
738 Context->CurrAttr = (PVOID)-1;
739 return NULL;
740 }
741
742 if (Context->CurrAttr < Context->NonResidentEnd &&
743 Context->CurrAttr->Type != AttributeEnd)
744 {
745 return Context->CurrAttr;
746 }
747
748 Context->CurrAttr = (PVOID)-1;
749 return NULL;
750 }
751
752 NTSTATUS
753 FindFirstAttribute(PFIND_ATTR_CONTXT Context,
754 PDEVICE_EXTENSION Vcb,
755 PFILE_RECORD_HEADER FileRecord,
756 BOOLEAN OnlyResident,
757 PNTFS_ATTR_RECORD * Attribute)
758 {
759 NTSTATUS Status;
760
761 DPRINT("FindFistAttribute(%p, %p, %p, %p, %u, %p)\n", Context, Vcb, FileRecord, OnlyResident, Attribute);
762
763 Context->Vcb = Vcb;
764 Context->OnlyResident = OnlyResident;
765 Context->FirstAttr = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
766 Context->CurrAttr = Context->FirstAttr;
767 Context->LastAttr = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->BytesInUse);
768 Context->NonResidentStart = NULL;
769 Context->NonResidentEnd = NULL;
770 Context->Offset = FileRecord->AttributeOffset;
771
772 if (Context->FirstAttr->Type == AttributeEnd)
773 {
774 Context->CurrAttr = (PVOID)-1;
775 return STATUS_END_OF_FILE;
776 }
777 else if (Context->FirstAttr->Type == AttributeAttributeList)
778 {
779 Status = InternalReadNonResidentAttributes(Context);
780 if (!NT_SUCCESS(Status))
781 {
782 return Status;
783 }
784
785 *Attribute = InternalGetNextAttribute(Context);
786 if (*Attribute == NULL)
787 {
788 return STATUS_END_OF_FILE;
789 }
790 }
791 else
792 {
793 *Attribute = Context->CurrAttr;
794 Context->Offset = (UCHAR*)Context->CurrAttr - (UCHAR*)FileRecord;
795 }
796
797 return STATUS_SUCCESS;
798 }
799
800 NTSTATUS
801 FindNextAttribute(PFIND_ATTR_CONTXT Context,
802 PNTFS_ATTR_RECORD * Attribute)
803 {
804 NTSTATUS Status;
805
806 DPRINT("FindNextAttribute(%p, %p)\n", Context, Attribute);
807
808 *Attribute = InternalGetNextAttribute(Context);
809 if (*Attribute == NULL)
810 {
811 return STATUS_END_OF_FILE;
812 }
813
814 if (Context->CurrAttr->Type != AttributeAttributeList)
815 {
816 return STATUS_SUCCESS;
817 }
818
819 Status = InternalReadNonResidentAttributes(Context);
820 if (!NT_SUCCESS(Status))
821 {
822 return Status;
823 }
824
825 *Attribute = InternalGetNextAttribute(Context);
826 if (*Attribute == NULL)
827 {
828 return STATUS_END_OF_FILE;
829 }
830
831 return STATUS_SUCCESS;
832 }
833
834 VOID
835 FindCloseAttribute(PFIND_ATTR_CONTXT Context)
836 {
837 if (Context->NonResidentStart != NULL)
838 {
839 ExFreePoolWithTag(Context->NonResidentStart, TAG_NTFS);
840 Context->NonResidentStart = NULL;
841 }
842 }
843
844 static
845 VOID
846 NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute)
847 {
848 PFILENAME_ATTRIBUTE FileNameAttr;
849
850 DbgPrint(" $FILE_NAME ");
851
852 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
853
854 FileNameAttr = (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
855 DbgPrint(" (%x) '%.*S' ", FileNameAttr->NameType, FileNameAttr->NameLength, FileNameAttr->Name);
856 DbgPrint(" '%x' \n", FileNameAttr->FileAttributes);
857 DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr->AllocatedSize, FileNameAttr->DataSize);
858 }
859
860
861 static
862 VOID
863 NtfsDumpStandardInformationAttribute(PNTFS_ATTR_RECORD Attribute)
864 {
865 PSTANDARD_INFORMATION StandardInfoAttr;
866
867 DbgPrint(" $STANDARD_INFORMATION ");
868
869 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
870
871 StandardInfoAttr = (PSTANDARD_INFORMATION)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
872 DbgPrint(" '%x' ", StandardInfoAttr->FileAttribute);
873 }
874
875
876 static
877 VOID
878 NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute)
879 {
880 PWCHAR VolumeName;
881
882 DbgPrint(" $VOLUME_NAME ");
883
884 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
885
886 VolumeName = (PWCHAR)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
887 DbgPrint(" '%.*S' ", Attribute->Resident.ValueLength / sizeof(WCHAR), VolumeName);
888 }
889
890
891 static
892 VOID
893 NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute)
894 {
895 PVOLINFO_ATTRIBUTE VolInfoAttr;
896
897 DbgPrint(" $VOLUME_INFORMATION ");
898
899 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
900
901 VolInfoAttr = (PVOLINFO_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
902 DbgPrint(" NTFS Version %u.%u Flags 0x%04hx ",
903 VolInfoAttr->MajorVersion,
904 VolInfoAttr->MinorVersion,
905 VolInfoAttr->Flags);
906 }
907
908
909 static
910 VOID
911 NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute)
912 {
913 PINDEX_ROOT_ATTRIBUTE IndexRootAttr;
914
915 IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
916
917 if (IndexRootAttr->AttributeType == AttributeFileName)
918 ASSERT(IndexRootAttr->CollationRule == COLLATION_FILE_NAME);
919
920 DbgPrint(" $INDEX_ROOT (%uB, %u) ", IndexRootAttr->SizeOfEntry, IndexRootAttr->ClustersPerIndexRecord);
921
922 if (IndexRootAttr->Header.Flags == INDEX_ROOT_SMALL)
923 {
924 DbgPrint(" (small) ");
925 }
926 else
927 {
928 ASSERT(IndexRootAttr->Header.Flags == INDEX_ROOT_LARGE);
929 DbgPrint(" (large) ");
930 }
931 }
932
933
934 static
935 VOID
936 NtfsDumpAttribute(PDEVICE_EXTENSION Vcb,
937 PNTFS_ATTR_RECORD Attribute)
938 {
939 UNICODE_STRING Name;
940
941 ULONGLONG lcn = 0;
942 ULONGLONG runcount = 0;
943
944 switch (Attribute->Type)
945 {
946 case AttributeFileName:
947 NtfsDumpFileNameAttribute(Attribute);
948 break;
949
950 case AttributeStandardInformation:
951 NtfsDumpStandardInformationAttribute(Attribute);
952 break;
953
954 case AttributeObjectId:
955 DbgPrint(" $OBJECT_ID ");
956 break;
957
958 case AttributeSecurityDescriptor:
959 DbgPrint(" $SECURITY_DESCRIPTOR ");
960 break;
961
962 case AttributeVolumeName:
963 NtfsDumpVolumeNameAttribute(Attribute);
964 break;
965
966 case AttributeVolumeInformation:
967 NtfsDumpVolumeInformationAttribute(Attribute);
968 break;
969
970 case AttributeData:
971 DbgPrint(" $DATA ");
972 //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
973 break;
974
975 case AttributeIndexRoot:
976 NtfsDumpIndexRootAttribute(Attribute);
977 break;
978
979 case AttributeIndexAllocation:
980 DbgPrint(" $INDEX_ALLOCATION ");
981 break;
982
983 case AttributeBitmap:
984 DbgPrint(" $BITMAP ");
985 break;
986
987 case AttributeReparsePoint:
988 DbgPrint(" $REPARSE_POINT ");
989 break;
990
991 case AttributeEAInformation:
992 DbgPrint(" $EA_INFORMATION ");
993 break;
994
995 case AttributeEA:
996 DbgPrint(" $EA ");
997 break;
998
999 case AttributePropertySet:
1000 DbgPrint(" $PROPERTY_SET ");
1001 break;
1002
1003 case AttributeLoggedUtilityStream:
1004 DbgPrint(" $LOGGED_UTILITY_STREAM ");
1005 break;
1006
1007 default:
1008 DbgPrint(" Attribute %lx ",
1009 Attribute->Type);
1010 break;
1011 }
1012
1013 if (Attribute->Type != AttributeAttributeList)
1014 {
1015 if (Attribute->NameLength != 0)
1016 {
1017 Name.Length = Attribute->NameLength * sizeof(WCHAR);
1018 Name.MaximumLength = Name.Length;
1019 Name.Buffer = (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset);
1020
1021 DbgPrint("'%wZ' ", &Name);
1022 }
1023
1024 DbgPrint("(%s)\n",
1025 Attribute->IsNonResident ? "non-resident" : "resident");
1026
1027 if (Attribute->IsNonResident)
1028 {
1029 FindRun(Attribute,0,&lcn, &runcount);
1030
1031 DbgPrint(" AllocatedSize %I64u DataSize %I64u InitilizedSize %I64u\n",
1032 Attribute->NonResident.AllocatedSize, Attribute->NonResident.DataSize, Attribute->NonResident.InitializedSize);
1033 DbgPrint(" logical clusters: %I64u - %I64u\n",
1034 lcn, lcn + runcount - 1);
1035 }
1036 else
1037 DbgPrint(" %u bytes of data\n", Attribute->Resident.ValueLength);
1038 }
1039 }
1040
1041
1042 VOID NtfsDumpDataRunData(PUCHAR DataRun)
1043 {
1044 UCHAR DataRunOffsetSize;
1045 UCHAR DataRunLengthSize;
1046 CHAR i;
1047
1048 DbgPrint("%02x ", *DataRun);
1049
1050 if (*DataRun == 0)
1051 return;
1052
1053 DataRunOffsetSize = (*DataRun >> 4) & 0xF;
1054 DataRunLengthSize = *DataRun & 0xF;
1055
1056 DataRun++;
1057 for (i = 0; i < DataRunLengthSize; i++)
1058 {
1059 DbgPrint("%02x ", *DataRun);
1060 DataRun++;
1061 }
1062
1063 for (i = 0; i < DataRunOffsetSize; i++)
1064 {
1065 DbgPrint("%02x ", *DataRun);
1066 DataRun++;
1067 }
1068
1069 NtfsDumpDataRunData(DataRun);
1070 }
1071
1072
1073 VOID
1074 NtfsDumpDataRuns(PVOID StartOfRun,
1075 ULONGLONG CurrentLCN)
1076 {
1077 PUCHAR DataRun = StartOfRun;
1078 LONGLONG DataRunOffset;
1079 ULONGLONG DataRunLength;
1080
1081 if (CurrentLCN == 0)
1082 {
1083 DPRINT1("Dumping data runs.\n\tData:\n\t\t");
1084 NtfsDumpDataRunData(StartOfRun);
1085 DbgPrint("\n\tRuns:\n\t\tOff\t\tLCN\t\tLength\n");
1086 }
1087
1088 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1089
1090 if (DataRunOffset != -1)
1091 CurrentLCN += DataRunOffset;
1092
1093 DbgPrint("\t\t%I64d\t", DataRunOffset);
1094 if (DataRunOffset < 99999)
1095 DbgPrint("\t");
1096 DbgPrint("%I64u\t", CurrentLCN);
1097 if (CurrentLCN < 99999)
1098 DbgPrint("\t");
1099 DbgPrint("%I64u\n", DataRunLength);
1100
1101 if (*DataRun == 0)
1102 DbgPrint("\t\t00\n");
1103 else
1104 NtfsDumpDataRuns(DataRun, CurrentLCN);
1105 }
1106
1107
1108 VOID
1109 NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb,
1110 PFILE_RECORD_HEADER FileRecord)
1111 {
1112 NTSTATUS Status;
1113 FIND_ATTR_CONTXT Context;
1114 PNTFS_ATTR_RECORD Attribute;
1115
1116 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
1117 while (NT_SUCCESS(Status))
1118 {
1119 NtfsDumpAttribute(Vcb, Attribute);
1120
1121 Status = FindNextAttribute(&Context, &Attribute);
1122 }
1123
1124 FindCloseAttribute(&Context);
1125 }
1126
1127 PFILENAME_ATTRIBUTE
1128 GetFileNameFromRecord(PDEVICE_EXTENSION Vcb,
1129 PFILE_RECORD_HEADER FileRecord,
1130 UCHAR NameType)
1131 {
1132 FIND_ATTR_CONTXT Context;
1133 PNTFS_ATTR_RECORD Attribute;
1134 PFILENAME_ATTRIBUTE Name;
1135 NTSTATUS Status;
1136
1137 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
1138 while (NT_SUCCESS(Status))
1139 {
1140 if (Attribute->Type == AttributeFileName)
1141 {
1142 Name = (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
1143 if (Name->NameType == NameType ||
1144 (Name->NameType == NTFS_FILE_NAME_WIN32_AND_DOS && NameType == NTFS_FILE_NAME_WIN32) ||
1145 (Name->NameType == NTFS_FILE_NAME_WIN32_AND_DOS && NameType == NTFS_FILE_NAME_DOS))
1146 {
1147 FindCloseAttribute(&Context);
1148 return Name;
1149 }
1150 }
1151
1152 Status = FindNextAttribute(&Context, &Attribute);
1153 }
1154
1155 FindCloseAttribute(&Context);
1156 return NULL;
1157 }
1158
1159 /**
1160 * GetPackedByteCount
1161 * Returns the minimum number of bytes needed to represent the value of a
1162 * 64-bit number. Used to encode data runs.
1163 */
1164 UCHAR
1165 GetPackedByteCount(LONGLONG NumberToPack,
1166 BOOLEAN IsSigned)
1167 {
1168 int bytes = 0;
1169 if (!IsSigned)
1170 {
1171 if (NumberToPack >= 0x0100000000000000)
1172 return 8;
1173 if (NumberToPack >= 0x0001000000000000)
1174 return 7;
1175 if (NumberToPack >= 0x0000010000000000)
1176 return 6;
1177 if (NumberToPack >= 0x0000000100000000)
1178 return 5;
1179 if (NumberToPack >= 0x0000000001000000)
1180 return 4;
1181 if (NumberToPack >= 0x0000000000010000)
1182 return 3;
1183 if (NumberToPack >= 0x0000000000000100)
1184 return 2;
1185 return 1;
1186 }
1187
1188 if (NumberToPack > 0)
1189 {
1190 // we have to make sure the number that gets encoded won't be interpreted as negative
1191 if (NumberToPack >= 0x0080000000000000)
1192 return 8;
1193 if (NumberToPack >= 0x0000800000000000)
1194 return 7;
1195 if (NumberToPack >= 0x0000008000000000)
1196 return 6;
1197 if (NumberToPack >= 0x0000000080000000)
1198 return 5;
1199 if (NumberToPack >= 0x0000000000800000)
1200 return 4;
1201 if (NumberToPack >= 0x0000000000008000)
1202 return 3;
1203 if (NumberToPack >= 0x0000000000000080)
1204 return 2;
1205 return 1;
1206 }
1207 else
1208 {
1209 // negative number
1210 if (NumberToPack <= 0xff80000000000000)
1211 return 8;
1212 if (NumberToPack <= 0xffff800000000000)
1213 return 7;
1214 if (NumberToPack <= 0xffffff8000000000)
1215 return 6;
1216 if (NumberToPack <= 0xffffffff80000000)
1217 return 5;
1218 if (NumberToPack <= 0xffffffffff800000)
1219 return 4;
1220 if (NumberToPack <= 0xffffffffffff8000)
1221 return 3;
1222 if (NumberToPack <= 0xffffffffffffff80)
1223 return 2;
1224 return 1;
1225 }
1226 return bytes;
1227 }
1228
1229 NTSTATUS
1230 GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_RECORD Attribute, PULONGLONG LastCluster)
1231 {
1232 LONGLONG DataRunOffset;
1233 ULONGLONG DataRunLength;
1234 LONGLONG DataRunStartLCN;
1235
1236 ULONGLONG LastLCN = 0;
1237 PUCHAR DataRun = (PUCHAR)Attribute + Attribute->NonResident.MappingPairsOffset;
1238
1239 if (!Attribute->IsNonResident)
1240 return STATUS_INVALID_PARAMETER;
1241
1242 while (1)
1243 {
1244 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1245
1246 if (DataRunOffset != -1)
1247 {
1248 // Normal data run.
1249 DataRunStartLCN = LastLCN + DataRunOffset;
1250 LastLCN = DataRunStartLCN;
1251 *LastCluster = LastLCN + DataRunLength - 1;
1252 }
1253
1254 if (*DataRun == 0)
1255 break;
1256 }
1257
1258 return STATUS_SUCCESS;
1259 }
1260
1261 PSTANDARD_INFORMATION
1262 GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb,
1263 PFILE_RECORD_HEADER FileRecord)
1264 {
1265 NTSTATUS Status;
1266 FIND_ATTR_CONTXT Context;
1267 PNTFS_ATTR_RECORD Attribute;
1268 PSTANDARD_INFORMATION StdInfo;
1269
1270 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
1271 while (NT_SUCCESS(Status))
1272 {
1273 if (Attribute->Type == AttributeStandardInformation)
1274 {
1275 StdInfo = (PSTANDARD_INFORMATION)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
1276 FindCloseAttribute(&Context);
1277 return StdInfo;
1278 }
1279
1280 Status = FindNextAttribute(&Context, &Attribute);
1281 }
1282
1283 FindCloseAttribute(&Context);
1284 return NULL;
1285 }
1286
1287 PFILENAME_ATTRIBUTE
1288 GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb,
1289 PFILE_RECORD_HEADER FileRecord)
1290 {
1291 PFILENAME_ATTRIBUTE FileName;
1292
1293 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_POSIX);
1294 if (FileName == NULL)
1295 {
1296 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_WIN32);
1297 if (FileName == NULL)
1298 {
1299 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_DOS);
1300 }
1301 }
1302
1303 return FileName;
1304 }
1305
1306 /* EOF */