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