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