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 */
17 vfatDirEntryGetFirstCluster (PDEVICE_EXTENSION pDeviceExt
,
18 PDIR_ENTRY pFatDirEntry
)
22 if (pDeviceExt
->FatInfo
.FatType
== FAT32
)
24 cluster
= pFatDirEntry
->Fat
.FirstCluster
|
25 (pFatDirEntry
->Fat
.FirstClusterHigh
<< 16);
27 else if (pDeviceExt
->Flags
& VCB_IS_FATX
)
29 cluster
= pFatDirEntry
->FatX
.FirstCluster
;
33 cluster
= pFatDirEntry
->Fat
.FirstCluster
;
41 FATIsDirectoryEmpty(PVFATFCB Fcb
)
43 LARGE_INTEGER FileOffset
;
45 PFAT_DIR_ENTRY FatDirEntry
;
46 ULONG Index
, MaxIndex
;
48 if (vfatFCBIsRoot(Fcb
))
57 FileOffset
.QuadPart
= 0;
58 MaxIndex
= Fcb
->RFCB
.FileSize
.u
.LowPart
/ sizeof(FAT_DIR_ENTRY
);
60 while (Index
< MaxIndex
)
62 if (Context
== NULL
|| (Index
% FAT_ENTRIES_PER_PAGE
) == 0)
69 if (!CcMapData(Fcb
->FileObject
, &FileOffset
, sizeof(FAT_DIR_ENTRY
), TRUE
, &Context
, (PVOID
*)&FatDirEntry
))
74 FatDirEntry
+= Index
% FAT_ENTRIES_PER_PAGE
;
75 FileOffset
.QuadPart
+= PAGE_SIZE
;
78 if (FAT_ENTRY_END(FatDirEntry
))
84 if (!FAT_ENTRY_DELETED(FatDirEntry
))
104 FATXIsDirectoryEmpty(PVFATFCB Fcb
)
106 LARGE_INTEGER FileOffset
;
107 PVOID Context
= NULL
;
108 PFATX_DIR_ENTRY FatXDirEntry
;
109 ULONG Index
= 0, MaxIndex
;
111 FileOffset
.QuadPart
= 0;
112 MaxIndex
= Fcb
->RFCB
.FileSize
.u
.LowPart
/ sizeof(FATX_DIR_ENTRY
);
114 while (Index
< MaxIndex
)
116 if (Context
== NULL
|| (Index
% FATX_ENTRIES_PER_PAGE
) == 0)
120 CcUnpinData(Context
);
123 if (!CcMapData(Fcb
->FileObject
, &FileOffset
, sizeof(FATX_DIR_ENTRY
), TRUE
, &Context
, (PVOID
*)&FatXDirEntry
))
128 FatXDirEntry
+= Index
% FATX_ENTRIES_PER_PAGE
;
129 FileOffset
.QuadPart
+= PAGE_SIZE
;
132 if (FATX_ENTRY_END(FatXDirEntry
))
134 CcUnpinData(Context
);
138 if (!FATX_ENTRY_DELETED(FatXDirEntry
))
140 CcUnpinData(Context
);
150 CcUnpinData(Context
);
157 VfatIsDirectoryEmpty(PVFATFCB Fcb
)
159 if (Fcb
->Flags
& FCB_IS_FATX_ENTRY
)
160 return FATXIsDirectoryEmpty(Fcb
);
162 return FATIsDirectoryEmpty(Fcb
);
166 FATGetNextDirEntry(PVOID
*pContext
,
169 PVFAT_DIRENTRY_CONTEXT DirContext
,
174 LARGE_INTEGER FileOffset
;
175 PFAT_DIR_ENTRY fatDirEntry
;
176 slot
* longNameEntry
;
179 UCHAR CheckSum
, shortCheckSum
;
181 BOOLEAN Valid
= TRUE
;
182 BOOLEAN Back
= FALSE
;
184 DirContext
->LongNameU
.Length
= 0;
185 DirContext
->LongNameU
.Buffer
[0] = UNICODE_NULL
;
187 FileOffset
.u
.HighPart
= 0;
188 FileOffset
.u
.LowPart
= ROUND_DOWN(DirContext
->DirIndex
* sizeof(FAT_DIR_ENTRY
), PAGE_SIZE
);
190 if (*pContext
== NULL
|| (DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
192 if (*pContext
!= NULL
)
194 CcUnpinData(*pContext
);
197 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
198 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
201 return STATUS_NO_MORE_ENTRIES
;
205 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
206 longNameEntry
= (slot
*) fatDirEntry
;
211 /* This is the first call to vfatGetNextDirEntry. Possible the start index points
212 * into a long name or points to a short name with an assigned long name.
213 * We must go back to the real start of the entry */
214 while (DirContext
->DirIndex
> 0 &&
215 !FAT_ENTRY_END(fatDirEntry
) &&
216 !FAT_ENTRY_DELETED(fatDirEntry
) &&
217 ((!FAT_ENTRY_LONG(fatDirEntry
) && !Back
) ||
218 (FAT_ENTRY_LONG(fatDirEntry
) && !(longNameEntry
->id
& 0x40))))
220 DirContext
->DirIndex
--;
223 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == FAT_ENTRIES_PER_PAGE
- 1)
225 CcUnpinData(*pContext
);
226 FileOffset
.u
.LowPart
-= PAGE_SIZE
;
228 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
229 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
232 return STATUS_NO_MORE_ENTRIES
;
235 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
236 longNameEntry
= (slot
*) fatDirEntry
;
245 if (Back
&& !FAT_ENTRY_END(fatDirEntry
) &&
246 (FAT_ENTRY_DELETED(fatDirEntry
) || !FAT_ENTRY_LONG(fatDirEntry
)))
248 DirContext
->DirIndex
++;
250 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
252 CcUnpinData(*pContext
);
253 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
255 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
256 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
259 return STATUS_NO_MORE_ENTRIES
;
262 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
263 longNameEntry
= (slot
*) *pPage
;
273 DirContext
->StartIndex
= DirContext
->DirIndex
;
278 if (FAT_ENTRY_END(fatDirEntry
))
280 CcUnpinData(*pContext
);
282 return STATUS_NO_MORE_ENTRIES
;
285 if (FAT_ENTRY_DELETED(fatDirEntry
))
288 DirContext
->LongNameU
.Buffer
[0] = 0;
289 DirContext
->StartIndex
= DirContext
->DirIndex
+ 1;
293 if (FAT_ENTRY_LONG(fatDirEntry
))
297 DPRINT (" long name entry found at %d\n", DirContext
->DirIndex
);
298 RtlZeroMemory(DirContext
->LongNameU
.Buffer
, DirContext
->LongNameU
.MaximumLength
);
299 CheckSum
= longNameEntry
->alias_checksum
;
303 DPRINT(" name chunk1:[%.*S] chunk2:[%.*S] chunk3:[%.*S]\n",
304 5, longNameEntry
->name0_4
,
305 6, longNameEntry
->name5_10
,
306 2, longNameEntry
->name11_12
);
308 index
= longNameEntry
->id
& 0x3f; // Note: it can be 0 for corrupted FS
310 /* Make sure index is valid and we have enaugh space in buffer
311 (we count one char for \0) */
313 index
* 13 < DirContext
->LongNameU
.MaximumLength
/ sizeof(WCHAR
))
315 index
--; // make index 0 based
316 dirMap
|= 1 << index
;
318 pName
= DirContext
->LongNameU
.Buffer
+ index
* 13;
319 RtlCopyMemory(pName
, longNameEntry
->name0_4
, 5 * sizeof(WCHAR
));
320 RtlCopyMemory(pName
+ 5, longNameEntry
->name5_10
, 6 * sizeof(WCHAR
));
321 RtlCopyMemory(pName
+ 11, longNameEntry
->name11_12
, 2 * sizeof(WCHAR
));
323 if (longNameEntry
->id
& 0x40)
325 /* It's last LFN entry. Terminate filename with \0 */
326 pName
[13] = UNICODE_NULL
;
330 DPRINT1("Long name entry has invalid index: %x!\n", longNameEntry
->id
);
332 DPRINT (" longName: [%S]\n", DirContext
->LongNameU
.Buffer
);
334 if (CheckSum
!= longNameEntry
->alias_checksum
)
336 DPRINT1("Found wrong alias checksum in long name entry (first %x, current %x, %S)\n",
337 CheckSum
, longNameEntry
->alias_checksum
, DirContext
->LongNameU
.Buffer
);
344 for (i
= 0; i
< 11; i
++)
346 shortCheckSum
= (((shortCheckSum
& 1) << 7)
347 | ((shortCheckSum
& 0xfe) >> 1))
348 + fatDirEntry
->ShortName
[i
];
351 if (shortCheckSum
!= CheckSum
&& DirContext
->LongNameU
.Buffer
[0])
353 DPRINT1("Checksum from long and short name is not equal (short: %x, long: %x, %S)\n",
354 shortCheckSum
, CheckSum
, DirContext
->LongNameU
.Buffer
);
355 DirContext
->LongNameU
.Buffer
[0] = 0;
360 DirContext
->LongNameU
.Buffer
[0] = 0;
363 RtlCopyMemory (&DirContext
->DirEntry
.Fat
, fatDirEntry
, sizeof (FAT_DIR_ENTRY
));
368 DirContext
->DirIndex
++;
370 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
372 CcUnpinData(*pContext
);
373 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
375 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
376 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
379 return STATUS_NO_MORE_ENTRIES
;
382 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
383 longNameEntry
= (slot
*) *pPage
;
392 /* Make sure filename is NULL terminate and calculate length */
393 DirContext
->LongNameU
.Buffer
[DirContext
->LongNameU
.MaximumLength
/ sizeof(WCHAR
) - 1]
395 DirContext
->LongNameU
.Length
= wcslen(DirContext
->LongNameU
.Buffer
) * sizeof(WCHAR
);
397 /* Init short name */
398 vfat8Dot3ToString(&DirContext
->DirEntry
.Fat
, &DirContext
->ShortNameU
);
400 /* If we found no LFN, use short name as long */
401 if (DirContext
->LongNameU
.Length
== 0)
402 RtlCopyUnicodeString(&DirContext
->LongNameU
, &DirContext
->ShortNameU
);
404 return STATUS_SUCCESS
;
407 NTSTATUS
FATXGetNextDirEntry(PVOID
* pContext
,
410 PVFAT_DIRENTRY_CONTEXT DirContext
,
413 LARGE_INTEGER FileOffset
;
414 PFATX_DIR_ENTRY fatxDirEntry
;
416 ULONG DirIndex
= DirContext
->DirIndex
;
418 FileOffset
.u
.HighPart
= 0;
420 if (!vfatFCBIsRoot(pDirFcb
))
422 /* need to add . and .. entries */
423 switch (DirContext
->DirIndex
)
425 case 0: /* entry . */
427 DirContext
->ShortNameU
.Buffer
[0] = 0;
428 DirContext
->ShortNameU
.Length
= 0;
429 wcscpy(DirContext
->LongNameU
.Buffer
, L
".");
430 DirContext
->LongNameU
.Length
= sizeof(WCHAR
);
431 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
432 DirContext
->DirEntry
.FatX
.Filename
[0] = '.';
433 DirContext
->DirEntry
.FatX
.FilenameLength
= 1;
434 DirContext
->StartIndex
= 0;
435 return STATUS_SUCCESS
;
437 case 1: /* entry .. */
439 DirContext
->ShortNameU
.Buffer
[0] = 0;
440 DirContext
->ShortNameU
.Length
= 0;
441 wcscpy(DirContext
->LongNameU
.Buffer
, L
"..");
442 DirContext
->LongNameU
.Length
= 2 * sizeof(WCHAR
);
443 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
444 DirContext
->DirEntry
.FatX
.Filename
[0] = DirContext
->DirEntry
.FatX
.Filename
[1] = '.';
445 DirContext
->DirEntry
.FatX
.FilenameLength
= 2;
446 DirContext
->StartIndex
= 1;
447 return STATUS_SUCCESS
;
454 if (*pContext
== NULL
|| (DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
456 if (*pContext
!= NULL
)
458 CcUnpinData(*pContext
);
460 FileOffset
.u
.LowPart
= ROUND_DOWN(DirIndex
* sizeof(FATX_DIR_ENTRY
), PAGE_SIZE
);
461 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
462 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
465 return STATUS_NO_MORE_ENTRIES
;
469 fatxDirEntry
= (PFATX_DIR_ENTRY
)(*pPage
) + DirIndex
% FATX_ENTRIES_PER_PAGE
;
471 DirContext
->StartIndex
= DirContext
->DirIndex
;
475 if (FATX_ENTRY_END(fatxDirEntry
))
477 CcUnpinData(*pContext
);
479 return STATUS_NO_MORE_ENTRIES
;
482 if (!FATX_ENTRY_DELETED(fatxDirEntry
))
484 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, fatxDirEntry
, sizeof(FATX_DIR_ENTRY
));
487 DirContext
->DirIndex
++;
488 DirContext
->StartIndex
++;
490 if ((DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
492 CcUnpinData(*pContext
);
493 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
494 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
495 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
498 return STATUS_NO_MORE_ENTRIES
;
500 fatxDirEntry
= (PFATX_DIR_ENTRY
)*pPage
;
507 DirContext
->ShortNameU
.Buffer
[0] = 0;
508 DirContext
->ShortNameU
.Length
= 0;
509 StringO
.Buffer
= (PCHAR
)fatxDirEntry
->Filename
;
510 StringO
.Length
= StringO
.MaximumLength
= fatxDirEntry
->FilenameLength
;
511 RtlOemStringToUnicodeString(&DirContext
->LongNameU
, &StringO
, FALSE
);
512 return STATUS_SUCCESS
;