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