[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 NTSTATUS
39 AddRun(PNTFS_ATTR_CONTEXT AttrContext,
40 ULONGLONG NextAssignedCluster,
41 ULONG RunLength)
42 {
43 UNIMPLEMENTED;
44
45 if (!AttrContext->Record.IsNonResident)
46 return STATUS_INVALID_PARAMETER;
47
48 return STATUS_NOT_IMPLEMENTED;
49 }
50
51 PUCHAR
52 DecodeRun(PUCHAR DataRun,
53 LONGLONG *DataRunOffset,
54 ULONGLONG *DataRunLength)
55 {
56 UCHAR DataRunOffsetSize;
57 UCHAR DataRunLengthSize;
58 CHAR i;
59
60 DataRunOffsetSize = (*DataRun >> 4) & 0xF;
61 DataRunLengthSize = *DataRun & 0xF;
62 *DataRunOffset = 0;
63 *DataRunLength = 0;
64 DataRun++;
65 for (i = 0; i < DataRunLengthSize; i++)
66 {
67 *DataRunLength += ((ULONG64)*DataRun) << (i * 8);
68 DataRun++;
69 }
70
71 /* NTFS 3+ sparse files */
72 if (DataRunOffsetSize == 0)
73 {
74 *DataRunOffset = -1;
75 }
76 else
77 {
78 for (i = 0; i < DataRunOffsetSize - 1; i++)
79 {
80 *DataRunOffset += ((ULONG64)*DataRun) << (i * 8);
81 DataRun++;
82 }
83 /* The last byte contains sign so we must process it different way. */
84 *DataRunOffset = ((LONG64)(CHAR)(*(DataRun++)) << (i * 8)) + *DataRunOffset;
85 }
86
87 DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize);
88 DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize);
89 DPRINT("DataRunOffset: %x\n", *DataRunOffset);
90 DPRINT("DataRunLength: %x\n", *DataRunLength);
91
92 return DataRun;
93 }
94
95 BOOLEAN
96 FindRun(PNTFS_ATTR_RECORD NresAttr,
97 ULONGLONG vcn,
98 PULONGLONG lcn,
99 PULONGLONG count)
100 {
101 if (vcn < NresAttr->NonResident.LowestVCN || vcn > NresAttr->NonResident.HighestVCN)
102 return FALSE;
103
104 DecodeRun((PUCHAR)((ULONG_PTR)NresAttr + NresAttr->NonResident.MappingPairsOffset), (PLONGLONG)lcn, count);
105
106 return TRUE;
107 }
108
109 static
110 NTSTATUS
111 InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context)
112 {
113 ULONGLONG ListSize;
114 PNTFS_ATTR_RECORD Attribute;
115 PNTFS_ATTR_CONTEXT ListContext;
116
117 DPRINT("InternalReadNonResidentAttributes(%p)\n", Context);
118
119 Attribute = Context->CurrAttr;
120 ASSERT(Attribute->Type == AttributeAttributeList);
121
122 if (Context->OnlyResident)
123 {
124 Context->NonResidentStart = NULL;
125 Context->NonResidentEnd = NULL;
126 return STATUS_SUCCESS;
127 }
128
129 if (Context->NonResidentStart != NULL)
130 {
131 return STATUS_FILE_CORRUPT_ERROR;
132 }
133
134 ListContext = PrepareAttributeContext(Attribute);
135 ListSize = AttributeDataLength(&ListContext->Record);
136 if (ListSize > 0xFFFFFFFF)
137 {
138 ReleaseAttributeContext(ListContext);
139 return STATUS_BUFFER_OVERFLOW;
140 }
141
142 Context->NonResidentStart = ExAllocatePoolWithTag(NonPagedPool, (ULONG)ListSize, TAG_NTFS);
143 if (Context->NonResidentStart == NULL)
144 {
145 ReleaseAttributeContext(ListContext);
146 return STATUS_INSUFFICIENT_RESOURCES;
147 }
148
149 if (ReadAttribute(Context->Vcb, ListContext, 0, (PCHAR)Context->NonResidentStart, (ULONG)ListSize) != ListSize)
150 {
151 ExFreePoolWithTag(Context->NonResidentStart, TAG_NTFS);
152 Context->NonResidentStart = NULL;
153 ReleaseAttributeContext(ListContext);
154 return STATUS_FILE_CORRUPT_ERROR;
155 }
156
157 ReleaseAttributeContext(ListContext);
158 Context->NonResidentEnd = (PNTFS_ATTR_RECORD)((PCHAR)Context->NonResidentStart + ListSize);
159 return STATUS_SUCCESS;
160 }
161
162 static
163 PNTFS_ATTR_RECORD
164 InternalGetNextAttribute(PFIND_ATTR_CONTXT Context)
165 {
166 PNTFS_ATTR_RECORD NextAttribute;
167
168 if (Context->CurrAttr == (PVOID)-1)
169 {
170 return NULL;
171 }
172
173 if (Context->CurrAttr >= Context->FirstAttr &&
174 Context->CurrAttr < Context->LastAttr)
175 {
176 if (Context->CurrAttr->Length == 0)
177 {
178 DPRINT1("Broken length!\n");
179 Context->CurrAttr = (PVOID)-1;
180 return NULL;
181 }
182
183 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Context->CurrAttr + Context->CurrAttr->Length);
184
185 if (NextAttribute > Context->LastAttr || NextAttribute < Context->FirstAttr)
186 {
187 DPRINT1("Broken length: 0x%lx!\n", Context->CurrAttr->Length);
188 Context->CurrAttr = (PVOID)-1;
189 return NULL;
190 }
191
192 Context->Offset += ((ULONG_PTR)NextAttribute - (ULONG_PTR)Context->CurrAttr);
193 Context->CurrAttr = NextAttribute;
194
195 if (Context->CurrAttr < Context->LastAttr &&
196 Context->CurrAttr->Type != AttributeEnd)
197 {
198 return Context->CurrAttr;
199 }
200 }
201
202 if (Context->NonResidentStart == NULL)
203 {
204 Context->CurrAttr = (PVOID)-1;
205 return NULL;
206 }
207
208 if (Context->CurrAttr < Context->NonResidentStart ||
209 Context->CurrAttr >= Context->NonResidentEnd)
210 {
211 Context->CurrAttr = Context->NonResidentStart;
212 }
213 else if (Context->CurrAttr->Length != 0)
214 {
215 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Context->CurrAttr + Context->CurrAttr->Length);
216 Context->Offset += ((ULONG_PTR)NextAttribute - (ULONG_PTR)Context->CurrAttr);
217 Context->CurrAttr = NextAttribute;
218 }
219 else
220 {
221 DPRINT1("Broken length!\n");
222 Context->CurrAttr = (PVOID)-1;
223 return NULL;
224 }
225
226 if (Context->CurrAttr < Context->NonResidentEnd &&
227 Context->CurrAttr->Type != AttributeEnd)
228 {
229 return Context->CurrAttr;
230 }
231
232 Context->CurrAttr = (PVOID)-1;
233 return NULL;
234 }
235
236 NTSTATUS
237 FindFirstAttribute(PFIND_ATTR_CONTXT Context,
238 PDEVICE_EXTENSION Vcb,
239 PFILE_RECORD_HEADER FileRecord,
240 BOOLEAN OnlyResident,
241 PNTFS_ATTR_RECORD * Attribute)
242 {
243 NTSTATUS Status;
244
245 DPRINT("FindFistAttribute(%p, %p, %p, %p, %u, %p)\n", Context, Vcb, FileRecord, OnlyResident, Attribute);
246
247 Context->Vcb = Vcb;
248 Context->OnlyResident = OnlyResident;
249 Context->FirstAttr = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
250 Context->CurrAttr = Context->FirstAttr;
251 Context->LastAttr = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->BytesInUse);
252 Context->NonResidentStart = NULL;
253 Context->NonResidentEnd = NULL;
254 Context->Offset = FileRecord->AttributeOffset;
255
256 if (Context->FirstAttr->Type == AttributeEnd)
257 {
258 Context->CurrAttr = (PVOID)-1;
259 return STATUS_END_OF_FILE;
260 }
261 else if (Context->FirstAttr->Type == AttributeAttributeList)
262 {
263 Status = InternalReadNonResidentAttributes(Context);
264 if (!NT_SUCCESS(Status))
265 {
266 return Status;
267 }
268
269 *Attribute = InternalGetNextAttribute(Context);
270 if (*Attribute == NULL)
271 {
272 return STATUS_END_OF_FILE;
273 }
274 }
275 else
276 {
277 *Attribute = Context->CurrAttr;
278 Context->Offset = (UCHAR*)Context->CurrAttr - (UCHAR*)FileRecord;
279 }
280
281 return STATUS_SUCCESS;
282 }
283
284 NTSTATUS
285 FindNextAttribute(PFIND_ATTR_CONTXT Context,
286 PNTFS_ATTR_RECORD * Attribute)
287 {
288 NTSTATUS Status;
289
290 DPRINT("FindNextAttribute(%p, %p)\n", Context, Attribute);
291
292 *Attribute = InternalGetNextAttribute(Context);
293 if (*Attribute == NULL)
294 {
295 return STATUS_END_OF_FILE;
296 }
297
298 if (Context->CurrAttr->Type != AttributeAttributeList)
299 {
300 return STATUS_SUCCESS;
301 }
302
303 Status = InternalReadNonResidentAttributes(Context);
304 if (!NT_SUCCESS(Status))
305 {
306 return Status;
307 }
308
309 *Attribute = InternalGetNextAttribute(Context);
310 if (*Attribute == NULL)
311 {
312 return STATUS_END_OF_FILE;
313 }
314
315 return STATUS_SUCCESS;
316 }
317
318 VOID
319 FindCloseAttribute(PFIND_ATTR_CONTXT Context)
320 {
321 if (Context->NonResidentStart != NULL)
322 {
323 ExFreePoolWithTag(Context->NonResidentStart, TAG_NTFS);
324 Context->NonResidentStart = NULL;
325 }
326 }
327
328 static
329 VOID
330 NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute)
331 {
332 PFILENAME_ATTRIBUTE FileNameAttr;
333
334 DbgPrint(" $FILE_NAME ");
335
336 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
337
338 FileNameAttr = (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
339 DbgPrint(" (%x) '%.*S' ", FileNameAttr->NameType, FileNameAttr->NameLength, FileNameAttr->Name);
340 DbgPrint(" '%x' \n", FileNameAttr->FileAttributes);
341 DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr->AllocatedSize, FileNameAttr->DataSize);
342 }
343
344
345 static
346 VOID
347 NtfsDumpStandardInformationAttribute(PNTFS_ATTR_RECORD Attribute)
348 {
349 PSTANDARD_INFORMATION StandardInfoAttr;
350
351 DbgPrint(" $STANDARD_INFORMATION ");
352
353 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
354
355 StandardInfoAttr = (PSTANDARD_INFORMATION)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
356 DbgPrint(" '%x' ", StandardInfoAttr->FileAttribute);
357 }
358
359
360 static
361 VOID
362 NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute)
363 {
364 PWCHAR VolumeName;
365
366 DbgPrint(" $VOLUME_NAME ");
367
368 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
369
370 VolumeName = (PWCHAR)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
371 DbgPrint(" '%.*S' ", Attribute->Resident.ValueLength / sizeof(WCHAR), VolumeName);
372 }
373
374
375 static
376 VOID
377 NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute)
378 {
379 PVOLINFO_ATTRIBUTE VolInfoAttr;
380
381 DbgPrint(" $VOLUME_INFORMATION ");
382
383 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
384
385 VolInfoAttr = (PVOLINFO_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
386 DbgPrint(" NTFS Version %u.%u Flags 0x%04hx ",
387 VolInfoAttr->MajorVersion,
388 VolInfoAttr->MinorVersion,
389 VolInfoAttr->Flags);
390 }
391
392
393 static
394 VOID
395 NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute)
396 {
397 PINDEX_ROOT_ATTRIBUTE IndexRootAttr;
398
399 IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
400
401 if (IndexRootAttr->AttributeType == AttributeFileName)
402 ASSERT(IndexRootAttr->CollationRule == COLLATION_FILE_NAME);
403
404 DbgPrint(" $INDEX_ROOT (%uB, %u) ", IndexRootAttr->SizeOfEntry, IndexRootAttr->ClustersPerIndexRecord);
405
406 if (IndexRootAttr->Header.Flags == INDEX_ROOT_SMALL)
407 {
408 DbgPrint(" (small) ");
409 }
410 else
411 {
412 ASSERT(IndexRootAttr->Header.Flags == INDEX_ROOT_LARGE);
413 DbgPrint(" (large) ");
414 }
415 }
416
417
418 static
419 VOID
420 NtfsDumpAttribute(PDEVICE_EXTENSION Vcb,
421 PNTFS_ATTR_RECORD Attribute)
422 {
423 UNICODE_STRING Name;
424
425 ULONGLONG lcn = 0;
426 ULONGLONG runcount = 0;
427
428 switch (Attribute->Type)
429 {
430 case AttributeFileName:
431 NtfsDumpFileNameAttribute(Attribute);
432 break;
433
434 case AttributeStandardInformation:
435 NtfsDumpStandardInformationAttribute(Attribute);
436 break;
437
438 case AttributeObjectId:
439 DbgPrint(" $OBJECT_ID ");
440 break;
441
442 case AttributeSecurityDescriptor:
443 DbgPrint(" $SECURITY_DESCRIPTOR ");
444 break;
445
446 case AttributeVolumeName:
447 NtfsDumpVolumeNameAttribute(Attribute);
448 break;
449
450 case AttributeVolumeInformation:
451 NtfsDumpVolumeInformationAttribute(Attribute);
452 break;
453
454 case AttributeData:
455 DbgPrint(" $DATA ");
456 //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
457 break;
458
459 case AttributeIndexRoot:
460 NtfsDumpIndexRootAttribute(Attribute);
461 break;
462
463 case AttributeIndexAllocation:
464 DbgPrint(" $INDEX_ALLOCATION ");
465 break;
466
467 case AttributeBitmap:
468 DbgPrint(" $BITMAP ");
469 break;
470
471 case AttributeReparsePoint:
472 DbgPrint(" $REPARSE_POINT ");
473 break;
474
475 case AttributeEAInformation:
476 DbgPrint(" $EA_INFORMATION ");
477 break;
478
479 case AttributeEA:
480 DbgPrint(" $EA ");
481 break;
482
483 case AttributePropertySet:
484 DbgPrint(" $PROPERTY_SET ");
485 break;
486
487 case AttributeLoggedUtilityStream:
488 DbgPrint(" $LOGGED_UTILITY_STREAM ");
489 break;
490
491 default:
492 DbgPrint(" Attribute %lx ",
493 Attribute->Type);
494 break;
495 }
496
497 if (Attribute->Type != AttributeAttributeList)
498 {
499 if (Attribute->NameLength != 0)
500 {
501 Name.Length = Attribute->NameLength * sizeof(WCHAR);
502 Name.MaximumLength = Name.Length;
503 Name.Buffer = (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset);
504
505 DbgPrint("'%wZ' ", &Name);
506 }
507
508 DbgPrint("(%s)\n",
509 Attribute->IsNonResident ? "non-resident" : "resident");
510
511 if (Attribute->IsNonResident)
512 {
513 FindRun(Attribute,0,&lcn, &runcount);
514
515 DbgPrint(" AllocatedSize %I64u DataSize %I64u InitilizedSize %I64u\n",
516 Attribute->NonResident.AllocatedSize, Attribute->NonResident.DataSize, Attribute->NonResident.InitializedSize);
517 DbgPrint(" logical clusters: %I64u - %I64u\n",
518 lcn, lcn + runcount - 1);
519 }
520 else
521 DbgPrint(" %u bytes of data\n", Attribute->Resident.ValueLength);
522 }
523 }
524
525
526 VOID NtfsDumpDataRunData(PUCHAR DataRun)
527 {
528 UCHAR DataRunOffsetSize;
529 UCHAR DataRunLengthSize;
530 CHAR i;
531
532 DbgPrint("%02x ", *DataRun);
533
534 if (*DataRun == 0)
535 return;
536
537 DataRunOffsetSize = (*DataRun >> 4) & 0xF;
538 DataRunLengthSize = *DataRun & 0xF;
539
540 DataRun++;
541 for (i = 0; i < DataRunLengthSize; i++)
542 {
543 DbgPrint("%02x ", *DataRun);
544 DataRun++;
545 }
546
547 for (i = 0; i < DataRunOffsetSize; i++)
548 {
549 DbgPrint("%02x ", *DataRun);
550 DataRun++;
551 }
552
553 NtfsDumpDataRunData(DataRun);
554 }
555
556
557 VOID
558 NtfsDumpDataRuns(PVOID StartOfRun,
559 ULONGLONG CurrentLCN)
560 {
561 PUCHAR DataRun = StartOfRun;
562 LONGLONG DataRunOffset;
563 ULONGLONG DataRunLength;
564
565 if (CurrentLCN == 0)
566 {
567 DPRINT1("Dumping data runs.\n\tData:\n\t\t");
568 NtfsDumpDataRunData(StartOfRun);
569 DbgPrint("\n\tRuns:\n\t\tOff\t\tLCN\t\tLength\n");
570 }
571
572 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
573
574 if (DataRunOffset != -1)
575 CurrentLCN += DataRunOffset;
576
577 DbgPrint("\t\t%I64d\t", DataRunOffset);
578 if (DataRunOffset < 99999)
579 DbgPrint("\t");
580 DbgPrint("%I64u\t", CurrentLCN);
581 if (CurrentLCN < 99999)
582 DbgPrint("\t");
583 DbgPrint("%I64u\n", DataRunLength);
584
585 if (*DataRun == 0)
586 DbgPrint("\t\t00\n");
587 else
588 NtfsDumpDataRuns(DataRun, CurrentLCN);
589 }
590
591
592 VOID
593 NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb,
594 PFILE_RECORD_HEADER FileRecord)
595 {
596 NTSTATUS Status;
597 FIND_ATTR_CONTXT Context;
598 PNTFS_ATTR_RECORD Attribute;
599
600 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
601 while (NT_SUCCESS(Status))
602 {
603 NtfsDumpAttribute(Vcb, Attribute);
604
605 Status = FindNextAttribute(&Context, &Attribute);
606 }
607
608 FindCloseAttribute(&Context);
609 }
610
611 PFILENAME_ATTRIBUTE
612 GetFileNameFromRecord(PDEVICE_EXTENSION Vcb,
613 PFILE_RECORD_HEADER FileRecord,
614 UCHAR NameType)
615 {
616 FIND_ATTR_CONTXT Context;
617 PNTFS_ATTR_RECORD Attribute;
618 PFILENAME_ATTRIBUTE Name;
619 NTSTATUS Status;
620
621 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
622 while (NT_SUCCESS(Status))
623 {
624 if (Attribute->Type == AttributeFileName)
625 {
626 Name = (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
627 if (Name->NameType == NameType ||
628 (Name->NameType == NTFS_FILE_NAME_WIN32_AND_DOS && NameType == NTFS_FILE_NAME_WIN32) ||
629 (Name->NameType == NTFS_FILE_NAME_WIN32_AND_DOS && NameType == NTFS_FILE_NAME_DOS))
630 {
631 FindCloseAttribute(&Context);
632 return Name;
633 }
634 }
635
636 Status = FindNextAttribute(&Context, &Attribute);
637 }
638
639 FindCloseAttribute(&Context);
640 return NULL;
641 }
642
643 /**
644 * GetPackedByteCount
645 * Returns the minimum number of bytes needed to represent the value of a
646 * 64-bit number. Used to encode data runs.
647 */
648 UCHAR
649 GetPackedByteCount(LONGLONG NumberToPack,
650 BOOLEAN IsSigned)
651 {
652 int bytes = 0;
653 if (!IsSigned)
654 {
655 if (NumberToPack >= 0x0100000000000000)
656 return 8;
657 if (NumberToPack >= 0x0001000000000000)
658 return 7;
659 if (NumberToPack >= 0x0000010000000000)
660 return 6;
661 if (NumberToPack >= 0x0000000100000000)
662 return 5;
663 if (NumberToPack >= 0x0000000001000000)
664 return 4;
665 if (NumberToPack >= 0x0000000000010000)
666 return 3;
667 if (NumberToPack >= 0x0000000000000100)
668 return 2;
669 return 1;
670 }
671
672 if (NumberToPack > 0)
673 {
674 // we have to make sure the number that gets encoded won't be interpreted as negative
675 if (NumberToPack >= 0x0080000000000000)
676 return 8;
677 if (NumberToPack >= 0x0000800000000000)
678 return 7;
679 if (NumberToPack >= 0x0000008000000000)
680 return 6;
681 if (NumberToPack >= 0x0000000080000000)
682 return 5;
683 if (NumberToPack >= 0x0000000000800000)
684 return 4;
685 if (NumberToPack >= 0x0000000000008000)
686 return 3;
687 if (NumberToPack >= 0x0000000000000080)
688 return 2;
689 return 1;
690 }
691 else
692 {
693 // negative number
694 if (NumberToPack <= 0xff80000000000000)
695 return 8;
696 if (NumberToPack <= 0xffff800000000000)
697 return 7;
698 if (NumberToPack <= 0xffffff8000000000)
699 return 6;
700 if (NumberToPack <= 0xffffffff80000000)
701 return 5;
702 if (NumberToPack <= 0xffffffffff800000)
703 return 4;
704 if (NumberToPack <= 0xffffffffffff8000)
705 return 3;
706 if (NumberToPack <= 0xffffffffffffff80)
707 return 2;
708 return 1;
709 }
710 return bytes;
711 }
712
713 NTSTATUS
714 GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_RECORD Attribute, PULONGLONG LastCluster)
715 {
716 LONGLONG DataRunOffset;
717 ULONGLONG DataRunLength;
718 LONGLONG DataRunStartLCN;
719
720 ULONGLONG LastLCN = 0;
721 PUCHAR DataRun = (PUCHAR)Attribute + Attribute->NonResident.MappingPairsOffset;
722
723 if (!Attribute->IsNonResident)
724 return STATUS_INVALID_PARAMETER;
725
726 while (1)
727 {
728 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
729
730 if (DataRunOffset != -1)
731 {
732 // Normal data run.
733 DataRunStartLCN = LastLCN + DataRunOffset;
734 LastLCN = DataRunStartLCN;
735 *LastCluster = LastLCN + DataRunLength - 1;
736 }
737
738 if (*DataRun == 0)
739 break;
740 }
741
742 return STATUS_SUCCESS;
743 }
744
745 PSTANDARD_INFORMATION
746 GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb,
747 PFILE_RECORD_HEADER FileRecord)
748 {
749 NTSTATUS Status;
750 FIND_ATTR_CONTXT Context;
751 PNTFS_ATTR_RECORD Attribute;
752 PSTANDARD_INFORMATION StdInfo;
753
754 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
755 while (NT_SUCCESS(Status))
756 {
757 if (Attribute->Type == AttributeStandardInformation)
758 {
759 StdInfo = (PSTANDARD_INFORMATION)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
760 FindCloseAttribute(&Context);
761 return StdInfo;
762 }
763
764 Status = FindNextAttribute(&Context, &Attribute);
765 }
766
767 FindCloseAttribute(&Context);
768 return NULL;
769 }
770
771 PFILENAME_ATTRIBUTE
772 GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb,
773 PFILE_RECORD_HEADER FileRecord)
774 {
775 PFILENAME_ATTRIBUTE FileName;
776
777 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_POSIX);
778 if (FileName == NULL)
779 {
780 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_WIN32);
781 if (FileName == NULL)
782 {
783 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_DOS);
784 }
785 }
786
787 return FileName;
788 }
789
790 /* EOF */