3 * Copyright (C) 2002,2003 ReactOS Team
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.
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.
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.
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
25 * Hervé Poussineau (hpoussin@reactos.org)
26 * Pierre Schweitzer (pierre@reactos.org)
29 /* INCLUDES *****************************************************************/
36 /* FUNCTIONS ****************************************************************/
39 DecodeRun(PUCHAR DataRun
,
40 LONGLONG
*DataRunOffset
,
41 ULONGLONG
*DataRunLength
)
43 UCHAR DataRunOffsetSize
;
44 UCHAR DataRunLengthSize
;
47 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
48 DataRunLengthSize
= *DataRun
& 0xF;
52 for (i
= 0; i
< DataRunLengthSize
; i
++)
54 *DataRunLength
+= ((ULONG64
)*DataRun
) << (i
* 8);
58 /* NTFS 3+ sparse files */
59 if (DataRunOffsetSize
== 0)
65 for (i
= 0; i
< DataRunOffsetSize
- 1; i
++)
67 *DataRunOffset
+= ((ULONG64
)*DataRun
) << (i
* 8);
70 /* The last byte contains sign so we must process it different way. */
71 *DataRunOffset
= ((LONG64
)(CHAR
)(*(DataRun
++)) << (i
* 8)) + *DataRunOffset
;
74 DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize
);
75 DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize
);
76 DPRINT("DataRunOffset: %x\n", *DataRunOffset
);
77 DPRINT("DataRunLength: %x\n", *DataRunLength
);
83 FindRun(PNTFS_ATTR_RECORD NresAttr
,
88 if (vcn
< NresAttr
->NonResident
.LowestVCN
|| vcn
> NresAttr
->NonResident
.HighestVCN
)
91 DecodeRun((PUCHAR
)((ULONG_PTR
)NresAttr
+ NresAttr
->NonResident
.MappingPairsOffset
), (PLONGLONG
)lcn
, count
);
98 InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context
)
101 PNTFS_ATTR_RECORD Attribute
;
102 PNTFS_ATTR_CONTEXT ListContext
;
104 DPRINT("InternalReadNonResidentAttributes(%p)\n", Context
);
106 Attribute
= Context
->CurrAttr
;
107 ASSERT(Attribute
->Type
== AttributeAttributeList
);
109 if (Context
->OnlyResident
)
111 Context
->NonResidentStart
= NULL
;
112 Context
->NonResidentEnd
= NULL
;
113 return STATUS_SUCCESS
;
116 if (Context
->NonResidentStart
!= NULL
)
118 return STATUS_FILE_CORRUPT_ERROR
;
121 ListContext
= PrepareAttributeContext(Attribute
);
122 ListSize
= AttributeDataLength(&ListContext
->Record
);
123 if (ListSize
> 0xFFFFFFFF)
125 ReleaseAttributeContext(ListContext
);
126 return STATUS_BUFFER_OVERFLOW
;
129 Context
->NonResidentStart
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)ListSize
, TAG_NTFS
);
130 if (Context
->NonResidentStart
== NULL
)
132 ReleaseAttributeContext(ListContext
);
133 return STATUS_INSUFFICIENT_RESOURCES
;
136 if (ReadAttribute(Context
->Vcb
, ListContext
, 0, (PCHAR
)Context
->NonResidentStart
, (ULONG
)ListSize
) != ListSize
)
138 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
139 Context
->NonResidentStart
= NULL
;
140 ReleaseAttributeContext(ListContext
);
141 return STATUS_FILE_CORRUPT_ERROR
;
144 ReleaseAttributeContext(ListContext
);
145 Context
->NonResidentEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)Context
->NonResidentStart
+ ListSize
);
146 return STATUS_SUCCESS
;
151 InternalGetNextAttribute(PFIND_ATTR_CONTXT Context
)
153 PNTFS_ATTR_RECORD NextAttribute
;
155 if (Context
->CurrAttr
== (PVOID
)-1)
160 if (Context
->CurrAttr
>= Context
->FirstAttr
&&
161 Context
->CurrAttr
< Context
->LastAttr
)
163 if (Context
->CurrAttr
->Length
== 0)
165 DPRINT1("Broken length!\n");
166 Context
->CurrAttr
= (PVOID
)-1;
170 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Context
->CurrAttr
+ Context
->CurrAttr
->Length
);
171 Context
->Offset
+= ((ULONG_PTR
)NextAttribute
- (ULONG_PTR
)Context
->CurrAttr
);
172 Context
->CurrAttr
= NextAttribute
;
174 if (Context
->CurrAttr
< Context
->LastAttr
&&
175 Context
->CurrAttr
->Type
!= AttributeEnd
)
177 return Context
->CurrAttr
;
181 if (Context
->NonResidentStart
== NULL
)
183 Context
->CurrAttr
= (PVOID
)-1;
187 if (Context
->CurrAttr
< Context
->NonResidentStart
||
188 Context
->CurrAttr
>= Context
->NonResidentEnd
)
190 Context
->CurrAttr
= Context
->NonResidentStart
;
192 else if (Context
->CurrAttr
->Length
!= 0)
194 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Context
->CurrAttr
+ Context
->CurrAttr
->Length
);
195 Context
->Offset
+= ((ULONG_PTR
)NextAttribute
- (ULONG_PTR
)Context
->CurrAttr
);
196 Context
->CurrAttr
= NextAttribute
;
200 DPRINT1("Broken length!\n");
201 Context
->CurrAttr
= (PVOID
)-1;
205 if (Context
->CurrAttr
< Context
->NonResidentEnd
&&
206 Context
->CurrAttr
->Type
!= AttributeEnd
)
208 return Context
->CurrAttr
;
211 Context
->CurrAttr
= (PVOID
)-1;
216 FindFirstAttribute(PFIND_ATTR_CONTXT Context
,
217 PDEVICE_EXTENSION Vcb
,
218 PFILE_RECORD_HEADER FileRecord
,
219 BOOLEAN OnlyResident
,
220 PNTFS_ATTR_RECORD
* Attribute
)
224 DPRINT("FindFistAttribute(%p, %p, %p, %p, %u, %p)\n", Context
, Vcb
, FileRecord
, OnlyResident
, Attribute
);
227 Context
->OnlyResident
= OnlyResident
;
228 Context
->FirstAttr
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ FileRecord
->AttributeOffset
);
229 Context
->CurrAttr
= Context
->FirstAttr
;
230 Context
->LastAttr
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)FileRecord
+ FileRecord
->BytesInUse
);
231 Context
->NonResidentStart
= NULL
;
232 Context
->NonResidentEnd
= NULL
;
233 Context
->Offset
= FileRecord
->AttributeOffset
;
235 if (Context
->FirstAttr
->Type
== AttributeEnd
)
237 Context
->CurrAttr
= (PVOID
)-1;
238 return STATUS_END_OF_FILE
;
240 else if (Context
->FirstAttr
->Type
== AttributeAttributeList
)
242 Status
= InternalReadNonResidentAttributes(Context
);
243 if (!NT_SUCCESS(Status
))
248 *Attribute
= InternalGetNextAttribute(Context
);
249 if (*Attribute
== NULL
)
251 return STATUS_END_OF_FILE
;
256 *Attribute
= Context
->CurrAttr
;
257 Context
->Offset
= (UCHAR
*)Context
->CurrAttr
- (UCHAR
*)FileRecord
;
260 return STATUS_SUCCESS
;
264 FindNextAttribute(PFIND_ATTR_CONTXT Context
,
265 PNTFS_ATTR_RECORD
* Attribute
)
269 DPRINT("FindNextAttribute(%p, %p)\n", Context
, Attribute
);
271 *Attribute
= InternalGetNextAttribute(Context
);
272 if (*Attribute
== NULL
)
274 return STATUS_END_OF_FILE
;
277 if (Context
->CurrAttr
->Type
!= AttributeAttributeList
)
279 return STATUS_SUCCESS
;
282 Status
= InternalReadNonResidentAttributes(Context
);
283 if (!NT_SUCCESS(Status
))
288 *Attribute
= InternalGetNextAttribute(Context
);
289 if (*Attribute
== NULL
)
291 return STATUS_END_OF_FILE
;
294 return STATUS_SUCCESS
;
298 FindCloseAttribute(PFIND_ATTR_CONTXT Context
)
300 if (Context
->NonResidentStart
!= NULL
)
302 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
303 Context
->NonResidentStart
= NULL
;
309 NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute
)
311 PFILENAME_ATTRIBUTE FileNameAttr
;
313 DbgPrint(" $FILE_NAME ");
315 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
317 FileNameAttr
= (PFILENAME_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
318 DbgPrint(" (%x) '%.*S' ", FileNameAttr
->NameType
, FileNameAttr
->NameLength
, FileNameAttr
->Name
);
319 DbgPrint(" '%x' \n", FileNameAttr
->FileAttributes
);
320 DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr
->AllocatedSize
, FileNameAttr
->DataSize
);
326 NtfsDumpStandardInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
328 PSTANDARD_INFORMATION StandardInfoAttr
;
330 DbgPrint(" $STANDARD_INFORMATION ");
332 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
334 StandardInfoAttr
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
335 DbgPrint(" '%x' ", StandardInfoAttr
->FileAttribute
);
341 NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute
)
345 DbgPrint(" $VOLUME_NAME ");
347 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
349 VolumeName
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
350 DbgPrint(" '%.*S' ", Attribute
->Resident
.ValueLength
/ sizeof(WCHAR
), VolumeName
);
356 NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
358 PVOLINFO_ATTRIBUTE VolInfoAttr
;
360 DbgPrint(" $VOLUME_INFORMATION ");
362 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
364 VolInfoAttr
= (PVOLINFO_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
365 DbgPrint(" NTFS Version %u.%u Flags 0x%04hx ",
366 VolInfoAttr
->MajorVersion
,
367 VolInfoAttr
->MinorVersion
,
374 NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute
)
376 PINDEX_ROOT_ATTRIBUTE IndexRootAttr
;
378 IndexRootAttr
= (PINDEX_ROOT_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
380 if (IndexRootAttr
->AttributeType
== AttributeFileName
)
381 ASSERT(IndexRootAttr
->CollationRule
== COLLATION_FILE_NAME
);
383 DbgPrint(" $INDEX_ROOT (%uB, %u) ", IndexRootAttr
->SizeOfEntry
, IndexRootAttr
->ClustersPerIndexRecord
);
385 if (IndexRootAttr
->Header
.Flags
== INDEX_ROOT_SMALL
)
387 DbgPrint(" (small) ");
391 ASSERT(IndexRootAttr
->Header
.Flags
== INDEX_ROOT_LARGE
);
392 DbgPrint(" (large) ");
399 NtfsDumpAttribute(PDEVICE_EXTENSION Vcb
,
400 PNTFS_ATTR_RECORD Attribute
)
405 ULONGLONG runcount
= 0;
407 switch (Attribute
->Type
)
409 case AttributeFileName
:
410 NtfsDumpFileNameAttribute(Attribute
);
413 case AttributeStandardInformation
:
414 NtfsDumpStandardInformationAttribute(Attribute
);
417 case AttributeObjectId
:
418 DbgPrint(" $OBJECT_ID ");
421 case AttributeSecurityDescriptor
:
422 DbgPrint(" $SECURITY_DESCRIPTOR ");
425 case AttributeVolumeName
:
426 NtfsDumpVolumeNameAttribute(Attribute
);
429 case AttributeVolumeInformation
:
430 NtfsDumpVolumeInformationAttribute(Attribute
);
435 //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
438 case AttributeIndexRoot
:
439 NtfsDumpIndexRootAttribute(Attribute
);
442 case AttributeIndexAllocation
:
443 DbgPrint(" $INDEX_ALLOCATION ");
446 case AttributeBitmap
:
447 DbgPrint(" $BITMAP ");
450 case AttributeReparsePoint
:
451 DbgPrint(" $REPARSE_POINT ");
454 case AttributeEAInformation
:
455 DbgPrint(" $EA_INFORMATION ");
462 case AttributePropertySet
:
463 DbgPrint(" $PROPERTY_SET ");
466 case AttributeLoggedUtilityStream
:
467 DbgPrint(" $LOGGED_UTILITY_STREAM ");
471 DbgPrint(" Attribute %lx ",
476 if (Attribute
->Type
!= AttributeAttributeList
)
478 if (Attribute
->NameLength
!= 0)
480 Name
.Length
= Attribute
->NameLength
* sizeof(WCHAR
);
481 Name
.MaximumLength
= Name
.Length
;
482 Name
.Buffer
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->NameOffset
);
484 DbgPrint("'%wZ' ", &Name
);
488 Attribute
->IsNonResident
? "non-resident" : "resident");
490 if (Attribute
->IsNonResident
)
492 FindRun(Attribute
,0,&lcn
, &runcount
);
494 DbgPrint(" AllocatedSize %I64u DataSize %I64u InitilizedSize %I64u\n",
495 Attribute
->NonResident
.AllocatedSize
, Attribute
->NonResident
.DataSize
, Attribute
->NonResident
.InitializedSize
);
496 DbgPrint(" logical clusters: %I64u - %I64u\n",
497 lcn
, lcn
+ runcount
- 1);
504 NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb
,
505 PFILE_RECORD_HEADER FileRecord
)
508 FIND_ATTR_CONTXT Context
;
509 PNTFS_ATTR_RECORD Attribute
;
511 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
512 while (NT_SUCCESS(Status
))
514 NtfsDumpAttribute(Vcb
, Attribute
);
516 Status
= FindNextAttribute(&Context
, &Attribute
);
519 FindCloseAttribute(&Context
);
523 GetFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
524 PFILE_RECORD_HEADER FileRecord
,
527 FIND_ATTR_CONTXT Context
;
528 PNTFS_ATTR_RECORD Attribute
;
529 PFILENAME_ATTRIBUTE Name
;
532 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
533 while (NT_SUCCESS(Status
))
535 if (Attribute
->Type
== AttributeFileName
)
537 Name
= (PFILENAME_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
538 if (Name
->NameType
== NameType
||
539 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_WIN32
) ||
540 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_DOS
))
542 FindCloseAttribute(&Context
);
547 Status
= FindNextAttribute(&Context
, &Attribute
);
550 FindCloseAttribute(&Context
);
554 PSTANDARD_INFORMATION
555 GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb
,
556 PFILE_RECORD_HEADER FileRecord
)
559 FIND_ATTR_CONTXT Context
;
560 PNTFS_ATTR_RECORD Attribute
;
561 PSTANDARD_INFORMATION StdInfo
;
563 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
564 while (NT_SUCCESS(Status
))
566 if (Attribute
->Type
== AttributeStandardInformation
)
568 StdInfo
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
569 FindCloseAttribute(&Context
);
573 Status
= FindNextAttribute(&Context
, &Attribute
);
576 FindCloseAttribute(&Context
);
581 GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
582 PFILE_RECORD_HEADER FileRecord
)
584 PFILENAME_ATTRIBUTE FileName
;
586 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_POSIX
);
587 if (FileName
== NULL
)
589 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_WIN32
);
590 if (FileName
== NULL
)
592 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_DOS
);