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 (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)
73 if (!CcMapData(Fcb
->FileObject
, &FileOffset
, sizeof(FAT_DIR_ENTRY
), TRUE
, &Context
, (PVOID
*)&FatDirEntry
))
78 FatDirEntry
+= Index
% FAT_ENTRIES_PER_PAGE
;
79 FileOffset
.QuadPart
+= PAGE_SIZE
;
82 if (FAT_ENTRY_END(FatDirEntry
))
88 if (!FAT_ENTRY_DELETED(FatDirEntry
))
100 CcUnpinData(Context
);
108 FATXIsDirectoryEmpty(
111 LARGE_INTEGER FileOffset
;
112 PVOID Context
= NULL
;
113 PFATX_DIR_ENTRY FatXDirEntry
;
114 ULONG Index
= 0, MaxIndex
;
116 FileOffset
.QuadPart
= 0;
117 MaxIndex
= Fcb
->RFCB
.FileSize
.u
.LowPart
/ sizeof(FATX_DIR_ENTRY
);
119 while (Index
< MaxIndex
)
121 if (Context
== NULL
|| (Index
% FATX_ENTRIES_PER_PAGE
) == 0)
125 CcUnpinData(Context
);
128 if (!CcMapData(Fcb
->FileObject
, &FileOffset
, sizeof(FATX_DIR_ENTRY
), TRUE
, &Context
, (PVOID
*)&FatXDirEntry
))
133 FatXDirEntry
+= Index
% FATX_ENTRIES_PER_PAGE
;
134 FileOffset
.QuadPart
+= PAGE_SIZE
;
137 if (FATX_ENTRY_END(FatXDirEntry
))
139 CcUnpinData(Context
);
143 if (!FATX_ENTRY_DELETED(FatXDirEntry
))
145 CcUnpinData(Context
);
155 CcUnpinData(Context
);
162 VfatIsDirectoryEmpty(
165 if (Fcb
->Flags
& FCB_IS_FATX_ENTRY
)
166 return FATXIsDirectoryEmpty(Fcb
);
168 return FATIsDirectoryEmpty(Fcb
);
176 PVFAT_DIRENTRY_CONTEXT DirContext
,
181 LARGE_INTEGER FileOffset
;
182 PFAT_DIR_ENTRY fatDirEntry
;
183 slot
* longNameEntry
;
186 UCHAR CheckSum
, shortCheckSum
;
188 BOOLEAN Valid
= TRUE
;
189 BOOLEAN Back
= FALSE
;
191 DirContext
->LongNameU
.Length
= 0;
192 DirContext
->LongNameU
.Buffer
[0] = UNICODE_NULL
;
194 FileOffset
.u
.HighPart
= 0;
195 FileOffset
.u
.LowPart
= ROUND_DOWN(DirContext
->DirIndex
* sizeof(FAT_DIR_ENTRY
), PAGE_SIZE
);
197 if (*pContext
== NULL
|| (DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
199 if (*pContext
!= NULL
)
201 CcUnpinData(*pContext
);
204 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
205 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
208 return STATUS_NO_MORE_ENTRIES
;
212 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
213 longNameEntry
= (slot
*) fatDirEntry
;
218 /* This is the first call to vfatGetNextDirEntry. Possible the start index points
219 * into a long name or points to a short name with an assigned long name.
220 * We must go back to the real start of the entry */
221 while (DirContext
->DirIndex
> 0 &&
222 !FAT_ENTRY_END(fatDirEntry
) &&
223 !FAT_ENTRY_DELETED(fatDirEntry
) &&
224 ((!FAT_ENTRY_LONG(fatDirEntry
) && !Back
) ||
225 (FAT_ENTRY_LONG(fatDirEntry
) && !(longNameEntry
->id
& 0x40))))
227 DirContext
->DirIndex
--;
230 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == FAT_ENTRIES_PER_PAGE
- 1)
232 CcUnpinData(*pContext
);
233 FileOffset
.u
.LowPart
-= PAGE_SIZE
;
235 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
236 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
239 return STATUS_NO_MORE_ENTRIES
;
242 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
243 longNameEntry
= (slot
*) fatDirEntry
;
252 if (Back
&& !FAT_ENTRY_END(fatDirEntry
) &&
253 (FAT_ENTRY_DELETED(fatDirEntry
) || !FAT_ENTRY_LONG(fatDirEntry
)))
255 DirContext
->DirIndex
++;
257 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
259 CcUnpinData(*pContext
);
260 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
262 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
263 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
266 return STATUS_NO_MORE_ENTRIES
;
269 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
270 longNameEntry
= (slot
*) *pPage
;
280 DirContext
->StartIndex
= DirContext
->DirIndex
;
285 if (FAT_ENTRY_END(fatDirEntry
))
287 CcUnpinData(*pContext
);
289 return STATUS_NO_MORE_ENTRIES
;
292 if (FAT_ENTRY_DELETED(fatDirEntry
))
295 DirContext
->LongNameU
.Buffer
[0] = 0;
296 DirContext
->StartIndex
= DirContext
->DirIndex
+ 1;
300 if (FAT_ENTRY_LONG(fatDirEntry
))
304 DPRINT (" long name entry found at %u\n", DirContext
->DirIndex
);
305 RtlZeroMemory(DirContext
->LongNameU
.Buffer
, DirContext
->LongNameU
.MaximumLength
);
306 CheckSum
= longNameEntry
->alias_checksum
;
310 DPRINT(" name chunk1:[%.*S] chunk2:[%.*S] chunk3:[%.*S]\n",
311 5, longNameEntry
->name0_4
,
312 6, longNameEntry
->name5_10
,
313 2, longNameEntry
->name11_12
);
315 index
= longNameEntry
->id
& 0x3f; // Note: it can be 0 for corrupted FS
317 /* Make sure index is valid and we have enaugh space in buffer
318 (we count one char for \0) */
320 index
* 13 < DirContext
->LongNameU
.MaximumLength
/ sizeof(WCHAR
))
322 index
--; // make index 0 based
323 dirMap
|= 1 << index
;
325 pName
= DirContext
->LongNameU
.Buffer
+ index
* 13;
326 RtlCopyMemory(pName
, longNameEntry
->name0_4
, 5 * sizeof(WCHAR
));
327 RtlCopyMemory(pName
+ 5, longNameEntry
->name5_10
, 6 * sizeof(WCHAR
));
328 RtlCopyMemory(pName
+ 11, longNameEntry
->name11_12
, 2 * sizeof(WCHAR
));
330 if (longNameEntry
->id
& 0x40)
332 /* It's last LFN entry. Terminate filename with \0 */
333 pName
[13] = UNICODE_NULL
;
337 DPRINT1("Long name entry has invalid index: %x!\n", longNameEntry
->id
);
339 DPRINT (" longName: [%S]\n", DirContext
->LongNameU
.Buffer
);
341 if (CheckSum
!= longNameEntry
->alias_checksum
)
343 DPRINT1("Found wrong alias checksum in long name entry (first %x, current %x, %S)\n",
344 CheckSum
, longNameEntry
->alias_checksum
, DirContext
->LongNameU
.Buffer
);
351 for (i
= 0; i
< 11; i
++)
353 shortCheckSum
= (((shortCheckSum
& 1) << 7)
354 | ((shortCheckSum
& 0xfe) >> 1))
355 + fatDirEntry
->ShortName
[i
];
358 if (shortCheckSum
!= CheckSum
&& DirContext
->LongNameU
.Buffer
[0])
360 DPRINT1("Checksum from long and short name is not equal (short: %x, long: %x, %S)\n",
361 shortCheckSum
, CheckSum
, DirContext
->LongNameU
.Buffer
);
362 DirContext
->LongNameU
.Buffer
[0] = 0;
367 DirContext
->LongNameU
.Buffer
[0] = 0;
370 RtlCopyMemory (&DirContext
->DirEntry
.Fat
, fatDirEntry
, sizeof (FAT_DIR_ENTRY
));
375 DirContext
->DirIndex
++;
377 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
379 CcUnpinData(*pContext
);
380 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
382 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
383 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
386 return STATUS_NO_MORE_ENTRIES
;
389 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
390 longNameEntry
= (slot
*) *pPage
;
399 /* Make sure filename is NULL terminate and calculate length */
400 DirContext
->LongNameU
.Buffer
[DirContext
->LongNameU
.MaximumLength
/ sizeof(WCHAR
) - 1]
402 DirContext
->LongNameU
.Length
= wcslen(DirContext
->LongNameU
.Buffer
) * sizeof(WCHAR
);
404 /* Init short name */
405 vfat8Dot3ToString(&DirContext
->DirEntry
.Fat
, &DirContext
->ShortNameU
);
407 /* If we found no LFN, use short name as long */
408 if (DirContext
->LongNameU
.Length
== 0)
409 RtlCopyUnicodeString(&DirContext
->LongNameU
, &DirContext
->ShortNameU
);
411 return STATUS_SUCCESS
;
419 PVFAT_DIRENTRY_CONTEXT DirContext
,
422 LARGE_INTEGER FileOffset
;
423 PFATX_DIR_ENTRY fatxDirEntry
;
425 ULONG DirIndex
= DirContext
->DirIndex
;
427 FileOffset
.u
.HighPart
= 0;
429 UNREFERENCED_PARAMETER(First
);
431 if (!vfatFCBIsRoot(pDirFcb
))
433 /* need to add . and .. entries */
434 switch (DirContext
->DirIndex
)
436 case 0: /* entry . */
437 DirContext
->ShortNameU
.Buffer
[0] = 0;
438 DirContext
->ShortNameU
.Length
= 0;
439 wcscpy(DirContext
->LongNameU
.Buffer
, L
".");
440 DirContext
->LongNameU
.Length
= sizeof(WCHAR
);
441 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
442 DirContext
->DirEntry
.FatX
.Filename
[0] = '.';
443 DirContext
->DirEntry
.FatX
.FilenameLength
= 1;
444 DirContext
->StartIndex
= 0;
445 return STATUS_SUCCESS
;
447 case 1: /* entry .. */
448 DirContext
->ShortNameU
.Buffer
[0] = 0;
449 DirContext
->ShortNameU
.Length
= 0;
450 wcscpy(DirContext
->LongNameU
.Buffer
, L
"..");
451 DirContext
->LongNameU
.Length
= 2 * sizeof(WCHAR
);
452 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
453 DirContext
->DirEntry
.FatX
.Filename
[0] = DirContext
->DirEntry
.FatX
.Filename
[1] = '.';
454 DirContext
->DirEntry
.FatX
.FilenameLength
= 2;
455 DirContext
->StartIndex
= 1;
456 return STATUS_SUCCESS
;
463 if (*pContext
== NULL
|| (DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
465 if (*pContext
!= NULL
)
467 CcUnpinData(*pContext
);
469 FileOffset
.u
.LowPart
= ROUND_DOWN(DirIndex
* sizeof(FATX_DIR_ENTRY
), PAGE_SIZE
);
470 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
471 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
474 return STATUS_NO_MORE_ENTRIES
;
478 fatxDirEntry
= (PFATX_DIR_ENTRY
)(*pPage
) + DirIndex
% FATX_ENTRIES_PER_PAGE
;
480 DirContext
->StartIndex
= DirContext
->DirIndex
;
484 if (FATX_ENTRY_END(fatxDirEntry
))
486 CcUnpinData(*pContext
);
488 return STATUS_NO_MORE_ENTRIES
;
491 if (!FATX_ENTRY_DELETED(fatxDirEntry
))
493 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, fatxDirEntry
, sizeof(FATX_DIR_ENTRY
));
496 DirContext
->DirIndex
++;
497 DirContext
->StartIndex
++;
499 if ((DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
501 CcUnpinData(*pContext
);
502 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
503 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
504 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
507 return STATUS_NO_MORE_ENTRIES
;
509 fatxDirEntry
= (PFATX_DIR_ENTRY
)*pPage
;
516 DirContext
->ShortNameU
.Buffer
[0] = 0;
517 DirContext
->ShortNameU
.Length
= 0;
518 StringO
.Buffer
= (PCHAR
)fatxDirEntry
->Filename
;
519 StringO
.Length
= StringO
.MaximumLength
= fatxDirEntry
->FilenameLength
;
520 RtlOemStringToUnicodeString(&DirContext
->LongNameU
, &StringO
, FALSE
);
521 return STATUS_SUCCESS
;