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
;
44 PDEVICE_EXTENSION DeviceExt
,
47 LARGE_INTEGER FileOffset
;
49 PFAT_DIR_ENTRY FatDirEntry
;
50 ULONG Index
, MaxIndex
;
53 if (vfatFCBIsRoot(Fcb
))
62 FileOffset
.QuadPart
= 0;
63 MaxIndex
= Fcb
->RFCB
.FileSize
.u
.LowPart
/ sizeof(FAT_DIR_ENTRY
);
65 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, Fcb
);
66 if (!NT_SUCCESS(Status
))
71 while (Index
< MaxIndex
)
73 if (Context
== NULL
|| (Index
% FAT_ENTRIES_PER_PAGE
) == 0)
82 CcMapData(Fcb
->FileObject
, &FileOffset
, sizeof(FAT_DIR_ENTRY
), MAP_WAIT
, &Context
, (PVOID
*)&FatDirEntry
);
84 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
86 _SEH2_YIELD(return TRUE
);
90 FatDirEntry
+= Index
% FAT_ENTRIES_PER_PAGE
;
91 FileOffset
.QuadPart
+= PAGE_SIZE
;
94 if (FAT_ENTRY_END(FatDirEntry
))
100 if (!FAT_ENTRY_DELETED(FatDirEntry
))
102 CcUnpinData(Context
);
112 CcUnpinData(Context
);
119 FATXIsDirectoryEmpty(
120 PDEVICE_EXTENSION DeviceExt
,
123 LARGE_INTEGER FileOffset
;
124 PVOID Context
= NULL
;
125 PFATX_DIR_ENTRY FatXDirEntry
;
126 ULONG Index
= 0, MaxIndex
;
129 FileOffset
.QuadPart
= 0;
130 MaxIndex
= Fcb
->RFCB
.FileSize
.u
.LowPart
/ sizeof(FATX_DIR_ENTRY
);
132 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, Fcb
);
133 if (!NT_SUCCESS(Status
))
138 while (Index
< MaxIndex
)
140 if (Context
== NULL
|| (Index
% FATX_ENTRIES_PER_PAGE
) == 0)
144 CcUnpinData(Context
);
149 CcMapData(Fcb
->FileObject
, &FileOffset
, sizeof(FATX_DIR_ENTRY
), MAP_WAIT
, &Context
, (PVOID
*)&FatXDirEntry
);
151 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
153 _SEH2_YIELD(return TRUE
);
157 FatXDirEntry
+= Index
% FATX_ENTRIES_PER_PAGE
;
158 FileOffset
.QuadPart
+= PAGE_SIZE
;
161 if (FATX_ENTRY_END(FatXDirEntry
))
163 CcUnpinData(Context
);
167 if (!FATX_ENTRY_DELETED(FatXDirEntry
))
169 CcUnpinData(Context
);
179 CcUnpinData(Context
);
190 PVFAT_DIRENTRY_CONTEXT DirContext
,
195 LARGE_INTEGER FileOffset
;
196 PFAT_DIR_ENTRY fatDirEntry
;
197 slot
* longNameEntry
;
200 UCHAR CheckSum
, shortCheckSum
;
202 BOOLEAN Valid
= TRUE
;
203 BOOLEAN Back
= FALSE
;
207 DirContext
->LongNameU
.Length
= 0;
208 DirContext
->LongNameU
.Buffer
[0] = UNICODE_NULL
;
210 FileOffset
.u
.HighPart
= 0;
211 FileOffset
.u
.LowPart
= ROUND_DOWN(DirContext
->DirIndex
* sizeof(FAT_DIR_ENTRY
), PAGE_SIZE
);
213 Status
= vfatFCBInitializeCacheFromVolume(DirContext
->DeviceExt
, pDirFcb
);
214 if (!NT_SUCCESS(Status
))
219 if (*pContext
== NULL
|| (DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
221 if (*pContext
!= NULL
)
223 CcUnpinData(*pContext
);
226 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
229 return STATUS_NO_MORE_ENTRIES
;
234 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
236 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
239 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
244 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
245 longNameEntry
= (slot
*) fatDirEntry
;
250 /* This is the first call to vfatGetNextDirEntry. Possible the start index points
251 * into a long name or points to a short name with an assigned long name.
252 * We must go back to the real start of the entry */
253 while (DirContext
->DirIndex
> 0 &&
254 !FAT_ENTRY_END(fatDirEntry
) &&
255 !FAT_ENTRY_DELETED(fatDirEntry
) &&
256 ((!FAT_ENTRY_LONG(fatDirEntry
) && !Back
) ||
257 (FAT_ENTRY_LONG(fatDirEntry
) && !(longNameEntry
->id
& 0x40))))
259 DirContext
->DirIndex
--;
262 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == FAT_ENTRIES_PER_PAGE
- 1)
264 CcUnpinData(*pContext
);
265 FileOffset
.u
.LowPart
-= PAGE_SIZE
;
267 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
270 return STATUS_NO_MORE_ENTRIES
;
275 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
277 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
280 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
284 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
285 longNameEntry
= (slot
*) fatDirEntry
;
294 if (Back
&& !FAT_ENTRY_END(fatDirEntry
) &&
295 (FAT_ENTRY_DELETED(fatDirEntry
) || !FAT_ENTRY_LONG(fatDirEntry
)))
297 DirContext
->DirIndex
++;
299 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
301 CcUnpinData(*pContext
);
302 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
304 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
307 return STATUS_NO_MORE_ENTRIES
;
312 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
314 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
317 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
321 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
322 longNameEntry
= (slot
*) *pPage
;
332 DirContext
->StartIndex
= DirContext
->DirIndex
;
337 if (FAT_ENTRY_END(fatDirEntry
))
339 CcUnpinData(*pContext
);
341 return STATUS_NO_MORE_ENTRIES
;
344 if (FAT_ENTRY_DELETED(fatDirEntry
))
347 DirContext
->LongNameU
.Buffer
[0] = 0;
348 DirContext
->StartIndex
= DirContext
->DirIndex
+ 1;
352 if (FAT_ENTRY_LONG(fatDirEntry
))
356 DPRINT (" long name entry found at %u\n", DirContext
->DirIndex
);
357 RtlZeroMemory(DirContext
->LongNameU
.Buffer
, DirContext
->LongNameU
.MaximumLength
);
358 CheckSum
= longNameEntry
->alias_checksum
;
362 DPRINT(" name chunk1:[%.*S] chunk2:[%.*S] chunk3:[%.*S]\n",
363 5, longNameEntry
->name0_4
,
364 6, longNameEntry
->name5_10
,
365 2, longNameEntry
->name11_12
);
367 index
= longNameEntry
->id
& 0x3f; // Note: it can be 0 for corrupted FS
369 /* Make sure index is valid and we have enough space in buffer
370 (we count one char for \0) */
372 index
* 13 < DirContext
->LongNameU
.MaximumLength
/ sizeof(WCHAR
))
374 index
--; // make index 0 based
375 dirMap
|= 1 << index
;
377 pName
= DirContext
->LongNameU
.Buffer
+ index
* 13;
378 RtlCopyMemory(pName
, longNameEntry
->name0_4
, 5 * sizeof(WCHAR
));
379 RtlCopyMemory(pName
+ 5, longNameEntry
->name5_10
, 6 * sizeof(WCHAR
));
380 RtlCopyMemory(pName
+ 11, longNameEntry
->name11_12
, 2 * sizeof(WCHAR
));
382 if (longNameEntry
->id
& 0x40)
384 /* It's last LFN entry. Terminate filename with \0 */
385 pName
[13] = UNICODE_NULL
;
389 DPRINT1("Long name entry has invalid index: %x!\n", longNameEntry
->id
);
391 DPRINT (" longName: [%S]\n", DirContext
->LongNameU
.Buffer
);
393 if (CheckSum
!= longNameEntry
->alias_checksum
)
395 DPRINT1("Found wrong alias checksum in long name entry (first %x, current %x, %S)\n",
396 CheckSum
, longNameEntry
->alias_checksum
, DirContext
->LongNameU
.Buffer
);
403 for (i
= 0; i
< 11; i
++)
405 shortCheckSum
= (((shortCheckSum
& 1) << 7)
406 | ((shortCheckSum
& 0xfe) >> 1))
407 + fatDirEntry
->ShortName
[i
];
410 if (shortCheckSum
!= CheckSum
&& DirContext
->LongNameU
.Buffer
[0])
412 DPRINT1("Checksum from long and short name is not equal (short: %x, long: %x, %S)\n",
413 shortCheckSum
, CheckSum
, DirContext
->LongNameU
.Buffer
);
414 DirContext
->LongNameU
.Buffer
[0] = 0;
419 DirContext
->LongNameU
.Buffer
[0] = 0;
422 RtlCopyMemory (&DirContext
->DirEntry
.Fat
, fatDirEntry
, sizeof (FAT_DIR_ENTRY
));
427 DirContext
->DirIndex
++;
429 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
431 CcUnpinData(*pContext
);
432 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
434 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
437 return STATUS_NO_MORE_ENTRIES
;
442 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
444 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
447 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
451 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
452 longNameEntry
= (slot
*) *pPage
;
461 /* Make sure filename is NULL terminate and calculate length */
462 DirContext
->LongNameU
.Buffer
[DirContext
->LongNameU
.MaximumLength
/ sizeof(WCHAR
) - 1]
464 DirContext
->LongNameU
.Length
= wcslen(DirContext
->LongNameU
.Buffer
) * sizeof(WCHAR
);
466 /* Init short name */
467 vfat8Dot3ToString(&DirContext
->DirEntry
.Fat
, &DirContext
->ShortNameU
);
469 /* If we found no LFN, use short name as long */
470 if (DirContext
->LongNameU
.Length
== 0)
471 RtlCopyUnicodeString(&DirContext
->LongNameU
, &DirContext
->ShortNameU
);
473 return STATUS_SUCCESS
;
481 PVFAT_DIRENTRY_CONTEXT DirContext
,
484 LARGE_INTEGER FileOffset
;
485 PFATX_DIR_ENTRY fatxDirEntry
;
487 ULONG DirIndex
= DirContext
->DirIndex
;
490 FileOffset
.u
.HighPart
= 0;
492 UNREFERENCED_PARAMETER(First
);
494 if (!vfatFCBIsRoot(pDirFcb
))
496 /* need to add . and .. entries */
497 switch (DirContext
->DirIndex
)
499 case 0: /* entry . */
500 DirContext
->ShortNameU
.Buffer
[0] = 0;
501 DirContext
->ShortNameU
.Length
= 0;
502 wcscpy(DirContext
->LongNameU
.Buffer
, L
".");
503 DirContext
->LongNameU
.Length
= sizeof(WCHAR
);
504 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
505 DirContext
->DirEntry
.FatX
.Filename
[0] = '.';
506 DirContext
->DirEntry
.FatX
.FilenameLength
= 1;
507 DirContext
->StartIndex
= 0;
508 return STATUS_SUCCESS
;
510 case 1: /* entry .. */
511 DirContext
->ShortNameU
.Buffer
[0] = 0;
512 DirContext
->ShortNameU
.Length
= 0;
513 wcscpy(DirContext
->LongNameU
.Buffer
, L
"..");
514 DirContext
->LongNameU
.Length
= 2 * sizeof(WCHAR
);
515 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
516 DirContext
->DirEntry
.FatX
.Filename
[0] = DirContext
->DirEntry
.FatX
.Filename
[1] = '.';
517 DirContext
->DirEntry
.FatX
.FilenameLength
= 2;
518 DirContext
->StartIndex
= 1;
519 return STATUS_SUCCESS
;
526 Status
= vfatFCBInitializeCacheFromVolume(DirContext
->DeviceExt
, pDirFcb
);
527 if (!NT_SUCCESS(Status
))
532 if (*pContext
== NULL
|| (DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
534 if (*pContext
!= NULL
)
536 CcUnpinData(*pContext
);
538 FileOffset
.u
.LowPart
= ROUND_DOWN(DirIndex
* sizeof(FATX_DIR_ENTRY
), PAGE_SIZE
);
539 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
542 return STATUS_NO_MORE_ENTRIES
;
547 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
549 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
552 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
557 fatxDirEntry
= (PFATX_DIR_ENTRY
)(*pPage
) + DirIndex
% FATX_ENTRIES_PER_PAGE
;
559 DirContext
->StartIndex
= DirContext
->DirIndex
;
563 if (FATX_ENTRY_END(fatxDirEntry
))
565 CcUnpinData(*pContext
);
567 return STATUS_NO_MORE_ENTRIES
;
570 if (!FATX_ENTRY_DELETED(fatxDirEntry
))
572 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, fatxDirEntry
, sizeof(FATX_DIR_ENTRY
));
575 DirContext
->DirIndex
++;
576 DirContext
->StartIndex
++;
578 if ((DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
580 CcUnpinData(*pContext
);
581 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
582 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
)
585 return STATUS_NO_MORE_ENTRIES
;
590 CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, MAP_WAIT
, pContext
, pPage
);
592 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
595 _SEH2_YIELD(return STATUS_NO_MORE_ENTRIES
);
599 fatxDirEntry
= (PFATX_DIR_ENTRY
)*pPage
;
606 DirContext
->ShortNameU
.Buffer
[0] = 0;
607 DirContext
->ShortNameU
.Length
= 0;
608 StringO
.Buffer
= (PCHAR
)fatxDirEntry
->Filename
;
609 StringO
.Length
= StringO
.MaximumLength
= fatxDirEntry
->FilenameLength
;
610 RtlOemStringToUnicodeString(&DirContext
->LongNameU
, &StringO
, FALSE
);
611 return STATUS_SUCCESS
;