336f014ad5f8bd74295b52fab63475bbc1672f1a
[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 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 (FileOffset.u.LowPart >= pDirFcb->RFCB.FileSize.u.LowPart ||
205 !CcMapData(pDirFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, pContext, pPage))
206 {
207 *pContext = NULL;
208 return STATUS_NO_MORE_ENTRIES;
209 }
210 }
211
212 fatDirEntry = (PFAT_DIR_ENTRY)(*pPage) + DirContext->DirIndex % FAT_ENTRIES_PER_PAGE;
213 longNameEntry = (slot*) fatDirEntry;
214 dirMap = 0;
215
216 if (First)
217 {
218 /* This is the first call to vfatGetNextDirEntry. Possible the start index points
219 * into a long name or points to a short name with an assigned long name.
220 * We must go back to the real start of the entry */
221 while (DirContext->DirIndex > 0 &&
222 !FAT_ENTRY_END(fatDirEntry) &&
223 !FAT_ENTRY_DELETED(fatDirEntry) &&
224 ((!FAT_ENTRY_LONG(fatDirEntry) && !Back) ||
225 (FAT_ENTRY_LONG(fatDirEntry) && !(longNameEntry->id & 0x40))))
226 {
227 DirContext->DirIndex--;
228 Back = TRUE;
229
230 if ((DirContext->DirIndex % FAT_ENTRIES_PER_PAGE) == FAT_ENTRIES_PER_PAGE - 1)
231 {
232 CcUnpinData(*pContext);
233 FileOffset.u.LowPart -= PAGE_SIZE;
234
235 if (FileOffset.u.LowPart >= pDirFcb->RFCB.FileSize.u.LowPart ||
236 !CcMapData(pDirFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, pContext, pPage))
237 {
238 *pContext = NULL;
239 return STATUS_NO_MORE_ENTRIES;
240 }
241
242 fatDirEntry = (PFAT_DIR_ENTRY)(*pPage) + DirContext->DirIndex % FAT_ENTRIES_PER_PAGE;
243 longNameEntry = (slot*) fatDirEntry;
244 }
245 else
246 {
247 fatDirEntry--;
248 longNameEntry--;
249 }
250 }
251
252 if (Back && !FAT_ENTRY_END(fatDirEntry) &&
253 (FAT_ENTRY_DELETED(fatDirEntry) || !FAT_ENTRY_LONG(fatDirEntry)))
254 {
255 DirContext->DirIndex++;
256
257 if ((DirContext->DirIndex % FAT_ENTRIES_PER_PAGE) == 0)
258 {
259 CcUnpinData(*pContext);
260 FileOffset.u.LowPart += PAGE_SIZE;
261
262 if (FileOffset.u.LowPart >= pDirFcb->RFCB.FileSize.u.LowPart ||
263 !CcMapData(pDirFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, pContext, pPage))
264 {
265 *pContext = NULL;
266 return STATUS_NO_MORE_ENTRIES;
267 }
268
269 fatDirEntry = (PFAT_DIR_ENTRY)*pPage;
270 longNameEntry = (slot*) *pPage;
271 }
272 else
273 {
274 fatDirEntry++;
275 longNameEntry++;
276 }
277 }
278 }
279
280 DirContext->StartIndex = DirContext->DirIndex;
281 CheckSum = 0;
282
283 while (TRUE)
284 {
285 if (FAT_ENTRY_END(fatDirEntry))
286 {
287 CcUnpinData(*pContext);
288 *pContext = NULL;
289 return STATUS_NO_MORE_ENTRIES;
290 }
291
292 if (FAT_ENTRY_DELETED(fatDirEntry))
293 {
294 dirMap = 0;
295 DirContext->LongNameU.Buffer[0] = 0;
296 DirContext->StartIndex = DirContext->DirIndex + 1;
297 }
298 else
299 {
300 if (FAT_ENTRY_LONG(fatDirEntry))
301 {
302 if (dirMap == 0)
303 {
304 DPRINT (" long name entry found at %u\n", DirContext->DirIndex);
305 RtlZeroMemory(DirContext->LongNameU.Buffer, DirContext->LongNameU.MaximumLength);
306 CheckSum = longNameEntry->alias_checksum;
307 Valid = TRUE;
308 }
309
310 DPRINT(" name chunk1:[%.*S] chunk2:[%.*S] chunk3:[%.*S]\n",
311 5, longNameEntry->name0_4,
312 6, longNameEntry->name5_10,
313 2, longNameEntry->name11_12);
314
315 index = longNameEntry->id & 0x3f; // Note: it can be 0 for corrupted FS
316
317 /* Make sure index is valid and we have enaugh space in buffer
318 (we count one char for \0) */
319 if (index > 0 &&
320 index * 13 < DirContext->LongNameU.MaximumLength / sizeof(WCHAR))
321 {
322 index--; // make index 0 based
323 dirMap |= 1 << index;
324
325 pName = DirContext->LongNameU.Buffer + index * 13;
326 RtlCopyMemory(pName, longNameEntry->name0_4, 5 * sizeof(WCHAR));
327 RtlCopyMemory(pName + 5, longNameEntry->name5_10, 6 * sizeof(WCHAR));
328 RtlCopyMemory(pName + 11, longNameEntry->name11_12, 2 * sizeof(WCHAR));
329
330 if (longNameEntry->id & 0x40)
331 {
332 /* It's last LFN entry. Terminate filename with \0 */
333 pName[13] = UNICODE_NULL;
334 }
335 }
336 else
337 DPRINT1("Long name entry has invalid index: %x!\n", longNameEntry->id);
338
339 DPRINT (" longName: [%S]\n", DirContext->LongNameU.Buffer);
340
341 if (CheckSum != longNameEntry->alias_checksum)
342 {
343 DPRINT1("Found wrong alias checksum in long name entry (first %x, current %x, %S)\n",
344 CheckSum, longNameEntry->alias_checksum, DirContext->LongNameU.Buffer);
345 Valid = FALSE;
346 }
347 }
348 else
349 {
350 shortCheckSum = 0;
351 for (i = 0; i < 11; i++)
352 {
353 shortCheckSum = (((shortCheckSum & 1) << 7)
354 | ((shortCheckSum & 0xfe) >> 1))
355 + fatDirEntry->ShortName[i];
356 }
357
358 if (shortCheckSum != CheckSum && DirContext->LongNameU.Buffer[0])
359 {
360 DPRINT1("Checksum from long and short name is not equal (short: %x, long: %x, %S)\n",
361 shortCheckSum, CheckSum, DirContext->LongNameU.Buffer);
362 DirContext->LongNameU.Buffer[0] = 0;
363 }
364
365 if (Valid == FALSE)
366 {
367 DirContext->LongNameU.Buffer[0] = 0;
368 }
369
370 RtlCopyMemory (&DirContext->DirEntry.Fat, fatDirEntry, sizeof (FAT_DIR_ENTRY));
371 break;
372 }
373 }
374
375 DirContext->DirIndex++;
376
377 if ((DirContext->DirIndex % FAT_ENTRIES_PER_PAGE) == 0)
378 {
379 CcUnpinData(*pContext);
380 FileOffset.u.LowPart += PAGE_SIZE;
381
382 if (FileOffset.u.LowPart >= pDirFcb->RFCB.FileSize.u.LowPart ||
383 !CcMapData(pDirFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, pContext, pPage))
384 {
385 *pContext = NULL;
386 return STATUS_NO_MORE_ENTRIES;
387 }
388
389 fatDirEntry = (PFAT_DIR_ENTRY)*pPage;
390 longNameEntry = (slot*) *pPage;
391 }
392 else
393 {
394 fatDirEntry++;
395 longNameEntry++;
396 }
397 }
398
399 /* Make sure filename is NULL terminate and calculate length */
400 DirContext->LongNameU.Buffer[DirContext->LongNameU.MaximumLength / sizeof(WCHAR) - 1]
401 = UNICODE_NULL;
402 DirContext->LongNameU.Length = wcslen(DirContext->LongNameU.Buffer) * sizeof(WCHAR);
403
404 /* Init short name */
405 vfat8Dot3ToString(&DirContext->DirEntry.Fat, &DirContext->ShortNameU);
406
407 /* If we found no LFN, use short name as long */
408 if (DirContext->LongNameU.Length == 0)
409 RtlCopyUnicodeString(&DirContext->LongNameU, &DirContext->ShortNameU);
410
411 return STATUS_SUCCESS;
412 }
413
414 NTSTATUS
415 FATXGetNextDirEntry(
416 PVOID *pContext,
417 PVOID *pPage,
418 IN PVFATFCB pDirFcb,
419 PVFAT_DIRENTRY_CONTEXT DirContext,
420 BOOLEAN First)
421 {
422 LARGE_INTEGER FileOffset;
423 PFATX_DIR_ENTRY fatxDirEntry;
424 OEM_STRING StringO;
425 ULONG DirIndex = DirContext->DirIndex;
426
427 FileOffset.u.HighPart = 0;
428
429 UNREFERENCED_PARAMETER(First);
430
431 if (!vfatFCBIsRoot(pDirFcb))
432 {
433 /* need to add . and .. entries */
434 switch (DirContext->DirIndex)
435 {
436 case 0: /* entry . */
437 DirContext->ShortNameU.Buffer[0] = 0;
438 DirContext->ShortNameU.Length = 0;
439 wcscpy(DirContext->LongNameU.Buffer, L".");
440 DirContext->LongNameU.Length = sizeof(WCHAR);
441 RtlCopyMemory(&DirContext->DirEntry.FatX, &pDirFcb->entry.FatX, sizeof(FATX_DIR_ENTRY));
442 DirContext->DirEntry.FatX.Filename[0] = '.';
443 DirContext->DirEntry.FatX.FilenameLength = 1;
444 DirContext->StartIndex = 0;
445 return STATUS_SUCCESS;
446
447 case 1: /* entry .. */
448 DirContext->ShortNameU.Buffer[0] = 0;
449 DirContext->ShortNameU.Length = 0;
450 wcscpy(DirContext->LongNameU.Buffer, L"..");
451 DirContext->LongNameU.Length = 2 * sizeof(WCHAR);
452 RtlCopyMemory(&DirContext->DirEntry.FatX, &pDirFcb->entry.FatX, sizeof(FATX_DIR_ENTRY));
453 DirContext->DirEntry.FatX.Filename[0] = DirContext->DirEntry.FatX.Filename[1] = '.';
454 DirContext->DirEntry.FatX.FilenameLength = 2;
455 DirContext->StartIndex = 1;
456 return STATUS_SUCCESS;
457
458 default:
459 DirIndex -= 2;
460 }
461 }
462
463 if (*pContext == NULL || (DirIndex % FATX_ENTRIES_PER_PAGE) == 0)
464 {
465 if (*pContext != NULL)
466 {
467 CcUnpinData(*pContext);
468 }
469 FileOffset.u.LowPart = ROUND_DOWN(DirIndex * sizeof(FATX_DIR_ENTRY), PAGE_SIZE);
470 if (FileOffset.u.LowPart >= pDirFcb->RFCB.FileSize.u.LowPart ||
471 !CcMapData(pDirFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, pContext, pPage))
472 {
473 *pContext = NULL;
474 return STATUS_NO_MORE_ENTRIES;
475 }
476 }
477
478 fatxDirEntry = (PFATX_DIR_ENTRY)(*pPage) + DirIndex % FATX_ENTRIES_PER_PAGE;
479
480 DirContext->StartIndex = DirContext->DirIndex;
481
482 while (TRUE)
483 {
484 if (FATX_ENTRY_END(fatxDirEntry))
485 {
486 CcUnpinData(*pContext);
487 *pContext = NULL;
488 return STATUS_NO_MORE_ENTRIES;
489 }
490
491 if (!FATX_ENTRY_DELETED(fatxDirEntry))
492 {
493 RtlCopyMemory(&DirContext->DirEntry.FatX, fatxDirEntry, sizeof(FATX_DIR_ENTRY));
494 break;
495 }
496 DirContext->DirIndex++;
497 DirContext->StartIndex++;
498 DirIndex++;
499 if ((DirIndex % FATX_ENTRIES_PER_PAGE) == 0)
500 {
501 CcUnpinData(*pContext);
502 FileOffset.u.LowPart += PAGE_SIZE;
503 if (FileOffset.u.LowPart >= pDirFcb->RFCB.FileSize.u.LowPart ||
504 !CcMapData(pDirFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, pContext, pPage))
505 {
506 *pContext = NULL;
507 return STATUS_NO_MORE_ENTRIES;
508 }
509 fatxDirEntry = (PFATX_DIR_ENTRY)*pPage;
510 }
511 else
512 {
513 fatxDirEntry++;
514 }
515 }
516 DirContext->ShortNameU.Buffer[0] = 0;
517 DirContext->ShortNameU.Length = 0;
518 StringO.Buffer = (PCHAR)fatxDirEntry->Filename;
519 StringO.Length = StringO.MaximumLength = fatxDirEntry->FilenameLength;
520 RtlOemStringToUnicodeString(&DirContext->LongNameU, &StringO, FALSE);
521 return STATUS_SUCCESS;
522 }