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