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 AddRun(PNTFS_ATTR_CONTEXT AttrContext
,
40 ULONGLONG NextAssignedCluster
,
45 if (!AttrContext
->Record
.IsNonResident
)
46 return STATUS_INVALID_PARAMETER
;
48 return STATUS_NOT_IMPLEMENTED
;
52 DecodeRun(PUCHAR DataRun
,
53 LONGLONG
*DataRunOffset
,
54 ULONGLONG
*DataRunLength
)
56 UCHAR DataRunOffsetSize
;
57 UCHAR DataRunLengthSize
;
60 DataRunOffsetSize
= (*DataRun
>> 4) & 0xF;
61 DataRunLengthSize
= *DataRun
& 0xF;
65 for (i
= 0; i
< DataRunLengthSize
; i
++)
67 *DataRunLength
+= ((ULONG64
)*DataRun
) << (i
* 8);
71 /* NTFS 3+ sparse files */
72 if (DataRunOffsetSize
== 0)
78 for (i
= 0; i
< DataRunOffsetSize
- 1; i
++)
80 *DataRunOffset
+= ((ULONG64
)*DataRun
) << (i
* 8);
83 /* The last byte contains sign so we must process it different way. */
84 *DataRunOffset
= ((LONG64
)(CHAR
)(*(DataRun
++)) << (i
* 8)) + *DataRunOffset
;
87 DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize
);
88 DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize
);
89 DPRINT("DataRunOffset: %x\n", *DataRunOffset
);
90 DPRINT("DataRunLength: %x\n", *DataRunLength
);
96 FindRun(PNTFS_ATTR_RECORD NresAttr
,
101 if (vcn
< NresAttr
->NonResident
.LowestVCN
|| vcn
> NresAttr
->NonResident
.HighestVCN
)
104 DecodeRun((PUCHAR
)((ULONG_PTR
)NresAttr
+ NresAttr
->NonResident
.MappingPairsOffset
), (PLONGLONG
)lcn
, count
);
111 InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context
)
114 PNTFS_ATTR_RECORD Attribute
;
115 PNTFS_ATTR_CONTEXT ListContext
;
117 DPRINT("InternalReadNonResidentAttributes(%p)\n", Context
);
119 Attribute
= Context
->CurrAttr
;
120 ASSERT(Attribute
->Type
== AttributeAttributeList
);
122 if (Context
->OnlyResident
)
124 Context
->NonResidentStart
= NULL
;
125 Context
->NonResidentEnd
= NULL
;
126 return STATUS_SUCCESS
;
129 if (Context
->NonResidentStart
!= NULL
)
131 return STATUS_FILE_CORRUPT_ERROR
;
134 ListContext
= PrepareAttributeContext(Attribute
);
135 ListSize
= AttributeDataLength(&ListContext
->Record
);
136 if (ListSize
> 0xFFFFFFFF)
138 ReleaseAttributeContext(ListContext
);
139 return STATUS_BUFFER_OVERFLOW
;
142 Context
->NonResidentStart
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)ListSize
, TAG_NTFS
);
143 if (Context
->NonResidentStart
== NULL
)
145 ReleaseAttributeContext(ListContext
);
146 return STATUS_INSUFFICIENT_RESOURCES
;
149 if (ReadAttribute(Context
->Vcb
, ListContext
, 0, (PCHAR
)Context
->NonResidentStart
, (ULONG
)ListSize
) != ListSize
)
151 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
152 Context
->NonResidentStart
= NULL
;
153 ReleaseAttributeContext(ListContext
);
154 return STATUS_FILE_CORRUPT_ERROR
;
157 ReleaseAttributeContext(ListContext
);
158 Context
->NonResidentEnd
= (PNTFS_ATTR_RECORD
)((PCHAR
)Context
->NonResidentStart
+ ListSize
);
159 return STATUS_SUCCESS
;
164 InternalGetNextAttribute(PFIND_ATTR_CONTXT Context
)
166 PNTFS_ATTR_RECORD NextAttribute
;
168 if (Context
->CurrAttr
== (PVOID
)-1)
173 if (Context
->CurrAttr
>= Context
->FirstAttr
&&
174 Context
->CurrAttr
< Context
->LastAttr
)
176 if (Context
->CurrAttr
->Length
== 0)
178 DPRINT1("Broken length!\n");
179 Context
->CurrAttr
= (PVOID
)-1;
183 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)Context
->CurrAttr
+ Context
->CurrAttr
->Length
);
185 if (NextAttribute
> Context
->LastAttr
|| NextAttribute
< Context
->FirstAttr
)
187 DPRINT1("Broken length: 0x%lx!\n", Context
->CurrAttr
->Length
);
188 Context
->CurrAttr
= (PVOID
)-1;
192 Context
->Offset
+= ((ULONG_PTR
)NextAttribute
- (ULONG_PTR
)Context
->CurrAttr
);
193 Context
->CurrAttr
= NextAttribute
;
195 if (Context
->CurrAttr
< Context
->LastAttr
&&
196 Context
->CurrAttr
->Type
!= AttributeEnd
)
198 return Context
->CurrAttr
;
202 if (Context
->NonResidentStart
== NULL
)
204 Context
->CurrAttr
= (PVOID
)-1;
208 if (Context
->CurrAttr
< Context
->NonResidentStart
||
209 Context
->CurrAttr
>= Context
->NonResidentEnd
)
211 Context
->CurrAttr
= Context
->NonResidentStart
;
213 else if (Context
->CurrAttr
->Length
!= 0)
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
;
221 DPRINT1("Broken length!\n");
222 Context
->CurrAttr
= (PVOID
)-1;
226 if (Context
->CurrAttr
< Context
->NonResidentEnd
&&
227 Context
->CurrAttr
->Type
!= AttributeEnd
)
229 return Context
->CurrAttr
;
232 Context
->CurrAttr
= (PVOID
)-1;
237 FindFirstAttribute(PFIND_ATTR_CONTXT Context
,
238 PDEVICE_EXTENSION Vcb
,
239 PFILE_RECORD_HEADER FileRecord
,
240 BOOLEAN OnlyResident
,
241 PNTFS_ATTR_RECORD
* Attribute
)
245 DPRINT("FindFistAttribute(%p, %p, %p, %p, %u, %p)\n", Context
, Vcb
, FileRecord
, OnlyResident
, Attribute
);
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
;
256 if (Context
->FirstAttr
->Type
== AttributeEnd
)
258 Context
->CurrAttr
= (PVOID
)-1;
259 return STATUS_END_OF_FILE
;
261 else if (Context
->FirstAttr
->Type
== AttributeAttributeList
)
263 Status
= InternalReadNonResidentAttributes(Context
);
264 if (!NT_SUCCESS(Status
))
269 *Attribute
= InternalGetNextAttribute(Context
);
270 if (*Attribute
== NULL
)
272 return STATUS_END_OF_FILE
;
277 *Attribute
= Context
->CurrAttr
;
278 Context
->Offset
= (UCHAR
*)Context
->CurrAttr
- (UCHAR
*)FileRecord
;
281 return STATUS_SUCCESS
;
285 FindNextAttribute(PFIND_ATTR_CONTXT Context
,
286 PNTFS_ATTR_RECORD
* Attribute
)
290 DPRINT("FindNextAttribute(%p, %p)\n", Context
, Attribute
);
292 *Attribute
= InternalGetNextAttribute(Context
);
293 if (*Attribute
== NULL
)
295 return STATUS_END_OF_FILE
;
298 if (Context
->CurrAttr
->Type
!= AttributeAttributeList
)
300 return STATUS_SUCCESS
;
303 Status
= InternalReadNonResidentAttributes(Context
);
304 if (!NT_SUCCESS(Status
))
309 *Attribute
= InternalGetNextAttribute(Context
);
310 if (*Attribute
== NULL
)
312 return STATUS_END_OF_FILE
;
315 return STATUS_SUCCESS
;
319 FindCloseAttribute(PFIND_ATTR_CONTXT Context
)
321 if (Context
->NonResidentStart
!= NULL
)
323 ExFreePoolWithTag(Context
->NonResidentStart
, TAG_NTFS
);
324 Context
->NonResidentStart
= NULL
;
330 NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute
)
332 PFILENAME_ATTRIBUTE FileNameAttr
;
334 DbgPrint(" $FILE_NAME ");
336 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
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
);
347 NtfsDumpStandardInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
349 PSTANDARD_INFORMATION StandardInfoAttr
;
351 DbgPrint(" $STANDARD_INFORMATION ");
353 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
355 StandardInfoAttr
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
356 DbgPrint(" '%x' ", StandardInfoAttr
->FileAttribute
);
362 NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute
)
366 DbgPrint(" $VOLUME_NAME ");
368 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
370 VolumeName
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
371 DbgPrint(" '%.*S' ", Attribute
->Resident
.ValueLength
/ sizeof(WCHAR
), VolumeName
);
377 NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute
)
379 PVOLINFO_ATTRIBUTE VolInfoAttr
;
381 DbgPrint(" $VOLUME_INFORMATION ");
383 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
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
,
395 NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute
)
397 PINDEX_ROOT_ATTRIBUTE IndexRootAttr
;
399 IndexRootAttr
= (PINDEX_ROOT_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
401 if (IndexRootAttr
->AttributeType
== AttributeFileName
)
402 ASSERT(IndexRootAttr
->CollationRule
== COLLATION_FILE_NAME
);
404 DbgPrint(" $INDEX_ROOT (%uB, %u) ", IndexRootAttr
->SizeOfEntry
, IndexRootAttr
->ClustersPerIndexRecord
);
406 if (IndexRootAttr
->Header
.Flags
== INDEX_ROOT_SMALL
)
408 DbgPrint(" (small) ");
412 ASSERT(IndexRootAttr
->Header
.Flags
== INDEX_ROOT_LARGE
);
413 DbgPrint(" (large) ");
420 NtfsDumpAttribute(PDEVICE_EXTENSION Vcb
,
421 PNTFS_ATTR_RECORD Attribute
)
426 ULONGLONG runcount
= 0;
428 switch (Attribute
->Type
)
430 case AttributeFileName
:
431 NtfsDumpFileNameAttribute(Attribute
);
434 case AttributeStandardInformation
:
435 NtfsDumpStandardInformationAttribute(Attribute
);
438 case AttributeObjectId
:
439 DbgPrint(" $OBJECT_ID ");
442 case AttributeSecurityDescriptor
:
443 DbgPrint(" $SECURITY_DESCRIPTOR ");
446 case AttributeVolumeName
:
447 NtfsDumpVolumeNameAttribute(Attribute
);
450 case AttributeVolumeInformation
:
451 NtfsDumpVolumeInformationAttribute(Attribute
);
456 //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
459 case AttributeIndexRoot
:
460 NtfsDumpIndexRootAttribute(Attribute
);
463 case AttributeIndexAllocation
:
464 DbgPrint(" $INDEX_ALLOCATION ");
467 case AttributeBitmap
:
468 DbgPrint(" $BITMAP ");
471 case AttributeReparsePoint
:
472 DbgPrint(" $REPARSE_POINT ");
475 case AttributeEAInformation
:
476 DbgPrint(" $EA_INFORMATION ");
483 case AttributePropertySet
:
484 DbgPrint(" $PROPERTY_SET ");
487 case AttributeLoggedUtilityStream
:
488 DbgPrint(" $LOGGED_UTILITY_STREAM ");
492 DbgPrint(" Attribute %lx ",
497 if (Attribute
->Type
!= AttributeAttributeList
)
499 if (Attribute
->NameLength
!= 0)
501 Name
.Length
= Attribute
->NameLength
* sizeof(WCHAR
);
502 Name
.MaximumLength
= Name
.Length
;
503 Name
.Buffer
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->NameOffset
);
505 DbgPrint("'%wZ' ", &Name
);
509 Attribute
->IsNonResident
? "non-resident" : "resident");
511 if (Attribute
->IsNonResident
)
513 FindRun(Attribute
,0,&lcn
, &runcount
);
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);
521 DbgPrint(" %u bytes of data\n", Attribute
->Resident
.ValueLength
);
527 NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb
,
528 PFILE_RECORD_HEADER FileRecord
)
531 FIND_ATTR_CONTXT Context
;
532 PNTFS_ATTR_RECORD Attribute
;
534 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
535 while (NT_SUCCESS(Status
))
537 NtfsDumpAttribute(Vcb
, Attribute
);
539 Status
= FindNextAttribute(&Context
, &Attribute
);
542 FindCloseAttribute(&Context
);
546 GetFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
547 PFILE_RECORD_HEADER FileRecord
,
550 FIND_ATTR_CONTXT Context
;
551 PNTFS_ATTR_RECORD Attribute
;
552 PFILENAME_ATTRIBUTE Name
;
555 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
556 while (NT_SUCCESS(Status
))
558 if (Attribute
->Type
== AttributeFileName
)
560 Name
= (PFILENAME_ATTRIBUTE
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
561 if (Name
->NameType
== NameType
||
562 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_WIN32
) ||
563 (Name
->NameType
== NTFS_FILE_NAME_WIN32_AND_DOS
&& NameType
== NTFS_FILE_NAME_DOS
))
565 FindCloseAttribute(&Context
);
570 Status
= FindNextAttribute(&Context
, &Attribute
);
573 FindCloseAttribute(&Context
);
578 GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb
, PNTFS_ATTR_RECORD Attribute
, PULONGLONG LastCluster
)
580 LONGLONG DataRunOffset
;
581 ULONGLONG DataRunLength
;
582 LONGLONG DataRunStartLCN
;
584 ULONGLONG LastLCN
= 0;
585 PUCHAR DataRun
= (PUCHAR
)Attribute
+ Attribute
->NonResident
.MappingPairsOffset
;
587 if (!Attribute
->IsNonResident
)
588 return STATUS_INVALID_PARAMETER
;
592 DataRun
= DecodeRun(DataRun
, &DataRunOffset
, &DataRunLength
);
594 if (DataRunOffset
!= -1)
597 DataRunStartLCN
= LastLCN
+ DataRunOffset
;
598 LastLCN
= DataRunStartLCN
;
599 *LastCluster
= LastLCN
+ DataRunLength
- 1;
606 return STATUS_SUCCESS
;
609 PSTANDARD_INFORMATION
610 GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb
,
611 PFILE_RECORD_HEADER FileRecord
)
614 FIND_ATTR_CONTXT Context
;
615 PNTFS_ATTR_RECORD Attribute
;
616 PSTANDARD_INFORMATION StdInfo
;
618 Status
= FindFirstAttribute(&Context
, Vcb
, FileRecord
, FALSE
, &Attribute
);
619 while (NT_SUCCESS(Status
))
621 if (Attribute
->Type
== AttributeStandardInformation
)
623 StdInfo
= (PSTANDARD_INFORMATION
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
624 FindCloseAttribute(&Context
);
628 Status
= FindNextAttribute(&Context
, &Attribute
);
631 FindCloseAttribute(&Context
);
636 GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb
,
637 PFILE_RECORD_HEADER FileRecord
)
639 PFILENAME_ATTRIBUTE FileName
;
641 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_POSIX
);
642 if (FileName
== NULL
)
644 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_WIN32
);
645 if (FileName
== NULL
)
647 FileName
= GetFileNameFromRecord(Vcb
, FileRecord
, NTFS_FILE_NAME_DOS
);