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
;
39 static BOOLEAN
FATIsDirectoryEmpty(PVFATFCB Fcb
)
41 LARGE_INTEGER FileOffset
;
43 PFAT_DIR_ENTRY FatDirEntry
;
44 ULONG Index
, MaxIndex
;
46 if (vfatFCBIsRoot(Fcb
))
55 FileOffset
.QuadPart
= 0;
56 MaxIndex
= Fcb
->RFCB
.FileSize
.u
.LowPart
/ sizeof(FAT_DIR_ENTRY
);
58 while (Index
< MaxIndex
)
60 if (Context
== NULL
|| (Index
% FAT_ENTRIES_PER_PAGE
) == 0)
66 if (!CcMapData(Fcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, &Context
, (PVOID
*)&FatDirEntry
))
70 FatDirEntry
+= Index
% FAT_ENTRIES_PER_PAGE
;
72 if (FAT_ENTRY_END(FatDirEntry
))
77 if (!FAT_ENTRY_DELETED(FatDirEntry
))
92 static BOOLEAN
FATXIsDirectoryEmpty(PVFATFCB Fcb
)
94 LARGE_INTEGER FileOffset
;
96 PFATX_DIR_ENTRY FatXDirEntry
;
97 ULONG Index
, MaxIndex
;
101 FileOffset
.QuadPart
= 0;
102 MaxIndex
= Fcb
->RFCB
.FileSize
.u
.LowPart
/ sizeof(FATX_DIR_ENTRY
);
104 while (Index
< MaxIndex
)
106 if (Context
== NULL
|| (Index
% FATX_ENTRIES_PER_PAGE
) == 0)
110 CcUnpinData(Context
);
112 if (!CcMapData(Fcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, &Context
, (PVOID
*)&FatXDirEntry
))
116 FatXDirEntry
+= Index
% FATX_ENTRIES_PER_PAGE
;
118 if (FATX_ENTRY_END(FatXDirEntry
))
120 CcUnpinData(Context
);
123 if (!FATX_ENTRY_DELETED(FatXDirEntry
))
125 CcUnpinData(Context
);
133 CcUnpinData(Context
);
138 BOOLEAN
VfatIsDirectoryEmpty(PVFATFCB Fcb
)
140 if (Fcb
->Flags
& FCB_IS_FATX_ENTRY
)
141 return FATXIsDirectoryEmpty(Fcb
);
143 return FATIsDirectoryEmpty(Fcb
);
146 NTSTATUS
FATGetNextDirEntry(PVOID
* pContext
,
149 PVFAT_DIRENTRY_CONTEXT DirContext
,
154 LARGE_INTEGER FileOffset
;
155 PFAT_DIR_ENTRY fatDirEntry
;
156 slot
* longNameEntry
;
159 UCHAR CheckSum
, shortCheckSum
;
161 BOOLEAN Valid
= TRUE
;
162 BOOLEAN Back
= FALSE
;
164 DirContext
->LongNameU
.Buffer
[0] = 0;
166 FileOffset
.u
.HighPart
= 0;
167 FileOffset
.u
.LowPart
= ROUND_DOWN(DirContext
->DirIndex
* sizeof(FAT_DIR_ENTRY
), PAGE_SIZE
);
169 if (*pContext
== NULL
|| (DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
171 if (*pContext
!= NULL
)
173 CcUnpinData(*pContext
);
175 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
176 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
179 return STATUS_NO_MORE_ENTRIES
;
184 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
185 longNameEntry
= (slot
*) fatDirEntry
;
190 /* This is the first call to vfatGetNextDirEntry. Possible the start index points
191 * into a long name or points to a short name with an assigned long name.
192 * We must go back to the real start of the entry */
193 while (DirContext
->DirIndex
> 0 &&
194 !FAT_ENTRY_END(fatDirEntry
) &&
195 !FAT_ENTRY_DELETED(fatDirEntry
) &&
196 ((!FAT_ENTRY_LONG(fatDirEntry
) && !Back
) ||
197 (FAT_ENTRY_LONG(fatDirEntry
) && !(longNameEntry
->id
& 0x40))))
199 DirContext
->DirIndex
--;
201 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == FAT_ENTRIES_PER_PAGE
- 1)
203 CcUnpinData(*pContext
);
204 FileOffset
.u
.LowPart
-= PAGE_SIZE
;
205 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
206 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
210 return STATUS_NO_MORE_ENTRIES
;
212 fatDirEntry
= (PFAT_DIR_ENTRY
)(*pPage
) + DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
;
213 longNameEntry
= (slot
*) fatDirEntry
;
222 if (Back
&& !FAT_ENTRY_END(fatDirEntry
) &&
223 (FAT_ENTRY_DELETED(fatDirEntry
) || !FAT_ENTRY_LONG(fatDirEntry
)))
225 DirContext
->DirIndex
++;
226 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
228 CcUnpinData(*pContext
);
229 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
230 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
231 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
235 return STATUS_NO_MORE_ENTRIES
;
237 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
238 longNameEntry
= (slot
*) *pPage
;
248 DirContext
->StartIndex
= DirContext
->DirIndex
;
253 if (FAT_ENTRY_END(fatDirEntry
))
255 CcUnpinData(*pContext
);
257 return STATUS_NO_MORE_ENTRIES
;
260 if (FAT_ENTRY_DELETED(fatDirEntry
))
263 DirContext
->LongNameU
.Buffer
[0] = 0;
264 DirContext
->StartIndex
= DirContext
->DirIndex
+ 1;
268 if (FAT_ENTRY_LONG(fatDirEntry
))
272 DPRINT (" long name entry found at %d\n", DirContext
->DirIndex
);
273 RtlZeroMemory(DirContext
->LongNameU
.Buffer
, DirContext
->LongNameU
.MaximumLength
);
274 CheckSum
= longNameEntry
->alias_checksum
;
278 DPRINT (" name chunk1:[%.*S] chunk2:[%.*S] chunk3:[%.*S]\n",
279 5, longNameEntry
->name0_4
,
280 6, longNameEntry
->name5_10
,
281 2, longNameEntry
->name11_12
);
283 index
= (longNameEntry
->id
& 0x1f) - 1;
284 dirMap
|= 1 << index
;
285 pName
= DirContext
->LongNameU
.Buffer
+ 13 * index
;
287 RtlCopyMemory(pName
, longNameEntry
->name0_4
, 5 * sizeof(WCHAR
));
288 RtlCopyMemory(pName
+ 5, longNameEntry
->name5_10
, 6 * sizeof(WCHAR
));
289 RtlCopyMemory(pName
+ 11, longNameEntry
->name11_12
, 2 * sizeof(WCHAR
));
291 DPRINT (" longName: [%S]\n", DirContext
->LongNameU
.Buffer
);
292 if (CheckSum
!= longNameEntry
->alias_checksum
)
294 DPRINT1("Found wrong alias checksum in long name entry (first %x, current %x, %S)\n",
295 CheckSum
, longNameEntry
->alias_checksum
, DirContext
->LongNameU
.Buffer
);
302 for (i
= 0; i
< 11; i
++)
304 shortCheckSum
= (((shortCheckSum
& 1) << 7)
305 | ((shortCheckSum
& 0xfe) >> 1))
306 + fatDirEntry
->ShortName
[i
];
308 if (shortCheckSum
!= CheckSum
&& DirContext
->LongNameU
.Buffer
[0])
310 DPRINT1("Checksum from long and short name is not equal (short: %x, long: %x, %S)\n",
311 shortCheckSum
, CheckSum
, DirContext
->LongNameU
.Buffer
);
312 DirContext
->LongNameU
.Buffer
[0] = 0;
316 DirContext
->LongNameU
.Buffer
[0] = 0;
319 RtlCopyMemory (&DirContext
->DirEntry
.Fat
, fatDirEntry
, sizeof (FAT_DIR_ENTRY
));
323 DirContext
->DirIndex
++;
324 if ((DirContext
->DirIndex
% FAT_ENTRIES_PER_PAGE
) == 0)
326 CcUnpinData(*pContext
);
327 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
328 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
329 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
333 return STATUS_NO_MORE_ENTRIES
;
335 fatDirEntry
= (PFAT_DIR_ENTRY
)*pPage
;
336 longNameEntry
= (slot
*) *pPage
;
344 DirContext
->LongNameU
.Length
= wcslen(DirContext
->LongNameU
.Buffer
) * sizeof(WCHAR
);
345 vfat8Dot3ToString(&DirContext
->DirEntry
.Fat
, &DirContext
->ShortNameU
);
346 if (DirContext
->LongNameU
.Length
== 0)
348 RtlCopyUnicodeString(&DirContext
->LongNameU
, &DirContext
->ShortNameU
);
350 return STATUS_SUCCESS
;
353 NTSTATUS
FATXGetNextDirEntry(PVOID
* pContext
,
356 PVFAT_DIRENTRY_CONTEXT DirContext
,
359 LARGE_INTEGER FileOffset
;
360 PFATX_DIR_ENTRY fatxDirEntry
;
362 ULONG DirIndex
= DirContext
->DirIndex
;
364 FileOffset
.u
.HighPart
= 0;
366 if (!vfatFCBIsRoot(pDirFcb
))
368 /* need to add . and .. entries */
369 switch (DirContext
->DirIndex
)
371 case 0: /* entry . */
373 DirContext
->ShortNameU
.Buffer
[0] = 0;
374 DirContext
->ShortNameU
.Length
= 0;
375 DirContext
->LongNameU
.Buffer
[0] = L
'.';
376 DirContext
->LongNameU
.Length
= sizeof(WCHAR
);
377 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
378 DirContext
->DirEntry
.FatX
.Filename
[0] = '.';
379 DirContext
->DirEntry
.FatX
.FilenameLength
= 1;
380 DirContext
->StartIndex
= 0;
381 return STATUS_SUCCESS
;
383 case 1: /* entry .. */
385 DirContext
->ShortNameU
.Buffer
[0] = 0;
386 DirContext
->ShortNameU
.Length
= 0;
387 DirContext
->LongNameU
.Buffer
[0] = DirContext
->LongNameU
.Buffer
[1] = L
'.';
388 DirContext
->LongNameU
.Length
= 2 * sizeof(WCHAR
);
389 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, &pDirFcb
->entry
.FatX
, sizeof(FATX_DIR_ENTRY
));
390 DirContext
->DirEntry
.FatX
.Filename
[0] = DirContext
->DirEntry
.FatX
.Filename
[1] = '.';
391 DirContext
->DirEntry
.FatX
.FilenameLength
= 2;
392 DirContext
->StartIndex
= 1;
393 return STATUS_SUCCESS
;
400 if (*pContext
== NULL
|| (DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
402 if (*pContext
!= NULL
)
404 CcUnpinData(*pContext
);
406 FileOffset
.u
.LowPart
= ROUND_DOWN(DirIndex
* sizeof(FATX_DIR_ENTRY
), PAGE_SIZE
);
407 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
408 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
411 return STATUS_NO_MORE_ENTRIES
;
415 fatxDirEntry
= (PFATX_DIR_ENTRY
)(*pPage
) + DirIndex
% FATX_ENTRIES_PER_PAGE
;
417 DirContext
->StartIndex
= DirContext
->DirIndex
;
421 if (FATX_ENTRY_END(fatxDirEntry
))
423 CcUnpinData(*pContext
);
425 return STATUS_NO_MORE_ENTRIES
;
428 if (!FATX_ENTRY_DELETED(fatxDirEntry
))
430 RtlCopyMemory(&DirContext
->DirEntry
.FatX
, fatxDirEntry
, sizeof(FATX_DIR_ENTRY
));
433 DirContext
->DirIndex
++;
434 DirContext
->StartIndex
++;
436 if ((DirIndex
% FATX_ENTRIES_PER_PAGE
) == 0)
438 CcUnpinData(*pContext
);
439 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
440 if (FileOffset
.u
.LowPart
>= pDirFcb
->RFCB
.FileSize
.u
.LowPart
||
441 !CcMapData(pDirFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, pContext
, pPage
))
445 return STATUS_NO_MORE_ENTRIES
;
447 fatxDirEntry
= (PFATX_DIR_ENTRY
)*pPage
;
454 DirContext
->ShortNameU
.Buffer
[0] = 0;
455 DirContext
->ShortNameU
.Length
= 0;
456 StringO
.Buffer
= (PCHAR
)fatxDirEntry
->Filename
;
457 StringO
.Length
= StringO
.MaximumLength
= fatxDirEntry
->FilenameLength
;
458 RtlOemStringToUnicodeString(&DirContext
->LongNameU
, &StringO
, FALSE
);
459 return STATUS_SUCCESS
;