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 (vfatVolumeIsFatX(pDeviceExt
))
32 cluster
= pFatDirEntry
->FatX
.FirstCluster
;
36 cluster
= pFatDirEntry
->Fat
.FirstCluster
;
46 LARGE_INTEGER FileOffset
;
48 PFAT_DIR_ENTRY FatDirEntry
;
49 ULONG Index
, MaxIndex
;
51 if (vfatFCBIsRoot(Fcb
))
60 FileOffset
.QuadPart
= 0;
61 MaxIndex
= Fcb
->RFCB
.FileSize
.u
.LowPart
/ sizeof(FAT_DIR_ENTRY
);
63 while (Index
< MaxIndex
)
65 if (Context
== NULL
|| (Index
% FAT_ENTRIES_PER_PAGE
) == 0)
74 CcMapData(Fcb
->FileObject
, &FileOffset
, sizeof(FAT_DIR_ENTRY
), MAP_WAIT
, &Context
, (PVOID
*)&FatDirEntry
);
76 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
78 _SEH2_YIELD(return TRUE
);
82 FatDirEntry
+= Index
% FAT_ENTRIES_PER_PAGE
;
83 FileOffset
.QuadPart
+= PAGE_SIZE
;
86 if (FAT_ENTRY_END(FatDirEntry
))
92 if (!FAT_ENTRY_DELETED(FatDirEntry
))
104 CcUnpinData(Context
);
111 FATXIsDirectoryEmpty(
114 LARGE_INTEGER FileOffset
;
115 PVOID Context
= NULL
;
116 PFATX_DIR_ENTRY FatXDirEntry
;
117 ULONG Index
= 0, MaxIndex
;
119 FileOffset
.QuadPart
= 0;
120 MaxIndex
= Fcb
->RFCB
.FileSize
.u
.LowPart
/ sizeof(FATX_DIR_ENTRY
);
122 while (Index
< MaxIndex
)
124 if (Context
== NULL
|| (Index
% FATX_ENTRIES_PER_PAGE
) == 0)
128 CcUnpinData(Context
);
133 CcMapData(Fcb
->FileObject
, &FileOffset
, sizeof(FATX_DIR_ENTRY
), MAP_WAIT
, &Context
, (PVOID
*)&FatXDirEntry
);
135 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
137 _SEH2_YIELD(return TRUE
);
141 FatXDirEntry
+= Index
% FATX_ENTRIES_PER_PAGE
;
142 FileOffset
.QuadPart
+= PAGE_SIZE
;
145 if (FATX_ENTRY_END(FatXDirEntry
))
147 CcUnpinData(Context
);
151 if (!FATX_ENTRY_DELETED(FatXDirEntry
))
153 CcUnpinData(Context
);
163 CcUnpinData(Context
);
174 PVFAT_DIRENTRY_CONTEXT DirContext
,
179 LARGE_INTEGER FileOffset
;
180 PFAT_DIR_ENTRY fatDirEntry
;
181 slot
* longNameEntry
;
184 UCHAR CheckSum
, shortCheckSum
;
186 BOOLEAN Valid
= TRUE
;
187 BOOLEAN Back
= FALSE
;
189 DirContext
->LongNameU
.Length
= 0;
190 DirContext
->LongNameU
.Buffer
[0] = UNICODE_NULL
;
192 FileOffset
.u
.HighPart
= 0;
193 FileOffset
.u
.LowPart
= ROUND_DOWN(DirContext
->DirIndex
* sizeof(FAT_DIR_ENTRY
), PAGE_SIZE
);
195 if (*pContext
== NULL
|| (DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
197 if (*pContext
!= NULL
)
199 CcUnpinData(*pContext
);
202 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
205 return STATUS_NO_MORE_ENTRIES
;
210 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
212 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
215 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
220 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
221 longNameEntry
= (slot
*) fatDirEntry
;
226 /* This is the first call to vfatGetNextDirEntry. Possible the start index points
227 * into a long name or points to a short name with an assigned long name.
228 * We must go back to the real start of the entry */
229 while (DirContext
->DirIndex
> 0 &&
230 !FAT_ENTRY_END(fatDirEntry
) &&
231 !FAT_ENTRY_DELETED(fatDirEntry
) &&
232 ((!FAT_ENTRY_LONG(fatDirEntry
) && !Back
) ||
233 (FAT_ENTRY_LONG(fatDirEntry
) && !(longNameEntry
->id
& 0x40))))
235 DirContext
->DirIndex
--;
238 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == FAT_ENTRIES_PER_PAGE
- 1)
240 CcUnpinData(*pContext
);
241 FileOffset
.u
.LowPart
-= PAGE_SIZE
;
243 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
246 return STATUS_NO_MORE_ENTRIES
;
251 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
253 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
256 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
260 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
261 longNameEntry
= (slot
*) fatDirEntry
;
270 if (Back
&& !FAT_ENTRY_END(fatDirEntry
) &&
271 (FAT_ENTRY_DELETED(fatDirEntry
) || !FAT_ENTRY_LONG(fatDirEntry
)))
273 DirContext
->DirIndex
++;
275 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
277 CcUnpinData(*pContext
);
278 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
280 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
283 return STATUS_NO_MORE_ENTRIES
;
288 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
290 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
293 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
297 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
298 longNameEntry
= (slot
*) *pPage
;
308 DirContext
->StartIndex
= DirContext
->DirIndex
;
313 if (FAT_ENTRY_END(fatDirEntry
))
315 CcUnpinData(*pContext
);
317 return STATUS_NO_MORE_ENTRIES
;
320 if (FAT_ENTRY_DELETED(fatDirEntry
))
323 DirContext
->LongNameU
.Buffer
[0] = 0;
324 DirContext
->StartIndex
= DirContext
->DirIndex
+ 1;
328 if (FAT_ENTRY_LONG(fatDirEntry
))
332 DPRINT (" long name entry found at %u\n", DirContext
->DirIndex
);
333 RtlZeroMemory(DirContext
->LongNameU
.Buffer
, DirContext
->LongNameU
.MaximumLength
);
334 CheckSum
= longNameEntry
->alias_checksum
;
338 DPRINT(" name chunk1:[%.*S] chunk2:[%.*S] chunk3:[%.*S]\n",
339 5, longNameEntry
->name0_4
,
340 6, longNameEntry
->name5_10
,
341 2, longNameEntry
->name11_12
);
343 index
= longNameEntry
->id
& 0x3f; // Note: it can be 0 for corrupted FS
345 /* Make sure index is valid and we have enough space in buffer
346 (we count one char for \0) */
348 index
* 13 < DirContext
->LongNameU
.MaximumLength
/ sizeof(WCHAR
))
350 index
--; // make index 0 based
351 dirMap
|= 1 << index
;
353 pName
= DirContext
->LongNameU
.Buffer
+ index
* 13;
354 RtlCopyMemory(pName
, longNameEntry
->name0_4
, 5 * sizeof(WCHAR
));
355 RtlCopyMemory(pName
+ 5, longNameEntry
->name5_10
, 6 * sizeof(WCHAR
));
356 RtlCopyMemory(pName
+ 11, longNameEntry
->name11_12
, 2 * sizeof(WCHAR
));
358 if (longNameEntry
->id
& 0x40)
360 /* It's last LFN entry. Terminate filename with \0 */
361 pName
[13] = UNICODE_NULL
;
365 DPRINT1("Long name entry has invalid index: %x!\n", longNameEntry
->id
);
367 DPRINT (" longName: [%S]\n", DirContext
->LongNameU
.Buffer
);
369 if (CheckSum
!= longNameEntry
->alias_checksum
)
371 DPRINT1("Found wrong alias checksum in long name entry (first %x, current %x, %S)\n",
372 CheckSum
, longNameEntry
->alias_checksum
, DirContext
->LongNameU
.Buffer
);
379 for (i
= 0; i
< 11; i
++)
381 shortCheckSum
= (((shortCheckSum
& 1) << 7)
382 | ((shortCheckSum
& 0xfe) >> 1))
383 + fatDirEntry
->ShortName
[i
];
386 if (shortCheckSum
!= CheckSum
&& DirContext
->LongNameU
.Buffer
[0])
388 DPRINT1("Checksum from long and short name is not equal (short: %x, long: %x, %S)\n",
389 shortCheckSum
, CheckSum
, DirContext
->LongNameU
.Buffer
);
390 DirContext
->LongNameU
.Buffer
[0] = 0;
395 DirContext
->LongNameU
.Buffer
[0] = 0;
398 RtlCopyMemory (&DirContext
->DirEntry
.Fat
, fatDirEntry
, sizeof (FAT_DIR_ENTRY
));
403 DirContext
->DirIndex
++;
405 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
407 CcUnpinData(*pContext
);
408 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
410 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
413 return STATUS_NO_MORE_ENTRIES
;
418 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
420 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
423 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
427 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
428 longNameEntry
= (slot
*) *pPage
;
437 /* Make sure filename is NULL terminate and calculate length */
438 DirContext
->LongNameU
.Buffer
[DirContext
->LongNameU
.MaximumLength
/ sizeof(WCHAR
) - 1]
440 DirContext
->LongNameU
.Length
= wcslen(DirContext
->LongNameU
.Buffer
) * sizeof(WCHAR
);
442 /* Init short name */
443 vfat8Dot3ToString(&DirContext
->DirEntry
.Fat
, &DirContext
->ShortNameU
);
445 /* If we found no LFN, use short name as long */
446 if (DirContext
->LongNameU
.Length
== 0)
447 RtlCopyUnicodeString(&DirContext
->LongNameU
, &DirContext
->ShortNameU
);
449 return STATUS_SUCCESS
;
457 PVFAT_DIRENTRY_CONTEXT DirContext
,
460 LARGE_INTEGER FileOffset
;
461 PFATX_DIR_ENTRY fatxDirEntry
;
463 ULONG DirIndex
= DirContext
->DirIndex
;
465 FileOffset
.u
.HighPart
= 0;
467 UNREFERENCED_PARAMETER(First
);
469 if (!vfatFCBIsRoot(pDirFcb
))
471 /* need to add . and .. entries */
472 switch (DirContext
->DirIndex
)
474 case 0: /* entry . */
475 DirContext
->ShortNameU
.Buffer
[0] = 0;
476 DirContext
->ShortNameU
.Length
= 0;
477 wcscpy(DirContext
->LongNameU
.Buffer
, L
".");
478 DirContext
->LongNameU
.Length
= sizeof(WCHAR
);
479 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
480 DirContext
->DirEntry
.FatX
.Filename
[0] = '.';
481 DirContext
->DirEntry
.FatX
.FilenameLength
= 1;
482 DirContext
->StartIndex
= 0;
483 return STATUS_SUCCESS
;
485 case 1: /* entry .. */
486 DirContext
->ShortNameU
.Buffer
[0] = 0;
487 DirContext
->ShortNameU
.Length
= 0;
488 wcscpy(DirContext
->LongNameU
.Buffer
, L
"..");
489 DirContext
->LongNameU
.Length
= 2 * sizeof(WCHAR
);
490 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
491 DirContext
->DirEntry
.FatX
.Filename
[0] = DirContext
->DirEntry
.FatX
.Filename
[1] = '.';
492 DirContext
->DirEntry
.FatX
.FilenameLength
= 2;
493 DirContext
->StartIndex
= 1;
494 return STATUS_SUCCESS
;
501 if (*pContext
== NULL
|| (DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
503 if (*pContext
!= NULL
)
505 CcUnpinData(*pContext
);
507 FileOffset
.u
.LowPart
= ROUND_DOWN(DirIndex
* sizeof(FATX_DIR_ENTRY
), PAGE_SIZE
);
508 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
511 return STATUS_NO_MORE_ENTRIES
;
516 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
518 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
521 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
526 fatxDirEntry
= (PFATX_DIR_ENTRY
)(*pPage
) + DirIndex
% FATX_ENTRIES_PER_PAGE
;
528 DirContext
->StartIndex
= DirContext
->DirIndex
;
532 if (FATX_ENTRY_END(fatxDirEntry
))
534 CcUnpinData(*pContext
);
536 return STATUS_NO_MORE_ENTRIES
;
539 if (!FATX_ENTRY_DELETED(fatxDirEntry
))
541 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, fatxDirEntry
, sizeof(FATX_DIR_ENTRY
));
544 DirContext
->DirIndex
++;
545 DirContext
->StartIndex
++;
547 if ((DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
549 CcUnpinData(*pContext
);
550 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
551 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
554 return STATUS_NO_MORE_ENTRIES
;
559 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
561 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
564 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
568 fatxDirEntry
= (PFATX_DIR_ENTRY
)*pPage
;
575 DirContext
->ShortNameU
.Buffer
[0] = 0;
576 DirContext
->ShortNameU
.Length
= 0;
577 StringO
.Buffer
= (PCHAR
)fatxDirEntry
->Filename
;
578 StringO
.Length
= StringO
.MaximumLength
= fatxDirEntry
->FilenameLength
;
579 RtlOemStringToUnicodeString(&DirContext
->LongNameU
, &StringO
, FALSE
);
580 return STATUS_SUCCESS
;