[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 PUCHAR
39 DecodeRun(PUCHAR DataRun,
40 LONGLONG *DataRunOffset,
41 ULONGLONG *DataRunLength)
42 {
43 UCHAR DataRunOffsetSize;
44 UCHAR DataRunLengthSize;
45 CHAR i;
46
47 DataRunOffsetSize = (*DataRun >> 4) & 0xF;
48 DataRunLengthSize = *DataRun & 0xF;
49 *DataRunOffset = 0;
50 *DataRunLength = 0;
51 DataRun++;
52 for (i = 0; i < DataRunLengthSize; i++)
53 {
54 *DataRunLength += ((ULONG64)*DataRun) << (i * 8);
55 DataRun++;
56 }
57
58 /* NTFS 3+ sparse files */
59 if (DataRunOffsetSize == 0)
60 {
61 *DataRunOffset = -1;
62 }
63 else
64 {
65 for (i = 0; i < DataRunOffsetSize - 1; i++)
66 {
67 *DataRunOffset += ((ULONG64)*DataRun) << (i * 8);
68 DataRun++;
69 }
70 /* The last byte contains sign so we must process it different way. */
71 *DataRunOffset = ((LONG64)(CHAR)(*(DataRun++)) << (i * 8)) + *DataRunOffset;
72 }
73
74 DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize);
75 DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize);
76 DPRINT("DataRunOffset: %x\n", *DataRunOffset);
77 DPRINT("DataRunLength: %x\n", *DataRunLength);
78
79 return DataRun;
80 }
81
82 BOOLEAN
83 FindRun(PNTFS_ATTR_RECORD NresAttr,
84 ULONGLONG vcn,
85 PULONGLONG lcn,
86 PULONGLONG count)
87 {
88 if (vcn < NresAttr->NonResident.LowestVCN || vcn > NresAttr->NonResident.HighestVCN)
89 return FALSE;
90
91 DecodeRun((PUCHAR)((ULONG_PTR)NresAttr + NresAttr->NonResident.MappingPairsOffset), (PLONGLONG)lcn, count);
92
93 return TRUE;
94 }
95
96 static
97 NTSTATUS
98 InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context)
99 {
100 ULONGLONG ListSize;
101 PNTFS_ATTR_RECORD Attribute;
102 PNTFS_ATTR_CONTEXT ListContext;
103
104 DPRINT("InternalReadNonResidentAttributes(%p)\n", Context);
105
106 Attribute = Context->CurrAttr;
107 ASSERT(Attribute->Type == AttributeAttributeList);
108
109 if (Context->OnlyResident)
110 {
111 Context->NonResidentStart = NULL;
112 Context->NonResidentEnd = NULL;
113 return STATUS_SUCCESS;
114 }
115
116 if (Context->NonResidentStart != NULL)
117 {
118 return STATUS_FILE_CORRUPT_ERROR;
119 }
120
121 ListContext = PrepareAttributeContext(Attribute);
122 ListSize = AttributeDataLength(&ListContext->Record);
123 if (ListSize > 0xFFFFFFFF)
124 {
125 ReleaseAttributeContext(ListContext);
126 return STATUS_BUFFER_OVERFLOW;
127 }
128
129 Context->NonResidentStart = ExAllocatePoolWithTag(NonPagedPool, (ULONG)ListSize, TAG_NTFS);
130 if (Context->NonResidentStart == NULL)
131 {
132 ReleaseAttributeContext(ListContext);
133 return STATUS_INSUFFICIENT_RESOURCES;
134 }
135
136 if (ReadAttribute(Context->Vcb, ListContext, 0, (PCHAR)Context->NonResidentStart, (ULONG)ListSize) != ListSize)
137 {
138 ExFreePoolWithTag(Context->NonResidentStart, TAG_NTFS);
139 Context->NonResidentStart = NULL;
140 ReleaseAttributeContext(ListContext);
141 return STATUS_FILE_CORRUPT_ERROR;
142 }
143
144 ReleaseAttributeContext(ListContext);
145 Context->NonResidentEnd = (PNTFS_ATTR_RECORD)((PCHAR)Context->NonResidentStart + ListSize);
146 return STATUS_SUCCESS;
147 }
148
149 static
150 PNTFS_ATTR_RECORD
151 InternalGetNextAttribute(PFIND_ATTR_CONTXT Context)
152 {
153 PNTFS_ATTR_RECORD NextAttribute;
154
155 if (Context->CurrAttr == (PVOID)-1)
156 {
157 return NULL;
158 }
159
160 if (Context->CurrAttr >= Context->FirstAttr &&
161 Context->CurrAttr < Context->LastAttr)
162 {
163 if (Context->CurrAttr->Length == 0)
164 {
165 DPRINT1("Broken length!\n");
166 Context->CurrAttr = (PVOID)-1;
167 return NULL;
168 }
169
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;
173
174 if (Context->CurrAttr < Context->LastAttr &&
175 Context->CurrAttr->Type != AttributeEnd)
176 {
177 return Context->CurrAttr;
178 }
179 }
180
181 if (Context->NonResidentStart == NULL)
182 {
183 Context->CurrAttr = (PVOID)-1;
184 return NULL;
185 }
186
187 if (Context->CurrAttr < Context->NonResidentStart ||
188 Context->CurrAttr >= Context->NonResidentEnd)
189 {
190 Context->CurrAttr = Context->NonResidentStart;
191 }
192 else if (Context->CurrAttr->Length != 0)
193 {
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;
197 }
198 else
199 {
200 DPRINT1("Broken length!\n");
201 Context->CurrAttr = (PVOID)-1;
202 return NULL;
203 }
204
205 if (Context->CurrAttr < Context->NonResidentEnd &&
206 Context->CurrAttr->Type != AttributeEnd)
207 {
208 return Context->CurrAttr;
209 }
210
211 Context->CurrAttr = (PVOID)-1;
212 return NULL;
213 }
214
215 NTSTATUS
216 FindFirstAttribute(PFIND_ATTR_CONTXT Context,
217 PDEVICE_EXTENSION Vcb,
218 PFILE_RECORD_HEADER FileRecord,
219 BOOLEAN OnlyResident,
220 PNTFS_ATTR_RECORD * Attribute)
221 {
222 NTSTATUS Status;
223
224 DPRINT("FindFistAttribute(%p, %p, %p, %p, %u, %p)\n", Context, Vcb, FileRecord, OnlyResident, Attribute);
225
226 Context->Vcb = Vcb;
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;
234
235 if (Context->FirstAttr->Type == AttributeEnd)
236 {
237 Context->CurrAttr = (PVOID)-1;
238 return STATUS_END_OF_FILE;
239 }
240 else if (Context->FirstAttr->Type == AttributeAttributeList)
241 {
242 Status = InternalReadNonResidentAttributes(Context);
243 if (!NT_SUCCESS(Status))
244 {
245 return Status;
246 }
247
248 *Attribute = InternalGetNextAttribute(Context);
249 if (*Attribute == NULL)
250 {
251 return STATUS_END_OF_FILE;
252 }
253 }
254 else
255 {
256 *Attribute = Context->CurrAttr;
257 Context->Offset = (UCHAR*)Context->CurrAttr - (UCHAR*)FileRecord;
258 }
259
260 return STATUS_SUCCESS;
261 }
262
263 NTSTATUS
264 FindNextAttribute(PFIND_ATTR_CONTXT Context,
265 PNTFS_ATTR_RECORD * Attribute)
266 {
267 NTSTATUS Status;
268
269 DPRINT("FindNextAttribute(%p, %p)\n", Context, Attribute);
270
271 *Attribute = InternalGetNextAttribute(Context);
272 if (*Attribute == NULL)
273 {
274 return STATUS_END_OF_FILE;
275 }
276
277 if (Context->CurrAttr->Type != AttributeAttributeList)
278 {
279 return STATUS_SUCCESS;
280 }
281
282 Status = InternalReadNonResidentAttributes(Context);
283 if (!NT_SUCCESS(Status))
284 {
285 return Status;
286 }
287
288 *Attribute = InternalGetNextAttribute(Context);
289 if (*Attribute == NULL)
290 {
291 return STATUS_END_OF_FILE;
292 }
293
294 return STATUS_SUCCESS;
295 }
296
297 VOID
298 FindCloseAttribute(PFIND_ATTR_CONTXT Context)
299 {
300 if (Context->NonResidentStart != NULL)
301 {
302 ExFreePoolWithTag(Context->NonResidentStart, TAG_NTFS);
303 Context->NonResidentStart = NULL;
304 }
305 }
306
307 static
308 VOID
309 NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute)
310 {
311 PFILENAME_ATTRIBUTE FileNameAttr;
312
313 DbgPrint(" $FILE_NAME ");
314
315 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
316
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);
321 }
322
323
324 static
325 VOID
326 NtfsDumpStandardInformationAttribute(PNTFS_ATTR_RECORD Attribute)
327 {
328 PSTANDARD_INFORMATION StandardInfoAttr;
329
330 DbgPrint(" $STANDARD_INFORMATION ");
331
332 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
333
334 StandardInfoAttr = (PSTANDARD_INFORMATION)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
335 DbgPrint(" '%x' ", StandardInfoAttr->FileAttribute);
336 }
337
338
339 static
340 VOID
341 NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute)
342 {
343 PWCHAR VolumeName;
344
345 DbgPrint(" $VOLUME_NAME ");
346
347 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
348
349 VolumeName = (PWCHAR)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
350 DbgPrint(" '%.*S' ", Attribute->Resident.ValueLength / sizeof(WCHAR), VolumeName);
351 }
352
353
354 static
355 VOID
356 NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute)
357 {
358 PVOLINFO_ATTRIBUTE VolInfoAttr;
359
360 DbgPrint(" $VOLUME_INFORMATION ");
361
362 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
363
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,
368 VolInfoAttr->Flags);
369 }
370
371
372 static
373 VOID
374 NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute)
375 {
376 PINDEX_ROOT_ATTRIBUTE IndexRootAttr;
377
378 IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
379
380 if (IndexRootAttr->AttributeType == AttributeFileName)
381 ASSERT(IndexRootAttr->CollationRule == COLLATION_FILE_NAME);
382
383 DbgPrint(" $INDEX_ROOT (%uB, %u) ", IndexRootAttr->SizeOfEntry, IndexRootAttr->ClustersPerIndexRecord);
384
385 if (IndexRootAttr->Header.Flags == INDEX_ROOT_SMALL)
386 {
387 DbgPrint(" (small) ");
388 }
389 else
390 {
391 ASSERT(IndexRootAttr->Header.Flags == INDEX_ROOT_LARGE);
392 DbgPrint(" (large) ");
393 }
394 }
395
396
397 static
398 VOID
399 NtfsDumpAttribute(PDEVICE_EXTENSION Vcb,
400 PNTFS_ATTR_RECORD Attribute)
401 {
402 UNICODE_STRING Name;
403
404 ULONGLONG lcn = 0;
405 ULONGLONG runcount = 0;
406
407 switch (Attribute->Type)
408 {
409 case AttributeFileName:
410 NtfsDumpFileNameAttribute(Attribute);
411 break;
412
413 case AttributeStandardInformation:
414 NtfsDumpStandardInformationAttribute(Attribute);
415 break;
416
417 case AttributeObjectId:
418 DbgPrint(" $OBJECT_ID ");
419 break;
420
421 case AttributeSecurityDescriptor:
422 DbgPrint(" $SECURITY_DESCRIPTOR ");
423 break;
424
425 case AttributeVolumeName:
426 NtfsDumpVolumeNameAttribute(Attribute);
427 break;
428
429 case AttributeVolumeInformation:
430 NtfsDumpVolumeInformationAttribute(Attribute);
431 break;
432
433 case AttributeData:
434 DbgPrint(" $DATA ");
435 //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
436 break;
437
438 case AttributeIndexRoot:
439 NtfsDumpIndexRootAttribute(Attribute);
440 break;
441
442 case AttributeIndexAllocation:
443 DbgPrint(" $INDEX_ALLOCATION ");
444 break;
445
446 case AttributeBitmap:
447 DbgPrint(" $BITMAP ");
448 break;
449
450 case AttributeReparsePoint:
451 DbgPrint(" $REPARSE_POINT ");
452 break;
453
454 case AttributeEAInformation:
455 DbgPrint(" $EA_INFORMATION ");
456 break;
457
458 case AttributeEA:
459 DbgPrint(" $EA ");
460 break;
461
462 case AttributePropertySet:
463 DbgPrint(" $PROPERTY_SET ");
464 break;
465
466 case AttributeLoggedUtilityStream:
467 DbgPrint(" $LOGGED_UTILITY_STREAM ");
468 break;
469
470 default:
471 DbgPrint(" Attribute %lx ",
472 Attribute->Type);
473 break;
474 }
475
476 if (Attribute->Type != AttributeAttributeList)
477 {
478 if (Attribute->NameLength != 0)
479 {
480 Name.Length = Attribute->NameLength * sizeof(WCHAR);
481 Name.MaximumLength = Name.Length;
482 Name.Buffer = (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset);
483
484 DbgPrint("'%wZ' ", &Name);
485 }
486
487 DbgPrint("(%s)\n",
488 Attribute->IsNonResident ? "non-resident" : "resident");
489
490 if (Attribute->IsNonResident)
491 {
492 FindRun(Attribute,0,&lcn, &runcount);
493
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);
498 }
499 }
500 }
501
502
503 VOID
504 NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb,
505 PFILE_RECORD_HEADER FileRecord)
506 {
507 NTSTATUS Status;
508 FIND_ATTR_CONTXT Context;
509 PNTFS_ATTR_RECORD Attribute;
510
511 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
512 while (NT_SUCCESS(Status))
513 {
514 NtfsDumpAttribute(Vcb, Attribute);
515
516 Status = FindNextAttribute(&Context, &Attribute);
517 }
518
519 FindCloseAttribute(&Context);
520 }
521
522 PFILENAME_ATTRIBUTE
523 GetFileNameFromRecord(PDEVICE_EXTENSION Vcb,
524 PFILE_RECORD_HEADER FileRecord,
525 UCHAR NameType)
526 {
527 FIND_ATTR_CONTXT Context;
528 PNTFS_ATTR_RECORD Attribute;
529 PFILENAME_ATTRIBUTE Name;
530 NTSTATUS Status;
531
532 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
533 while (NT_SUCCESS(Status))
534 {
535 if (Attribute->Type == AttributeFileName)
536 {
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))
541 {
542 FindCloseAttribute(&Context);
543 return Name;
544 }
545 }
546
547 Status = FindNextAttribute(&Context, &Attribute);
548 }
549
550 FindCloseAttribute(&Context);
551 return NULL;
552 }
553
554 PSTANDARD_INFORMATION
555 GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb,
556 PFILE_RECORD_HEADER FileRecord)
557 {
558 NTSTATUS Status;
559 FIND_ATTR_CONTXT Context;
560 PNTFS_ATTR_RECORD Attribute;
561 PSTANDARD_INFORMATION StdInfo;
562
563 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
564 while (NT_SUCCESS(Status))
565 {
566 if (Attribute->Type == AttributeStandardInformation)
567 {
568 StdInfo = (PSTANDARD_INFORMATION)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
569 FindCloseAttribute(&Context);
570 return StdInfo;
571 }
572
573 Status = FindNextAttribute(&Context, &Attribute);
574 }
575
576 FindCloseAttribute(&Context);
577 return NULL;
578 }
579
580 PFILENAME_ATTRIBUTE
581 GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb,
582 PFILE_RECORD_HEADER FileRecord)
583 {
584 PFILENAME_ATTRIBUTE FileName;
585
586 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_POSIX);
587 if (FileName == NULL)
588 {
589 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_WIN32);
590 if (FileName == NULL)
591 {
592 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_DOS);
593 }
594 }
595
596 return FileName;
597 }
598
599 /* EOF */