[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / fsrtl / largemcb.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL v2 - See COPYING in the top level directory
4 * FILE: ntoskrnl/fsrtl/largemcb.c
5 * PURPOSE: Large Mapped Control Block (MCB) support for File System Drivers
6 * PROGRAMMERS: Aleksey Bragin <aleksey@reactos.org>
7 * Jan Kratochvil <project-captive@jankratochvil.net>
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #define MIN(x,y) (((x)<(y))?(x):(y))
17 #define MAX(x,y) (((x)>(y))?(x):(y))
18
19 /* GLOBALS *******************************************************************/
20
21 PAGED_LOOKASIDE_LIST FsRtlFirstMappingLookasideList;
22 NPAGED_LOOKASIDE_LIST FsRtlFastMutexLookasideList;
23
24 /* We use only real 'mapping' runs; we do not store 'holes' to our GTree. */
25 typedef struct _LARGE_MCB_MAPPING_ENTRY // run
26 {
27 LARGE_INTEGER RunStartVbn;
28 LARGE_INTEGER RunEndVbn; /* RunStartVbn+SectorCount; that means +1 after the last sector */
29 LARGE_INTEGER StartingLbn; /* Lbn of 'RunStartVbn' */
30 } LARGE_MCB_MAPPING_ENTRY, *PLARGE_MCB_MAPPING_ENTRY;
31
32 typedef struct _LARGE_MCB_MAPPING // mcb_priv
33 {
34 RTL_GENERIC_TABLE Table;
35 } LARGE_MCB_MAPPING, *PLARGE_MCB_MAPPING;
36
37 typedef struct _BASE_MCB_INTERNAL {
38 ULONG MaximumPairCount;
39 ULONG PairCount;
40 USHORT PoolType;
41 USHORT Flags;
42 PLARGE_MCB_MAPPING Mapping;
43 } BASE_MCB_INTERNAL, *PBASE_MCB_INTERNAL;
44
45 static LARGE_MCB_MAPPING_ENTRY StaticRunBelow0 = {
46 {{-1}}, /* ignored */
47 {{0}},
48 {{-1}}, /* ignored */
49 };
50
51 static PVOID NTAPI McbMappingAllocate(PRTL_GENERIC_TABLE Table, CLONG Bytes)
52 {
53 PVOID Result;
54 PBASE_MCB Mcb = (PBASE_MCB)Table->TableContext;
55 Result = ExAllocatePoolWithTag(Mcb->PoolType, Bytes, 'LMCB');
56 DPRINT("McbMappingAllocate(%lu) => %p\n", Bytes, Result);
57 return Result;
58 }
59
60 static VOID NTAPI McbMappingFree(PRTL_GENERIC_TABLE Table, PVOID Buffer)
61 {
62 DPRINT("McbMappingFree(%p)\n", Buffer);
63 ExFreePoolWithTag(Buffer, 'LMCB');
64 }
65
66 static
67 RTL_GENERIC_COMPARE_RESULTS
68 NTAPI
69 McbMappingCompare(PRTL_GENERIC_TABLE Table,
70 PVOID PtrA,
71 PVOID PtrB)
72 {
73 PLARGE_MCB_MAPPING_ENTRY A = PtrA, B = PtrB;
74 RTL_GENERIC_COMPARE_RESULTS Res;
75
76 ASSERT(A);
77 ASSERT(B);
78
79 if (A->RunStartVbn.QuadPart == B->RunStartVbn.QuadPart && A->RunEndVbn.QuadPart == B->RunEndVbn.QuadPart)
80 Res = GenericEqual;
81 else if (A->RunEndVbn.QuadPart <= B->RunStartVbn.QuadPart)
82 Res = GenericLessThan;
83 else if (A->RunEndVbn.QuadPart >= B->RunStartVbn.QuadPart)
84 Res = GenericGreaterThan;
85 else
86 {
87 ASSERT(FALSE);
88 Res = GenericEqual;
89 }
90
91 return Res;
92 }
93
94 static RTL_GENERIC_COMPARE_RESULTS NTAPI McbMappingIntersectCompare(PRTL_GENERIC_TABLE Table, PVOID PtrA, PVOID PtrB)
95 {
96 PLARGE_MCB_MAPPING_ENTRY A = PtrA, B = PtrB;
97 RTL_GENERIC_COMPARE_RESULTS Res;
98
99 if (A->RunStartVbn.QuadPart <= B->RunStartVbn.QuadPart && A->RunEndVbn.QuadPart > B->RunStartVbn.QuadPart)
100 Res = GenericEqual;
101 else if (A->RunStartVbn.QuadPart >= B->RunStartVbn.QuadPart && B->RunEndVbn.QuadPart > A->RunStartVbn.QuadPart)
102 Res = GenericEqual;
103 else if (A->RunStartVbn.QuadPart < B->RunStartVbn.QuadPart)
104 Res = GenericLessThan;
105 else if (A->RunStartVbn.QuadPart > B->RunStartVbn.QuadPart)
106 Res = GenericGreaterThan;
107 else
108 Res = GenericEqual;
109
110 return Res;
111 }
112
113
114 /* PUBLIC FUNCTIONS **********************************************************/
115
116 /*
117 * @implemented
118 * @Mcb: #PLARGE_MCB initialized by FsRtlInitializeLargeMcb().
119 * %NULL value is forbidden.
120 * @Vbn: Starting virtual block number of the wished range.
121 * @Lbn: Starting logical block number of the wished range.
122 * @SectorCount: Length of the wished range.
123 * Value less or equal to %0 is forbidden; FIXME: Is the reject of %0 W32 compliant?
124 *
125 * Adds the specified range @Vbn ... @Vbn+@SectorCount-1 to @Mcb.
126 * Any mappings previously in this range are deleted first.
127 *
128 * Returns: %TRUE if successful.
129 */
130 BOOLEAN
131 NTAPI
132 FsRtlAddBaseMcbEntry(IN PBASE_MCB OpaqueMcb,
133 IN LONGLONG Vbn,
134 IN LONGLONG Lbn,
135 IN LONGLONG SectorCount)
136 {
137 BOOLEAN Result = TRUE;
138 PBASE_MCB_INTERNAL Mcb = (PBASE_MCB_INTERNAL)OpaqueMcb;
139 LARGE_MCB_MAPPING_ENTRY Node, NeedleRun;
140 PLARGE_MCB_MAPPING_ENTRY LowerRun, HigherRun;
141 BOOLEAN NewElement;
142
143 DPRINT("FsRtlAddBaseMcbEntry(%p, %I64d, %I64d, %I64d)\n", OpaqueMcb, Vbn, Lbn, SectorCount);
144
145 if (Vbn < 0)
146 {
147 Result = FALSE;
148 goto quit;
149 }
150
151 if (SectorCount <= 0)
152 {
153 Result = FALSE;
154 goto quit;
155 }
156
157 /* clean any possible previous entries in our range */
158 FsRtlRemoveBaseMcbEntry(OpaqueMcb, Vbn, SectorCount);
159
160 // We need to map [Vbn, Vbn+SectorCount) to [Lbn, Lbn+SectorCount),
161 // taking in account the fact that we need to merge these runs if
162 // they are adjacent or overlap, but fail if new run fully fits into another run
163
164 /* initially we think we will be inserted as a separate run */
165 Node.RunStartVbn.QuadPart = Vbn;
166 Node.RunEndVbn.QuadPart = Vbn + SectorCount;
167 Node.StartingLbn.QuadPart = Lbn;
168
169 /* optionally merge with lower run */
170 NeedleRun.RunStartVbn.QuadPart = Node.RunStartVbn.QuadPart - 1;
171 NeedleRun.RunEndVbn.QuadPart = NeedleRun.RunStartVbn.QuadPart + 1;
172 NeedleRun.StartingLbn.QuadPart = ~0ULL;
173 Mcb->Mapping->Table.CompareRoutine = McbMappingIntersectCompare;
174 if ((LowerRun = RtlLookupElementGenericTable(&Mcb->Mapping->Table, &NeedleRun)) &&
175 (LowerRun->StartingLbn.QuadPart < Node.StartingLbn.QuadPart))
176 {
177 ASSERT(LowerRun->RunEndVbn.QuadPart == Node.RunStartVbn.QuadPart);
178 Node.RunStartVbn.QuadPart = LowerRun->RunStartVbn.QuadPart;
179 Node.StartingLbn.QuadPart = LowerRun->StartingLbn.QuadPart;
180 Mcb->Mapping->Table.CompareRoutine = McbMappingCompare;
181 RtlDeleteElementGenericTable(&Mcb->Mapping->Table, LowerRun);
182 DPRINT("Intersecting lower run found (%I64d,%I64d) Lbn: %I64d\n", LowerRun->RunStartVbn.QuadPart, LowerRun->RunEndVbn.QuadPart, LowerRun->StartingLbn.QuadPart);
183 }
184
185 /* optionally merge with higher run */
186 NeedleRun.RunStartVbn.QuadPart = Node.RunEndVbn.QuadPart;
187 NeedleRun.RunEndVbn.QuadPart = NeedleRun.RunStartVbn.QuadPart + 1;
188 Mcb->Mapping->Table.CompareRoutine = McbMappingIntersectCompare;
189 if ((HigherRun = RtlLookupElementGenericTable(&Mcb->Mapping->Table, &NeedleRun)) &&
190 (Node.StartingLbn.QuadPart <= HigherRun->StartingLbn.QuadPart))
191 {
192 ASSERT(HigherRun->RunStartVbn.QuadPart == Node.RunEndVbn.QuadPart);
193 Node.RunEndVbn.QuadPart = HigherRun->RunEndVbn.QuadPart;
194 Mcb->Mapping->Table.CompareRoutine = McbMappingCompare;
195 RtlDeleteElementGenericTable(&Mcb->Mapping->Table, HigherRun);
196 DPRINT("Intersecting higher run found (%I64d,%I64d) Lbn: %I64d\n", HigherRun->RunStartVbn.QuadPart, HigherRun->RunEndVbn.QuadPart, HigherRun->StartingLbn.QuadPart);
197 }
198 Mcb->Mapping->Table.CompareRoutine = McbMappingCompare;
199
200 /* finally insert the resulting run */
201 RtlInsertElementGenericTable(&Mcb->Mapping->Table, &Node, sizeof(Node), &NewElement);
202 ASSERT(NewElement);
203
204 // NB: Two consecutive runs can only be merged, if actual LBNs also match!
205
206 /* 1.
207 Existing->RunStartVbn
208 |
209 |///////|
210 |/////////////|
211 |
212 Node->RunStartVbn
213
214 2.
215 Existing->RunStartVbn
216 |
217 |///////|
218 |//////|
219 |
220 Node->RunStartVbn
221
222 3.
223 Existing->RunStartVbn
224 |
225 |///////|
226 |///|
227 |
228 Node->RunStartVbn
229
230 4.
231 Existing->RunStartVbn
232 |
233 |///////|
234 |///////////////|
235 |
236 Node->RunStartVbn
237
238
239 Situation with holes:
240 1. Holes at both ends
241 2. Hole at the right, new run merged with the previous run
242 3. Hole at the right, new run is not merged with the previous run
243 4. Hole at the left, new run merged with the next run
244 5. Hole at the left, new run is not merged with the next run
245 6. No holes, exact fit to merge with both previous and next runs
246 7. No holes, merges only with the next run
247 8. No holes, merges only with the previous run
248 9. No holes, does not merge with next or prev runs
249
250
251 Overwriting existing mapping is not possible and results in FALSE being returned
252 */
253
254 quit:
255 DPRINT("FsRtlAddBaseMcbEntry(%p, %I64d, %I64d, %I64d) = %d\n", Mcb, Vbn, Lbn, SectorCount, Result);
256 return Result;
257 }
258
259 /*
260 * @implemented
261 */
262 BOOLEAN
263 NTAPI
264 FsRtlAddLargeMcbEntry(IN PLARGE_MCB Mcb,
265 IN LONGLONG Vbn,
266 IN LONGLONG Lbn,
267 IN LONGLONG SectorCount)
268 {
269 BOOLEAN Result;
270
271 DPRINT("FsRtlAddLargeMcbEntry(%p, %I64d, %I64d, %I64d)\n", Mcb, Vbn, Lbn, SectorCount);
272
273 KeAcquireGuardedMutex(Mcb->GuardedMutex);
274 Result = FsRtlAddBaseMcbEntry(&(Mcb->BaseMcb),
275 Vbn,
276 Lbn,
277 SectorCount);
278 KeReleaseGuardedMutex(Mcb->GuardedMutex);
279
280 DPRINT("FsRtlAddLargeMcbEntry(%p, %I64d, %I64d, %I64d) = %d\n", Mcb, Vbn, Lbn, SectorCount, Result);
281
282 return Result;
283 }
284
285 /*
286 * @implemented
287 * @Mcb: #PLARGE_MCB initialized by FsRtlInitializeLargeMcb().
288 * %NULL value is forbidden.
289 * @RunIndex: Requested range index to retrieve.
290 * @Vbn: Returns the starting virtual block number of the wished range.
291 * %NULL pointer is forbidden.
292 * @Lbn: Returns the starting logical block number of the wished range (or -1 if it is a hole).
293 * %NULL pointer is forbidden.
294 * @SectorCount: Returns the length of the wished range.
295 * %NULL pointer is forbidden.
296 * Value less or equal to %0 is forbidden; FIXME: Is the reject of %0 W32 compliant?
297 *
298 * Retrieves the parameters of the specified run with index @RunIndex.
299 *
300 * Mapping %0 always starts at virtual block %0, either as 'hole' or as 'real' mapping.
301 * libcaptive does not store 'hole' information to its #GTree.
302 * Last run is always a 'real' run. 'hole' runs appear as mapping to constant @Lbn value %-1.
303 *
304 * Returns: %TRUE if successful.
305 */
306 BOOLEAN
307 NTAPI
308 FsRtlGetNextBaseMcbEntry(IN PBASE_MCB OpaqueMcb,
309 IN ULONG RunIndex,
310 OUT PLONGLONG Vbn,
311 OUT PLONGLONG Lbn,
312 OUT PLONGLONG SectorCount)
313 {
314 BOOLEAN Result = FALSE;
315 PBASE_MCB_INTERNAL Mcb = (PBASE_MCB_INTERNAL)OpaqueMcb;
316 ULONG RunIndexRemaining;
317 PLARGE_MCB_MAPPING_ENTRY Run, RunFound = NULL, RunFoundLower = NULL, RunFoundHigher = NULL;
318 BOOLEAN First = TRUE;
319
320 DPRINT("FsRtlGetNextBaseMcbEntry(%p, %d, %p, %p, %p)\n", OpaqueMcb, RunIndex, Vbn, Lbn, SectorCount);
321
322 RunIndexRemaining = RunIndex;
323
324 /* Traverse the tree */
325 for (Run = (PLARGE_MCB_MAPPING_ENTRY)RtlEnumerateGenericTable(&Mcb->Mapping->Table, TRUE);
326 Run;
327 Run = (PLARGE_MCB_MAPPING_ENTRY)RtlEnumerateGenericTable(&Mcb->Mapping->Table, FALSE))
328 {
329 if (First)
330 {
331 /* Take care when we must emulate missing 'hole' run at start of our run list. */
332 if (Run->RunStartVbn.QuadPart > 0)
333 {
334 if (RunIndexRemaining == 0)
335 {
336 RunFoundLower = &StaticRunBelow0;
337 RunFoundHigher = Run;
338
339 /* stop the traversal */
340 break;
341 }
342 /* If someone wants RunIndex #1 we are already on it. */
343 RunIndexRemaining--;
344 }
345 First = FALSE;
346 }
347
348 if (RunIndexRemaining > 0)
349 {
350 /* FIXME: performance: non-linear direct seek to the requested RunIndex */
351 RunIndexRemaining--;
352 if (RunIndexRemaining == 0)
353 RunFoundLower = Run;
354 else
355 RunIndexRemaining--;
356
357 /* continue the traversal */
358 continue;
359 }
360
361 if (RunFoundLower)
362 RunFoundHigher = Run;
363 else
364 RunFound = Run;
365
366 /* stop the traversal */
367 break;
368 }
369
370 if (RunFound) DPRINT("RunFound(%lu %lu %lu)\n", RunFound->RunStartVbn.LowPart, RunFound->RunEndVbn.LowPart, RunFound->StartingLbn.LowPart);
371 if (RunFoundLower) DPRINT("RunFoundLower(%lu %lu %lu)\n", RunFoundLower->RunStartVbn.LowPart, RunFoundLower->RunEndVbn.LowPart, RunFoundLower->StartingLbn.LowPart);
372 if (RunFoundHigher) DPRINT("RunFoundHigher(%lu %lu %lu)\n", RunFoundHigher->RunStartVbn.LowPart, RunFoundHigher->RunEndVbn.LowPart, RunFoundHigher->StartingLbn.LowPart);
373
374 if (RunFound)
375 {
376 ASSERT(RunFoundLower == NULL);
377 ASSERT(RunFoundHigher == NULL);
378
379 if (Vbn)
380 *Vbn = RunFound->RunStartVbn.QuadPart;
381 if (Lbn)
382 *Lbn = RunFound->StartingLbn.QuadPart;
383 if (SectorCount)
384 *SectorCount = RunFound->RunEndVbn.QuadPart - RunFound->RunStartVbn.QuadPart;
385
386 Result = TRUE;
387 goto quit;
388 }
389
390 if (RunFoundLower && RunFoundHigher)
391 {
392 //ASSERT(RunFoundHigher != NULL);
393
394 if (Vbn)
395 *Vbn = RunFoundLower->RunEndVbn.QuadPart;
396 if (Lbn)
397 *Lbn = -1;
398 if (SectorCount)
399 *SectorCount = RunFoundHigher->RunStartVbn.QuadPart - RunFoundLower->RunEndVbn.QuadPart;
400
401 Result = TRUE;
402 goto quit;
403 }
404
405 ASSERT(RunFoundHigher == NULL);
406
407 quit:
408 DPRINT("FsRtlGetNextBaseMcbEntry(%p, %d, %p, %p, %p) = %d (%I64d, %I64d, %I64d)\n", Mcb, RunIndex, Vbn, Lbn, SectorCount, Result, *Vbn, *Lbn, *SectorCount);
409 return Result;
410 }
411
412 /*
413 * @implemented
414 */
415 BOOLEAN
416 NTAPI
417 FsRtlGetNextLargeMcbEntry(IN PLARGE_MCB Mcb,
418 IN ULONG RunIndex,
419 OUT PLONGLONG Vbn,
420 OUT PLONGLONG Lbn,
421 OUT PLONGLONG SectorCount)
422 {
423 BOOLEAN Result;
424
425 DPRINT("FsRtlGetNextLargeMcbEntry(%p, %d, %p, %p, %p)\n", Mcb, RunIndex, Vbn, Lbn, SectorCount);
426
427 KeAcquireGuardedMutex(Mcb->GuardedMutex);
428 Result = FsRtlGetNextBaseMcbEntry(&(Mcb->BaseMcb),
429 RunIndex,
430 Vbn,
431 Lbn,
432 SectorCount);
433 KeReleaseGuardedMutex(Mcb->GuardedMutex);
434
435 DPRINT("FsRtlGetNextLargeMcbEntry(%p, %d, %p, %p, %p) = %d (%I64d, %I64d, %I64d)\n", Mcb, RunIndex, Vbn, Lbn, SectorCount, Result, *Vbn, *Lbn, *SectorCount);
436
437 return Result;
438 }
439
440 /*
441 * @implemented
442 */
443 VOID
444 NTAPI
445 FsRtlInitializeBaseMcb(IN PBASE_MCB OpaqueMcb,
446 IN POOL_TYPE PoolType)
447 {
448 PBASE_MCB_INTERNAL Mcb = (PBASE_MCB_INTERNAL)OpaqueMcb;
449
450 if (PoolType == PagedPool)
451 {
452 Mcb->Mapping = ExAllocateFromPagedLookasideList(&FsRtlFirstMappingLookasideList);
453 }
454 else
455 {
456 Mcb->Mapping = ExAllocatePoolWithTag(PoolType | POOL_RAISE_IF_ALLOCATION_FAILURE,
457 sizeof(LARGE_MCB_MAPPING),
458 'FSBC');
459 }
460
461 Mcb->PoolType = PoolType;
462 Mcb->PairCount = 0;
463 Mcb->MaximumPairCount = MAXIMUM_PAIR_COUNT;
464 RtlInitializeGenericTable(&Mcb->Mapping->Table,
465 McbMappingCompare,
466 McbMappingAllocate,
467 McbMappingFree,
468 Mcb);
469 }
470
471 /*
472 * @implemented
473 */
474 VOID
475 NTAPI
476 FsRtlInitializeLargeMcb(IN PLARGE_MCB Mcb,
477 IN POOL_TYPE PoolType)
478 {
479 DPRINT("FsRtlInitializeLargeMcb(%p, %d)\n", Mcb, PoolType);
480
481 Mcb->GuardedMutex = ExAllocateFromNPagedLookasideList(&FsRtlFastMutexLookasideList);
482
483 KeInitializeGuardedMutex(Mcb->GuardedMutex);
484
485 _SEH2_TRY
486 {
487 FsRtlInitializeBaseMcb(&(Mcb->BaseMcb), PoolType);
488 }
489 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
490 {
491 ExFreeToNPagedLookasideList(&FsRtlFastMutexLookasideList,
492 Mcb->GuardedMutex);
493 Mcb->GuardedMutex = NULL;
494 }
495 _SEH2_END;
496 }
497
498 /*
499 * @implemented
500 */
501 INIT_FUNCTION
502 VOID
503 NTAPI
504 FsRtlInitializeLargeMcbs(VOID)
505 {
506 /* Initialize the list for the MCB */
507 ExInitializePagedLookasideList(&FsRtlFirstMappingLookasideList,
508 NULL,
509 NULL,
510 POOL_RAISE_IF_ALLOCATION_FAILURE,
511 sizeof(LARGE_MCB_MAPPING),
512 IFS_POOL_TAG,
513 0); /* FIXME: Should be 4 */
514
515 /* Initialize the list for the guarded mutex */
516 ExInitializeNPagedLookasideList(&FsRtlFastMutexLookasideList,
517 NULL,
518 NULL,
519 POOL_RAISE_IF_ALLOCATION_FAILURE,
520 sizeof(KGUARDED_MUTEX),
521 IFS_POOL_TAG,
522 0); /* FIXME: Should be 32 */
523 }
524
525 /*
526 * @unimplemented
527 */
528 BOOLEAN
529 NTAPI
530 FsRtlLookupBaseMcbEntry(IN PBASE_MCB OpaqueMcb,
531 IN LONGLONG Vbn,
532 OUT PLONGLONG Lbn OPTIONAL,
533 OUT PLONGLONG SectorCountFromLbn OPTIONAL,
534 OUT PLONGLONG StartingLbn OPTIONAL,
535 OUT PLONGLONG SectorCountFromStartingLbn OPTIONAL,
536 OUT PULONG Index OPTIONAL)
537 {
538 BOOLEAN Result = FALSE;
539 PBASE_MCB_INTERNAL Mcb = (PBASE_MCB_INTERNAL)OpaqueMcb;
540
541 ULONG RunIndex = 0;
542 PLARGE_MCB_MAPPING_ENTRY Run, RunFound = NULL, RunFoundLower = NULL, RunFoundHigher = NULL;
543 BOOLEAN First = TRUE;
544
545 DPRINT("FsRtlLookupBaseMcbEntry(%p, %I64d, %p, %p, %p, %p, %p)\n", OpaqueMcb, Vbn, Lbn, SectorCountFromLbn, StartingLbn, SectorCountFromStartingLbn, Index);
546
547 /* Traverse the tree */
548 for (Run = (PLARGE_MCB_MAPPING_ENTRY)RtlEnumerateGenericTable(&Mcb->Mapping->Table, TRUE);
549 Run;
550 Run = (PLARGE_MCB_MAPPING_ENTRY)RtlEnumerateGenericTable(&Mcb->Mapping->Table, FALSE))
551 {
552 if (First)
553 {
554 /* Take care when we must emulate missing 'hole' run at start of our run list. */
555 if (Run->RunStartVbn.QuadPart > 0)
556 {
557 RunIndex++;
558 RunFoundLower = &StaticRunBelow0;
559 }
560 First = FALSE;
561 }
562
563 if (Run->RunStartVbn.QuadPart <= Vbn && Vbn < Run->RunEndVbn.QuadPart)
564 {
565 RunFound = Run;
566 RunFoundLower = NULL;
567 /* stop the traversal; hit */
568 break;
569 }
570
571 if (Run->RunEndVbn.QuadPart <= Vbn)
572 {
573 RunFoundLower = Run;
574 if (Run->StartingLbn.QuadPart > 0)
575 {
576 RunIndex += 2;
577 }
578 /* continue the traversal; not yet crossed by the run */
579 continue;
580 }
581
582 if (Vbn < Run->RunStartVbn.QuadPart)
583 {
584 RunFoundHigher = Run;
585 RunIndex++;
586 /* stop the traversal; the run skipped us */
587 break;
588 }
589
590 ASSERT(FALSE);
591 /* stop the traversal */
592 break;
593 }
594
595 if (RunFound)
596 {
597 ASSERT(RunFoundLower == NULL);
598 ASSERT(RunFoundHigher == NULL);
599
600 if (Lbn)
601 *Lbn = RunFound->StartingLbn.QuadPart + (Vbn - RunFound->RunStartVbn.QuadPart);
602
603 if (SectorCountFromLbn) /* FIXME: 'after' means including current 'Lbn' or without it? */
604 *SectorCountFromLbn = RunFound->RunEndVbn.QuadPart - Vbn;
605 if (StartingLbn)
606 *StartingLbn = RunFound->StartingLbn.QuadPart;
607 if (SectorCountFromStartingLbn)
608 *SectorCountFromStartingLbn = RunFound->RunEndVbn.QuadPart - RunFound->RunStartVbn.QuadPart;
609 if (Index)
610 *Index = RunIndex;
611
612 Result = TRUE;
613 goto quit;
614 }
615
616 if (RunFoundHigher)
617 {
618 /* search for hole */
619 ASSERT(RunFoundLower != NULL);
620
621 if (Lbn)
622 *Lbn = ~0ull;
623 if (SectorCountFromLbn) /* FIXME: 'after' means including current 'Lbn' or without it? */
624 *SectorCountFromLbn = RunFoundHigher->RunStartVbn.QuadPart - Vbn;
625 if (StartingLbn)
626 *StartingLbn = ~0ull;
627 if (SectorCountFromStartingLbn)
628 *SectorCountFromStartingLbn = RunFoundHigher->RunStartVbn.QuadPart - RunFoundLower->RunEndVbn.QuadPart;
629 if (Index)
630 *Index = RunIndex - 2;
631
632 Result = TRUE;
633 goto quit;
634 }
635
636 /* We may have some 'RunFoundLower'. */
637
638 quit:
639 DPRINT("FsRtlLookupBaseMcbEntry(%p, %I64d, %p, %p, %p, %p, %p) = %d (%I64d, %I64d, %I64d, %I64d, %d)\n",
640 OpaqueMcb, Vbn, Lbn, SectorCountFromLbn, StartingLbn, SectorCountFromStartingLbn, Index, Result,
641 (Lbn ? *Lbn : (ULONGLONG)-1), (SectorCountFromLbn ? *SectorCountFromLbn : (ULONGLONG)-1), (StartingLbn ? *StartingLbn : (ULONGLONG)-1),
642 (SectorCountFromStartingLbn ? *SectorCountFromStartingLbn : (ULONGLONG)-1), (Index ? *Index : (ULONG)-1));
643
644 return Result;
645 }
646
647 /*
648 * @implemented
649 */
650 BOOLEAN
651 NTAPI
652 FsRtlLookupLargeMcbEntry(IN PLARGE_MCB Mcb,
653 IN LONGLONG Vbn,
654 OUT PLONGLONG Lbn OPTIONAL,
655 OUT PLONGLONG SectorCountFromLbn OPTIONAL,
656 OUT PLONGLONG StartingLbn OPTIONAL,
657 OUT PLONGLONG SectorCountFromStartingLbn OPTIONAL,
658 OUT PULONG Index OPTIONAL)
659 {
660 BOOLEAN Result;
661
662 DPRINT("FsRtlLookupLargeMcbEntry(%p, %I64d, %p, %p, %p, %p, %p)\n", Mcb, Vbn, Lbn, SectorCountFromLbn, StartingLbn, SectorCountFromStartingLbn, Index);
663
664 KeAcquireGuardedMutex(Mcb->GuardedMutex);
665 Result = FsRtlLookupBaseMcbEntry(&(Mcb->BaseMcb),
666 Vbn,
667 Lbn,
668 SectorCountFromLbn,
669 StartingLbn,
670 SectorCountFromStartingLbn,
671 Index);
672 KeReleaseGuardedMutex(Mcb->GuardedMutex);
673
674 DPRINT("FsRtlLookupLargeMcbEntry(%p, %I64d, %p, %p, %p, %p, %p) = %d (%I64d, %I64d, %I64d, %I64d, %d)\n",
675 Mcb, Vbn, Lbn, SectorCountFromLbn, StartingLbn, SectorCountFromStartingLbn, Index, Result,
676 (Lbn ? *Lbn : (ULONGLONG)-1), (SectorCountFromLbn ? *SectorCountFromLbn : (ULONGLONG)-1), (StartingLbn ? *StartingLbn : (ULONGLONG)-1),
677 (SectorCountFromStartingLbn ? *SectorCountFromStartingLbn : (ULONGLONG)-1), (Index ? *Index : (ULONG)-1));
678
679 return Result;
680 }
681
682 static BOOLEAN
683 NTAPI
684 FsRtlLookupLastLargeMcbEntryAndIndex_internal(IN PBASE_MCB_INTERNAL Mcb,
685 OUT PLONGLONG Vbn,
686 OUT PLONGLONG Lbn,
687 OUT PULONG Index OPTIONAL)
688 {
689 ULONG RunIndex = 0;
690 PLARGE_MCB_MAPPING_ENTRY Run, RunFound = NULL;
691 LONGLONG LastVbn = 0;
692
693 for (Run = (PLARGE_MCB_MAPPING_ENTRY)RtlEnumerateGenericTable(&Mcb->Mapping->Table, TRUE);
694 Run;
695 Run = (PLARGE_MCB_MAPPING_ENTRY)RtlEnumerateGenericTable(&Mcb->Mapping->Table, FALSE))
696 {
697 /* Take care when we must emulate missing 'hole' runs. */
698 if (Run->RunStartVbn.QuadPart > LastVbn)
699 {
700 RunIndex++;
701 }
702 LastVbn = Run->RunEndVbn.QuadPart;
703 RunIndex++;
704 RunFound = Run;
705 }
706
707 if (!RunFound)
708 {
709 return FALSE;
710 }
711
712 if (Vbn)
713 {
714 *Vbn = RunFound->RunEndVbn.QuadPart - 1;
715 }
716 if (Lbn)
717 {
718 if (1)
719 {
720 *Lbn = RunFound->StartingLbn.QuadPart + (RunFound->RunEndVbn.QuadPart - RunFound->RunStartVbn.QuadPart) - 1;
721 }
722 else
723 {
724 *Lbn = ~0ULL;
725 }
726 }
727 if (Index)
728 {
729 *Index = RunIndex - 1;
730 }
731
732 return TRUE;
733 }
734
735
736 /*
737 * @implemented
738 */
739 BOOLEAN
740 NTAPI
741 FsRtlLookupLastBaseMcbEntryAndIndex(IN PBASE_MCB OpaqueMcb,
742 IN OUT PLONGLONG LargeVbn,
743 IN OUT PLONGLONG LargeLbn,
744 IN OUT PULONG Index)
745 {
746 BOOLEAN Result;
747 PBASE_MCB_INTERNAL Mcb = (PBASE_MCB_INTERNAL)OpaqueMcb;
748
749 DPRINT("FsRtlLookupLastBaseMcbEntryAndIndex(%p, %p, %p, %p)\n", OpaqueMcb, LargeVbn, LargeLbn, Index);
750
751 Result = FsRtlLookupLastLargeMcbEntryAndIndex_internal(Mcb, LargeVbn, LargeLbn, Index);
752
753 DPRINT("FsRtlLookupLastBaseMcbEntryAndIndex(%p, %p, %p, %p) = %d (%I64d, %I64d, %d)\n", OpaqueMcb, LargeVbn, LargeLbn, Index, Result, *LargeVbn, *LargeLbn, *Index);
754
755 return Result;
756 }
757
758 /*
759 * @implemented
760 */
761 BOOLEAN
762 NTAPI
763 FsRtlLookupLastLargeMcbEntryAndIndex(IN PLARGE_MCB OpaqueMcb,
764 OUT PLONGLONG LargeVbn,
765 OUT PLONGLONG LargeLbn,
766 OUT PULONG Index)
767 {
768 BOOLEAN Result;
769
770 DPRINT("FsRtlLookupLastLargeMcbEntryAndIndex(%p, %p, %p, %p)\n", OpaqueMcb, LargeVbn, LargeLbn, Index);
771
772 KeAcquireGuardedMutex(OpaqueMcb->GuardedMutex);
773 Result = FsRtlLookupLastBaseMcbEntryAndIndex(&(OpaqueMcb->BaseMcb),
774 LargeVbn,
775 LargeLbn,
776 Index);
777 KeReleaseGuardedMutex(OpaqueMcb->GuardedMutex);
778
779 DPRINT("FsRtlLookupLastLargeMcbEntryAndIndex(%p, %p, %p, %p) = %d (%I64d, %I64d, %d)\n", OpaqueMcb, LargeVbn, LargeLbn, Index, Result, *LargeVbn, *LargeLbn, *Index);
780
781 return Result;
782 }
783
784 /*
785 * @unimplemented
786 */
787 BOOLEAN
788 NTAPI
789 FsRtlLookupLastBaseMcbEntry(IN PBASE_MCB OpaqueMcb,
790 OUT PLONGLONG Vbn,
791 OUT PLONGLONG Lbn)
792 {
793 BOOLEAN Result;
794 PBASE_MCB_INTERNAL Mcb = (PBASE_MCB_INTERNAL)OpaqueMcb;
795
796 DPRINT("FsRtlLookupLastBaseMcbEntry(%p, %p, %p)\n", OpaqueMcb, Vbn, Lbn);
797
798 Result = FsRtlLookupLastLargeMcbEntryAndIndex_internal(Mcb, Vbn, Lbn, NULL); /* Index */
799
800 DPRINT("FsRtlLookupLastBaseMcbEntry(%p, %p, %p) = %d (%I64d, %I64d)\n", Mcb, Vbn, Lbn, Result, *Vbn, *Lbn);
801
802 return Result;
803 }
804
805 /*
806 * @implemented
807 */
808 BOOLEAN
809 NTAPI
810 FsRtlLookupLastLargeMcbEntry(IN PLARGE_MCB Mcb,
811 OUT PLONGLONG Vbn,
812 OUT PLONGLONG Lbn)
813 {
814 BOOLEAN Result;
815
816 DPRINT("FsRtlLookupLastLargeMcbEntry(%p, %p, %p)\n", Mcb, Vbn, Lbn);
817
818 KeAcquireGuardedMutex(Mcb->GuardedMutex);
819 Result = FsRtlLookupLastBaseMcbEntry(&(Mcb->BaseMcb),
820 Vbn,
821 Lbn);
822 KeReleaseGuardedMutex(Mcb->GuardedMutex);
823
824 DPRINT("FsRtlLookupLastLargeMcbEntry(%p, %p, %p) = %d (%I64d, %I64d)\n", Mcb, Vbn, Lbn, Result, *Vbn, *Lbn);
825
826 return Result;
827 }
828
829 /*
830 * @implemented
831 */
832 ULONG
833 NTAPI
834 FsRtlNumberOfRunsInBaseMcb(IN PBASE_MCB OpaqueMcb)
835 {
836 PBASE_MCB_INTERNAL Mcb = (PBASE_MCB_INTERNAL)OpaqueMcb;
837 LONGLONG LbnAtVbn0 = -1;
838 ULONG Nodes = RtlNumberGenericTableElements(&Mcb->Mapping->Table);
839 ULONG NumberOfRuns = 0;
840
841 DPRINT("FsRtlNumberOfRunsInBaseMcb(%p)\n", OpaqueMcb);
842
843 if (Nodes == 0) goto quit;
844
845 FsRtlLookupBaseMcbEntry(OpaqueMcb,
846 0, /* Vbn */
847 &LbnAtVbn0, /* Lbn */
848 NULL, NULL, NULL, NULL); /* 4 output arguments - not interested in them */
849
850
851 /* Return the count */
852 //return Mcb->PairCount;
853 /* Return the number of 'real' and 'hole' runs.
854 * If we do not have sector 0 as 'real' emulate a 'hole' there.
855 */
856 NumberOfRuns = Nodes * 2 - (LbnAtVbn0 != -1 ? 1 : 0); /* include holes as runs */
857
858 quit:
859 DPRINT("FsRtlNumberOfRunsInBaseMcb(%p) = %d\n", OpaqueMcb, NumberOfRuns);
860 return NumberOfRuns;
861 }
862
863 /*
864 * @implemented
865 */
866 ULONG
867 NTAPI
868 FsRtlNumberOfRunsInLargeMcb(IN PLARGE_MCB Mcb)
869 {
870 ULONG NumberOfRuns;
871
872 DPRINT("FsRtlNumberOfRunsInLargeMcb(%p)\n", Mcb);
873
874 /* Read the number of runs while holding the MCB lock */
875 KeAcquireGuardedMutex(Mcb->GuardedMutex);
876 NumberOfRuns = FsRtlNumberOfRunsInBaseMcb(&(Mcb->BaseMcb));
877 KeReleaseGuardedMutex(Mcb->GuardedMutex);
878
879 DPRINT("FsRtlNumberOfRunsInLargeMcb(%p) = %d\n", Mcb, NumberOfRuns);
880
881 /* Return the count */
882 return NumberOfRuns;
883 }
884
885 /*
886 * @implemented
887 * @Mcb: #PLARGE_MCB initialized by FsRtlInitializeLargeMcb().
888 * %NULL value is forbidden.
889 * @Vbn: Starting virtual block number to specify the range to delete.
890 * @SectorCount: Length of the range to delete.
891 * Value less or equal to %0 is forbidden; FIXME: Is the reject of %0 W32 compliant?
892 *
893 * Deletes any possible @Mcb mappings in the given range @Vbn ... @Vbn+@SectorCount-1.
894 * This call has no problems if no mappings exist there yet.
895 */
896 BOOLEAN
897 NTAPI
898 FsRtlRemoveBaseMcbEntry(IN PBASE_MCB OpaqueMcb,
899 IN LONGLONG Vbn,
900 IN LONGLONG SectorCount)
901 {
902 PBASE_MCB_INTERNAL Mcb = (PBASE_MCB_INTERNAL)OpaqueMcb;
903 LARGE_MCB_MAPPING_ENTRY NeedleRun;
904 PLARGE_MCB_MAPPING_ENTRY HaystackRun;
905 BOOLEAN Result = TRUE;
906
907 DPRINT("FsRtlRemoveBaseMcbEntry(%p, %I64d, %I64d)\n", OpaqueMcb, Vbn, SectorCount);
908
909 if (Vbn < 0 || SectorCount <= 0)
910 {
911 Result = FALSE;
912 goto quit;
913 }
914
915 if (Vbn + SectorCount <= Vbn)
916 {
917 Result = FALSE;
918 goto quit;
919 }
920
921 NeedleRun.RunStartVbn.QuadPart = Vbn;
922 NeedleRun.RunEndVbn.QuadPart = Vbn + SectorCount;
923 NeedleRun.StartingLbn.QuadPart = ~0ULL;
924
925 /* adjust/destroy all intersecting ranges */
926 Mcb->Mapping->Table.CompareRoutine = McbMappingIntersectCompare;
927 while ((HaystackRun = RtlLookupElementGenericTable(&Mcb->Mapping->Table, &NeedleRun)))
928 {
929 if (HaystackRun->RunStartVbn.QuadPart < NeedleRun.RunStartVbn.QuadPart)
930 {
931 ASSERT(HaystackRun->RunEndVbn.QuadPart > NeedleRun.RunStartVbn.QuadPart);
932 HaystackRun->RunEndVbn.QuadPart = NeedleRun.RunStartVbn.QuadPart;
933 }
934 else if (HaystackRun->RunEndVbn.QuadPart > NeedleRun.RunEndVbn.QuadPart)
935 {
936 ASSERT(HaystackRun->RunStartVbn.QuadPart < NeedleRun.RunEndVbn.QuadPart);
937 HaystackRun->RunStartVbn.QuadPart = NeedleRun.RunEndVbn.QuadPart;
938 }
939 else
940 {
941 //ASSERT(NeedleRun.RunStartVbn.QuadPart >= HaystackRun->RunStartVbn.QuadPart);
942 //ASSERT(NeedleRun.RunEndVbn.QuadPart <= HaystackRun->RunEndVbn.QuadPart);
943 Mcb->Mapping->Table.CompareRoutine = McbMappingCompare;
944 RtlDeleteElementGenericTable(&Mcb->Mapping->Table, HaystackRun);
945 Mcb->Mapping->Table.CompareRoutine = McbMappingIntersectCompare;
946 }
947 }
948 Mcb->Mapping->Table.CompareRoutine = McbMappingCompare;
949
950 quit:
951 DPRINT("FsRtlRemoveBaseMcbEntry(%p, %I64d, %I64d) = %d\n", OpaqueMcb, Vbn, SectorCount, Result);
952 return Result;
953 }
954
955 /*
956 * @implemented
957 */
958 VOID
959 NTAPI
960 FsRtlRemoveLargeMcbEntry(IN PLARGE_MCB Mcb,
961 IN LONGLONG Vbn,
962 IN LONGLONG SectorCount)
963 {
964 DPRINT("FsRtlRemoveLargeMcbEntry(%p, %I64d, %I64d)\n", Mcb, Vbn, SectorCount);
965
966 KeAcquireGuardedMutex(Mcb->GuardedMutex);
967 FsRtlRemoveBaseMcbEntry(&(Mcb->BaseMcb), Vbn, SectorCount);
968 KeReleaseGuardedMutex(Mcb->GuardedMutex);
969 }
970
971 /*
972 * @implemented
973 */
974 VOID
975 NTAPI
976 FsRtlResetBaseMcb(IN PBASE_MCB OpaqueMcb)
977 {
978 PBASE_MCB_INTERNAL Mcb = (PBASE_MCB_INTERNAL)OpaqueMcb;
979 PLARGE_MCB_MAPPING_ENTRY Element;
980
981 DPRINT("FsRtlResetBaseMcb(%p)\n", OpaqueMcb);
982
983 while (RtlNumberGenericTableElements(&Mcb->Mapping->Table) &&
984 (Element = (PLARGE_MCB_MAPPING_ENTRY)RtlGetElementGenericTable(&Mcb->Mapping->Table, 0)))
985 {
986 RtlDeleteElementGenericTable(&Mcb->Mapping->Table, Element);
987 }
988
989 Mcb->PairCount = 0;
990 Mcb->MaximumPairCount = 0;
991 }
992
993 /*
994 * @implemented
995 */
996 VOID
997 NTAPI
998 FsRtlResetLargeMcb(IN PLARGE_MCB Mcb,
999 IN BOOLEAN SelfSynchronized)
1000 {
1001 DPRINT("FsRtlResetLargeMcb(%p, %d)\n", Mcb, SelfSynchronized);
1002
1003 if (!SelfSynchronized)
1004 KeAcquireGuardedMutex(Mcb->GuardedMutex);
1005
1006 FsRtlResetBaseMcb(&Mcb->BaseMcb);
1007
1008 if (!SelfSynchronized)
1009 KeReleaseGuardedMutex(Mcb->GuardedMutex);
1010 }
1011
1012 /*
1013 * @unimplemented
1014 */
1015 BOOLEAN
1016 NTAPI
1017 FsRtlSplitBaseMcb(IN PBASE_MCB OpaqueMcb,
1018 IN LONGLONG Vbn,
1019 IN LONGLONG Amount)
1020 {
1021 PBASE_MCB_INTERNAL Mcb = (PBASE_MCB_INTERNAL)OpaqueMcb;
1022 PLARGE_MCB_MAPPING_ENTRY Run, InsertLowerRun = NULL, ExistingRun = NULL;
1023 BOOLEAN NewElement;
1024
1025 DPRINT("FsRtlSplitBaseMcb(%p, %I64d, %I64d)\n", OpaqueMcb, Vbn, Amount);
1026
1027 /* Traverse the tree */
1028 for (Run = (PLARGE_MCB_MAPPING_ENTRY)RtlEnumerateGenericTable(&Mcb->Mapping->Table, TRUE);
1029 Run;
1030 Run = (PLARGE_MCB_MAPPING_ENTRY)RtlEnumerateGenericTable(&Mcb->Mapping->Table, FALSE))
1031 {
1032 /* unaffected run? */
1033 /* FIXME: performance: effective skip of all 'lower' runs without traversing them */
1034 if (Vbn >= Run->RunEndVbn.QuadPart) { DPRINT("Skipping it\n"); continue; }
1035
1036 /* crossing run to be split?
1037 * 'lower_run' is created on the original place; just shortened.
1038 * current 'run' is shifted up later
1039 */
1040 if (Vbn < Run->RunEndVbn.QuadPart)
1041 {
1042 /* FIXME: shift 'run->Lbn_start' ? */
1043 Run->RunStartVbn.QuadPart = Vbn;
1044
1045 InsertLowerRun = NULL;
1046 }
1047
1048 /* Shift the current 'run'.
1049 * Ordering is not changed in Generic Tree so I hope I do not need to reinsert it.
1050 */
1051 Run->RunStartVbn.QuadPart += Amount;
1052 ASSERT(Run->RunEndVbn.QuadPart + Amount > Run->RunEndVbn.QuadPart); /* overflow? */
1053 Run->RunEndVbn.QuadPart += Amount;
1054 /* FIXME: shift 'run->Lbn_start' ? */
1055
1056 /* continue the traversal */
1057 }
1058
1059 if (InsertLowerRun)
1060 ExistingRun = RtlInsertElementGenericTable(&Mcb->Mapping->Table, InsertLowerRun, sizeof(*InsertLowerRun), &NewElement);
1061
1062 ASSERT(ExistingRun == NULL);
1063
1064 DPRINT("FsRtlSplitBaseMcb(%p, %I64d, %I64d) = %d\n", OpaqueMcb, Vbn, Amount, TRUE);
1065
1066 return TRUE;
1067 }
1068
1069 /*
1070 * @implemented
1071 */
1072 BOOLEAN
1073 NTAPI
1074 FsRtlSplitLargeMcb(IN PLARGE_MCB Mcb,
1075 IN LONGLONG Vbn,
1076 IN LONGLONG Amount)
1077 {
1078 BOOLEAN Result;
1079
1080 DPRINT("FsRtlSplitLargeMcb(%p, %I64d, %I64d)\n", Mcb, Vbn, Amount);
1081
1082 KeAcquireGuardedMutex(Mcb->GuardedMutex);
1083 Result = FsRtlSplitBaseMcb(&(Mcb->BaseMcb),
1084 Vbn,
1085 Amount);
1086 KeReleaseGuardedMutex(Mcb->GuardedMutex);
1087
1088 DPRINT("FsRtlSplitLargeMcb(%p, %I64d, %I64d) = %d\n", Mcb, Vbn, Amount, Result);
1089
1090 return Result;
1091 }
1092
1093 /*
1094 * @unimplemented
1095 */
1096 VOID
1097 NTAPI
1098 FsRtlTruncateBaseMcb(IN PBASE_MCB OpaqueMcb,
1099 IN LONGLONG Vbn)
1100 {
1101 DPRINT("FsRtlTruncateBaseMcb(%p, %I64d)\n", OpaqueMcb, Vbn);
1102
1103 FsRtlRemoveBaseMcbEntry(OpaqueMcb, Vbn, MAXLONG - Vbn + 1);
1104 }
1105
1106 /*
1107 * @implemented
1108 */
1109 VOID
1110 NTAPI
1111 FsRtlTruncateLargeMcb(IN PLARGE_MCB Mcb,
1112 IN LONGLONG Vbn)
1113 {
1114 DPRINT("FsRtlTruncateLargeMcb(%p, %I64d)\n", Mcb, Vbn);
1115
1116 KeAcquireGuardedMutex(Mcb->GuardedMutex);
1117 FsRtlTruncateBaseMcb(&(Mcb->BaseMcb), Vbn);
1118 KeReleaseGuardedMutex(Mcb->GuardedMutex);
1119 }
1120
1121 /*
1122 * @implemented
1123 */
1124 VOID
1125 NTAPI
1126 FsRtlUninitializeBaseMcb(IN PBASE_MCB Mcb)
1127 {
1128 DPRINT("FsRtlUninitializeBaseMcb(%p)\n", Mcb);
1129
1130 FsRtlResetBaseMcb(Mcb);
1131
1132 if ((Mcb->PoolType == PagedPool)/* && (Mcb->MaximumPairCount == MAXIMUM_PAIR_COUNT)*/)
1133 {
1134 ExFreeToPagedLookasideList(&FsRtlFirstMappingLookasideList,
1135 Mcb->Mapping);
1136 }
1137 else
1138 {
1139 ExFreePoolWithTag(Mcb->Mapping, 'FSBC');
1140 }
1141 }
1142
1143 /*
1144 * @implemented
1145 */
1146 VOID
1147 NTAPI
1148 FsRtlUninitializeLargeMcb(IN PLARGE_MCB Mcb)
1149 {
1150 DPRINT("FsRtlUninitializeLargeMcb(%p)\n", Mcb);
1151
1152 if (Mcb->GuardedMutex)
1153 {
1154 ExFreeToNPagedLookasideList(&FsRtlFastMutexLookasideList,
1155 Mcb->GuardedMutex);
1156 FsRtlUninitializeBaseMcb(&(Mcb->BaseMcb));
1157 }
1158 }