[FASTFAT][CDFS]
[reactos.git] / reactos / drivers / filesystems / fastfat / direntry.c
1 /*
2 * FILE: DirEntry.c
3 * PURPOSE: Routines to manipulate directory entries.
4 * COPYRIGHT: See COPYING in the top level directory
5 * PROJECT: ReactOS kernel
6 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
7 * Rex Jolliff (rex@lvcablemodem.com)
8 * Herve Poussineau (reactos@poussine.freesurf.fr)
9 */
10
11 /* ------------------------------------------------------- INCLUDES */
12
13 #include "vfat.h"
14
15 #define NDEBUG
16 #include <debug.h>
17
18 ULONG
19 vfatDirEntryGetFirstCluster(
20 PDEVICE_EXTENSION pDeviceExt,
21 PDIR_ENTRY pFatDirEntry)
22 {
23 ULONG cluster;
24
25 if (pDeviceExt->FatInfo.FatType == FAT32)
26 {
27 cluster = pFatDirEntry->Fat.FirstCluster |
28 (pFatDirEntry->Fat.FirstClusterHigh << 16);
29 }
30 else if (pDeviceExt->Flags & VCB_IS_FATX)
31 {
32 cluster = pFatDirEntry->FatX.FirstCluster;
33 }
34 else
35 {
36 cluster = pFatDirEntry->Fat.FirstCluster;
37 }
38
39 return cluster;
40 }
41
42 static
43 BOOLEAN
44 FATIsDirectoryEmpty(
45 PVFATFCB Fcb)
46 {
47 LARGE_INTEGER FileOffset;
48 PVOID Context = NULL;
49 PFAT_DIR_ENTRY FatDirEntry;
50 ULONG Index, MaxIndex;
51
52 if (vfatFCBIsRoot(Fcb))
53 {
54 Index = 0;
55 }
56 else
57 {
58 Index = 2;
59 }
60
61 FileOffset.QuadPart = 0;
62 MaxIndex = Fcb->RFCB.FileSize.u.LowPart / sizeof(FAT_DIR_ENTRY);
63
64 while (Index < MaxIndex)
65 {
66 if (Context == NULL || (Index % FAT_ENTRIES_PER_PAGE) == 0)
67 {
68 if (Context != NULL)
69 {
70 CcUnpinData(Context);
71 }
72
73 _SEH2_TRY
74 {
75 CcMapData(Fcb->FileObject, &FileOffset, sizeof(FAT_DIR_ENTRY), TRUE, &Context, (PVOID*)&FatDirEntry);
76 }
77 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
78 {
79 _SEH2_YIELD(return TRUE);
80 }
81 _SEH2_END;
82
83 FatDirEntry += Index % FAT_ENTRIES_PER_PAGE;
84 FileOffset.QuadPart += PAGE_SIZE;
85 }
86
87 if (FAT_ENTRY_END(FatDirEntry))
88 {
89 CcUnpinData(Context);
90 return TRUE;
91 }
92
93 if (!FAT_ENTRY_DELETED(FatDirEntry))
94 {
95 CcUnpinData(Context);
96 return FALSE;
97 }
98
99 Index++;
100 FatDirEntry++;
101 }
102
103 if (Context)
104 {
105 CcUnpinData(Context);
106 }
107
108 return TRUE;
109 }
110
111 static
112 BOOLEAN
113 FATXIsDirectoryEmpty(
114 PVFATFCB Fcb)
115 {
116 LARGE_INTEGER FileOffset;
117 PVOID Context = NULL;
118 PFATX_DIR_ENTRY FatXDirEntry;
119 ULONG Index = 0, MaxIndex;
120
121 FileOffset.QuadPart = 0;
122 MaxIndex = Fcb->RFCB.FileSize.u.LowPart / sizeof(FATX_DIR_ENTRY);
123
124 while (Index < MaxIndex)
125 {
126 if (Context == NULL || (Index % FATX_ENTRIES_PER_PAGE) == 0)
127 {
128 if (Context != NULL)
129 {
130 CcUnpinData(Context);
131 }
132
133 _SEH2_TRY
134 {
135 CcMapData(Fcb->FileObject, &FileOffset, sizeof(FATX_DIR_ENTRY), TRUE, &Context, (PVOID*)&FatXDirEntry);
136 }
137 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
138 {
139 _SEH2_YIELD(return TRUE);
140 }
141 _SEH2_END;
142
143 FatXDirEntry += Index % FATX_ENTRIES_PER_PAGE;
144 FileOffset.QuadPart += PAGE_SIZE;
145 }
146
147 if (FATX_ENTRY_END(FatXDirEntry))
148 {
149 CcUnpinData(Context);
150 return TRUE;
151 }
152
153 if (!FATX_ENTRY_DELETED(FatXDirEntry))
154 {
155 CcUnpinData(Context);
156 return FALSE;
157 }
158
159 Index++;
160 FatXDirEntry++;
161 }
162
163 if (Context)
164 {
165 CcUnpinData(Context);
166 }
167
168 return TRUE;
169 }
170
171 BOOLEAN
172 VfatIsDirectoryEmpty(
173 PVFATFCB Fcb)
174 {
175 if (Fcb->Flags & FCB_IS_FATX_ENTRY)
176 return FATXIsDirectoryEmpty(Fcb);
177 else
178 return FATIsDirectoryEmpty(Fcb);
179 }
180
181 NTSTATUS
182 FATGetNextDirEntry(
183 PVOID *pContext,
184 PVOID *pPage,
185 IN PVFATFCB pDirFcb,
186 PVFAT_DIRENTRY_CONTEXT DirContext,
187 BOOLEAN First)
188 {
189 ULONG dirMap;
190 PWCHAR pName;
191 LARGE_INTEGER FileOffset;
192 PFAT_DIR_ENTRY fatDirEntry;
193 slot * longNameEntry;
194 ULONG index;
195
196 UCHAR CheckSum, shortCheckSum;
197 USHORT i;
198 BOOLEAN Valid = TRUE;
199 BOOLEAN Back = FALSE;
200
201 DirContext->LongNameU.Length = 0;
202 DirContext->LongNameU.Buffer[0] = UNICODE_NULL;
203
204 FileOffset.u.HighPart = 0;
205 FileOffset.u.LowPart = ROUND_DOWN(DirContext->DirIndex * sizeof(FAT_DIR_ENTRY), PAGE_SIZE);
206
207 if (*pContext == NULL || (DirContext->DirIndex % FAT_ENTRIES_PER_PAGE) == 0)
208 {
209 if (*pContext != NULL)
210 {
211 CcUnpinData(*pContext);
212 }
213
214 if (FileOffset.u.LowPart >= pDirFcb->RFCB.FileSize.u.LowPart)
215 {
216 *pContext = NULL;
217 return STATUS_NO_MORE_ENTRIES;
218 }
219
220 _SEH2_TRY
221 {
222 CcMapData(pDirFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, pContext, pPage);
223 }
224 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
225 {
226 *pContext = NULL;
227 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES);
228 }
229 _SEH2_END;
230 }
231
232 fatDirEntry = (PFAT_DIR_ENTRY)(*pPage) + DirContext->DirIndex % FAT_ENTRIES_PER_PAGE;
233 longNameEntry = (slot*) fatDirEntry;
234 dirMap = 0;
235
236 if (First)
237 {
238 /* This is the first call to vfatGetNextDirEntry. Possible the start index points
239 * into a long name or points to a short name with an assigned long name.
240 * We must go back to the real start of the entry */
241 while (DirContext->DirIndex > 0 &&
242 !FAT_ENTRY_END(fatDirEntry) &&
243 !FAT_ENTRY_DELETED(fatDirEntry) &&
244 ((!FAT_ENTRY_LONG(fatDirEntry) && !Back) ||
245 (FAT_ENTRY_LONG(fatDirEntry) && !(longNameEntry->id & 0x40))))
246 {
247 DirContext->DirIndex--;
248 Back = TRUE;
249
250 if ((DirContext->DirIndex % FAT_ENTRIES_PER_PAGE) == FAT_ENTRIES_PER_PAGE - 1)
251 {
252 CcUnpinData(*pContext);
253 FileOffset.u.LowPart -= PAGE_SIZE;
254
255 if (FileOffset.u.LowPart >= pDirFcb->RFCB.FileSize.u.LowPart)
256 {
257 *pContext = NULL;
258 return STATUS_NO_MORE_ENTRIES;
259 }
260
261 _SEH2_TRY
262 {
263 CcMapData(pDirFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, pContext, pPage);
264 }
265 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
266 {
267 *pContext = NULL;
268 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES);
269 }
270 _SEH2_END;
271
272 fatDirEntry = (PFAT_DIR_ENTRY)(*pPage) + DirContext->DirIndex % FAT_ENTRIES_PER_PAGE;
273 longNameEntry = (slot*) fatDirEntry;
274 }
275 else
276 {
277 fatDirEntry--;
278 longNameEntry--;
279 }
280 }
281
282 if (Back && !FAT_ENTRY_END(fatDirEntry) &&
283 (FAT_ENTRY_DELETED(fatDirEntry) || !FAT_ENTRY_LONG(fatDirEntry)))
284 {
285 DirContext->DirIndex++;
286
287 if ((DirContext->DirIndex % FAT_ENTRIES_PER_PAGE) == 0)
288 {
289 CcUnpinData(*pContext);
290 FileOffset.u.LowPart += PAGE_SIZE;
291
292 if (FileOffset.u.LowPart >= pDirFcb->RFCB.FileSize.u.LowPart)
293 {
294 *pContext = NULL;
295 return STATUS_NO_MORE_ENTRIES;
296 }
297
298 _SEH2_TRY
299 {
300 CcMapData(pDirFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, pContext, pPage);
301 }
302 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
303 {
304 *pContext = NULL;
305 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES);
306 }
307 _SEH2_END;
308
309 fatDirEntry = (PFAT_DIR_ENTRY)*pPage;
310 longNameEntry = (slot*) *pPage;
311 }
312 else
313 {
314 fatDirEntry++;
315 longNameEntry++;
316 }
317 }
318 }
319
320 DirContext->StartIndex = DirContext->DirIndex;
321 CheckSum = 0;
322
323 while (TRUE)
324 {
325 if (FAT_ENTRY_END(fatDirEntry))
326 {
327 CcUnpinData(*pContext);
328 *pContext = NULL;
329 return STATUS_NO_MORE_ENTRIES;
330 }
331
332 if (FAT_ENTRY_DELETED(fatDirEntry))
333 {
334 dirMap = 0;
335 DirContext->LongNameU.Buffer[0] = 0;
336 DirContext->StartIndex = DirContext->DirIndex + 1;
337 }
338 else
339 {
340 if (FAT_ENTRY_LONG(fatDirEntry))
341 {
342 if (dirMap == 0)
343 {
344 DPRINT (" long name entry found at %u\n", DirContext->DirIndex);
345 RtlZeroMemory(DirContext->LongNameU.Buffer, DirContext->LongNameU.MaximumLength);
346 CheckSum = longNameEntry->alias_checksum;
347 Valid = TRUE;
348 }
349
350 DPRINT(" name chunk1:[%.*S] chunk2:[%.*S] chunk3:[%.*S]\n",
351 5, longNameEntry->name0_4,
352 6, longNameEntry->name5_10,
353 2, longNameEntry->name11_12);
354
355 index = longNameEntry->id & 0x3f; // Note: it can be 0 for corrupted FS
356
357 /* Make sure index is valid and we have enaugh space in buffer
358 (we count one char for \0) */
359 if (index > 0 &&
360 index * 13 < DirContext->LongNameU.MaximumLength / sizeof(WCHAR))
361 {
362 index--; // make index 0 based
363 dirMap |= 1 << index;
364
365 pName = DirContext->LongNameU.Buffer + index * 13;
366 RtlCopyMemory(pName, longNameEntry->name0_4, 5 * sizeof(WCHAR));
367 RtlCopyMemory(pName + 5, longNameEntry->name5_10, 6 * sizeof(WCHAR));
368 RtlCopyMemory(pName + 11, longNameEntry->name11_12, 2 * sizeof(WCHAR));
369
370 if (longNameEntry->id & 0x40)
371 {
372 /* It's last LFN entry. Terminate filename with \0 */
373 pName[13] = UNICODE_NULL;
374 }
375 }
376 else
377 DPRINT1("Long name entry has invalid index: %x!\n", longNameEntry->id);
378
379 DPRINT (" longName: [%S]\n", DirContext->LongNameU.Buffer);
380
381 if (CheckSum != longNameEntry->alias_checksum)
382 {
383 DPRINT1("Found wrong alias checksum in long name entry (first %x, current %x, %S)\n",
384 CheckSum, longNameEntry->alias_checksum, DirContext->LongNameU.Buffer);
385 Valid = FALSE;
386 }
387 }
388 else
389 {
390 shortCheckSum = 0;
391 for (i = 0; i < 11; i++)
392 {
393 shortCheckSum = (((shortCheckSum & 1) << 7)
394 | ((shortCheckSum & 0xfe) >> 1))
395 + fatDirEntry->ShortName[i];
396 }
397
398 if (shortCheckSum != CheckSum && DirContext->LongNameU.Buffer[0])
399 {
400 DPRINT1("Checksum from long and short name is not equal (short: %x, long: %x, %S)\n",
401 shortCheckSum, CheckSum, DirContext->LongNameU.Buffer);
402 DirContext->LongNameU.Buffer[0] = 0;
403 }
404
405 if (Valid == FALSE)
406 {
407 DirContext->LongNameU.Buffer[0] = 0;
408 }
409
410 RtlCopyMemory (&DirContext->DirEntry.Fat, fatDirEntry, sizeof (FAT_DIR_ENTRY));
411 break;
412 }
413 }
414
415 DirContext->DirIndex++;
416
417 if ((DirContext->DirIndex % FAT_ENTRIES_PER_PAGE) == 0)
418 {
419 CcUnpinData(*pContext);
420 FileOffset.u.LowPart += PAGE_SIZE;
421
422 if (FileOffset.u.LowPart >= pDirFcb->RFCB.FileSize.u.LowPart)
423 {
424 *pContext = NULL;
425 return STATUS_NO_MORE_ENTRIES;
426 }
427
428 _SEH2_TRY
429 {
430 CcMapData(pDirFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, pContext, pPage);
431 }
432 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
433 {
434 *pContext = NULL;
435 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES);
436 }
437 _SEH2_END;
438
439 fatDirEntry = (PFAT_DIR_ENTRY)*pPage;
440 longNameEntry = (slot*) *pPage;
441 }
442 else
443 {
444 fatDirEntry++;
445 longNameEntry++;
446 }
447 }
448
449 /* Make sure filename is NULL terminate and calculate length */
450 DirContext->LongNameU.Buffer[DirContext->LongNameU.MaximumLength / sizeof(WCHAR) - 1]
451 = UNICODE_NULL;
452 DirContext->LongNameU.Length = wcslen(DirContext->LongNameU.Buffer) * sizeof(WCHAR);
453
454 /* Init short name */
455 vfat8Dot3ToString(&DirContext->DirEntry.Fat, &DirContext->ShortNameU);
456
457 /* If we found no LFN, use short name as long */
458 if (DirContext->LongNameU.Length == 0)
459 RtlCopyUnicodeString(&DirContext->LongNameU, &DirContext->ShortNameU);
460
461 return STATUS_SUCCESS;
462 }
463
464 NTSTATUS
465 FATXGetNextDirEntry(
466 PVOID *pContext,
467 PVOID *pPage,
468 IN PVFATFCB pDirFcb,
469 PVFAT_DIRENTRY_CONTEXT DirContext,
470 BOOLEAN First)
471 {
472 LARGE_INTEGER FileOffset;
473 PFATX_DIR_ENTRY fatxDirEntry;
474 OEM_STRING StringO;
475 ULONG DirIndex = DirContext->DirIndex;
476
477 FileOffset.u.HighPart = 0;
478
479 UNREFERENCED_PARAMETER(First);
480
481 if (!vfatFCBIsRoot(pDirFcb))
482 {
483 /* need to add . and .. entries */
484 switch (DirContext->DirIndex)
485 {
486 case 0: /* entry . */
487 DirContext->ShortNameU.Buffer[0] = 0;
488 DirContext->ShortNameU.Length = 0;
489 wcscpy(DirContext->LongNameU.Buffer, L".");
490 DirContext->LongNameU.Length = sizeof(WCHAR);
491 RtlCopyMemory(&DirContext->DirEntry.FatX, &pDirFcb->entry.FatX, sizeof(FATX_DIR_ENTRY));
492 DirContext->DirEntry.FatX.Filename[0] = '.';
493 DirContext->DirEntry.FatX.FilenameLength = 1;
494 DirContext->StartIndex = 0;
495 return STATUS_SUCCESS;
496
497 case 1: /* entry .. */
498 DirContext->ShortNameU.Buffer[0] = 0;
499 DirContext->ShortNameU.Length = 0;
500 wcscpy(DirContext->LongNameU.Buffer, L"..");
501 DirContext->LongNameU.Length = 2 * sizeof(WCHAR);
502 RtlCopyMemory(&DirContext->DirEntry.FatX, &pDirFcb->entry.FatX, sizeof(FATX_DIR_ENTRY));
503 DirContext->DirEntry.FatX.Filename[0] = DirContext->DirEntry.FatX.Filename[1] = '.';
504 DirContext->DirEntry.FatX.FilenameLength = 2;
505 DirContext->StartIndex = 1;
506 return STATUS_SUCCESS;
507
508 default:
509 DirIndex -= 2;
510 }
511 }
512
513 if (*pContext == NULL || (DirIndex % FATX_ENTRIES_PER_PAGE) == 0)
514 {
515 if (*pContext != NULL)
516 {
517 CcUnpinData(*pContext);
518 }
519 FileOffset.u.LowPart = ROUND_DOWN(DirIndex * sizeof(FATX_DIR_ENTRY), PAGE_SIZE);
520 if (FileOffset.u.LowPart >= pDirFcb->RFCB.FileSize.u.LowPart)
521 {
522 *pContext = NULL;
523 return STATUS_NO_MORE_ENTRIES;
524 }
525
526 _SEH2_TRY
527 {
528 CcMapData(pDirFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, pContext, pPage);
529 }
530 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
531 {
532 *pContext = NULL;
533 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES);
534 }
535 _SEH2_END;
536 }
537
538 fatxDirEntry = (PFATX_DIR_ENTRY)(*pPage) + DirIndex % FATX_ENTRIES_PER_PAGE;
539
540 DirContext->StartIndex = DirContext->DirIndex;
541
542 while (TRUE)
543 {
544 if (FATX_ENTRY_END(fatxDirEntry))
545 {
546 CcUnpinData(*pContext);
547 *pContext = NULL;
548 return STATUS_NO_MORE_ENTRIES;
549 }
550
551 if (!FATX_ENTRY_DELETED(fatxDirEntry))
552 {
553 RtlCopyMemory(&DirContext->DirEntry.FatX, fatxDirEntry, sizeof(FATX_DIR_ENTRY));
554 break;
555 }
556 DirContext->DirIndex++;
557 DirContext->StartIndex++;
558 DirIndex++;
559 if ((DirIndex % FATX_ENTRIES_PER_PAGE) == 0)
560 {
561 CcUnpinData(*pContext);
562 FileOffset.u.LowPart += PAGE_SIZE;
563 if (FileOffset.u.LowPart >= pDirFcb->RFCB.FileSize.u.LowPart)
564 {
565 *pContext = NULL;
566 return STATUS_NO_MORE_ENTRIES;
567 }
568
569 _SEH2_TRY
570 {
571 CcMapData(pDirFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, pContext, pPage);
572 }
573 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
574 {
575 *pContext = NULL;
576 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES);
577 }
578 _SEH2_END;
579
580 fatxDirEntry = (PFATX_DIR_ENTRY)*pPage;
581 }
582 else
583 {
584 fatxDirEntry++;
585 }
586 }
587 DirContext->ShortNameU.Buffer[0] = 0;
588 DirContext->ShortNameU.Length = 0;
589 StringO.Buffer = (PCHAR)fatxDirEntry->Filename;
590 StringO.Length = StringO.MaximumLength = fatxDirEntry->FilenameLength;
591 RtlOemStringToUnicodeString(&DirContext->LongNameU, &StringO, FALSE);
592 return STATUS_SUCCESS;
593 }