[fastfat_new]
[reactos.git] / reactos / drivers / filesystems / fastfat_new / fcb.c
1 /*
2 * PROJECT: ReactOS FAT file system driver
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/filesystems/fastfat/fcb.c
5 * PURPOSE: FCB manipulation routines.
6 * PROGRAMMERS: Aleksey Bragin <aleksey@reactos.org>
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #define NDEBUG
12 #include "fastfat.h"
13
14 #define TAG_FILENAME 'fBnF'
15
16 /* FUNCTIONS ****************************************************************/
17
18 FSRTL_COMPARISON_RESULT
19 NTAPI
20 FatiCompareNames(PSTRING NameA,
21 PSTRING NameB)
22 {
23 ULONG MinimumLen, i;
24
25 /* Calc the minimum length */
26 MinimumLen = NameA->Length < NameB->Length ? NameA->Length :
27 NameB->Length;
28
29 /* Actually compare them */
30 i = (ULONG)RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinimumLen );
31
32 if (i < MinimumLen)
33 {
34 /* Compare prefixes */
35 if (NameA->Buffer[i] < NameB->Buffer[i])
36 return LessThan;
37 else
38 return GreaterThan;
39 }
40
41 /* Final comparison */
42 if (NameA->Length < NameB->Length)
43 return LessThan;
44 else if (NameA->Length > NameB->Length)
45 return GreaterThan;
46 else
47 return EqualTo;
48 }
49
50 PFCB
51 NTAPI
52 FatFindFcb(PFAT_IRP_CONTEXT IrpContext,
53 PRTL_SPLAY_LINKS *RootNode,
54 PSTRING AnsiName,
55 PBOOLEAN IsDosName)
56 {
57 PFCB_NAME_LINK Node;
58 FSRTL_COMPARISON_RESULT Comparison;
59 PRTL_SPLAY_LINKS Links;
60
61 Links = *RootNode;
62
63 while (Links)
64 {
65 Node = CONTAINING_RECORD(Links, FCB_NAME_LINK, Links);
66
67 /* Compare the prefix */
68 if (*(PUCHAR)Node->Name.Ansi.Buffer != *(PUCHAR)AnsiName->Buffer)
69 {
70 if (*(PUCHAR)Node->Name.Ansi.Buffer < *(PUCHAR)AnsiName->Buffer)
71 Comparison = LessThan;
72 else
73 Comparison = GreaterThan;
74 }
75 else
76 {
77 /* Perform real comparison */
78 Comparison = FatiCompareNames(&Node->Name.Ansi, AnsiName);
79 }
80
81 /* Do they match? */
82 if (Comparison == GreaterThan)
83 {
84 /* No, it's greater, go to the left child */
85 Links = RtlLeftChild(Links);
86 }
87 else if (Comparison == LessThan)
88 {
89 /* No, it's lesser, go to the right child */
90 Links = RtlRightChild(Links);
91 }
92 else
93 {
94 /* Exact match, balance the tree */
95 *RootNode = RtlSplay(Links);
96
97 /* Save type of the name, if needed */
98 if (IsDosName)
99 *IsDosName = Node->IsDosName;
100
101 /* Return the found fcb */
102 return Node->Fcb;
103 }
104 }
105
106 /* Nothing found */
107 return NULL;
108 }
109
110 PFCB
111 NTAPI
112 FatCreateFcb(IN PFAT_IRP_CONTEXT IrpContext,
113 IN PVCB Vcb,
114 IN PFCB ParentDcb,
115 IN FF_FILE *FileHandle)
116 {
117 PFCB Fcb;
118
119 /* Allocate it and zero it */
120 Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(FCB), TAG_FCB);
121 RtlZeroMemory(Fcb, sizeof(FCB));
122
123 /* Set node types */
124 Fcb->Header.NodeTypeCode = FAT_NTC_FCB;
125 Fcb->Header.NodeByteSize = sizeof(FCB);
126 Fcb->Condition = FcbGood;
127
128 /* Initialize resources */
129 Fcb->Header.Resource = &Fcb->Resource;
130 ExInitializeResourceLite(Fcb->Header.Resource);
131
132 Fcb->Header.PagingIoResource = &Fcb->PagingIoResource;
133 ExInitializeResourceLite(Fcb->Header.PagingIoResource);
134
135 /* Initialize mutexes */
136 Fcb->Header.FastMutex = &Fcb->HeaderMutex;
137 ExInitializeFastMutex(&Fcb->HeaderMutex);
138 FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
139
140 /* Insert into parent's DCB list */
141 InsertTailList(&ParentDcb->Dcb.ParentDcbList, &Fcb->ParentDcbLinks);
142
143 /* Set backlinks */
144 Fcb->ParentFcb = ParentDcb;
145 Fcb->Vcb = Vcb;
146
147 /* Set file handle and sizes */
148 Fcb->Header.FileSize.LowPart = FileHandle->Filesize;
149 Fcb->Header.ValidDataLength.LowPart = FileHandle->Filesize;
150 Fcb->FatHandle = FileHandle;
151
152 /* Set names */
153 FatSetFcbNames(IrpContext, Fcb);
154
155 return Fcb;
156 }
157
158 PCCB
159 NTAPI
160 FatCreateCcb()
161 {
162 PCCB Ccb;
163
164 /* Allocate the CCB and zero it */
165 Ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
166 RtlZeroMemory(Ccb, sizeof(CCB));
167
168 /* Set mandatory header */
169 Ccb->NodeTypeCode = FAT_NTC_FCB;
170 Ccb->NodeByteSize = sizeof(CCB);
171
172 return Ccb;
173 }
174
175 VOID
176 NTAPI
177 FatSetFullNameInFcb(PFCB Fcb,
178 PUNICODE_STRING Name)
179 {
180 PUNICODE_STRING ParentName;
181
182 /* Make sure this FCB's name wasn't already set */
183 ASSERT(Fcb->FullFileName.Buffer == NULL);
184
185 /* First of all, check exact case name */
186 if (Fcb->ExactCaseLongName.Buffer)
187 {
188 ASSERT(Fcb->ExactCaseLongName.Length != 0);
189
190 /* Use exact case name */
191 Name = &Fcb->ExactCaseLongName;
192 }
193
194 /* Treat root dir different */
195 if (FatNodeType(Fcb->ParentFcb) == FAT_NTC_ROOT_DCB)
196 {
197 /* Set lengths */
198 Fcb->FullFileName.MaximumLength = sizeof(WCHAR) + Name->Length;
199 Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;
200
201 /* Allocate a buffer */
202 Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
203 Fcb->FullFileName.Length,
204 TAG_FILENAME);
205
206 /* Prefix with a backslash */
207 Fcb->FullFileName.Buffer[0] = L'\\';
208
209 /* Copy the name here */
210 RtlCopyMemory(&Fcb->FullFileName.Buffer[1],
211 &Name->Buffer[0],
212 Name->Length );
213 }
214 else
215 {
216 ParentName = &Fcb->ParentFcb->FullFileName;
217
218 /* Check if parent's name is set */
219 if (!ParentName->Buffer)
220 return;
221
222 /* Set lengths */
223 Fcb->FullFileName.MaximumLength =
224 ParentName->Length + sizeof(WCHAR) + Name->Length;
225 Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;
226
227 /* Allocate a buffer */
228 Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
229 Fcb->FullFileName.Length,
230 TAG_FILENAME );
231
232 /* Copy parent's name here */
233 RtlCopyMemory(&Fcb->FullFileName.Buffer[0],
234 &ParentName->Buffer[0],
235 ParentName->Length );
236
237 /* Add a backslash */
238 Fcb->FullFileName.Buffer[ParentName->Length / sizeof(WCHAR)] = L'\\';
239
240 /* Copy given name here */
241 RtlCopyMemory(&Fcb->FullFileName.Buffer[(ParentName->Length / sizeof(WCHAR)) + 1],
242 &Name->Buffer[0],
243 Name->Length );
244 }
245 }
246
247 VOID
248 NTAPI
249 FatSetFcbNames(IN PFAT_IRP_CONTEXT IrpContext,
250 IN PFCB Fcb)
251 {
252 FF_DIRENT DirEnt;
253 FF_ERROR Err;
254 POEM_STRING ShortName;
255 CHAR ShortNameRaw[13];
256 UCHAR EntryBuffer[32];
257 UCHAR NumLFNs;
258 PUNICODE_STRING UnicodeName;
259 OEM_STRING LongNameOem;
260 NTSTATUS Status;
261
262 /* Get the dir entry */
263 Err = FF_GetEntry(Fcb->Vcb->Ioman,
264 Fcb->FatHandle->DirEntry,
265 Fcb->FatHandle->DirCluster,
266 &DirEnt);
267
268 if (Err != FF_ERR_NONE)
269 {
270 DPRINT1("Error %d getting dirent of a file\n", Err);
271 return;
272 }
273
274 /* Read the dirent to fetch the raw short name */
275 FF_FetchEntry(Fcb->Vcb->Ioman,
276 Fcb->FatHandle->DirCluster,
277 Fcb->FatHandle->DirEntry,
278 EntryBuffer);
279 NumLFNs = (UCHAR)(EntryBuffer[0] & ~0x40);
280 RtlCopyMemory(ShortNameRaw, EntryBuffer, 11);
281
282 /* Initialize short name string */
283 ShortName = &Fcb->ShortName.Name.Ansi;
284 ShortName->Buffer = Fcb->ShortNameBuffer;
285 ShortName->Length = 0;
286 ShortName->MaximumLength = sizeof(Fcb->ShortNameBuffer);
287
288 /* Convert raw short name to a proper string */
289 Fati8dot3ToString(ShortNameRaw, FALSE, ShortName);
290
291 /* Add the short name link */
292 FatInsertName(IrpContext, &Fcb->ParentFcb->Dcb.SplayLinksAnsi, &Fcb->ShortName);
293 Fcb->ShortName.Fcb = Fcb;
294
295 /* Get the long file name (if any) */
296 if (NumLFNs > 0)
297 {
298 /* Prepare the oem string */
299 LongNameOem.Buffer = DirEnt.FileName;
300 LongNameOem.MaximumLength = FF_MAX_FILENAME;
301 LongNameOem.Length = strlen(DirEnt.FileName);
302
303 /* Prepare the unicode string */
304 UnicodeName = &Fcb->LongName.Name.String;
305 UnicodeName->Length = (LongNameOem.Length + 1) * sizeof(WCHAR);
306 UnicodeName->MaximumLength = UnicodeName->Length;
307 UnicodeName->Buffer = FsRtlAllocatePool(PagedPool, UnicodeName->Length);
308
309 /* Convert it to unicode */
310 Status = RtlOemStringToUnicodeString(UnicodeName, &LongNameOem, FALSE);
311 if (!NT_SUCCESS(Status))
312 {
313 ASSERT(FALSE);
314 }
315
316 RtlDowncaseUnicodeString(UnicodeName, UnicodeName, FALSE);
317 RtlUpcaseUnicodeString(UnicodeName, UnicodeName, FALSE);
318
319 DPRINT1("Converted long name: %wZ\n", UnicodeName);
320
321 /* Add the long unicode name link */
322 FatInsertName(IrpContext, &Fcb->ParentFcb->Dcb.SplayLinksUnicode, &Fcb->LongName);
323 Fcb->LongName.Fcb = Fcb;
324
325 /* Indicate that this FCB has a unicode long name */
326 SetFlag(Fcb->State, FCB_STATE_HAS_UNICODE_NAME);
327 }
328
329 /* Mark the fact that names were added to splay trees*/
330 SetFlag(Fcb->State, FCB_STATE_HAS_NAMES);
331 }
332
333 VOID
334 NTAPI
335 Fati8dot3ToString(IN PCHAR FileName,
336 IN BOOLEAN DownCase,
337 OUT POEM_STRING OutString)
338 {
339 ULONG BaseLen, ExtLen;
340 CHAR *cString = OutString->Buffer;
341 ULONG i;
342
343 /* Calc base and ext lens */
344 for (BaseLen = 8; BaseLen > 0; BaseLen--)
345 {
346 if (FileName[BaseLen - 1] != ' ') break;
347 }
348
349 for (ExtLen = 3; ExtLen > 0; ExtLen--)
350 {
351 if (FileName[8 + ExtLen - 1] != ' ') break;
352 }
353
354 /* Process base name */
355 if (BaseLen)
356 {
357 RtlCopyMemory(cString, FileName, BaseLen);
358
359 /* Substitute the e5 thing */
360 if (cString[0] == 0x05) cString[0] = 0xe5;
361
362 /* Downcase if asked to */
363 if (DownCase)
364 {
365 /* Do it manually */
366 for (i = 0; i < BaseLen; i++)
367 {
368 if (cString[i] >= 'A' &&
369 cString[i] <= 'Z')
370 {
371 /* Lowercase it */
372 cString[i] += 'a' - 'A';
373 }
374
375 }
376 }
377 }
378
379 /* Process extension */
380 if (ExtLen)
381 {
382 /* Add the dot */
383 cString[BaseLen] = '.';
384 BaseLen++;
385
386 /* Copy the extension */
387 for (i = 0; i < ExtLen; i++)
388 {
389 cString[BaseLen + i] = FileName[8 + i];
390 }
391
392 /* Lowercase the extension if asked to */
393 if (DownCase)
394 {
395 /* Do it manually */
396 for (i = BaseLen; i < BaseLen + ExtLen; i++)
397 {
398 if (cString[i] >= 'A' &&
399 cString[i] <= 'Z')
400 {
401 /* Lowercase it */
402 cString[i] += 'a' - 'A';
403 }
404 }
405 }
406 }
407
408 /* Set the length */
409 OutString->Length = BaseLen + ExtLen;
410
411 DPRINT1("'%s', len %d\n", OutString->Buffer, OutString->Length);
412 }
413
414 VOID
415 NTAPI
416 FatInsertName(IN PFAT_IRP_CONTEXT IrpContext,
417 IN PRTL_SPLAY_LINKS *RootNode,
418 IN PFCB_NAME_LINK Name)
419 {
420 PFCB_NAME_LINK NameLink;
421 FSRTL_COMPARISON_RESULT Comparison;
422
423 /* Initialize the splay links */
424 RtlInitializeSplayLinks(&Name->Links);
425
426 /* Is this the first entry? */
427 if (*RootNode == NULL)
428 {
429 /* Yes, become root and return */
430 *RootNode = &Name->Links;
431 return;
432 }
433
434 /* Get the name link */
435 NameLink = CONTAINING_RECORD(*RootNode, FCB_NAME_LINK, Links);
436 while (TRUE)
437 {
438 /* Compare prefixes */
439 Comparison = FatiCompareNames(&NameLink->Name.Ansi, &Name->Name.Ansi);
440
441 /* Check the bad case first */
442 if (Comparison == EqualTo)
443 {
444 /* Must not happen */
445 ASSERT(FALSE);
446 }
447
448 /* Check comparison result */
449 if (Comparison == GreaterThan)
450 {
451 /* Go to the left child */
452 if (!RtlLeftChild(&NameLink->Links))
453 {
454 /* It's absent, insert here and break */
455 RtlInsertAsLeftChild(&NameLink->Links, &NameLink->Links);
456 break;
457 }
458 else
459 {
460 /* It's present, go inside it */
461 NameLink = CONTAINING_RECORD(RtlLeftChild(&NameLink->Links),
462 FCB_NAME_LINK,
463 Links);
464 }
465 }
466 else
467 {
468 /* Go to the right child */
469 if (!RtlRightChild(&NameLink->Links))
470 {
471 /* It's absent, insert here and break */
472 RtlInsertAsRightChild(&NameLink->Links, &Name->Links);
473 break;
474 }
475 else
476 {
477 /* It's present, go inside it */
478 NameLink = CONTAINING_RECORD(RtlRightChild(&NameLink->Links),
479 FCB_NAME_LINK,
480 Links);
481 }
482 }
483 }
484 }
485
486 VOID
487 NTAPI
488 FatRemoveNames(IN PFAT_IRP_CONTEXT IrpContext,
489 IN PFCB Fcb)
490 {
491 PRTL_SPLAY_LINKS RootNew;
492 PFCB Parent;
493
494 /* Reference the parent for simplicity */
495 Parent = Fcb->ParentFcb;
496
497 /* If this FCB hasn't been added to splay trees - just return */
498 if (!FlagOn( Fcb->State, FCB_STATE_HAS_NAMES ))
499 return;
500
501 /* Delete the short name link */
502 RootNew = RtlDelete(&Fcb->ShortName.Links);
503
504 /* Set the new root */
505 Parent->Dcb.SplayLinksAnsi = RootNew;
506
507 /* Deal with a unicode name if it exists */
508 if (FlagOn( Fcb->State, FCB_STATE_HAS_UNICODE_NAME ))
509 {
510 /* Delete the long unicode name link */
511 RootNew = RtlDelete(&Fcb->LongName.Links);
512
513 /* Set the new root */
514 Parent->Dcb.SplayLinksUnicode = RootNew;
515
516 /* Free the long name string's buffer*/
517 RtlFreeUnicodeString(&Fcb->LongName.Name.String);
518
519 /* Clear the "has unicode name" flag */
520 ClearFlag(Fcb->State, FCB_STATE_HAS_UNICODE_NAME);
521 }
522
523 /* This FCB has no names added to splay trees now */
524 ClearFlag(Fcb->State, FCB_STATE_HAS_NAMES);
525 }
526
527
528 /* EOF */