e67cdd70f90c556e931cc043850b063c0f2e0fdc
[reactos.git] / reactos / drivers / filesystems / fastfat_new / direntry.c
1 /*
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
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #define NDEBUG
12 #include "fastfat.h"
13
14 /* PROTOTYPES ***************************************************************/
15 #define BYTES_PER_DIRENT_LOG 0x05
16
17 typedef struct _FAT_ENUM_DIR_CONTEXT *PFAT_ENUM_DIR_CONTEXT;
18
19 typedef ULONG (*PFAT_COPY_DIRENT_ROUTINE) (struct _FAT_ENUM_DIR_CONTEXT *, PDIR_ENTRY, PVOID);
20
21 typedef struct _FAT_ENUM_DIR_CONTEXT
22 {
23 PFILE_OBJECT FileObject;
24 LARGE_INTEGER PageOffset;
25 LONGLONG BeyoundLastEntryOffset;
26 PVOID PageBuffer;
27 PBCB PageBcb;
28
29 /*
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.
34 */
35 UCHAR DirentFileName[RTL_FIELD_SIZE(DIR_ENTRY, FileName) + 1];
36 PFAT_COPY_DIRENT_ROUTINE CopyDirent;
37 LONGLONG BytesPerClusterMask;
38 /*
39 * The following fields are
40 * set by the copy routine
41 */
42 PULONG NextEntryOffset;
43 PULONG FileNameLength;
44 PWCHAR FileName;
45 } FAT_ENUM_DIR_CONTEXT;
46
47 typedef enum _FILE_TIME_INDEX
48 {
49 FileCreationTime = 0,
50 FileLastAccessTime,
51 FileLastWriteTime,
52 FileChangeTime
53 } FILE_TIME_INDEX;
54
55 VOID
56 FatQueryFileTimes(OUT PLARGE_INTEGER FileTimes,
57 IN PDIR_ENTRY Dirent);
58
59 VOID
60 Fat8dot3ToUnicodeString(OUT PUNICODE_STRING FileName,
61 IN PUCHAR ShortName,
62 IN UCHAR Flags);
63
64 ULONG
65 FatDirentToDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
66 IN PDIR_ENTRY Dirent,
67 IN PVOID Buffer);
68
69 ULONG
70 FatDirentToFullDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
71 IN PDIR_ENTRY Dirent,
72 IN PVOID Buffer);
73
74 ULONG
75 FatDirentToIdFullDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
76 IN PDIR_ENTRY Dirent,
77 IN PVOID Buffer);
78
79 ULONG
80 FatDirentToBothDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
81 IN PDIR_ENTRY Dirent,
82 IN PVOID Buffer);
83
84 ULONG
85 FatDirentToIdBothDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
86 IN PDIR_ENTRY Dirent,
87 IN PVOID Buffer);
88
89 ULONG
90 FatDirentToNamesInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
91 IN PDIR_ENTRY Dirent,
92 IN PVOID Buffer);
93
94 ULONG
95 FatDirentToObjectIdInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
96 IN PDIR_ENTRY Dirent,
97 IN PVOID Buffer);
98
99 /* FUNCTIONS *****************************************************************/
100
101 FORCEINLINE
102 VOID
103 FatDateTimeToSystemTime(OUT PLARGE_INTEGER SystemTime,
104 IN PFAT_DATETIME FatDateTime,
105 IN UCHAR TenMs OPTIONAL)
106 {
107 TIME_FIELDS TimeFields;
108
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);
116
117 /* Adjust up to 10 milliseconds
118 * if the parameter was supplied
119 */
120 if (ARGUMENT_PRESENT(TenMs))
121 {
122 TimeFields.Second += TenMs / 100;
123 TimeFields.Milliseconds = (TenMs % 100) * 10;
124 }
125 else
126 {
127 TimeFields.Milliseconds = 0;
128 }
129
130 /* Fix seconds value that might get beyoud the bound */
131 if (TimeFields.Second > 59) TimeFields.Second = 0;
132
133 /* Perform ceonversion to system time if possible */
134 if (RtlTimeFieldsToTime(&TimeFields, SystemTime))
135 {
136 /* Convert to system time */
137 ExLocalTimeToSystemTime(SystemTime, SystemTime);
138 }
139 else
140 {
141 /* Set to default time if conversion failed */
142 *SystemTime = FatGlobalData.DefaultFileTime;
143 }
144 }
145
146 VOID
147 FatQueryFileTimes(OUT PLARGE_INTEGER FileTimes,
148 IN PDIR_ENTRY Dirent)
149 {
150 /* Convert LastWriteTime */
151 FatDateTimeToSystemTime(&FileTimes[FileLastWriteTime],
152 &Dirent->LastWriteDateTime,
153 0);
154 /* All other time fileds are valid (according to MS)
155 * only if Win31 compatability mode is set.
156 */
157 if (FatGlobalData.Win31FileSystem)
158 {
159 /* We can avoid calling conversion routine
160 * if time in dirent is 0 or equals to already
161 * known time (LastWriteTime).
162 */
163 if (Dirent->CreationDateTime.Value == 0)
164 {
165 /* Set it to default time */
166 FileTimes[FileCreationTime] = FatGlobalData.DefaultFileTime;
167 }
168 else if (Dirent->CreationDateTime.Value
169 == Dirent->LastWriteDateTime.Value)
170 {
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;
176 }
177 else
178 {
179 /* Perform conversion */
180 FatDateTimeToSystemTime(&FileTimes[FileCreationTime],
181 &Dirent->CreationDateTime,
182 Dirent->CreationTimeTenMs);
183 }
184 if (Dirent->LastAccessDate.Value == 0)
185 {
186 /* Set it to default time */
187 FileTimes[FileLastAccessTime] = FatGlobalData.DefaultFileTime;
188 }
189 else if (Dirent->LastAccessDate.Value
190 == Dirent->LastWriteDateTime.Date.Value)
191 {
192 /* Assign the already known time */
193 FileTimes[FileLastAccessTime] = FileTimes[FileLastWriteTime];
194 }
195 else
196 {
197 /* Perform conversion */
198 FAT_DATETIME LastAccessDateTime;
199
200 LastAccessDateTime.Date.Value = Dirent->LastAccessDate.Value;
201 LastAccessDateTime.Time.Value = 0;
202 FatDateTimeToSystemTime(&FileTimes[FileLastAccessTime],
203 &LastAccessDateTime,
204 0);
205 }
206 }
207 }
208
209 VOID
210 Fat8dot3ToUnicodeString(OUT PUNICODE_STRING FileName,
211 IN PUCHAR ShortName,
212 IN UCHAR Flags)
213 {
214 PCHAR Name;
215 UCHAR Index, Ext = 0;
216 OEM_STRING Oem;
217
218 Name = Add2Ptr(FileName->Buffer, 0x0c, PCHAR);
219 RtlCopyMemory(Name, ShortName, 0x0b);
220
221 /* Restore the name byte used to mark deleted entries */
222 if (Name[0] == 0x05)
223 Name[0] |= 0xe0;
224
225 /* Locate the end of name part */
226 for (Index = 0; Index < 0x08
227 && Name[Index] != 0x20; Index++);
228
229 /* Locate the end of extension part */
230 if (Name[0x08] != 0x20)
231 {
232 Ext = 0x2;
233 Name[Index++] = 0x2e;
234 Name[Index++] = Name[0x08];
235 if ((Name[Index] = Name[0x09]) != 0x20)
236 {
237 Index ++; Ext ++;
238 }
239 if ((Name[Index] = Name[0x0a]) != 0x20)
240 {
241 Index ++; Ext ++;
242 }
243 }
244
245 /* Perform Oem to Unicode conversion. */
246 Oem.Buffer = Name;
247 Oem.Length = Index;
248 Oem.MaximumLength = Index;
249 RtlOemStringToUnicodeString(FileName, &Oem, FALSE);
250 Index = FlagOn(Flags, FAT_CASE_LOWER_BASE|FAT_CASE_LOWER_EXT);
251 if (Index > 0)
252 {
253 /* Downcase the whole name */
254 if (Index == (FAT_CASE_LOWER_BASE|FAT_CASE_LOWER_EXT))
255 {
256 RtlUpcaseUnicodeString(FileName, FileName, FALSE);
257 }
258 else
259 {
260 if (Index == FAT_CASE_LOWER_EXT)
261 {
262 /* Set extension for downcase */
263 Oem.Length = Ext * sizeof(WCHAR);
264 Oem.Buffer = Add2Ptr(FileName->Buffer,
265 FileName->Length - Oem.Length,
266 PSTR);
267 }
268 else
269 {
270 /* Set base name for downcase */
271 Oem.Buffer = (PSTR) FileName->Buffer;
272 Oem.Length = FileName->Length - Ext * sizeof(WCHAR);
273 }
274 Oem.MaximumLength = Oem.Length;
275 RtlUpcaseUnicodeString((PUNICODE_STRING)&Oem,
276 (PUNICODE_STRING)&Oem,
277 FALSE);
278 }
279 }
280 }
281
282 ULONG
283 FatDirentToDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
284 IN PDIR_ENTRY Dirent,
285 IN PVOID Buffer)
286 {
287 PFILE_DIRECTORY_INFORMATION Info;
288 //UNICODE_STRING FileName;
289
290 Info = (PFILE_DIRECTORY_INFORMATION) Buffer;
291 /* Setup Attributes */
292 Info->FileAttributes = Dirent->Attributes;
293 /* Setup times */
294 FatQueryFileTimes(&Info->CreationTime, Dirent);
295 /* Setup sizes */
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.
307 */
308 Context->NextEntryOffset = &Info->NextEntryOffset;
309 Context->FileName = Info->FileName;
310 Context->FileNameLength = &Info->FileNameLength;
311 return Info->NextEntryOffset;
312 }
313
314 ULONG
315 FatDirentToFullDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
316 IN PDIR_ENTRY Dirent,
317 IN PVOID Buffer)
318 {
319 PFILE_FULL_DIR_INFORMATION Info;
320 //UNICODE_STRING FileName;
321
322 Info = (PFILE_FULL_DIR_INFORMATION) Buffer;
323 /* Setup Attributes */
324 Info->FileAttributes = Dirent->Attributes;
325 /* Setup times */
326 FatQueryFileTimes(&Info->CreationTime, Dirent);
327 /* Setup sizes */
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);
337 /*
338 * Associate LFN buffer and length pointers
339 * of this entry with the context.
340 */
341 Context->NextEntryOffset = &Info->NextEntryOffset;
342 Context->FileName = Info->FileName;
343 Context->FileNameLength = &Info->FileNameLength;
344 return Info->NextEntryOffset;
345 }
346
347 ULONG
348 FatDirentToIdFullDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
349 IN PDIR_ENTRY Dirent,
350 IN PVOID Buffer)
351 {
352 PFILE_ID_FULL_DIR_INFORMATION Info;
353 //UNICODE_STRING FileName;
354
355 Info = (PFILE_ID_FULL_DIR_INFORMATION) Buffer;
356 /* Setup Attributes */
357 Info->FileAttributes = Dirent->Attributes;
358 /* Setup times */
359 FatQueryFileTimes(&Info->CreationTime, Dirent);
360 /* Setup sizes */
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);
370 /*
371 * Associate LFN buffer and length pointers
372 * of this entry with the context.
373 */
374 Context->NextEntryOffset = &Info->NextEntryOffset;
375 Context->FileName = Info->FileName;
376 Context->FileNameLength = &Info->FileNameLength;
377 return Info->NextEntryOffset;
378 }
379
380 ULONG
381 FatDirentToBothDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
382 IN PDIR_ENTRY Dirent,
383 IN PVOID Buffer)
384 {
385 PFILE_BOTH_DIR_INFORMATION Info;
386 UNICODE_STRING FileName;
387
388 Info = (PFILE_BOTH_DIR_INFORMATION) Buffer;
389 /* Setup Attributes */
390 Info->FileAttributes = Dirent->Attributes;
391 /* Setup times */
392 FatQueryFileTimes(&Info->CreationTime, Dirent);
393 /* Setup sizes */
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.
405 */
406 Context->NextEntryOffset = &Info->NextEntryOffset;
407 Context->FileName = Info->FileName;
408 Context->FileNameLength = &Info->FileNameLength;
409 return Info->NextEntryOffset;
410 }
411
412 ULONG
413 FatDirentToIdBothDirInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
414 IN PDIR_ENTRY Dirent,
415 IN PVOID Buffer)
416 {
417 PFILE_ID_BOTH_DIR_INFORMATION Info;
418 UNICODE_STRING FileName;
419
420 Info = (PFILE_ID_BOTH_DIR_INFORMATION) Buffer;
421 /* Setup Attributes */
422 Info->FileAttributes = Dirent->Attributes;
423 /* Setup times */
424 FatQueryFileTimes(&Info->CreationTime, Dirent);
425 /* Setup sizes */
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);
435 /*
436 * Associate LFN buffer and length pointers
437 * of this entry with the context.
438 */
439 Context->NextEntryOffset = &Info->NextEntryOffset;
440 Context->FileName = Info->FileName;
441 Context->FileNameLength = &Info->FileNameLength;
442 return Info->NextEntryOffset;
443 }
444
445 ULONG
446 FatDirentToNamesInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
447 IN PDIR_ENTRY Dirent,
448 IN PVOID Buffer)
449 {
450 PFILE_NAMES_INFORMATION Info;
451
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.
457 */
458 Context->NextEntryOffset = &Info->NextEntryOffset;
459 Context->FileName = Info->FileName;
460 Context->FileNameLength = &Info->FileNameLength;
461 return Info->NextEntryOffset;
462 }
463
464 ULONG
465 FatDirentToObjectIdInfo(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
466 IN PDIR_ENTRY Dirent,
467 IN PVOID Buffer)
468 {
469 PFILE_OBJECTID_INFORMATION Info;
470
471 Info = (PFILE_OBJECTID_INFORMATION) Buffer;
472 return sizeof(*Info);
473 }
474
475 ULONG
476 FatEnumerateDirents(IN OUT PFAT_ENUM_DIR_CONTEXT Context,
477 IN ULONG Index,
478 IN BOOLEAN CanWait)
479 {
480 LONGLONG PageOffset;
481 SIZE_T OffsetWithinPage, PageValidLength;
482 PUCHAR Entry, BeyondLastEntry;
483 /* Determine page offset and the offset within page
484 * for the first cluster.
485 */
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.
492 */
493 if (PageOffset != Context->PageOffset.QuadPart)
494 {
495 Context->PageOffset.QuadPart = PageOffset;
496 if (Context->PageBcb != NULL)
497 {
498 CcUnpinData(Context->PageBcb);
499 Context->PageBcb = NULL;
500 }
501 if (!CcMapData(Context->FileObject,
502 &Context->PageOffset,
503 PAGE_SIZE,
504 CanWait,
505 &Context->PageBcb,
506 &Context->PageBuffer))
507 {
508 Context->PageOffset.QuadPart = 0LL;
509 ExRaiseStatus(STATUS_CANT_WAIT);
510 }
511 }
512 Entry = Add2Ptr(Context->PageBuffer, OffsetWithinPage, PUCHAR);
513 /* Next Page Offset */
514 PageOffset = Context->PageOffset.QuadPart + PAGE_SIZE;
515 if (PageOffset > Context->BeyoundLastEntryOffset)
516 {
517 PageValidLength = (SIZE_T) (Context->BeyoundLastEntryOffset
518 - Context->PageOffset.QuadPart);
519 }
520 BeyondLastEntry = Add2Ptr(Context->PageBuffer, PageValidLength, PUCHAR);
521 while (TRUE)
522 {
523 do
524 {
525 if (*Entry == FAT_DIRENT_NEVER_USED)
526 return 0; // TODO: return something reasonable.
527 if (*Entry == FAT_DIRENT_DELETED)
528 {
529 continue;
530 }
531 if (Entry[0x0a] == FAT_DIRENT_ATTR_LFN)
532 {
533 PLONG_FILE_NAME_ENTRY Lfnent;
534 Lfnent = (PLONG_FILE_NAME_ENTRY) Entry;
535 }
536 else
537 {
538 PDIR_ENTRY Dirent;
539 Dirent = (PDIR_ENTRY) Entry;
540 RtlCopyMemory(Context->DirentFileName,
541 Dirent->FileName,
542 sizeof(Dirent->FileName));
543 }
544 } while (++Entry < BeyondLastEntry);
545 /* Check if this is the last available entry */
546 if (PageValidLength < PAGE_SIZE)
547 break;
548 /* We are getting beyound current page and
549 * are still in the continous run, map the next page.
550 */
551 Context->PageOffset.QuadPart = PageOffset;
552 CcUnpinData(Context->PageBcb);
553 if (!CcMapData(Context->FileObject,
554 &Context->PageOffset,
555 PAGE_SIZE,
556 CanWait,
557 &Context->PageBcb,
558 &Context->PageBuffer))
559 {
560 Context->PageBcb = NULL;
561 Context->PageOffset.QuadPart = 0LL;
562 ExRaiseStatus(STATUS_CANT_WAIT);
563 }
564 Entry = (PUCHAR) Context->PageBuffer;
565 /* Next Page Offset */
566 PageOffset = Context->PageOffset.QuadPart + PAGE_SIZE;
567 if (PageOffset > Context->BeyoundLastEntryOffset)
568 {
569 PageValidLength = (SIZE_T) (Context->BeyoundLastEntryOffset
570 - Context->PageOffset.QuadPart);
571 }
572 BeyondLastEntry = Add2Ptr(Context->PageBuffer, PageValidLength, PUCHAR);
573 }
574 return 0;
575 }
576
577 /* EOF */