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