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