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