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