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
, PAGE_SIZE
, 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
, PAGE_SIZE
, 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
.Buffer
[0] = 0;
186 FileOffset
.u
.HighPart
= 0;
187 FileOffset
.u
.LowPart
= ROUND_DOWN(DirContext
->DirIndex
* sizeof(FAT_DIR_ENTRY
), PAGE_SIZE
);
189 if (*pContext
== NULL
|| (DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
191 if (*pContext
!= NULL
)
193 CcUnpinData(*pContext
);
196 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
197 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
200 return STATUS_NO_MORE_ENTRIES
;
204 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
205 longNameEntry
= (slot
*) fatDirEntry
;
210 /* This is the first call to vfatGetNextDirEntry. Possible the start index points
211 * into a long name or points to a short name with an assigned long name.
212 * We must go back to the real start of the entry */
213 while (DirContext
->DirIndex
> 0 &&
214 !FAT_ENTRY_END(fatDirEntry
) &&
215 !FAT_ENTRY_DELETED(fatDirEntry
) &&
216 ((!FAT_ENTRY_LONG(fatDirEntry
) && !Back
) ||
217 (FAT_ENTRY_LONG(fatDirEntry
) && !(longNameEntry
->id
& 0x40))))
219 DirContext
->DirIndex
--;
222 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == FAT_ENTRIES_PER_PAGE
- 1)
224 CcUnpinData(*pContext
);
225 FileOffset
.u
.LowPart
-= PAGE_SIZE
;
227 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
228 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
231 return STATUS_NO_MORE_ENTRIES
;
234 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
235 longNameEntry
= (slot
*) fatDirEntry
;
244 if (Back
&& !FAT_ENTRY_END(fatDirEntry
) &&
245 (FAT_ENTRY_DELETED(fatDirEntry
) || !FAT_ENTRY_LONG(fatDirEntry
)))
247 DirContext
->DirIndex
++;
249 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
251 CcUnpinData(*pContext
);
252 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
254 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
255 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
258 return STATUS_NO_MORE_ENTRIES
;
261 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
262 longNameEntry
= (slot
*) *pPage
;
272 DirContext
->StartIndex
= DirContext
->DirIndex
;
277 if (FAT_ENTRY_END(fatDirEntry
))
279 CcUnpinData(*pContext
);
281 return STATUS_NO_MORE_ENTRIES
;
284 if (FAT_ENTRY_DELETED(fatDirEntry
))
287 DirContext
->LongNameU
.Buffer
[0] = 0;
288 DirContext
->StartIndex
= DirContext
->DirIndex
+ 1;
292 if (FAT_ENTRY_LONG(fatDirEntry
))
296 DPRINT (" long name entry found at %d\n", DirContext
->DirIndex
);
297 RtlZeroMemory(DirContext
->LongNameU
.Buffer
, DirContext
->LongNameU
.MaximumLength
);
298 CheckSum
= longNameEntry
->alias_checksum
;
302 DPRINT(" name chunk1:[%.*S] chunk2:[%.*S] chunk3:[%.*S]\n",
303 5, longNameEntry
->name0_4
,
304 6, longNameEntry
->name5_10
,
305 2, longNameEntry
->name11_12
);
307 index
= (longNameEntry
->id
& 0x1f) - 1;
308 dirMap
|= 1 << index
;
309 pName
= DirContext
->LongNameU
.Buffer
+ 13 * index
;
311 RtlCopyMemory(pName
, longNameEntry
->name0_4
, 5 * sizeof(WCHAR
));
312 RtlCopyMemory(pName
+ 5, longNameEntry
->name5_10
, 6 * sizeof(WCHAR
));
313 RtlCopyMemory(pName
+ 11, longNameEntry
->name11_12
, 2 * sizeof(WCHAR
));
315 DPRINT (" longName: [%S]\n", DirContext
->LongNameU
.Buffer
);
317 if (CheckSum
!= longNameEntry
->alias_checksum
)
319 DPRINT1("Found wrong alias checksum in long name entry (first %x, current %x, %S)\n",
320 CheckSum
, longNameEntry
->alias_checksum
, DirContext
->LongNameU
.Buffer
);
327 for (i
= 0; i
< 11; i
++)
329 shortCheckSum
= (((shortCheckSum
& 1) << 7)
330 | ((shortCheckSum
& 0xfe) >> 1))
331 + fatDirEntry
->ShortName
[i
];
334 if (shortCheckSum
!= CheckSum
&& DirContext
->LongNameU
.Buffer
[0])
336 DPRINT1("Checksum from long and short name is not equal (short: %x, long: %x, %S)\n",
337 shortCheckSum
, CheckSum
, DirContext
->LongNameU
.Buffer
);
338 DirContext
->LongNameU
.Buffer
[0] = 0;
343 DirContext
->LongNameU
.Buffer
[0] = 0;
346 RtlCopyMemory (&DirContext
->DirEntry
.Fat
, fatDirEntry
, sizeof (FAT_DIR_ENTRY
));
351 DirContext
->DirIndex
++;
353 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
355 CcUnpinData(*pContext
);
356 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
358 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
359 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
362 return STATUS_NO_MORE_ENTRIES
;
365 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
366 longNameEntry
= (slot
*) *pPage
;
375 DirContext
->LongNameU
.Length
= wcslen(DirContext
->LongNameU
.Buffer
) * sizeof(WCHAR
);
376 vfat8Dot3ToString(&DirContext
->DirEntry
.Fat
, &DirContext
->ShortNameU
);
378 if (DirContext
->LongNameU
.Length
== 0)
380 RtlCopyUnicodeString(&DirContext
->LongNameU
, &DirContext
->ShortNameU
);
383 return STATUS_SUCCESS
;
386 NTSTATUS
FATXGetNextDirEntry(PVOID
* pContext
,
389 PVFAT_DIRENTRY_CONTEXT DirContext
,
392 LARGE_INTEGER FileOffset
;
393 PFATX_DIR_ENTRY fatxDirEntry
;
395 ULONG DirIndex
= DirContext
->DirIndex
;
397 FileOffset
.u
.HighPart
= 0;
399 if (!vfatFCBIsRoot(pDirFcb
))
401 /* need to add . and .. entries */
402 switch (DirContext
->DirIndex
)
404 case 0: /* entry . */
406 DirContext
->ShortNameU
.Buffer
[0] = 0;
407 DirContext
->ShortNameU
.Length
= 0;
408 DirContext
->LongNameU
.Buffer
[0] = L
'.';
409 DirContext
->LongNameU
.Length
= sizeof(WCHAR
);
410 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
411 DirContext
->DirEntry
.FatX
.Filename
[0] = '.';
412 DirContext
->DirEntry
.FatX
.FilenameLength
= 1;
413 DirContext
->StartIndex
= 0;
414 return STATUS_SUCCESS
;
416 case 1: /* entry .. */
418 DirContext
->ShortNameU
.Buffer
[0] = 0;
419 DirContext
->ShortNameU
.Length
= 0;
420 DirContext
->LongNameU
.Buffer
[0] = DirContext
->LongNameU
.Buffer
[1] = L
'.';
421 DirContext
->LongNameU
.Length
= 2 * sizeof(WCHAR
);
422 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
423 DirContext
->DirEntry
.FatX
.Filename
[0] = DirContext
->DirEntry
.FatX
.Filename
[1] = '.';
424 DirContext
->DirEntry
.FatX
.FilenameLength
= 2;
425 DirContext
->StartIndex
= 1;
426 return STATUS_SUCCESS
;
433 if (*pContext
== NULL
|| (DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
435 if (*pContext
!= NULL
)
437 CcUnpinData(*pContext
);
439 FileOffset
.u
.LowPart
= ROUND_DOWN(DirIndex
* sizeof(FATX_DIR_ENTRY
), PAGE_SIZE
);
440 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
441 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
444 return STATUS_NO_MORE_ENTRIES
;
448 fatxDirEntry
= (PFATX_DIR_ENTRY
)(*pPage
) + DirIndex
% FATX_ENTRIES_PER_PAGE
;
450 DirContext
->StartIndex
= DirContext
->DirIndex
;
454 if (FATX_ENTRY_END(fatxDirEntry
))
456 CcUnpinData(*pContext
);
458 return STATUS_NO_MORE_ENTRIES
;
461 if (!FATX_ENTRY_DELETED(fatxDirEntry
))
463 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, fatxDirEntry
, sizeof(FATX_DIR_ENTRY
));
466 DirContext
->DirIndex
++;
467 DirContext
->StartIndex
++;
469 if ((DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
471 CcUnpinData(*pContext
);
472 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
473 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
474 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
477 return STATUS_NO_MORE_ENTRIES
;
479 fatxDirEntry
= (PFATX_DIR_ENTRY
)*pPage
;
486 DirContext
->ShortNameU
.Buffer
[0] = 0;
487 DirContext
->ShortNameU
.Length
= 0;
488 StringO
.Buffer
= (PCHAR
)fatxDirEntry
->Filename
;
489 StringO
.Length
= StringO
.MaximumLength
= fatxDirEntry
->FilenameLength
;
490 RtlOemStringToUnicodeString(&DirContext
->LongNameU
, &StringO
, FALSE
);
491 return STATUS_SUCCESS
;