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)
11 /* ------------------------------------------------------- INCLUDES */
19 vfatDirEntryGetFirstCluster(
20 PDEVICE_EXTENSION pDeviceExt
,
21 PDIR_ENTRY pFatDirEntry
)
25 if (pDeviceExt
->FatInfo
.FatType
== FAT32
)
27 cluster
= pFatDirEntry
->Fat
.FirstCluster
|
28 (pFatDirEntry
->Fat
.FirstClusterHigh
<< 16);
30 else if (BooleanFlagOn(pDeviceExt
->Flags
, VCB_IS_FATX
))
32 cluster
= pFatDirEntry
->FatX
.FirstCluster
;
36 cluster
= pFatDirEntry
->Fat
.FirstCluster
;
47 LARGE_INTEGER FileOffset
;
49 PFAT_DIR_ENTRY FatDirEntry
;
50 ULONG Index
, MaxIndex
;
52 if (vfatFCBIsRoot(Fcb
))
61 FileOffset
.QuadPart
= 0;
62 MaxIndex
= Fcb
->RFCB
.FileSize
.u
.LowPart
/ sizeof(FAT_DIR_ENTRY
);
64 while (Index
< MaxIndex
)
66 if (Context
== NULL
|| (Index
% FAT_ENTRIES_PER_PAGE
) == 0)
75 CcMapData(Fcb
->FileObject
, &FileOffset
, sizeof(FAT_DIR_ENTRY
), MAP_WAIT
, &Context
, (PVOID
*)&FatDirEntry
);
77 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
79 _SEH2_YIELD(return TRUE
);
83 FatDirEntry
+= Index
% FAT_ENTRIES_PER_PAGE
;
84 FileOffset
.QuadPart
+= PAGE_SIZE
;
87 if (FAT_ENTRY_END(FatDirEntry
))
93 if (!FAT_ENTRY_DELETED(FatDirEntry
))
105 CcUnpinData(Context
);
113 FATXIsDirectoryEmpty(
116 LARGE_INTEGER FileOffset
;
117 PVOID Context
= NULL
;
118 PFATX_DIR_ENTRY FatXDirEntry
;
119 ULONG Index
= 0, MaxIndex
;
121 FileOffset
.QuadPart
= 0;
122 MaxIndex
= Fcb
->RFCB
.FileSize
.u
.LowPart
/ sizeof(FATX_DIR_ENTRY
);
124 while (Index
< MaxIndex
)
126 if (Context
== NULL
|| (Index
% FATX_ENTRIES_PER_PAGE
) == 0)
130 CcUnpinData(Context
);
135 CcMapData(Fcb
->FileObject
, &FileOffset
, sizeof(FATX_DIR_ENTRY
), MAP_WAIT
, &Context
, (PVOID
*)&FatXDirEntry
);
137 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
139 _SEH2_YIELD(return TRUE
);
143 FatXDirEntry
+= Index
% FATX_ENTRIES_PER_PAGE
;
144 FileOffset
.QuadPart
+= PAGE_SIZE
;
147 if (FATX_ENTRY_END(FatXDirEntry
))
149 CcUnpinData(Context
);
153 if (!FATX_ENTRY_DELETED(FatXDirEntry
))
155 CcUnpinData(Context
);
165 CcUnpinData(Context
);
172 VfatIsDirectoryEmpty(
175 if (BooleanFlagOn(Fcb
->Flags
, FCB_IS_FATX_ENTRY
))
176 return FATXIsDirectoryEmpty(Fcb
);
178 return FATIsDirectoryEmpty(Fcb
);
186 PVFAT_DIRENTRY_CONTEXT DirContext
,
191 LARGE_INTEGER FileOffset
;
192 PFAT_DIR_ENTRY fatDirEntry
;
193 slot
* longNameEntry
;
196 UCHAR CheckSum
, shortCheckSum
;
198 BOOLEAN Valid
= TRUE
;
199 BOOLEAN Back
= FALSE
;
201 DirContext
->LongNameU
.Length
= 0;
202 DirContext
->LongNameU
.Buffer
[0] = UNICODE_NULL
;
204 FileOffset
.u
.HighPart
= 0;
205 FileOffset
.u
.LowPart
= ROUND_DOWN(DirContext
->DirIndex
* sizeof(FAT_DIR_ENTRY
), PAGE_SIZE
);
207 if (*pContext
== NULL
|| (DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
209 if (*pContext
!= NULL
)
211 CcUnpinData(*pContext
);
214 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
217 return STATUS_NO_MORE_ENTRIES
;
222 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
224 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
227 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
232 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
233 longNameEntry
= (slot
*) fatDirEntry
;
238 /* This is the first call to vfatGetNextDirEntry. Possible the start index points
239 * into a long name or points to a short name with an assigned long name.
240 * We must go back to the real start of the entry */
241 while (DirContext
->DirIndex
> 0 &&
242 !FAT_ENTRY_END(fatDirEntry
) &&
243 !FAT_ENTRY_DELETED(fatDirEntry
) &&
244 ((!FAT_ENTRY_LONG(fatDirEntry
) && !Back
) ||
245 (FAT_ENTRY_LONG(fatDirEntry
) && !(longNameEntry
->id
& 0x40))))
247 DirContext
->DirIndex
--;
250 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == FAT_ENTRIES_PER_PAGE
- 1)
252 CcUnpinData(*pContext
);
253 FileOffset
.u
.LowPart
-= PAGE_SIZE
;
255 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
258 return STATUS_NO_MORE_ENTRIES
;
263 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
265 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
268 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
272 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
273 longNameEntry
= (slot
*) fatDirEntry
;
282 if (Back
&& !FAT_ENTRY_END(fatDirEntry
) &&
283 (FAT_ENTRY_DELETED(fatDirEntry
) || !FAT_ENTRY_LONG(fatDirEntry
)))
285 DirContext
->DirIndex
++;
287 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
289 CcUnpinData(*pContext
);
290 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
292 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
295 return STATUS_NO_MORE_ENTRIES
;
300 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
302 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
305 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
309 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
310 longNameEntry
= (slot
*) *pPage
;
320 DirContext
->StartIndex
= DirContext
->DirIndex
;
325 if (FAT_ENTRY_END(fatDirEntry
))
327 CcUnpinData(*pContext
);
329 return STATUS_NO_MORE_ENTRIES
;
332 if (FAT_ENTRY_DELETED(fatDirEntry
))
335 DirContext
->LongNameU
.Buffer
[0] = 0;
336 DirContext
->StartIndex
= DirContext
->DirIndex
+ 1;
340 if (FAT_ENTRY_LONG(fatDirEntry
))
344 DPRINT (" long name entry found at %u\n", DirContext
->DirIndex
);
345 RtlZeroMemory(DirContext
->LongNameU
.Buffer
, DirContext
->LongNameU
.MaximumLength
);
346 CheckSum
= longNameEntry
->alias_checksum
;
350 DPRINT(" name chunk1:[%.*S] chunk2:[%.*S] chunk3:[%.*S]\n",
351 5, longNameEntry
->name0_4
,
352 6, longNameEntry
->name5_10
,
353 2, longNameEntry
->name11_12
);
355 index
= longNameEntry
->id
& 0x3f; // Note: it can be 0 for corrupted FS
357 /* Make sure index is valid and we have enough space in buffer
358 (we count one char for \0) */
360 index
* 13 < DirContext
->LongNameU
.MaximumLength
/ sizeof(WCHAR
))
362 index
--; // make index 0 based
363 dirMap
|= 1 << index
;
365 pName
= DirContext
->LongNameU
.Buffer
+ index
* 13;
366 RtlCopyMemory(pName
, longNameEntry
->name0_4
, 5 * sizeof(WCHAR
));
367 RtlCopyMemory(pName
+ 5, longNameEntry
->name5_10
, 6 * sizeof(WCHAR
));
368 RtlCopyMemory(pName
+ 11, longNameEntry
->name11_12
, 2 * sizeof(WCHAR
));
370 if (longNameEntry
->id
& 0x40)
372 /* It's last LFN entry. Terminate filename with \0 */
373 pName
[13] = UNICODE_NULL
;
377 DPRINT1("Long name entry has invalid index: %x!\n", longNameEntry
->id
);
379 DPRINT (" longName: [%S]\n", DirContext
->LongNameU
.Buffer
);
381 if (CheckSum
!= longNameEntry
->alias_checksum
)
383 DPRINT1("Found wrong alias checksum in long name entry (first %x, current %x, %S)\n",
384 CheckSum
, longNameEntry
->alias_checksum
, DirContext
->LongNameU
.Buffer
);
391 for (i
= 0; i
< 11; i
++)
393 shortCheckSum
= (((shortCheckSum
& 1) << 7)
394 | ((shortCheckSum
& 0xfe) >> 1))
395 + fatDirEntry
->ShortName
[i
];
398 if (shortCheckSum
!= CheckSum
&& DirContext
->LongNameU
.Buffer
[0])
400 DPRINT1("Checksum from long and short name is not equal (short: %x, long: %x, %S)\n",
401 shortCheckSum
, CheckSum
, DirContext
->LongNameU
.Buffer
);
402 DirContext
->LongNameU
.Buffer
[0] = 0;
407 DirContext
->LongNameU
.Buffer
[0] = 0;
410 RtlCopyMemory (&DirContext
->DirEntry
.Fat
, fatDirEntry
, sizeof (FAT_DIR_ENTRY
));
415 DirContext
->DirIndex
++;
417 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
419 CcUnpinData(*pContext
);
420 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
422 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
425 return STATUS_NO_MORE_ENTRIES
;
430 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
432 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
435 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
439 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
440 longNameEntry
= (slot
*) *pPage
;
449 /* Make sure filename is NULL terminate and calculate length */
450 DirContext
->LongNameU
.Buffer
[DirContext
->LongNameU
.MaximumLength
/ sizeof(WCHAR
) - 1]
452 DirContext
->LongNameU
.Length
= wcslen(DirContext
->LongNameU
.Buffer
) * sizeof(WCHAR
);
454 /* Init short name */
455 vfat8Dot3ToString(&DirContext
->DirEntry
.Fat
, &DirContext
->ShortNameU
);
457 /* If we found no LFN, use short name as long */
458 if (DirContext
->LongNameU
.Length
== 0)
459 RtlCopyUnicodeString(&DirContext
->LongNameU
, &DirContext
->ShortNameU
);
461 return STATUS_SUCCESS
;
469 PVFAT_DIRENTRY_CONTEXT DirContext
,
472 LARGE_INTEGER FileOffset
;
473 PFATX_DIR_ENTRY fatxDirEntry
;
475 ULONG DirIndex
= DirContext
->DirIndex
;
477 FileOffset
.u
.HighPart
= 0;
479 UNREFERENCED_PARAMETER(First
);
481 if (!vfatFCBIsRoot(pDirFcb
))
483 /* need to add . and .. entries */
484 switch (DirContext
->DirIndex
)
486 case 0: /* entry . */
487 DirContext
->ShortNameU
.Buffer
[0] = 0;
488 DirContext
->ShortNameU
.Length
= 0;
489 wcscpy(DirContext
->LongNameU
.Buffer
, L
".");
490 DirContext
->LongNameU
.Length
= sizeof(WCHAR
);
491 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
492 DirContext
->DirEntry
.FatX
.Filename
[0] = '.';
493 DirContext
->DirEntry
.FatX
.FilenameLength
= 1;
494 DirContext
->StartIndex
= 0;
495 return STATUS_SUCCESS
;
497 case 1: /* entry .. */
498 DirContext
->ShortNameU
.Buffer
[0] = 0;
499 DirContext
->ShortNameU
.Length
= 0;
500 wcscpy(DirContext
->LongNameU
.Buffer
, L
"..");
501 DirContext
->LongNameU
.Length
= 2 * sizeof(WCHAR
);
502 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
503 DirContext
->DirEntry
.FatX
.Filename
[0] = DirContext
->DirEntry
.FatX
.Filename
[1] = '.';
504 DirContext
->DirEntry
.FatX
.FilenameLength
= 2;
505 DirContext
->StartIndex
= 1;
506 return STATUS_SUCCESS
;
513 if (*pContext
== NULL
|| (DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
515 if (*pContext
!= NULL
)
517 CcUnpinData(*pContext
);
519 FileOffset
.u
.LowPart
= ROUND_DOWN(DirIndex
* sizeof(FATX_DIR_ENTRY
), PAGE_SIZE
);
520 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
523 return STATUS_NO_MORE_ENTRIES
;
528 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
530 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
533 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
538 fatxDirEntry
= (PFATX_DIR_ENTRY
)(*pPage
) + DirIndex
% FATX_ENTRIES_PER_PAGE
;
540 DirContext
->StartIndex
= DirContext
->DirIndex
;
544 if (FATX_ENTRY_END(fatxDirEntry
))
546 CcUnpinData(*pContext
);
548 return STATUS_NO_MORE_ENTRIES
;
551 if (!FATX_ENTRY_DELETED(fatxDirEntry
))
553 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, fatxDirEntry
, sizeof(FATX_DIR_ENTRY
));
556 DirContext
->DirIndex
++;
557 DirContext
->StartIndex
++;
559 if ((DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
561 CcUnpinData(*pContext
);
562 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
563 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
566 return STATUS_NO_MORE_ENTRIES
;
571 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
573 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
576 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
580 fatxDirEntry
= (PFATX_DIR_ENTRY
)*pPage
;
587 DirContext
->ShortNameU
.Buffer
[0] = 0;
588 DirContext
->ShortNameU
.Length
= 0;
589 StringO
.Buffer
= (PCHAR
)fatxDirEntry
->Filename
;
590 StringO
.Length
= StringO
.MaximumLength
= fatxDirEntry
->FilenameLength
;
591 RtlOemStringToUnicodeString(&DirContext
->LongNameU
, &StringO
, FALSE
);
592 return STATUS_SUCCESS
;