2 * PROJECT: ReactOS FAT file system driver
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/filesystems/fastfat/direntry.c
5 * PURPOSE: Directory entries
6 * PROGRAMMERS: Alexey Vlasov
9 /* INCLUDES *****************************************************************/
14 /* PROTOTYPES ***************************************************************/
15 #define BYTES_PER_DIRENT_LOG 0x05
17 typedef struct _FAT_ENUM_DIR_CONTEXT
*PFAT_ENUM_DIR_CONTEXT
;
19 typedef ULONG (*PFAT_COPY_DIRENT_ROUTINE
) (struct _FAT_ENUM_DIR_CONTEXT
*, PDIR_ENTRY
, PVOID
);
21 typedef struct _FAT_ENUM_DIR_CONTEXT
23 PFILE_OBJECT FileObject
;
24 LARGE_INTEGER PageOffset
;
25 LONGLONG BeyoundLastEntryOffset
;
30 * We should always cache short file name
31 * because we never know if there is a long
32 * name for the dirent and when we find out
33 * that our base dirent might become unpinned.
35 UCHAR DirentFileName
[RTL_FIELD_SIZE(DIR_ENTRY
, FileName
) + 1];
36 PFAT_COPY_DIRENT_ROUTINE CopyDirent
;
37 LONGLONG BytesPerClusterMask
;
39 * The following fields are
40 * set by the copy routine
42 PULONG NextEntryOffset
;
43 PULONG FileNameLength
;
45 } FAT_ENUM_DIR_CONTEXT
;
47 typedef enum _FILE_TIME_INDEX
56 FatQueryFileTimes(OUT PLARGE_INTEGER FileTimes
,
57 IN PDIR_ENTRY Dirent
);
60 Fat8dot3ToUnicodeString(OUT PUNICODE_STRING FileName
,
65 FatDirentToDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
70 FatDirentToFullDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
75 FatDirentToIdFullDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
80 FatDirentToBothDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
85 FatDirentToIdBothDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
90 FatDirentToNamesInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
95 FatDirentToObjectIdInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
99 /* FUNCTIONS *****************************************************************/
103 FatDateTimeToSystemTime(OUT PLARGE_INTEGER SystemTime
,
104 IN PFAT_DATETIME FatDateTime
,
105 IN UCHAR TenMs OPTIONAL
)
107 TIME_FIELDS TimeFields
;
109 /* Setup time fields */
110 TimeFields
.Year
= FatDateTime
->Date
.Year
+ 1980;
111 TimeFields
.Month
= FatDateTime
->Date
.Month
;
112 TimeFields
.Day
= FatDateTime
->Date
.Day
;
113 TimeFields
.Hour
= FatDateTime
->Time
.Hour
;
114 TimeFields
.Minute
= FatDateTime
->Time
.Minute
;
115 TimeFields
.Second
= (FatDateTime
->Time
.DoubleSeconds
<< 1);
117 /* Adjust up to 10 milliseconds
118 * if the parameter was supplied
120 if (ARGUMENT_PRESENT(TenMs
))
122 TimeFields
.Second
+= TenMs
/ 100;
123 TimeFields
.Milliseconds
= (TenMs
% 100) * 10;
127 TimeFields
.Milliseconds
= 0;
130 /* Fix seconds value that might get beyoud the bound */
131 if (TimeFields
.Second
> 59) TimeFields
.Second
= 0;
133 /* Perform ceonversion to system time if possible */
134 if (RtlTimeFieldsToTime(&TimeFields
, SystemTime
))
136 /* Convert to system time */
137 ExLocalTimeToSystemTime(SystemTime
, SystemTime
);
141 /* Set to default time if conversion failed */
142 *SystemTime
= FatGlobalData
.DefaultFileTime
;
147 FatQueryFileTimes(OUT PLARGE_INTEGER FileTimes
,
148 IN PDIR_ENTRY Dirent
)
150 /* Convert LastWriteTime */
151 FatDateTimeToSystemTime(&FileTimes
[FileLastWriteTime
],
152 &Dirent
->LastWriteDateTime
,
154 /* All other time fileds are valid (according to MS)
155 * only if Win31 compatability mode is set.
157 if (FatGlobalData
.Win31FileSystem
)
159 /* We can avoid calling conversion routine
160 * if time in dirent is 0 or equals to already
161 * known time (LastWriteTime).
163 if (Dirent
->CreationDateTime
.Value
== 0)
165 /* Set it to default time */
166 FileTimes
[FileCreationTime
] = FatGlobalData
.DefaultFileTime
;
168 else if (Dirent
->CreationDateTime
.Value
169 == Dirent
->LastWriteDateTime
.Value
)
171 /* Assign the already known time */
172 FileTimes
[FileCreationTime
] = FileTimes
[FileLastWriteTime
];
173 /* Adjust milliseconds from extra dirent field */
174 FileTimes
[FileCreationTime
].QuadPart
175 += (ULONG
) Dirent
->CreationTimeTenMs
* 100000;
179 /* Perform conversion */
180 FatDateTimeToSystemTime(&FileTimes
[FileCreationTime
],
181 &Dirent
->CreationDateTime
,
182 Dirent
->CreationTimeTenMs
);
184 if (Dirent
->LastAccessDate
.Value
== 0)
186 /* Set it to default time */
187 FileTimes
[FileLastAccessTime
] = FatGlobalData
.DefaultFileTime
;
189 else if (Dirent
->LastAccessDate
.Value
190 == Dirent
->LastWriteDateTime
.Date
.Value
)
192 /* Assign the already known time */
193 FileTimes
[FileLastAccessTime
] = FileTimes
[FileLastWriteTime
];
197 /* Perform conversion */
198 FAT_DATETIME LastAccessDateTime
;
200 LastAccessDateTime
.Date
.Value
= Dirent
->LastAccessDate
.Value
;
201 LastAccessDateTime
.Time
.Value
= 0;
202 FatDateTimeToSystemTime(&FileTimes
[FileLastAccessTime
],
210 Fat8dot3ToUnicodeString(OUT PUNICODE_STRING FileName
,
215 UCHAR Index
, Ext
= 0;
218 Name
= Add2Ptr(FileName
->Buffer
, 0x0c, PCHAR
);
219 RtlCopyMemory(Name
, ShortName
, 0x0b);
221 /* Restore the name byte used to mark deleted entries */
225 /* Locate the end of name part */
226 for (Index
= 0; Index
< 0x08
227 && Name
[Index
] != 0x20; Index
++);
229 /* Locate the end of extension part */
230 if (Name
[0x08] != 0x20)
233 Name
[Index
++] = 0x2e;
234 Name
[Index
++] = Name
[0x08];
235 if ((Name
[Index
] = Name
[0x09]) != 0x20)
239 if ((Name
[Index
] = Name
[0x0a]) != 0x20)
245 /* Perform Oem to Unicode conversion. */
248 Oem
.MaximumLength
= Index
;
249 RtlOemStringToUnicodeString(FileName
, &Oem
, FALSE
);
250 Index
= FlagOn(Flags
, FAT_CASE_LOWER_BASE
|FAT_CASE_LOWER_EXT
);
253 /* Downcase the whole name */
254 if (Index
== (FAT_CASE_LOWER_BASE
|FAT_CASE_LOWER_EXT
))
256 RtlUpcaseUnicodeString(FileName
, FileName
, FALSE
);
260 if (Index
== FAT_CASE_LOWER_EXT
)
262 /* Set extension for downcase */
263 Oem
.Length
= Ext
* sizeof(WCHAR
);
264 Oem
.Buffer
= Add2Ptr(FileName
->Buffer
,
265 FileName
->Length
- Oem
.Length
,
270 /* Set base name for downcase */
271 Oem
.Buffer
= (PSTR
) FileName
->Buffer
;
272 Oem
.Length
= FileName
->Length
- Ext
* sizeof(WCHAR
);
274 Oem
.MaximumLength
= Oem
.Length
;
275 RtlUpcaseUnicodeString((PUNICODE_STRING
)&Oem
,
276 (PUNICODE_STRING
)&Oem
,
283 FatDirentToDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
284 IN PDIR_ENTRY Dirent
,
287 PFILE_DIRECTORY_INFORMATION Info
;
288 //UNICODE_STRING FileName;
290 Info
= (PFILE_DIRECTORY_INFORMATION
) Buffer
;
291 /* Setup Attributes */
292 Info
->FileAttributes
= Dirent
->Attributes
;
294 FatQueryFileTimes(&Info
->CreationTime
, Dirent
);
296 Info
->EndOfFile
.QuadPart
= Dirent
->FileSize
;
297 Info
->AllocationSize
.QuadPart
=
298 (Context
->BytesPerClusterMask
+ Dirent
->FileSize
)
299 & ~(Context
->BytesPerClusterMask
);
300 //FileName.Buffer = Info->ShortName;
301 //FileName.MaximumLength = sizeof(Info->ShortName);
302 // FatQueryShortName(&FileName, Dirent);
303 // Info->ShortNameLength = (CCHAR) FileName.Length;
304 Info
->NextEntryOffset
= sizeof(*Info
);
305 /* Associate LFN buffer and length pointers
306 * of this entry with the context.
308 Context
->NextEntryOffset
= &Info
->NextEntryOffset
;
309 Context
->FileName
= Info
->FileName
;
310 Context
->FileNameLength
= &Info
->FileNameLength
;
311 return Info
->NextEntryOffset
;
315 FatDirentToFullDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
316 IN PDIR_ENTRY Dirent
,
319 PFILE_FULL_DIR_INFORMATION Info
;
320 //UNICODE_STRING FileName;
322 Info
= (PFILE_FULL_DIR_INFORMATION
) Buffer
;
323 /* Setup Attributes */
324 Info
->FileAttributes
= Dirent
->Attributes
;
326 FatQueryFileTimes(&Info
->CreationTime
, Dirent
);
328 Info
->EndOfFile
.QuadPart
= Dirent
->FileSize
;
329 Info
->AllocationSize
.QuadPart
=
330 (Context
->BytesPerClusterMask
+ Dirent
->FileSize
)
331 & ~(Context
->BytesPerClusterMask
);
332 //FileName.Buffer = Info->ShortName;
333 //FileName.MaximumLength = sizeof(Info->ShortName);
334 // FatQueryShortName(&FileName, Dirent);
335 // Info->ShortNameLength = (CCHAR) FileName.Length;
336 Info
->NextEntryOffset
= sizeof(*Info
);
338 * Associate LFN buffer and length pointers
339 * of this entry with the context.
341 Context
->NextEntryOffset
= &Info
->NextEntryOffset
;
342 Context
->FileName
= Info
->FileName
;
343 Context
->FileNameLength
= &Info
->FileNameLength
;
344 return Info
->NextEntryOffset
;
348 FatDirentToIdFullDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
349 IN PDIR_ENTRY Dirent
,
352 PFILE_ID_FULL_DIR_INFORMATION Info
;
353 //UNICODE_STRING FileName;
355 Info
= (PFILE_ID_FULL_DIR_INFORMATION
) Buffer
;
356 /* Setup Attributes */
357 Info
->FileAttributes
= Dirent
->Attributes
;
359 FatQueryFileTimes(&Info
->CreationTime
, Dirent
);
361 Info
->EndOfFile
.QuadPart
= Dirent
->FileSize
;
362 Info
->AllocationSize
.QuadPart
=
363 (Context
->BytesPerClusterMask
+ Dirent
->FileSize
)
364 & ~(Context
->BytesPerClusterMask
);
365 //FileName.Buffer = Info->ShortName;
366 //FileName.MaximumLength = sizeof(Info->ShortName);
367 // FatQueryShortName(&FileName, Dirent);
368 // Info->ShortNameLength = (CCHAR) FileName.Length;
369 Info
->NextEntryOffset
= sizeof(*Info
);
371 * Associate LFN buffer and length pointers
372 * of this entry with the context.
374 Context
->NextEntryOffset
= &Info
->NextEntryOffset
;
375 Context
->FileName
= Info
->FileName
;
376 Context
->FileNameLength
= &Info
->FileNameLength
;
377 return Info
->NextEntryOffset
;
381 FatDirentToBothDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
382 IN PDIR_ENTRY Dirent
,
385 PFILE_BOTH_DIR_INFORMATION Info
;
386 UNICODE_STRING FileName
;
388 Info
= (PFILE_BOTH_DIR_INFORMATION
) Buffer
;
389 /* Setup Attributes */
390 Info
->FileAttributes
= Dirent
->Attributes
;
392 FatQueryFileTimes(&Info
->CreationTime
, Dirent
);
394 Info
->EndOfFile
.QuadPart
= Dirent
->FileSize
;
395 Info
->AllocationSize
.QuadPart
=
396 (Context
->BytesPerClusterMask
+ Dirent
->FileSize
)
397 & ~(Context
->BytesPerClusterMask
);
398 FileName
.Buffer
= Info
->ShortName
;
399 FileName
.MaximumLength
= sizeof(Info
->ShortName
);
400 Fat8dot3ToUnicodeString(&FileName
, Dirent
->FileName
, Dirent
->Case
);
401 Info
->ShortNameLength
= (CCHAR
) FileName
.Length
;
402 Info
->NextEntryOffset
= sizeof(*Info
);
403 /* Associate LFN buffer and length pointers
404 * of this entry with the context.
406 Context
->NextEntryOffset
= &Info
->NextEntryOffset
;
407 Context
->FileName
= Info
->FileName
;
408 Context
->FileNameLength
= &Info
->FileNameLength
;
409 return Info
->NextEntryOffset
;
413 FatDirentToIdBothDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
414 IN PDIR_ENTRY Dirent
,
417 PFILE_ID_BOTH_DIR_INFORMATION Info
;
418 UNICODE_STRING FileName
;
420 Info
= (PFILE_ID_BOTH_DIR_INFORMATION
) Buffer
;
421 /* Setup Attributes */
422 Info
->FileAttributes
= Dirent
->Attributes
;
424 FatQueryFileTimes(&Info
->CreationTime
, Dirent
);
426 Info
->EndOfFile
.QuadPart
= Dirent
->FileSize
;
427 Info
->AllocationSize
.QuadPart
=
428 (Context
->BytesPerClusterMask
+ Dirent
->FileSize
)
429 & ~(Context
->BytesPerClusterMask
);
430 FileName
.Buffer
= Info
->ShortName
;
431 FileName
.MaximumLength
= sizeof(Info
->ShortName
);
432 Fat8dot3ToUnicodeString(&FileName
, Dirent
->FileName
, Dirent
->Case
);
433 Info
->ShortNameLength
= (CCHAR
) FileName
.Length
;
434 Info
->NextEntryOffset
= sizeof(*Info
);
436 * Associate LFN buffer and length pointers
437 * of this entry with the context.
439 Context
->NextEntryOffset
= &Info
->NextEntryOffset
;
440 Context
->FileName
= Info
->FileName
;
441 Context
->FileNameLength
= &Info
->FileNameLength
;
442 return Info
->NextEntryOffset
;
446 FatDirentToNamesInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
447 IN PDIR_ENTRY Dirent
,
450 PFILE_NAMES_INFORMATION Info
;
452 Info
= (PFILE_NAMES_INFORMATION
) Buffer
;
453 // FatQueryShortName(&FileName, Dirent);
454 Info
->NextEntryOffset
= sizeof(*Info
);
455 /* Associate LFN buffer and length pointers
456 * of this entry with the context.
458 Context
->NextEntryOffset
= &Info
->NextEntryOffset
;
459 Context
->FileName
= Info
->FileName
;
460 Context
->FileNameLength
= &Info
->FileNameLength
;
461 return Info
->NextEntryOffset
;
465 FatDirentToObjectIdInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
466 IN PDIR_ENTRY Dirent
,
469 PFILE_OBJECTID_INFORMATION Info
;
471 Info
= (PFILE_OBJECTID_INFORMATION
) Buffer
;
472 return sizeof(*Info
);
476 FatEnumerateDirents(IN OUT PFAT_ENUM_DIR_CONTEXT Context
,
481 SIZE_T OffsetWithinPage
, PageValidLength
;
482 PUCHAR Entry
, BeyondLastEntry
;
483 /* Determine page offset and the offset within page
484 * for the first cluster.
486 PageValidLength
= PAGE_SIZE
;
487 PageOffset
= ((LONGLONG
) Index
) << BYTES_PER_DIRENT_LOG
;
488 OffsetWithinPage
= (SIZE_T
) (PageOffset
& (PAGE_SIZE
- 1));
489 PageOffset
-= OffsetWithinPage
;
490 /* Check if the context already has the required page mapped.
491 * Map the first page is necessary.
493 if (PageOffset
!= Context
->PageOffset
.QuadPart
)
495 Context
->PageOffset
.QuadPart
= PageOffset
;
496 if (Context
->PageBcb
!= NULL
)
498 CcUnpinData(Context
->PageBcb
);
499 Context
->PageBcb
= NULL
;
501 if (!CcMapData(Context
->FileObject
,
502 &Context
->PageOffset
,
506 &Context
->PageBuffer
))
508 Context
->PageOffset
.QuadPart
= 0LL;
509 ExRaiseStatus(STATUS_CANT_WAIT
);
512 Entry
= Add2Ptr(Context
->PageBuffer
, OffsetWithinPage
, PUCHAR
);
513 /* Next Page Offset */
514 PageOffset
= Context
->PageOffset
.QuadPart
+ PAGE_SIZE
;
515 if (PageOffset
> Context
->BeyoundLastEntryOffset
)
517 PageValidLength
= (SIZE_T
) (Context
->BeyoundLastEntryOffset
518 - Context
->PageOffset
.QuadPart
);
520 BeyondLastEntry
= Add2Ptr(Context
->PageBuffer
, PageValidLength
, PUCHAR
);
525 if (*Entry
== FAT_DIRENT_NEVER_USED
)
526 return 0; // TODO: return something reasonable.
527 if (*Entry
== FAT_DIRENT_DELETED
)
531 if (Entry
[0x0a] == FAT_DIRENT_ATTR_LFN
)
533 PLONG_FILE_NAME_ENTRY Lfnent
;
534 Lfnent
= (PLONG_FILE_NAME_ENTRY
) Entry
;
539 Dirent
= (PDIR_ENTRY
) Entry
;
540 RtlCopyMemory(Context
->DirentFileName
,
542 sizeof(Dirent
->FileName
));
544 } while (++Entry
< BeyondLastEntry
);
545 /* Check if this is the last available entry */
546 if (PageValidLength
< PAGE_SIZE
)
548 /* We are getting beyound current page and
549 * are still in the continous run, map the next page.
551 Context
->PageOffset
.QuadPart
= PageOffset
;
552 CcUnpinData(Context
->PageBcb
);
553 if (!CcMapData(Context
->FileObject
,
554 &Context
->PageOffset
,
558 &Context
->PageBuffer
))
560 Context
->PageBcb
= NULL
;
561 Context
->PageOffset
.QuadPart
= 0LL;
562 ExRaiseStatus(STATUS_CANT_WAIT
);
564 Entry
= (PUCHAR
) Context
->PageBuffer
;
565 /* Next Page Offset */
566 PageOffset
= Context
->PageOffset
.QuadPart
+ PAGE_SIZE
;
567 if (PageOffset
> Context
->BeyoundLastEntryOffset
)
569 PageValidLength
= (SIZE_T
) (Context
->BeyoundLastEntryOffset
570 - Context
->PageOffset
.QuadPart
);
572 BeyondLastEntry
= Add2Ptr(Context
->PageBuffer
, PageValidLength
, PUCHAR
);