1 ////////////////////////////////////////////////////////////////////
2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
3 // All rights reserved
4 // This file was released under the GPLv2 on June 2015.
5 ////////////////////////////////////////////////////////////////////
6 
7 /*********************************************************************/
8 
9 OSSTATUS __fastcall WCacheCheckLimits(IN PW_CACHE Cache,
10                              IN PVOID Context,
11                              IN lba_t ReqLba,
12                              IN ULONG BCount);
13 
14 OSSTATUS __fastcall WCacheCheckLimitsRAM(IN PW_CACHE Cache,
15                              IN PVOID Context,
16                              IN lba_t ReqLba,
17                              IN ULONG BCount);
18 
19 OSSTATUS __fastcall WCacheCheckLimitsRW(IN PW_CACHE Cache,
20                              IN PVOID Context,
21                              IN lba_t ReqLba,
22                              IN ULONG BCount);
23 
24 OSSTATUS __fastcall WCacheCheckLimitsR(IN PW_CACHE Cache,
25                              IN PVOID Context,
26                              IN lba_t ReqLba,
27                              IN ULONG BCount);
28 
29 VOID     __fastcall WCachePurgeAllRW(IN PW_CACHE Cache,
30                              IN PVOID Context);
31 
32 VOID     __fastcall WCacheFlushAllRW(IN PW_CACHE Cache,
33                              IN PVOID Context);
34 
35 VOID     __fastcall WCachePurgeAllR(IN PW_CACHE Cache,
36                              IN PVOID Context);
37 
38 OSSTATUS __fastcall WCacheDecodeFlags(IN PW_CACHE Cache,
39                              IN ULONG Flags);
40 
41 #define ASYNC_STATE_NONE      0
42 #define ASYNC_STATE_READ_PRE  1
43 #define ASYNC_STATE_READ      2
44 #define ASYNC_STATE_WRITE_PRE 3
45 #define ASYNC_STATE_WRITE     4
46 #define ASYNC_STATE_DONE      5
47 
48 #define ASYNC_CMD_NONE        0
49 #define ASYNC_CMD_READ        1
50 #define ASYNC_CMD_UPDATE      2
51 
52 #define WCACHE_MAX_CHAIN      (0x10)
53 
54 #define MEM_WCCTX_TAG         'xtCW'
55 #define MEM_WCFRM_TAG         'rfCW'
56 #define MEM_WCBUF_TAG         'fbCW'
57 
58 #define USE_WC_PRINT
59 
60 #ifdef USE_WC_PRINT
61  #define WcPrint UDFPrint
62 #else
63  #define WcPrint(x) {;}
64 #endif
65 
66 typedef struct _W_CACHE_ASYNC {
67     UDF_PH_CALL_CONTEXT PhContext;
68     ULONG State;
69     ULONG Cmd;
70     PW_CACHE Cache;
71     PVOID Buffer;
72     PVOID Buffer2;
73     SIZE_T TransferredBytes;
74     ULONG BCount;
75     lba_t Lba;
76     struct _W_CACHE_ASYNC* NextWContext;
77     struct _W_CACHE_ASYNC* PrevWContext;
78 } W_CACHE_ASYNC, *PW_CACHE_ASYNC;
79 
80 VOID
81 WCacheUpdatePacketComplete(
82     IN PW_CACHE Cache,        // pointer to the Cache Control structure
83     IN PVOID Context,         // user-supplied context for IO callbacks
84     IN OUT PW_CACHE_ASYNC* FirstWContext, // pointer to head async IO context
85     IN OUT PW_CACHE_ASYNC* PrevWContext,  // pointer to tail async IO context
86     IN BOOLEAN FreePacket = TRUE
87     );
88 
89 /*********************************************************************/
90 ULONG WCache_random;
91 
92 /*
93   WCacheInit__() fills all necesary fileds in passed in PW_CACHE Cache
94   structure, allocates memory and synchronization resources.
95   Cacheable area is subdiveded on Frames - contiguous sets of blocks.
96   Internally each Frame is an array of pointers and attributes of cached
97   Blocks. To optimize memory usage WCache keeps in memory limited number
98   of frames (MaxFrames).
99   Frame length (number of Blocks) must be be a power of 2 and aligned on
100   minimum writeable block size - Packet.
101   Packet size must be a power of 2 (2, 4, 8, 16, etc.).
102   Each cached Block belongs to one of the Frames. To optimize memory usage
103   WCache keeps in memory limited number of Blocks (MaxBlocks). Block size
104   must be a power of 2.
105   WCache splits low-level request(s) into some parts if requested data length
106   exceeds MaxBytesToRead.
107   If requested data length exceeds maximum cache size WCache makes
108   recursive calls to read/write routines with shorter requests
109 
110   WCacheInit__() returns initialization status. If initialization failed,
111   all allocated memory and resources are automaticelly freed.
112 
113   Public routine
114  */
115 OSSTATUS
116 WCacheInit__(
117     IN PW_CACHE Cache,        // pointer to the Cache Control structure to be initialized
118     IN ULONG MaxFrames,       // maximum number of Frames to be kept in memory
119                               //   simultaneously
120     IN ULONG MaxBlocks,       // maximum number of Blocks to be kept in memory
121                               //   simultaneously
122     IN SIZE_T MaxBytesToRead,  // maximum IO length (split boundary)
123     IN ULONG PacketSizeSh,    // number of blocks in packet (bit shift)
124                               //   Packes size = 2^PacketSizeSh
125     IN ULONG BlockSizeSh,     // Block size (bit shift)
126                               //   Block size = 2^BlockSizeSh
127     IN ULONG BlocksPerFrameSh,// number of blocks in Frame (bit shift)
128                               //   Frame size = 2^BlocksPerFrameSh
129     IN lba_t FirstLba,        // Logical Block Address (LBA) of the 1st block
130                               //   in cacheable area
131     IN lba_t LastLba,         // Logical Block Address (LBA) of the last block
132                               //   in cacheable area
133     IN ULONG Mode,            // media mode:
134                               //   WCACHE_MODE_ROM
135                               //   WCACHE_MODE_RW
136                               //   WCACHE_MODE_R
137                               //   WCACHE_MODE_RAM
138                               //   the following modes are planned to be implemented:
139                               //   WCACHE_MODE_EWR
140     IN ULONG Flags,           // cache mode flags:
141                               //   WCACHE_CACHE_WHOLE_PACKET
142                               //     read long (Packet-sized) blocks of
143                               //     data from media
144     IN ULONG FramesToKeepFree,
145                               // number of Frames to be flushed & purged from cache
146                               //   when Frame counter reaches top-limit and allocation
147                               //   of a new Frame required
148     IN PWRITE_BLOCK WriteProc,
149                               // pointer to synchronous physical write call-back routine
150     IN PREAD_BLOCK ReadProc,
151                               // pointer to synchronous physical read call-back routine
152     IN PWRITE_BLOCK_ASYNC WriteProcAsync,
153                               // pointer to _asynchronous_ physical write call-back routine
154                               //   currently must be set to NULL because async support
155                               //   is not completly implemented
156     IN PREAD_BLOCK_ASYNC ReadProcAsync,
157                               // pointer to _asynchronous_ physical read call-back routine
158                               //   must be set to NULL (see above)
159     IN PCHECK_BLOCK CheckUsedProc,
160                               // pointer to call-back routine that checks whether the Block
161                               //   specified (by LBA) is allocated for some data or should
162                               //   be treated as unused (and thus, zero-filled).
163                               //   Is used to avoid physical reads and writes from/to such Blocks
164     IN PUPDATE_RELOC UpdateRelocProc,
165                               // pointer to call-back routine that updates caller's
166                               //   relocation table _after_ physical write (append) in WORM
167                               //   (WCACHE_MODE_R) mode. WCache sends original and new
168                               //   (derived from last LBA) logical addresses to this routine
169     IN PWC_ERROR_HANDLER ErrorHandlerProc
170     )
171 {
172     ULONG l1, l2, l3;
173     ULONG PacketSize = (1) << PacketSizeSh;
174     ULONG BlockSize = (1) << BlockSizeSh;
175     ULONG BlocksPerFrame = (1) << BlocksPerFrameSh;
176     OSSTATUS RC = STATUS_SUCCESS;
177     LARGE_INTEGER rseed;
178     ULONG res_init_flags = 0;
179 
180 #define WCLOCK_RES   1
181 
182     _SEH2_TRY {
183         // check input parameters
184         if(Mode == WCACHE_MODE_R) {
185             UDFPrint(("Disable Async-Write for WORM media\n"));
186             WriteProcAsync = NULL;
187         }
188         if((MaxBlocks % PacketSize) || !MaxBlocks) {
189             UDFPrint(("Total number of sectors must be packet-size-aligned\n"));
190             try_return(RC = STATUS_INVALID_PARAMETER);
191         }
192         if(BlocksPerFrame % PacketSize) {
193             UDFPrint(("Number of sectors per Frame must be packet-size-aligned\n"));
194             try_return(RC = STATUS_INVALID_PARAMETER);
195         }
196         if(!ReadProc) {
197             UDFPrint(("Read routine pointer must be valid\n"));
198             try_return(RC = STATUS_INVALID_PARAMETER);
199         }
200         if(FirstLba >= LastLba) {
201             UDFPrint(("Invalid cached area parameters: (%x - %x)\n",FirstLba, LastLba));
202             try_return(RC = STATUS_INVALID_PARAMETER);
203         }
204         if(!MaxFrames) {
205             UDFPrint(("Total frame number must be non-zero\n",FirstLba, LastLba));
206             try_return(RC = STATUS_INVALID_PARAMETER);
207         }
208         if(Mode > WCACHE_MODE_MAX) {
209             UDFPrint(("Invalid media mode. Should be 0-%x\n",WCACHE_MODE_MAX));
210             try_return(RC = STATUS_INVALID_PARAMETER);
211         }
212         if(FramesToKeepFree >= MaxFrames/2) {
213             UDFPrint(("Invalid FramesToKeepFree (%x). Should be Less or equal to MaxFrames/2 (%x)\n", FramesToKeepFree, MaxFrames/2));
214             try_return(RC = STATUS_INVALID_PARAMETER);
215         }
216         // check 'features'
217         if(!WriteProc) {
218             UDFPrint(("Write routine not specified\n"));
219             UDFPrint(("Read-only mode enabled\n"));
220         }
221         MaxBlocks = max(MaxBlocks, BlocksPerFrame*3);
222         // initialize required structures
223         // we'll align structure size on system page size to
224         // avoid system crashes caused by pool fragmentation
225         if(!(Cache->FrameList =
226             (PW_CACHE_FRAME)MyAllocatePoolTag__(NonPagedPool, l1 = (((LastLba >> BlocksPerFrameSh)+1)*sizeof(W_CACHE_FRAME)), MEM_WCFRM_TAG) )) {
227             UDFPrint(("Cache init err 1\n"));
228             try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
229         }
230         if(!(Cache->CachedBlocksList =
231             (PULONG)MyAllocatePoolTag__(NonPagedPool, l2 = ((MaxBlocks+2)*sizeof(lba_t)), MEM_WCFRM_TAG) )) {
232             UDFPrint(("Cache init err 2\n"));
233             try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
234         }
235         if(!(Cache->CachedModifiedBlocksList =
236             (PULONG)MyAllocatePoolTag__(NonPagedPool, l2, MEM_WCFRM_TAG) )) {
237             UDFPrint(("Cache init err 3\n"));
238             try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
239         }
240         if(!(Cache->CachedFramesList =
241             (PULONG)MyAllocatePoolTag__(NonPagedPool, l3 = ((MaxFrames+2)*sizeof(lba_t)), MEM_WCFRM_TAG) )) {
242             UDFPrint(("Cache init err 4\n"));
243             try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
244         }
245         RtlZeroMemory(Cache->FrameList, l1);
246         RtlZeroMemory(Cache->CachedBlocksList, l2);
247         RtlZeroMemory(Cache->CachedModifiedBlocksList, l2);
248         RtlZeroMemory(Cache->CachedFramesList, l3);
249         // remember all useful parameters
250         Cache->BlocksPerFrame = BlocksPerFrame;
251         Cache->BlocksPerFrameSh = BlocksPerFrameSh;
252         Cache->BlockCount = 0;
253         Cache->MaxBlocks = MaxBlocks;
254         Cache->MaxBytesToRead = MaxBytesToRead;
255         Cache->FrameCount = 0;
256         Cache->MaxFrames = MaxFrames;
257         Cache->PacketSize = PacketSize;
258         Cache->PacketSizeSh = PacketSizeSh;
259         Cache->BlockSize = BlockSize;
260         Cache->BlockSizeSh = BlockSizeSh;
261         Cache->WriteCount = 0;
262         Cache->FirstLba = FirstLba;
263         Cache->LastLba = LastLba;
264         Cache->Mode = Mode;
265 
266         if(!OS_SUCCESS(RC = WCacheDecodeFlags(Cache, Flags))) {
267             return RC;
268         }
269 
270         Cache->FramesToKeepFree = FramesToKeepFree;
271         Cache->WriteProc = WriteProc;
272         Cache->ReadProc = ReadProc;
273         Cache->WriteProcAsync = WriteProcAsync;
274         Cache->ReadProcAsync = ReadProcAsync;
275         Cache->CheckUsedProc = CheckUsedProc;
276         Cache->UpdateRelocProc = UpdateRelocProc;
277         Cache->ErrorHandlerProc = ErrorHandlerProc;
278         // init permanent tmp buffers
279         if(!(Cache->tmp_buff =
280             (PCHAR)MyAllocatePoolTag__(NonPagedPool, PacketSize*BlockSize, MEM_WCFRM_TAG))) {
281             UDFPrint(("Cache init err 5.W\n"));
282             try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
283         }
284         if(!(Cache->tmp_buff_r =
285             (PCHAR)MyAllocatePoolTag__(NonPagedPool, PacketSize*BlockSize, MEM_WCFRM_TAG))) {
286             UDFPrint(("Cache init err 5.R\n"));
287             try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
288         }
289         if(!(Cache->reloc_tab =
290             (PULONG)MyAllocatePoolTag__(NonPagedPool, Cache->PacketSize*sizeof(ULONG), MEM_WCFRM_TAG))) {
291             UDFPrint(("Cache init err 6\n"));
292             try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
293         }
294         if(!OS_SUCCESS(RC = ExInitializeResourceLite(&(Cache->WCacheLock)))) {
295             UDFPrint(("Cache init err (res)\n"));
296             try_return(RC);
297         }
298         res_init_flags |= WCLOCK_RES;
299         KeQuerySystemTime((PLARGE_INTEGER)(&rseed));
300         WCache_random = rseed.LowPart;
301 
302 try_exit: NOTHING;
303 
304     } _SEH2_FINALLY {
305 
306         if(!OS_SUCCESS(RC)) {
307             if(res_init_flags & WCLOCK_RES)
308                 ExDeleteResourceLite(&(Cache->WCacheLock));
309             if(Cache->FrameList)
310                 MyFreePool__(Cache->FrameList);
311             if(Cache->CachedBlocksList)
312                 MyFreePool__(Cache->CachedBlocksList);
313             if(Cache->CachedModifiedBlocksList)
314                 MyFreePool__(Cache->CachedModifiedBlocksList);
315             if(Cache->CachedFramesList)
316                 MyFreePool__(Cache->CachedFramesList);
317             if(Cache->tmp_buff_r)
318                 MyFreePool__(Cache->tmp_buff_r);
319             if(Cache->tmp_buff)
320                 MyFreePool__(Cache->tmp_buff);
321             if(Cache->reloc_tab)
322                 MyFreePool__(Cache->reloc_tab);
323             RtlZeroMemory(Cache, sizeof(W_CACHE));
324         } else {
325             Cache->Tag = 0xCAC11E00;
326         }
327 
328     } _SEH2_END;
329 
330     return RC;
331 } // end WCacheInit__()
332 
333 /*
334   WCacheRandom() - just a random generator
335   Returns random LONGLONG number
336   Internal routine
337  */
338 LONGLONG
339 WCacheRandom(VOID)
340 {
341     WCache_random = (WCache_random * 0x8088405 + 1);
342     return WCache_random;
343 } // end WCacheRandom()
344 
345 /*
346   WCacheFindLbaToRelease() finds Block to be flushed and purged from cache
347   Returns random LBA
348   Internal routine
349  */
350 lba_t
351 __fastcall
352 WCacheFindLbaToRelease(
353     IN PW_CACHE Cache
354     )
355 {
356     if(!(Cache->BlockCount))
357         return WCACHE_INVALID_LBA;
358     return(Cache->CachedBlocksList[((ULONG)WCacheRandom() % Cache->BlockCount)]);
359 } // end WCacheFindLbaToRelease()
360 
361 /*
362   WCacheFindModifiedLbaToRelease() finds Block to be flushed and purged from cache.
363   This routine looks for Blocks among modified ones
364   Returns random LBA (nodified)
365   Internal routine
366  */
367 lba_t
368 __fastcall
369 WCacheFindModifiedLbaToRelease(
370     IN PW_CACHE Cache
371     )
372 {
373     if(!(Cache->WriteCount))
374         return WCACHE_INVALID_LBA;
375     return(Cache->CachedModifiedBlocksList[((ULONG)WCacheRandom() % Cache->WriteCount)]);
376 } // end WCacheFindModifiedLbaToRelease()
377 
378 /*
379   WCacheFindFrameToRelease() finds Frame to be flushed and purged with all
380   Blocks (from this Frame) from cache
381   Returns random Frame number
382   Internal routine
383  */
384 lba_t
385 __fastcall
386 WCacheFindFrameToRelease(
387     IN PW_CACHE Cache
388     )
389 {
390     ULONG i, j;
391     ULONG frame = 0;
392     ULONG prev_uc = -1;
393     ULONG uc = -1;
394     lba_t lba;
395     BOOLEAN mod = FALSE;
396 
397     if(!(Cache->FrameCount))
398         return 0;
399     /*
400     return(Cache->CachedFramesList[((ULONG)WCacheRandom() % Cache->FrameCount)]);
401     */
402 
403     for(i=0; i<Cache->FrameCount; i++) {
404 
405         j = Cache->CachedFramesList[i];
406 
407         mod |= (Cache->FrameList[j].UpdateCount != 0);
408         uc = Cache->FrameList[j].UpdateCount*32 + Cache->FrameList[j].AccessCount;
409 
410         if(prev_uc > uc) {
411             prev_uc = uc;
412             frame = j;
413         }
414     }
415     if(!mod) {
416         frame = Cache->CachedFramesList[((ULONG)WCacheRandom() % Cache->FrameCount)];
417         lba = frame << Cache->BlocksPerFrameSh;
418         WcPrint(("WC:-frm %x\n", lba));
419     } else {
420         lba = frame << Cache->BlocksPerFrameSh;
421         WcPrint(("WC:-frm(mod) %x\n", lba));
422         for(i=0; i<Cache->FrameCount; i++) {
423 
424             j = Cache->CachedFramesList[i];
425             Cache->FrameList[j].UpdateCount = (Cache->FrameList[j].UpdateCount*2)/3;
426             Cache->FrameList[j].AccessCount = (Cache->FrameList[j].AccessCount*3)/4;
427         }
428     }
429     return frame;
430 } // end WCacheFindFrameToRelease()
431 
432 /*
433   WCacheGetSortedListIndex() returns index of searched Lba
434   (Lba is ULONG in sorted array) or index of minimal cached Lba
435   greater than searched.
436   If requested Lba is less than minimum cached, 0 is returned.
437   If requested Lba is greater than maximum cached, BlockCount value
438   is returned.
439   Internal routine
440  */
441 
442 #ifdef _MSC_VER
443 #pragma warning(push)
444 #pragma warning(disable:4035)               // re-enable below
445 #endif
446 
447 ULONG
448 //__fastcall
449 WCacheGetSortedListIndex(
450     IN ULONG BlockCount,      // number of items in array (pointed by List)
451     IN lba_t* List,           // pointer to sorted (ASC) array of ULONGs
452     IN lba_t Lba              // ULONG value to be searched for
453     )
454 {
455     if(!BlockCount)
456         return 0;
457 
458 #if defined(_X86_) && defined(_MSC_VER) && !defined(__clang__)
459 
460     __asm push  ecx
461     __asm push  ebx
462     __asm push  edx
463     __asm push  esi
464     __asm push  edi
465 //    left = 0;
466 //    right = BlockCount - 1;
467 //    pos = 0;
468     __asm xor   edx,edx                 // left
469     __asm mov   ebx,BlockCount
470     __asm dec   ebx                     // right
471     __asm xor   esi,esi                 // pos
472     __asm mov   edi,List                // List
473     __asm mov   ecx,Lba                 // Lba
474 
475 While_1:
476 //    while(left != right) {
477     __asm cmp   edx,ebx
478     __asm jz    EO_while_1
479 
480 //        pos = (left + right) >> 1;
481     __asm lea   esi,[ebx+edx]
482     __asm shr   esi,1
483 //        if(List[pos] == Lba)
484 //            return pos;
485     __asm mov   eax,[edi+esi*4]
486     __asm cmp   eax,ecx
487     __asm jz    EO_while_2
488 
489 //        if(right - left == 1) {
490     __asm sub   ebx,edx
491     __asm cmp   ebx,1
492     __asm jne    NO_r_sub_l_eq_1
493 //            if(List[pos+1] < Lba)      <=>        if(List[pos+1] >= Lba)
494 //                return (pos+2);        <=>            break;
495 //            break;                     <=>        return (pos+2);
496     __asm cmp   [edi+esi*4+4],ecx
497     __asm jae   EO_while_1
498     __asm add   esi,2
499     __asm jmp   EO_while_2
500 //        }
501 NO_r_sub_l_eq_1:
502 //        if(List[pos] < Lba) {
503     __asm cmp   eax,ecx
504     __asm jae   Update_r
505 //            left = pos;
506     __asm add   ebx,edx
507     __asm mov   edx,esi
508     __asm jmp   While_1
509 //        } else {
510 Update_r:
511 //            right = pos;
512     __asm mov   ebx,esi
513     __asm jmp   While_1
514 //        }
515 //    }
516 EO_while_1:
517 //    if((List[pos] < Lba) && ((pos+1) <= BlockCount)) pos++;
518     __asm mov   eax,[edi+esi*4]
519     __asm cmp   eax,ecx
520     __asm jae   EO_while_2
521     __asm inc   esi
522     __asm cmp   esi,BlockCount
523     __asm jbe   EO_while_2
524     __asm dec   esi
525 EO_while_2:
526 //  return pos;
527     __asm mov   eax,esi
528 
529     __asm pop   edi
530     __asm pop   esi
531     __asm pop   edx
532     __asm pop   ebx
533     __asm pop   ecx
534 
535 #else   // NO X86 optimization , use generic C/C++
536 
537     ULONG pos;
538     ULONG left;
539     ULONG right;
540 
541     if(!BlockCount)
542         return 0;
543 
544     left = 0;
545     right = BlockCount - 1;
546     pos = 0;
547     while(left != right) {
548         pos = (left + right) >> 1;
549         if(List[pos] == Lba)
550             return pos;
551         if(right - left == 1) {
552             if(List[pos+1] < Lba)
553                 return (pos+2);
554             break;
555         }
556         if(List[pos] < Lba) {
557             left = pos;
558         } else {
559             right = pos;
560         }
561     }
562     if((List[pos] < Lba) && ((pos+1) <= BlockCount)) pos++;
563 
564     return pos;
565 
566 #endif // _X86_
567 
568 }
569 
570 #ifdef _MSC_VER
571 #pragma warning(pop) // re-enable warning #4035
572 #endif
573 
574 /*
575   WCacheInsertRangeToList() inserts values laying in range described
576   by Lba (1st value) and BCount (number of sequentially incremented
577   values) in sorted array of ULONGs pointed by List.
578   Ex.: (Lba, BCount)=(7,3) will insert values {7,8,9}.
579   If target array already contains one or more values falling in
580   requested range, they will be removed before insertion.
581   WCacheInsertRangeToList() updates value of (*BlockCount) to reflect
582   performed changes.
583   WCacheInsertRangeToList() assumes that target array is of enough size.
584   Internal routine
585  */
586 VOID
587 __fastcall
588 WCacheInsertRangeToList(
589     IN lba_t* List,           // pointer to sorted (ASC) array of ULONGs
590     IN PULONG BlockCount,     // pointer to number of items in array (pointed by List)
591     IN lba_t Lba,             // initial value for insertion
592     IN ULONG BCount           // number of sequentially incremented values to be inserted
593     )
594 {
595     if(!BCount)
596         return;
597 
598     ASSERT(!(BCount & 0x80000000));
599 
600     ULONG firstPos = WCacheGetSortedListIndex(*BlockCount, List, Lba);
601     ULONG lastPos = WCacheGetSortedListIndex(*BlockCount, List, Lba+BCount);
602     ULONG offs = firstPos + BCount - lastPos;
603 
604     if(offs) {
605         // move list tail
606 //        ASSERT(lastPos+offs + ((*BlockCount) - lastPos) <= qq);
607         if(*BlockCount) {
608 #ifdef WCACHE_BOUND_CHECKS
609             MyCheckArray(List, lastPos+offs+(*BlockCount)-lastPos-1);
610 #endif //WCACHE_BOUND_CHECKS
611             DbgMoveMemory(&(List[lastPos+offs]), &(List[lastPos]), ((*BlockCount) - lastPos) * sizeof(ULONG));
612         }
613         lastPos += offs;
614         for(; firstPos<lastPos; firstPos++) {
615 #ifdef WCACHE_BOUND_CHECKS
616             MyCheckArray(List, firstPos);
617 #endif //WCACHE_BOUND_CHECKS
618             List[firstPos] = Lba;
619             Lba++;
620         }
621         (*BlockCount) += offs;
622     }
623 } // end WCacheInsertRangeToList()
624 
625 /*
626   WCacheInsertItemToList() inserts value Lba in sorted array of
627   ULONGs pointed by List.
628   If target array already contains requested value, no
629   operations are performed.
630   WCacheInsertItemToList() updates value of (*BlockCount) to reflect
631   performed changes.
632   WCacheInsertItemToList() assumes that target array is of enough size.
633   Internal routine
634  */
635 VOID
636 __fastcall
637 WCacheInsertItemToList(
638     IN lba_t* List,           // pointer to sorted (ASC) array of lba_t's
639     IN PULONG BlockCount,     // pointer to number of items in array (pointed by List)
640     IN lba_t Lba              // value to be inserted
641     )
642 {
643     ULONG firstPos = WCacheGetSortedListIndex(*BlockCount, List, Lba+1);
644     if(firstPos && (List[firstPos-1] == Lba))
645         return;
646 
647     // move list tail
648     if(*BlockCount) {
649 #ifdef WCACHE_BOUND_CHECKS
650         MyCheckArray(List, firstPos+1+(*BlockCount)-firstPos-1);
651 #endif //WCACHE_BOUND_CHECKS
652 //        DbgMoveMemory(&(List[firstPos+1]), &(List[firstPos]), ((*BlockCount) - firstPos)*sizeof(ULONG));
653         DbgMoveMemory(&(List[firstPos+1]), &(List[firstPos]), ((*BlockCount) - firstPos) * sizeof(ULONG));
654     }
655 #ifdef WCACHE_BOUND_CHECKS
656     MyCheckArray(List, firstPos);
657 #endif //WCACHE_BOUND_CHECKS
658     List[firstPos] = Lba;
659     (*BlockCount) ++;
660 } // end WCacheInsertItemToList()
661 
662 /*
663   WCacheRemoveRangeFromList() removes values falling in range described
664   by Lba (1st value) and BCount (number of sequentially incremented
665   values) from sorted array of ULONGs pointed by List.
666   Ex.: (Lba, BCount)=(7,3) will remove values {7,8,9}.
667   If target array doesn't contain values falling in
668   requested range, no operation is performed.
669   WCacheRemoveRangeFromList() updates value of (*BlockCount) to reflect
670   performed changes.
671   Internal routine
672  */
673 VOID
674 __fastcall
675 WCacheRemoveRangeFromList(
676     IN lba_t* List,           // pointer to sorted (ASC) array of ULONGs
677     IN PULONG BlockCount,     // pointer to number of items in array (pointed by List)
678     IN lba_t Lba,             // initial value for removal
679     IN ULONG BCount           // number of sequentially incremented values to be removed
680     )
681 {
682     ULONG firstPos = WCacheGetSortedListIndex(*BlockCount, List, Lba);
683     ULONG lastPos = WCacheGetSortedListIndex(*BlockCount, List, Lba+BCount);
684     ULONG offs = lastPos - firstPos;
685 
686     if(offs) {
687         // move list tail
688         DbgMoveMemory(&(List[lastPos-offs]), &(List[lastPos]), ((*BlockCount) - lastPos) * sizeof(ULONG));
689         (*BlockCount) -= offs;
690     }
691 } // end WCacheRemoveRangeFromList()
692 
693 /*
694   WCacheRemoveItemFromList() removes value Lba from sorted array
695   of ULONGs pointed by List.
696   If target array doesn't contain requested value, no
697   operations are performed.
698   WCacheRemoveItemFromList() updates value of (*BlockCount) to reflect
699   performed changes.
700   Internal routine
701  */
702 VOID
703 __fastcall
704 WCacheRemoveItemFromList(
705     IN lba_t* List,           // pointer to sorted (ASC) array of ULONGs
706     IN PULONG BlockCount,     // pointer to number of items in array (pointed by List)
707     IN lba_t Lba              // value to be removed
708     )
709 {
710     if(!(*BlockCount)) return;
711     ULONG lastPos = WCacheGetSortedListIndex(*BlockCount, List, Lba+1);
712     if(!lastPos || (lastPos && (List[lastPos-1] != Lba)))
713         return;
714 
715     // move list tail
716     DbgMoveMemory(&(List[lastPos-1]), &(List[lastPos]), ((*BlockCount) - lastPos) * sizeof(ULONG));
717     (*BlockCount) --;
718 } // end WCacheRemoveItemFromList()
719 
720 /*
721   WCacheInitFrame() allocates storage for Frame (block_array)
722   with index 'frame', fills it with 0 (none of Blocks from
723   this Frame is cached) and inserts it's index to sorted array
724   of frame indexes.
725   WCacheInitFrame() also checks if number of frames reaches limit
726   and invokes WCacheCheckLimits() to free some Frames/Blocks
727   Internal routine
728  */
729 PW_CACHE_ENTRY
730 __fastcall
731 WCacheInitFrame(
732     IN PW_CACHE Cache,        // pointer to the Cache Control structure
733     IN PVOID Context,         // caller's context (currently unused)
734     IN ULONG frame            // frame index
735     )
736 {
737     PW_CACHE_ENTRY block_array;
738     ULONG l;
739 #ifdef DBG
740     ULONG old_count = Cache->FrameCount;
741 #endif //DBG
742 
743     // We are about to add new cache frame.
744     // Thus check if we have enough free entries and
745     // flush unused ones if it is neccessary.
746     if(Cache->FrameCount >= Cache->MaxFrames) {
747         BrutePoint();
748         WCacheCheckLimits(Cache, Context, frame << Cache->BlocksPerFrameSh, Cache->PacketSize*2);
749     }
750     ASSERT(Cache->FrameCount < Cache->MaxFrames);
751     block_array = (PW_CACHE_ENTRY)MyAllocatePoolTag__(NonPagedPool, l = sizeof(W_CACHE_ENTRY) << Cache->BlocksPerFrameSh, MEM_WCFRM_TAG);
752     Cache->FrameList[frame].Frame = block_array;
753 
754     // Keep history !!!
755     //Cache->FrameList[frame].UpdateCount = 0;
756     //Cache->FrameList[frame].AccessCount = 0;
757 
758     if(block_array) {
759         ASSERT((ULONG_PTR)block_array > 0x1000);
760         WCacheInsertItemToList(Cache->CachedFramesList, &(Cache->FrameCount), frame);
761         RtlZeroMemory(block_array, l);
762     } else {
763         BrutePoint();
764     }
765     ASSERT(Cache->FrameCount <= Cache->MaxFrames);
766 #ifdef DBG
767     ASSERT(old_count < Cache->FrameCount);
768 #endif //DBG
769     return block_array;
770 } // end WCacheInitFrame()
771 
772 /*
773   WCacheRemoveFrame() frees storage for Frame (block_array) with
774   index 'frame' and removes it's index from sorted array of
775   frame indexes.
776   Internal routine
777  */
778 VOID
779 __fastcall
780 WCacheRemoveFrame(
781     IN PW_CACHE Cache,        // pointer to the Cache Control structure
782     IN PVOID Context,         // user's context (currently unused)
783     IN ULONG frame            // frame index
784     )
785 {
786     PW_CACHE_ENTRY block_array;
787 #ifdef DBG
788     ULONG old_count = Cache->FrameCount;
789 #endif //DBG
790 
791     ASSERT(Cache->FrameCount <= Cache->MaxFrames);
792     block_array = Cache->FrameList[frame].Frame;
793 
794     WCacheRemoveItemFromList(Cache->CachedFramesList, &(Cache->FrameCount), frame);
795     MyFreePool__(block_array);
796 //    ASSERT(!(Cache->FrameList[frame].WriteCount));
797 //    ASSERT(!(Cache->FrameList[frame].WriteCount));
798     Cache->FrameList[frame].Frame = NULL;
799     ASSERT(Cache->FrameCount < Cache->MaxFrames);
800 #ifdef DBG
801     ASSERT(old_count > Cache->FrameCount);
802 #endif //DBG
803 
804 } // end WCacheRemoveFrame()
805 
806 /*
807   WCacheSetModFlag() sets Modified flag for Block with offset 'i'
808   in Frame 'block_array'
809   Internal routine
810  */
811 #define WCacheSetModFlag(block_array, i) \
812     *((PULONG)&(block_array[i].Sector)) |= WCACHE_FLAG_MODIFIED
813 
814 /*
815   WCacheClrModFlag() clears Modified flag for Block with offset 'i'
816   in Frame 'block_array'
817   Internal routine
818  */
819 #define WCacheClrModFlag(block_array, i) \
820     *((PULONG)&(block_array[i].Sector)) &= ~WCACHE_FLAG_MODIFIED
821 
822 /*
823   WCacheGetModFlag() returns non-zero value if Modified flag for
824   Block with offset 'i' in Frame 'block_array' is set. Otherwise
825   0 is returned.
826   Internal routine
827  */
828 #define WCacheGetModFlag(block_array, i) \
829     (*((PULONG)&(block_array[i].Sector)) & WCACHE_FLAG_MODIFIED)
830 
831 #if 0
832 /*
833   WCacheSetBadFlag() sets Modified flag for Block with offset 'i'
834   in Frame 'block_array'
835   Internal routine
836  */
837 #define WCacheSetBadFlag(block_array, i) \
838     *((PULONG)&(block_array[i].Sector)) |= WCACHE_FLAG_BAD
839 
840 /*
841   WCacheClrBadFlag() clears Modified flag for Block with offset 'i'
842   in Frame 'block_array'
843   Internal routine
844  */
845 #define WCacheClrBadFlag(block_array, i) \
846     *((PULONG)&(block_array[i].Sector)) &= ~WCACHE_FLAG_BAD
847 
848 /*
849   WCacheGetBadFlag() returns non-zero value if Modified flag for
850   Block with offset 'i' in Frame 'block_array' is set. Otherwise
851   0 is returned.
852   Internal routine
853  */
854 #define WCacheGetBadFlag(block_array, i) \
855     (((UCHAR)(block_array[i].Sector)) & WCACHE_FLAG_BAD)
856 #endif //0
857 
858 /*
859   WCacheSectorAddr() returns pointer to memory block containing cached
860   data for Block described by Frame (block_array) and offset in this
861   Frame (i). If requested Block is not cached yet NULL is returned.
862   Internal routine
863  */
864 #define WCacheSectorAddr(block_array, i) \
865     ((ULONG_PTR)(block_array[i].Sector) & WCACHE_ADDR_MASK)
866 
867 /*
868   WCacheFreeSector() releases memory block containing cached
869   data for Block described by Frame (block_array) and offset in this
870   Frame (i). Should never be called for non-cached Blocks.
871   Internal routine
872  */
873 #define WCacheFreeSector(frame, offs) \
874 {                          \
875     DbgFreePool((PVOID)WCacheSectorAddr(block_array, offs)); \
876     block_array[offs].Sector = NULL; \
877     Cache->FrameList[frame].BlockCount--; \
878 }
879 
880 /*
881   WCacheAllocAsyncEntry() allocates storage for async IO context,
882   links it to previously allocated async IO context (if any),
883   initializes synchronization (completion) event
884   and allocates temporary IO buffers.
885   Async IO contexts are used to create chained set of IO requests
886   durring top-level request precessing and wait for their completion.
887   Internal routine
888  */
889 PW_CACHE_ASYNC
890 WCacheAllocAsyncEntry(
891     IN PW_CACHE Cache,        // pointer to the Cache Control structure
892     IN OUT PW_CACHE_ASYNC* FirstWContext, // pointer to the pointer to
893                               //   the head of async IO context chain
894     IN OUT PW_CACHE_ASYNC* PrevWContext,  // pointer to the storage for pointer
895                               //   to newly allocated async IO context chain
896     IN ULONG BufferSize       // requested IO buffer size
897     )
898 {
899     PW_CACHE_ASYNC WContext;
900     PCHAR Buffer;
901 
902     WContext = (PW_CACHE_ASYNC)MyAllocatePoolTag__(NonPagedPool,sizeof(W_CACHE_ASYNC), MEM_WCCTX_TAG);
903     if(!WContext)
904         return NULL;
905     Buffer = (PCHAR)DbgAllocatePoolWithTag(NonPagedPool, BufferSize*(2-Cache->Chained), MEM_WCBUF_TAG);
906     if(!Buffer) {
907         MyFreePool__(WContext);
908         return NULL;
909     }
910 
911     if(!Cache->Chained)
912         KeInitializeEvent(&(WContext->PhContext.event), SynchronizationEvent, FALSE);
913     WContext->Cache = Cache;
914     if(*PrevWContext)
915         (*PrevWContext)->NextWContext = WContext;
916 //    WContext->NextWContext = (*PrevWContext);
917     WContext->NextWContext = NULL;
918     WContext->Buffer = Buffer;
919     WContext->Buffer2 = Buffer+(Cache->Chained ? 0 : BufferSize);
920 
921     if(!(*FirstWContext))
922         (*FirstWContext) = WContext;
923     (*PrevWContext) = WContext;
924 
925     return WContext;
926 } // end WCacheAllocAsyncEntry()
927 
928 /*
929   WCacheFreeAsyncEntry() releases storage previously allocated for
930   async IO context.
931   Internal routine
932  */
933 VOID
934 WCacheFreeAsyncEntry(
935     IN PW_CACHE Cache,        // pointer to the Cache Control structure
936     PW_CACHE_ASYNC WContext   // pointer to async IO context to release
937     )
938 {
939     DbgFreePool(WContext->Buffer);
940     MyFreePool__(WContext);
941 } // end WCacheFreeAsyncEntry()
942 
943 //#define WCacheRaiseIoError(c, ct, s, l, bc, b, o, r)
944 
945 OSSTATUS
946 WCacheRaiseIoError(
947     IN PW_CACHE Cache,        // pointer to the Cache Control structure
948     IN PVOID Context,
949     IN OSSTATUS Status,
950     IN ULONG Lba,
951     IN ULONG BCount,
952     IN PVOID Buffer,
953     IN BOOLEAN ReadOp,
954     IN PBOOLEAN Retry
955     )
956 {
957     if(!Cache->ErrorHandlerProc)
958         return Status;
959 
960     WCACHE_ERROR_CONTEXT ec;
961 
962     ec.WCErrorCode = ReadOp ? WCACHE_ERROR_READ : WCACHE_ERROR_WRITE;
963     ec.Status = Status;
964     ec.ReadWrite.Lba    = Lba;
965     ec.ReadWrite.BCount = BCount;
966     ec.ReadWrite.Buffer = Buffer;
967     Status = Cache->ErrorHandlerProc(Context, &ec);
968     if(Retry)
969         (*Retry) = ec.Retry;
970 
971     return Status;
972 
973 } // end WCacheRaiseIoError()
974 
975 /*
976   WCacheUpdatePacket() attempts to updates packet containing target Block.
977   If async IO is enabled new IO context is added to the chain.
978   If packet containing target Block is modified and PrefereWrite flag
979   is NOT set, function returns with status STATUS_RETRY. This setting is
980   user in WCACHE_MODE_R mode to reduce physical writes on flush.
981   'State' parameter is used in async mode to determine the next processing
982   stege for given request
983   Internal routine
984  */
985 OSSTATUS
986 WCacheUpdatePacket(
987     IN PW_CACHE Cache,        // pointer to the Cache Control structure
988     IN PVOID Context,         // user's context to be passed to user-supplied
989                               //   low-level IO routines (IO callbacks)
990     IN OUT PW_CACHE_ASYNC* FirstWContext, // pointer to head async IO context
991     IN OUT PW_CACHE_ASYNC* PrevWContext,  // pointer to tail async IO context
992     IN PW_CACHE_ENTRY block_array, // pointer to target Frame
993     IN lba_t firstLba,        // LBA of the 1st block in target Frame
994     IN lba_t Lba,             // LBA of target Block
995     IN ULONG BSh,             // bit shift for Block size
996     IN ULONG BS,              // Block size (bytes)
997     IN ULONG PS,              // Packet size (bytes)
998     IN ULONG PSs,             // Packet size (sectors)
999     IN PSIZE_T ReadBytes,      // pointer to number of successfully read/written bytes
1000     IN BOOLEAN PrefereWrite,  // allow physical write (flush) of modified packet
1001     IN ULONG State            // callers state
1002     )
1003 {
1004     OSSTATUS status;
1005     PCHAR tmp_buff = Cache->tmp_buff;
1006     PCHAR tmp_buff2 = Cache->tmp_buff;
1007     BOOLEAN mod;
1008     BOOLEAN read;
1009     BOOLEAN zero;
1010     ULONG i;
1011     lba_t Lba0;
1012     PW_CACHE_ASYNC WContext;
1013     BOOLEAN Async = (Cache->ReadProcAsync && Cache->WriteProcAsync);
1014     ULONG block_type;
1015     BOOLEAN Chained = Cache->Chained;
1016 
1017     // Check if we are going to write down to disk
1018     // all prewiously prepared (chained) data
1019     if(State == ASYNC_STATE_WRITE) {
1020         WContext = (*PrevWContext);
1021         tmp_buff  = (PCHAR)(WContext->Buffer);
1022         tmp_buff2 = (PCHAR)(WContext->Buffer2);
1023         if(!Chained)
1024             mod = (DbgCompareMemory(tmp_buff2, tmp_buff, PS) != PS);
1025         goto try_write;
1026     }
1027 
1028     // Check if packet contains modified blocks
1029     // If packet contains non-cached and unchanged, but used
1030     // blocks, it must be read from media before modification
1031     mod = read = zero = FALSE;
1032     Lba0 = Lba - firstLba;
1033     for(i=0; i<PSs; i++, Lba0++) {
1034         if(WCacheGetModFlag(block_array, Lba0)) {
1035             mod = TRUE;
1036         } else if(!WCacheSectorAddr(block_array,Lba0) &&
1037                   ((block_type = Cache->CheckUsedProc(Context, Lba+i)) & WCACHE_BLOCK_USED) ) {
1038             //
1039             if(block_type & WCACHE_BLOCK_ZERO) {
1040                 zero = TRUE;
1041             } else {
1042                 read = TRUE;
1043             }
1044         }
1045     }
1046     // check if we are allowed to write to media
1047     if(mod && !PrefereWrite) {
1048         return STATUS_RETRY;
1049     }
1050     // return STATUS_SUCCESS if requested packet contains no modified blocks
1051     if(!mod) {
1052         (*ReadBytes) = PS;
1053         return STATUS_SUCCESS;
1054     }
1055 
1056     // pefrorm full update cycle: prepare(optional)/read/modify/write
1057 
1058     // do some preparations
1059     if(Chained || Async) {
1060         // For chained and async I/O we allocates context entry
1061         // and add it to list (chain)
1062         // We shall only read data to temporary buffer and
1063         // modify it. Write operations will be invoked later.
1064         // This is introduced in order to avoid frequent
1065         // read.write mode switching, because it significantly degrades
1066         // performance
1067         WContext = WCacheAllocAsyncEntry(Cache, FirstWContext, PrevWContext, PS);
1068         if(!WContext) {
1069             //return STATUS_INSUFFICIENT_RESOURCES;
1070             // try to recover
1071             Chained = FALSE;
1072             Async = FALSE;
1073         } else {
1074             tmp_buff = tmp_buff2 = (PCHAR)(WContext->Buffer);
1075             WContext->Lba = Lba;
1076             WContext->Cmd = ASYNC_CMD_UPDATE;
1077             WContext->State = ASYNC_STATE_NONE;
1078         }
1079     }
1080 
1081     // read packet (if it necessary)
1082     if(read) {
1083         if(Async) {
1084             WContext->State = ASYNC_STATE_READ;
1085             status = Cache->ReadProcAsync(Context, WContext, tmp_buff, PS, Lba,
1086                                            &(WContext->TransferredBytes));
1087 //                tmp_buff2 = (PCHAR)(WContext->Buffer2);
1088             (*ReadBytes) = PS;
1089             return status;
1090         } else {
1091             status = Cache->ReadProc(Context, tmp_buff, PS, Lba, ReadBytes, PH_TMP_BUFFER);
1092         }
1093         if(!OS_SUCCESS(status)) {
1094             status = WCacheRaiseIoError(Cache, Context, status, Lba, PSs, tmp_buff, WCACHE_R_OP, NULL);
1095             if(!OS_SUCCESS(status)) {
1096                 return status;
1097             }
1098         }
1099     } else
1100     if(zero) {
1101         RtlZeroMemory(tmp_buff, PS);
1102     }
1103 
1104     if(Chained) {
1105         // indicate that we prepared for writing block to disk
1106         WContext->State = ASYNC_STATE_WRITE_PRE;
1107         tmp_buff2 = tmp_buff;
1108         status = STATUS_SUCCESS;
1109     }
1110 
1111     // modify packet
1112 
1113     // If we didn't read packet from media, we can't
1114     // perform comparison to assure that packet was really modified.
1115     // Thus, assume that it is modified in this case.
1116     mod = !read || Cache->DoNotCompare;
1117     Lba0 = Lba - firstLba;
1118     for(i=0; i<PSs; i++, Lba0++) {
1119         if( WCacheGetModFlag(block_array, Lba0) ||
1120             (!read && WCacheSectorAddr(block_array,Lba0)) ) {
1121 
1122 #ifdef _NTDEF_
1123             ASSERT((ULONG)WCacheSectorAddr(block_array,Lba0) & 0x80000000);
1124 #endif //_NTDEF_
1125             if(!mod) {
1126                 ASSERT(read);
1127                 mod = (DbgCompareMemory(tmp_buff2 + (i << BSh),
1128                             (PVOID)WCacheSectorAddr(block_array, Lba0),
1129                             BS) != BS);
1130             }
1131             if(mod)
1132                 DbgCopyMemory(tmp_buff2 + (i << BSh),
1133                             (PVOID)WCacheSectorAddr(block_array, Lba0),
1134                             BS);
1135         }
1136     }
1137 
1138     if(Chained &&
1139        WContext->State == ASYNC_STATE_WRITE_PRE) {
1140         // Return if block is prepared for write and we are in chained mode.
1141         if(!mod) {
1142             // Mark block as written if we have found that data in it
1143             // is not actually modified.
1144             WContext->State = ASYNC_STATE_DONE;
1145             (*ReadBytes) = PS;
1146         }
1147         return STATUS_SUCCESS;
1148     }
1149 
1150     // write packet
1151 
1152     // If the check above reported some changes in packet
1153     // we should write packet out to media.
1154     // Otherwise, just complete request.
1155     if(mod) {
1156 try_write:
1157         if(Async) {
1158             WContext->State = ASYNC_STATE_WRITE;
1159             status = Cache->WriteProcAsync(Context, WContext, tmp_buff2, PS, Lba,
1160                                            &(WContext->TransferredBytes), FALSE);
1161             (*ReadBytes) = PS;
1162         } else {
1163             status = Cache->WriteProc(Context, tmp_buff2, PS, Lba, ReadBytes, 0);
1164             if(!OS_SUCCESS(status)) {
1165                 status = WCacheRaiseIoError(Cache, Context, status, Lba, PSs, tmp_buff2, WCACHE_W_OP, NULL);
1166             }
1167         }
1168     } else {
1169         if(Async)
1170             WCacheCompleteAsync__(WContext, STATUS_SUCCESS);
1171         (*ReadBytes) = PS;
1172         return STATUS_SUCCESS;
1173     }
1174 
1175     return status;
1176 } // end WCacheUpdatePacket()
1177 
1178 /*
1179   WCacheFreePacket() releases storage for all Blocks in packet.
1180   'frame' describes Frame, offset - Block in Frame. offset should be
1181   aligned on Packet size.
1182   Internal routine
1183  */
1184 VOID
1185 WCacheFreePacket(
1186     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1187 //    IN PVOID Context,
1188     IN ULONG frame,           // Frame index
1189     IN PW_CACHE_ENTRY block_array, // Frame
1190     IN ULONG offs,            // offset in Frame
1191     IN ULONG PSs              // Packet size (in Blocks)
1192     )
1193 {
1194     ULONG i;
1195     // mark as non-cached & free pool
1196     for(i=0; i<PSs; i++, offs++) {
1197         if(WCacheSectorAddr(block_array,offs)) {
1198             WCacheFreeSector(frame, offs);
1199         }
1200     }
1201 } // end WCacheFreePacket()
1202 
1203 /*
1204   WCacheUpdatePacketComplete() is called to continue processing of packet
1205   being updated.
1206   In async mode it waits for completion of pre-read requests,
1207   initiates writes, waits for their completion and returns control to
1208   caller.
1209   Internal routine
1210  */
1211 VOID
1212 WCacheUpdatePacketComplete(
1213     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1214     IN PVOID Context,         // user-supplied context for IO callbacks
1215     IN OUT PW_CACHE_ASYNC* FirstWContext, // pointer to head async IO context
1216     IN OUT PW_CACHE_ASYNC* PrevWContext,  // pointer to tail async IO context
1217     IN BOOLEAN FreePacket
1218     )
1219 {
1220     PW_CACHE_ASYNC WContext = (*FirstWContext);
1221     if(!WContext)
1222         return;
1223     PW_CACHE_ASYNC NextWContext;
1224     ULONG PS = Cache->BlockSize << Cache->PacketSizeSh; // packet size (bytes)
1225     ULONG PSs = Cache->PacketSize;
1226     ULONG frame;
1227     lba_t firstLba;
1228 
1229     // Walk through all chained blocks and wait
1230     // for completion of read operations.
1231     // Also invoke writes of already prepared packets.
1232     while(WContext) {
1233         if(WContext->Cmd == ASYNC_CMD_UPDATE &&
1234            WContext->State == ASYNC_STATE_READ) {
1235             // wait for async read for update
1236             DbgWaitForSingleObject(&(WContext->PhContext.event), NULL);
1237 
1238             WContext->State = ASYNC_STATE_WRITE;
1239             WCacheUpdatePacket(Cache, Context, NULL, &WContext, NULL, -1, WContext->Lba, -1, -1,
1240                                PS, -1, &(WContext->TransferredBytes), TRUE, ASYNC_STATE_WRITE);
1241         } else
1242         if(WContext->Cmd == ASYNC_CMD_UPDATE &&
1243            WContext->State == ASYNC_STATE_WRITE_PRE) {
1244             // invoke physical write it the packet is prepared for writing
1245             // by previuous call to WCacheUpdatePacket()
1246             WContext->State = ASYNC_STATE_WRITE;
1247             WCacheUpdatePacket(Cache, Context, NULL, &WContext, NULL, -1, WContext->Lba, -1, -1,
1248                                PS, -1, &(WContext->TransferredBytes), TRUE, ASYNC_STATE_WRITE);
1249             WContext->State = ASYNC_STATE_DONE;
1250         } else
1251         if(WContext->Cmd == ASYNC_CMD_READ &&
1252            WContext->State == ASYNC_STATE_READ) {
1253             // wait for async read
1254             DbgWaitForSingleObject(&(WContext->PhContext.event), NULL);
1255         }
1256         WContext = WContext->NextWContext;
1257     }
1258     // Walk through all chained blocks and wait
1259     // and wait for completion of async writes (if any).
1260     // Also free temporary buffers containing already written blocks.
1261     WContext = (*FirstWContext);
1262     while(WContext) {
1263         NextWContext = WContext->NextWContext;
1264         if(WContext->Cmd == ASYNC_CMD_UPDATE &&
1265            WContext->State == ASYNC_STATE_WRITE) {
1266 
1267             if(!Cache->Chained)
1268                 DbgWaitForSingleObject(&(WContext->PhContext.event), NULL);
1269 
1270             frame = WContext->Lba >> Cache->BlocksPerFrameSh;
1271             firstLba = frame << Cache->BlocksPerFrameSh;
1272 
1273             if(FreePacket) {
1274                 WCacheFreePacket(Cache, frame,
1275                                 Cache->FrameList[frame].Frame,
1276                                 WContext->Lba - firstLba, PSs);
1277             }
1278         }
1279         WCacheFreeAsyncEntry(Cache, WContext);
1280         WContext = NextWContext;
1281     }
1282     (*FirstWContext) = NULL;
1283     (*PrevWContext) = NULL;
1284 } // end WCacheUpdatePacketComplete()
1285 
1286 /*
1287   WCacheCheckLimits() checks if we've enough free Frame- &
1288   Block-entries under Frame- and Block-limit to feet
1289   requested Blocks.
1290   If there is not enough entries, WCache initiates flush & purge
1291   process to satisfy request.
1292   This is dispatch routine, which calls
1293   WCacheCheckLimitsR() or WCacheCheckLimitsRW() depending on
1294   media type.
1295   Internal routine
1296  */
1297 OSSTATUS
1298 __fastcall
1299 WCacheCheckLimits(
1300     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1301     IN PVOID Context,         // user-supplied context for IO callbacks
1302     IN lba_t ReqLba,          // first LBA to access/cache
1303     IN ULONG BCount           // number of Blocks to access/cache
1304     )
1305 {
1306 /*    if(!Cache->FrameCount || !Cache->BlockCount) {
1307         ASSERT(!Cache->FrameCount);
1308         ASSERT(!Cache->BlockCount);
1309         if(!Cache->FrameCount)
1310             return STATUS_SUCCESS;
1311     }*/
1312 
1313     // check if we have reached Frame or Block limit
1314     if(!Cache->FrameCount && !Cache->BlockCount) {
1315         return STATUS_SUCCESS;
1316     }
1317 
1318     // check for empty frames
1319     if(Cache->FrameCount > (Cache->MaxFrames*3)/4) {
1320         ULONG frame;
1321         ULONG i;
1322         for(i=Cache->FrameCount; i>0; i--) {
1323             frame = Cache->CachedFramesList[i-1];
1324             // check if frame is empty
1325             if(!(Cache->FrameList[frame].BlockCount)) {
1326                 WCacheRemoveFrame(Cache, Context, frame);
1327             } else {
1328                 ASSERT(Cache->FrameList[frame].Frame);
1329             }
1330         }
1331     }
1332 
1333     if(!Cache->BlockCount) {
1334         return STATUS_SUCCESS;
1335     }
1336 
1337     // invoke media-specific limit-checker
1338     switch(Cache->Mode) {
1339     case WCACHE_MODE_RAM:
1340         return WCacheCheckLimitsRAM(Cache, Context, ReqLba, BCount);
1341     case WCACHE_MODE_ROM:
1342     case WCACHE_MODE_RW:
1343         return WCacheCheckLimitsRW(Cache, Context, ReqLba, BCount);
1344     case WCACHE_MODE_R:
1345         return WCacheCheckLimitsR(Cache, Context, ReqLba, BCount);
1346     }
1347     return STATUS_DRIVER_INTERNAL_ERROR;
1348 } // end WCacheCheckLimits()
1349 
1350 /*
1351   WCacheCheckLimitsRW() implements automatic flush and purge of
1352   unused blocks to keep enough free cache entries for newly
1353   read/written blocks for Random Access and ReWritable media
1354   using Read/Modify/Write technology.
1355   See also WCacheCheckLimits()
1356   Internal routine
1357  */
1358 OSSTATUS
1359 __fastcall
1360 WCacheCheckLimitsRW(
1361     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1362     IN PVOID Context,         // user-supplied context for IO callbacks
1363     IN lba_t ReqLba,          // first LBA to access/cache
1364     IN ULONG BCount           // number of Blocks to access/cache
1365     )
1366 {
1367     ULONG frame;
1368     lba_t firstLba;
1369     lba_t* List = Cache->CachedBlocksList;
1370     lba_t lastLba;
1371     lba_t Lba;
1372 //    PCHAR tmp_buff = Cache->tmp_buff;
1373     ULONG firstPos;
1374     ULONG lastPos;
1375     ULONG BSh = Cache->BlockSizeSh;
1376     ULONG BS = Cache->BlockSize;
1377     ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
1378     ULONG PSs = Cache->PacketSize;
1379     ULONG try_count = 0;
1380     PW_CACHE_ENTRY block_array;
1381     OSSTATUS status;
1382     SIZE_T ReadBytes;
1383     ULONG FreeFrameCount = 0;
1384 //    PVOID addr;
1385     PW_CACHE_ASYNC FirstWContext = NULL;
1386     PW_CACHE_ASYNC PrevWContext = NULL;
1387     ULONG chain_count = 0;
1388 
1389     if(Cache->FrameCount >= Cache->MaxFrames) {
1390         FreeFrameCount = Cache->FramesToKeepFree;
1391     } else
1392     if((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
1393            BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) {
1394         // we need free space to grow WCache without flushing data
1395         // for some period of time
1396         FreeFrameCount = Cache->FramesToKeepFree;
1397         goto Try_Another_Frame;
1398     }
1399     // remove(flush) some frames
1400     while((Cache->FrameCount >= Cache->MaxFrames) || FreeFrameCount) {
1401 Try_Another_Frame:
1402         if(!Cache->FrameCount || !Cache->BlockCount) {
1403             //ASSERT(!Cache->FrameCount);
1404             if(Cache->FrameCount) {
1405                 UDFPrint(("ASSERT: Cache->FrameCount = %d, when 0 is expected\n", Cache->FrameCount));
1406             }
1407             ASSERT(!Cache->BlockCount);
1408             if(!Cache->FrameCount)
1409                 break;
1410         }
1411 
1412         frame = WCacheFindFrameToRelease(Cache);
1413 #if 0
1414         if(Cache->FrameList[frame].WriteCount) {
1415             try_count++;
1416             if(try_count < MAX_TRIES_FOR_NA) goto Try_Another_Frame;
1417         } else {
1418             try_count = 0;
1419         }
1420 #else
1421         if(Cache->FrameList[frame].UpdateCount) {
1422             try_count = MAX_TRIES_FOR_NA;
1423         } else {
1424             try_count = 0;
1425         }
1426 #endif
1427 
1428         if(FreeFrameCount)
1429             FreeFrameCount--;
1430 
1431         firstLba = frame << Cache->BlocksPerFrameSh;
1432         lastLba = firstLba + Cache->BlocksPerFrame;
1433         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba);
1434         lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba);
1435         block_array = Cache->FrameList[frame].Frame;
1436 
1437         if(!block_array) {
1438             UDFPrint(("Hmm...\n"));
1439             BrutePoint();
1440             return STATUS_DRIVER_INTERNAL_ERROR;
1441         }
1442 
1443         while(firstPos < lastPos) {
1444             // flush packet
1445             Lba = List[firstPos] & ~(PSs-1);
1446 
1447             // write packet out or prepare and add to chain (if chained mode enabled)
1448             status = WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
1449                 Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE);
1450 
1451             if(status != STATUS_PENDING) {
1452                 // free memory
1453                 WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, PSs);
1454             }
1455 
1456             Lba += PSs;
1457             while((firstPos < lastPos) && (Lba > List[firstPos])) {
1458                 firstPos++;
1459             }
1460             chain_count++;
1461             // write chained packets
1462             if(chain_count >= WCACHE_MAX_CHAIN) {
1463                 WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
1464                 chain_count = 0;
1465             }
1466         }
1467         // remove flushed blocks from all lists
1468         WCacheRemoveRangeFromList(List, &(Cache->BlockCount), firstLba, Cache->BlocksPerFrame);
1469         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame);
1470 
1471         WCacheRemoveFrame(Cache, Context, frame);
1472     }
1473 
1474     // check if we try to read too much data
1475     if(BCount > Cache->MaxBlocks) {
1476         WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext);
1477         return STATUS_INVALID_PARAMETER;
1478     }
1479 
1480     // remove(flush) packet
1481     while((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
1482            BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) {
1483         try_count = 0;
1484 Try_Another_Block:
1485 
1486         Lba = WCacheFindLbaToRelease(Cache) & ~(PSs-1);
1487         if(Lba == WCACHE_INVALID_LBA) {
1488             ASSERT(!Cache->FrameCount);
1489             ASSERT(!Cache->BlockCount);
1490             break;
1491         }
1492         frame = Lba >> Cache->BlocksPerFrameSh;
1493         firstLba = frame << Cache->BlocksPerFrameSh;
1494         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba);
1495         lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba+PSs);
1496         block_array = Cache->FrameList[frame].Frame;
1497         if(!block_array) {
1498             // write already prepared blocks to disk and return error
1499             WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext);
1500             ASSERT(FALSE);
1501             return STATUS_DRIVER_INTERNAL_ERROR;
1502         }
1503 
1504         // write packet out or prepare and add to chain (if chained mode enabled)
1505         status = WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
1506             Lba, BSh, BS, PS, PSs, &ReadBytes, (try_count >= MAX_TRIES_FOR_NA), ASYNC_STATE_NONE);
1507 
1508         if(status == STATUS_RETRY) {
1509             try_count++;
1510             goto Try_Another_Block;
1511         }
1512 
1513         // free memory
1514         WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, PSs);
1515 
1516         WCacheRemoveRangeFromList(List, &(Cache->BlockCount), Lba, PSs);
1517         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, PSs);
1518         // check if frame is empty
1519         if(!(Cache->FrameList[frame].BlockCount)) {
1520             WCacheRemoveFrame(Cache, Context, frame);
1521         } else {
1522             ASSERT(Cache->FrameList[frame].Frame);
1523         }
1524         chain_count++;
1525         if(chain_count >= WCACHE_MAX_CHAIN) {
1526             WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
1527             chain_count = 0;
1528         }
1529     }
1530     WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext);
1531     return STATUS_SUCCESS;
1532 } // end WCacheCheckLimitsRW()
1533 
1534 OSSTATUS
1535 __fastcall
1536 WCacheFlushBlocksRAM(
1537     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1538     IN PVOID Context,         // user-supplied context for IO callbacks
1539     PW_CACHE_ENTRY block_array,
1540     lba_t* List,
1541     ULONG firstPos,
1542     ULONG lastPos,
1543     BOOLEAN Purge
1544     )
1545 {
1546     ULONG frame;
1547     lba_t Lba;
1548     lba_t PrevLba;
1549     lba_t firstLba;
1550     PCHAR tmp_buff = NULL;
1551     ULONG n;
1552     ULONG BSh = Cache->BlockSizeSh;
1553     ULONG BS = Cache->BlockSize;
1554 //    ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
1555     ULONG PSs = Cache->PacketSize;
1556     SIZE_T _WrittenBytes;
1557     OSSTATUS status = STATUS_SUCCESS;
1558 
1559     frame = List[firstPos] >> Cache->BlocksPerFrameSh;
1560     firstLba = frame << Cache->BlocksPerFrameSh;
1561 
1562     while(firstPos < lastPos) {
1563         // flush blocks
1564         ASSERT(Cache->FrameCount <= Cache->MaxFrames);
1565         Lba = List[firstPos];
1566         if(!WCacheGetModFlag(block_array, Lba - firstLba)) {
1567             // free memory
1568             if(Purge) {
1569                 WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, 1);
1570             }
1571             firstPos++;
1572             continue;
1573         }
1574         tmp_buff = Cache->tmp_buff;
1575         PrevLba = Lba;
1576         n=1;
1577         while((firstPos+n < lastPos) &&
1578               (List[firstPos+n] == PrevLba+1)) {
1579             PrevLba++;
1580             if(!WCacheGetModFlag(block_array, PrevLba - firstLba))
1581                 break;
1582             DbgCopyMemory(tmp_buff + (n << BSh),
1583                         (PVOID)WCacheSectorAddr(block_array, PrevLba - firstLba),
1584                         BS);
1585             n++;
1586             if(n >= PSs)
1587                 break;
1588         }
1589         if(n > 1) {
1590             DbgCopyMemory(tmp_buff,
1591                         (PVOID)WCacheSectorAddr(block_array, Lba - firstLba),
1592                         BS);
1593         } else {
1594             tmp_buff = (PCHAR)WCacheSectorAddr(block_array, Lba - firstLba);
1595         }
1596         // write sectors out
1597         status = Cache->WriteProc(Context, tmp_buff, n<<BSh, Lba, &_WrittenBytes, 0);
1598         if(!OS_SUCCESS(status)) {
1599             status = WCacheRaiseIoError(Cache, Context, status, Lba, n, tmp_buff, WCACHE_W_OP, NULL);
1600             if(!OS_SUCCESS(status)) {
1601                 BrutePoint();
1602             }
1603         }
1604         firstPos += n;
1605         if(Purge) {
1606             // free memory
1607             WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, n);
1608         } else {
1609             // clear Modified flag
1610             ULONG i;
1611             Lba -= firstLba;
1612             for(i=0; i<n; i++) {
1613                 WCacheClrModFlag(block_array, Lba+i);
1614             }
1615         }
1616     }
1617 
1618     return status;
1619 } // end WCacheFlushBlocksRAM()
1620 
1621 /*
1622   WCacheCheckLimitsRAM() implements automatic flush and purge of
1623   unused blocks to keep enough free cache entries for newly
1624   read/written blocks for Random Access media.
1625   See also WCacheCheckLimits()
1626   Internal routine
1627  */
1628 OSSTATUS
1629 __fastcall
1630 WCacheCheckLimitsRAM(
1631     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1632     IN PVOID Context,         // user-supplied context for IO callbacks
1633     IN lba_t ReqLba,          // first LBA to access/cache
1634     IN ULONG BCount           // number of Blocks to access/cache
1635     )
1636 {
1637     ULONG frame;
1638     lba_t firstLba;
1639     lba_t* List = Cache->CachedBlocksList;
1640     lba_t lastLba;
1641     lba_t Lba;
1642 //    PCHAR tmp_buff = Cache->tmp_buff;
1643     ULONG firstPos;
1644     ULONG lastPos;
1645 //    ULONG BSh = Cache->BlockSizeSh;
1646 //    ULONG BS = Cache->BlockSize;
1647 //    ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
1648     ULONG PSs = Cache->PacketSize;
1649 //    ULONG try_count = 0;
1650     PW_CACHE_ENTRY block_array;
1651 //    OSSTATUS status;
1652     ULONG FreeFrameCount = 0;
1653 //    PVOID addr;
1654 
1655     if(Cache->FrameCount >= Cache->MaxFrames) {
1656         FreeFrameCount = Cache->FramesToKeepFree;
1657     } else
1658     if((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
1659            BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) {
1660         // we need free space to grow WCache without flushing data
1661         // for some period of time
1662         FreeFrameCount = Cache->FramesToKeepFree;
1663         goto Try_Another_Frame;
1664     }
1665     // remove(flush) some frames
1666     while((Cache->FrameCount >= Cache->MaxFrames) || FreeFrameCount) {
1667         ASSERT(Cache->FrameCount <= Cache->MaxFrames);
1668 Try_Another_Frame:
1669         if(!Cache->FrameCount || !Cache->BlockCount) {
1670             ASSERT(!Cache->FrameCount);
1671             ASSERT(!Cache->BlockCount);
1672             if(!Cache->FrameCount)
1673                 break;
1674         }
1675 
1676         frame = WCacheFindFrameToRelease(Cache);
1677 #if 0
1678         if(Cache->FrameList[frame].WriteCount) {
1679             try_count++;
1680             if(try_count < MAX_TRIES_FOR_NA) goto Try_Another_Frame;
1681         } else {
1682             try_count = 0;
1683         }
1684 #else
1685 /*
1686         if(Cache->FrameList[frame].UpdateCount) {
1687             try_count = MAX_TRIES_FOR_NA;
1688         } else {
1689             try_count = 0;
1690         }
1691 */
1692 #endif
1693 
1694         if(FreeFrameCount)
1695             FreeFrameCount--;
1696 
1697         firstLba = frame << Cache->BlocksPerFrameSh;
1698         lastLba = firstLba + Cache->BlocksPerFrame;
1699         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba);
1700         lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba);
1701         block_array = Cache->FrameList[frame].Frame;
1702 
1703         if(!block_array) {
1704             UDFPrint(("Hmm...\n"));
1705             BrutePoint();
1706             return STATUS_DRIVER_INTERNAL_ERROR;
1707         }
1708         WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, TRUE);
1709 
1710         WCacheRemoveRangeFromList(List, &(Cache->BlockCount), firstLba, Cache->BlocksPerFrame);
1711         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame);
1712         WCacheRemoveFrame(Cache, Context, frame);
1713     }
1714 
1715     // check if we try to read too much data
1716     if(BCount > Cache->MaxBlocks) {
1717         return STATUS_INVALID_PARAMETER;
1718     }
1719 
1720     // remove(flush) packet
1721     while((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
1722            BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) {
1723 //        try_count = 0;
1724 //Try_Another_Block:
1725 
1726         ASSERT(Cache->FrameCount <= Cache->MaxFrames);
1727         Lba = WCacheFindLbaToRelease(Cache) & ~(PSs-1);
1728         if(Lba == WCACHE_INVALID_LBA) {
1729             ASSERT(!Cache->FrameCount);
1730             ASSERT(!Cache->BlockCount);
1731             break;
1732         }
1733         frame = Lba >> Cache->BlocksPerFrameSh;
1734         firstLba = frame << Cache->BlocksPerFrameSh;
1735         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba);
1736         lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba+PSs);
1737         block_array = Cache->FrameList[frame].Frame;
1738         if(!block_array) {
1739             ASSERT(FALSE);
1740             return STATUS_DRIVER_INTERNAL_ERROR;
1741         }
1742         WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, TRUE);
1743         WCacheRemoveRangeFromList(List, &(Cache->BlockCount), Lba, PSs);
1744         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, PSs);
1745         // check if frame is empty
1746         if(!(Cache->FrameList[frame].BlockCount)) {
1747             WCacheRemoveFrame(Cache, Context, frame);
1748         } else {
1749             ASSERT(Cache->FrameList[frame].Frame);
1750         }
1751     }
1752     return STATUS_SUCCESS;
1753 } // end WCacheCheckLimitsRAM()
1754 
1755 /*
1756   WCachePurgeAllRAM()
1757   Internal routine
1758  */
1759 OSSTATUS
1760 __fastcall
1761 WCachePurgeAllRAM(
1762     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1763     IN PVOID Context          // user-supplied context for IO callbacks
1764     )
1765 {
1766     ULONG frame;
1767     lba_t firstLba;
1768     lba_t* List = Cache->CachedBlocksList;
1769     lba_t lastLba;
1770     ULONG firstPos;
1771     ULONG lastPos;
1772     PW_CACHE_ENTRY block_array;
1773 //    OSSTATUS status;
1774 
1775     // remove(flush) some frames
1776     while(Cache->FrameCount) {
1777 
1778         frame = Cache->CachedFramesList[0];
1779 
1780         firstLba = frame << Cache->BlocksPerFrameSh;
1781         lastLba = firstLba + Cache->BlocksPerFrame;
1782         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba);
1783         lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba);
1784         block_array = Cache->FrameList[frame].Frame;
1785 
1786         if(!block_array) {
1787             UDFPrint(("Hmm...\n"));
1788             BrutePoint();
1789             return STATUS_DRIVER_INTERNAL_ERROR;
1790         }
1791         WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, TRUE);
1792 
1793         WCacheRemoveRangeFromList(List, &(Cache->BlockCount), firstLba, Cache->BlocksPerFrame);
1794         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame);
1795         WCacheRemoveFrame(Cache, Context, frame);
1796     }
1797 
1798     ASSERT(!Cache->FrameCount);
1799     ASSERT(!Cache->BlockCount);
1800     return STATUS_SUCCESS;
1801 } // end WCachePurgeAllRAM()
1802 
1803 /*
1804   WCacheFlushAllRAM()
1805   Internal routine
1806  */
1807 OSSTATUS
1808 __fastcall
1809 WCacheFlushAllRAM(
1810     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1811     IN PVOID Context          // user-supplied context for IO callbacks
1812     )
1813 {
1814     ULONG frame;
1815     lba_t firstLba;
1816     lba_t* List = Cache->CachedBlocksList;
1817     lba_t lastLba;
1818     ULONG firstPos;
1819     ULONG lastPos;
1820     PW_CACHE_ENTRY block_array;
1821 //    OSSTATUS status;
1822 
1823     // flush frames
1824     while(Cache->WriteCount) {
1825 
1826         frame = Cache->CachedModifiedBlocksList[0] >> Cache->BlocksPerFrameSh;
1827 
1828         firstLba = frame << Cache->BlocksPerFrameSh;
1829         lastLba = firstLba + Cache->BlocksPerFrame;
1830         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba);
1831         lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba);
1832         block_array = Cache->FrameList[frame].Frame;
1833 
1834         if(!block_array) {
1835             UDFPrint(("Hmm...\n"));
1836             BrutePoint();
1837             return STATUS_DRIVER_INTERNAL_ERROR;
1838         }
1839         WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, FALSE);
1840 
1841         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame);
1842     }
1843 
1844     return STATUS_SUCCESS;
1845 } // end WCacheFlushAllRAM()
1846 
1847 /*
1848   WCachePreReadPacket__() reads & caches the whole packet containing
1849   requested LBA. This routine just caches data, it doesn't copy anything
1850   to user buffer.
1851   In general we have no user buffer here... ;)
1852   Public routine
1853 */
1854 OSSTATUS
1855 WCachePreReadPacket__(
1856     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1857     IN PVOID Context,         // user-supplied context for IO callbacks
1858     IN lba_t Lba              // LBA to cache together with whole packet
1859     )
1860 {
1861     ULONG frame;
1862     OSSTATUS status = STATUS_SUCCESS;
1863     PW_CACHE_ENTRY block_array;
1864     ULONG BSh = Cache->BlockSizeSh;
1865     ULONG BS = Cache->BlockSize;
1866     PCHAR addr;
1867     SIZE_T _ReadBytes;
1868     ULONG PS = Cache->PacketSize; // (in blocks)
1869     ULONG BCount = PS;
1870     ULONG i, n, err_count;
1871     BOOLEAN sector_added = FALSE;
1872     ULONG block_type;
1873     BOOLEAN zero = FALSE;//TRUE;
1874 /*
1875     ULONG first_zero=0, last_zero=0;
1876     BOOLEAN count_first_zero = TRUE;
1877 */
1878 
1879     Lba &= ~(PS-1);
1880     frame = Lba >> Cache->BlocksPerFrameSh;
1881     i = Lba - (frame << Cache->BlocksPerFrameSh);
1882 
1883     // assume successful operation
1884     block_array = Cache->FrameList[frame].Frame;
1885     if(!block_array) {
1886         ASSERT(Cache->FrameCount < Cache->MaxFrames);
1887         block_array = WCacheInitFrame(Cache, Context, frame);
1888         if(!block_array)
1889             return STATUS_INSUFFICIENT_RESOURCES;
1890     }
1891 
1892     // skip cached extent (if any)
1893     n=0;
1894     while((n < BCount) &&
1895           (n < Cache->BlocksPerFrame)) {
1896 
1897         addr = (PCHAR)WCacheSectorAddr(block_array, i+n);
1898         block_type = Cache->CheckUsedProc(Context, Lba+n);
1899         if(/*WCacheGetBadFlag(block_array,i+n)*/
1900            block_type & WCACHE_BLOCK_BAD) {
1901             // bad packet. no pre-read
1902             return STATUS_DEVICE_DATA_ERROR;
1903         }
1904         if(!(block_type & WCACHE_BLOCK_ZERO)) {
1905             zero = FALSE;
1906             //count_first_zero = FALSE;
1907             //last_zero = 0;
1908             if(!addr) {
1909                 // sector is not cached, stop search
1910                 break;
1911             }
1912         } else {
1913 /*
1914             if(count_first_zero) {
1915                 first_zero++;
1916             }
1917             last_zero++;
1918 */
1919         }
1920         n++;
1921     }
1922     // do nothing if all sectors are already cached
1923     if(n < BCount) {
1924 
1925         // read whole packet
1926         if(!zero) {
1927             status = Cache->ReadProc(Context, Cache->tmp_buff_r, PS<<BSh, Lba, &_ReadBytes, PH_TMP_BUFFER);
1928             if(!OS_SUCCESS(status)) {
1929                 status = WCacheRaiseIoError(Cache, Context, status, Lba, PS, Cache->tmp_buff_r, WCACHE_R_OP, NULL);
1930             }
1931         } else {
1932             status = STATUS_SUCCESS;
1933             //RtlZeroMemory(Cache->tmp_buff_r, PS<<BSh);
1934             _ReadBytes = PS<<BSh;
1935         }
1936         if(OS_SUCCESS(status)) {
1937             // and now we'll copy them to cache
1938             for(n=0; n<BCount; n++, i++) {
1939                 if(WCacheSectorAddr(block_array,i)) {
1940                     continue;
1941                 }
1942                 addr = block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
1943                 if(!addr) {
1944                     BrutePoint();
1945                     break;
1946                 }
1947                 sector_added = TRUE;
1948                 if(!zero) {
1949                     DbgCopyMemory(addr, Cache->tmp_buff_r+(n<<BSh), BS);
1950                 } else {
1951                     RtlZeroMemory(addr, BS);
1952                 }
1953                 Cache->FrameList[frame].BlockCount++;
1954             }
1955         } else {
1956             // read sectors one by one and copy them to cache
1957             // unreadable sectors will be treated as zero-filled
1958             err_count = 0;
1959             for(n=0; n<BCount; n++, i++) {
1960                 if(WCacheSectorAddr(block_array,i)) {
1961                     continue;
1962                 }
1963                 addr = block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
1964                 if(!addr) {
1965                     BrutePoint();
1966                     break;
1967                 }
1968                 sector_added = TRUE;
1969                 status = Cache->ReadProc(Context, Cache->tmp_buff_r, BS, Lba+n, &_ReadBytes, PH_TMP_BUFFER);
1970                 if(!OS_SUCCESS(status)) {
1971                     status = WCacheRaiseIoError(Cache, Context, status, Lba+n, 1, Cache->tmp_buff_r, WCACHE_R_OP, NULL);
1972                     if(!OS_SUCCESS(status)) {
1973                         err_count++;
1974                     }
1975                 }
1976                 if(!zero && OS_SUCCESS(status)) {
1977                     DbgCopyMemory(addr, Cache->tmp_buff_r, BS);
1978                 } else
1979                 if(Cache->RememberBB) {
1980                     RtlZeroMemory(addr, BS);
1981                     /*
1982                     if(!OS_SUCCESS(status)) {
1983                         WCacheSetBadFlag(block_array,i);
1984                     }
1985                     */
1986                 }
1987                 Cache->FrameList[frame].BlockCount++;
1988                 if(err_count >= 2) {
1989                     break;
1990                 }
1991             }
1992 //            _ReadBytes = n<<BSh;
1993         }
1994     }
1995 
1996     // we know the number of unread sectors if an error occured
1997     // so we can need to update BlockCount
1998     // return number of read bytes
1999     if(sector_added)
2000         WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, n);
2001 
2002     return status;
2003 } // end WCachePreReadPacket__()
2004 
2005 /*
2006   WCacheReadBlocks__() reads data from cache or
2007   read it form media and store in cache.
2008   Public routine
2009  */
2010 OSSTATUS
2011 WCacheReadBlocks__(
2012     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2013     IN PVOID Context,         // user-supplied context for IO callbacks
2014     IN PCHAR Buffer,          // user-supplied buffer for read blocks
2015     IN lba_t Lba,             // LBA to start read from
2016     IN ULONG BCount,          // number of blocks to be read
2017     OUT PSIZE_T ReadBytes,     // user-supplied pointer to ULONG that will
2018                               //   recieve number of actually read bytes
2019     IN BOOLEAN CachedOnly     // specifies that cache is already locked
2020     )
2021 {
2022     ULONG frame;
2023     ULONG i, saved_i, saved_BC = BCount, n;
2024     OSSTATUS status = STATUS_SUCCESS;
2025     PW_CACHE_ENTRY block_array;
2026     ULONG BSh = Cache->BlockSizeSh;
2027     SIZE_T BS = Cache->BlockSize;
2028     PCHAR addr;
2029     ULONG to_read, saved_to_read;
2030 //    PCHAR saved_buff = Buffer;
2031     SIZE_T _ReadBytes;
2032     ULONG PS = Cache->PacketSize;
2033     ULONG MaxR = Cache->MaxBytesToRead;
2034     ULONG PacketMask = PS-1; // here we assume that Packet Size value is 2^n
2035     ULONG d;
2036     ULONG block_type;
2037 
2038     WcPrint(("WC:R %x (%x)\n", Lba, BCount));
2039 
2040     (*ReadBytes) = 0;
2041     // check if we try to read too much data
2042     if(BCount >= Cache->MaxBlocks) {
2043         i = 0;
2044         if(CachedOnly) {
2045             status = STATUS_INVALID_PARAMETER;
2046             goto EO_WCache_R2;
2047         }
2048         while(TRUE) {
2049             status = WCacheReadBlocks__(Cache, Context, Buffer + (i<<BSh), Lba, PS, &_ReadBytes, FALSE);
2050             (*ReadBytes) += _ReadBytes;
2051             if(!OS_SUCCESS(status) || (BCount <= PS)) break;
2052             BCount -= PS;
2053             Lba += PS;
2054             i += PS;
2055         }
2056         return status;
2057     }
2058     // check if we try to access beyond cached area
2059     if((Lba < Cache->FirstLba) ||
2060        (Lba + BCount - 1 > Cache->LastLba)) {
2061         status = Cache->ReadProc(Context, Buffer, BCount, Lba, ReadBytes, 0);
2062         if(!OS_SUCCESS(status)) {
2063             status = WCacheRaiseIoError(Cache, Context, status, Lba, BCount, Buffer, WCACHE_R_OP, NULL);
2064         }
2065         return status;
2066     }
2067     if(!CachedOnly) {
2068         ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2069     }
2070 
2071     frame = Lba >> Cache->BlocksPerFrameSh;
2072     i = Lba - (frame << Cache->BlocksPerFrameSh);
2073 
2074     if(Cache->CacheWholePacket && (BCount < PS)) {
2075         if(!CachedOnly &&
2076            !OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba & ~(PS-1), PS*2)) ) {
2077             ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2078             return status;
2079         }
2080     } else {
2081         if(!CachedOnly &&
2082            !OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba, BCount))) {
2083             ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2084             return status;
2085         }
2086     }
2087     if(!CachedOnly) {
2088         // convert to shared
2089 //        ExConvertExclusiveToSharedLite(&(Cache->WCacheLock));
2090     }
2091 
2092     // pre-read packet. It is very useful for
2093     // highly fragmented files
2094     if(Cache->CacheWholePacket && (BCount < PS)) {
2095 //        status = WCacheReadBlocks__(Cache, Context, Cache->tmp_buff_r, Lba & (~PacketMask), PS, &_ReadBytes, TRUE);
2096         // we should not perform IO if user requested CachedOnly data
2097         if(!CachedOnly) {
2098             status = WCachePreReadPacket__(Cache, Context, Lba);
2099         }
2100         status = STATUS_SUCCESS;
2101     }
2102 
2103     // assume successful operation
2104     block_array = Cache->FrameList[frame].Frame;
2105     if(!block_array) {
2106         ASSERT(!CachedOnly);
2107         ASSERT(Cache->FrameCount < Cache->MaxFrames);
2108         block_array = WCacheInitFrame(Cache, Context, frame);
2109         if(!block_array) {
2110             status = STATUS_INSUFFICIENT_RESOURCES;
2111             goto EO_WCache_R;
2112         }
2113     }
2114 
2115     Cache->FrameList[frame].AccessCount++;
2116     while(BCount) {
2117         if(i >= Cache->BlocksPerFrame) {
2118             frame++;
2119             block_array = Cache->FrameList[frame].Frame;
2120             i -= Cache->BlocksPerFrame;
2121         }
2122         if(!block_array) {
2123             ASSERT(Cache->FrameCount < Cache->MaxFrames);
2124             block_array = WCacheInitFrame(Cache, Context, frame);
2125             if(!block_array) {
2126                 status = STATUS_INSUFFICIENT_RESOURCES;
2127                 goto EO_WCache_R;
2128             }
2129         }
2130         // 'read' cached extent (if any)
2131         // it is just copying
2132         while(BCount &&
2133               (i < Cache->BlocksPerFrame) &&
2134               (addr = (PCHAR)WCacheSectorAddr(block_array, i)) ) {
2135             block_type = Cache->CheckUsedProc(Context, Lba+saved_BC-BCount);
2136             if(block_type & WCACHE_BLOCK_BAD) {
2137             //if(WCacheGetBadFlag(block_array,i)) {
2138                 status = STATUS_DEVICE_DATA_ERROR;
2139                 goto EO_WCache_R;
2140             }
2141             DbgCopyMemory(Buffer, addr, BS);
2142             Buffer += BS;
2143             *ReadBytes += BS;
2144             i++;
2145             BCount--;
2146         }
2147         // read non-cached packet-size-aligned extent (if any)
2148         // now we'll calculate total length & decide if it has enough size
2149         if(!((d = Lba+saved_BC-BCount) & PacketMask) && d ) {
2150             n = 0;
2151             while(BCount &&
2152                   (i < Cache->BlocksPerFrame) &&
2153                   (!WCacheSectorAddr(block_array, i)) ) {
2154                  n++;
2155                  BCount--;
2156             }
2157             BCount += n;
2158             n &= ~PacketMask;
2159             if(n>PS) {
2160                 if(!OS_SUCCESS(status = Cache->ReadProc(Context, Buffer, BS*n, Lba+saved_BC-BCount, &_ReadBytes, 0))) {
2161                     status = WCacheRaiseIoError(Cache, Context, status, Lba+saved_BC-BCount, n, Buffer, WCACHE_R_OP, NULL);
2162                     if(!OS_SUCCESS(status)) {
2163                         goto EO_WCache_R;
2164                     }
2165                 }
2166 //                WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount);
2167                 BCount -= n;
2168                 Lba += saved_BC - BCount;
2169                 saved_BC = BCount;
2170                 i += n;
2171                 Buffer += BS*n;
2172                 *ReadBytes += BS*n;
2173             }
2174 //        } else {
2175 //            UDFPrint(("Unaligned\n"));
2176         }
2177         // read non-cached extent (if any)
2178         // firstable, we'll get total number of sectors to read
2179         to_read = 0;
2180         saved_i = i;
2181         d = BCount;
2182         while(d &&
2183               (i < Cache->BlocksPerFrame) &&
2184               (!WCacheSectorAddr(block_array, i)) ) {
2185             i++;
2186             to_read += BS;
2187             d--;
2188         }
2189         // read some not cached sectors
2190         if(to_read) {
2191             i = saved_i;
2192             saved_to_read = to_read;
2193             d = BCount - d;
2194             // split request if necessary
2195             if(saved_to_read > MaxR) {
2196                 WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount);
2197                 n = MaxR >> BSh;
2198                 do {
2199                     status = Cache->ReadProc(Context, Buffer, MaxR, i + (frame << Cache->BlocksPerFrameSh), &_ReadBytes, 0);
2200                     *ReadBytes += _ReadBytes;
2201                     if(!OS_SUCCESS(status)) {
2202                         _ReadBytes &= ~(BS-1);
2203                         BCount -= _ReadBytes >> BSh;
2204                         saved_to_read -= _ReadBytes;
2205                         Buffer += _ReadBytes;
2206                         saved_BC = BCount;
2207                         goto store_read_data_1;
2208                     }
2209                     Buffer += MaxR;
2210                     saved_to_read -= MaxR;
2211                     i += n;
2212                     BCount -= n;
2213                     d -= n;
2214                 } while(saved_to_read > MaxR);
2215                 saved_BC = BCount;
2216             }
2217             if(saved_to_read) {
2218                 status = Cache->ReadProc(Context, Buffer, saved_to_read, i + (frame << Cache->BlocksPerFrameSh), &_ReadBytes, 0);
2219                 *ReadBytes += _ReadBytes;
2220                 if(!OS_SUCCESS(status)) {
2221                     _ReadBytes &= ~(BS-1);
2222                     BCount -= _ReadBytes >> BSh;
2223                     saved_to_read -= _ReadBytes;
2224                     Buffer += _ReadBytes;
2225                     goto store_read_data_1;
2226                 }
2227                 Buffer += saved_to_read;
2228                 saved_to_read = 0;
2229                 BCount -= d;
2230             }
2231 
2232 store_read_data_1:
2233             // and now we'll copy them to cache
2234 
2235             //
2236             Buffer -= (to_read - saved_to_read);
2237             i = saved_i;
2238             while(to_read - saved_to_read) {
2239                 block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
2240                 if(!block_array[i].Sector) {
2241                     BCount += to_read >> BSh;
2242                     status = STATUS_INSUFFICIENT_RESOURCES;
2243                     goto EO_WCache_R;
2244                 }
2245                 DbgCopyMemory(block_array[i].Sector, Buffer, BS);
2246                 Cache->FrameList[frame].BlockCount++;
2247                 i++;
2248                 Buffer += BS;
2249                 to_read -= BS;
2250             }
2251             if(!OS_SUCCESS(status))
2252                 goto EO_WCache_R;
2253             to_read = 0;
2254         }
2255     }
2256 
2257 EO_WCache_R:
2258 
2259     // we know the number of unread sectors if an error occured
2260     // so we can need to update BlockCount
2261     // return number of read bytes
2262     WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount);
2263 //    Cache->FrameList[frame].BlockCount -= BCount;
2264 EO_WCache_R2:
2265     if(!CachedOnly) {
2266         ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2267     }
2268 
2269     return status;
2270 } // end WCacheReadBlocks__()
2271 
2272 /*
2273   WCacheWriteBlocks__() writes data to cache.
2274   Data is written directly to media if:
2275   1) requested block is Packet-aligned
2276   2) requested Lba(s) lays beyond cached area
2277   Public routine
2278  */
2279 OSSTATUS
2280 WCacheWriteBlocks__(
2281     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2282     IN PVOID Context,         // user-supplied context for IO callbacks
2283     IN PCHAR Buffer,          // user-supplied buffer containing data to be written
2284     IN lba_t Lba,             // LBA to start write from
2285     IN ULONG BCount,          // number of blocks to be written
2286     OUT PSIZE_T WrittenBytes,  // user-supplied pointer to ULONG that will
2287                               //   recieve number of actually written bytes
2288     IN BOOLEAN CachedOnly     // specifies that cache is already locked
2289     )
2290 {
2291     ULONG frame;
2292     ULONG i, saved_BC = BCount, n, d;
2293     OSSTATUS status = STATUS_SUCCESS;
2294     PW_CACHE_ENTRY block_array;
2295     ULONG BSh = Cache->BlockSizeSh;
2296     ULONG BS = Cache->BlockSize;
2297     PCHAR addr;
2298 //    PCHAR saved_buff = Buffer;
2299     SIZE_T _WrittenBytes;
2300     ULONG PS = Cache->PacketSize;
2301     ULONG PacketMask = PS-1; // here we assume that Packet Size value is 2^n
2302     ULONG block_type;
2303 //    BOOLEAN Aligned = FALSE;
2304 
2305     BOOLEAN WriteThrough = FALSE;
2306     lba_t   WTh_Lba;
2307     ULONG   WTh_BCount;
2308 
2309     WcPrint(("WC:W %x (%x)\n", Lba, BCount));
2310 
2311     *WrittenBytes = 0;
2312 //    UDFPrint(("BCount:%x\n",BCount));
2313     // check if we try to read too much data
2314     if(BCount >= Cache->MaxBlocks) {
2315         i = 0;
2316         if(CachedOnly) {
2317             status = STATUS_INVALID_PARAMETER;
2318             goto EO_WCache_W2;
2319         }
2320         while(TRUE) {
2321 //            UDFPrint(("  BCount:%x\n",BCount));
2322             status = WCacheWriteBlocks__(Cache, Context, Buffer + (i<<BSh), Lba, min(PS,BCount), &_WrittenBytes, FALSE);
2323             (*WrittenBytes) += _WrittenBytes;
2324             BCount -= PS;
2325             Lba += PS;
2326             i += PS;
2327             if(!OS_SUCCESS(status) || (BCount < PS))
2328                 return status;
2329         }
2330     }
2331     // check if we try to access beyond cached area
2332     if((Lba < Cache->FirstLba) ||
2333        (Lba + BCount - 1 > Cache->LastLba)) {
2334         return STATUS_INVALID_PARAMETER;
2335     }
2336     if(!CachedOnly) {
2337         ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2338     }
2339 
2340     frame = Lba >> Cache->BlocksPerFrameSh;
2341     i = Lba - (frame << Cache->BlocksPerFrameSh);
2342 
2343     if(!CachedOnly &&
2344        !OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba, BCount))) {
2345         ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2346         return status;
2347     }
2348 
2349     // assume successful operation
2350     block_array = Cache->FrameList[frame].Frame;
2351     if(!block_array) {
2352 
2353         if(BCount && !(BCount & (PS-1)) && !(Lba & (PS-1)) &&
2354            (Cache->Mode != WCACHE_MODE_R) &&
2355            (i+BCount <= Cache->BlocksPerFrame) &&
2356             !Cache->NoWriteThrough) {
2357             status = Cache->WriteProc(Context, Buffer, BCount<<BSh, Lba, WrittenBytes, 0);
2358             if(!OS_SUCCESS(status)) {
2359                 status = WCacheRaiseIoError(Cache, Context, status, Lba, BCount, Buffer, WCACHE_W_OP, NULL);
2360             }
2361             goto EO_WCache_W2;
2362         }
2363 
2364         ASSERT(!CachedOnly);
2365         ASSERT(Cache->FrameCount < Cache->MaxFrames);
2366         block_array = WCacheInitFrame(Cache, Context, frame);
2367         if(!block_array) {
2368             status = STATUS_INSUFFICIENT_RESOURCES;
2369             goto EO_WCache_W;
2370         }
2371     }
2372 
2373     if(Cache->Mode == WCACHE_MODE_RAM &&
2374        BCount &&
2375 //       !(Lba & (PS-1)) &&
2376        (!(BCount & (PS-1)) || (BCount > PS)) ) {
2377         WriteThrough = TRUE;
2378         WTh_Lba = Lba;
2379         WTh_BCount = BCount;
2380     } else
2381     if(Cache->Mode == WCACHE_MODE_RAM &&
2382        ((Lba & ~PacketMask) != ((Lba+BCount-1) & ~PacketMask))
2383       ) {
2384         WriteThrough = TRUE;
2385         WTh_Lba = Lba & ~PacketMask;
2386         WTh_BCount = PS;
2387     }
2388 
2389     Cache->FrameList[frame].UpdateCount++;
2390 //    UDFPrint(("    BCount:%x\n",BCount));
2391     while(BCount) {
2392         if(i >= Cache->BlocksPerFrame) {
2393             frame++;
2394             block_array = Cache->FrameList[frame].Frame;
2395             i -= Cache->BlocksPerFrame;
2396         }
2397         if(!block_array) {
2398             ASSERT(Cache->FrameCount < Cache->MaxFrames);
2399             block_array = WCacheInitFrame(Cache, Context, frame);
2400             if(!block_array) {
2401                 status = STATUS_INSUFFICIENT_RESOURCES;
2402                 goto EO_WCache_W;
2403             }
2404         }
2405         // 'write' cached extent (if any)
2406         // it is just copying
2407         while(BCount &&
2408               (i < Cache->BlocksPerFrame) &&
2409               (addr = (PCHAR)WCacheSectorAddr(block_array, i)) ) {
2410 //            UDFPrint(("addr:%x:Buffer:%x:BS:%x:BCount:%x\n",addr, Buffer, BS, BCount));
2411             block_type = Cache->CheckUsedProc(Context, Lba+saved_BC-BCount);
2412             if(Cache->NoWriteBB &&
2413                /*WCacheGetBadFlag(block_array,i)*/
2414                (block_type & WCACHE_BLOCK_BAD)) {
2415                 // bad packet. no cached write
2416                 status = STATUS_DEVICE_DATA_ERROR;
2417                 goto EO_WCache_W;
2418             }
2419             DbgCopyMemory(addr, Buffer, BS);
2420             WCacheSetModFlag(block_array, i);
2421             Buffer += BS;
2422             *WrittenBytes += BS;
2423             i++;
2424             BCount--;
2425         }
2426         // write non-cached not-aligned extent (if any) till aligned one
2427         while(BCount &&
2428               (i & PacketMask) &&
2429               (Cache->Mode != WCACHE_MODE_R) &&
2430               (i < Cache->BlocksPerFrame) &&
2431               (!WCacheSectorAddr(block_array, i)) ) {
2432             block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
2433             if(!block_array[i].Sector) {
2434                 status = STATUS_INSUFFICIENT_RESOURCES;
2435                 goto EO_WCache_W;
2436             }
2437 //            UDFPrint(("addr:%x:Buffer:%x:BS:%x:BCount:%x\n",block_array[i].Sector, Buffer, BS, BCount));
2438             DbgCopyMemory(block_array[i].Sector, Buffer, BS);
2439             WCacheSetModFlag(block_array, i);
2440             i++;
2441             Buffer += BS;
2442             *WrittenBytes += BS;
2443             BCount--;
2444             Cache->FrameList[frame].BlockCount ++;
2445         }
2446         // write non-cached packet-size-aligned extent (if any)
2447         // now we'll calculate total length & decide if has enough size
2448         if(!Cache->NoWriteThrough
2449                      &&
2450            ( !(i & PacketMask) ||
2451              ((Cache->Mode == WCACHE_MODE_R) && (BCount >= PS)) )) {
2452             n = 0;
2453             while(BCount &&
2454                   (i < Cache->BlocksPerFrame) &&
2455                   (!WCacheSectorAddr(block_array, i)) ) {
2456                  n++;
2457                  BCount--;
2458             }
2459             BCount += n;
2460             n &= ~PacketMask;
2461 //                if(!OS_SUCCESS(status = Cache->WriteProcAsync(Context, Buffer, BS*n, Lba+saved_BC-BCount, &_WrittenBytes, FALSE)))
2462             if(n) {
2463                 // add previously written data to list
2464                 d = saved_BC - BCount;
2465                 WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, d);
2466                 WCacheInsertRangeToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, d);
2467                 Lba += d;
2468                 saved_BC = BCount;
2469 
2470                 while(n) {
2471                     if(Cache->Mode == WCACHE_MODE_R)
2472                         Cache->UpdateRelocProc(Context, Lba, NULL, PS);
2473                     if(!OS_SUCCESS(status = Cache->WriteProc(Context, Buffer, PS<<BSh, Lba, &_WrittenBytes, 0))) {
2474                         status = WCacheRaiseIoError(Cache, Context, status, Lba, PS, Buffer, WCACHE_W_OP, NULL);
2475                         if(!OS_SUCCESS(status)) {
2476                             goto EO_WCache_W;
2477                         }
2478                     }
2479                     BCount -= PS;
2480                     Lba += PS;
2481                     saved_BC = BCount;
2482                     i += PS;
2483                     Buffer += PS<<BSh;
2484                     *WrittenBytes += PS<<BSh;
2485                     n-=PS;
2486                 }
2487             }
2488         }
2489         // write non-cached not-aligned extent (if any)
2490         while(BCount &&
2491               (i < Cache->BlocksPerFrame) &&
2492               (!WCacheSectorAddr(block_array, i)) ) {
2493             block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
2494             if(!block_array[i].Sector) {
2495                 status = STATUS_INSUFFICIENT_RESOURCES;
2496                 goto EO_WCache_W;
2497             }
2498 //            UDFPrint(("addr:%x:Buffer:%x:BS:%x:BCount:%x\n",block_array[i].Sector, Buffer, BS, BCount));
2499             DbgCopyMemory(block_array[i].Sector, Buffer, BS);
2500             WCacheSetModFlag(block_array, i);
2501             i++;
2502             Buffer += BS;
2503             *WrittenBytes += BS;
2504             BCount--;
2505             Cache->FrameList[frame].BlockCount ++;
2506         }
2507     }
2508 
2509 EO_WCache_W:
2510 
2511     // we know the number of unread sectors if an error occured
2512     // so we can need to update BlockCount
2513     // return number of read bytes
2514     WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount);
2515     WCacheInsertRangeToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, saved_BC - BCount);
2516 
2517     if(WriteThrough && !BCount) {
2518         ULONG d;
2519 //        lba_t lastLba;
2520         ULONG firstPos;
2521         ULONG lastPos;
2522 
2523         BCount = WTh_BCount;
2524         Lba = WTh_Lba;
2525         while(BCount) {
2526             frame = Lba >> Cache->BlocksPerFrameSh;
2527 //            firstLba = frame << Cache->BlocksPerFrameSh;
2528             firstPos = WCacheGetSortedListIndex(Cache->BlockCount, Cache->CachedBlocksList, Lba);
2529             d = min(Lba+BCount, (frame+1) << Cache->BlocksPerFrameSh) - Lba;
2530             lastPos = WCacheGetSortedListIndex(Cache->BlockCount, Cache->CachedBlocksList, Lba+d);
2531             block_array = Cache->FrameList[frame].Frame;
2532             if(!block_array) {
2533                 ASSERT(FALSE);
2534                 BCount -= d;
2535                 Lba += d;
2536                 continue;
2537             }
2538             status = WCacheFlushBlocksRAM(Cache, Context, block_array, Cache->CachedBlocksList, firstPos, lastPos, FALSE);
2539             WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, d);
2540             BCount -= d;
2541             Lba += d;
2542         }
2543     }
2544 
2545 EO_WCache_W2:
2546 
2547     if(!CachedOnly) {
2548         ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2549     }
2550     return status;
2551 } // end WCacheWriteBlocks__()
2552 
2553 /*
2554   WCacheFlushAll__() copies all data stored in cache to media.
2555   Flushed blocks are kept in cache.
2556   Public routine
2557  */
2558 VOID
2559 WCacheFlushAll__(
2560     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2561     IN PVOID Context)         // user-supplied context for IO callbacks
2562 {
2563     if(!(Cache->ReadProc)) return;
2564     ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2565 
2566     switch(Cache->Mode) {
2567     case WCACHE_MODE_RAM:
2568         WCacheFlushAllRAM(Cache, Context);
2569         break;
2570     case WCACHE_MODE_ROM:
2571     case WCACHE_MODE_RW:
2572         WCacheFlushAllRW(Cache, Context);
2573         break;
2574     case WCACHE_MODE_R:
2575         WCachePurgeAllR(Cache, Context);
2576         break;
2577     }
2578 
2579     ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2580     return;
2581 } // end WCacheFlushAll__()
2582 
2583 /*
2584   WCachePurgeAll__() copies all data stored in cache to media.
2585   Flushed blocks are removed cache.
2586   Public routine
2587  */
2588 VOID
2589 WCachePurgeAll__(
2590     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2591     IN PVOID Context)         // user-supplied context for IO callbacks
2592 {
2593     if(!(Cache->ReadProc)) return;
2594     ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2595 
2596     switch(Cache->Mode) {
2597     case WCACHE_MODE_RAM:
2598         WCachePurgeAllRAM(Cache, Context);
2599         break;
2600     case WCACHE_MODE_ROM:
2601     case WCACHE_MODE_RW:
2602         WCachePurgeAllRW(Cache, Context);
2603         break;
2604     case WCACHE_MODE_R:
2605         WCachePurgeAllR(Cache, Context);
2606         break;
2607     }
2608 
2609     ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2610     return;
2611 } // end WCachePurgeAll__()
2612 /*
2613   WCachePurgeAllRW() copies modified blocks from cache to media
2614   and removes them from cache
2615   This routine can be used for RAM, RW and ROM media.
2616   For ROM media blocks are just removed.
2617   Internal routine
2618  */
2619 VOID
2620 __fastcall
2621 WCachePurgeAllRW(
2622     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2623     IN PVOID Context)         // user-supplied context for IO callbacks
2624 {
2625     ULONG frame;
2626     lba_t firstLba;
2627     lba_t* List = Cache->CachedBlocksList;
2628     lba_t Lba;
2629 //    ULONG firstPos;
2630 //    ULONG lastPos;
2631     ULONG BSh = Cache->BlockSizeSh;
2632     ULONG BS = Cache->BlockSize;
2633     ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
2634     ULONG PSs = Cache->PacketSize;
2635     PW_CACHE_ENTRY block_array;
2636 //    OSSTATUS status;
2637     SIZE_T ReadBytes;
2638     PW_CACHE_ASYNC FirstWContext = NULL;
2639     PW_CACHE_ASYNC PrevWContext = NULL;
2640     ULONG chain_count = 0;
2641 
2642     if(!(Cache->ReadProc)) return;
2643 
2644     while(Cache->BlockCount) {
2645         Lba = List[0] & ~(PSs-1);
2646         frame = Lba >> Cache->BlocksPerFrameSh;
2647         firstLba = frame << Cache->BlocksPerFrameSh;
2648 //        firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba);
2649 //        lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba+PSs);
2650         block_array = Cache->FrameList[frame].Frame;
2651         if(!block_array) {
2652             BrutePoint();
2653             return;
2654         }
2655 
2656         WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
2657             Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE);
2658 
2659         // free memory
2660         WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, PSs);
2661 
2662         WCacheRemoveRangeFromList(List, &(Cache->BlockCount), Lba, PSs);
2663         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, PSs);
2664         // check if frame is empty
2665         if(!(Cache->FrameList[frame].BlockCount)) {
2666             WCacheRemoveFrame(Cache, Context, frame);
2667         } else {
2668             ASSERT(Cache->FrameList[frame].Frame);
2669         }
2670         chain_count++;
2671         if(chain_count >= WCACHE_MAX_CHAIN) {
2672             WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
2673             chain_count = 0;
2674         }
2675     }
2676     WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext);
2677     return;
2678 } // end WCachePurgeAllRW()
2679 
2680 /*
2681   WCacheFlushAllRW() copies modified blocks from cache to media.
2682   All blocks are not removed from cache.
2683   This routine can be used for RAM, RW and ROM media.
2684   Internal routine
2685  */
2686 VOID
2687 __fastcall
2688 WCacheFlushAllRW(
2689     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2690     IN PVOID Context)         // user-supplied context for IO callbacks
2691 {
2692     ULONG frame;
2693     lba_t firstLba;
2694     lba_t* List = Cache->CachedModifiedBlocksList;
2695     lba_t Lba;
2696 //    ULONG firstPos;
2697 //    ULONG lastPos;
2698     ULONG BSh = Cache->BlockSizeSh;
2699     ULONG BS = Cache->BlockSize;
2700     ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
2701     ULONG PSs = Cache->PacketSize;
2702     ULONG BFs = Cache->BlocksPerFrameSh;
2703     PW_CACHE_ENTRY block_array;
2704 //    OSSTATUS status;
2705     SIZE_T ReadBytes;
2706     PW_CACHE_ASYNC FirstWContext = NULL;
2707     PW_CACHE_ASYNC PrevWContext = NULL;
2708     ULONG i;
2709     ULONG chain_count = 0;
2710 
2711     if(!(Cache->ReadProc)) return;
2712 
2713     // walk through modified blocks
2714     while(Cache->WriteCount) {
2715         Lba = List[0] & ~(PSs-1);
2716         frame = Lba >> BFs;
2717         firstLba = frame << BFs;
2718 //        firstPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba);
2719 //        lastPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba+PSs);
2720         block_array = Cache->FrameList[frame].Frame;
2721         if(!block_array) {
2722             BrutePoint();
2723             continue;;
2724         }
2725         // queue modify request
2726         WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
2727             Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE);
2728         // clear MODIFIED flag for queued blocks
2729         WCacheRemoveRangeFromList(List, &(Cache->WriteCount), Lba, PSs);
2730         Lba -= firstLba;
2731         for(i=0; i<PSs; i++) {
2732             WCacheClrModFlag(block_array, Lba+i);
2733         }
2734         chain_count++;
2735         // check queue size
2736         if(chain_count >= WCACHE_MAX_CHAIN) {
2737             WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
2738             chain_count = 0;
2739         }
2740     }
2741     WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
2742 #ifdef DBG
2743 #if 1
2744     // check consistency
2745     List = Cache->CachedBlocksList;
2746     for(i=0; i<Cache->BlockCount; i++) {
2747         Lba = List[i] /*& ~(PSs-1)*/;
2748         frame = Lba >> Cache->BlocksPerFrameSh;
2749         firstLba = frame << Cache->BlocksPerFrameSh;
2750         block_array = Cache->FrameList[frame].Frame;
2751         if(!block_array) {
2752             BrutePoint();
2753         }
2754         ASSERT(!WCacheGetModFlag(block_array, Lba-firstLba));
2755     }
2756 #endif // 1
2757 #endif // DBG
2758     return;
2759 } // end WCacheFlushAllRW()
2760 
2761 /*
2762   WCacheRelease__() frees all allocated memory blocks and
2763   deletes synchronization resources
2764   Public routine
2765  */
2766 VOID
2767 WCacheRelease__(
2768     IN PW_CACHE Cache         // pointer to the Cache Control structure
2769     )
2770 {
2771     ULONG i, j, k;
2772     PW_CACHE_ENTRY block_array;
2773 
2774     Cache->Tag = 0xDEADCACE;
2775     if(!(Cache->ReadProc)) return;
2776 //    ASSERT(Cache->Tag == 0xCAC11E00);
2777     ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2778     for(i=0; i<Cache->FrameCount; i++) {
2779         j = Cache->CachedFramesList[i];
2780         block_array = Cache->FrameList[j].Frame;
2781         if(block_array) {
2782             for(k=0; k<Cache->BlocksPerFrame; k++) {
2783                 if(WCacheSectorAddr(block_array, k)) {
2784                     WCacheFreeSector(j, k);
2785                 }
2786             }
2787             MyFreePool__(block_array);
2788         }
2789     }
2790     if(Cache->FrameList)
2791         MyFreePool__(Cache->FrameList);
2792     if(Cache->CachedBlocksList)
2793         MyFreePool__(Cache->CachedBlocksList);
2794     if(Cache->CachedModifiedBlocksList)
2795         MyFreePool__(Cache->CachedModifiedBlocksList);
2796     if(Cache->CachedFramesList)
2797         MyFreePool__(Cache->CachedFramesList);
2798     if(Cache->tmp_buff_r)
2799         MyFreePool__(Cache->tmp_buff_r);
2800     if(Cache->CachedFramesList)
2801         MyFreePool__(Cache->tmp_buff);
2802     if(Cache->CachedFramesList)
2803         MyFreePool__(Cache->reloc_tab);
2804     ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2805     ExDeleteResourceLite(&(Cache->WCacheLock));
2806     RtlZeroMemory(Cache, sizeof(W_CACHE));
2807     return;
2808 } // end WCacheRelease__()
2809 
2810 /*
2811   WCacheIsInitialized__() checks if the pointer supplied points
2812   to initialized cache structure.
2813   Public routine
2814  */
2815 BOOLEAN
2816 WCacheIsInitialized__(
2817     IN PW_CACHE Cache
2818     )
2819 {
2820     return (Cache->ReadProc != NULL);
2821 } // end WCacheIsInitialized__()
2822 
2823 OSSTATUS
2824 WCacheFlushBlocksRW(
2825     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2826     IN PVOID Context,         // user-supplied context for IO callbacks
2827     IN lba_t _Lba,             // LBA to start flush from
2828     IN ULONG BCount           // number of blocks to be flushed
2829     )
2830 {
2831     ULONG frame;
2832     lba_t firstLba;
2833     lba_t* List = Cache->CachedModifiedBlocksList;
2834     lba_t Lba;
2835 //    ULONG firstPos;
2836 //    ULONG lastPos;
2837     ULONG BSh = Cache->BlockSizeSh;
2838     ULONG BS = Cache->BlockSize;
2839     ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
2840     ULONG PSs = Cache->PacketSize;
2841     ULONG BFs = Cache->BlocksPerFrameSh;
2842     PW_CACHE_ENTRY block_array;
2843 //    OSSTATUS status;
2844     SIZE_T ReadBytes;
2845     PW_CACHE_ASYNC FirstWContext = NULL;
2846     PW_CACHE_ASYNC PrevWContext = NULL;
2847     ULONG i;
2848     ULONG chain_count = 0;
2849     lba_t lim;
2850 
2851     if(!(Cache->ReadProc)) return STATUS_INVALID_PARAMETER;
2852 
2853     // walk through modified blocks
2854     lim = (_Lba+BCount+PSs-1) & ~(PSs-1);
2855     for(Lba = _Lba & ~(PSs-1);Lba < lim ; Lba += PSs) {
2856         frame = Lba >> BFs;
2857         firstLba = frame << BFs;
2858 //        firstPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba);
2859 //        lastPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba+PSs);
2860         block_array = Cache->FrameList[frame].Frame;
2861         if(!block_array) {
2862             // not cached block may be requested for flush
2863             Lba += (1 << BFs) - PSs;
2864             continue;
2865         }
2866         // queue modify request
2867         WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
2868             Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE);
2869         // clear MODIFIED flag for queued blocks
2870         WCacheRemoveRangeFromList(List, &(Cache->WriteCount), Lba, PSs);
2871         Lba -= firstLba;
2872         for(i=0; i<PSs; i++) {
2873             WCacheClrModFlag(block_array, Lba+i);
2874         }
2875         Lba += firstLba;
2876         chain_count++;
2877         // check queue size
2878         if(chain_count >= WCACHE_MAX_CHAIN) {
2879             WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
2880             chain_count = 0;
2881         }
2882     }
2883     WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
2884 /*
2885     if(Cache->Mode != WCACHE_MODE_RAM)
2886         return STATUS_SUCCESS;
2887 */
2888 
2889     return STATUS_SUCCESS;
2890 } // end WCacheFlushBlocksRW()
2891 
2892 /*
2893   WCacheFlushBlocks__() copies specified blocks stored in cache to media.
2894   Flushed blocks are kept in cache.
2895   Public routine
2896  */
2897 OSSTATUS
2898 WCacheFlushBlocks__(
2899     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2900     IN PVOID Context,         // user-supplied context for IO callbacks
2901     IN lba_t Lba,             // LBA to start flush from
2902     IN ULONG BCount           // number of blocks to be flushed
2903     )
2904 {
2905     OSSTATUS status;
2906 
2907     if(!(Cache->ReadProc)) return STATUS_INVALID_PARAMETER;
2908     ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2909 
2910     // check if we try to access beyond cached area
2911     if((Lba < Cache->FirstLba) ||
2912        (Lba+BCount-1 > Cache->LastLba)) {
2913         UDFPrint(("LBA %#x (%x) is beyond cacheable area\n", Lba, BCount));
2914         BrutePoint();
2915         status = STATUS_INVALID_PARAMETER;
2916         goto EO_WCache_F;
2917     }
2918 
2919     switch(Cache->Mode) {
2920     case WCACHE_MODE_RAM:
2921 //        WCacheFlushBlocksRW(Cache, Context);
2922 //        break;
2923     case WCACHE_MODE_ROM:
2924     case WCACHE_MODE_RW:
2925         status = WCacheFlushBlocksRW(Cache, Context, Lba, BCount);
2926         break;
2927     case WCACHE_MODE_R:
2928         status = STATUS_SUCCESS;
2929         break;
2930     }
2931 EO_WCache_F:
2932     ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2933     return status;
2934 } // end WCacheFlushBlocks__()
2935 
2936 /*
2937   WCacheDirect__() returns pointer to memory block where
2938   requested block is stored in.
2939   If no #CachedOnly flag specified this routine locks cache,
2940   otherwise it assumes that cache is already locked by previous call
2941   to WCacheStartDirect__().
2942   Cache can be unlocked by WCacheEODirect__().
2943   Using this routine caller can access cached block directly in memory
2944   without Read_to_Tmp and Modify/Write steps.
2945   Public routine
2946  */
2947 OSSTATUS
2948 WCacheDirect__(
2949     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2950     IN PVOID Context,         // user-supplied context for IO callbacks
2951     IN lba_t Lba,             // LBA of block to get pointer to
2952     IN BOOLEAN Modified,      // indicates that block will be modified
2953     OUT PCHAR* CachedBlock,   // address for pointer to cached block to be stored in
2954     IN BOOLEAN CachedOnly     // specifies that cache is already locked
2955     )
2956 {
2957     ULONG frame;
2958     ULONG i;
2959     OSSTATUS status = STATUS_SUCCESS;
2960     PW_CACHE_ENTRY block_array;
2961     ULONG BS = Cache->BlockSize;
2962     PCHAR addr;
2963     SIZE_T _ReadBytes;
2964     ULONG block_type;
2965 
2966     WcPrint(("WC:%sD %x (1)\n", Modified ? "W" : "R", Lba));
2967 
2968     // lock cache if nececcary
2969     if(!CachedOnly) {
2970         ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2971     }
2972     // check if we try to access beyond cached area
2973     if((Lba < Cache->FirstLba) ||
2974        (Lba > Cache->LastLba)) {
2975         UDFPrint(("LBA %#x is beyond cacheable area\n", Lba));
2976         BrutePoint();
2977         status = STATUS_INVALID_PARAMETER;
2978         goto EO_WCache_D;
2979     }
2980 
2981     frame = Lba >> Cache->BlocksPerFrameSh;
2982     i = Lba - (frame << Cache->BlocksPerFrameSh);
2983     // check if we have enough space to store requested block
2984     if(!CachedOnly &&
2985        !OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba, 1))) {
2986         BrutePoint();
2987         goto EO_WCache_D;
2988     }
2989 
2990     // small updates are more important
2991     block_array = Cache->FrameList[frame].Frame;
2992     if(Modified) {
2993         Cache->FrameList[frame].UpdateCount+=8;
2994     } else {
2995         Cache->FrameList[frame].AccessCount+=8;
2996     }
2997     if(!block_array) {
2998         ASSERT(Cache->FrameCount < Cache->MaxFrames);
2999         block_array = WCacheInitFrame(Cache, Context, frame);
3000         if(!block_array) {
3001             status = STATUS_INSUFFICIENT_RESOURCES;
3002             goto EO_WCache_D;
3003         }
3004     }
3005     // check if requested block is already cached
3006     if( !(addr = (PCHAR)WCacheSectorAddr(block_array, i)) ) {
3007         // block is not cached
3008         // allocate memory and read block from media
3009         // do not set block_array[i].Sector here, because if media access fails and recursive access to cache
3010         // comes, this block should not be marked as 'cached'
3011         addr = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
3012         if(!addr) {
3013             status = STATUS_INSUFFICIENT_RESOURCES;
3014             goto EO_WCache_D;
3015         }
3016         block_type = Cache->CheckUsedProc(Context, Lba);
3017         if(block_type == WCACHE_BLOCK_USED) {
3018             status = Cache->ReadProc(Context, addr, BS, Lba, &_ReadBytes, PH_TMP_BUFFER);
3019             if(Cache->RememberBB) {
3020                 if(!OS_SUCCESS(status)) {
3021                     RtlZeroMemory(addr, BS);
3022                     //WCacheSetBadFlag(block_array,i);
3023                 }
3024             }
3025         } else {
3026             if(block_type & WCACHE_BLOCK_BAD) {
3027                 DbgFreePool(addr);
3028                 addr = NULL;
3029                 status = STATUS_DEVICE_DATA_ERROR;
3030                 goto EO_WCache_D;
3031             }
3032             if(!(block_type & WCACHE_BLOCK_ZERO)) {
3033                 BrutePoint();
3034             }
3035             status = STATUS_SUCCESS;
3036             RtlZeroMemory(addr, BS);
3037         }
3038         // now add pointer to buffer to common storage
3039         block_array[i].Sector = addr;
3040         WCacheInsertItemToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba);
3041         if(Modified) {
3042             WCacheInsertItemToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3043             WCacheSetModFlag(block_array, i);
3044         }
3045         Cache->FrameList[frame].BlockCount ++;
3046     } else {
3047         // block is not cached
3048         // just return pointer
3049         block_type = Cache->CheckUsedProc(Context, Lba);
3050         if(block_type & WCACHE_BLOCK_BAD) {
3051         //if(WCacheGetBadFlag(block_array,i)) {
3052             // bad packet. no pre-read
3053             status = STATUS_DEVICE_DATA_ERROR;
3054             goto EO_WCache_D;
3055         }
3056 #ifndef UDF_CHECK_UTIL
3057         ASSERT(block_type & WCACHE_BLOCK_USED);
3058 #else
3059         if(!(block_type & WCACHE_BLOCK_USED)) {
3060             UDFPrint(("LBA %#x is not marked as used\n", Lba));
3061         }
3062 #endif
3063         if(Modified &&
3064            !WCacheGetModFlag(block_array, i)) {
3065             WCacheInsertItemToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3066             WCacheSetModFlag(block_array, i);
3067         }
3068     }
3069     (*CachedBlock) = addr;
3070 
3071 EO_WCache_D:
3072 
3073     return status;
3074 } // end WCacheDirect__()
3075 
3076 /*
3077   WCacheEODirect__() must be used to unlock cache after calls to
3078   to WCacheStartDirect__().
3079   Public routine
3080  */
3081 OSSTATUS
3082 WCacheEODirect__(
3083     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3084     IN PVOID Context          // user-supplied context for IO callbacks
3085     )
3086 {
3087     ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
3088     return STATUS_SUCCESS;
3089 } // end WCacheEODirect__()
3090 
3091 /*
3092   WCacheStartDirect__() locks cache for exclusive use.
3093   Using this routine caller can access cached block directly in memory
3094   without Read_to_Tmp and Modify/Write steps.
3095   See also WCacheDirect__()
3096   Cache can be unlocked by WCacheEODirect__().
3097   Public routine
3098  */
3099 OSSTATUS
3100 WCacheStartDirect__(
3101     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3102     IN PVOID Context,         // user-supplied context for IO callbacks
3103     IN BOOLEAN Exclusive      // lock cache for exclusive use,
3104                               //   currently must be TRUE.
3105     )
3106 {
3107     if(Exclusive) {
3108         ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
3109     } else {
3110         BrutePoint();
3111         ExAcquireResourceSharedLite(&(Cache->WCacheLock), TRUE);
3112     }
3113     return STATUS_SUCCESS;
3114 } // end WCacheStartDirect__()
3115 
3116 /*
3117   WCacheIsCached__() checks if requested blocks are immediately available.
3118   Cache must be previously locked for exclusive use with WCacheStartDirect__().
3119   Using this routine caller can access cached block directly in memory
3120   without Read_to_Tmp and Modify/Write steps.
3121   See also WCacheDirect__().
3122   Cache can be unlocked by WCacheEODirect__().
3123   Public routine
3124  */
3125 BOOLEAN
3126 WCacheIsCached__(
3127     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3128     IN lba_t Lba,             // LBA to start check from
3129     IN ULONG BCount           // number of blocks to be checked
3130     )
3131 {
3132     ULONG frame;
3133     ULONG i;
3134     PW_CACHE_ENTRY block_array;
3135 
3136     // check if we try to access beyond cached area
3137     if((Lba < Cache->FirstLba) ||
3138        (Lba + BCount - 1 > Cache->LastLba)) {
3139         return FALSE;
3140     }
3141 
3142     frame = Lba >> Cache->BlocksPerFrameSh;
3143     i = Lba - (frame << Cache->BlocksPerFrameSh);
3144 
3145     block_array = Cache->FrameList[frame].Frame;
3146     if(!block_array) {
3147         return FALSE;
3148     }
3149 
3150     while(BCount) {
3151         if(i >= Cache->BlocksPerFrame) {
3152             frame++;
3153             block_array = Cache->FrameList[frame].Frame;
3154             i -= Cache->BlocksPerFrame;
3155         }
3156         if(!block_array) {
3157             return FALSE;
3158         }
3159         // 'read' cached extent (if any)
3160         while(BCount &&
3161               (i < Cache->BlocksPerFrame) &&
3162               WCacheSectorAddr(block_array, i) &&
3163               /*!WCacheGetBadFlag(block_array, i)*/
3164               /*!(Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_BAD)*/
3165               TRUE ) {
3166             i++;
3167             BCount--;
3168             Lba++;
3169         }
3170         if(BCount &&
3171               (i < Cache->BlocksPerFrame) /*&&
3172               (!WCacheSectorAddr(block_array, i))*/ ) {
3173             return FALSE;
3174         }
3175     }
3176     return TRUE;
3177 } // end WCacheIsCached__()
3178 
3179 /*
3180   WCacheCheckLimitsR() implements automatic flush and purge of
3181   unused blocks to keep enough free cache entries for newly
3182   read/written blocks for WORM media.
3183   See also WCacheCheckLimits()
3184   Internal routine
3185  */
3186 OSSTATUS
3187 __fastcall
3188 WCacheCheckLimitsR(
3189     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3190     IN PVOID Context,         // user-supplied context for IO callbacks
3191     IN lba_t ReqLba,          // first LBA to access/cache
3192     IN ULONG BCount           // number of Blocks to access/cache
3193     )
3194 {
3195     ULONG frame;
3196     lba_t firstLba;
3197     lba_t* List = Cache->CachedBlocksList;
3198     lba_t Lba;
3199     PCHAR tmp_buff = Cache->tmp_buff;
3200     ULONG firstPos;
3201     ULONG BSh = Cache->BlockSizeSh;
3202     ULONG BS = Cache->BlockSize;
3203     ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
3204     ULONG PSs = Cache->PacketSize;
3205     ULONG i;
3206     PW_CACHE_ENTRY block_array;
3207     BOOLEAN mod;
3208     OSSTATUS status;
3209     SIZE_T ReadBytes;
3210     ULONG MaxReloc = Cache->PacketSize;
3211     PULONG reloc_tab = Cache->reloc_tab;
3212 
3213     // check if we try to read too much data
3214     if(BCount > Cache->MaxBlocks) {
3215         return STATUS_INVALID_PARAMETER;
3216     }
3217 
3218     // remove(flush) packets from entire frame(s)
3219     while( ((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
3220              BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) ||
3221            (Cache->FrameCount >= Cache->MaxFrames) ) {
3222 
3223 WCCL_retry_1:
3224 
3225         Lba = WCacheFindLbaToRelease(Cache);
3226         if(Lba == WCACHE_INVALID_LBA) {
3227             ASSERT(!Cache->FrameCount);
3228             ASSERT(!Cache->BlockCount);
3229             break;
3230         }
3231         frame = Lba >> Cache->BlocksPerFrameSh;
3232         firstLba = frame << Cache->BlocksPerFrameSh;
3233         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba);
3234         block_array = Cache->FrameList[frame].Frame;
3235         if(!block_array) {
3236             return STATUS_DRIVER_INTERNAL_ERROR;
3237         }
3238         // check if modified
3239         mod = WCacheGetModFlag(block_array, Lba - firstLba);
3240         // read/modify/write
3241         if(mod && (Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED)) {
3242             if(Cache->WriteCount < MaxReloc) goto WCCL_retry_1;
3243             firstPos = WCacheGetSortedListIndex(Cache->WriteCount, Cache->CachedModifiedBlocksList, Lba);
3244             if(!block_array) {
3245                 return STATUS_DRIVER_INTERNAL_ERROR;
3246             }
3247             // prepare packet & reloc table
3248             for(i=0; i<MaxReloc; i++) {
3249                 Lba = Cache->CachedModifiedBlocksList[firstPos];
3250                 frame = Lba >> Cache->BlocksPerFrameSh;
3251                 firstLba = frame << Cache->BlocksPerFrameSh;
3252                 block_array = Cache->FrameList[frame].Frame;
3253                 DbgCopyMemory(tmp_buff + (i << BSh),
3254                               (PVOID)WCacheSectorAddr(block_array, Lba-firstLba),
3255                               BS);
3256                 reloc_tab[i] = Lba;
3257                 WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
3258                 WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3259                 // mark as non-cached & free pool
3260                 WCacheFreeSector(frame, Lba-firstLba);
3261                 // check if frame is empty
3262                 if(!Cache->FrameList[frame].BlockCount) {
3263                     WCacheRemoveFrame(Cache, Context, frame);
3264                 }
3265                 if(firstPos >= Cache->WriteCount) firstPos=0;
3266             }
3267             // write packet
3268 //            status = Cache->WriteProcAsync(Context, tmp_buff, PS, Lba, &ReadBytes, FALSE);
3269             Cache->UpdateRelocProc(Context, NULL, reloc_tab, MaxReloc);
3270             status = Cache->WriteProc(Context, tmp_buff, PS, NULL, &ReadBytes, 0);
3271             if(!OS_SUCCESS(status)) {
3272                 status = WCacheRaiseIoError(Cache, Context, status, NULL, PSs, tmp_buff, WCACHE_W_OP, NULL);
3273             }
3274         } else {
3275 
3276             if((i = Cache->BlockCount - Cache->WriteCount) > MaxReloc) i = MaxReloc;
3277             // discard blocks
3278             for(; i; i--) {
3279                 Lba = List[firstPos];
3280                 frame = Lba >> Cache->BlocksPerFrameSh;
3281                 firstLba = frame << Cache->BlocksPerFrameSh;
3282                 block_array = Cache->FrameList[frame].Frame;
3283 
3284                 if( (mod = WCacheGetModFlag(block_array, Lba - firstLba)) &&
3285                     (Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED) )
3286                     continue;
3287                 WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
3288                 if(mod)
3289                     WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3290                 // mark as non-cached & free pool
3291                 WCacheFreeSector(frame, Lba-firstLba);
3292                 // check if frame is empty
3293                 if(!Cache->FrameList[frame].BlockCount) {
3294                     WCacheRemoveFrame(Cache, Context, frame);
3295                 }
3296                 if(firstPos >= Cache->WriteCount) firstPos=0;
3297             }
3298         }
3299     }
3300     return STATUS_SUCCESS;
3301 } // end WCacheCheckLimitsR()
3302 
3303 /*
3304   WCachePurgeAllR() copies modified blocks from cache to media
3305   and removes them from cache
3306   This routine can be used for R media only.
3307   Internal routine
3308  */
3309 VOID
3310 __fastcall
3311 WCachePurgeAllR(
3312     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3313     IN PVOID Context)         // user-supplied context for IO callbacks
3314 {
3315     ULONG frame;
3316     lba_t firstLba;
3317     lba_t* List = Cache->CachedBlocksList;
3318     lba_t Lba;
3319     PCHAR tmp_buff = Cache->tmp_buff;
3320     ULONG BSh = Cache->BlockSizeSh;
3321     ULONG BS = Cache->BlockSize;
3322 //    ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
3323 //    ULONG PSs = Cache->PacketSize;
3324     PW_CACHE_ENTRY block_array;
3325     BOOLEAN mod;
3326     OSSTATUS status;
3327     SIZE_T ReadBytes;
3328     ULONG MaxReloc = Cache->PacketSize;
3329     PULONG reloc_tab = Cache->reloc_tab;
3330     ULONG RelocCount = 0;
3331     BOOLEAN IncompletePacket;
3332     ULONG i=0;
3333     ULONG PacketTail;
3334 
3335     while(Cache->WriteCount < Cache->BlockCount) {
3336 
3337         Lba = List[i];
3338         frame = Lba >> Cache->BlocksPerFrameSh;
3339         firstLba = frame << Cache->BlocksPerFrameSh;
3340         block_array = Cache->FrameList[frame].Frame;
3341         if(!block_array) {
3342             BrutePoint();
3343             return;
3344         }
3345         // check if modified
3346         mod = WCacheGetModFlag(block_array, Lba - firstLba);
3347         // just discard
3348         if(!mod || !(Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED)) {
3349             // mark as non-cached & free pool
3350             if(WCacheSectorAddr(block_array,Lba-firstLba)) {
3351                 WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
3352                 if(mod)
3353                     WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3354                 // mark as non-cached & free pool
3355                 WCacheFreeSector(frame, Lba-firstLba);
3356                 // check if frame is empty
3357                 if(!Cache->FrameList[frame].BlockCount) {
3358                     WCacheRemoveFrame(Cache, Context, frame);
3359                 }
3360             } else {
3361                 BrutePoint();
3362             }
3363         } else {
3364             i++;
3365         }
3366     }
3367 
3368     PacketTail = Cache->WriteCount & (MaxReloc-1);
3369     IncompletePacket = (Cache->WriteCount >= MaxReloc) ? FALSE : TRUE;
3370 
3371     // remove(flush) packet
3372     while((Cache->WriteCount > PacketTail) || (Cache->WriteCount && IncompletePacket)) {
3373 
3374         Lba = List[0];
3375         frame = Lba >> Cache->BlocksPerFrameSh;
3376         firstLba = frame << Cache->BlocksPerFrameSh;
3377         block_array = Cache->FrameList[frame].Frame;
3378         if(!block_array) {
3379             BrutePoint();
3380             return;
3381         }
3382         // check if modified
3383         mod = WCacheGetModFlag(block_array, Lba - firstLba);
3384         // pack/reloc/write
3385         if(mod) {
3386             DbgCopyMemory(tmp_buff + (RelocCount << BSh),
3387                           (PVOID)WCacheSectorAddr(block_array, Lba-firstLba),
3388                           BS);
3389             reloc_tab[RelocCount] = Lba;
3390             RelocCount++;
3391             // write packet
3392             if((RelocCount >= MaxReloc) || (Cache->BlockCount == 1)) {
3393 //                status = Cache->WriteProcAsync(Context, tmp_buff, PS, Lba, &ReadBytes, FALSE);
3394                 Cache->UpdateRelocProc(Context, NULL, reloc_tab, RelocCount);
3395                 status = Cache->WriteProc(Context, tmp_buff, RelocCount<<BSh, NULL, &ReadBytes, 0);
3396                 if(!OS_SUCCESS(status)) {
3397                     status = WCacheRaiseIoError(Cache, Context, status, NULL, RelocCount, tmp_buff, WCACHE_W_OP, NULL);
3398                 }
3399                 RelocCount = 0;
3400             }
3401             WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3402         } else {
3403             BrutePoint();
3404         }
3405         // mark as non-cached & free pool
3406         if(WCacheSectorAddr(block_array,Lba-firstLba)) {
3407             WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
3408             // mark as non-cached & free pool
3409             WCacheFreeSector(frame, Lba-firstLba);
3410             // check if frame is empty
3411             if(!Cache->FrameList[frame].BlockCount) {
3412                 WCacheRemoveFrame(Cache, Context, frame);
3413             }
3414         } else {
3415             BrutePoint();
3416         }
3417     }
3418 } // end WCachePurgeAllR()
3419 
3420 /*
3421   WCacheSetMode__() changes cache operating mode (ROM/R/RW/RAM).
3422   Public routine
3423  */
3424 OSSTATUS
3425 WCacheSetMode__(
3426     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3427     IN ULONG Mode             // cache mode/media type to be used
3428     )
3429 {
3430     if(Mode > WCACHE_MODE_MAX) return STATUS_INVALID_PARAMETER;
3431     Cache->Mode = Mode;
3432     return STATUS_SUCCESS;
3433 } // end WCacheSetMode__()
3434 
3435 /*
3436   WCacheGetMode__() returns cache operating mode (ROM/R/RW/RAM).
3437   Public routine
3438  */
3439 ULONG
3440 WCacheGetMode__(
3441     IN PW_CACHE Cache
3442     )
3443 {
3444     return Cache->Mode;
3445 } // end WCacheGetMode__()
3446 
3447 /*
3448   WCacheGetWriteBlockCount__() returns number of modified blocks, those are
3449   not flushed to media. Is usually used to preallocate blocks for
3450   relocation table on WORM (R) media.
3451   Public routine
3452  */
3453 ULONG
3454 WCacheGetWriteBlockCount__(
3455     IN PW_CACHE Cache
3456     )
3457 {
3458     return Cache->WriteCount;
3459 } // end WCacheGetWriteBlockCount__()
3460 
3461 /*
3462   WCacheSyncReloc__() builds list of all modified blocks, currently
3463   stored in cache. For each modified block WCacheSyncReloc__() calls
3464   user-supplied callback routine in order to update relocation table
3465   on WORM (R) media.
3466   Public routine
3467  */
3468 VOID
3469 WCacheSyncReloc__(
3470     IN PW_CACHE Cache,
3471     IN PVOID Context)
3472 {
3473     ULONG frame;
3474     lba_t firstLba;
3475     lba_t* List = Cache->CachedBlocksList;
3476     lba_t Lba;
3477 //    ULONG BSh = Cache->BlockSizeSh;
3478 //    ULONG BS = Cache->BlockSize;
3479 //    ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
3480 //    ULONG PSs = Cache->PacketSize;
3481     PW_CACHE_ENTRY block_array;
3482     BOOLEAN mod;
3483     ULONG MaxReloc = Cache->PacketSize;
3484     PULONG reloc_tab = Cache->reloc_tab;
3485     ULONG RelocCount = 0;
3486     BOOLEAN IncompletePacket;
3487 
3488     IncompletePacket = (Cache->WriteCount >= MaxReloc) ? FALSE : TRUE;
3489     // enumerate modified blocks
3490     for(ULONG i=0; IncompletePacket && (i<Cache->BlockCount); i++) {
3491 
3492         Lba = List[i];
3493         frame = Lba >> Cache->BlocksPerFrameSh;
3494         firstLba = frame << Cache->BlocksPerFrameSh;
3495         block_array = Cache->FrameList[frame].Frame;
3496         if(!block_array) {
3497             return;
3498         }
3499         // check if modified
3500         mod = WCacheGetModFlag(block_array, Lba - firstLba);
3501         // update relocation table for modified sectors
3502         if(mod && (Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED)) {
3503             reloc_tab[RelocCount] = Lba;
3504             RelocCount++;
3505             if(RelocCount >= Cache->WriteCount) {
3506                 Cache->UpdateRelocProc(Context, NULL, reloc_tab, RelocCount);
3507                 break;
3508             }
3509         }
3510     }
3511 } // end WCacheSyncReloc__()
3512 
3513 /*
3514   WCacheDiscardBlocks__() removes specified blocks from cache.
3515   Blocks are not flushed to media.
3516   Public routine
3517  */
3518 VOID
3519 WCacheDiscardBlocks__(
3520     IN PW_CACHE Cache,
3521     IN PVOID Context,
3522     IN lba_t ReqLba,
3523     IN ULONG BCount
3524     )
3525 {
3526     ULONG frame;
3527     lba_t firstLba;
3528     lba_t* List;
3529     lba_t Lba;
3530     PW_CACHE_ENTRY block_array;
3531     BOOLEAN mod;
3532     ULONG i;
3533 
3534     ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
3535 
3536     UDFPrint(("  Discard req: %x@%x\n",BCount, ReqLba));
3537 
3538     List = Cache->CachedBlocksList;
3539     if(!List) {
3540         ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
3541         return;
3542     }
3543     i = WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba);
3544 
3545     // enumerate requested blocks
3546     while((List[i] < (ReqLba+BCount)) && (i < Cache->BlockCount)) {
3547 
3548         Lba = List[i];
3549         frame = Lba >> Cache->BlocksPerFrameSh;
3550         firstLba = frame << Cache->BlocksPerFrameSh;
3551         block_array = Cache->FrameList[frame].Frame;
3552         if(!block_array) {
3553             ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
3554             BrutePoint();
3555             return;
3556         }
3557         // check if modified
3558         mod = WCacheGetModFlag(block_array, Lba - firstLba);
3559         // just discard
3560 
3561         // mark as non-cached & free pool
3562         if(WCacheSectorAddr(block_array,Lba-firstLba)) {
3563             WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
3564             if(mod)
3565                 WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3566             // mark as non-cached & free pool
3567             WCacheFreeSector(frame, Lba-firstLba);
3568             // check if frame is empty
3569             if(!Cache->FrameList[frame].BlockCount) {
3570                 WCacheRemoveFrame(Cache, Context, frame);
3571             } else {
3572                 ASSERT(Cache->FrameList[frame].Frame);
3573             }
3574         } else {
3575             // we should never get here !!!
3576             // getting this part of code means that we have
3577             // placed non-cached block in CachedBlocksList
3578             BrutePoint();
3579         }
3580     }
3581     ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
3582 } // end WCacheDiscardBlocks__()
3583 
3584 OSSTATUS
3585 WCacheCompleteAsync__(
3586     IN PVOID WContext,
3587     IN OSSTATUS Status
3588     )
3589 {
3590     PW_CACHE_ASYNC AsyncCtx = (PW_CACHE_ASYNC)WContext;
3591 //    PW_CACHE Cache = AsyncCtx->Cache;
3592 
3593     AsyncCtx->PhContext.IosbToUse.Status = Status;
3594     KeSetEvent(&(AsyncCtx->PhContext.event), 0, FALSE);
3595 
3596     return STATUS_SUCCESS;
3597 } // end WCacheSetMode__()
3598 
3599 /*
3600   WCacheDecodeFlags() updates internal BOOLEANs according to Flags
3601   Internal routine
3602  */
3603 OSSTATUS
3604 __fastcall
3605 WCacheDecodeFlags(
3606     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3607     IN ULONG Flags            // cache mode flags
3608     )
3609 {
3610     //ULONG OldFlags;
3611     if(Flags & ~WCACHE_VALID_FLAGS) {
3612         UDFPrint(("Invalid flags: %x\n", Flags & ~WCACHE_VALID_FLAGS));
3613         return STATUS_INVALID_PARAMETER;
3614     }
3615     Cache->CacheWholePacket = (Flags & WCACHE_CACHE_WHOLE_PACKET) ? TRUE : FALSE;
3616     Cache->DoNotCompare = (Flags & WCACHE_DO_NOT_COMPARE) ? TRUE : FALSE;
3617     Cache->Chained = (Flags & WCACHE_CHAINED_IO) ? TRUE : FALSE;
3618     Cache->RememberBB = (Flags & WCACHE_MARK_BAD_BLOCKS) ? TRUE : FALSE;
3619     if(Cache->RememberBB) {
3620         Cache->NoWriteBB = (Flags & WCACHE_RO_BAD_BLOCKS) ? TRUE : FALSE;
3621     }
3622     Cache->NoWriteThrough = (Flags & WCACHE_NO_WRITE_THROUGH) ? TRUE : FALSE;
3623 
3624     Cache->Flags = Flags;
3625 
3626     return STATUS_SUCCESS;
3627 }
3628 
3629 /*
3630   WCacheChFlags__() changes cache flags.
3631   Public routine
3632  */
3633 ULONG
3634 WCacheChFlags__(
3635     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3636     IN ULONG SetFlags,        // cache mode/media type to be set
3637     IN ULONG ClrFlags         // cache mode/media type to be cleared
3638     )
3639 {
3640     ULONG Flags;
3641 
3642     if(SetFlags || ClrFlags) {
3643         Flags = (Cache->Flags & ~ClrFlags) | SetFlags;
3644 
3645         if(!OS_SUCCESS(WCacheDecodeFlags(Cache, Flags))) {
3646             return -1;
3647         }
3648     } else {
3649         return Cache->Flags;
3650     }
3651     return Flags;
3652 } // end WCacheSetMode__()
3653