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