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
WCacheInit__(IN PW_CACHE Cache,IN ULONG MaxFrames,IN ULONG MaxBlocks,IN SIZE_T MaxBytesToRead,IN ULONG PacketSizeSh,IN ULONG BlockSizeSh,IN ULONG BlocksPerFrameSh,IN lba_t FirstLba,IN lba_t LastLba,IN ULONG Mode,IN ULONG Flags,IN ULONG FramesToKeepFree,IN PWRITE_BLOCK WriteProc,IN PREAD_BLOCK ReadProc,IN PWRITE_BLOCK_ASYNC WriteProcAsync,IN PREAD_BLOCK_ASYNC ReadProcAsync,IN PCHECK_BLOCK CheckUsedProc,IN PUPDATE_RELOC UpdateRelocProc,IN PWC_ERROR_HANDLER ErrorHandlerProc)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
WCacheRandom(VOID)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
WCacheFindLbaToRelease(IN PW_CACHE Cache)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
WCacheFindModifiedLbaToRelease(IN PW_CACHE Cache)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
WCacheFindFrameToRelease(IN PW_CACHE Cache)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
WCacheGetSortedListIndex(IN ULONG BlockCount,IN lba_t * List,IN lba_t Lba)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
WCacheInsertRangeToList(IN lba_t * List,IN PULONG BlockCount,IN lba_t Lba,IN ULONG BCount)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
WCacheInsertItemToList(IN lba_t * List,IN PULONG BlockCount,IN lba_t Lba)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
WCacheRemoveRangeFromList(IN lba_t * List,IN PULONG BlockCount,IN lba_t Lba,IN ULONG BCount)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
WCacheRemoveItemFromList(IN lba_t * List,IN PULONG BlockCount,IN lba_t Lba)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
WCacheInitFrame(IN PW_CACHE Cache,IN PVOID Context,IN ULONG frame)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
WCacheRemoveFrame(IN PW_CACHE Cache,IN PVOID Context,IN ULONG frame)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
WCacheAllocAsyncEntry(IN PW_CACHE Cache,IN OUT PW_CACHE_ASYNC * FirstWContext,IN OUT PW_CACHE_ASYNC * PrevWContext,IN ULONG BufferSize)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
WCacheFreeAsyncEntry(IN PW_CACHE Cache,PW_CACHE_ASYNC WContext)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
WCacheRaiseIoError(IN PW_CACHE Cache,IN PVOID Context,IN OSSTATUS Status,IN ULONG Lba,IN ULONG BCount,IN PVOID Buffer,IN BOOLEAN ReadOp,IN PBOOLEAN Retry)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
WCacheUpdatePacket(IN PW_CACHE Cache,IN PVOID Context,IN OUT PW_CACHE_ASYNC * FirstWContext,IN OUT PW_CACHE_ASYNC * PrevWContext,IN PW_CACHE_ENTRY block_array,IN lba_t firstLba,IN lba_t Lba,IN ULONG BSh,IN ULONG BS,IN ULONG PS,IN ULONG PSs,IN PSIZE_T ReadBytes,IN BOOLEAN PrefereWrite,IN ULONG State)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 
1139     if(Chained &&
1140        WContext->State == ASYNC_STATE_WRITE_PRE) {
1141         // Return if block is prepared for write and we are in chained mode.
1142         if(!mod) {
1143             // Mark block as written if we have found that data in it
1144             // is not actually modified.
1145             WContext->State = ASYNC_STATE_DONE;
1146             (*ReadBytes) = PS;
1147         }
1148         return STATUS_SUCCESS;
1149     }
1150 
1151     // write packet
1152 
1153     // If the check above reported some changes in packet
1154     // we should write packet out to media.
1155     // Otherwise, just complete request.
1156     if(mod) {
1157 try_write:
1158         if(Async) {
1159             WContext->State = ASYNC_STATE_WRITE;
1160             status = Cache->WriteProcAsync(Context, WContext, tmp_buff2, PS, Lba,
1161                                            &(WContext->TransferredBytes), FALSE);
1162             (*ReadBytes) = PS;
1163         } else {
1164             status = Cache->WriteProc(Context, tmp_buff2, PS, Lba, ReadBytes, 0);
1165             if(!OS_SUCCESS(status)) {
1166                 status = WCacheRaiseIoError(Cache, Context, status, Lba, PSs, tmp_buff2, WCACHE_W_OP, NULL);
1167             }
1168         }
1169     } else {
1170         if(Async)
1171             WCacheCompleteAsync__(WContext, STATUS_SUCCESS);
1172         (*ReadBytes) = PS;
1173         return STATUS_SUCCESS;
1174     }
1175 
1176     return status;
1177 } // end WCacheUpdatePacket()
1178 
1179 /*
1180   WCacheFreePacket() releases storage for all Blocks in packet.
1181   'frame' describes Frame, offset - Block in Frame. offset should be
1182   aligned on Packet size.
1183   Internal routine
1184  */
1185 VOID
WCacheFreePacket(IN PW_CACHE Cache,IN ULONG frame,IN PW_CACHE_ENTRY block_array,IN ULONG offs,IN ULONG PSs)1186 WCacheFreePacket(
1187     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1188 //    IN PVOID Context,
1189     IN ULONG frame,           // Frame index
1190     IN PW_CACHE_ENTRY block_array, // Frame
1191     IN ULONG offs,            // offset in Frame
1192     IN ULONG PSs              // Packet size (in Blocks)
1193     )
1194 {
1195     ULONG i;
1196     // mark as non-cached & free pool
1197     for(i=0; i<PSs; i++, offs++) {
1198         if(WCacheSectorAddr(block_array,offs)) {
1199             WCacheFreeSector(frame, offs);
1200         }
1201     }
1202 } // end WCacheFreePacket()
1203 
1204 /*
1205   WCacheUpdatePacketComplete() is called to continue processing of packet
1206   being updated.
1207   In async mode it waits for completion of pre-read requests,
1208   initiates writes, waits for their completion and returns control to
1209   caller.
1210   Internal routine
1211  */
1212 VOID
WCacheUpdatePacketComplete(IN PW_CACHE Cache,IN PVOID Context,IN OUT PW_CACHE_ASYNC * FirstWContext,IN OUT PW_CACHE_ASYNC * PrevWContext,IN BOOLEAN FreePacket)1213 WCacheUpdatePacketComplete(
1214     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1215     IN PVOID Context,         // user-supplied context for IO callbacks
1216     IN OUT PW_CACHE_ASYNC* FirstWContext, // pointer to head async IO context
1217     IN OUT PW_CACHE_ASYNC* PrevWContext,  // pointer to tail async IO context
1218     IN BOOLEAN FreePacket
1219     )
1220 {
1221     PW_CACHE_ASYNC WContext = (*FirstWContext);
1222     if(!WContext)
1223         return;
1224     PW_CACHE_ASYNC NextWContext;
1225     ULONG PS = Cache->BlockSize << Cache->PacketSizeSh; // packet size (bytes)
1226     ULONG PSs = Cache->PacketSize;
1227     ULONG frame;
1228     lba_t firstLba;
1229 
1230     // Walk through all chained blocks and wait
1231     // for completion of read operations.
1232     // Also invoke writes of already prepared packets.
1233     while(WContext) {
1234         if(WContext->Cmd == ASYNC_CMD_UPDATE &&
1235            WContext->State == ASYNC_STATE_READ) {
1236             // wait for async read for update
1237             DbgWaitForSingleObject(&(WContext->PhContext.event), NULL);
1238 
1239             WContext->State = ASYNC_STATE_WRITE;
1240             WCacheUpdatePacket(Cache, Context, NULL, &WContext, NULL, -1, WContext->Lba, -1, -1,
1241                                PS, -1, &(WContext->TransferredBytes), TRUE, ASYNC_STATE_WRITE);
1242         } else
1243         if(WContext->Cmd == ASYNC_CMD_UPDATE &&
1244            WContext->State == ASYNC_STATE_WRITE_PRE) {
1245             // invoke physical write it the packet is prepared for writing
1246             // by previuous call to WCacheUpdatePacket()
1247             WContext->State = ASYNC_STATE_WRITE;
1248             WCacheUpdatePacket(Cache, Context, NULL, &WContext, NULL, -1, WContext->Lba, -1, -1,
1249                                PS, -1, &(WContext->TransferredBytes), TRUE, ASYNC_STATE_WRITE);
1250             WContext->State = ASYNC_STATE_DONE;
1251         } else
1252         if(WContext->Cmd == ASYNC_CMD_READ &&
1253            WContext->State == ASYNC_STATE_READ) {
1254             // wait for async read
1255             DbgWaitForSingleObject(&(WContext->PhContext.event), NULL);
1256         }
1257         WContext = WContext->NextWContext;
1258     }
1259     // Walk through all chained blocks and wait
1260     // and wait for completion of async writes (if any).
1261     // Also free temporary buffers containing already written blocks.
1262     WContext = (*FirstWContext);
1263     while(WContext) {
1264         NextWContext = WContext->NextWContext;
1265         if(WContext->Cmd == ASYNC_CMD_UPDATE &&
1266            WContext->State == ASYNC_STATE_WRITE) {
1267 
1268             if(!Cache->Chained)
1269                 DbgWaitForSingleObject(&(WContext->PhContext.event), NULL);
1270 
1271             frame = WContext->Lba >> Cache->BlocksPerFrameSh;
1272             firstLba = frame << Cache->BlocksPerFrameSh;
1273 
1274             if(FreePacket) {
1275                 WCacheFreePacket(Cache, frame,
1276                                 Cache->FrameList[frame].Frame,
1277                                 WContext->Lba - firstLba, PSs);
1278             }
1279         }
1280         WCacheFreeAsyncEntry(Cache, WContext);
1281         WContext = NextWContext;
1282     }
1283     (*FirstWContext) = NULL;
1284     (*PrevWContext) = NULL;
1285 } // end WCacheUpdatePacketComplete()
1286 
1287 /*
1288   WCacheCheckLimits() checks if we've enough free Frame- &
1289   Block-entries under Frame- and Block-limit to feet
1290   requested Blocks.
1291   If there is not enough entries, WCache initiates flush & purge
1292   process to satisfy request.
1293   This is dispatch routine, which calls
1294   WCacheCheckLimitsR() or WCacheCheckLimitsRW() depending on
1295   media type.
1296   Internal routine
1297  */
1298 OSSTATUS
1299 __fastcall
WCacheCheckLimits(IN PW_CACHE Cache,IN PVOID Context,IN lba_t ReqLba,IN ULONG BCount)1300 WCacheCheckLimits(
1301     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1302     IN PVOID Context,         // user-supplied context for IO callbacks
1303     IN lba_t ReqLba,          // first LBA to access/cache
1304     IN ULONG BCount           // number of Blocks to access/cache
1305     )
1306 {
1307 /*    if(!Cache->FrameCount || !Cache->BlockCount) {
1308         ASSERT(!Cache->FrameCount);
1309         ASSERT(!Cache->BlockCount);
1310         if(!Cache->FrameCount)
1311             return STATUS_SUCCESS;
1312     }*/
1313 
1314     // check if we have reached Frame or Block limit
1315     if(!Cache->FrameCount && !Cache->BlockCount) {
1316         return STATUS_SUCCESS;
1317     }
1318 
1319     // check for empty frames
1320     if(Cache->FrameCount > (Cache->MaxFrames*3)/4) {
1321         ULONG frame;
1322         ULONG i;
1323         for(i=Cache->FrameCount; i>0; i--) {
1324             frame = Cache->CachedFramesList[i-1];
1325             // check if frame is empty
1326             if(!(Cache->FrameList[frame].BlockCount)) {
1327                 WCacheRemoveFrame(Cache, Context, frame);
1328             } else {
1329                 ASSERT(Cache->FrameList[frame].Frame);
1330             }
1331         }
1332     }
1333 
1334     if(!Cache->BlockCount) {
1335         return STATUS_SUCCESS;
1336     }
1337 
1338     // invoke media-specific limit-checker
1339     switch(Cache->Mode) {
1340     case WCACHE_MODE_RAM:
1341         return WCacheCheckLimitsRAM(Cache, Context, ReqLba, BCount);
1342     case WCACHE_MODE_ROM:
1343     case WCACHE_MODE_RW:
1344         return WCacheCheckLimitsRW(Cache, Context, ReqLba, BCount);
1345     case WCACHE_MODE_R:
1346         return WCacheCheckLimitsR(Cache, Context, ReqLba, BCount);
1347     }
1348     return STATUS_DRIVER_INTERNAL_ERROR;
1349 } // end WCacheCheckLimits()
1350 
1351 /*
1352   WCacheCheckLimitsRW() implements automatic flush and purge of
1353   unused blocks to keep enough free cache entries for newly
1354   read/written blocks for Random Access and ReWritable media
1355   using Read/Modify/Write technology.
1356   See also WCacheCheckLimits()
1357   Internal routine
1358  */
1359 OSSTATUS
1360 __fastcall
WCacheCheckLimitsRW(IN PW_CACHE Cache,IN PVOID Context,IN lba_t ReqLba,IN ULONG BCount)1361 WCacheCheckLimitsRW(
1362     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1363     IN PVOID Context,         // user-supplied context for IO callbacks
1364     IN lba_t ReqLba,          // first LBA to access/cache
1365     IN ULONG BCount           // number of Blocks to access/cache
1366     )
1367 {
1368     ULONG frame;
1369     lba_t firstLba;
1370     lba_t* List = Cache->CachedBlocksList;
1371     lba_t lastLba;
1372     lba_t Lba;
1373 //    PCHAR tmp_buff = Cache->tmp_buff;
1374     ULONG firstPos;
1375     ULONG lastPos;
1376     ULONG BSh = Cache->BlockSizeSh;
1377     ULONG BS = Cache->BlockSize;
1378     ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
1379     ULONG PSs = Cache->PacketSize;
1380     ULONG try_count = 0;
1381     PW_CACHE_ENTRY block_array;
1382     OSSTATUS status;
1383     SIZE_T ReadBytes;
1384     ULONG FreeFrameCount = 0;
1385 //    PVOID addr;
1386     PW_CACHE_ASYNC FirstWContext = NULL;
1387     PW_CACHE_ASYNC PrevWContext = NULL;
1388     ULONG chain_count = 0;
1389 
1390     if(Cache->FrameCount >= Cache->MaxFrames) {
1391         FreeFrameCount = Cache->FramesToKeepFree;
1392     } else
1393     if((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
1394            BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) {
1395         // we need free space to grow WCache without flushing data
1396         // for some period of time
1397         FreeFrameCount = Cache->FramesToKeepFree;
1398         goto Try_Another_Frame;
1399     }
1400     // remove(flush) some frames
1401     while((Cache->FrameCount >= Cache->MaxFrames) || FreeFrameCount) {
1402 Try_Another_Frame:
1403         if(!Cache->FrameCount || !Cache->BlockCount) {
1404             //ASSERT(!Cache->FrameCount);
1405             if(Cache->FrameCount) {
1406                 UDFPrint(("ASSERT: Cache->FrameCount = %d, when 0 is expected\n", Cache->FrameCount));
1407             }
1408             ASSERT(!Cache->BlockCount);
1409             if(!Cache->FrameCount)
1410                 break;
1411         }
1412 
1413         frame = WCacheFindFrameToRelease(Cache);
1414 #if 0
1415         if(Cache->FrameList[frame].WriteCount) {
1416             try_count++;
1417             if(try_count < MAX_TRIES_FOR_NA) goto Try_Another_Frame;
1418         } else {
1419             try_count = 0;
1420         }
1421 #else
1422         if(Cache->FrameList[frame].UpdateCount) {
1423             try_count = MAX_TRIES_FOR_NA;
1424         } else {
1425             try_count = 0;
1426         }
1427 #endif
1428 
1429         if(FreeFrameCount)
1430             FreeFrameCount--;
1431 
1432         firstLba = frame << Cache->BlocksPerFrameSh;
1433         lastLba = firstLba + Cache->BlocksPerFrame;
1434         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba);
1435         lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba);
1436         block_array = Cache->FrameList[frame].Frame;
1437 
1438         if(!block_array) {
1439             UDFPrint(("Hmm...\n"));
1440             BrutePoint();
1441             return STATUS_DRIVER_INTERNAL_ERROR;
1442         }
1443 
1444         while(firstPos < lastPos) {
1445             // flush packet
1446             Lba = List[firstPos] & ~(PSs-1);
1447 
1448             // write packet out or prepare and add to chain (if chained mode enabled)
1449             status = WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
1450                 Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE);
1451 
1452             if(status != STATUS_PENDING) {
1453                 // free memory
1454                 WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, PSs);
1455             }
1456 
1457             Lba += PSs;
1458             while((firstPos < lastPos) && (Lba > List[firstPos])) {
1459                 firstPos++;
1460             }
1461             chain_count++;
1462             // write chained packets
1463             if(chain_count >= WCACHE_MAX_CHAIN) {
1464                 WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
1465                 chain_count = 0;
1466             }
1467         }
1468         // remove flushed blocks from all lists
1469         WCacheRemoveRangeFromList(List, &(Cache->BlockCount), firstLba, Cache->BlocksPerFrame);
1470         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame);
1471 
1472         WCacheRemoveFrame(Cache, Context, frame);
1473     }
1474 
1475     // check if we try to read too much data
1476     if(BCount > Cache->MaxBlocks) {
1477         WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext);
1478         return STATUS_INVALID_PARAMETER;
1479     }
1480 
1481     // remove(flush) packet
1482     while((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
1483            BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) {
1484         try_count = 0;
1485 Try_Another_Block:
1486 
1487         Lba = WCacheFindLbaToRelease(Cache) & ~(PSs-1);
1488         if(Lba == WCACHE_INVALID_LBA) {
1489             ASSERT(!Cache->FrameCount);
1490             ASSERT(!Cache->BlockCount);
1491             break;
1492         }
1493         frame = Lba >> Cache->BlocksPerFrameSh;
1494         firstLba = frame << Cache->BlocksPerFrameSh;
1495         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba);
1496         lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba+PSs);
1497         block_array = Cache->FrameList[frame].Frame;
1498         if(!block_array) {
1499             // write already prepared blocks to disk and return error
1500             WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext);
1501             ASSERT(FALSE);
1502             return STATUS_DRIVER_INTERNAL_ERROR;
1503         }
1504 
1505         // write packet out or prepare and add to chain (if chained mode enabled)
1506         status = WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
1507             Lba, BSh, BS, PS, PSs, &ReadBytes, (try_count >= MAX_TRIES_FOR_NA), ASYNC_STATE_NONE);
1508 
1509         if(status == STATUS_RETRY) {
1510             try_count++;
1511             goto Try_Another_Block;
1512         }
1513 
1514         // free memory
1515         WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, PSs);
1516 
1517         WCacheRemoveRangeFromList(List, &(Cache->BlockCount), Lba, PSs);
1518         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, PSs);
1519         // check if frame is empty
1520         if(!(Cache->FrameList[frame].BlockCount)) {
1521             WCacheRemoveFrame(Cache, Context, frame);
1522         } else {
1523             ASSERT(Cache->FrameList[frame].Frame);
1524         }
1525         chain_count++;
1526         if(chain_count >= WCACHE_MAX_CHAIN) {
1527             WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
1528             chain_count = 0;
1529         }
1530     }
1531     WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext);
1532     return STATUS_SUCCESS;
1533 } // end WCacheCheckLimitsRW()
1534 
1535 OSSTATUS
1536 __fastcall
WCacheFlushBlocksRAM(IN PW_CACHE Cache,IN PVOID Context,PW_CACHE_ENTRY block_array,lba_t * List,ULONG firstPos,ULONG lastPos,BOOLEAN Purge)1537 WCacheFlushBlocksRAM(
1538     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1539     IN PVOID Context,         // user-supplied context for IO callbacks
1540     PW_CACHE_ENTRY block_array,
1541     lba_t* List,
1542     ULONG firstPos,
1543     ULONG lastPos,
1544     BOOLEAN Purge
1545     )
1546 {
1547     ULONG frame;
1548     lba_t Lba;
1549     lba_t PrevLba;
1550     lba_t firstLba;
1551     PCHAR tmp_buff = NULL;
1552     ULONG n;
1553     ULONG BSh = Cache->BlockSizeSh;
1554     ULONG BS = Cache->BlockSize;
1555 //    ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
1556     ULONG PSs = Cache->PacketSize;
1557     SIZE_T _WrittenBytes;
1558     OSSTATUS status = STATUS_SUCCESS;
1559 
1560     frame = List[firstPos] >> Cache->BlocksPerFrameSh;
1561     firstLba = frame << Cache->BlocksPerFrameSh;
1562 
1563     while(firstPos < lastPos) {
1564         // flush blocks
1565         ASSERT(Cache->FrameCount <= Cache->MaxFrames);
1566         Lba = List[firstPos];
1567         if(!WCacheGetModFlag(block_array, Lba - firstLba)) {
1568             // free memory
1569             if(Purge) {
1570                 WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, 1);
1571             }
1572             firstPos++;
1573             continue;
1574         }
1575         tmp_buff = Cache->tmp_buff;
1576         PrevLba = Lba;
1577         n=1;
1578         while((firstPos+n < lastPos) &&
1579               (List[firstPos+n] == PrevLba+1)) {
1580             PrevLba++;
1581             if(!WCacheGetModFlag(block_array, PrevLba - firstLba))
1582                 break;
1583             DbgCopyMemory(tmp_buff + (n << BSh),
1584                         (PVOID)WCacheSectorAddr(block_array, PrevLba - firstLba),
1585                         BS);
1586             n++;
1587             if(n >= PSs)
1588                 break;
1589         }
1590         if(n > 1) {
1591             DbgCopyMemory(tmp_buff,
1592                         (PVOID)WCacheSectorAddr(block_array, Lba - firstLba),
1593                         BS);
1594         } else {
1595             tmp_buff = (PCHAR)WCacheSectorAddr(block_array, Lba - firstLba);
1596         }
1597         // write sectors out
1598         status = Cache->WriteProc(Context, tmp_buff, n<<BSh, Lba, &_WrittenBytes, 0);
1599         if(!OS_SUCCESS(status)) {
1600             status = WCacheRaiseIoError(Cache, Context, status, Lba, n, tmp_buff, WCACHE_W_OP, NULL);
1601             if(!OS_SUCCESS(status)) {
1602                 BrutePoint();
1603             }
1604         }
1605         firstPos += n;
1606         if(Purge) {
1607             // free memory
1608             WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, n);
1609         } else {
1610             // clear Modified flag
1611             ULONG i;
1612             Lba -= firstLba;
1613             for(i=0; i<n; i++) {
1614                 WCacheClrModFlag(block_array, Lba+i);
1615             }
1616         }
1617     }
1618 
1619     return status;
1620 } // end WCacheFlushBlocksRAM()
1621 
1622 /*
1623   WCacheCheckLimitsRAM() implements automatic flush and purge of
1624   unused blocks to keep enough free cache entries for newly
1625   read/written blocks for Random Access media.
1626   See also WCacheCheckLimits()
1627   Internal routine
1628  */
1629 OSSTATUS
1630 __fastcall
WCacheCheckLimitsRAM(IN PW_CACHE Cache,IN PVOID Context,IN lba_t ReqLba,IN ULONG BCount)1631 WCacheCheckLimitsRAM(
1632     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1633     IN PVOID Context,         // user-supplied context for IO callbacks
1634     IN lba_t ReqLba,          // first LBA to access/cache
1635     IN ULONG BCount           // number of Blocks to access/cache
1636     )
1637 {
1638     ULONG frame;
1639     lba_t firstLba;
1640     lba_t* List = Cache->CachedBlocksList;
1641     lba_t lastLba;
1642     lba_t Lba;
1643 //    PCHAR tmp_buff = Cache->tmp_buff;
1644     ULONG firstPos;
1645     ULONG lastPos;
1646 //    ULONG BSh = Cache->BlockSizeSh;
1647 //    ULONG BS = Cache->BlockSize;
1648 //    ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
1649     ULONG PSs = Cache->PacketSize;
1650 //    ULONG try_count = 0;
1651     PW_CACHE_ENTRY block_array;
1652 //    OSSTATUS status;
1653     ULONG FreeFrameCount = 0;
1654 //    PVOID addr;
1655 
1656     if(Cache->FrameCount >= Cache->MaxFrames) {
1657         FreeFrameCount = Cache->FramesToKeepFree;
1658     } else
1659     if((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
1660            BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) {
1661         // we need free space to grow WCache without flushing data
1662         // for some period of time
1663         FreeFrameCount = Cache->FramesToKeepFree;
1664         goto Try_Another_Frame;
1665     }
1666     // remove(flush) some frames
1667     while((Cache->FrameCount >= Cache->MaxFrames) || FreeFrameCount) {
1668         ASSERT(Cache->FrameCount <= Cache->MaxFrames);
1669 Try_Another_Frame:
1670         if(!Cache->FrameCount || !Cache->BlockCount) {
1671             ASSERT(!Cache->FrameCount);
1672             ASSERT(!Cache->BlockCount);
1673             if(!Cache->FrameCount)
1674                 break;
1675         }
1676 
1677         frame = WCacheFindFrameToRelease(Cache);
1678 #if 0
1679         if(Cache->FrameList[frame].WriteCount) {
1680             try_count++;
1681             if(try_count < MAX_TRIES_FOR_NA) goto Try_Another_Frame;
1682         } else {
1683             try_count = 0;
1684         }
1685 #else
1686 /*
1687         if(Cache->FrameList[frame].UpdateCount) {
1688             try_count = MAX_TRIES_FOR_NA;
1689         } else {
1690             try_count = 0;
1691         }
1692 */
1693 #endif
1694 
1695         if(FreeFrameCount)
1696             FreeFrameCount--;
1697 
1698         firstLba = frame << Cache->BlocksPerFrameSh;
1699         lastLba = firstLba + Cache->BlocksPerFrame;
1700         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba);
1701         lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba);
1702         block_array = Cache->FrameList[frame].Frame;
1703 
1704         if(!block_array) {
1705             UDFPrint(("Hmm...\n"));
1706             BrutePoint();
1707             return STATUS_DRIVER_INTERNAL_ERROR;
1708         }
1709         WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, TRUE);
1710 
1711         WCacheRemoveRangeFromList(List, &(Cache->BlockCount), firstLba, Cache->BlocksPerFrame);
1712         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame);
1713         WCacheRemoveFrame(Cache, Context, frame);
1714     }
1715 
1716     // check if we try to read too much data
1717     if(BCount > Cache->MaxBlocks) {
1718         return STATUS_INVALID_PARAMETER;
1719     }
1720 
1721     // remove(flush) packet
1722     while((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
1723            BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) {
1724 //        try_count = 0;
1725 //Try_Another_Block:
1726 
1727         ASSERT(Cache->FrameCount <= Cache->MaxFrames);
1728         Lba = WCacheFindLbaToRelease(Cache) & ~(PSs-1);
1729         if(Lba == WCACHE_INVALID_LBA) {
1730             ASSERT(!Cache->FrameCount);
1731             ASSERT(!Cache->BlockCount);
1732             break;
1733         }
1734         frame = Lba >> Cache->BlocksPerFrameSh;
1735         firstLba = frame << Cache->BlocksPerFrameSh;
1736         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba);
1737         lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba+PSs);
1738         block_array = Cache->FrameList[frame].Frame;
1739         if(!block_array) {
1740             ASSERT(FALSE);
1741             return STATUS_DRIVER_INTERNAL_ERROR;
1742         }
1743         WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, TRUE);
1744         WCacheRemoveRangeFromList(List, &(Cache->BlockCount), Lba, PSs);
1745         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, PSs);
1746         // check if frame is empty
1747         if(!(Cache->FrameList[frame].BlockCount)) {
1748             WCacheRemoveFrame(Cache, Context, frame);
1749         } else {
1750             ASSERT(Cache->FrameList[frame].Frame);
1751         }
1752     }
1753     return STATUS_SUCCESS;
1754 } // end WCacheCheckLimitsRAM()
1755 
1756 /*
1757   WCachePurgeAllRAM()
1758   Internal routine
1759  */
1760 OSSTATUS
1761 __fastcall
WCachePurgeAllRAM(IN PW_CACHE Cache,IN PVOID Context)1762 WCachePurgeAllRAM(
1763     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1764     IN PVOID Context          // user-supplied context for IO callbacks
1765     )
1766 {
1767     ULONG frame;
1768     lba_t firstLba;
1769     lba_t* List = Cache->CachedBlocksList;
1770     lba_t lastLba;
1771     ULONG firstPos;
1772     ULONG lastPos;
1773     PW_CACHE_ENTRY block_array;
1774 //    OSSTATUS status;
1775 
1776     // remove(flush) some frames
1777     while(Cache->FrameCount) {
1778 
1779         frame = Cache->CachedFramesList[0];
1780 
1781         firstLba = frame << Cache->BlocksPerFrameSh;
1782         lastLba = firstLba + Cache->BlocksPerFrame;
1783         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba);
1784         lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba);
1785         block_array = Cache->FrameList[frame].Frame;
1786 
1787         if(!block_array) {
1788             UDFPrint(("Hmm...\n"));
1789             BrutePoint();
1790             return STATUS_DRIVER_INTERNAL_ERROR;
1791         }
1792         WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, TRUE);
1793 
1794         WCacheRemoveRangeFromList(List, &(Cache->BlockCount), firstLba, Cache->BlocksPerFrame);
1795         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame);
1796         WCacheRemoveFrame(Cache, Context, frame);
1797     }
1798 
1799     ASSERT(!Cache->FrameCount);
1800     ASSERT(!Cache->BlockCount);
1801     return STATUS_SUCCESS;
1802 } // end WCachePurgeAllRAM()
1803 
1804 /*
1805   WCacheFlushAllRAM()
1806   Internal routine
1807  */
1808 OSSTATUS
1809 __fastcall
WCacheFlushAllRAM(IN PW_CACHE Cache,IN PVOID Context)1810 WCacheFlushAllRAM(
1811     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1812     IN PVOID Context          // user-supplied context for IO callbacks
1813     )
1814 {
1815     ULONG frame;
1816     lba_t firstLba;
1817     lba_t* List = Cache->CachedBlocksList;
1818     lba_t lastLba;
1819     ULONG firstPos;
1820     ULONG lastPos;
1821     PW_CACHE_ENTRY block_array;
1822 //    OSSTATUS status;
1823 
1824     // flush frames
1825     while(Cache->WriteCount) {
1826 
1827         frame = Cache->CachedModifiedBlocksList[0] >> Cache->BlocksPerFrameSh;
1828 
1829         firstLba = frame << Cache->BlocksPerFrameSh;
1830         lastLba = firstLba + Cache->BlocksPerFrame;
1831         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba);
1832         lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba);
1833         block_array = Cache->FrameList[frame].Frame;
1834 
1835         if(!block_array) {
1836             UDFPrint(("Hmm...\n"));
1837             BrutePoint();
1838             return STATUS_DRIVER_INTERNAL_ERROR;
1839         }
1840         WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, FALSE);
1841 
1842         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame);
1843     }
1844 
1845     return STATUS_SUCCESS;
1846 } // end WCacheFlushAllRAM()
1847 
1848 /*
1849   WCachePreReadPacket__() reads & caches the whole packet containing
1850   requested LBA. This routine just caches data, it doesn't copy anything
1851   to user buffer.
1852   In general we have no user buffer here... ;)
1853   Public routine
1854 */
1855 OSSTATUS
WCachePreReadPacket__(IN PW_CACHE Cache,IN PVOID Context,IN lba_t Lba)1856 WCachePreReadPacket__(
1857     IN PW_CACHE Cache,        // pointer to the Cache Control structure
1858     IN PVOID Context,         // user-supplied context for IO callbacks
1859     IN lba_t Lba              // LBA to cache together with whole packet
1860     )
1861 {
1862     ULONG frame;
1863     OSSTATUS status = STATUS_SUCCESS;
1864     PW_CACHE_ENTRY block_array;
1865     ULONG BSh = Cache->BlockSizeSh;
1866     ULONG BS = Cache->BlockSize;
1867     PCHAR addr;
1868     SIZE_T _ReadBytes;
1869     ULONG PS = Cache->PacketSize; // (in blocks)
1870     ULONG BCount = PS;
1871     ULONG i, n, err_count;
1872     BOOLEAN sector_added = FALSE;
1873     ULONG block_type;
1874     BOOLEAN zero = FALSE;//TRUE;
1875 /*
1876     ULONG first_zero=0, last_zero=0;
1877     BOOLEAN count_first_zero = TRUE;
1878 */
1879 
1880     Lba &= ~(PS-1);
1881     frame = Lba >> Cache->BlocksPerFrameSh;
1882     i = Lba - (frame << Cache->BlocksPerFrameSh);
1883 
1884     // assume successful operation
1885     block_array = Cache->FrameList[frame].Frame;
1886     if(!block_array) {
1887         ASSERT(Cache->FrameCount < Cache->MaxFrames);
1888         block_array = WCacheInitFrame(Cache, Context, frame);
1889         if(!block_array)
1890             return STATUS_INSUFFICIENT_RESOURCES;
1891     }
1892 
1893     // skip cached extent (if any)
1894     n=0;
1895     while((n < BCount) &&
1896           (n < Cache->BlocksPerFrame)) {
1897 
1898         addr = (PCHAR)WCacheSectorAddr(block_array, i+n);
1899         block_type = Cache->CheckUsedProc(Context, Lba+n);
1900         if(/*WCacheGetBadFlag(block_array,i+n)*/
1901            block_type & WCACHE_BLOCK_BAD) {
1902             // bad packet. no pre-read
1903             return STATUS_DEVICE_DATA_ERROR;
1904         }
1905         if(!(block_type & WCACHE_BLOCK_ZERO)) {
1906             zero = FALSE;
1907             //count_first_zero = FALSE;
1908             //last_zero = 0;
1909             if(!addr) {
1910                 // sector is not cached, stop search
1911                 break;
1912             }
1913         } else {
1914 /*
1915             if(count_first_zero) {
1916                 first_zero++;
1917             }
1918             last_zero++;
1919 */
1920         }
1921         n++;
1922     }
1923     // do nothing if all sectors are already cached
1924     if(n < BCount) {
1925 
1926         // read whole packet
1927         if(!zero) {
1928             status = Cache->ReadProc(Context, Cache->tmp_buff_r, PS<<BSh, Lba, &_ReadBytes, PH_TMP_BUFFER);
1929             if(!OS_SUCCESS(status)) {
1930                 status = WCacheRaiseIoError(Cache, Context, status, Lba, PS, Cache->tmp_buff_r, WCACHE_R_OP, NULL);
1931             }
1932         } else {
1933             status = STATUS_SUCCESS;
1934             //RtlZeroMemory(Cache->tmp_buff_r, PS<<BSh);
1935             _ReadBytes = PS<<BSh;
1936         }
1937         if(OS_SUCCESS(status)) {
1938             // and now we'll copy them to cache
1939             for(n=0; n<BCount; n++, i++) {
1940                 if(WCacheSectorAddr(block_array,i)) {
1941                     continue;
1942                 }
1943                 addr = block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
1944                 if(!addr) {
1945                     BrutePoint();
1946                     break;
1947                 }
1948                 sector_added = TRUE;
1949                 if(!zero) {
1950                     DbgCopyMemory(addr, Cache->tmp_buff_r+(n<<BSh), BS);
1951                 } else {
1952                     RtlZeroMemory(addr, BS);
1953                 }
1954                 Cache->FrameList[frame].BlockCount++;
1955             }
1956         } else {
1957             // read sectors one by one and copy them to cache
1958             // unreadable sectors will be treated as zero-filled
1959             err_count = 0;
1960             for(n=0; n<BCount; n++, i++) {
1961                 if(WCacheSectorAddr(block_array,i)) {
1962                     continue;
1963                 }
1964                 addr = block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
1965                 if(!addr) {
1966                     BrutePoint();
1967                     break;
1968                 }
1969                 sector_added = TRUE;
1970                 status = Cache->ReadProc(Context, Cache->tmp_buff_r, BS, Lba+n, &_ReadBytes, PH_TMP_BUFFER);
1971                 if(!OS_SUCCESS(status)) {
1972                     status = WCacheRaiseIoError(Cache, Context, status, Lba+n, 1, Cache->tmp_buff_r, WCACHE_R_OP, NULL);
1973                     if(!OS_SUCCESS(status)) {
1974                         err_count++;
1975                     }
1976                 }
1977                 if(!zero && OS_SUCCESS(status)) {
1978                     DbgCopyMemory(addr, Cache->tmp_buff_r, BS);
1979                 } else
1980                 if(Cache->RememberBB) {
1981                     RtlZeroMemory(addr, BS);
1982                     /*
1983                     if(!OS_SUCCESS(status)) {
1984                         WCacheSetBadFlag(block_array,i);
1985                     }
1986                     */
1987                 }
1988                 Cache->FrameList[frame].BlockCount++;
1989                 if(err_count >= 2) {
1990                     break;
1991                 }
1992             }
1993 //            _ReadBytes = n<<BSh;
1994         }
1995     }
1996 
1997     // we know the number of unread sectors if an error occured
1998     // so we can need to update BlockCount
1999     // return number of read bytes
2000     if(sector_added)
2001         WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, n);
2002 
2003     return status;
2004 } // end WCachePreReadPacket__()
2005 
2006 /*
2007   WCacheReadBlocks__() reads data from cache or
2008   read it form media and store in cache.
2009   Public routine
2010  */
2011 OSSTATUS
WCacheReadBlocks__(IN PW_CACHE Cache,IN PVOID Context,IN PCHAR Buffer,IN lba_t Lba,IN ULONG BCount,OUT PSIZE_T ReadBytes,IN BOOLEAN CachedOnly)2012 WCacheReadBlocks__(
2013     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2014     IN PVOID Context,         // user-supplied context for IO callbacks
2015     IN PCHAR Buffer,          // user-supplied buffer for read blocks
2016     IN lba_t Lba,             // LBA to start read from
2017     IN ULONG BCount,          // number of blocks to be read
2018     OUT PSIZE_T ReadBytes,     // user-supplied pointer to ULONG that will
2019                               //   recieve number of actually read bytes
2020     IN BOOLEAN CachedOnly     // specifies that cache is already locked
2021     )
2022 {
2023     ULONG frame;
2024     ULONG i, saved_i, saved_BC = BCount, n;
2025     OSSTATUS status = STATUS_SUCCESS;
2026     PW_CACHE_ENTRY block_array;
2027     ULONG BSh = Cache->BlockSizeSh;
2028     SIZE_T BS = Cache->BlockSize;
2029     PCHAR addr;
2030     ULONG to_read, saved_to_read;
2031 //    PCHAR saved_buff = Buffer;
2032     SIZE_T _ReadBytes;
2033     ULONG PS = Cache->PacketSize;
2034     ULONG MaxR = Cache->MaxBytesToRead;
2035     ULONG PacketMask = PS-1; // here we assume that Packet Size value is 2^n
2036     ULONG d;
2037     ULONG block_type;
2038 
2039     WcPrint(("WC:R %x (%x)\n", Lba, BCount));
2040 
2041     (*ReadBytes) = 0;
2042     // check if we try to read too much data
2043     if(BCount >= Cache->MaxBlocks) {
2044         i = 0;
2045         if(CachedOnly) {
2046             status = STATUS_INVALID_PARAMETER;
2047             goto EO_WCache_R2;
2048         }
2049         while(TRUE) {
2050             status = WCacheReadBlocks__(Cache, Context, Buffer + (i<<BSh), Lba, PS, &_ReadBytes, FALSE);
2051             (*ReadBytes) += _ReadBytes;
2052             if(!OS_SUCCESS(status) || (BCount <= PS)) break;
2053             BCount -= PS;
2054             Lba += PS;
2055             i += PS;
2056         }
2057         return status;
2058     }
2059     // check if we try to access beyond cached area
2060     if((Lba < Cache->FirstLba) ||
2061        (Lba + BCount - 1 > Cache->LastLba)) {
2062         status = Cache->ReadProc(Context, Buffer, BCount, Lba, ReadBytes, 0);
2063         if(!OS_SUCCESS(status)) {
2064             status = WCacheRaiseIoError(Cache, Context, status, Lba, BCount, Buffer, WCACHE_R_OP, NULL);
2065         }
2066         return status;
2067     }
2068     if(!CachedOnly) {
2069         ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2070     }
2071 
2072     frame = Lba >> Cache->BlocksPerFrameSh;
2073     i = Lba - (frame << Cache->BlocksPerFrameSh);
2074 
2075     if(Cache->CacheWholePacket && (BCount < PS)) {
2076         if(!CachedOnly &&
2077            !OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba & ~(PS-1), PS*2)) ) {
2078             ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2079             return status;
2080         }
2081     } else {
2082         if(!CachedOnly &&
2083            !OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba, BCount))) {
2084             ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2085             return status;
2086         }
2087     }
2088     if(!CachedOnly) {
2089         // convert to shared
2090 //        ExConvertExclusiveToSharedLite(&(Cache->WCacheLock));
2091     }
2092 
2093     // pre-read packet. It is very useful for
2094     // highly fragmented files
2095     if(Cache->CacheWholePacket && (BCount < PS)) {
2096 //        status = WCacheReadBlocks__(Cache, Context, Cache->tmp_buff_r, Lba & (~PacketMask), PS, &_ReadBytes, TRUE);
2097         // we should not perform IO if user requested CachedOnly data
2098         if(!CachedOnly) {
2099             status = WCachePreReadPacket__(Cache, Context, Lba);
2100         }
2101         status = STATUS_SUCCESS;
2102     }
2103 
2104     // assume successful operation
2105     block_array = Cache->FrameList[frame].Frame;
2106     if(!block_array) {
2107         ASSERT(!CachedOnly);
2108         ASSERT(Cache->FrameCount < Cache->MaxFrames);
2109         block_array = WCacheInitFrame(Cache, Context, frame);
2110         if(!block_array) {
2111             status = STATUS_INSUFFICIENT_RESOURCES;
2112             goto EO_WCache_R;
2113         }
2114     }
2115 
2116     Cache->FrameList[frame].AccessCount++;
2117     while(BCount) {
2118         if(i >= Cache->BlocksPerFrame) {
2119             frame++;
2120             block_array = Cache->FrameList[frame].Frame;
2121             i -= Cache->BlocksPerFrame;
2122         }
2123         if(!block_array) {
2124             ASSERT(Cache->FrameCount < Cache->MaxFrames);
2125             block_array = WCacheInitFrame(Cache, Context, frame);
2126             if(!block_array) {
2127                 status = STATUS_INSUFFICIENT_RESOURCES;
2128                 goto EO_WCache_R;
2129             }
2130         }
2131         // 'read' cached extent (if any)
2132         // it is just copying
2133         while(BCount &&
2134               (i < Cache->BlocksPerFrame) &&
2135               (addr = (PCHAR)WCacheSectorAddr(block_array, i)) ) {
2136             block_type = Cache->CheckUsedProc(Context, Lba+saved_BC-BCount);
2137             if(block_type & WCACHE_BLOCK_BAD) {
2138             //if(WCacheGetBadFlag(block_array,i)) {
2139                 status = STATUS_DEVICE_DATA_ERROR;
2140                 goto EO_WCache_R;
2141             }
2142             DbgCopyMemory(Buffer, addr, BS);
2143             Buffer += BS;
2144             *ReadBytes += BS;
2145             i++;
2146             BCount--;
2147         }
2148         // read non-cached packet-size-aligned extent (if any)
2149         // now we'll calculate total length & decide if it has enough size
2150         if(!((d = Lba+saved_BC-BCount) & PacketMask) && d ) {
2151             n = 0;
2152             while(BCount &&
2153                   (i < Cache->BlocksPerFrame) &&
2154                   (!WCacheSectorAddr(block_array, i)) ) {
2155                  n++;
2156                  BCount--;
2157             }
2158             BCount += n;
2159             n &= ~PacketMask;
2160             if(n>PS) {
2161                 if(!OS_SUCCESS(status = Cache->ReadProc(Context, Buffer, BS*n, Lba+saved_BC-BCount, &_ReadBytes, 0))) {
2162                     status = WCacheRaiseIoError(Cache, Context, status, Lba+saved_BC-BCount, n, Buffer, WCACHE_R_OP, NULL);
2163                     if(!OS_SUCCESS(status)) {
2164                         goto EO_WCache_R;
2165                     }
2166                 }
2167 //                WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount);
2168                 BCount -= n;
2169                 Lba += saved_BC - BCount;
2170                 saved_BC = BCount;
2171                 i += n;
2172                 Buffer += BS*n;
2173                 *ReadBytes += BS*n;
2174             }
2175 //        } else {
2176 //            UDFPrint(("Unaligned\n"));
2177         }
2178         // read non-cached extent (if any)
2179         // firstable, we'll get total number of sectors to read
2180         to_read = 0;
2181         saved_i = i;
2182         d = BCount;
2183         while(d &&
2184               (i < Cache->BlocksPerFrame) &&
2185               (!WCacheSectorAddr(block_array, i)) ) {
2186             i++;
2187             to_read += BS;
2188             d--;
2189         }
2190         // read some not cached sectors
2191         if(to_read) {
2192             i = saved_i;
2193             saved_to_read = to_read;
2194             d = BCount - d;
2195             // split request if necessary
2196             if(saved_to_read > MaxR) {
2197                 WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount);
2198                 n = MaxR >> BSh;
2199                 do {
2200                     status = Cache->ReadProc(Context, Buffer, MaxR, i + (frame << Cache->BlocksPerFrameSh), &_ReadBytes, 0);
2201                     *ReadBytes += _ReadBytes;
2202                     if(!OS_SUCCESS(status)) {
2203                         _ReadBytes &= ~(BS-1);
2204                         BCount -= _ReadBytes >> BSh;
2205                         saved_to_read -= _ReadBytes;
2206                         Buffer += _ReadBytes;
2207                         saved_BC = BCount;
2208                         goto store_read_data_1;
2209                     }
2210                     Buffer += MaxR;
2211                     saved_to_read -= MaxR;
2212                     i += n;
2213                     BCount -= n;
2214                     d -= n;
2215                 } while(saved_to_read > MaxR);
2216                 saved_BC = BCount;
2217             }
2218             if(saved_to_read) {
2219                 status = Cache->ReadProc(Context, Buffer, saved_to_read, i + (frame << Cache->BlocksPerFrameSh), &_ReadBytes, 0);
2220                 *ReadBytes += _ReadBytes;
2221                 if(!OS_SUCCESS(status)) {
2222                     _ReadBytes &= ~(BS-1);
2223                     BCount -= _ReadBytes >> BSh;
2224                     saved_to_read -= _ReadBytes;
2225                     Buffer += _ReadBytes;
2226                     goto store_read_data_1;
2227                 }
2228                 Buffer += saved_to_read;
2229                 saved_to_read = 0;
2230                 BCount -= d;
2231             }
2232 
2233 store_read_data_1:
2234             // and now we'll copy them to cache
2235 
2236             //
2237             Buffer -= (to_read - saved_to_read);
2238             i = saved_i;
2239             while(to_read - saved_to_read) {
2240                 block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
2241                 if(!block_array[i].Sector) {
2242                     BCount += to_read >> BSh;
2243                     status = STATUS_INSUFFICIENT_RESOURCES;
2244                     goto EO_WCache_R;
2245                 }
2246                 DbgCopyMemory(block_array[i].Sector, Buffer, BS);
2247                 Cache->FrameList[frame].BlockCount++;
2248                 i++;
2249                 Buffer += BS;
2250                 to_read -= BS;
2251             }
2252             if(!OS_SUCCESS(status))
2253                 goto EO_WCache_R;
2254             to_read = 0;
2255         }
2256     }
2257 
2258 EO_WCache_R:
2259 
2260     // we know the number of unread sectors if an error occured
2261     // so we can need to update BlockCount
2262     // return number of read bytes
2263     WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount);
2264 //    Cache->FrameList[frame].BlockCount -= BCount;
2265 EO_WCache_R2:
2266     if(!CachedOnly) {
2267         ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2268     }
2269 
2270     return status;
2271 } // end WCacheReadBlocks__()
2272 
2273 /*
2274   WCacheWriteBlocks__() writes data to cache.
2275   Data is written directly to media if:
2276   1) requested block is Packet-aligned
2277   2) requested Lba(s) lays beyond cached area
2278   Public routine
2279  */
2280 OSSTATUS
WCacheWriteBlocks__(IN PW_CACHE Cache,IN PVOID Context,IN PCHAR Buffer,IN lba_t Lba,IN ULONG BCount,OUT PSIZE_T WrittenBytes,IN BOOLEAN CachedOnly)2281 WCacheWriteBlocks__(
2282     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2283     IN PVOID Context,         // user-supplied context for IO callbacks
2284     IN PCHAR Buffer,          // user-supplied buffer containing data to be written
2285     IN lba_t Lba,             // LBA to start write from
2286     IN ULONG BCount,          // number of blocks to be written
2287     OUT PSIZE_T WrittenBytes,  // user-supplied pointer to ULONG that will
2288                               //   recieve number of actually written bytes
2289     IN BOOLEAN CachedOnly     // specifies that cache is already locked
2290     )
2291 {
2292     ULONG frame;
2293     ULONG i, saved_BC = BCount, n, d;
2294     OSSTATUS status = STATUS_SUCCESS;
2295     PW_CACHE_ENTRY block_array;
2296     ULONG BSh = Cache->BlockSizeSh;
2297     ULONG BS = Cache->BlockSize;
2298     PCHAR addr;
2299 //    PCHAR saved_buff = Buffer;
2300     SIZE_T _WrittenBytes;
2301     ULONG PS = Cache->PacketSize;
2302     ULONG PacketMask = PS-1; // here we assume that Packet Size value is 2^n
2303     ULONG block_type;
2304 //    BOOLEAN Aligned = FALSE;
2305 
2306     BOOLEAN WriteThrough = FALSE;
2307     lba_t   WTh_Lba;
2308     ULONG   WTh_BCount;
2309 
2310     WcPrint(("WC:W %x (%x)\n", Lba, BCount));
2311 
2312     *WrittenBytes = 0;
2313 //    UDFPrint(("BCount:%x\n",BCount));
2314     // check if we try to read too much data
2315     if(BCount >= Cache->MaxBlocks) {
2316         i = 0;
2317         if(CachedOnly) {
2318             status = STATUS_INVALID_PARAMETER;
2319             goto EO_WCache_W2;
2320         }
2321         while(TRUE) {
2322 //            UDFPrint(("  BCount:%x\n",BCount));
2323             status = WCacheWriteBlocks__(Cache, Context, Buffer + (i<<BSh), Lba, min(PS,BCount), &_WrittenBytes, FALSE);
2324             (*WrittenBytes) += _WrittenBytes;
2325             BCount -= PS;
2326             Lba += PS;
2327             i += PS;
2328             if(!OS_SUCCESS(status) || (BCount < PS))
2329                 return status;
2330         }
2331     }
2332     // check if we try to access beyond cached area
2333     if((Lba < Cache->FirstLba) ||
2334        (Lba + BCount - 1 > Cache->LastLba)) {
2335         return STATUS_INVALID_PARAMETER;
2336     }
2337     if(!CachedOnly) {
2338         ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2339     }
2340 
2341     frame = Lba >> Cache->BlocksPerFrameSh;
2342     i = Lba - (frame << Cache->BlocksPerFrameSh);
2343 
2344     if(!CachedOnly &&
2345        !OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba, BCount))) {
2346         ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2347         return status;
2348     }
2349 
2350     // assume successful operation
2351     block_array = Cache->FrameList[frame].Frame;
2352     if(!block_array) {
2353 
2354         if(BCount && !(BCount & (PS-1)) && !(Lba & (PS-1)) &&
2355            (Cache->Mode != WCACHE_MODE_R) &&
2356            (i+BCount <= Cache->BlocksPerFrame) &&
2357             !Cache->NoWriteThrough) {
2358             status = Cache->WriteProc(Context, Buffer, BCount<<BSh, Lba, WrittenBytes, 0);
2359             if(!OS_SUCCESS(status)) {
2360                 status = WCacheRaiseIoError(Cache, Context, status, Lba, BCount, Buffer, WCACHE_W_OP, NULL);
2361             }
2362             goto EO_WCache_W2;
2363         }
2364 
2365         ASSERT(!CachedOnly);
2366         ASSERT(Cache->FrameCount < Cache->MaxFrames);
2367         block_array = WCacheInitFrame(Cache, Context, frame);
2368         if(!block_array) {
2369             status = STATUS_INSUFFICIENT_RESOURCES;
2370             goto EO_WCache_W;
2371         }
2372     }
2373 
2374     if(Cache->Mode == WCACHE_MODE_RAM &&
2375        BCount &&
2376 //       !(Lba & (PS-1)) &&
2377        (!(BCount & (PS-1)) || (BCount > PS)) ) {
2378         WriteThrough = TRUE;
2379         WTh_Lba = Lba;
2380         WTh_BCount = BCount;
2381     } else
2382     if(Cache->Mode == WCACHE_MODE_RAM &&
2383        ((Lba & ~PacketMask) != ((Lba+BCount-1) & ~PacketMask))
2384       ) {
2385         WriteThrough = TRUE;
2386         WTh_Lba = Lba & ~PacketMask;
2387         WTh_BCount = PS;
2388     }
2389 
2390     Cache->FrameList[frame].UpdateCount++;
2391 //    UDFPrint(("    BCount:%x\n",BCount));
2392     while(BCount) {
2393         if(i >= Cache->BlocksPerFrame) {
2394             frame++;
2395             block_array = Cache->FrameList[frame].Frame;
2396             i -= Cache->BlocksPerFrame;
2397         }
2398         if(!block_array) {
2399             ASSERT(Cache->FrameCount < Cache->MaxFrames);
2400             block_array = WCacheInitFrame(Cache, Context, frame);
2401             if(!block_array) {
2402                 status = STATUS_INSUFFICIENT_RESOURCES;
2403                 goto EO_WCache_W;
2404             }
2405         }
2406         // 'write' cached extent (if any)
2407         // it is just copying
2408         while(BCount &&
2409               (i < Cache->BlocksPerFrame) &&
2410               (addr = (PCHAR)WCacheSectorAddr(block_array, i)) ) {
2411 //            UDFPrint(("addr:%x:Buffer:%x:BS:%x:BCount:%x\n",addr, Buffer, BS, BCount));
2412             block_type = Cache->CheckUsedProc(Context, Lba+saved_BC-BCount);
2413             if(Cache->NoWriteBB &&
2414                /*WCacheGetBadFlag(block_array,i)*/
2415                (block_type & WCACHE_BLOCK_BAD)) {
2416                 // bad packet. no cached write
2417                 status = STATUS_DEVICE_DATA_ERROR;
2418                 goto EO_WCache_W;
2419             }
2420             DbgCopyMemory(addr, Buffer, BS);
2421             WCacheSetModFlag(block_array, i);
2422             Buffer += BS;
2423             *WrittenBytes += BS;
2424             i++;
2425             BCount--;
2426         }
2427         // write non-cached not-aligned extent (if any) till aligned one
2428         while(BCount &&
2429               (i & PacketMask) &&
2430               (Cache->Mode != WCACHE_MODE_R) &&
2431               (i < Cache->BlocksPerFrame) &&
2432               (!WCacheSectorAddr(block_array, i)) ) {
2433             block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
2434             if(!block_array[i].Sector) {
2435                 status = STATUS_INSUFFICIENT_RESOURCES;
2436                 goto EO_WCache_W;
2437             }
2438 //            UDFPrint(("addr:%x:Buffer:%x:BS:%x:BCount:%x\n",block_array[i].Sector, Buffer, BS, BCount));
2439             DbgCopyMemory(block_array[i].Sector, Buffer, BS);
2440             WCacheSetModFlag(block_array, i);
2441             i++;
2442             Buffer += BS;
2443             *WrittenBytes += BS;
2444             BCount--;
2445             Cache->FrameList[frame].BlockCount ++;
2446         }
2447         // write non-cached packet-size-aligned extent (if any)
2448         // now we'll calculate total length & decide if has enough size
2449         if(!Cache->NoWriteThrough
2450                      &&
2451            ( !(i & PacketMask) ||
2452              ((Cache->Mode == WCACHE_MODE_R) && (BCount >= PS)) )) {
2453             n = 0;
2454             while(BCount &&
2455                   (i < Cache->BlocksPerFrame) &&
2456                   (!WCacheSectorAddr(block_array, i)) ) {
2457                  n++;
2458                  BCount--;
2459             }
2460             BCount += n;
2461             n &= ~PacketMask;
2462 //                if(!OS_SUCCESS(status = Cache->WriteProcAsync(Context, Buffer, BS*n, Lba+saved_BC-BCount, &_WrittenBytes, FALSE)))
2463             if(n) {
2464                 // add previously written data to list
2465                 d = saved_BC - BCount;
2466                 WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, d);
2467                 WCacheInsertRangeToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, d);
2468                 Lba += d;
2469                 saved_BC = BCount;
2470 
2471                 while(n) {
2472                     if(Cache->Mode == WCACHE_MODE_R)
2473                         Cache->UpdateRelocProc(Context, Lba, NULL, PS);
2474                     if(!OS_SUCCESS(status = Cache->WriteProc(Context, Buffer, PS<<BSh, Lba, &_WrittenBytes, 0))) {
2475                         status = WCacheRaiseIoError(Cache, Context, status, Lba, PS, Buffer, WCACHE_W_OP, NULL);
2476                         if(!OS_SUCCESS(status)) {
2477                             goto EO_WCache_W;
2478                         }
2479                     }
2480                     BCount -= PS;
2481                     Lba += PS;
2482                     saved_BC = BCount;
2483                     i += PS;
2484                     Buffer += PS<<BSh;
2485                     *WrittenBytes += PS<<BSh;
2486                     n-=PS;
2487                 }
2488             }
2489         }
2490         // write non-cached not-aligned extent (if any)
2491         while(BCount &&
2492               (i < Cache->BlocksPerFrame) &&
2493               (!WCacheSectorAddr(block_array, i)) ) {
2494             block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
2495             if(!block_array[i].Sector) {
2496                 status = STATUS_INSUFFICIENT_RESOURCES;
2497                 goto EO_WCache_W;
2498             }
2499 //            UDFPrint(("addr:%x:Buffer:%x:BS:%x:BCount:%x\n",block_array[i].Sector, Buffer, BS, BCount));
2500             DbgCopyMemory(block_array[i].Sector, Buffer, BS);
2501             WCacheSetModFlag(block_array, i);
2502             i++;
2503             Buffer += BS;
2504             *WrittenBytes += BS;
2505             BCount--;
2506             Cache->FrameList[frame].BlockCount ++;
2507         }
2508     }
2509 
2510 EO_WCache_W:
2511 
2512     // we know the number of unread sectors if an error occured
2513     // so we can need to update BlockCount
2514     // return number of read bytes
2515     WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount);
2516     WCacheInsertRangeToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, saved_BC - BCount);
2517 
2518     if(WriteThrough && !BCount) {
2519         ULONG d;
2520 //        lba_t lastLba;
2521         ULONG firstPos;
2522         ULONG lastPos;
2523 
2524         BCount = WTh_BCount;
2525         Lba = WTh_Lba;
2526         while(BCount) {
2527             frame = Lba >> Cache->BlocksPerFrameSh;
2528 //            firstLba = frame << Cache->BlocksPerFrameSh;
2529             firstPos = WCacheGetSortedListIndex(Cache->BlockCount, Cache->CachedBlocksList, Lba);
2530             d = min(Lba+BCount, (frame+1) << Cache->BlocksPerFrameSh) - Lba;
2531             lastPos = WCacheGetSortedListIndex(Cache->BlockCount, Cache->CachedBlocksList, Lba+d);
2532             block_array = Cache->FrameList[frame].Frame;
2533             if(!block_array) {
2534                 ASSERT(FALSE);
2535                 BCount -= d;
2536                 Lba += d;
2537                 continue;
2538             }
2539             status = WCacheFlushBlocksRAM(Cache, Context, block_array, Cache->CachedBlocksList, firstPos, lastPos, FALSE);
2540             WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, d);
2541             BCount -= d;
2542             Lba += d;
2543         }
2544     }
2545 
2546 EO_WCache_W2:
2547 
2548     if(!CachedOnly) {
2549         ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2550     }
2551     return status;
2552 } // end WCacheWriteBlocks__()
2553 
2554 /*
2555   WCacheFlushAll__() copies all data stored in cache to media.
2556   Flushed blocks are kept in cache.
2557   Public routine
2558  */
2559 VOID
WCacheFlushAll__(IN PW_CACHE Cache,IN PVOID Context)2560 WCacheFlushAll__(
2561     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2562     IN PVOID Context)         // user-supplied context for IO callbacks
2563 {
2564     if(!(Cache->ReadProc)) return;
2565     ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2566 
2567     switch(Cache->Mode) {
2568     case WCACHE_MODE_RAM:
2569         WCacheFlushAllRAM(Cache, Context);
2570         break;
2571     case WCACHE_MODE_ROM:
2572     case WCACHE_MODE_RW:
2573         WCacheFlushAllRW(Cache, Context);
2574         break;
2575     case WCACHE_MODE_R:
2576         WCachePurgeAllR(Cache, Context);
2577         break;
2578     }
2579 
2580     ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2581     return;
2582 } // end WCacheFlushAll__()
2583 
2584 /*
2585   WCachePurgeAll__() copies all data stored in cache to media.
2586   Flushed blocks are removed cache.
2587   Public routine
2588  */
2589 VOID
WCachePurgeAll__(IN PW_CACHE Cache,IN PVOID Context)2590 WCachePurgeAll__(
2591     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2592     IN PVOID Context)         // user-supplied context for IO callbacks
2593 {
2594     if(!(Cache->ReadProc)) return;
2595     ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2596 
2597     switch(Cache->Mode) {
2598     case WCACHE_MODE_RAM:
2599         WCachePurgeAllRAM(Cache, Context);
2600         break;
2601     case WCACHE_MODE_ROM:
2602     case WCACHE_MODE_RW:
2603         WCachePurgeAllRW(Cache, Context);
2604         break;
2605     case WCACHE_MODE_R:
2606         WCachePurgeAllR(Cache, Context);
2607         break;
2608     }
2609 
2610     ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2611     return;
2612 } // end WCachePurgeAll__()
2613 /*
2614   WCachePurgeAllRW() copies modified blocks from cache to media
2615   and removes them from cache
2616   This routine can be used for RAM, RW and ROM media.
2617   For ROM media blocks are just removed.
2618   Internal routine
2619  */
2620 VOID
2621 __fastcall
WCachePurgeAllRW(IN PW_CACHE Cache,IN PVOID Context)2622 WCachePurgeAllRW(
2623     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2624     IN PVOID Context)         // user-supplied context for IO callbacks
2625 {
2626     ULONG frame;
2627     lba_t firstLba;
2628     lba_t* List = Cache->CachedBlocksList;
2629     lba_t Lba;
2630 //    ULONG firstPos;
2631 //    ULONG lastPos;
2632     ULONG BSh = Cache->BlockSizeSh;
2633     ULONG BS = Cache->BlockSize;
2634     ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
2635     ULONG PSs = Cache->PacketSize;
2636     PW_CACHE_ENTRY block_array;
2637 //    OSSTATUS status;
2638     SIZE_T ReadBytes;
2639     PW_CACHE_ASYNC FirstWContext = NULL;
2640     PW_CACHE_ASYNC PrevWContext = NULL;
2641     ULONG chain_count = 0;
2642 
2643     if(!(Cache->ReadProc)) return;
2644 
2645     while(Cache->BlockCount) {
2646         Lba = List[0] & ~(PSs-1);
2647         frame = Lba >> Cache->BlocksPerFrameSh;
2648         firstLba = frame << Cache->BlocksPerFrameSh;
2649 //        firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba);
2650 //        lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba+PSs);
2651         block_array = Cache->FrameList[frame].Frame;
2652         if(!block_array) {
2653             BrutePoint();
2654             return;
2655         }
2656 
2657         WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
2658             Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE);
2659 
2660         // free memory
2661         WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, PSs);
2662 
2663         WCacheRemoveRangeFromList(List, &(Cache->BlockCount), Lba, PSs);
2664         WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, PSs);
2665         // check if frame is empty
2666         if(!(Cache->FrameList[frame].BlockCount)) {
2667             WCacheRemoveFrame(Cache, Context, frame);
2668         } else {
2669             ASSERT(Cache->FrameList[frame].Frame);
2670         }
2671         chain_count++;
2672         if(chain_count >= WCACHE_MAX_CHAIN) {
2673             WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
2674             chain_count = 0;
2675         }
2676     }
2677     WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext);
2678     return;
2679 } // end WCachePurgeAllRW()
2680 
2681 /*
2682   WCacheFlushAllRW() copies modified blocks from cache to media.
2683   All blocks are not removed from cache.
2684   This routine can be used for RAM, RW and ROM media.
2685   Internal routine
2686  */
2687 VOID
2688 __fastcall
WCacheFlushAllRW(IN PW_CACHE Cache,IN PVOID Context)2689 WCacheFlushAllRW(
2690     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2691     IN PVOID Context)         // user-supplied context for IO callbacks
2692 {
2693     ULONG frame;
2694     lba_t firstLba;
2695     lba_t* List = Cache->CachedModifiedBlocksList;
2696     lba_t Lba;
2697 //    ULONG firstPos;
2698 //    ULONG lastPos;
2699     ULONG BSh = Cache->BlockSizeSh;
2700     ULONG BS = Cache->BlockSize;
2701     ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
2702     ULONG PSs = Cache->PacketSize;
2703     ULONG BFs = Cache->BlocksPerFrameSh;
2704     PW_CACHE_ENTRY block_array;
2705 //    OSSTATUS status;
2706     SIZE_T ReadBytes;
2707     PW_CACHE_ASYNC FirstWContext = NULL;
2708     PW_CACHE_ASYNC PrevWContext = NULL;
2709     ULONG i;
2710     ULONG chain_count = 0;
2711 
2712     if(!(Cache->ReadProc)) return;
2713 
2714     // walk through modified blocks
2715     while(Cache->WriteCount) {
2716         Lba = List[0] & ~(PSs-1);
2717         frame = Lba >> BFs;
2718         firstLba = frame << BFs;
2719 //        firstPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba);
2720 //        lastPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba+PSs);
2721         block_array = Cache->FrameList[frame].Frame;
2722         if(!block_array) {
2723             BrutePoint();
2724             continue;;
2725         }
2726         // queue modify request
2727         WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
2728             Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE);
2729         // clear MODIFIED flag for queued blocks
2730         WCacheRemoveRangeFromList(List, &(Cache->WriteCount), Lba, PSs);
2731         Lba -= firstLba;
2732         for(i=0; i<PSs; i++) {
2733             WCacheClrModFlag(block_array, Lba+i);
2734         }
2735         chain_count++;
2736         // check queue size
2737         if(chain_count >= WCACHE_MAX_CHAIN) {
2738             WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
2739             chain_count = 0;
2740         }
2741     }
2742     WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
2743 #ifdef DBG
2744 #if 1
2745     // check consistency
2746     List = Cache->CachedBlocksList;
2747     for(i=0; i<Cache->BlockCount; i++) {
2748         Lba = List[i] /*& ~(PSs-1)*/;
2749         frame = Lba >> Cache->BlocksPerFrameSh;
2750         firstLba = frame << Cache->BlocksPerFrameSh;
2751         block_array = Cache->FrameList[frame].Frame;
2752         if(!block_array) {
2753             BrutePoint();
2754         }
2755         ASSERT(!WCacheGetModFlag(block_array, Lba-firstLba));
2756     }
2757 #endif // 1
2758 #endif // DBG
2759     return;
2760 } // end WCacheFlushAllRW()
2761 
2762 /*
2763   WCacheRelease__() frees all allocated memory blocks and
2764   deletes synchronization resources
2765   Public routine
2766  */
2767 VOID
WCacheRelease__(IN PW_CACHE Cache)2768 WCacheRelease__(
2769     IN PW_CACHE Cache         // pointer to the Cache Control structure
2770     )
2771 {
2772     ULONG i, j, k;
2773     PW_CACHE_ENTRY block_array;
2774 
2775     Cache->Tag = 0xDEADCACE;
2776     if(!(Cache->ReadProc)) return;
2777 //    ASSERT(Cache->Tag == 0xCAC11E00);
2778     ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2779     for(i=0; i<Cache->FrameCount; i++) {
2780         j = Cache->CachedFramesList[i];
2781         block_array = Cache->FrameList[j].Frame;
2782         if(block_array) {
2783             for(k=0; k<Cache->BlocksPerFrame; k++) {
2784                 if(WCacheSectorAddr(block_array, k)) {
2785                     WCacheFreeSector(j, k);
2786                 }
2787             }
2788             MyFreePool__(block_array);
2789         }
2790     }
2791     if(Cache->FrameList)
2792         MyFreePool__(Cache->FrameList);
2793     if(Cache->CachedBlocksList)
2794         MyFreePool__(Cache->CachedBlocksList);
2795     if(Cache->CachedModifiedBlocksList)
2796         MyFreePool__(Cache->CachedModifiedBlocksList);
2797     if(Cache->CachedFramesList)
2798         MyFreePool__(Cache->CachedFramesList);
2799     if(Cache->tmp_buff_r)
2800         MyFreePool__(Cache->tmp_buff_r);
2801     if(Cache->CachedFramesList)
2802         MyFreePool__(Cache->tmp_buff);
2803     if(Cache->CachedFramesList)
2804         MyFreePool__(Cache->reloc_tab);
2805     ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2806     ExDeleteResourceLite(&(Cache->WCacheLock));
2807     RtlZeroMemory(Cache, sizeof(W_CACHE));
2808     return;
2809 } // end WCacheRelease__()
2810 
2811 /*
2812   WCacheIsInitialized__() checks if the pointer supplied points
2813   to initialized cache structure.
2814   Public routine
2815  */
2816 BOOLEAN
WCacheIsInitialized__(IN PW_CACHE Cache)2817 WCacheIsInitialized__(
2818     IN PW_CACHE Cache
2819     )
2820 {
2821     return (Cache->ReadProc != NULL);
2822 } // end WCacheIsInitialized__()
2823 
2824 OSSTATUS
WCacheFlushBlocksRW(IN PW_CACHE Cache,IN PVOID Context,IN lba_t _Lba,IN ULONG BCount)2825 WCacheFlushBlocksRW(
2826     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2827     IN PVOID Context,         // user-supplied context for IO callbacks
2828     IN lba_t _Lba,             // LBA to start flush from
2829     IN ULONG BCount           // number of blocks to be flushed
2830     )
2831 {
2832     ULONG frame;
2833     lba_t firstLba;
2834     lba_t* List = Cache->CachedModifiedBlocksList;
2835     lba_t Lba;
2836 //    ULONG firstPos;
2837 //    ULONG lastPos;
2838     ULONG BSh = Cache->BlockSizeSh;
2839     ULONG BS = Cache->BlockSize;
2840     ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
2841     ULONG PSs = Cache->PacketSize;
2842     ULONG BFs = Cache->BlocksPerFrameSh;
2843     PW_CACHE_ENTRY block_array;
2844 //    OSSTATUS status;
2845     SIZE_T ReadBytes;
2846     PW_CACHE_ASYNC FirstWContext = NULL;
2847     PW_CACHE_ASYNC PrevWContext = NULL;
2848     ULONG i;
2849     ULONG chain_count = 0;
2850     lba_t lim;
2851 
2852     if(!(Cache->ReadProc)) return STATUS_INVALID_PARAMETER;
2853 
2854     // walk through modified blocks
2855     lim = (_Lba+BCount+PSs-1) & ~(PSs-1);
2856     for(Lba = _Lba & ~(PSs-1);Lba < lim ; Lba += PSs) {
2857         frame = Lba >> BFs;
2858         firstLba = frame << BFs;
2859 //        firstPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba);
2860 //        lastPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba+PSs);
2861         block_array = Cache->FrameList[frame].Frame;
2862         if(!block_array) {
2863             // not cached block may be requested for flush
2864             Lba += (1 << BFs) - PSs;
2865             continue;
2866         }
2867         // queue modify request
2868         WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba,
2869             Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE);
2870         // clear MODIFIED flag for queued blocks
2871         WCacheRemoveRangeFromList(List, &(Cache->WriteCount), Lba, PSs);
2872         Lba -= firstLba;
2873         for(i=0; i<PSs; i++) {
2874             WCacheClrModFlag(block_array, Lba+i);
2875         }
2876         Lba += firstLba;
2877         chain_count++;
2878         // check queue size
2879         if(chain_count >= WCACHE_MAX_CHAIN) {
2880             WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
2881             chain_count = 0;
2882         }
2883     }
2884     WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE);
2885 /*
2886     if(Cache->Mode != WCACHE_MODE_RAM)
2887         return STATUS_SUCCESS;
2888 */
2889 
2890     return STATUS_SUCCESS;
2891 } // end WCacheFlushBlocksRW()
2892 
2893 /*
2894   WCacheFlushBlocks__() copies specified blocks stored in cache to media.
2895   Flushed blocks are kept in cache.
2896   Public routine
2897  */
2898 OSSTATUS
WCacheFlushBlocks__(IN PW_CACHE Cache,IN PVOID Context,IN lba_t Lba,IN ULONG BCount)2899 WCacheFlushBlocks__(
2900     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2901     IN PVOID Context,         // user-supplied context for IO callbacks
2902     IN lba_t Lba,             // LBA to start flush from
2903     IN ULONG BCount           // number of blocks to be flushed
2904     )
2905 {
2906     OSSTATUS status;
2907 
2908     if(!(Cache->ReadProc)) return STATUS_INVALID_PARAMETER;
2909     ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2910 
2911     // check if we try to access beyond cached area
2912     if((Lba < Cache->FirstLba) ||
2913        (Lba+BCount-1 > Cache->LastLba)) {
2914         UDFPrint(("LBA %#x (%x) is beyond cacheable area\n", Lba, BCount));
2915         BrutePoint();
2916         status = STATUS_INVALID_PARAMETER;
2917         goto EO_WCache_F;
2918     }
2919 
2920     switch(Cache->Mode) {
2921     case WCACHE_MODE_RAM:
2922 //        WCacheFlushBlocksRW(Cache, Context);
2923 //        break;
2924     case WCACHE_MODE_ROM:
2925     case WCACHE_MODE_RW:
2926         status = WCacheFlushBlocksRW(Cache, Context, Lba, BCount);
2927         break;
2928     case WCACHE_MODE_R:
2929         status = STATUS_SUCCESS;
2930         break;
2931     }
2932 EO_WCache_F:
2933     ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
2934     return status;
2935 } // end WCacheFlushBlocks__()
2936 
2937 /*
2938   WCacheDirect__() returns pointer to memory block where
2939   requested block is stored in.
2940   If no #CachedOnly flag specified this routine locks cache,
2941   otherwise it assumes that cache is already locked by previous call
2942   to WCacheStartDirect__().
2943   Cache can be unlocked by WCacheEODirect__().
2944   Using this routine caller can access cached block directly in memory
2945   without Read_to_Tmp and Modify/Write steps.
2946   Public routine
2947  */
2948 OSSTATUS
WCacheDirect__(IN PW_CACHE Cache,IN PVOID Context,IN lba_t Lba,IN BOOLEAN Modified,OUT PCHAR * CachedBlock,IN BOOLEAN CachedOnly)2949 WCacheDirect__(
2950     IN PW_CACHE Cache,        // pointer to the Cache Control structure
2951     IN PVOID Context,         // user-supplied context for IO callbacks
2952     IN lba_t Lba,             // LBA of block to get pointer to
2953     IN BOOLEAN Modified,      // indicates that block will be modified
2954     OUT PCHAR* CachedBlock,   // address for pointer to cached block to be stored in
2955     IN BOOLEAN CachedOnly     // specifies that cache is already locked
2956     )
2957 {
2958     ULONG frame;
2959     ULONG i;
2960     OSSTATUS status = STATUS_SUCCESS;
2961     PW_CACHE_ENTRY block_array;
2962     ULONG BS = Cache->BlockSize;
2963     PCHAR addr;
2964     SIZE_T _ReadBytes;
2965     ULONG block_type;
2966 
2967     WcPrint(("WC:%sD %x (1)\n", Modified ? "W" : "R", Lba));
2968 
2969     // lock cache if nececcary
2970     if(!CachedOnly) {
2971         ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
2972     }
2973     // check if we try to access beyond cached area
2974     if((Lba < Cache->FirstLba) ||
2975        (Lba > Cache->LastLba)) {
2976         UDFPrint(("LBA %#x is beyond cacheable area\n", Lba));
2977         BrutePoint();
2978         status = STATUS_INVALID_PARAMETER;
2979         goto EO_WCache_D;
2980     }
2981 
2982     frame = Lba >> Cache->BlocksPerFrameSh;
2983     i = Lba - (frame << Cache->BlocksPerFrameSh);
2984     // check if we have enough space to store requested block
2985     if(!CachedOnly &&
2986        !OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba, 1))) {
2987         BrutePoint();
2988         goto EO_WCache_D;
2989     }
2990 
2991     // small updates are more important
2992     block_array = Cache->FrameList[frame].Frame;
2993     if(Modified) {
2994         Cache->FrameList[frame].UpdateCount+=8;
2995     } else {
2996         Cache->FrameList[frame].AccessCount+=8;
2997     }
2998     if(!block_array) {
2999         ASSERT(Cache->FrameCount < Cache->MaxFrames);
3000         block_array = WCacheInitFrame(Cache, Context, frame);
3001         if(!block_array) {
3002             status = STATUS_INSUFFICIENT_RESOURCES;
3003             goto EO_WCache_D;
3004         }
3005     }
3006     // check if requested block is already cached
3007     if( !(addr = (PCHAR)WCacheSectorAddr(block_array, i)) ) {
3008         // block is not cached
3009         // allocate memory and read block from media
3010         // do not set block_array[i].Sector here, because if media access fails and recursive access to cache
3011         // comes, this block should not be marked as 'cached'
3012         addr = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG);
3013         if(!addr) {
3014             status = STATUS_INSUFFICIENT_RESOURCES;
3015             goto EO_WCache_D;
3016         }
3017         block_type = Cache->CheckUsedProc(Context, Lba);
3018         if(block_type == WCACHE_BLOCK_USED) {
3019             status = Cache->ReadProc(Context, addr, BS, Lba, &_ReadBytes, PH_TMP_BUFFER);
3020             if(Cache->RememberBB) {
3021                 if(!OS_SUCCESS(status)) {
3022                     RtlZeroMemory(addr, BS);
3023                     //WCacheSetBadFlag(block_array,i);
3024                 }
3025             }
3026         } else {
3027             if(block_type & WCACHE_BLOCK_BAD) {
3028                 DbgFreePool(addr);
3029                 addr = NULL;
3030                 status = STATUS_DEVICE_DATA_ERROR;
3031                 goto EO_WCache_D;
3032             }
3033             if(!(block_type & WCACHE_BLOCK_ZERO)) {
3034                 BrutePoint();
3035             }
3036             status = STATUS_SUCCESS;
3037             RtlZeroMemory(addr, BS);
3038         }
3039         // now add pointer to buffer to common storage
3040         block_array[i].Sector = addr;
3041         WCacheInsertItemToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba);
3042         if(Modified) {
3043             WCacheInsertItemToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3044             WCacheSetModFlag(block_array, i);
3045         }
3046         Cache->FrameList[frame].BlockCount ++;
3047     } else {
3048         // block is not cached
3049         // just return pointer
3050         block_type = Cache->CheckUsedProc(Context, Lba);
3051         if(block_type & WCACHE_BLOCK_BAD) {
3052         //if(WCacheGetBadFlag(block_array,i)) {
3053             // bad packet. no pre-read
3054             status = STATUS_DEVICE_DATA_ERROR;
3055             goto EO_WCache_D;
3056         }
3057 #ifndef UDF_CHECK_UTIL
3058         ASSERT(block_type & WCACHE_BLOCK_USED);
3059 #else
3060         if(!(block_type & WCACHE_BLOCK_USED)) {
3061             UDFPrint(("LBA %#x is not marked as used\n", Lba));
3062         }
3063 #endif
3064         if(Modified &&
3065            !WCacheGetModFlag(block_array, i)) {
3066             WCacheInsertItemToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3067             WCacheSetModFlag(block_array, i);
3068         }
3069     }
3070     (*CachedBlock) = addr;
3071 
3072 EO_WCache_D:
3073 
3074     return status;
3075 } // end WCacheDirect__()
3076 
3077 /*
3078   WCacheEODirect__() must be used to unlock cache after calls to
3079   to WCacheStartDirect__().
3080   Public routine
3081  */
3082 OSSTATUS
WCacheEODirect__(IN PW_CACHE Cache,IN PVOID Context)3083 WCacheEODirect__(
3084     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3085     IN PVOID Context          // user-supplied context for IO callbacks
3086     )
3087 {
3088     ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
3089     return STATUS_SUCCESS;
3090 } // end WCacheEODirect__()
3091 
3092 /*
3093   WCacheStartDirect__() locks cache for exclusive use.
3094   Using this routine caller can access cached block directly in memory
3095   without Read_to_Tmp and Modify/Write steps.
3096   See also WCacheDirect__()
3097   Cache can be unlocked by WCacheEODirect__().
3098   Public routine
3099  */
3100 OSSTATUS
WCacheStartDirect__(IN PW_CACHE Cache,IN PVOID Context,IN BOOLEAN Exclusive)3101 WCacheStartDirect__(
3102     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3103     IN PVOID Context,         // user-supplied context for IO callbacks
3104     IN BOOLEAN Exclusive      // lock cache for exclusive use,
3105                               //   currently must be TRUE.
3106     )
3107 {
3108     if(Exclusive) {
3109         ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
3110     } else {
3111         BrutePoint();
3112         ExAcquireResourceSharedLite(&(Cache->WCacheLock), TRUE);
3113     }
3114     return STATUS_SUCCESS;
3115 } // end WCacheStartDirect__()
3116 
3117 /*
3118   WCacheIsCached__() checks if requested blocks are immediately available.
3119   Cache must be previously locked for exclusive use with WCacheStartDirect__().
3120   Using this routine caller can access cached block directly in memory
3121   without Read_to_Tmp and Modify/Write steps.
3122   See also WCacheDirect__().
3123   Cache can be unlocked by WCacheEODirect__().
3124   Public routine
3125  */
3126 BOOLEAN
WCacheIsCached__(IN PW_CACHE Cache,IN lba_t Lba,IN ULONG BCount)3127 WCacheIsCached__(
3128     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3129     IN lba_t Lba,             // LBA to start check from
3130     IN ULONG BCount           // number of blocks to be checked
3131     )
3132 {
3133     ULONG frame;
3134     ULONG i;
3135     PW_CACHE_ENTRY block_array;
3136 
3137     // check if we try to access beyond cached area
3138     if((Lba < Cache->FirstLba) ||
3139        (Lba + BCount - 1 > Cache->LastLba)) {
3140         return FALSE;
3141     }
3142 
3143     frame = Lba >> Cache->BlocksPerFrameSh;
3144     i = Lba - (frame << Cache->BlocksPerFrameSh);
3145 
3146     block_array = Cache->FrameList[frame].Frame;
3147     if(!block_array) {
3148         return FALSE;
3149     }
3150 
3151     while(BCount) {
3152         if(i >= Cache->BlocksPerFrame) {
3153             frame++;
3154             block_array = Cache->FrameList[frame].Frame;
3155             i -= Cache->BlocksPerFrame;
3156         }
3157         if(!block_array) {
3158             return FALSE;
3159         }
3160         // 'read' cached extent (if any)
3161         while(BCount &&
3162               (i < Cache->BlocksPerFrame) &&
3163               WCacheSectorAddr(block_array, i) &&
3164               /*!WCacheGetBadFlag(block_array, i)*/
3165               /*!(Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_BAD)*/
3166               TRUE ) {
3167             i++;
3168             BCount--;
3169             Lba++;
3170         }
3171         if(BCount &&
3172               (i < Cache->BlocksPerFrame) /*&&
3173               (!WCacheSectorAddr(block_array, i))*/ ) {
3174             return FALSE;
3175         }
3176     }
3177     return TRUE;
3178 } // end WCacheIsCached__()
3179 
3180 /*
3181   WCacheCheckLimitsR() implements automatic flush and purge of
3182   unused blocks to keep enough free cache entries for newly
3183   read/written blocks for WORM media.
3184   See also WCacheCheckLimits()
3185   Internal routine
3186  */
3187 OSSTATUS
3188 __fastcall
WCacheCheckLimitsR(IN PW_CACHE Cache,IN PVOID Context,IN lba_t ReqLba,IN ULONG BCount)3189 WCacheCheckLimitsR(
3190     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3191     IN PVOID Context,         // user-supplied context for IO callbacks
3192     IN lba_t ReqLba,          // first LBA to access/cache
3193     IN ULONG BCount           // number of Blocks to access/cache
3194     )
3195 {
3196     ULONG frame;
3197     lba_t firstLba;
3198     lba_t* List = Cache->CachedBlocksList;
3199     lba_t Lba;
3200     PCHAR tmp_buff = Cache->tmp_buff;
3201     ULONG firstPos;
3202     ULONG BSh = Cache->BlockSizeSh;
3203     ULONG BS = Cache->BlockSize;
3204     ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
3205     ULONG PSs = Cache->PacketSize;
3206     ULONG i;
3207     PW_CACHE_ENTRY block_array;
3208     BOOLEAN mod;
3209     OSSTATUS status;
3210     SIZE_T ReadBytes;
3211     ULONG MaxReloc = Cache->PacketSize;
3212     PULONG reloc_tab = Cache->reloc_tab;
3213 
3214     // check if we try to read too much data
3215     if(BCount > Cache->MaxBlocks) {
3216         return STATUS_INVALID_PARAMETER;
3217     }
3218 
3219     // remove(flush) packets from entire frame(s)
3220     while( ((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) +
3221              BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) ||
3222            (Cache->FrameCount >= Cache->MaxFrames) ) {
3223 
3224 WCCL_retry_1:
3225 
3226         Lba = WCacheFindLbaToRelease(Cache);
3227         if(Lba == WCACHE_INVALID_LBA) {
3228             ASSERT(!Cache->FrameCount);
3229             ASSERT(!Cache->BlockCount);
3230             break;
3231         }
3232         frame = Lba >> Cache->BlocksPerFrameSh;
3233         firstLba = frame << Cache->BlocksPerFrameSh;
3234         firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba);
3235         block_array = Cache->FrameList[frame].Frame;
3236         if(!block_array) {
3237             return STATUS_DRIVER_INTERNAL_ERROR;
3238         }
3239         // check if modified
3240         mod = WCacheGetModFlag(block_array, Lba - firstLba);
3241         // read/modify/write
3242         if(mod && (Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED)) {
3243             if(Cache->WriteCount < MaxReloc) goto WCCL_retry_1;
3244             firstPos = WCacheGetSortedListIndex(Cache->WriteCount, Cache->CachedModifiedBlocksList, Lba);
3245             if(!block_array) {
3246                 return STATUS_DRIVER_INTERNAL_ERROR;
3247             }
3248             // prepare packet & reloc table
3249             for(i=0; i<MaxReloc; i++) {
3250                 Lba = Cache->CachedModifiedBlocksList[firstPos];
3251                 frame = Lba >> Cache->BlocksPerFrameSh;
3252                 firstLba = frame << Cache->BlocksPerFrameSh;
3253                 block_array = Cache->FrameList[frame].Frame;
3254                 DbgCopyMemory(tmp_buff + (i << BSh),
3255                               (PVOID)WCacheSectorAddr(block_array, Lba-firstLba),
3256                               BS);
3257                 reloc_tab[i] = Lba;
3258                 WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
3259                 WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3260                 // mark as non-cached & free pool
3261                 WCacheFreeSector(frame, Lba-firstLba);
3262                 // check if frame is empty
3263                 if(!Cache->FrameList[frame].BlockCount) {
3264                     WCacheRemoveFrame(Cache, Context, frame);
3265                 }
3266                 if(firstPos >= Cache->WriteCount) firstPos=0;
3267             }
3268             // write packet
3269 //            status = Cache->WriteProcAsync(Context, tmp_buff, PS, Lba, &ReadBytes, FALSE);
3270             Cache->UpdateRelocProc(Context, NULL, reloc_tab, MaxReloc);
3271             status = Cache->WriteProc(Context, tmp_buff, PS, NULL, &ReadBytes, 0);
3272             if(!OS_SUCCESS(status)) {
3273                 status = WCacheRaiseIoError(Cache, Context, status, NULL, PSs, tmp_buff, WCACHE_W_OP, NULL);
3274             }
3275         } else {
3276 
3277             if((i = Cache->BlockCount - Cache->WriteCount) > MaxReloc) i = MaxReloc;
3278             // discard blocks
3279             for(; i; i--) {
3280                 Lba = List[firstPos];
3281                 frame = Lba >> Cache->BlocksPerFrameSh;
3282                 firstLba = frame << Cache->BlocksPerFrameSh;
3283                 block_array = Cache->FrameList[frame].Frame;
3284 
3285                 if( (mod = WCacheGetModFlag(block_array, Lba - firstLba)) &&
3286                     (Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED) )
3287                     continue;
3288                 WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
3289                 if(mod)
3290                     WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3291                 // mark as non-cached & free pool
3292                 WCacheFreeSector(frame, Lba-firstLba);
3293                 // check if frame is empty
3294                 if(!Cache->FrameList[frame].BlockCount) {
3295                     WCacheRemoveFrame(Cache, Context, frame);
3296                 }
3297                 if(firstPos >= Cache->WriteCount) firstPos=0;
3298             }
3299         }
3300     }
3301     return STATUS_SUCCESS;
3302 } // end WCacheCheckLimitsR()
3303 
3304 /*
3305   WCachePurgeAllR() copies modified blocks from cache to media
3306   and removes them from cache
3307   This routine can be used for R media only.
3308   Internal routine
3309  */
3310 VOID
3311 __fastcall
WCachePurgeAllR(IN PW_CACHE Cache,IN PVOID Context)3312 WCachePurgeAllR(
3313     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3314     IN PVOID Context)         // user-supplied context for IO callbacks
3315 {
3316     ULONG frame;
3317     lba_t firstLba;
3318     lba_t* List = Cache->CachedBlocksList;
3319     lba_t Lba;
3320     PCHAR tmp_buff = Cache->tmp_buff;
3321     ULONG BSh = Cache->BlockSizeSh;
3322     ULONG BS = Cache->BlockSize;
3323 //    ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
3324 //    ULONG PSs = Cache->PacketSize;
3325     PW_CACHE_ENTRY block_array;
3326     BOOLEAN mod;
3327     OSSTATUS status;
3328     SIZE_T ReadBytes;
3329     ULONG MaxReloc = Cache->PacketSize;
3330     PULONG reloc_tab = Cache->reloc_tab;
3331     ULONG RelocCount = 0;
3332     BOOLEAN IncompletePacket;
3333     ULONG i=0;
3334     ULONG PacketTail;
3335 
3336     while(Cache->WriteCount < Cache->BlockCount) {
3337 
3338         Lba = List[i];
3339         frame = Lba >> Cache->BlocksPerFrameSh;
3340         firstLba = frame << Cache->BlocksPerFrameSh;
3341         block_array = Cache->FrameList[frame].Frame;
3342         if(!block_array) {
3343             BrutePoint();
3344             return;
3345         }
3346         // check if modified
3347         mod = WCacheGetModFlag(block_array, Lba - firstLba);
3348         // just discard
3349         if(!mod || !(Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED)) {
3350             // mark as non-cached & free pool
3351             if(WCacheSectorAddr(block_array,Lba-firstLba)) {
3352                 WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
3353                 if(mod)
3354                     WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3355                 // mark as non-cached & free pool
3356                 WCacheFreeSector(frame, Lba-firstLba);
3357                 // check if frame is empty
3358                 if(!Cache->FrameList[frame].BlockCount) {
3359                     WCacheRemoveFrame(Cache, Context, frame);
3360                 }
3361             } else {
3362                 BrutePoint();
3363             }
3364         } else {
3365             i++;
3366         }
3367     }
3368 
3369     PacketTail = Cache->WriteCount & (MaxReloc-1);
3370     IncompletePacket = (Cache->WriteCount >= MaxReloc) ? FALSE : TRUE;
3371 
3372     // remove(flush) packet
3373     while((Cache->WriteCount > PacketTail) || (Cache->WriteCount && IncompletePacket)) {
3374 
3375         Lba = List[0];
3376         frame = Lba >> Cache->BlocksPerFrameSh;
3377         firstLba = frame << Cache->BlocksPerFrameSh;
3378         block_array = Cache->FrameList[frame].Frame;
3379         if(!block_array) {
3380             BrutePoint();
3381             return;
3382         }
3383         // check if modified
3384         mod = WCacheGetModFlag(block_array, Lba - firstLba);
3385         // pack/reloc/write
3386         if(mod) {
3387             DbgCopyMemory(tmp_buff + (RelocCount << BSh),
3388                           (PVOID)WCacheSectorAddr(block_array, Lba-firstLba),
3389                           BS);
3390             reloc_tab[RelocCount] = Lba;
3391             RelocCount++;
3392             // write packet
3393             if((RelocCount >= MaxReloc) || (Cache->BlockCount == 1)) {
3394 //                status = Cache->WriteProcAsync(Context, tmp_buff, PS, Lba, &ReadBytes, FALSE);
3395                 Cache->UpdateRelocProc(Context, NULL, reloc_tab, RelocCount);
3396                 status = Cache->WriteProc(Context, tmp_buff, RelocCount<<BSh, NULL, &ReadBytes, 0);
3397                 if(!OS_SUCCESS(status)) {
3398                     status = WCacheRaiseIoError(Cache, Context, status, NULL, RelocCount, tmp_buff, WCACHE_W_OP, NULL);
3399                 }
3400                 RelocCount = 0;
3401             }
3402             WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3403         } else {
3404             BrutePoint();
3405         }
3406         // mark as non-cached & free pool
3407         if(WCacheSectorAddr(block_array,Lba-firstLba)) {
3408             WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
3409             // mark as non-cached & free pool
3410             WCacheFreeSector(frame, Lba-firstLba);
3411             // check if frame is empty
3412             if(!Cache->FrameList[frame].BlockCount) {
3413                 WCacheRemoveFrame(Cache, Context, frame);
3414             }
3415         } else {
3416             BrutePoint();
3417         }
3418     }
3419 } // end WCachePurgeAllR()
3420 
3421 /*
3422   WCacheSetMode__() changes cache operating mode (ROM/R/RW/RAM).
3423   Public routine
3424  */
3425 OSSTATUS
WCacheSetMode__(IN PW_CACHE Cache,IN ULONG Mode)3426 WCacheSetMode__(
3427     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3428     IN ULONG Mode             // cache mode/media type to be used
3429     )
3430 {
3431     if(Mode > WCACHE_MODE_MAX) return STATUS_INVALID_PARAMETER;
3432     Cache->Mode = Mode;
3433     return STATUS_SUCCESS;
3434 } // end WCacheSetMode__()
3435 
3436 /*
3437   WCacheGetMode__() returns cache operating mode (ROM/R/RW/RAM).
3438   Public routine
3439  */
3440 ULONG
WCacheGetMode__(IN PW_CACHE Cache)3441 WCacheGetMode__(
3442     IN PW_CACHE Cache
3443     )
3444 {
3445     return Cache->Mode;
3446 } // end WCacheGetMode__()
3447 
3448 /*
3449   WCacheGetWriteBlockCount__() returns number of modified blocks, those are
3450   not flushed to media. Is usually used to preallocate blocks for
3451   relocation table on WORM (R) media.
3452   Public routine
3453  */
3454 ULONG
WCacheGetWriteBlockCount__(IN PW_CACHE Cache)3455 WCacheGetWriteBlockCount__(
3456     IN PW_CACHE Cache
3457     )
3458 {
3459     return Cache->WriteCount;
3460 } // end WCacheGetWriteBlockCount__()
3461 
3462 /*
3463   WCacheSyncReloc__() builds list of all modified blocks, currently
3464   stored in cache. For each modified block WCacheSyncReloc__() calls
3465   user-supplied callback routine in order to update relocation table
3466   on WORM (R) media.
3467   Public routine
3468  */
3469 VOID
WCacheSyncReloc__(IN PW_CACHE Cache,IN PVOID Context)3470 WCacheSyncReloc__(
3471     IN PW_CACHE Cache,
3472     IN PVOID Context)
3473 {
3474     ULONG frame;
3475     lba_t firstLba;
3476     lba_t* List = Cache->CachedBlocksList;
3477     lba_t Lba;
3478 //    ULONG BSh = Cache->BlockSizeSh;
3479 //    ULONG BS = Cache->BlockSize;
3480 //    ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes)
3481 //    ULONG PSs = Cache->PacketSize;
3482     PW_CACHE_ENTRY block_array;
3483     BOOLEAN mod;
3484     ULONG MaxReloc = Cache->PacketSize;
3485     PULONG reloc_tab = Cache->reloc_tab;
3486     ULONG RelocCount = 0;
3487     BOOLEAN IncompletePacket;
3488 
3489     IncompletePacket = (Cache->WriteCount >= MaxReloc) ? FALSE : TRUE;
3490     // enumerate modified blocks
3491     for(ULONG i=0; IncompletePacket && (i<Cache->BlockCount); i++) {
3492 
3493         Lba = List[i];
3494         frame = Lba >> Cache->BlocksPerFrameSh;
3495         firstLba = frame << Cache->BlocksPerFrameSh;
3496         block_array = Cache->FrameList[frame].Frame;
3497         if(!block_array) {
3498             return;
3499         }
3500         // check if modified
3501         mod = WCacheGetModFlag(block_array, Lba - firstLba);
3502         // update relocation table for modified sectors
3503         if(mod && (Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED)) {
3504             reloc_tab[RelocCount] = Lba;
3505             RelocCount++;
3506             if(RelocCount >= Cache->WriteCount) {
3507                 Cache->UpdateRelocProc(Context, NULL, reloc_tab, RelocCount);
3508                 break;
3509             }
3510         }
3511     }
3512 } // end WCacheSyncReloc__()
3513 
3514 /*
3515   WCacheDiscardBlocks__() removes specified blocks from cache.
3516   Blocks are not flushed to media.
3517   Public routine
3518  */
3519 VOID
WCacheDiscardBlocks__(IN PW_CACHE Cache,IN PVOID Context,IN lba_t ReqLba,IN ULONG BCount)3520 WCacheDiscardBlocks__(
3521     IN PW_CACHE Cache,
3522     IN PVOID Context,
3523     IN lba_t ReqLba,
3524     IN ULONG BCount
3525     )
3526 {
3527     ULONG frame;
3528     lba_t firstLba;
3529     lba_t* List;
3530     lba_t Lba;
3531     PW_CACHE_ENTRY block_array;
3532     BOOLEAN mod;
3533     ULONG i;
3534 
3535     ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE);
3536 
3537     UDFPrint(("  Discard req: %x@%x\n",BCount, ReqLba));
3538 
3539     List = Cache->CachedBlocksList;
3540     if(!List) {
3541         ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
3542         return;
3543     }
3544     i = WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba);
3545 
3546     // enumerate requested blocks
3547     while((List[i] < (ReqLba+BCount)) && (i < Cache->BlockCount)) {
3548 
3549         Lba = List[i];
3550         frame = Lba >> Cache->BlocksPerFrameSh;
3551         firstLba = frame << Cache->BlocksPerFrameSh;
3552         block_array = Cache->FrameList[frame].Frame;
3553         if(!block_array) {
3554             ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
3555             BrutePoint();
3556             return;
3557         }
3558         // check if modified
3559         mod = WCacheGetModFlag(block_array, Lba - firstLba);
3560         // just discard
3561 
3562         // mark as non-cached & free pool
3563         if(WCacheSectorAddr(block_array,Lba-firstLba)) {
3564             WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba);
3565             if(mod)
3566                 WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba);
3567             // mark as non-cached & free pool
3568             WCacheFreeSector(frame, Lba-firstLba);
3569             // check if frame is empty
3570             if(!Cache->FrameList[frame].BlockCount) {
3571                 WCacheRemoveFrame(Cache, Context, frame);
3572             } else {
3573                 ASSERT(Cache->FrameList[frame].Frame);
3574             }
3575         } else {
3576             // we should never get here !!!
3577             // getting this part of code means that we have
3578             // placed non-cached block in CachedBlocksList
3579             BrutePoint();
3580         }
3581     }
3582     ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread());
3583 } // end WCacheDiscardBlocks__()
3584 
3585 OSSTATUS
WCacheCompleteAsync__(IN PVOID WContext,IN OSSTATUS Status)3586 WCacheCompleteAsync__(
3587     IN PVOID WContext,
3588     IN OSSTATUS Status
3589     )
3590 {
3591     PW_CACHE_ASYNC AsyncCtx = (PW_CACHE_ASYNC)WContext;
3592 //    PW_CACHE Cache = AsyncCtx->Cache;
3593 
3594     AsyncCtx->PhContext.IosbToUse.Status = Status;
3595     KeSetEvent(&(AsyncCtx->PhContext.event), 0, FALSE);
3596 
3597     return STATUS_SUCCESS;
3598 } // end WCacheSetMode__()
3599 
3600 /*
3601   WCacheDecodeFlags() updates internal BOOLEANs according to Flags
3602   Internal routine
3603  */
3604 OSSTATUS
3605 __fastcall
WCacheDecodeFlags(IN PW_CACHE Cache,IN ULONG Flags)3606 WCacheDecodeFlags(
3607     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3608     IN ULONG Flags            // cache mode flags
3609     )
3610 {
3611     //ULONG OldFlags;
3612     if(Flags & ~WCACHE_VALID_FLAGS) {
3613         UDFPrint(("Invalid flags: %x\n", Flags & ~WCACHE_VALID_FLAGS));
3614         return STATUS_INVALID_PARAMETER;
3615     }
3616     Cache->CacheWholePacket = (Flags & WCACHE_CACHE_WHOLE_PACKET) ? TRUE : FALSE;
3617     Cache->DoNotCompare = (Flags & WCACHE_DO_NOT_COMPARE) ? TRUE : FALSE;
3618     Cache->Chained = (Flags & WCACHE_CHAINED_IO) ? TRUE : FALSE;
3619     Cache->RememberBB = (Flags & WCACHE_MARK_BAD_BLOCKS) ? TRUE : FALSE;
3620     if(Cache->RememberBB) {
3621         Cache->NoWriteBB = (Flags & WCACHE_RO_BAD_BLOCKS) ? TRUE : FALSE;
3622     }
3623     Cache->NoWriteThrough = (Flags & WCACHE_NO_WRITE_THROUGH) ? TRUE : FALSE;
3624 
3625     Cache->Flags = Flags;
3626 
3627     return STATUS_SUCCESS;
3628 }
3629 
3630 /*
3631   WCacheChFlags__() changes cache flags.
3632   Public routine
3633  */
3634 ULONG
WCacheChFlags__(IN PW_CACHE Cache,IN ULONG SetFlags,IN ULONG ClrFlags)3635 WCacheChFlags__(
3636     IN PW_CACHE Cache,        // pointer to the Cache Control structure
3637     IN ULONG SetFlags,        // cache mode/media type to be set
3638     IN ULONG ClrFlags         // cache mode/media type to be cleared
3639     )
3640 {
3641     ULONG Flags;
3642 
3643     if(SetFlags || ClrFlags) {
3644         Flags = (Cache->Flags & ~ClrFlags) | SetFlags;
3645 
3646         if(!OS_SUCCESS(WCacheDecodeFlags(Cache, Flags))) {
3647             return -1;
3648         }
3649     } else {
3650         return Cache->Flags;
3651     }
3652     return Flags;
3653 } // end WCacheSetMode__()
3654