fc2463fee577cee5aae5d52005f5f821ffc770fe
[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
527 NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb,
528 PFILE_RECORD_HEADER FileRecord)
529 {
530 NTSTATUS Status;
531 FIND_ATTR_CONTXT Context;
532 PNTFS_ATTR_RECORD Attribute;
533
534 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
535 while (NT_SUCCESS(Status))
536 {
537 NtfsDumpAttribute(Vcb, Attribute);
538
539 Status = FindNextAttribute(&Context, &Attribute);
540 }
541
542 FindCloseAttribute(&Context);
543 }
544
545 PFILENAME_ATTRIBUTE
546 GetFileNameFromRecord(PDEVICE_EXTENSION Vcb,
547 PFILE_RECORD_HEADER FileRecord,
548 UCHAR NameType)
549 {
550 FIND_ATTR_CONTXT Context;
551 PNTFS_ATTR_RECORD Attribute;
552 PFILENAME_ATTRIBUTE Name;
553 NTSTATUS Status;
554
555 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
556 while (NT_SUCCESS(Status))
557 {
558 if (Attribute->Type == AttributeFileName)
559 {
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))
564 {
565 FindCloseAttribute(&Context);
566 return Name;
567 }
568 }
569
570 Status = FindNextAttribute(&Context, &Attribute);
571 }
572
573 FindCloseAttribute(&Context);
574 return NULL;
575 }
576
577 NTSTATUS
578 GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_RECORD Attribute, PULONGLONG LastCluster)
579 {
580 LONGLONG DataRunOffset;
581 ULONGLONG DataRunLength;
582 LONGLONG DataRunStartLCN;
583
584 ULONGLONG LastLCN = 0;
585 PUCHAR DataRun = (PUCHAR)Attribute + Attribute->NonResident.MappingPairsOffset;
586
587 if (!Attribute->IsNonResident)
588 return STATUS_INVALID_PARAMETER;
589
590 while (1)
591 {
592 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
593
594 if (DataRunOffset != -1)
595 {
596 // Normal data run.
597 DataRunStartLCN = LastLCN + DataRunOffset;
598 LastLCN = DataRunStartLCN;
599 *LastCluster = LastLCN + DataRunLength - 1;
600 }
601
602 if (*DataRun == 0)
603 break;
604 }
605
606 return STATUS_SUCCESS;
607 }
608
609 PSTANDARD_INFORMATION
610 GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb,
611 PFILE_RECORD_HEADER FileRecord)
612 {
613 NTSTATUS Status;
614 FIND_ATTR_CONTXT Context;
615 PNTFS_ATTR_RECORD Attribute;
616 PSTANDARD_INFORMATION StdInfo;
617
618 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
619 while (NT_SUCCESS(Status))
620 {
621 if (Attribute->Type == AttributeStandardInformation)
622 {
623 StdInfo = (PSTANDARD_INFORMATION)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
624 FindCloseAttribute(&Context);
625 return StdInfo;
626 }
627
628 Status = FindNextAttribute(&Context, &Attribute);
629 }
630
631 FindCloseAttribute(&Context);
632 return NULL;
633 }
634
635 PFILENAME_ATTRIBUTE
636 GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb,
637 PFILE_RECORD_HEADER FileRecord)
638 {
639 PFILENAME_ATTRIBUTE FileName;
640
641 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_POSIX);
642 if (FileName == NULL)
643 {
644 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_WIN32);
645 if (FileName == NULL)
646 {
647 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_DOS);
648 }
649 }
650
651 return FileName;
652 }
653
654 /* EOF */