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