[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 Context->Offset += ((ULONG_PTR)NextAttribute - (ULONG_PTR)Context->CurrAttr);
185 Context->CurrAttr = NextAttribute;
186
187 if (Context->CurrAttr < Context->LastAttr &&
188 Context->CurrAttr->Type != AttributeEnd)
189 {
190 return Context->CurrAttr;
191 }
192 }
193
194 if (Context->NonResidentStart == NULL)
195 {
196 Context->CurrAttr = (PVOID)-1;
197 return NULL;
198 }
199
200 if (Context->CurrAttr < Context->NonResidentStart ||
201 Context->CurrAttr >= Context->NonResidentEnd)
202 {
203 Context->CurrAttr = Context->NonResidentStart;
204 }
205 else if (Context->CurrAttr->Length != 0)
206 {
207 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Context->CurrAttr + Context->CurrAttr->Length);
208 Context->Offset += ((ULONG_PTR)NextAttribute - (ULONG_PTR)Context->CurrAttr);
209 Context->CurrAttr = NextAttribute;
210 }
211 else
212 {
213 DPRINT1("Broken length!\n");
214 Context->CurrAttr = (PVOID)-1;
215 return NULL;
216 }
217
218 if (Context->CurrAttr < Context->NonResidentEnd &&
219 Context->CurrAttr->Type != AttributeEnd)
220 {
221 return Context->CurrAttr;
222 }
223
224 Context->CurrAttr = (PVOID)-1;
225 return NULL;
226 }
227
228 NTSTATUS
229 FindFirstAttribute(PFIND_ATTR_CONTXT Context,
230 PDEVICE_EXTENSION Vcb,
231 PFILE_RECORD_HEADER FileRecord,
232 BOOLEAN OnlyResident,
233 PNTFS_ATTR_RECORD * Attribute)
234 {
235 NTSTATUS Status;
236
237 DPRINT("FindFistAttribute(%p, %p, %p, %p, %u, %p)\n", Context, Vcb, FileRecord, OnlyResident, Attribute);
238
239 Context->Vcb = Vcb;
240 Context->OnlyResident = OnlyResident;
241 Context->FirstAttr = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
242 Context->CurrAttr = Context->FirstAttr;
243 Context->LastAttr = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->BytesInUse);
244 Context->NonResidentStart = NULL;
245 Context->NonResidentEnd = NULL;
246 Context->Offset = FileRecord->AttributeOffset;
247
248 if (Context->FirstAttr->Type == AttributeEnd)
249 {
250 Context->CurrAttr = (PVOID)-1;
251 return STATUS_END_OF_FILE;
252 }
253 else if (Context->FirstAttr->Type == AttributeAttributeList)
254 {
255 Status = InternalReadNonResidentAttributes(Context);
256 if (!NT_SUCCESS(Status))
257 {
258 return Status;
259 }
260
261 *Attribute = InternalGetNextAttribute(Context);
262 if (*Attribute == NULL)
263 {
264 return STATUS_END_OF_FILE;
265 }
266 }
267 else
268 {
269 *Attribute = Context->CurrAttr;
270 Context->Offset = (UCHAR*)Context->CurrAttr - (UCHAR*)FileRecord;
271 }
272
273 return STATUS_SUCCESS;
274 }
275
276 NTSTATUS
277 FindNextAttribute(PFIND_ATTR_CONTXT Context,
278 PNTFS_ATTR_RECORD * Attribute)
279 {
280 NTSTATUS Status;
281
282 DPRINT("FindNextAttribute(%p, %p)\n", Context, Attribute);
283
284 *Attribute = InternalGetNextAttribute(Context);
285 if (*Attribute == NULL)
286 {
287 return STATUS_END_OF_FILE;
288 }
289
290 if (Context->CurrAttr->Type != AttributeAttributeList)
291 {
292 return STATUS_SUCCESS;
293 }
294
295 Status = InternalReadNonResidentAttributes(Context);
296 if (!NT_SUCCESS(Status))
297 {
298 return Status;
299 }
300
301 *Attribute = InternalGetNextAttribute(Context);
302 if (*Attribute == NULL)
303 {
304 return STATUS_END_OF_FILE;
305 }
306
307 return STATUS_SUCCESS;
308 }
309
310 VOID
311 FindCloseAttribute(PFIND_ATTR_CONTXT Context)
312 {
313 if (Context->NonResidentStart != NULL)
314 {
315 ExFreePoolWithTag(Context->NonResidentStart, TAG_NTFS);
316 Context->NonResidentStart = NULL;
317 }
318 }
319
320 static
321 VOID
322 NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute)
323 {
324 PFILENAME_ATTRIBUTE FileNameAttr;
325
326 DbgPrint(" $FILE_NAME ");
327
328 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
329
330 FileNameAttr = (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
331 DbgPrint(" (%x) '%.*S' ", FileNameAttr->NameType, FileNameAttr->NameLength, FileNameAttr->Name);
332 DbgPrint(" '%x' \n", FileNameAttr->FileAttributes);
333 DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr->AllocatedSize, FileNameAttr->DataSize);
334 }
335
336
337 static
338 VOID
339 NtfsDumpStandardInformationAttribute(PNTFS_ATTR_RECORD Attribute)
340 {
341 PSTANDARD_INFORMATION StandardInfoAttr;
342
343 DbgPrint(" $STANDARD_INFORMATION ");
344
345 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
346
347 StandardInfoAttr = (PSTANDARD_INFORMATION)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
348 DbgPrint(" '%x' ", StandardInfoAttr->FileAttribute);
349 }
350
351
352 static
353 VOID
354 NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute)
355 {
356 PWCHAR VolumeName;
357
358 DbgPrint(" $VOLUME_NAME ");
359
360 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
361
362 VolumeName = (PWCHAR)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
363 DbgPrint(" '%.*S' ", Attribute->Resident.ValueLength / sizeof(WCHAR), VolumeName);
364 }
365
366
367 static
368 VOID
369 NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute)
370 {
371 PVOLINFO_ATTRIBUTE VolInfoAttr;
372
373 DbgPrint(" $VOLUME_INFORMATION ");
374
375 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
376
377 VolInfoAttr = (PVOLINFO_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
378 DbgPrint(" NTFS Version %u.%u Flags 0x%04hx ",
379 VolInfoAttr->MajorVersion,
380 VolInfoAttr->MinorVersion,
381 VolInfoAttr->Flags);
382 }
383
384
385 static
386 VOID
387 NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute)
388 {
389 PINDEX_ROOT_ATTRIBUTE IndexRootAttr;
390
391 IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
392
393 if (IndexRootAttr->AttributeType == AttributeFileName)
394 ASSERT(IndexRootAttr->CollationRule == COLLATION_FILE_NAME);
395
396 DbgPrint(" $INDEX_ROOT (%uB, %u) ", IndexRootAttr->SizeOfEntry, IndexRootAttr->ClustersPerIndexRecord);
397
398 if (IndexRootAttr->Header.Flags == INDEX_ROOT_SMALL)
399 {
400 DbgPrint(" (small) ");
401 }
402 else
403 {
404 ASSERT(IndexRootAttr->Header.Flags == INDEX_ROOT_LARGE);
405 DbgPrint(" (large) ");
406 }
407 }
408
409
410 static
411 VOID
412 NtfsDumpAttribute(PDEVICE_EXTENSION Vcb,
413 PNTFS_ATTR_RECORD Attribute)
414 {
415 UNICODE_STRING Name;
416
417 ULONGLONG lcn = 0;
418 ULONGLONG runcount = 0;
419
420 switch (Attribute->Type)
421 {
422 case AttributeFileName:
423 NtfsDumpFileNameAttribute(Attribute);
424 break;
425
426 case AttributeStandardInformation:
427 NtfsDumpStandardInformationAttribute(Attribute);
428 break;
429
430 case AttributeObjectId:
431 DbgPrint(" $OBJECT_ID ");
432 break;
433
434 case AttributeSecurityDescriptor:
435 DbgPrint(" $SECURITY_DESCRIPTOR ");
436 break;
437
438 case AttributeVolumeName:
439 NtfsDumpVolumeNameAttribute(Attribute);
440 break;
441
442 case AttributeVolumeInformation:
443 NtfsDumpVolumeInformationAttribute(Attribute);
444 break;
445
446 case AttributeData:
447 DbgPrint(" $DATA ");
448 //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
449 break;
450
451 case AttributeIndexRoot:
452 NtfsDumpIndexRootAttribute(Attribute);
453 break;
454
455 case AttributeIndexAllocation:
456 DbgPrint(" $INDEX_ALLOCATION ");
457 break;
458
459 case AttributeBitmap:
460 DbgPrint(" $BITMAP ");
461 break;
462
463 case AttributeReparsePoint:
464 DbgPrint(" $REPARSE_POINT ");
465 break;
466
467 case AttributeEAInformation:
468 DbgPrint(" $EA_INFORMATION ");
469 break;
470
471 case AttributeEA:
472 DbgPrint(" $EA ");
473 break;
474
475 case AttributePropertySet:
476 DbgPrint(" $PROPERTY_SET ");
477 break;
478
479 case AttributeLoggedUtilityStream:
480 DbgPrint(" $LOGGED_UTILITY_STREAM ");
481 break;
482
483 default:
484 DbgPrint(" Attribute %lx ",
485 Attribute->Type);
486 break;
487 }
488
489 if (Attribute->Type != AttributeAttributeList)
490 {
491 if (Attribute->NameLength != 0)
492 {
493 Name.Length = Attribute->NameLength * sizeof(WCHAR);
494 Name.MaximumLength = Name.Length;
495 Name.Buffer = (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset);
496
497 DbgPrint("'%wZ' ", &Name);
498 }
499
500 DbgPrint("(%s)\n",
501 Attribute->IsNonResident ? "non-resident" : "resident");
502
503 if (Attribute->IsNonResident)
504 {
505 FindRun(Attribute,0,&lcn, &runcount);
506
507 DbgPrint(" AllocatedSize %I64u DataSize %I64u InitilizedSize %I64u\n",
508 Attribute->NonResident.AllocatedSize, Attribute->NonResident.DataSize, Attribute->NonResident.InitializedSize);
509 DbgPrint(" logical clusters: %I64u - %I64u\n",
510 lcn, lcn + runcount - 1);
511 }
512 }
513 }
514
515
516 VOID
517 NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb,
518 PFILE_RECORD_HEADER FileRecord)
519 {
520 NTSTATUS Status;
521 FIND_ATTR_CONTXT Context;
522 PNTFS_ATTR_RECORD Attribute;
523
524 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
525 while (NT_SUCCESS(Status))
526 {
527 NtfsDumpAttribute(Vcb, Attribute);
528
529 Status = FindNextAttribute(&Context, &Attribute);
530 }
531
532 FindCloseAttribute(&Context);
533 }
534
535 PFILENAME_ATTRIBUTE
536 GetFileNameFromRecord(PDEVICE_EXTENSION Vcb,
537 PFILE_RECORD_HEADER FileRecord,
538 UCHAR NameType)
539 {
540 FIND_ATTR_CONTXT Context;
541 PNTFS_ATTR_RECORD Attribute;
542 PFILENAME_ATTRIBUTE Name;
543 NTSTATUS Status;
544
545 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
546 while (NT_SUCCESS(Status))
547 {
548 if (Attribute->Type == AttributeFileName)
549 {
550 Name = (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
551 if (Name->NameType == NameType ||
552 (Name->NameType == NTFS_FILE_NAME_WIN32_AND_DOS && NameType == NTFS_FILE_NAME_WIN32) ||
553 (Name->NameType == NTFS_FILE_NAME_WIN32_AND_DOS && NameType == NTFS_FILE_NAME_DOS))
554 {
555 FindCloseAttribute(&Context);
556 return Name;
557 }
558 }
559
560 Status = FindNextAttribute(&Context, &Attribute);
561 }
562
563 FindCloseAttribute(&Context);
564 return NULL;
565 }
566
567 NTSTATUS
568 GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_RECORD Attribute, PULONGLONG LastCluster)
569 {
570 LONGLONG DataRunOffset;
571 ULONGLONG DataRunLength;
572 LONGLONG DataRunStartLCN;
573
574 ULONGLONG LastLCN = 0;
575 PUCHAR DataRun = (PUCHAR)Attribute + Attribute->NonResident.MappingPairsOffset;
576
577 if (!Attribute->IsNonResident)
578 return STATUS_INVALID_PARAMETER;
579
580 while (1)
581 {
582 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
583
584 if (DataRunOffset == -1)
585 {
586 // sparse run
587 if (*DataRun == 0)
588 {
589 // if it's the last run, return the last cluster of the last run
590 *LastCluster = LastLCN + DataRunLength - 1;
591 break;
592 }
593 }
594 else
595 {
596 // Normal data run.
597 DataRunStartLCN = LastLCN + DataRunOffset;
598 LastLCN = DataRunStartLCN;
599 }
600
601 if (*DataRun == 0)
602 {
603 *LastCluster = LastLCN + DataRunLength - 1;
604 break;
605 }
606 }
607
608 return STATUS_SUCCESS;
609 }
610
611 PSTANDARD_INFORMATION
612 GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb,
613 PFILE_RECORD_HEADER FileRecord)
614 {
615 NTSTATUS Status;
616 FIND_ATTR_CONTXT Context;
617 PNTFS_ATTR_RECORD Attribute;
618 PSTANDARD_INFORMATION StdInfo;
619
620 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
621 while (NT_SUCCESS(Status))
622 {
623 if (Attribute->Type == AttributeStandardInformation)
624 {
625 StdInfo = (PSTANDARD_INFORMATION)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
626 FindCloseAttribute(&Context);
627 return StdInfo;
628 }
629
630 Status = FindNextAttribute(&Context, &Attribute);
631 }
632
633 FindCloseAttribute(&Context);
634 return NULL;
635 }
636
637 PFILENAME_ATTRIBUTE
638 GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb,
639 PFILE_RECORD_HEADER FileRecord)
640 {
641 PFILENAME_ATTRIBUTE FileName;
642
643 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_POSIX);
644 if (FileName == NULL)
645 {
646 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_WIN32);
647 if (FileName == NULL)
648 {
649 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_DOS);
650 }
651 }
652
653 return FileName;
654 }
655
656 /* EOF */